Spaces:
Runtime error
Runtime error
import cv2 | |
import numpy as np | |
import pytesseract | |
import requests | |
import pandas as pd | |
import gradio as gr | |
from io import BytesIO | |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
# 1. Utility: Detect rectangular contours (approximate book covers) | |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
def detect_book_regions(image: np.ndarray, min_area=10000, eps_coef=0.02): | |
""" | |
Detect rectangular regions in an image that likely correspond to book covers. | |
Returns a list of bounding boxes: (x, y, w, h). | |
""" | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
blurred = cv2.GaussianBlur(gray, (5, 5), 0) | |
edges = cv2.Canny(blurred, 50, 150) | |
# Dilate + erode to close gaps | |
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5)) | |
closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel) | |
contours, _ = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
boxes = [] | |
for cnt in contours: | |
area = cv2.contourArea(cnt) | |
if area < min_area: | |
continue | |
peri = cv2.arcLength(cnt, True) | |
approx = cv2.approxPolyDP(cnt, eps_coef * peri, True) | |
# Keep only quadrilaterals | |
if len(approx) == 4: | |
x, y, w, h = cv2.boundingRect(approx) | |
ar = w / float(h) | |
# Filter by typical book-cover aspect ratios | |
if 0.4 < ar < 0.9 or 1.0 < ar < 1.6: | |
boxes.append((x, y, w, h)) | |
# Sort leftβright, topβbottom | |
boxes = sorted(boxes, key=lambda b: (b[1], b[0])) | |
return boxes | |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
# 2. OCR on a cropped region | |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
def ocr_on_region(image: np.ndarray, box: tuple): | |
""" | |
Crop the image to the given box and run Tesseract OCR. | |
Return the raw OCR text. | |
""" | |
x, y, w, h = box | |
cropped = image[y:y+h, x:x+w] | |
gray_crop = cv2.cvtColor(cropped, cv2.COLOR_BGR2GRAY) | |
_, thresh_crop = cv2.threshold(gray_crop, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) | |
custom_config = r'--oem 3 --psm 6' | |
text = pytesseract.image_to_string(thresh_crop, config=custom_config) | |
return text.strip() | |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
# 3. Query OpenLibrary API | |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
def query_openlibrary(title_text: str, author_text: str = None): | |
""" | |
Search OpenLibrary by title (and optional author). | |
Return a dict with title, author_name, publisher, first_publish_year, or None. | |
""" | |
base_url = "https://openlibrary.org/search.json" | |
params = {"title": title_text} | |
if author_text: | |
params["author"] = author_text | |
try: | |
resp = requests.get(base_url, params=params, timeout=5) |