doocus first ver
This commit is contained in:
79
doocus/extractors/image.py
Normal file
79
doocus/extractors/image.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Image-family extractor: jpg/jpeg, png.
|
||||
|
||||
Reads EXIF (including capture date) into metadata, writes a downscaled JPEG
|
||||
thumbnail, and — when options['ocr'] is set and pytesseract is available — OCRs
|
||||
the image into content. Without OCR, content is a short placeholder note.
|
||||
"""
|
||||
import io
|
||||
from pathlib import Path
|
||||
|
||||
from .base import ExtractionResult
|
||||
|
||||
EXTRACTOR_ID = "image/pillow@1"
|
||||
THUMB_MAX = 640 # longest edge, px
|
||||
|
||||
|
||||
def _exif(img) -> dict:
|
||||
try:
|
||||
from PIL.ExifTags import TAGS
|
||||
except ImportError:
|
||||
return {}
|
||||
raw = getattr(img, "_getexif", lambda: None)()
|
||||
if not raw:
|
||||
return {}
|
||||
out = {}
|
||||
for tag_id, value in raw.items():
|
||||
name = TAGS.get(tag_id, str(tag_id))
|
||||
if name in ("DateTime", "DateTimeOriginal", "Make", "Model", "Orientation"):
|
||||
out[name] = str(value)
|
||||
return out
|
||||
|
||||
|
||||
def _thumbnail(img) -> bytes:
|
||||
from PIL import Image # noqa: F401 (ensure Pillow present)
|
||||
im = img.copy()
|
||||
im.thumbnail((THUMB_MAX, THUMB_MAX))
|
||||
if im.mode not in ("RGB", "L"):
|
||||
im = im.convert("RGB")
|
||||
buf = io.BytesIO()
|
||||
im.save(buf, format="JPEG", quality=80)
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def extract(source: Path, options: dict) -> ExtractionResult:
|
||||
from PIL import Image # lazy
|
||||
|
||||
warnings = []
|
||||
metadata = {}
|
||||
content = ""
|
||||
thumb = None
|
||||
|
||||
with Image.open(source) as img:
|
||||
metadata["width"], metadata["height"] = img.size
|
||||
metadata["mode"] = img.mode
|
||||
exif = _exif(img)
|
||||
if exif:
|
||||
metadata["exif"] = exif
|
||||
if "DateTimeOriginal" in exif or "DateTime" in exif:
|
||||
metadata["captured"] = exif.get("DateTimeOriginal", exif.get("DateTime"))
|
||||
try:
|
||||
thumb = _thumbnail(img)
|
||||
except Exception as e:
|
||||
warnings.append(f"thumbnail failed: {e}")
|
||||
|
||||
if options.get("ocr"):
|
||||
try:
|
||||
import pytesseract
|
||||
content = pytesseract.image_to_string(img).strip()
|
||||
metadata["ocr"] = True
|
||||
except ImportError:
|
||||
warnings.append("--ocr requested but pytesseract not installed")
|
||||
except Exception as e:
|
||||
warnings.append(f"ocr failed: {e}")
|
||||
|
||||
if not content:
|
||||
content = f"\n\n_Image {metadata.get('width')}×{metadata.get('height')}. Run with --ocr to extract any text._\n"
|
||||
|
||||
return ExtractionResult(content=content, metadata=metadata, thumb=thumb,
|
||||
warnings=warnings, extractor=EXTRACTOR_ID)
|
||||
Reference in New Issue
Block a user