PrathameshRaut commited on
Commit
b9bce82
·
verified ·
1 Parent(s): 35b4b48

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +141 -37
main.py CHANGED
@@ -1,48 +1,152 @@
1
- # app.py
2
- from fastapi import FastAPI, UploadFile, File, Form, HTTPException
3
- from fastapi.responses import JSONResponse
4
- from predictor import predict_file
5
- import shutil
6
  import os
7
- import uuid
8
- import tempfile
 
 
 
 
 
 
9
 
10
- app = FastAPI(title="Indian ID Document Classifier API")
 
 
 
 
 
11
 
12
- ALLOWED_TYPES = {"Aadhar Card", "Pan Card", "Voter Id"}
 
 
 
 
 
13
 
 
 
 
 
14
 
15
- @app.post("/predict")
16
- async def predict_document(
17
- file: UploadFile = File(...),
18
- expected_type: str | None = Form(None),
19
- ):
20
- with tempfile.TemporaryDirectory() as temp_dir:
21
- file_ext = os.path.splitext(file.filename)[1].lower()
22
- temp_path = os.path.join(temp_dir, f"upload_{uuid.uuid4()}{file_ext}")
 
 
 
 
23
 
24
- try:
25
- with open(temp_path, "wb") as buffer:
26
- shutil.copyfileobj(file.file, buffer)
 
 
 
 
 
 
 
 
27
 
