125 lines
4.0 KiB
Python
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)
|