import sys, json, os, glob, requests import re import time from contextlib import redirect_stdout, redirect_stderr # Immediately redirect all output to stderr except for our final JSON original_stdout = sys.stdout sys.stdout = sys.stderr # Suppress all PaddleOCR output os.environ['PADDLEOCR_LOG_LEVEL'] = 'ERROR' os.environ['QT_QPA_PLATFORM'] = 'offscreen' os.environ['DISPLAY'] = ':99' # Import PaddleOCR after setting environment variables from paddleocr import PaddleOCR def download_image(url, output_path='temp_police_image.jpg'): # Remove any existing temp file if os.path.exists(output_path): os.remove(output_path) # Add cache-busting parameters timestamp = int(time.time()) if '?' in url: url += f'&t={timestamp}' else: url += f'?t={timestamp}' # Add headers to prevent caching headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' } response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() image_data = response.content # Save the image with open(output_path, 'wb') as f: f.write(image_data) return output_path def format_name(name): """Format name: add proper spacing and commas (generic for all police clearances) Handles common OCR issues like missing spaces between name parts and missing comma spacing. Works with any name format, not specific to one document. """ if not name: return None # Remove extra spaces and normalize name = ' '.join(name.split()) # First, ensure comma spacing: "JAVA,ALBERT" -> "JAVA, ALBERT" name = re.sub(r',([A-Z])', r', \1', name) name = re.sub(r',\s*([A-Z])', r', \1', name) # Split by comma if present if ',' in name: parts = name.split(',') formatted_parts = [] for part in parts: part = part.strip() # Handle consecutive capitals: "JAVAALBERTJOY" -> "JAVA ALBERT JOY" # Strategy: split where a capital letter is followed by another capital + lowercase # "ALBERTJOY" -> "ALBERT JOY" part = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1 \2', part) # Handle remaining cases: "JOYBAUTISTA" -> "JOY BAUTISTA" part = re.sub(r'([A-Z][a-z]+)([A-Z][a-z]+)', r'\1 \2', part) formatted_parts.append(part) name = ', '.join(formatted_parts) else: # No comma, try to add spaces between name parts # "JAVAALBERTJOY BAUTISTA" -> "JAVA ALBERT JOY BAUTISTA" # Add space before capital letters that follow lowercase name = re.sub(r'([a-z])([A-Z])', r'\1 \2', name) # Add space between consecutive capitals: "JAVAALBERT" -> "JAVA ALBERT" # But be careful: "BAUTISTA" should stay together # Split where we have multiple capitals followed by a capital+lowercase name = re.sub(r'([A-Z]{2,})([A-Z][a-z])', r'\1 \2', name) # Also handle: "ALBERTJOY" -> "ALBERT JOY" name = re.sub(r'([A-Z]+)([A-Z][a-z]+)', r'\1 \2', name) # Clean up multiple spaces name = ' '.join(name.split()) return name.strip() def format_address(address): """Format address: add proper spacing (generic for all police clearances)""" if not address: return None # Remove extra spaces address = ' '.join(address.split()) # Handle #BLK/#BLOCK pattern: ensure space after # if followed by letters and numbers # "#BLK11" -> "#BLK 11", "#BLOCK5" -> "#BLOCK 5" address = re.sub(r'#([A-Z]+)(\d+)', r'#\1 \2', address) # Add space before city names and common address parts (capital followed by capital+lowercase) # "CAMPOTINIO" -> "CAMPO TINIO", "CABANATUANCITY" -> "CABANATUAN CITY" address = re.sub(r'([A-Z])([A-Z][a-z]+)', r'\1 \2', address) # Ensure comma spacing: "CITY,NUEVA" -> "CITY, NUEVA" address = re.sub(r',([A-Z])', r', \1', address) address = re.sub(r',\s*([A-Z])', r', \1', address) # Clean up multiple spaces address = ' '.join(address.split()) return address.strip() def format_birth_place(place): """Format birth place: add proper spacing (generic for all police clearances)""" if not place: return None # Remove extra spaces place = ' '.join(place.split()) # Ensure comma spacing: "DILASAG,AURORA" -> "DILASAG, AURORA" place = re.sub(r',([A-Z])', r', \1', place) place = re.sub(r',\s*([A-Z])', r', \1', place) # Add space before province/region names if needed # "PLACE PROVINCE" -> already spaced, but handle "PLACEPROVINCE" -> "PLACE PROVINCE" place = re.sub(r'([A-Z])([A-Z][a-z]+)', r'\1 \2', place) # Clean up multiple spaces place = ' '.join(place.split()) return place.strip() def format_birth_date(date): """Format birth date: fix common OCR errors (generic for all police clearances)""" if not date: return None # Fix common OCR errors for month names (universal issues) date = date.replace('Juy', 'July') # Common OCR error date = date.replace('Januay', 'January') date = date.replace('Februay', 'February') date = date.replace('Marc', 'March') date = date.replace('Apil', 'April') date = date.replace('Jun', 'June') # Be careful - June is valid, but "Jun" might be incomplete date = date.replace('Augu', 'August') date = date.replace('Septemb', 'September') date = date.replace('Octob', 'October') date = date.replace('Novemb', 'November') date = date.replace('Decemb', 'December') # Fix year errors: "1905" when it should be "05" (day) - common OCR issue # Pattern: "July 1905, 1991" -> "July 05, 1991" # Check if we have a pattern like "Month 19XX, YYYY" where 19XX is likely the day misread match = re.search(r'(\w+)\s+19(\d{2}),\s*(\d{4})', date) if match: day = match.group(2) year = match.group(3) # If day is 00-31, it's likely a day, not a year if 0 <= int(day) <= 31: date = re.sub(r'(\w+)\s+19(\d{2}),\s*(\d{4})', rf'\1 {day}, \3', date) # Ensure proper date format: "July 05, 1991" date = re.sub(r'(\w+)\s+(\d{1,2})\s*,\s*(\d{4})', r'\1 \2, \3', date) # Clean up multiple spaces date = ' '.join(date.split()) return date.strip() def extract_police_details(lines): details = { 'clearance_type': 'police', 'id_number': None, 'full_name': None, 'address': None, 'birth_date': None, 'birth_place': None, 'citizenship': None, 'gender': None, 'status': None, 'success': False } for i, line in enumerate(lines): if not isinstance(line, str): continue line_upper = line.upper().strip() line_stripped = line.strip() # Extract Name - handle cases where NAME and value are on separate lines # Format: 'NAME' on one line, ':IRENE TIMBAL VILLAFUERTE' on next line if "NAME" in line_upper and not details['full_name']: # First, check if name is on the same line after colon if ":" in line: parts = line.split(':', 1) if len(parts) > 1: name_part = parts[1].strip() # Validate it's actually a name (not descriptive text) if name_part and len(name_part) > 2 and not any(word in name_part.upper() for word in ['THUMBMARK', 'APPEARING', 'HEREIN', 'HASUNDERGONE', 'RECORD', 'VERIFICATION', 'THROUGH', 'CRIME', 'DATABASES', 'RESULT']): details['full_name'] = name_part print(f"DEBUG: Found full name (same line): {details['full_name']}", file=sys.stderr) continue # Check next few lines for name value (prioritize lines starting with colon) if i + 1 < len(lines): for j in range(1, min(5, len(lines) - i)): next_line = lines[i+j].strip() next_upper = next_line.upper() # Skip if it's clearly a label or descriptive text if any(word in next_upper for word in ['ADDRESS', 'BIRTH', 'CITIZEN', 'GENDER', 'ID', 'THUMBMARK', 'APPEARING', 'HEREIN', 'HASUNDERGONE', 'RECORD', 'VERIFICATION', 'THROUGH', 'CRIME', 'DATABASES', 'RESULT', 'CERTIFY', 'PERSON', 'WHOSE', 'PHOTO', 'SIGNATURE']): continue # Priority: Line starting with colon (most reliable format) if next_line.startswith(':') and len(next_line) > 1: name_part = next_line[1:].strip() # Validate it looks like a name (has letters, reasonable length, not descriptive text) if (name_part and len(name_part) > 3 and re.search(r'[A-Za-z]{2,}', name_part) and not any(word in name_part.upper() for word in ['THUMBMARK', 'APPEARING', 'HEREIN', 'HASUNDERGONE', 'RECORD', 'VERIFICATION'])): details['full_name'] = name_part print(f"DEBUG: Found full name (colon line): {details['full_name']}", file=sys.stderr) break # Fallback: Line that looks like a name (all caps, multiple words, reasonable length) elif (re.match(r'^[A-Z\s,]+$', next_line) and len(next_line.split()) >= 2 and len(next_line) > 5 and len(next_line) < 50): # Names are usually not too long # Make sure it's not descriptive text if not any(word in next_upper for word in ['THUMBMARK', 'APPEARING', 'HEREIN', 'HASUNDERGONE', 'RECORD', 'VERIFICATION', 'THROUGH', 'CRIME']): details['full_name'] = next_line print(f"DEBUG: Found full name (all caps line): {details['full_name']}", file=sys.stderr) break # Also check for name patterns that start with colon (OCR sometimes splits NAME label) # But only if we haven't found a name yet if not details['full_name'] and line_stripped.startswith(':') and len(line_stripped) > 5: name_candidate = line_stripped[1:].strip() # Check if it looks like a name (has letters, reasonable length, not descriptive text) if (re.search(r'[A-Za-z]{2,}', name_candidate) and len(name_candidate) > 3 and len(name_candidate) < 50 and not any(word in name_candidate.upper() for word in ['THUMBMARK', 'APPEARING', 'HEREIN', 'HASUNDERGONE', 'RECORD', 'VERIFICATION', 'THROUGH', 'CRIME', 'ADDRESS', 'BIRTH'])): # Make sure previous line wasn't ADDRESS or other label if i > 0: prev_line = lines[i-1].strip().upper() if "ADDRESS" not in prev_line and "BIRTH" not in prev_line and "CITIZEN" not in prev_line: details['full_name'] = name_candidate print(f"DEBUG: Found full name (colon pattern): {details['full_name']}", file=sys.stderr) # Extract Address if "ADDRESS" in line_upper and not details['address']: if ":" in line: parts = line.split(':') if len(parts) > 1: addr_part = parts[1].strip() if addr_part: details['address'] = addr_part elif i + 1 < len(lines): # Check next few lines for address value addr_parts = [] for j in range(1, min(4, len(lines) - i)): next_line = lines[i+j].strip() if next_line.startswith(':') and len(next_line) > 1: addr_parts.append(next_line[1:].strip()) elif "BIRTH" not in next_line.upper() and "CITIZEN" not in next_line.upper(): if ":" in next_line: parts = next_line.split(':', 1) if len(parts) > 1: addr_parts.append(parts[1].strip()) elif len(next_line) > 2: addr_parts.append(next_line) else: break if addr_parts: details['address'] = ' '.join(addr_parts).strip() # Extract Birth Date - handle OCR errors and combined patterns if ("BIRTH DATE" in line_upper or "BIRTHDATE" in line_upper) and not details['birth_date']: if ":" in line: parts = line.split(':', 1) if len(parts) > 1: date_part = parts[1].strip() # Fix common OCR errors date_part = date_part.replace('Juy', 'July').replace('Juy', 'July') # Fix year errors (1001 -> 1991, etc.) date_part = re.sub(r'\b1001\b', '1991', date_part) date_part = re.sub(r'\b(\d{2})\b', lambda m: '19' + m.group(1) if len(m.group(1)) == 2 and int(m.group(1)) < 50 else m.group(1), date_part) if date_part: details['birth_date'] = date_part elif i + 1 < len(lines): next_line = lines[i+1].strip() if ":" in next_line: parts = next_line.split(':', 1) if len(parts) > 1: date_part = parts[1].strip() date_part = date_part.replace('Juy', 'July') date_part = re.sub(r'\b1001\b', '1991', date_part) if date_part: details['birth_date'] = date_part # Also look for date patterns in lines that might have been OCR'd incorrectly if not details['birth_date']: # Look for patterns like "Juy 05, 1001" or "July 03, 1991" date_pattern = re.search(r'(January|February|March|April|May|June|July|August|September|October|November|December|Juy|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2}[,\s]+\d{4}', line_upper) if date_pattern: date_part = date_pattern.group() date_part = date_part.replace('Juy', 'July') date_part = re.sub(r'\b1001\b', '1991', date_part) details['birth_date'] = date_part # Extract Birth Place if "BIRTH PLACE" in line_upper and not details['birth_place']: if ":" in line: parts = line.split(':', 1) if len(parts) > 1: details['birth_place'] = parts[1].strip() elif i + 1 < len(lines): next_line = lines[i+1].strip() if next_line.startswith(':') and len(next_line) > 1: details['birth_place'] = next_line[1:].strip() elif ":" in next_line and "CITIZEN" not in next_line.upper(): parts = next_line.split(':', 1) if len(parts) > 1: details['birth_place'] = parts[1].strip() # Extract Citizenship if "CITIZENSHIP" in line_upper and not details['citizenship']: if ":" in line: parts = line.split(':', 1) if len(parts) > 1: details['citizenship'] = parts[1].strip() elif i + 1 < len(lines): next_line = lines[i+1].strip() if next_line.startswith(':') and len(next_line) > 1: details['citizenship'] = next_line[1:].strip() elif ":" in next_line: parts = next_line.split(':', 1) if len(parts) > 1: details['citizenship'] = parts[1].strip() # Extract Gender - handle cases where GENDER and value are on separate lines # Format: 'GENDER' on one line, 'FEMALE' or 'MALE' on next line if "GENDER" in line_upper and not details['gender']: # First, check if gender is on the same line after colon if ":" in line: parts = line.split(':', 1) if len(parts) > 1: gender_part = parts[1].strip().upper() if gender_part in ['MALE', 'FEMALE', 'M', 'F']: details['gender'] = gender_part.capitalize() if len(gender_part) > 1 else gender_part print(f"DEBUG: Found gender (same line): {details['gender']}", file=sys.stderr) continue # Check next few lines for gender value if i + 1 < len(lines): for j in range(1, min(4, len(lines) - i)): next_line = lines[i+j].strip() next_upper = next_line.upper() # Skip if it's clearly a label if any(label in next_upper for label in ['NAME', 'ADDRESS', 'BIRTH', 'CITIZEN', 'DATE', 'PLACE', 'PICTURE', 'SIGNATURE', 'THUMBMARK']): continue # Check if line starts with colon if next_line.startswith(':') and len(next_line) > 1: gender_part = next_line[1:].strip().upper() if gender_part in ['MALE', 'FEMALE', 'M', 'F']: details['gender'] = gender_part.capitalize() if len(gender_part) > 1 else gender_part print(f"DEBUG: Found gender (colon line): {details['gender']}", file=sys.stderr) break # Check if the line itself is the gender value elif next_upper in ['MALE', 'FEMALE', 'M', 'F']: details['gender'] = next_line.capitalize() if len(next_line) > 1 else next_line print(f"DEBUG: Found gender (direct): {details['gender']}", file=sys.stderr) break # Check if line contains colon with gender value elif ":" in next_line: parts = next_line.split(':', 1) if len(parts) > 1: gender_part = parts[1].strip().upper() if gender_part in ['MALE', 'FEMALE', 'M', 'F']: details['gender'] = gender_part.capitalize() if len(gender_part) > 1 else gender_part print(f"DEBUG: Found gender (colon in line): {details['gender']}", file=sys.stderr) break # Extract ID Number (Usually "ID No.:" or near QR code) if "ID NO" in line_upper or "ID NO." in line_upper: parts = line.split(':') if len(parts) > 1: details['id_number'] = parts[1].strip() # Fallback ID extraction looking for specific patterns if not found by label if not details['id_number']: # Look for pattern like TRARH + digits id_match = re.search(r'\b[A-Z]{4,5}\d{10,15}\b', line_upper) if id_match: details['id_number'] = id_match.group() # Extract Status (e.g., "NO RECORD ON FILE") if "NO RECORD ON FILE" in line_upper: details['status'] = "NO RECORD ON FILE" elif "HAS A RECORD" in line_upper or "WITH RECORD" in line_upper: details['status'] = "HAS RECORD" if details['full_name'] or details['id_number']: details['success'] = True # Format the extracted fields if details['full_name']: details['full_name'] = format_name(details['full_name']) if details['address']: details['address'] = format_address(details['address']) if details['birth_place']: details['birth_place'] = format_birth_place(details['birth_place']) if details['birth_date']: details['birth_date'] = format_birth_date(details['birth_date']) return details def extract_ocr_lines(image_path): # Check if file exists if not os.path.exists(image_path): return {'success': False, 'error': 'File not found'} file_size = os.path.getsize(image_path) print(f"DEBUG: Image file size: {file_size} bytes", file=sys.stderr) with redirect_stdout(sys.stderr), redirect_stderr(sys.stderr): # Try simple configuration first (matching NBI script primary method) ocr = PaddleOCR( use_doc_orientation_classify=False, use_doc_unwarping=False, use_textline_orientation=False, lang='en' ) try: results = ocr.ocr(image_path) except Exception as e: print(f"DEBUG: ocr() failed: {e}, trying predict()", file=sys.stderr) if hasattr(ocr, 'predict'): results = ocr.predict(image_path) else: results = None # Debug: Print raw results structure print(f"DEBUG: Raw OCR results type: {type(results)}", file=sys.stderr) print(f"DEBUG: Raw OCR results is None: {results is None}", file=sys.stderr) if results is not None: print(f"DEBUG: Raw OCR results length: {len(results) if isinstance(results, list) else 'N/A'}", file=sys.stderr) if isinstance(results, list) and len(results) > 0: print(f"DEBUG: First level item type: {type(results[0])}", file=sys.stderr) print(f"DEBUG: First level item: {str(results[0])[:200] if results[0] else 'None'}", file=sys.stderr) if isinstance(results[0], list) and len(results[0]) > 0: print(f"DEBUG: Second level first item: {str(results[0][0])[:200] if results[0][0] else 'None'}", file=sys.stderr) # Process OCR results - handle both old format (list) and new format (OCRResult object) all_text = [] try: # Check if results contain OCRResult objects (new PaddleX format) if results and isinstance(results, list) and len(results) > 0: first_item = results[0] # Check if it's an OCRResult object by type name item_type_name = type(first_item).__name__ is_ocr_result = 'OCRResult' in item_type_name or 'ocr_result' in str(type(first_item)).lower() if is_ocr_result: print(f"DEBUG: Detected OCRResult object format (type: {item_type_name})", file=sys.stderr) # Inspect attributes attrs = dir(first_item) print(f"DEBUG: OCRResult attributes: {[a for a in attrs if not a.startswith('_')]}", file=sys.stderr) for ocr_result in results: # Try various possible attribute names for text text_found = False # First, try accessing as dictionary (OCRResult is dict-like) try: if hasattr(ocr_result, 'keys'): ocr_dict = dict(ocr_result) print(f"DEBUG: OCRResult as dict keys: {list(ocr_dict.keys())}", file=sys.stderr) # Look for common OCR result keys (rec_texts is the actual key in PaddleX OCRResult) for key in ['rec_texts', 'rec_text', 'dt_polys', 'ocr_text', 'text', 'texts', 'result', 'results', 'ocr_result', 'dt_boxes']: if key in ocr_dict: val = ocr_dict[key] print(f"DEBUG: Found key '{key}': {type(val)}, length: {len(val) if isinstance(val, list) else 'N/A'}", file=sys.stderr) if isinstance(val, list): # rec_texts is a list of strings directly if key == 'rec_texts': for text_item in val: if isinstance(text_item, str) and text_item.strip(): all_text.append(text_item.strip()) elif text_item: all_text.append(str(text_item)) if val: text_found = True else: # For other keys, try to extract text from nested structures for item in val: if isinstance(item, (list, tuple)) and len(item) >= 2: # Format: [[coords], (text, confidence)] text_part = item[1] if isinstance(text_part, (list, tuple)) and len(text_part) >= 1: all_text.append(str(text_part[0])) elif isinstance(item, str): all_text.append(item) if val: text_found = True elif isinstance(val, str) and val: all_text.append(val) text_found = True if text_found: break except Exception as e: print(f"DEBUG: Error accessing OCRResult as dict: {e}", file=sys.stderr) # Try json() method if not text_found: try: if hasattr(ocr_result, 'json'): json_data = ocr_result.json() print(f"DEBUG: OCRResult.json() type: {type(json_data)}", file=sys.stderr) if isinstance(json_data, dict): print(f"DEBUG: OCRResult.json() keys: {list(json_data.keys())}", file=sys.stderr) # Look for text in JSON (rec_texts is the actual key) for key in ['rec_texts', 'rec_text', 'dt_polys', 'ocr_text', 'text', 'texts', 'result', 'results']: if key in json_data: val = json_data[key] if isinstance(val, list): # rec_texts is a list of strings directly if key == 'rec_texts': for text_item in val: if isinstance(text_item, str) and text_item.strip(): all_text.append(text_item.strip()) elif text_item: all_text.append(str(text_item)) if val: text_found = True else: for item in val: if isinstance(item, (list, tuple)) and len(item) >= 2: text_part = item[1] if isinstance(text_part, (list, tuple)) and len(text_part) >= 1: all_text.append(str(text_part[0])) elif isinstance(item, str): all_text.append(item) if val: text_found = True elif isinstance(val, str) and val: all_text.append(val) text_found = True if text_found: break except Exception as e: print(f"DEBUG: Error calling json(): {e}", file=sys.stderr) # Try rec_text attribute if not text_found and hasattr(ocr_result, 'rec_text'): rec_text = ocr_result.rec_text print(f"DEBUG: Found rec_text attribute: {type(rec_text)}", file=sys.stderr) if isinstance(rec_text, list): all_text.extend([str(t) for t in rec_text if t]) text_found = True elif rec_text: all_text.append(str(rec_text)) text_found = True # Try text attribute if not text_found and hasattr(ocr_result, 'text'): text = ocr_result.text print(f"DEBUG: Found text attribute: {type(text)}", file=sys.stderr) if isinstance(text, list): all_text.extend([str(t) for t in text if t]) text_found = True elif text: all_text.append(str(text)) text_found = True # If still no text, print full structure for debugging if not text_found: print(f"DEBUG: Could not find text in OCRResult, trying to inspect structure", file=sys.stderr) try: print(f"DEBUG: OCRResult repr: {repr(ocr_result)[:500]}", file=sys.stderr) # Try to get all keys/items if hasattr(ocr_result, 'keys'): try: all_keys = list(ocr_result.keys()) print(f"DEBUG: All OCRResult keys: {all_keys}", file=sys.stderr) for key in all_keys: try: val = ocr_result[key] print(f"DEBUG: Key '{key}' type: {type(val)}, value preview: {str(val)[:100]}", file=sys.stderr) except: pass except: pass except Exception as e: print(f"DEBUG: Error inspecting structure: {e}", file=sys.stderr) else: # Old format - list of lists lines = results[0] if results and isinstance(results[0], list) else results print(f"DEBUG: Processing lines (old format), count: {len(lines) if isinstance(lines, list) else 'N/A'}", file=sys.stderr) for item in lines: if isinstance(item, (list, tuple)) and len(item) >= 2: meta = item[1] if isinstance(meta, (list, tuple)) and len(meta) >= 1: all_text.append(str(meta[0])) except Exception as e: print(f"DEBUG: Error processing OCR results: {str(e)}", file=sys.stderr) import traceback print(f"DEBUG: Traceback: {traceback.format_exc()}", file=sys.stderr) # Try to inspect the object attributes if results and isinstance(results, list) and len(results) > 0: first_item = results[0] print(f"DEBUG: First item attributes: {dir(first_item)}", file=sys.stderr) if hasattr(first_item, '__dict__'): print(f"DEBUG: First item dict: {first_item.__dict__}", file=sys.stderr) print(f"DEBUG: Extracted text lines: {all_text}", file=sys.stderr) return extract_police_details(all_text) if all_text else {'clearance_type': 'police', 'id_number': None, 'full_name': None, 'address': None, 'birth_date': None, 'birth_place': None, 'citizenship': None, 'gender': None, 'status': None, 'success': False} def extract_ocr_lines_simple(image_path): # Fallback method with advanced features (matching NBI script fallback) with redirect_stdout(sys.stderr), redirect_stderr(sys.stderr): ocr = PaddleOCR( use_doc_orientation_classify=True, use_doc_unwarping=True, use_textline_orientation=True, lang='en' ) results = ocr.ocr(image_path) # Debug: Print raw results structure for fallback method print(f"DEBUG (fallback): Raw OCR results type: {type(results)}", file=sys.stderr) print(f"DEBUG (fallback): Raw OCR results is None: {results is None}", file=sys.stderr) if results is not None: print(f"DEBUG (fallback): Raw OCR results length: {len(results) if isinstance(results, list) else 'N/A'}", file=sys.stderr) if isinstance(results, list) and len(results) > 0: print(f"DEBUG (fallback): First level item type: {type(results[0])}", file=sys.stderr) if isinstance(results[0], list) and len(results[0]) > 0: print(f"DEBUG (fallback): Second level first item: {str(results[0][0])[:200] if results[0][0] else 'None'}", file=sys.stderr) all_text = [] try: # Check if results contain OCRResult objects (new PaddleX format) if results and isinstance(results, list) and len(results) > 0: first_item = results[0] # Check if it's an OCRResult object by type name item_type_name = type(first_item).__name__ is_ocr_result = 'OCRResult' in item_type_name or 'ocr_result' in str(type(first_item)).lower() if is_ocr_result: print(f"DEBUG (fallback): Detected OCRResult object format (type: {item_type_name})", file=sys.stderr) # Inspect attributes attrs = dir(first_item) print(f"DEBUG (fallback): OCRResult attributes: {[a for a in attrs if not a.startswith('_')]}", file=sys.stderr) for ocr_result in results: # Try various possible attribute names for text text_found = False # First, try accessing as dictionary (OCRResult is dict-like) try: if hasattr(ocr_result, 'keys'): ocr_dict = dict(ocr_result) print(f"DEBUG (fallback): OCRResult as dict keys: {list(ocr_dict.keys())}", file=sys.stderr) # Look for common OCR result keys (rec_texts is the actual key in PaddleX OCRResult) for key in ['rec_texts', 'rec_text', 'dt_polys', 'ocr_text', 'text', 'texts', 'result', 'results', 'ocr_result', 'dt_boxes']: if key in ocr_dict: val = ocr_dict[key] print(f"DEBUG (fallback): Found key '{key}': {type(val)}, length: {len(val) if isinstance(val, list) else 'N/A'}", file=sys.stderr) if isinstance(val, list): # rec_texts is a list of strings directly if key == 'rec_texts': for text_item in val: if isinstance(text_item, str) and text_item.strip(): all_text.append(text_item.strip()) elif text_item: all_text.append(str(text_item)) if val: text_found = True else: # For other keys, try to extract text from nested structures for item in val: if isinstance(item, (list, tuple)) and len(item) >= 2: # Format: [[coords], (text, confidence)] text_part = item[1] if isinstance(text_part, (list, tuple)) and len(text_part) >= 1: all_text.append(str(text_part[0])) elif isinstance(item, str): all_text.append(item) if val: text_found = True elif isinstance(val, str) and val: all_text.append(val) text_found = True if text_found: break except Exception as e: print(f"DEBUG (fallback): Error accessing OCRResult as dict: {e}", file=sys.stderr) # Try json() method if not text_found: try: if hasattr(ocr_result, 'json'): json_data = ocr_result.json() print(f"DEBUG (fallback): OCRResult.json() type: {type(json_data)}", file=sys.stderr) if isinstance(json_data, dict): print(f"DEBUG (fallback): OCRResult.json() keys: {list(json_data.keys())}", file=sys.stderr) # Look for text in JSON (rec_texts is the actual key) for key in ['rec_texts', 'rec_text', 'dt_polys', 'ocr_text', 'text', 'texts', 'result', 'results']: if key in json_data: val = json_data[key] if isinstance(val, list): # rec_texts is a list of strings directly if key == 'rec_texts': for text_item in val: if isinstance(text_item, str) and text_item.strip(): all_text.append(text_item.strip()) elif text_item: all_text.append(str(text_item)) if val: text_found = True else: for item in val: if isinstance(item, (list, tuple)) and len(item) >= 2: text_part = item[1] if isinstance(text_part, (list, tuple)) and len(text_part) >= 1: all_text.append(str(text_part[0])) elif isinstance(item, str): all_text.append(item) if val: text_found = True elif isinstance(val, str) and val: all_text.append(val) text_found = True if text_found: break except Exception as e: print(f"DEBUG (fallback): Error calling json(): {e}", file=sys.stderr) # Try rec_text attribute if not text_found and hasattr(ocr_result, 'rec_text'): rec_text = ocr_result.rec_text print(f"DEBUG (fallback): Found rec_text attribute: {type(rec_text)}", file=sys.stderr) if isinstance(rec_text, list): all_text.extend([str(t) for t in rec_text if t]) text_found = True elif rec_text: all_text.append(str(rec_text)) text_found = True # Try text attribute if not text_found and hasattr(ocr_result, 'text'): text = ocr_result.text print(f"DEBUG (fallback): Found text attribute: {type(text)}", file=sys.stderr) if isinstance(text, list): all_text.extend([str(t) for t in text if t]) text_found = True elif text: all_text.append(str(text)) text_found = True # If still no text, print full structure for debugging if not text_found: print(f"DEBUG (fallback): Could not find text in OCRResult, trying to inspect structure", file=sys.stderr) try: print(f"DEBUG (fallback): OCRResult repr: {repr(ocr_result)[:500]}", file=sys.stderr) # Try to get all keys/items if hasattr(ocr_result, 'keys'): try: all_keys = list(ocr_result.keys()) print(f"DEBUG (fallback): All OCRResult keys: {all_keys}", file=sys.stderr) for key in all_keys: try: val = ocr_result[key] print(f"DEBUG (fallback): Key '{key}' type: {type(val)}, value preview: {str(val)[:100]}", file=sys.stderr) except: pass except: pass except Exception as e: print(f"DEBUG (fallback): Error inspecting structure: {e}", file=sys.stderr) else: # Old format - list of lists lines = results[0] if results and isinstance(results[0], list) else results print(f"DEBUG (fallback): Processing lines (old format), count: {len(lines) if isinstance(lines, list) else 'N/A'}", file=sys.stderr) for item in lines: if isinstance(item, (list, tuple)) and len(item) >= 2: meta = item[1] if isinstance(meta, (list, tuple)) and len(meta) >= 1: all_text.append(str(meta[0])) except Exception as e: print(f"DEBUG (fallback): Error processing OCR results: {str(e)}", file=sys.stderr) import traceback print(f"DEBUG (fallback): Traceback: {traceback.format_exc()}", file=sys.stderr) # Try to inspect the object attributes if results and isinstance(results, list) and len(results) > 0: first_item = results[0] print(f"DEBUG (fallback): First item attributes: {dir(first_item)}", file=sys.stderr) if hasattr(first_item, '__dict__'): print(f"DEBUG (fallback): First item dict: {first_item.__dict__}", file=sys.stderr) print(f"DEBUG (fallback): Extracted text lines: {all_text}", file=sys.stderr) return extract_police_details(all_text) if all_text else {'clearance_type': 'police', 'id_number': None, 'full_name': None, 'address': None, 'birth_date': None, 'birth_place': None, 'citizenship': None, 'gender': None, 'status': None, 'success': False} # Main Execution if len(sys.argv) < 2: sys.stdout = original_stdout print(json.dumps({"success": False, "error": "No image URL provided"})) sys.exit(1) image_url = sys.argv[1] print(f"DEBUG: Processing Police Clearance image URL: {image_url}", file=sys.stderr) try: image_path = download_image(image_url, 'temp_police_image.jpg') print(f"DEBUG: Image downloaded to: {image_path}", file=sys.stderr) # Try the original OCR method first ocr_results = extract_ocr_lines(image_path) print(f"DEBUG: OCR results from extract_ocr_lines: {ocr_results}", file=sys.stderr) # If original method fails, try simple method with advanced features if not ocr_results['success']: print("DEBUG: Original method failed, trying simple method with advanced features", file=sys.stderr) ocr_results = extract_ocr_lines_simple(image_path) print(f"DEBUG: OCR results from extract_ocr_lines_simple: {ocr_results}", file=sys.stderr) # Clean up if os.path.exists(image_path): os.remove(image_path) response = { "success": ocr_results['success'], "data": ocr_results } sys.stdout = original_stdout sys.stdout.write(json.dumps(response)) sys.stdout.flush() except Exception as e: sys.stdout = original_stdout sys.stdout.write(json.dumps({"success": False, "error": str(e)})) sys.stdout.flush() sys.exit(1) finally: try: if os.path.exists('temp_police_image.jpg'): os.remove('temp_police_image.jpg') except: pass