Spaces:
Sleeping
Sleeping
Download app.py from shrudhit/RVP-Label-Extractor: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/spaces/shrudhit/RVP-Label-Extractor/resolve/main/app.py
- Command line
-
hf download hf://spaces/shrudhit/RVP-Label-Extractor/app.py
-
curl -L -o app.py https://huggingface.co/spaces/shrudhit/RVP-Label-Extractor/resolve/main/app.py
10.3 kB
| import os | |
| import re | |
| import cv2 | |
| import pandas as pd | |
| import gradio as gr | |
| import pytesseract | |
| import spaces | |
| from PIL import Image | |
| # ========================================================= | |
| # ZERO GPU STARTUP CHECK | |
| # ========================================================= | |
| def zerogpu_probe(): | |
| return "OK" | |
| # ========================================================= | |
| # OCR CORRECTION | |
| # ========================================================= | |
| def correct_ocr_mistakes(text): | |
| if not text: | |
| return "" | |
| text = text.upper() | |
| replacements = { | |
| "O": "0", | |
| "I": "1", | |
| "L": "1", | |
| "S": "5", | |
| "B": "8", | |
| "G": "6", | |
| "Z": "2" | |
| } | |
| for k, v in replacements.items(): | |
| text = text.replace(k, v) | |
| return text | |
| # ========================================================= | |
| # EXTRACT LAST 3 DIGITS | |
| # ========================================================= | |
| def extract_last_3_digits(val): | |
| if not val: | |
| return "" | |
| val = correct_ocr_mistakes(val) | |
| digits = re.findall(r"\d", val) | |
| if len(digits) >= 3: | |
| return "".join(digits[-3:]) | |
| return "" | |
| # ========================================================= | |
| # PARSE OCR TEXT | |
| # ========================================================= | |
| def parse_ocr_text(text): | |
| awb_val = "" | |
| packet_val = "" | |
| ship_to_val = "" | |
| if not text: | |
| return "", "", "" | |
| lines = [ | |
| line.strip() | |
| for line in text.split("\n") | |
| if line.strip() | |
| ] | |
| for i, line in enumerate(lines): | |
| # ------------------------------------------------- | |
| # AWB | |
| # ------------------------------------------------- | |
| if ( | |
| re.search(r"AW[B8]\s*[:\-]", line, re.IGNORECASE) | |
| or "AWB" in line.upper() | |
| ): | |
| m = re.search( | |
| r"AW[B8]\s*[:\-]?\s*([A-Z0-9_]+)", | |
| line, | |
| re.IGNORECASE | |
| ) | |
| if m and len(m.group(1)) > 4: | |
| awb_val = m.group(1) | |
| elif i + 1 < len(lines): | |
| awb_val = lines[i + 1] | |
| # ------------------------------------------------- | |
| # PACKET ID | |
| # ------------------------------------------------- | |
| if re.search( | |
| r"Packet\s*[I1][D0]", | |
| line, | |
| re.IGNORECASE | |
| ): | |
| m = re.search( | |
| r"Packet\s*[I1][D0]\s*[:\-]?\s*([A-Z0-9_]+)", | |
| line, | |
| re.IGNORECASE | |
| ) | |
| if m and len(m.group(1)) > 4: | |
| packet_val = m.group(1) | |
| elif i + 1 < len(lines): | |
| packet_val = lines[i + 1] | |
| # ------------------------------------------------- | |
| # SHIP TO | |
| # ------------------------------------------------- | |
| if re.search( | |
| r"Ship\s*T[o0]", | |
| line, | |
| re.IGNORECASE | |
| ): | |
| m = re.search( | |
| r"Ship\s*T[o0]\s*[:\-]?\s*(.+)", | |
| line, | |
| re.IGNORECASE | |
| ) | |
| if m and m.group(1).strip(): | |
| ship_to_val = m.group(1).strip() | |
| elif i + 1 < len(lines): | |
| ship_to_val = lines[i + 1].strip() | |
| return ( | |
| extract_last_3_digits(awb_val), | |
| extract_last_3_digits(packet_val), | |
| ship_to_val | |
| ) | |
| # ========================================================= | |
| # IMAGE PREPROCESSING | |
| # ========================================================= | |
| def preprocess_image(image_path): | |
| img = cv2.imread(image_path) | |
| if img is None: | |
| return [] | |
| # Grayscale | |
| gray = cv2.cvtColor( | |
| img, | |
| cv2.COLOR_BGR2GRAY | |
| ) | |
| # Resize | |
| gray = cv2.resize( | |
| gray, | |
| None, | |
| fx=2, | |
| fy=2, | |
| interpolation=cv2.INTER_CUBIC | |
| ) | |
| # Denoise | |
| denoised = cv2.fastNlMeansDenoising( | |
| gray, | |
| None, | |
| h=10, | |
| templateWindowSize=7, | |
| searchWindowSize=21 | |
| ) | |
| # Adaptive threshold | |
| adaptive_thresh = cv2.adaptiveThreshold( | |
| denoised, | |
| 255, | |
| cv2.ADAPTIVE_THRESH_GAUSSIAN_C, | |
| cv2.THRESH_BINARY, | |
| 31, | |
| 2 | |
| ) | |
| return [ | |
| Image.fromarray(adaptive_thresh), | |
| Image.fromarray(gray) | |
| ] | |
| # ========================================================= | |
| # PROCESS IMAGES | |
| # ========================================================= | |
| def process_images(files, progress=gr.Progress()): | |
| if not files: | |
| return ( | |
| pd.DataFrame( | |
| columns=[ | |
| "AWB", | |
| "Packet ID", | |
| "Ship To" | |
| ] | |
| ), | |
| None | |
| ) | |
| results = [] | |
| # OCR configurations | |
| ocr_configs = [ | |
| "--psm 6", | |
| "--psm 11" | |
| ] | |
| total_files = len(files) | |
| # ===================================================== | |
| # LOOP THROUGH FILES | |
| # ===================================================== | |
| for index, file_obj in enumerate(files): | |
| progress( | |
| (index + 1) / total_files, | |
| desc=f"Processing {index + 1}/{total_files}" | |
| ) | |
| # ------------------------------------------------- | |
| # GET FILE PATH | |
| # ------------------------------------------------- | |
| if isinstance(file_obj, str): | |
| filepath = file_obj | |
| elif hasattr(file_obj, "name"): | |
| filepath = file_obj.name | |
| else: | |
| filepath = str(file_obj) | |
| # ------------------------------------------------- | |
| # PREPROCESS IMAGE | |
| # ------------------------------------------------- | |
| preprocessed_imgs = preprocess_image( | |
| filepath | |
| ) | |
| final_awb = "" | |
| final_packet = "" | |
| final_ship = "" | |
| found_all = False | |
| # ================================================= | |
| # OCR | |
| # ================================================= | |
| for img in preprocessed_imgs: | |
| if found_all: | |
| break | |
| for config in ocr_configs: | |
| try: | |
| text = pytesseract.image_to_string( | |
| img, | |
| config=config | |
| ) | |
| except Exception: | |
| continue | |
| # Parse OCR | |
| awb, packet, ship = parse_ocr_text( | |
| text | |
| ) | |
| # Save AWB | |
| if not final_awb and awb: | |
| final_awb = awb | |
| # Save Packet ID | |
| if not final_packet and packet: | |
| final_packet = packet | |
| # Save Ship To | |
| if not final_ship and ship: | |
| final_ship = ship | |
| # Stop if everything found | |
| if ( | |
| final_awb | |
| and final_packet | |
| and final_ship | |
| ): | |
| found_all = True | |
| break | |
| # ================================================= | |
| # ADD RESULT | |
| # ================================================= | |
| results.append({ | |
| "AWB": final_awb, | |
| "Packet ID": final_packet, | |
| "Ship To": final_ship | |
| }) | |
| # ===================================================== | |
| # CREATE DATAFRAME | |
| # ===================================================== | |
| df = pd.DataFrame( | |
| results, | |
| columns=[ | |
| "AWB", | |
| "Packet ID", | |
| "Ship To" | |
| ] | |
| ) | |
| # ===================================================== | |
| # CREATE EXCEL | |
| # ===================================================== | |
| output_excel = os.path.abspath( | |
| "RVP_Data.xlsx" | |
| ) | |
| df.to_excel( | |
| output_excel, | |
| index=False | |
| ) | |
| return df, output_excel | |
| # ========================================================= | |
| # GRADIO INTERFACE | |
| # ========================================================= | |
| with gr.Blocks( | |
| title="RVP Label OCR Extractor" | |
| ) as interface: | |
| # ----------------------------------------------------- | |
| # TITLE | |
| # ----------------------------------------------------- | |
| gr.Markdown( | |
| "# 📦 RVP Label OCR Extractor" | |
| ) | |
| gr.Markdown( | |
| "Upload RVP label images to extract " | |
| "the last 3 digits of AWB, " | |
| "last 3 digits of Packet ID, " | |
| "and Ship To." | |
| ) | |
| # ----------------------------------------------------- | |
| # FILE UPLOAD | |
| # ----------------------------------------------------- | |
| file_input = gr.File( | |
| file_count="multiple", | |
| file_types=[ | |
| ".jpg", | |
| ".jpeg", | |
| ".png" | |
| ], | |
| label="Upload RVP Photos", | |
| type="filepath" | |
| ) | |
| # ----------------------------------------------------- | |
| # EXTRACT BUTTON | |
| # ----------------------------------------------------- | |
| extract_btn = gr.Button( | |
| "Extract Data", | |
| variant="primary" | |
| ) | |
| # ----------------------------------------------------- | |
| # RESULTS | |
| # ----------------------------------------------------- | |
| output_df = gr.Dataframe( | |
| headers=[ | |
| "AWB", | |
| "Packet ID", | |
| "Ship To" | |
| ], | |
| datatype=[ | |
| "str", | |
| "str", | |
| "str" | |
| ], | |
| label="Results", | |
| interactive=False | |
| ) | |
| # ----------------------------------------------------- | |
| # EXCEL DOWNLOAD | |
| # ----------------------------------------------------- | |
| output_file = gr.File( | |
| label="Download Excel File" | |
| ) | |
| # ----------------------------------------------------- | |
| # BUTTON EVENT | |
| # ----------------------------------------------------- | |
| extract_btn.click( | |
| fn=process_images, | |
| inputs=file_input, | |
| outputs=[ | |
| output_df, | |
| output_file | |
| ] | |
| ) | |
| # ========================================================= | |
| # LAUNCH | |
| # ========================================================= | |
| if __name__ == "__main__": | |
| interface.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| ssr_mode=False | |
| ) |