Binayak Panigrahi commited on
Commit
8976c96
ยท
1 Parent(s): 611b849

Add application file

Browse files
Files changed (2) hide show
  1. app.py +130 -42
  2. index.html +6 -6
app.py CHANGED
@@ -245,58 +245,146 @@ class AadhaarBackData(BaseModel):
245
 
246
  # โ”€โ”€ Endpoints โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
247
 
248
- @app.post("/extract-front", response_model=AadhaarFrontData)
249
  async def extract_front(file: UploadFile = File(...)):
250
  """
251
- Upload the FRONT side of an Aadhaar card image.
252
  Pipeline:
253
- 1. nemotron-ocr-v1 โ†’ raw OCR text
254
- 2. nvidia-nemotron-nano-9b-v2 โ†’ structured JSON
255
- Returns: name, dob, gender, aadhaar_no
 
256
  """
257
- ocr_text = await run_ocr(file)
258
-
259
- if not ocr_text.strip():
260
- raise HTTPException(status_code=422, detail="OCR produced no text. Check the image quality.")
261
-
262
- parsed = call_llm(ocr_text, FRONT_SYSTEM_PROMPT)
263
-
264
- raw_aadhaar = str(parsed.get("aadhaar_no", ""))
265
- aadhaar_digits = re.sub(r"\D", "", raw_aadhaar)
266
-
267
- return AadhaarFrontData(
268
- name=str(parsed.get("name", "")).strip()[:100],
269
- dob=str(parsed.get("dob", "")).strip()[:12],
270
- gender=str(parsed.get("gender", "")).strip()[:20],
271
- aadhaar_no=aadhaar_digits[:12],
272
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
 
275
- @app.post("/extract-back", response_model=AadhaarBackData)
276
  async def extract_back(file: UploadFile = File(...)):
277
  """
278
- Upload the BACK side of an Aadhaar card image.
279
  Pipeline:
280
- 1. nemotron-ocr-v1 โ†’ raw OCR text
281
- 2. nvidia-nemotron-nano-9b-v2 โ†’ structured JSON
282
- Returns: address, village_city, state, pincode
 
283
  """
284
- ocr_text = await run_ocr(file)
285
-
286
- if not ocr_text.strip():
287
- raise HTTPException(status_code=422, detail="OCR produced no text. Check the image quality.")
288
-
289
- parsed = call_llm(ocr_text, BACK_SYSTEM_PROMPT)
290
-
291
- raw_pin = str(parsed.get("pincode", ""))
292
- pin_digits = re.sub(r"\D", "", raw_pin)[:6]
293
-
294
- return AadhaarBackData(
295
- address=str(parsed.get("address", "")).strip()[:200],
296
- village_city=str(parsed.get("village_city", "")).strip()[:100],
297
- state=str(parsed.get("state", "")).strip()[:60],
298
- pincode=pin_digits,
299
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
 
301
 
302
  @app.post("/extract-pdf")
 
245
 
246
  # โ”€โ”€ Endpoints โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
247
 
248
+ @app.post("/extract-front")
249
  async def extract_front(file: UploadFile = File(...)):
250
  """
251
+ Upload the FRONT side of an Aadhaar card (image or PDF).
252
  Pipeline:
253
+ 1. If PDF: extract images from PDF
254
+ 2. For each image: nemotron-ocr-v1 โ†’ raw OCR text
255
+ 3. nvidia-nemotron-nano-9b-v2 โ†’ structured JSON
256
+ Returns: extracted front data (for single image) or list (for PDF with multiple pages)
257
  """
258
+ # Check if it's a PDF
259
+ if file.filename.lower().endswith('.pdf'):
260
+ images = await extract_images_from_pdf(file)
261
+ if not images:
262
+ raise HTTPException(status_code=422, detail="No images found in PDF")
263
+
264
+ results = []
265
+ for page_num, pil_image in enumerate(images):
266
+ page_result = {"page": page_num + 1}
267
+ try:
268
+ img_byte_arr = BytesIO()
269
+ pil_image.save(img_byte_arr, format='PNG')
270
+ img_byte_arr.seek(0)
271
+ image_b64 = base64.b64encode(img_byte_arr.getvalue()).decode()
272
+
273
+ if len(image_b64) >= 2_600_000:
274
+ page_result["error"] = "Image too large. Resize and try again."
275
+ results.append(page_result)
276
+ continue
277
+
278
+ # Create a temporary upload file from the image
279
+ temp_file = UploadFile(filename=f"page_{page_num}.png", file=BytesIO(img_byte_arr.getvalue()))
280
+
281
+ ocr_text = await run_ocr(temp_file)
282
+ if not ocr_text.strip():
283
+ page_result["error"] = "OCR produced no text. Check the image quality."
284
+ results.append(page_result)
285
+ continue
286
+
287
+ parsed = call_llm(ocr_text, FRONT_SYSTEM_PROMPT)
288
+ raw_aadhaar = str(parsed.get("aadhaar_no", ""))
289
+ aadhaar_digits = re.sub(r"\D", "", raw_aadhaar)
290
+
291
+ page_result["name"] = str(parsed.get("name", "")).strip()[:100]
292
+ page_result["dob"] = str(parsed.get("dob", "")).strip()[:12]
293
+ page_result["gender"] = str(parsed.get("gender", "")).strip()[:20]
294
+ page_result["aadhaar_no"] = aadhaar_digits[:12]
295
+ results.append(page_result)
296
+ except Exception as e:
297
+ page_result["error"] = str(e)
298
+ results.append(page_result)
299
+
300
+ return results
301
+ else:
302
+ # Handle single image
303
+ ocr_text = await run_ocr(file)
304
+ if not ocr_text.strip():
305
+ raise HTTPException(status_code=422, detail="OCR produced no text. Check the image quality.")
306
+
307
+ parsed = call_llm(ocr_text, FRONT_SYSTEM_PROMPT)
308
+ raw_aadhaar = str(parsed.get("aadhaar_no", ""))
309
+ aadhaar_digits = re.sub(r"\D", "", raw_aadhaar)
310
+
311
+ return AadhaarFrontData(
312
+ name=str(parsed.get("name", "")).strip()[:100],
313
+ dob=str(parsed.get("dob", "")).strip()[:12],
314
+ gender=str(parsed.get("gender", "")).strip()[:20],
315
+ aadhaar_no=aadhaar_digits[:12],
316
+ )
317
 
318
 
319
+ @app.post("/extract-back")
320
  async def extract_back(file: UploadFile = File(...)):
321
  """
322
+ Upload the BACK side of an Aadhaar card (image or PDF).
323
  Pipeline:
324
+ 1. If PDF: extract images from PDF
325
+ 2. For each image: nemotron-ocr-v1 โ†’ raw OCR text
326
+ 3. nvidia-nemotron-nano-9b-v2 โ†’ structured JSON
327
+ Returns: extracted back data (for single image) or list (for PDF with multiple pages)
328
  """
329
+ # Check if it's a PDF
330
+ if file.filename.lower().endswith('.pdf'):
331
+ images = await extract_images_from_pdf(file)
332
+ if not images:
333
+ raise HTTPException(status_code=422, detail="No images found in PDF")
334
+
335
+ results = []
336
+ for page_num, pil_image in enumerate(images):
337
+ page_result = {"page": page_num + 1}
338
+ try:
339
+ img_byte_arr = BytesIO()
340
+ pil_image.save(img_byte_arr, format='PNG')
341
+ img_byte_arr.seek(0)
342
+ image_b64 = base64.b64encode(img_byte_arr.getvalue()).decode()
343
+
344
+ if len(image_b64) >= 2_600_000:
345
+ page_result["error"] = "Image too large. Resize and try again."
346
+ results.append(page_result)
347
+ continue
348
+
349
+ # Create a temporary upload file from the image
350
+ temp_file = UploadFile(filename=f"page_{page_num}.png", file=BytesIO(img_byte_arr.getvalue()))
351
+
352
+ ocr_text = await run_ocr(temp_file)
353
+ if not ocr_text.strip():
354
+ page_result["error"] = "OCR produced no text. Check the image quality."
355
+ results.append(page_result)
356
+ continue
357
+
358
+ parsed = call_llm(ocr_text, BACK_SYSTEM_PROMPT)
359
+ raw_pin = str(parsed.get("pincode", ""))
360
+ pin_digits = re.sub(r"\D", "", raw_pin)[:6]
361
+
362
+ page_result["address"] = str(parsed.get("address", "")).strip()[:200]
363
+ page_result["village_city"] = str(parsed.get("village_city", "")).strip()[:100]
364
+ page_result["state"] = str(parsed.get("state", "")).strip()[:60]
365
+ page_result["pincode"] = pin_digits
366
+ results.append(page_result)
367
+ except Exception as e:
368
+ page_result["error"] = str(e)
369
+ results.append(page_result)
370
+
371
+ return results
372
+ else:
373
+ # Handle single image
374
+ ocr_text = await run_ocr(file)
375
+ if not ocr_text.strip():
376
+ raise HTTPException(status_code=422, detail="OCR produced no text. Check the image quality.")
377
+
378
+ parsed = call_llm(ocr_text, BACK_SYSTEM_PROMPT)
379
+ raw_pin = str(parsed.get("pincode", ""))
380
+ pin_digits = re.sub(r"\D", "", raw_pin)[:6]
381
+
382
+ return AadhaarBackData(
383
+ address=str(parsed.get("address", "")).strip()[:200],
384
+ village_city=str(parsed.get("village_city", "")).strip()[:100],
385
+ state=str(parsed.get("state", "")).strip()[:60],
386
+ pincode=pin_digits,
387
+ )
388
 
389
 
390
  @app.post("/extract-pdf")
index.html CHANGED
@@ -253,11 +253,11 @@
253
  <div class="grid">
254
  <!-- Image Upload - Front -->
255
  <div class="card">
256
- <h2>๐Ÿ“ธ Extract Front (Image)</h2>
257
  <form id="frontImageForm">
258
  <div class="form-group">
259
- <label for="frontImageFile">Upload Front Side Image</label>
260
- <input type="file" id="frontImageFile" accept="image/*" required>
261
  </div>
262
  <button type="submit">Extract Front Data</button>
263
  <div class="loading" id="frontImageLoading">
@@ -268,11 +268,11 @@
268
 
269
  <!-- Image Upload - Back -->
270
  <div class="card">
271
- <h2>๐Ÿ“ธ Extract Back (Image)</h2>
272
  <form id="backImageForm">
273
  <div class="form-group">
274
- <label for="backImageFile">Upload Back Side Image</label>
275
- <input type="file" id="backImageFile" accept="image/*" required>
276
  </div>
277
  <button type="submit">Extract Back Data</button>
278
  <div class="loading" id="backImageLoading">
 
253
  <div class="grid">
254
  <!-- Image Upload - Front -->
255
  <div class="card">
256
+ <h2>๐Ÿ“ธ Extract Front (Image or PDF)</h2>
257
  <form id="frontImageForm">
258
  <div class="form-group">
259
+ <label for="frontImageFile">Upload Front Side Image or PDF</label>
260
+ <input type="file" id="frontImageFile" accept="image/*,.pdf" required>
261
  </div>
262
  <button type="submit">Extract Front Data</button>
263
  <div class="loading" id="frontImageLoading">
 
268
 
269
  <!-- Image Upload - Back -->
270
  <div class="card">
271
+ <h2>๐Ÿ“ธ Extract Back (Image or PDF)</h2>
272
  <form id="backImageForm">
273
  <div class="form-group">
274
+ <label for="backImageFile">Upload Back Side Image or PDF</label>
275
+ <input type="file" id="backImageFile" accept="image/*,.pdf" required>
276
  </div>
277
  <button type="submit">Extract Back Data</button>
278
  <div class="loading" id="backImageLoading">