| import gradio as gr |
| import re |
| import pandas as pd |
| import os |
| import cv2 |
| import numpy as np |
| from PIL import Image |
| import pytesseract |
|
|
| |
| Image.MAX_IMAGE_PIXELS = None |
|
|
| def clean_and_format_number(phone_str): |
| """ |
| Cleans the extracted string to ensure it's a valid phone number. |
| Removes spaces, dashes, brackets, and names. |
| """ |
| |
| cleaned = re.sub(r'[^\d+]', '', str(phone_str)) |
| |
| |
| digits_only = cleaned.replace('+', '') |
| |
| |
| if 10 <= len(digits_only) <= 15: |
| |
| if not cleaned.startswith('+'): |
| cleaned = '+' + cleaned |
| return cleaned |
| return None |
|
|
| def extract_numbers_from_images(image_files): |
| """Main processing function triggered by Gradio.""" |
| if not image_files: |
| return None, "⚠️ Please upload at least one image." |
|
|
| all_unique_numbers = set() |
| logs =[] |
|
|
| for i, file_obj in enumerate(image_files): |
| |
| file_path = file_obj if isinstance(file_obj, str) else file_obj.name |
| file_basename = os.path.basename(file_path) |
| |
| logs.append(f"⚡ Processing image {i+1}/{len(image_files)}: {file_basename}...") |
| |
| try: |
| |
| |
| img_array = np.fromfile(file_path, np.uint8) |
| img = cv2.imdecode(img_array, cv2.IMREAD_COLOR) |
| |
| if img is None: |
| logs.append(f"❌ Error: Could not read image {file_basename}.") |
| continue |
|
|
| |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
| |
| height, width = gray.shape |
| |
| |
| if width > 1080: |
| ratio = 1080.0 / width |
| new_height = int(height * ratio) |
| gray = cv2.resize(gray, (1080, new_height), interpolation=cv2.INTER_AREA) |
| height, width = gray.shape |
|
|
| |
| MAX_HEIGHT = 4000 |
| chunk_count = 0 |
| |
| for y in range(0, height, MAX_HEIGHT): |
| chunk_count += 1 |
| |
| chunk = gray[y:min(y + MAX_HEIGHT, height), :] |
| |
| |
| text = pytesseract.image_to_string(chunk) |
| |
| |
| |
| pattern = r'\+?[\d\s\-\(\)]{9,25}' |
| potential_matches = re.findall(pattern, text) |
| |
| valid_count = 0 |
| for match in potential_matches: |
| clean_num = clean_and_format_number(match) |
| if clean_num: |
| all_unique_numbers.add(clean_num) |
| valid_count += 1 |
| |
| logs.append(f" -> Scanned {chunk_count} chunk(s). Current total unique numbers: {len(all_unique_numbers)}") |
| |
| except Exception as e: |
| logs.append(f"❌ Error processing {file_basename}: {str(e)}") |
|
|
| |
| if not all_unique_numbers: |
| return None, "\n".join(logs) + "\n\n⚠️ No phone numbers were extracted from the uploaded images." |
|
|
| |
| sorted_numbers = sorted(list(all_unique_numbers)) |
| |
| |
| df = pd.DataFrame({"Phone Number": sorted_numbers}) |
| |
| |
| excel_filename = "extracted_whatsapp_numbers.xlsx" |
| df.to_excel(excel_filename, index=False) |
| |
| final_status = "\n".join(logs) + f"\n\n🎉 SUCCESS! Instantly extracted {len(sorted_numbers)} total unique numbers." |
| |
| return excel_filename, final_status |
|
|
| |
|
|
| with gr.Blocks(theme=gr.themes.Soft()) as demo: |
| gr.Markdown( |
| """ |
| """ |
| ) |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| image_input = gr.File( |
| file_count="multiple", |
| type="filepath", |
| label="Upload WhatsApp Screenshots", |
| file_types=["image"] |
| ) |
| submit_btn = gr.Button("Extract Numbers Instantly", variant="primary") |
| |
| with gr.Column(scale=1): |
| excel_output = gr.File(label="Download Extracted Numbers (Excel)") |
| status_output = gr.Textbox(label="Processing Logs & Status", lines=12, interactive=False) |
| |
| submit_btn.click( |
| fn=extract_numbers_from_images, |
| inputs=[image_input], |
| outputs=[excel_output, status_output] |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |