Spaces:
Sleeping
Sleeping
File size: 5,856 Bytes
1066715 b9bce82 86131c1 b9bce82 5a7d9c6 b9bce82 1066715 b9bce82 5a7d9c6 2e49730 1066715 86131c1 2e49730 b9bce82 1066715 5a7d9c6 b9bce82 1066715 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 8e5f0ef b9bce82 8e5f0ef b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 8e5f0ef b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 5a7d9c6 b9bce82 8e5f0ef b9bce82 5a7d9c6 8e5f0ef b9bce82 86131c1 5a7d9c6 b9bce82 86131c1 5a7d9c6 86131c1 5a7d9c6 b9bce82 5a7d9c6 86131c1 5a7d9c6 86131c1 5a7d9c6 b9bce82 86131c1 8e5f0ef 86131c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | import os
import json
import tempfile
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing import image
from pdf2image import convert_from_path
from fastapi import FastAPI, UploadFile, Form, HTTPException, Header, Depends
from fastapi.responses import JSONResponse
# -----------------------------
# CONFIG
# -----------------------------
IMG_SIZE = (224, 224)
MODEL_PATH = "./final_model.keras"
class_names = ['Other', 'Aadhaar Card', 'Pan Card', 'Voter Id']
type_mapping = {
"aadhaar_card": "Aadhaar Card",
"pan_card": "Pan Card",
"voter_id": "Voter Id"
}
# -----------------------------
# AUTH CONFIG (HF SECRETS)
# -----------------------------
MASTER_SECRET_KEY = os.getenv("MASTER_SECRET_KEY")
PROJECT_KEYS_JSON = os.getenv("PROJECT_KEYS_JSON", "{}")
try:
PROJECT_KEYS = json.loads(PROJECT_KEYS_JSON)
except Exception:
PROJECT_KEYS = {}
# -----------------------------
# AUTH VALIDATION
# -----------------------------
def verify_keys(
x_secret_key: str = Header(None),
x_project_id: str = Header(None),
x_project_key: str = Header(None),
):
# Server configuration check
if not MASTER_SECRET_KEY:
raise HTTPException(status_code=500, detail="MASTER_SECRET_KEY not configured in Space secrets")
# 1) Validate master key
if not x_secret_key or x_secret_key != MASTER_SECRET_KEY:
raise HTTPException(status_code=401, detail="Invalid Secret Key")
# 2) Validate project key
if not x_project_id or not x_project_key:
raise HTTPException(status_code=401, detail="Project Id and Project Key are required")
if x_project_id not in PROJECT_KEYS:
raise HTTPException(status_code=401, detail=f"Unknown project: {x_project_id}")
if PROJECT_KEYS.get(x_project_id) != x_project_key:
raise HTTPException(status_code=401, detail="Invalid Project Key")
return True
# -----------------------------
# LOAD MODEL
# -----------------------------
model = tf.keras.models.load_model(MODEL_PATH)
# -----------------------------
# Predict a single image array (H,W,C)
# -----------------------------
def predict_array(img_array):
img_array = tf.cast(img_array, tf.float32)
img_array = tf.image.resize(img_array, IMG_SIZE)
img_array = tf.expand_dims(img_array, axis=0)
pred = model.predict(img_array, verbose=0)[0]
class_id = int(np.argmax(pred))
conf = float(np.max(pred))
return class_names[class_id], conf, pred
# -----------------------------
# Predict from image path
# -----------------------------
def predict_image(img_path):
img = image.load_img(img_path, target_size=IMG_SIZE, color_mode="rgb")
img_array = image.img_to_array(img)
label, conf, _ = predict_array(img_array)
return {
"type": label,
"confidence": round(conf, 6)
}
# -----------------------------
# Predict from PDF (max 2 pages only)
# -----------------------------
def predict_pdf(pdf_path, dpi=200, max_pages=2):
pages = convert_from_path(pdf_path, dpi=dpi)
if len(pages) > max_pages:
return {
"error": "Maximum page limit reached",
"max_pages": max_pages,
"found_pages": len(pages)
}
results = []
labels = []
for page in pages:
page_np = np.array(page)
# RGBA -> RGB
if page_np.shape[-1] == 4:
page_np = page_np[..., :3]
label, conf, _ = predict_array(page_np)
results.append((label, conf))
labels.append(label)
# 2 pages validation
if len(labels) == 2:
if labels[0] != labels[1]:
return {
"error": "Invalid input",
"reason": "Pages belong to different document types",
"page1_type": labels[0],
"page2_type": labels[1]
}
final_label = labels[0]
final_conf = float(max(results[0][1], results[1][1]))
return {
"type": final_label,
"confidence": round(final_conf, 6)
}
# 1 page output
return {
"type": labels[0],
"confidence": round(float(results[0][1]), 6)
}
# -----------------------------
# Main function
# -----------------------------
def predict_file(path):
if not os.path.exists(path):
return {"error": "File not found", "path": path}
ext = os.path.splitext(path)[1].lower()
if ext in [".png", ".jpg", ".jpeg", ".bmp", ".webp"]:
return predict_image(path)
if ext == ".pdf":
return predict_pdf(path)
return {"error": "Unsupported file type", "ext": ext}
# -----------------------------
# FastAPI App
# -----------------------------
app = FastAPI(
title="ID Document Validator",
description="Predict document type and authenticate based on expected type."
)
@app.post("/predict")
async def predict(
file: UploadFile,
expected_type: str = Form(None),
auth: bool = Depends(verify_keys) # ✅ Authentication required
):
if not file.filename:
raise HTTPException(status_code=400, detail="No file provided")
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as temp_file:
temp_path = temp_file.name
temp_file.write(await file.read())
try:
result = predict_file(temp_path)
# authentication based on expected_type
if expected_type and expected_type in type_mapping and "type" in result:
if result["type"] == type_mapping[expected_type]:
result["Authentication"] = "Valid"
else:
result["Authentication"] = "Not valid"
return JSONResponse(content=result)
finally:
if os.path.exists(temp_path):
os.unlink(temp_path)
@app.get("/")
def read_root():
return {"message": "ID Validator API is running"} |