Spaces:
Running
Running
| import easyocr | |
| import easyocr.easyocr | |
| import sys | |
| import json | |
| import os | |
| from PIL import Image | |
| import io | |
| # Fix EasyOCR bug | |
| easyocr.easyocr.corrupt_msg = "Model error" | |
| def process_image(reader, image_path): | |
| try: | |
| if not os.path.exists(image_path): | |
| return {"error": f"Path not found: {image_path}"} | |
| # Single-pass OCR is much faster and usually accurate enough for Deep Learning models | |
| results = reader.readtext(image_path, detail=1) | |
| if not results: | |
| return {"error": "No text detected"} | |
| symbols = [] | |
| for (bbox, text, prob) in results: | |
| text = text.strip().upper() | |
| clean_text = "".join([c for c in text if c.isalpha()]) | |
| if not clean_text: continue | |
| x_center = (bbox[0][0] + bbox[2][0]) / 2 | |
| y_center = (bbox[0][1] + bbox[2][1]) / 2 | |
| if len(clean_text) > 1: | |
| # Approximate positions for concatenated chars | |
| w = bbox[2][0] - bbox[0][0] | |
| char_w = w / len(clean_text) | |
| for i, char in enumerate(clean_text): | |
| symbols.append({ | |
| "text": char, | |
| "x": bbox[0][0] + (i + 0.5) * char_w, | |
| "y": y_center | |
| }) | |
| else: | |
| symbols.append({"text": clean_text, "x": x_center, "y": y_center}) | |
| if not symbols: return [] | |
| # Find 8 distinct lanes for X and Y | |
| def get_lanes(coords, num_lanes=8): | |
| coords.sort() | |
| if not coords: return [] | |
| lanes = [] | |
| # Simple clustering: divide range into 8 buckets | |
| mi, ma = min(coords), max(coords) | |
| if ma == mi: return [mi] | |
| bucket_size = (ma - mi) / (num_lanes - 1) if num_lanes > 1 else 1 | |
| for i in range(num_lanes): | |
| center = mi + i * bucket_size | |
| lanes.append(center) | |
| return lanes | |
| xs = [s["x"] for s in symbols] | |
| ys = [s["y"] for s in symbols] | |
| x_lanes = get_lanes(xs, 8) | |
| y_lanes = get_lanes(ys, 8) | |
| grid = [[" " for _ in range(8)] for _ in range(8)] | |
| for s in symbols: | |
| # Map to nearest lane | |
| r = min(range(8), key=lambda i: abs(s["y"] - y_lanes[i])) | |
| c = min(range(8), key=lambda i: abs(s["x"] - x_lanes[i])) | |
| grid[r][c] = s["text"] | |
| final_symbols = [] | |
| for r in range(8): | |
| for c in range(8): | |
| if grid[r][c] != " ": | |
| final_symbols.append({"text": grid[r][c], "r": r, "c": c}) | |
| return final_symbols | |
| except Exception as e: | |
| return {"error": str(e)} | |
| def main(): | |
| # Initialize reader ONCE | |
| try: | |
| reader = easyocr.Reader(['en'], gpu=False, verbose=False) | |
| # Signal ready | |
| print("READY", flush=True) | |
| except Exception as e: | |
| print(json.dumps({"error": f"Init failed: {str(e)}"}), flush=True) | |
| return | |
| # Listen for image paths on stdin | |
| while True: | |
| line = sys.stdin.readline() | |
| if not line: | |
| break | |
| image_path = line.strip() | |
| if not image_path: | |
| continue | |
| result = process_image(reader, image_path) | |
| print(json.dumps(result), flush=True) | |
| if __name__ == "__main__": | |
| main() | |