""" eDOCr2 - Engineering Drawing OCR Gradio Interface for Hugging Face Spaces Extract dimensions, tables, and GD&T symbols from engineering drawings """ import gradio as gr import cv2 import numpy as np import json import os import time from pathlib import Path import zipfile import tempfile from PIL import Image # Import eDOCr2 modules from edocr2 import tools from edocr2.keras_ocr.recognition import Recognizer from edocr2.keras_ocr.detection import Detector from pdf2image import convert_from_path # Global variables for models recognizer_gdt = None recognizer_dim = None detector = None alphabet_dim = None models_loaded = False def load_models(): """Load OCR models at startup""" global recognizer_gdt, recognizer_dim, detector, alphabet_dim, models_loaded if models_loaded: return True try: print("๐ง Loading OCR models...") start_time = time.time() # Model paths gdt_model = 'edocr2/models/recognizer_gdts.keras' dim_model = 'edocr2/models/recognizer_dimensions_2.keras' if not os.path.exists(gdt_model) or not os.path.exists(dim_model): print("โ Model files not found!") return False # Load GD&T recognizer recognizer_gdt = Recognizer(alphabet=tools.ocr_pipelines.read_alphabet(gdt_model)) recognizer_gdt.model.load_weights(gdt_model) # Load dimension recognizer alphabet_dim = tools.ocr_pipelines.read_alphabet(dim_model) recognizer_dim = Recognizer(alphabet=alphabet_dim) recognizer_dim.model.load_weights(dim_model) # Load detector detector = Detector() # Warm up models dummy_image = np.zeros((1, 1, 3), dtype=np.float32) _ = recognizer_gdt.recognize(dummy_image) _ = recognizer_dim.recognize(dummy_image) dummy_image = np.zeros((32, 32, 3), dtype=np.float32) _ = detector.detect([dummy_image]) end_time = time.time() print(f"โ Models loaded in {end_time - start_time:.2f} seconds") models_loaded = True return True except Exception as e: print(f"โ Error loading models: {e}") import traceback traceback.print_exc() return False def process_drawing(image_file): """ Process an engineering drawing and extract information Args: image_file: Uploaded image file (PIL Image or file path) Returns: tuple: (annotated_image, json_data, csv_file, zip_file, stats_html) """ if not models_loaded: return None, "โ Models not loaded. Please check the logs.", None, None, "Error: Models not loaded" try: start_time = time.time() # Read image if isinstance(image_file, str): # File path if image_file.lower().endswith('.pdf'): img = convert_from_path(image_file) img = np.array(img[0]) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY) img = cv2.merge([img, img, img]) else: img = cv2.imread(image_file) else: # PIL Image img = np.array(image_file) if len(img.shape) == 2: # Grayscale img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) elif img.shape[2] == 4: # RGBA img = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR) else: # RGB img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) if img is None: return None, "โ Could not read image file", None, None, "Error: Invalid image" # Create temporary output directory with tempfile.TemporaryDirectory() as temp_dir: output_dir = temp_dir # Segmentation print("๐ Segmenting layers...") img_boxes, frame, gdt_boxes, tables, dim_boxes = tools.layer_segm.segment_img( img, autoframe=True, frame_thres=0.7, GDT_thres=0.02, binary_thres=127 ) # OCR Tables print("๐ Processing tables...") process_img = img.copy() table_results, updated_tables, process_img = tools.ocr_pipelines.ocr_tables( tables, process_img, language='eng' ) # OCR GD&T print("๐ฏ Processing GD&T symbols...") gdt_results, updated_gdt_boxes, process_img = tools.ocr_pipelines.ocr_gdt( process_img, gdt_boxes, recognizer_gdt ) # OCR Dimensions print("๐ Processing dimensions...") if frame: process_img = process_img[frame.y : frame.y + frame.h, frame.x : frame.x + frame.w] dimensions, other_info, process_img, dim_tess = tools.ocr_pipelines.ocr_dimensions( process_img, detector, recognizer_dim, alphabet_dim, frame, dim_boxes, cluster_thres=20, max_img_size=1048, language='eng', backg_save=False ) # Generate mask image print("๐จ Generating visualization...") mask_img = tools.output_tools.mask_img( img, updated_gdt_boxes, updated_tables, dimensions, frame, other_info ) # Convert to RGB for display mask_img_rgb = cv2.cvtColor(mask_img, cv2.COLOR_BGR2RGB) # Process and save results print("๐พ Saving results...") table_results, gdt_results, dimensions, other_info = tools.output_tools.process_raw_output( output_dir, table_results, gdt_results, dimensions, other_info, save=True ) # Prepare JSON data json_data = { 'tables': table_results, 'gdts': gdt_results, 'dimensions': dimensions, 'other_info': other_info } json_str = json.dumps(json_data, indent=2) # Save JSON file json_path = os.path.join(output_dir, 'results.json') with open(json_path, 'w') as f: f.write(json_str) # Create CSV file (if exists) csv_files = list(Path(output_dir).glob('*.csv')) csv_path = csv_files[0] if csv_files else None # Create ZIP file with all results zip_path = os.path.join(output_dir, 'edocr2_results.zip') with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: # Add mask image mask_img_path = os.path.join(output_dir, 'annotated_drawing.png') cv2.imwrite(mask_img_path, mask_img) zipf.write(mask_img_path, 'annotated_drawing.png') # Add JSON zipf.write(json_path, 'results.json') # Add CSV if exists if csv_path: zipf.write(csv_path, os.path.basename(csv_path)) # Copy ZIP to a permanent location permanent_zip = os.path.join(tempfile.gettempdir(), f'edocr2_results_{int(time.time())}.zip') import shutil shutil.copy(zip_path, permanent_zip) end_time = time.time() processing_time = round(end_time - start_time, 2) # Create statistics HTML stats_html = f"""