28
- result = predict_file(temp_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- # Authentication field logic
31
- if expected_type is not None:
32
- expected = expected_type.strip()
33
- if expected in ALLOWED_TYPES:
34
- predicted = result.get("type")
35
- if isinstance(predicted, str) and predicted == expected:
36
- result["Authentication"] = "Valid"
37
- else:
38
- result["Authentication"] = "Not valid"
 
 
 
39
 
40
- return JSONResponse(content=result)
41
-
42
- except Exception as e:
43
- raise HTTPException(status_code=500, detail=f"Server error: {str(e)}")
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
- @app.get("/health")
47
- async def health_check():
48
- return {"status": "ok"}
 
 
 
 
 
 
1
  import os
2
+ import json
3
+ import numpy as np
4
+ import tensorflow as tf
5
+ from tensorflow.keras.preprocessing import image
6
+ from pdf2image import convert_from_path
7
+ from fastapi import FastAPI, UploadFile, File, HTTPException, Query
8
+ from fastapi.responses import JSONResponse
9
+ import uvicorn
10
 
11
+ # -----------------------------
12
+ # CONFIG
13
+ # -----------------------------
14
+ IMG_SIZE = (224, 224)
15
+ MODEL_PATH = "final_model (2).keras" # Updated to relative path for Docker
16
+ class_names = ['Other', 'Aadhar Card', 'Pan Card', 'Voter Id']
17
 
18
+ # Mapping for expected_type input (normalize to match class_names)
19
+ EXPECTED_TYPE_MAPPING = {
20
+ "aadhar_card": "Aadhar Card",
21
+ "pan_card": "Pan Card",
22
+ "voter_id": "Voter Id"
23
+ }
24
 
25
+ # -----------------------------
26
+ # LOAD MODEL
27
+ # -----------------------------
28
+ model = tf.keras.models.load_model(MODEL_PATH)
29
 
30
+ # -----------------------------
31
+ # Predict a single image array (H,W,C)
32
+ # -----------------------------
33
+ def predict_array(img_array):
34
+ img_array = tf.cast(img_array, tf.float32)
35
+ img_array = tf.image.resize(img_array, IMG_SIZE)
36
+ # DO NOT divide by 255.0 (training did not use Rescaling)
37
+ img_array = tf.expand_dims(img_array, axis=0)
38
+ pred = model.predict(img_array, verbose=0)[0]
39
+ class_id = int(np.argmax(pred))
40
+ conf = float(np.max(pred))
41
+ return class_names[class_id], conf, pred
42
 
43
+ # -----------------------------
44
+ # Predict from image path
45
+ # -----------------------------
46
+ def predict_image(img_path):
47
+ img = image.load_img(img_path, target_size=IMG_SIZE, color_mode="rgb")
48
+ img_array = image.img_to_array(img)
49
+ label, conf, _ = predict_array(img_array)
50
+ return {
51
+ "type": label,
52
+ "confidence": round(conf, 6)
53
+ }
54
 
55
+ # -----------------------------
56
+ # Predict from PDF (max 2 pages only)
57
+ # -----------------------------
58
+ def predict_pdf(pdf_path, dpi=200, max_pages=2):
59
+ pages = convert_from_path(pdf_path, dpi=dpi)
60
+ if len(pages) > max_pages:
61
+ return {
62
+ "error": "Maximum page limit reached",
63
+ "max_pages": max_pages,
64
+ "found_pages": len(pages)
65
+ }
66
+ results = []
67
+ labels = []
68
+ for page in pages:
69
+ page_np = np.array(page)
70
+ # RGBA -> RGB
71
+ if page_np.shape[-1] == 4:
72
+ page_np = page_np[..., :3]
73
+ label, conf, _ = predict_array(page_np)
74
+ results.append((label, conf))
75
+ labels.append(label)
76
+ # 2 pages validation
77
+ if len(labels) == 2:
78
+ if labels[0] != labels[1]:
79
+ return {
80
+ "error": "Invalid input",
81
+ "reason": "Pages belong to different document types",
82
+ "page1_type": labels[0],
83
+ "page2_type": labels[1]
84
+ }
85
+ # both same -> return single output
86
+ final_label = labels[0]
87
+ final_conf = float(max(results[0][1], results[1][1]))
88
+ return {
89
+ "type": final_label,
90
+ "confidence": round(final_conf, 6)
91
+ }
92
+ # 1-page pdf normal output
93
+ return {
94
+ "type": labels[0],
95
+ "confidence": round(float(results[0][1]), 6)
96
+ }
97
 
98
+ # -----------------------------
99
+ # Main function
100
+ # -----------------------------
101
+ def predict_file(path):
102
+ if not os.path.exists(path):
103
+ return {"error": "File not found", "path": path}
104
+ ext = os.path.splitext(path)[1].lower()
105
+ if ext in [".png", ".jpg", ".jpeg", ".bmp", ".webp"]:
106
+ return predict_image(path)
107
+ if ext == ".pdf":
108
+ return predict_pdf(path)
109
+ return {"error": "Unsupported file type", "ext": ext}
110
 
111
+ # -----------------------------
112
+ # FastAPI App
113
+ # -----------------------------
114
+ app = FastAPI(title="ID Document Validator", description="Predict document type and optionally validate against expected type.")
115
 
116
+ @app.post("/predict")
117
+ async def predict(
118
+ file: UploadFile = File(...),
119
+ expected_type: str = Query(None, description="Optional: Expected document type ('aadhar_card', 'pan_card', 'voter_id'). If provided, adds 'Authentication' to response.")
120
+ ):
121
+ # Save uploaded file temporarily
122
+ temp_path = f"/tmp/{file.filename}"
123
+ with open(temp_path, "wb") as f:
124
+ f.write(await file.read())
125
+
126
+ try:
127
+ # Get prediction result
128
+ result = predict_file(temp_path)
129
+
130
+ # If there's an error in result, return it as-is
131
+ if "error" in result:
132
+ return JSONResponse(status_code=400, content=result)
133
+
134
+ # If expected_type is provided and valid, add Authentication
135
+ if expected_type and expected_type in EXPECTED_TYPE_MAPPING:
136
+ expected_label = EXPECTED_TYPE_MAPPING[expected_type]
137
+ predicted_label = result["type"]
138
+ authentication = "Valid" if predicted_label == expected_label else "Not Valid"
139
+ result["Authentication"] = authentication
140
+ elif expected_type and expected_type not in EXPECTED_TYPE_MAPPING:
141
+ # Invalid expected_type, but still return prediction without Authentication
142
+ pass # Just proceed without adding Authentication
143
+
144
+ return result
145
+ finally:
146
+ # Clean up temp file
147
+ if os.path.exists(temp_path):
148
+ os.remove(temp_path)
149
 
150
+ # For local testing
151
+ if __name__ == "__main__":
152
+ uvicorn.run(app, host="0.0.0.0", port=8000)