Update app.py
Browse files
app.py
CHANGED
|
@@ -5,8 +5,6 @@ from PIL import Image
|
|
| 5 |
import re
|
| 6 |
import pandas as pd
|
| 7 |
import os
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
import pytz
|
| 10 |
|
| 11 |
# --- 1. MODEL LOADING (GLOBAL) ---
|
| 12 |
# This part runs only once when the Gradio app starts, making it efficient.
|
|
@@ -25,8 +23,6 @@ try:
|
|
| 25 |
print("Model loaded successfully.")
|
| 26 |
except Exception as e:
|
| 27 |
print(f"Error loading model: {e}")
|
| 28 |
-
# If the model fails to load, the app is not usable.
|
| 29 |
-
# We can handle this by raising the exception or setting a flag.
|
| 30 |
model = None
|
| 31 |
processor = None
|
| 32 |
|
|
@@ -36,14 +32,13 @@ def extract_ktp_data(image_files):
|
|
| 36 |
Processes a list of uploaded image files, performs OCR, and extracts structured data.
|
| 37 |
|
| 38 |
Args:
|
| 39 |
-
image_files (list): A list of file
|
| 40 |
|
| 41 |
Returns:
|
| 42 |
pandas.DataFrame: A DataFrame containing the extracted data for each image.
|
| 43 |
"""
|
| 44 |
if not image_files:
|
| 45 |
print("No image files provided.")
|
| 46 |
-
# Return an empty dataframe with the correct columns if no files are uploaded
|
| 47 |
return pd.DataFrame(columns=['Filename', 'NIK', 'Nama', 'Tempat Lahir', 'Tanggal Lahir'])
|
| 48 |
|
| 49 |
if not model or not processor:
|
|
@@ -61,94 +56,70 @@ def extract_ktp_data(image_files):
|
|
| 61 |
}
|
| 62 |
|
| 63 |
# Loop through each uploaded file
|
| 64 |
-
for
|
| 65 |
-
filename = os.path.basename(
|
| 66 |
print(f"-> Processing: {filename}")
|
| 67 |
|
| 68 |
try:
|
| 69 |
-
|
| 70 |
-
image = Image.open(file_obj.name).convert("RGB")
|
| 71 |
|
| 72 |
-
# Perform inference
|
| 73 |
pixel_values = processor(images=image, return_tensors="pt").pixel_values.to(device)
|
| 74 |
generated_ids = model.generate(pixel_values, max_length=1024)
|
| 75 |
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
|
| 76 |
|
| 77 |
-
# Store results for the current image
|
| 78 |
current_image_data = {"Filename": filename}
|
| 79 |
|
| 80 |
-
# Apply regex patterns to the OCR output
|
| 81 |
for key, pattern in patterns.items():
|
| 82 |
match = re.search(pattern, generated_text)
|
| 83 |
if match:
|
| 84 |
-
# Specific capture groups are needed for 'nama' and 'tempat_lahir'
|
| 85 |
if key in ['nama', 'tempat_lahir']:
|
| 86 |
current_image_data[key.replace('_', ' ').title()] = match.group(1).strip()
|
| 87 |
else:
|
| 88 |
current_image_data[key.replace('_', ' ').title()] = match.group(0).strip()
|
| 89 |
else:
|
| 90 |
-
# If no match is found, record it as None
|
| 91 |
current_image_data[key.replace('_', ' ').title()] = None
|
| 92 |
|
| 93 |
all_results.append(current_image_data)
|
| 94 |
|
| 95 |
except Exception as e:
|
| 96 |
print(f"Error processing {filename}: {e}")
|
| 97 |
-
# Add an entry indicating the error for this file
|
| 98 |
all_results.append({
|
| 99 |
-
"Filename": filename,
|
| 100 |
-
"
|
| 101 |
-
"Nama": None,
|
| 102 |
-
"Tempat Lahir": None,
|
| 103 |
-
"Tanggal Lahir": None
|
| 104 |
})
|
| 105 |
|
| 106 |
-
# Convert the list of results into a Pandas DataFrame
|
| 107 |
results_df = pd.DataFrame(all_results)
|
| 108 |
print("Processing complete.")
|
| 109 |
return results_df
|
| 110 |
|
| 111 |
# --- 3. UI DEFINITION: THE GRADIO INTERFACE ---
|
| 112 |
|
| 113 |
-
# Get current time in WIB (Western Indonesia Time) for the description
|
| 114 |
-
jakarta_tz = pytz.timezone('Asia/Jakarta')
|
| 115 |
-
current_time_wib = datetime.now(jakarta_tz).strftime("%A, %B %d, %Y at %I:%M %p WIB")
|
| 116 |
-
|
| 117 |
-
# A description for the app header, written in Markdown
|
| 118 |
app_description = f"""
|
| 119 |
# KTP (Indonesian ID Card) OCR Extractor 🇮🇩
|
| 120 |
This application extracts key information (**NIK, Nama, Tempat Lahir, Tanggal Lahir**) from Indonesian ID cards (KTP).
|
| 121 |
You can upload one or multiple KTP images at once. The results will be displayed in a table below.
|
| 122 |
|
| 123 |
*Powered by the `emisilab/model-ocr-ktp-v1` model from Hugging Face.*
|
| 124 |
-
\n*Last Updated:
|
| 125 |
"""
|
| 126 |
|
| 127 |
-
# Example images for users to try
|
| 128 |
example_images = [
|
| 129 |
"https://huggingface.co/emisilab/model-ocr-ktp-v1/resolve/main/ocr-ktp-1.jpg",
|
| 130 |
"https://huggingface.co/emisilab/model-ocr-ktp-v1/resolve/main/ocr-ktp-2.jpg"
|
| 131 |
]
|
| 132 |
|
| 133 |
-
# Use gr.Blocks() for a custom layout
|
| 134 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 135 |
-
# Title and description
|
| 136 |
gr.Markdown(app_description)
|
| 137 |
|
| 138 |
with gr.Row():
|
| 139 |
with gr.Column(scale=1):
|
| 140 |
-
# Input component: Allows multiple image uploads
|
| 141 |
image_input = gr.File(
|
| 142 |
label="Upload KTP Images",
|
| 143 |
file_count="multiple",
|
| 144 |
file_types=["image"],
|
| 145 |
-
type="file
|
| 146 |
)
|
| 147 |
-
|
| 148 |
-
# Action button to trigger the process
|
| 149 |
extract_button = gr.Button("Extract KTP Data", variant="primary")
|
| 150 |
-
|
| 151 |
-
# Add examples for users to easily test the app
|
| 152 |
gr.Examples(
|
| 153 |
examples=example_images,
|
| 154 |
inputs=image_input,
|
|
@@ -156,13 +127,11 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 156 |
)
|
| 157 |
|
| 158 |
with gr.Column(scale=2):
|
| 159 |
-
# Output component: Displays the results in a table
|
| 160 |
output_dataframe = gr.DataFrame(
|
| 161 |
label="Extracted Information",
|
| 162 |
headers=['Filename', 'NIK', 'Nama', 'Tempat Lahir', 'Tanggal Lahir']
|
| 163 |
)
|
| 164 |
|
| 165 |
-
# Connect the button to the function
|
| 166 |
extract_button.click(
|
| 167 |
fn=extract_ktp_data,
|
| 168 |
inputs=image_input,
|
|
@@ -172,3 +141,4 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 172 |
# --- 4. LAUNCH THE APP ---
|
| 173 |
if __name__ == "__main__":
|
| 174 |
demo.launch()
|
|
|
|
|
|
| 5 |
import re
|
| 6 |
import pandas as pd
|
| 7 |
import os
|
|
|
|
|
|
|
| 8 |
|
| 9 |
# --- 1. MODEL LOADING (GLOBAL) ---
|
| 10 |
# This part runs only once when the Gradio app starts, making it efficient.
|
|
|
|
| 23 |
print("Model loaded successfully.")
|
| 24 |
except Exception as e:
|
| 25 |
print(f"Error loading model: {e}")
|
|
|
|
|
|
|
| 26 |
model = None
|
| 27 |
processor = None
|
| 28 |
|
|
|
|
| 32 |
Processes a list of uploaded image files, performs OCR, and extracts structured data.
|
| 33 |
|
| 34 |
Args:
|
| 35 |
+
image_files (list): A list of file path objects from the Gradio File input.
|
| 36 |
|
| 37 |
Returns:
|
| 38 |
pandas.DataFrame: A DataFrame containing the extracted data for each image.
|
| 39 |
"""
|
| 40 |
if not image_files:
|
| 41 |
print("No image files provided.")
|
|
|
|
| 42 |
return pd.DataFrame(columns=['Filename', 'NIK', 'Nama', 'Tempat Lahir', 'Tanggal Lahir'])
|
| 43 |
|
| 44 |
if not model or not processor:
|
|
|
|
| 56 |
}
|
| 57 |
|
| 58 |
# Loop through each uploaded file
|
| 59 |
+
for file_path in image_files:
|
| 60 |
+
filename = os.path.basename(file_path)
|
| 61 |
print(f"-> Processing: {filename}")
|
| 62 |
|
| 63 |
try:
|
| 64 |
+
image = Image.open(file_path).convert("RGB")
|
|
|
|
| 65 |
|
|
|
|
| 66 |
pixel_values = processor(images=image, return_tensors="pt").pixel_values.to(device)
|
| 67 |
generated_ids = model.generate(pixel_values, max_length=1024)
|
| 68 |
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
|
| 69 |
|
|
|
|
| 70 |
current_image_data = {"Filename": filename}
|
| 71 |
|
|
|
|
| 72 |
for key, pattern in patterns.items():
|
| 73 |
match = re.search(pattern, generated_text)
|
| 74 |
if match:
|
|
|
|
| 75 |
if key in ['nama', 'tempat_lahir']:
|
| 76 |
current_image_data[key.replace('_', ' ').title()] = match.group(1).strip()
|
| 77 |
else:
|
| 78 |
current_image_data[key.replace('_', ' ').title()] = match.group(0).strip()
|
| 79 |
else:
|
|
|
|
| 80 |
current_image_data[key.replace('_', ' ').title()] = None
|
| 81 |
|
| 82 |
all_results.append(current_image_data)
|
| 83 |
|
| 84 |
except Exception as e:
|
| 85 |
print(f"Error processing {filename}: {e}")
|
|
|
|
| 86 |
all_results.append({
|
| 87 |
+
"Filename": filename, "NIK": f"Error: {e}", "Nama": None,
|
| 88 |
+
"Tempat Lahir": None, "Tanggal Lahir": None
|
|
|
|
|
|
|
|
|
|
| 89 |
})
|
| 90 |
|
|
|
|
| 91 |
results_df = pd.DataFrame(all_results)
|
| 92 |
print("Processing complete.")
|
| 93 |
return results_df
|
| 94 |
|
| 95 |
# --- 3. UI DEFINITION: THE GRADIO INTERFACE ---
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
app_description = f"""
|
| 98 |
# KTP (Indonesian ID Card) OCR Extractor 🇮🇩
|
| 99 |
This application extracts key information (**NIK, Nama, Tempat Lahir, Tanggal Lahir**) from Indonesian ID cards (KTP).
|
| 100 |
You can upload one or multiple KTP images at once. The results will be displayed in a table below.
|
| 101 |
|
| 102 |
*Powered by the `emisilab/model-ocr-ktp-v1` model from Hugging Face.*
|
| 103 |
+
\n*Last Updated: Sunday, August 31, 2025 at 12:05 PM WIB (Cilegon)*
|
| 104 |
"""
|
| 105 |
|
|
|
|
| 106 |
example_images = [
|
| 107 |
"https://huggingface.co/emisilab/model-ocr-ktp-v1/resolve/main/ocr-ktp-1.jpg",
|
| 108 |
"https://huggingface.co/emisilab/model-ocr-ktp-v1/resolve/main/ocr-ktp-2.jpg"
|
| 109 |
]
|
| 110 |
|
|
|
|
| 111 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
|
|
| 112 |
gr.Markdown(app_description)
|
| 113 |
|
| 114 |
with gr.Row():
|
| 115 |
with gr.Column(scale=1):
|
|
|
|
| 116 |
image_input = gr.File(
|
| 117 |
label="Upload KTP Images",
|
| 118 |
file_count="multiple",
|
| 119 |
file_types=["image"],
|
| 120 |
+
type="filepath" # FIX: Changed 'file' to 'filepath'
|
| 121 |
)
|
|
|
|
|
|
|
| 122 |
extract_button = gr.Button("Extract KTP Data", variant="primary")
|
|
|
|
|
|
|
| 123 |
gr.Examples(
|
| 124 |
examples=example_images,
|
| 125 |
inputs=image_input,
|
|
|
|
| 127 |
)
|
| 128 |
|
| 129 |
with gr.Column(scale=2):
|
|
|
|
| 130 |
output_dataframe = gr.DataFrame(
|
| 131 |
label="Extracted Information",
|
| 132 |
headers=['Filename', 'NIK', 'Nama', 'Tempat Lahir', 'Tanggal Lahir']
|
| 133 |
)
|
| 134 |
|
|
|
|
| 135 |
extract_button.click(
|
| 136 |
fn=extract_ktp_data,
|
| 137 |
inputs=image_input,
|
|
|
|
| 141 |
# --- 4. LAUNCH THE APP ---
|
| 142 |
if __name__ == "__main__":
|
| 143 |
demo.launch()
|
| 144 |
+
|