chiruu12
fix: poppler path for HF Spaces
8c5e47d
Raw
History Blame Contribute Delete
4.1 kB
import torch
import cv2
import numpy as np
import os
from pdf2image import convert_from_path
import gradio as gr
from src.crnn_model import CRNN
DEVICE = torch.device("cpu")
MODEL_PATH = "src/models/crnn_final/crnn_real_data_model.pth"
CHAR_LIST_PATH = "char_list.txt"
IMAGE_HEIGHT = 32
POPPLER_PATH = "/usr/bin"
def decode_ctc_output(preds, int_to_char_):
texts = []
preds_idx = preds.argmax(2).cpu().numpy()
for pred_sequence in preds_idx:
decoded_sequence, last_char_idx = [], 0
for char_idx in pred_sequence:
if char_idx != last_char_idx:
if char_idx != 0: decoded_sequence.append(char_idx)
last_char_idx = char_idx
texts.append("".join([int_to_char_.get(c, '') for c in decoded_sequence]))
return texts
def find_text_lines(image_data):
gray = cv2.cvtColor(image_data, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
kernel = np.ones((1, 40), np.uint8)
connected = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
contours, _ = cv2.findContours(connected, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
line_images = []
bounding_boxes = [cv2.boundingRect(c) for c in contours]
contours = [c for _, c in sorted(zip(bounding_boxes, contours), key=lambda b: b[0][1])]
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
if w > 15 and h > 8:
line_images.append(binary[max(0, y - 2):y + h + 2, max(0, x - 2):x + w + 2])
return line_images
def preprocess_line_for_model(line_image):
inverted_image = cv2.bitwise_not(line_image)
h, w = inverted_image.shape
scale_factor = IMAGE_HEIGHT / h
new_w = int(w * scale_factor)
resized_image = cv2.resize(inverted_image, (new_w, IMAGE_HEIGHT), interpolation=cv2.INTER_AREA)
normalized_image = (resized_image / 255.0).astype(np.float32)
tensor = torch.from_numpy(normalized_image).unsqueeze(0).unsqueeze(0)
return tensor.to(DEVICE)
print("Loading CRNN model and character set...")
with open(CHAR_LIST_PATH, 'r', encoding='utf-8') as f:
char_list = f.read().split('<SEP>')
int_to_char = {i + 1: char for i, char in enumerate(char_list)}
model = CRNN(num_chars=len(char_list)).to(DEVICE)
model.load_state_dict(torch.load(MODEL_PATH, map_location=DEVICE))
model.eval()
print("Model loaded successfully.")
def ocr_process(pdf_file, page_number):
if pdf_file is None:
return "Please upload a PDF file."
page_number = int(page_number)
print(f"Processing PDF '{pdf_file.name}', page {page_number}...")
try:
pil_images = convert_from_path(
pdf_file.name,
first_page=page_number,
last_page=page_number,
poppler_path=POPPLER_PATH
)
if not pil_images:
return f"Error: Could not extract page {page_number}."
image_data = cv2.cvtColor(np.array(pil_images[0]), cv2.COLOR_RGB2BGR)
except Exception as e:
return f"PDF processing failed.\nError: {e}"
line_crops = find_text_lines(image_data)
if not line_crops:
return "No text lines were detected on the page."
full_text = []
with torch.no_grad():
for line_image in line_crops:
line_tensor = preprocess_line_for_model(line_image)
preds = model(line_tensor)
decoded_text = decode_ctc_output(preds, int_to_char)
full_text.append(decoded_text[0])
print("Recognition complete.")
return "\n".join(full_text)
iface = gr.Interface(
fn=ocr_process,
inputs=[gr.File(label="Upload PDF"), gr.Number(label="Page Number", value=1, precision=0)],
outputs=gr.Textbox(label="Recognized Text", lines=20, placeholder="The recognized text will appear here..."),
title="Custom Book OCR Engine",
description="A CRNN model built and trained from scratch to perform OCR on scanned book pages. This demo showcases the final result of the Digital Image Processing project.",
allow_flagging="never"
)
if __name__ == "__main__":
iface.launch()