gsv-ocr-check
A Python library for verifying Google Street View panorama existence and calibrating OCR bounding boxes through perspective re-projection.
Features
- Panorama Existence Check: Verify whether a Google Street View panorama ID is still accessible
- Panorama Download: Download panoramas to disk or into memory as PIL Images
- OCR Bounding Box Calibration: Refine OCR coordinates by re-running OCR on perspective crops at multiple fields of view
- Engine Agnostic: Works with any panoocr-compatible OCR engine
- Async Support: Both sync and async APIs for panorama operations
Installation
Install the base package:
pip install gsv-ocr-check
Install with macOS OCR support:
pip install "gsv-ocr-check[macocr]"
Using uv (recommended):
uv add gsv-ocr-check
uv add "gsv-ocr-check[macocr]"
Quick Start
from gsv_ocr_check import check_panorama_exists, calibrate_ocr, download_panorama
from panoocr.engines.macocr import MacOCREngine
# 1. Check if a panorama still exists on Google Street View
exists = check_panorama_exists("abc123")
# 2. Download the panorama
image = download_panorama("abc123", output_path="pano.jpg")
# 3. Calibrate an OCR bounding box
engine = MacOCREngine()
result = calibrate_ocr(
panorama="pano.jpg",
target_text="LOVE",
yaw=38.5,
pitch=-2.4,
engine=engine,
)
if result:
print(f"Text: {result.text}")
print(f"Position: yaw={result.yaw:.2f}°, pitch={result.pitch:.2f}°")
print(f"Size: {result.width:.2f}° x {result.height:.2f}°")
print(f"Confidence: {result.confidence}")
print(f"FOV used: {result.fov_used}°")
API
Panorama Operations
check_panorama_exists(panorama_id) -> bool
Check if a Google Street View panorama still exists. Returns True if accessible, False otherwise.
from gsv_ocr_check import check_panorama_exists
if check_panorama_exists("UVfBLpzNhpZdImXJqozDmg"):
print("Panorama is still available")
download_panorama(panorama_id, output_path=None, zoom=5, quality=95) -> Image | None
Download a panorama image. Returns a PIL Image, or None if the panorama is not found.
from gsv_ocr_check import download_panorama
# Download to memory only
image = download_panorama("abc123")
# Download and save to disk
image = download_panorama("abc123", output_path="panoramas/abc123.jpg")
# Download at lower resolution (zoom 0-5)
image = download_panorama("abc123", zoom=2)
Async Variants
Both functions have async counterparts for use in async contexts:
from gsv_ocr_check import check_panorama_exists_async, download_panorama_async
exists = await check_panorama_exists_async("abc123")
image = await download_panorama_async("abc123", output_path="pano.jpg")
OCR Calibration
calibrate_ocr(panorama, target_text, yaw, pitch, engine, **kwargs) -> CalibrationResult | None
Refine an OCR bounding box by generating perspective views centered on the original detection, re-running OCR, and returning the best match in spherical coordinates.
from gsv_ocr_check import calibrate_ocr
from panoocr.engines.macocr import MacOCREngine
engine = MacOCREngine()
result = calibrate_ocr(
panorama="pano.jpg", # path or PIL Image
target_text="LOVE", # exact match, case-insensitive
yaw=38.5, # original detection yaw
pitch=-2.4, # original detection pitch
engine=engine, # any panoocr-compatible engine
)
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
panorama |
str | Path | Image |
required | Path to equirectangular panorama or PIL Image |
target_text |
str |
required | Text to match (exact, case-insensitive) |
yaw |
float |
required | Original detection yaw in degrees |
pitch |
float |
required | Original detection pitch in degrees |
engine |
object |
required | panoocr-compatible OCR engine |
panorama_id |
str |
None |
ID for naming saved perspective files |
fovs |
list[float] |
[45, 90, 120] |
FOVs to try in order |
resolution |
int |
2048 |
Perspective image resolution in pixels |
save_perspectives |
str | Path |
None |
Directory to save perspective crops |
CalibrationResult
Dataclass returned by calibrate_ocr with the refined detection:
@dataclass
class CalibrationResult:
text: str # Matched text
confidence: float # OCR confidence score
yaw: float # Refined yaw in degrees
pitch: float # Refined pitch in degrees
width: float # Angular width in degrees
height: float # Angular height in degrees
engine: str # OCR engine class name
match_type: str # "exact"
original_yaw: float # Input yaw (preserved)
original_pitch: float # Input pitch (preserved)
fov_used: float # FOV that produced the match
resolution_used: int # Resolution used
How Calibration Works
- Load the equirectangular panorama image
- Generate a perspective view centered on the original yaw/pitch coordinates
- Try multiple FOVs in sequence (default: 45° → 90° → 120°) — narrow FOV gives better resolution, wider FOV captures more context
- Run OCR on each perspective image
- Filter for exact text matches (case-insensitive)
- Select the match closest to the image center
- Transform pixel coordinates back to spherical using
panoocr.geometry.perspective_to_sphere - Return the first successful match, or
Noneif no match at any FOV
Advanced Usage
Custom OCR Engine
Any object with a recognize(pil_image) method returning results with .text, .confidence, and .bounding_box attributes works:
from panoocr.engines.easyocr import EasyOCREngine
engine = EasyOCREngine(config={"language_preference": ["en"]})
result = calibrate_ocr("pano.jpg", "HELLO", yaw=0, pitch=0, engine=engine)
Saving Perspective Crops
Save the perspective images generated during calibration for debugging:
result = calibrate_ocr(
panorama="pano.jpg",
target_text="LOVE",
yaw=38.5,
pitch=-2.4,
engine=engine,
save_perspectives="debug/perspectives/",
)
# Saves: debug/perspectives/<pano_id>_fov45.jpg, etc.
Custom FOVs and Resolution
result = calibrate_ocr(
panorama="pano.jpg",
target_text="LOVE",
yaw=38.5,
pitch=-2.4,
engine=engine,
fovs=[30.0, 60.0, 90.0], # custom FOV sequence
resolution=4096, # higher resolution perspective
)
Using PIL Images Directly
from PIL import Image
image = Image.open("pano.jpg")
result = calibrate_ocr(
panorama=image,
target_text="LOVE",
yaw=38.5,
pitch=-2.4,
engine=engine,
panorama_id="my_pano", # required when passing PIL Image
)
Coordinate System
All coordinates use spherical (yaw, pitch) format:
- Yaw: Horizontal angle in degrees, normalized to [-180, 180]
- Pitch: Vertical angle in degrees (-90 to 90)
- Width/Height: Angular dimensions in degrees
Dependencies
- streetlevel — Google Street View panorama access
- panoocr — Perspective projection, coordinate transforms, OCR engines
- Pillow — Image handling
License
MIT License — see LICENSE for details.
Metadata
Release files for gsv-ocr-check 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| gsv_ocr_check-0.1.0.tar.gz | 11.9 kB | With provenance | Details for gsv_ocr_check-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gsv_ocr_check-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for gsv_ocr_check-0.1.0-py3-none-any.whl |
Total release size: 20.0 kB
Release files / gsv_ocr_check-0.1.0.tar.gz
| Download URL | gsv_ocr_check-0.1.0.tar.gz |
|---|---|
| Size | 11.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
55e765e4ed6e1aa289d137dfb864757770ff96d83182d1680b2b2e17508a0edc
|
|
BLAKE2b-256 checksum How to use checksums |
0350a7345c0a5cdab1b2c3db382f25b12d57e0817bf7605df05839b7699d3b6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 7, 2026.
Transparency logRelease files / gsv_ocr_check-0.1.0-py3-none-any.whl
| Download URL | gsv_ocr_check-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5cb44382c6539673c841521bc1150f2a33c1fe79601d363329e8b5385b83fcef
|
|
BLAKE2b-256 checksum How to use checksums |
dfed60ebba28a6cbcd386442e4ee6c0c46b6da2bcf581d6fbb4c9354c00abc03
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 7, 2026.
Transparency logSecurity for gsv-ocr-check 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for gsv_ocr_check-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.