Automate text extraction from images using GCP Vision and Python
Turn a scanned page or screenshot into text with a small Python command-line program. This example sends one local JPEG or PNG to Google Cloud Vision and prints the recognized text, or a JSON summary for another program to consume. It does not need a Cloud Storage bucket.
Requirements
- Python 3.14.7 or a newer maintenance release of Python 3.14, installed from Python’s downloads.
- The Google Cloud CLI and a Google Cloud project with billing and the Cloud Vision API enabled.
- A local JPEG or PNG that you are allowed to send to Google Cloud.
The shell commands below use Bash on macOS. The walkthrough was tested with Python 3.14.7 and
google-cloud-vision 3.16.0. Open a terminal in a dedicated example directory; keep the program and
your image there. Check that the prerequisites run before creating the virtual environment:
python3.14 --version && gcloud --version
Set up the cloud project
In the Google Cloud Console, select a project, confirm billing is enabled, and enable the Cloud Vision API. Note its project ID. If somebody else manages the project, ask them to complete those steps and authorize your account; see Google’s Vision setup guide.
Development setup
For local development, use Application Default Credentials (ADC). Replace PROJECT_ID below with
your project ID, then sign in with an account authorized to use that project:
gcloud auth application-default login &&
gcloud auth application-default set-quota-project PROJECT_ID
ADC credentials are separate from the CLI’s normal login. The quota-project command needs
serviceusage.services.use, supplied by the Service Usage Consumer role. An existing
GOOGLE_APPLICATION_CREDENTIALS setting takes precedence over the local ADC file; check that it
points to your intended credentials, or remove that override from this terminal before using the
local login. Google’s authentication guide
explains both the login and quota-project requirements. Do not put credentials in the program.
Production authentication
For an eventual deployment, prefer an attached service account on Google Cloud or Workload Identity Federation elsewhere. Both work with ADC without downloading a service-account key. This walkthrough uses local user credentials and does not configure a production deployment.
Install the Python client
Create a new virtual environment and install the pinned client. Calling its interpreter directly avoids shell activation and keeps installation inside this directory:
test ! -e .venv && test ! -L .venv &&
python3.14 -m venv .venv &&
./.venv/bin/python -m pip install 'google-cloud-vision==3.16.0'
The first check refuses to reuse an existing .venv. If installation fails after the environment
has been created, keep your files and retry just the installation command:
./.venv/bin/python -m pip install 'google-cloud-vision==3.16.0'
If virtual-environment creation itself failed, choose a fresh example directory before repeating
setup. Use the .venv interpreter for later commands, even if another environment is active in
your terminal.
Save the OCR program
Save the complete block as extract_text.py. It uses document_text_detection for dense page text
and reads full_text_annotation.text, as described in Google’s
document text detection tutorial.
The REST transport still uses the official Python client and ADC.
import argparse
import json
from pathlib import Path
import sys
import time
from google.api_core.exceptions import ServiceUnavailable
from google.cloud import vision
class OcrError(RuntimeError):
"""A safe diagnostic generated by this program."""
def process_ocr_text(raw_text: str) -> dict:
lines = raw_text.splitlines()
return {
'full_text': raw_text,
'lines': lines,
'line_count': len(lines),
'word_count': len(raw_text.split()),
'cleaned_text': ' '.join(raw_text.split()),
}
def safe_ocr_call(client, image):
for attempt in range(3):
try:
response = client.document_text_detection(
image=image, retry=None, timeout=30.0
)
except ServiceUnavailable:
if attempt == 2:
raise
time.sleep(2 ** attempt)
continue
if response.error.code:
raise OcrError(f'Vision OCR failed (code {response.error.code}).')
return response
def extract_text(image_path: str) -> str:
path = Path(image_path)
if path.suffix.lower() not in ('.jpg', '.jpeg', '.png'):
raise OcrError('Use one JPEG or PNG image, not a PDF or TIFF.')
content = path.read_bytes()
if not content.startswith((b'\x89PNG\r\n\x1a\n', b'\xff\xd8\xff')):
raise OcrError('Input does not have a JPEG or PNG signature.')
with vision.ImageAnnotatorClient(transport='rest') as client:
response = safe_ocr_call(client, vision.Image(content=content))
return response.full_text_annotation.text
def main() -> int:
parser = argparse.ArgumentParser(description='Extract text from one JPEG or PNG.')
parser.add_argument('image_path')
parser.add_argument('--json', action='store_true', help='Print a JSON summary.')
args = parser.parse_args()
try:
text = extract_text(args.image_path)
except OcrError as error:
print(error, file=sys.stderr)
return 1
except Exception as error:
print(f'OCR failed ({type(error).__name__}).', file=sys.stderr)
return 1
if args.json:
print(json.dumps(process_ocr_text(text), ensure_ascii=False))
else:
sys.stdout.write(text)
if not text:
print('No text detected.', file=sys.stderr)
return 0
if __name__ == '__main__':
sys.exit(main())
The signature check catches empty files and obvious format mistakes before a request. It does not fully decode or validate an image; damage beyond the header may be rejected or recovered by Vision. Inspect the source image and compare the recognized text with it before using the result.
Run it on your image
Save a clear scan or screenshot as invoice.png beside the program, then run:
./.venv/bin/python extract_text.py invoice.png
Recognized text goes to standard output with its original line breaks. A successful request with no
recognized text exits with status zero, leaves standard output empty, and prints No text detected.
to standard error. This is a recognition result, not proof that the image contains no writing.
Errors exit with status one and no extracted text; incorrect command-line arguments exit with
status two.
To get the connected normalization example, run the same program with --json:
./.venv/bin/python extract_text.py --json invoice.png
full_text retains the OCR response. lines, line_count, and word_count describe that text;
cleaned_text collapses whitespace for searching. These fields do not identify invoice totals,
tables, or document paragraphs. A blank result produces empty strings, an empty lines array, and
zero counts.
Diagnose a failed extraction
The program disables the client’s automatic retries and makes at most three OCR method calls,
retrying only ServiceUnavailable, with waits of one and two seconds. Each call has a 30-second
request timeout; that is not a deadline for the entire command, including authentication and retry
waits. Permission failures, quota exhaustion, and embedded per-image errors are not retried.
FileNotFoundError: check the image filename and your terminal’s current directory.DefaultCredentialsError: complete the ADC login or correct an unintended credential override.Forbidden,PermissionDenied, or embedded code 7: check project access, API enablement, and the ADC quota project. Repeating the same request cannot grant permission.TooManyRequests,ResourceExhausted, or embedded code 8: inspect the project’s quotas before retrying manually.- An embedded code 3 or
InvalidArgument: check that the input is a readable, intact JPEG or PNG.
Third-party error messages are not printed because they can contain request details. Embedded numeric codes and exception names identify the next check without exposing those details.
Keep the image workflow within its limits
This program reads one complete image into memory and sends it to a cloud service. Use a small test image first, check the supported formats and size limits, and review Vision pricing before running a larger workload. Batching can reduce request overhead but does not remove per-image feature charges.
For multipage PDFs or TIFFs, use the file annotation APIs instead of renaming the file or passing it to this image CLI. Automatic language detection and OCR can still misread text; evaluate your own scans, scripts, and layouts. A clean synthetic example does not establish accuracy on receipts, handwriting, or production documents.
