Spaces:
Running on Zero
Running on Zero
| """PP-OCRv6 medium recognition model demo. | |
| Upload a cropped text-line image and get recognized text with a confidence score. | |
| PP-OCRv6_medium_rec is a 19M-parameter text recognition model from PaddlePaddle. | |
| """ | |
| import json | |
| import os | |
| import tempfile | |
| # On ZeroGPU, `import spaces` must come before any torch/CUDA import. | |
| # PaddlePaddle doesn't use torch, but the ZeroGPU runtime still requires | |
| # this import and a @spaces.GPU decorator on the inference function. | |
| import spaces | |
| import gradio as gr | |
| from paddleocr import TextRecognition | |
| # Load model at module scope — PaddlePaddle runs on CPU natively. | |
| MODEL_NAME = "PP-OCRv6_medium_rec" | |
| rec_model = TextRecognition(model_name=MODEL_NAME) | |
| def recognize_text(image): | |
| """Recognize text from a cropped text-line image. | |
| Args: | |
| image: A file path to an image containing a single line of text. | |
| Returns: | |
| A tuple of (recognized_text, confidence_score, raw_json). | |
| """ | |
| if image is None: | |
| return "No image provided", 0.0, "" | |
| img_path = image if isinstance(image, str) else None | |
| if img_path is None: | |
| with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: | |
| image.save(f, format="PNG") | |
| img_path = f.name | |
| try: | |
| output = rec_model.predict(input=img_path, batch_size=1) | |
| results = [] | |
| for res in output: | |
| raw_json = getattr(res, "json", None) | |
| if isinstance(raw_json, dict): | |
| data = raw_json | |
| elif isinstance(raw_json, str): | |
| data = json.loads(raw_json) | |
| elif hasattr(res, "to_dict"): | |
| data = res.to_dict() | |
| else: | |
| data = { | |
| "rec_text": getattr(res, "rec_text", str(res)), | |
| "rec_score": getattr(res, "rec_score", 0.0), | |
| } | |
| results.append(data) | |
| if not results: | |
| return "No text recognized", 0.0, "" | |
| # Results may be nested under a "res" key | |
| first = results[0] | |
| if "res" in first: | |
| first = first["res"] | |
| text = first.get("rec_text", "") | |
| score = first.get("rec_score", 0.0) | |
| raw = json.dumps(results, ensure_ascii=False, indent=2) | |
| return text, float(score), raw | |
| except Exception as e: | |
| return f"Error: {e}", 0.0, "" | |
| finally: | |
| if not isinstance(image, str) and os.path.exists(img_path): | |
| os.unlink(img_path) | |
| # --- UI --- | |
| CSS = """ | |
| #col-container { max-width: 900px; margin: 0 auto; } | |
| .dark .gradio-container { color: var(--body-text-color); } | |
| """ | |
| with gr.Blocks() as demo: | |
| with gr.Column(elem_id="col-container"): | |
| gr.Markdown("# PP-OCRv6 Medium Text Recognition") | |
| gr.Markdown( | |
| "Upload a cropped image of a single text line to recognize text. " | |
| "This is the [PP-OCRv6_medium_rec](https://huggingface.co/PaddlePaddle/PP-OCRv6_medium_rec) " | |
| "model — a 19M-parameter OCR recognition model from PaddlePaddle that " | |
| "supports 50 languages." | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| input_image = gr.Image( | |
| label="Text-line image", | |
| type="filepath", | |
| height=200, | |
| ) | |
| run_btn = gr.Button("Recognize Text", variant="primary") | |
| with gr.Column(scale=1): | |
| text_output = gr.Textbox(label="Recognized text", interactive=False) | |
| score_output = gr.Number(label="Confidence score", precision=4, interactive=False) | |
| raw_output = gr.Code(label="Raw JSON output", language="json") | |
| gr.Examples( | |
| examples=[ | |
| ["examples/sample_readme.jpeg"], | |
| ["examples/sample_text.png"], | |
| ["examples/sample_text2.png"], | |
| ["examples/sample_text3.png"], | |
| ["examples/sample_text4.png"], | |
| ], | |
| inputs=input_image, | |
| outputs=[text_output, score_output, raw_output], | |
| fn=recognize_text, | |
| cache_examples=True, | |
| cache_mode="lazy", | |
| ) | |
| run_btn.click( | |
| fn=recognize_text, | |
| inputs=input_image, | |
| outputs=[text_output, score_output, raw_output], | |
| api_name="recognize", | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS) |