opencv/text_recognition_crnn
1
1import cv2 as cv2import numpy as np3import gradio as gr4from huggingface_hub import hf_hub_download5from ppocr_det import PPOCRDet6from crnn import CRNN7 8# Download model files from Hugging Face9det_model_path = hf_hub_download(10 repo_id="opencv/text_detection_ppocr",11 filename="text_detection_en_ppocrv3_2023may.onnx"12)13rec_model_path = hf_hub_download(14 repo_id="opencv/text_recognition_crnn",15 filename="text_recognition_CRNN_EN_2021sep.onnx"16)17 18# DNN backend and target19backend_id = cv.dnn.DNN_BACKEND_OPENCV20target_id = cv.dnn.DNN_TARGET_CPU21 22# Detector and recognizer setup23detector = PPOCRDet(24 modelPath=det_model_path,25 inputSize=[736, 736],26 binaryThreshold=0.3,27 polygonThreshold=0.5,28 maxCandidates=200,29 unclipRatio=2.0,30 backendId=backend_id,31 targetId=target_id32)33 34recognizer = CRNN(35 modelPath=rec_model_path,36 backendId=backend_id,37 targetId=target_id38)39 40def detect_and_recognize(input_image):41 bgr = cv.cvtColor(input_image, cv.COLOR_RGB2BGR)42 h_orig, w_orig = input_image.shape[:2]43 resized = cv.resize(bgr, (736, 736))44 scale_w = w_orig / 73645 scale_h = h_orig / 73646 47 # Detect & recognize48 det_results, _ = detector.infer(resized)49 texts = [recognizer.infer(resized, box.reshape(8)) for box in det_results]50 51 # Prepare canvases52 left = input_image.copy()53 right = np.ones_like(input_image) * 25554 55 for box_raw, text in zip(det_results, texts):56 # Rescale box to original image coords57 box = np.int32([[pt[0] * scale_w, pt[1] * scale_h] for pt in box_raw])58 59 # Compute box dimensions60 xs = box[:, 0]61 box_w = xs.max() - xs.min()62 # box height (average vertical edges)63 h1 = np.linalg.norm(box[1] - box[0])64 h2 = np.linalg.norm(box[2] - box[3])65 box_h = (h1 + h2) / 2.066 67 # Initial font scale so text height ≈ 80% of box height68 (_, th0), _ = cv.getTextSize(text, cv.FONT_HERSHEY_SIMPLEX, 1.0, 1)69 font_scale = (box_h * 0.8) / th0 if th0 > 0 else 1.070 font_thickness = max(1, int(font_scale))71 72 # Re-measure text size with this scale73 (tw, th), _ = cv.getTextSize(text, cv.FONT_HERSHEY_SIMPLEX, font_scale, font_thickness)74 75 # If text is wider than box or taller than box, scale down to fit76 scale_x = box_w / tw if tw > 0 else 1.077 scale_y = (box_h * 0.8) / th if th > 0 else 1.078 final_scale = font_scale * min(1.0, scale_x, scale_y)79 font_scale = final_scale80 font_thickness = max(1, int(np.floor(font_scale)))81 82 # Draw boxes on both panels83 cv.polylines(left, [box], isClosed=True, color=(0, 0, 255), thickness=2)84 cv.polylines(right, [box], isClosed=True, color=(0, 0, 255), thickness=2)85 86 # Draw text on whiteboard, just above top-left corner87 x0, y0 = box[0]88 y_text = max(0, int(y0 - 5))89 cv.putText(90 right, text, (int(x0), y_text),91 cv.FONT_HERSHEY_SIMPLEX,92 font_scale, (0, 0, 0), font_thickness93 )94 95 combined = cv.hconcat([left, right])96 return combined97 98with gr.Blocks(css='''.example * {99 font-style: italic;100 font-size: 18px !important;101 color: #0ea5e9 !important;102 }''') as demo:103 104 gr.Markdown("## Scene Text Detection and Recognition (PPOCR + CRNN)")105 gr.Markdown("Upload an image with scene text to detect text regions and recognize text using OpenCV DNN with PPOCR + CRNN models.")106 107 input_img = gr.Image(type="numpy", label="Upload Image")108 output_img = gr.Image(type="numpy", label="Detected Text Image")109 110 input_img.change(fn=lambda: (None), outputs=output_img)111 112 with gr.Row():113 submit_btn = gr.Button("Submit", variant="primary")114 clear_btn = gr.Button("Clear")115 116 submit_btn.click(117 fn=detect_and_recognize,118 inputs=input_img,119 outputs=output_img120 )121 122 clear_btn.click(123 fn=lambda: (None, None),124 inputs=[],125 outputs=[input_img, output_img]126 )127 128 gr.Markdown("Click on any example to try it.", elem_classes=["example"])129 130 gr.Examples(131 examples=[132 ["examples/text_det_test2.jpg"],133 ["examples/right.jpg"]134 ],135 inputs=input_img136 )137 138 gr.Markdown("**Note**: Left side of output shows detected regions, right side shows recognized text.")139 140if __name__ == "__main__":141 demo.launch()142 