sundeep-news-scan/claude_ocr.py

125 lines
4.0 KiB
Python

"""
Read Telugu text from cropped article images using Claude Vision API.
Run after a job completes to get accurate text extraction.
Usage:
python claude_ocr.py # processes latest job
python claude_ocr.py 20260526_002006 # processes specific job
"""
import os
import sys
import json
import base64
import anthropic
from pathlib import Path
OUTPUT_DIR = Path(__file__).parent / "output"
def read_article_image(client, img_path, model="claude-sonnet-4-6"):
"""Send article image to Claude and get Telugu text back."""
with open(img_path, "rb") as f:
img_data = base64.standard_b64encode(f.read()).decode("utf-8")
response = client.messages.create(
model=model,
max_tokens=4096,
messages=[{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": img_data,
},
},
{
"type": "text",
"text": (
"Read all Telugu text from this newspaper article image. "
"Output ONLY the Telugu text in correct reading order "
"(top to bottom, reading each column fully before moving to the next). "
"Include headlines first, then subheadlines, then body text. "
"Do not translate. Do not add any explanation. Just the Telugu text."
),
},
],
}],
)
return response.content[0].text
def process_job(job_id=None):
api_key = os.environ.get("ANTHROPIC_API_KEY")
if not api_key:
print("Error: Set ANTHROPIC_API_KEY environment variable")
sys.exit(1)
# Find job directory
if job_id:
job_dir = OUTPUT_DIR / job_id
else:
# Use latest job
jobs = sorted(OUTPUT_DIR.iterdir(), reverse=True)
if not jobs:
print("No jobs found")
sys.exit(1)
job_dir = jobs[0]
job_id = job_dir.name
articles_dir = job_dir / "articles"
if not articles_dir.exists():
print(f"No articles found in {job_dir}")
sys.exit(1)
client = anthropic.Anthropic(api_key=api_key)
article_dirs = sorted(articles_dir.iterdir())
print(f"Job: {job_id}")
print(f"Articles: {len(article_dirs)}")
print("-" * 50)
all_text = []
for art_dir in article_dirs:
img_path = art_dir / "article.png"
if not img_path.exists():
continue
name = art_dir.name
print(f"Reading {name}...", end=" ", flush=True)
try:
text = read_article_image(client, img_path)
# Save to article.txt (overwrites old OCR output)
(art_dir / "article.txt").write_text(text, encoding="utf-8")
print(f"OK ({len(text)} chars)")
all_text.append(f"--- {name} ---\n{text}\n")
# Update info.json headline preview
info_path = art_dir / "info.json"
if info_path.exists():
info = json.loads(info_path.read_text())
first_line = next((ln.strip() for ln in text.splitlines() if ln.strip()), "")
info["headline_preview"] = first_line[:120]
info["ocr_method"] = "claude_vision"
info_path.write_text(json.dumps(info, indent=2, ensure_ascii=False))
except Exception as e:
print(f"FAILED: {e}")
all_text.append(f"--- {name} ---\n[Error: {e}]\n")
# Save combined file
combined_path = job_dir / "all_articles_claude.txt"
combined_path.write_text("\n".join(all_text), encoding="utf-8")
print("-" * 50)
print(f"Done! Combined text saved to: {combined_path}")
if __name__ == "__main__":
job_id = sys.argv[1] if len(sys.argv) > 1 else None
process_job(job_id)