Midnightar commited on
Commit
b136c7f
·
verified ·
1 Parent(s): 78cdb93

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -78
app.py CHANGED
@@ -1,105 +1,124 @@
1
- # app.py
2
  import base64
3
  import cv2
4
  import numpy as np
5
  import requests
6
  from fastapi import FastAPI
7
  from pydantic import BaseModel
 
8
  import insightface
9
- import logging
10
 
11
- # ---------------------------
12
- # Reduce InsightFace logging
13
- # ---------------------------
14
- logging.getLogger("insightface").setLevel(logging.ERROR)
15
-
16
- # ---------------------------
17
- # Load face detector + recognition model
18
- # ---------------------------
19
  model = insightface.app.FaceAnalysis(name="buffalo_l")
20
- model.prepare(ctx_id=-1, det_size=(640, 640)) # CPU only, avoid CUDA warnings
21
 
22
- # ---------------------------
23
- # FastAPI app
24
- # ---------------------------
25
- app = FastAPI(title="Face Compare API")
26
 
27
- # ---------------------------
28
- # Request schema
29
- # ---------------------------
30
  class CompareRequest(BaseModel):
31
- image1: str # base64 string OR URL
32
- image2: str # base64 string OR URL
33
-
34
- # ---------------------------
35
- # Helper: Load image from base64 or URL
36
- # ---------------------------
37
- def load_image(input_str):
38
- if input_str.startswith("http://") or input_str.startswith("https://"):
39
- try:
40
- resp = requests.get(input_str, timeout=5)
41
- if resp.status_code != 200:
42
- return None
43
- arr = np.frombuffer(resp.content, np.uint8)
44
- img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
45
- return img
46
- except:
47
- return None
48
- else:
49
- try:
50
- if "," in input_str:
51
- b64 = input_str.split(",", 1)[1]
52
- else:
53
- b64 = input_str
54
- data = base64.b64decode(b64)
55
- arr = np.frombuffer(data, np.uint8)
56
- img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
57
- return img
58
- except:
59
- return None
60
-
61
- # ---------------------------
62
- # Helper: Get face embedding
63
- # ---------------------------
64
- def get_embedding(img):
65
  try:
66
- faces = model.get(img)
67
- if len(faces) == 0:
68
- return None
69
- return faces[0].embedding
70
  except:
71
  return None
72
 
73
- # ---------------------------
74
- # Helper: Cosine similarity
75
- # ---------------------------
76
- def cosine_similarity(a, b):
77
- a = np.array(a)
78
- b = np.array(b)
79
- denom = np.linalg.norm(a) * np.linalg.norm(b)
80
- if denom == 0:
81
- return 0.0
82
- return float(np.dot(a, b) / denom)
83
-
84
- # ---------------------------
85
- # API endpoint
86
- # ---------------------------
87
  @app.post("/compare")
88
  async def compare_faces(req: CompareRequest):
89
- img1 = load_image(req.image1)
90
- img2 = load_image(req.image2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
  if img1 is None or img2 is None:
93
- return {"error": "Cannot load one or both images"}
94
 
95
  emb1 = get_embedding(img1)
96
  emb2 = get_embedding(img2)
97
 
98
  if emb1 is None or emb2 is None:
99
- return {"error": "No face detected in one or both images"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- similarity = cosine_similarity(emb1, emb2)
102
- threshold = 0.55 # adjust threshold for stricter or looser matching
103
- match = similarity >= threshold
104
 
105
- return {"similarity": similarity, "match": match}
 
 
 
 
 
 
 
1
  import base64
2
  import cv2
3
  import numpy as np
4
  import requests
5
  from fastapi import FastAPI
6
  from pydantic import BaseModel
7
+ import uvicorn
8
  import insightface
9
+ import gradio as gr
10
 
11
+ # ---------- Load Face Detector + Recognition Model ----------
 
 
 
 
 
 
 
12
  model = insightface.app.FaceAnalysis(name="buffalo_l")
13
+ model.prepare(ctx_id=0, det_size=(640, 640))
14
 
15
+ # ---------- FastAPI App ----------
16
+ app = FastAPI()
 
 
17
 
18
+ # ---------- API Request Schema ----------
 
 
19
  class CompareRequest(BaseModel):
20
+ image1: str | None = None # base64
21
+ image2: str | None = None # base64
22
+ image1_url: str | None = None # URL
23
+ image2_url: str | None = None # URL
24
+
25
+ # ---------- Helper: Convert base64 to CV2 image ----------
26
+ def b64_to_img(b64_string):
27
+ try:
28
+ img_data = base64.b64decode(b64_string)
29
+ np_arr = np.frombuffer(img_data, np.uint8)
30
+ img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
31
+ return img
32
+ except:
33
+ return None
34
+
35
+ # ---------- Helper: Convert URL to CV2 image ----------
36
+ def url_to_img(url):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  try:
38
+ resp = requests.get(url, timeout=5)
39
+ np_arr = np.frombuffer(resp.content, np.uint8)
40
+ img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
41
+ return img
42
  except:
43
  return None
44
 
45
+ # ---------- Helper: Extract face embedding ----------
46
+ def get_embedding(img):
47
+ faces = model.get(img)
48
+ if len(faces) == 0:
49
+ return None
50
+ return faces[0].embedding # first detected face
51
+
52
+ # ---------- POST /compare API ----------
 
 
 
 
 
 
53
  @app.post("/compare")
54
  async def compare_faces(req: CompareRequest):
55
+
56
+ # ---- Load Image 1 ----
57
+ if req.image1:
58
+ img1 = b64_to_img(req.image1)
59
+ elif req.image1_url:
60
+ img1 = url_to_img(req.image1_url)
61
+ else:
62
+ img1 = None
63
+
64
+ # ---- Load Image 2 ----
65
+ if req.image2:
66
+ img2 = b64_to_img(req.image2)
67
+ elif req.image2_url:
68
+ img2 = url_to_img(req.image2_url)
69
+ else:
70
+ img2 = None
71
 
72
  if img1 is None or img2 is None:
73
+ return {"error": "Invalid image data or URL."}
74
 
75
  emb1 = get_embedding(img1)
76
  emb2 = get_embedding(img2)
77
 
78
  if emb1 is None or emb2 is None:
79
+ return {"error": "No face detected in one or both images."}
80
+
81
+ # Cosine similarity
82
+ similarity = np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2))
83
+
84
+ matched = similarity > 0.55 # threshold; adjust as needed
85
+
86
+ return {
87
+ "similarity": float(similarity),
88
+ "match": matched
89
+ }
90
+
91
+ # ---------- Gradio UI ----------
92
+ def gradio_ui(img1, img2):
93
+ import base64
94
+ import io
95
+
96
+ def to_b64(pil_img):
97
+ buf = io.BytesIO()
98
+ pil_img.save(buf, format="JPEG")
99
+ return base64.b64encode(buf.getvalue()).decode()
100
+
101
+ if img1 is None or img2 is None:
102
+ return "Upload both images."
103
+
104
+ # Convert to OpenCV
105
+ img1_cv = cv2.cvtColor(np.array(img1), cv2.COLOR_RGB2BGR)
106
+ img2_cv = cv2.cvtColor(np.array(img2), cv2.COLOR_RGB2BGR)
107
+
108
+ emb1 = get_embedding(img1_cv)
109
+ emb2 = get_embedding(img2_cv)
110
+
111
+ if emb1 is None or emb2 is None:
112
+ return "Face not detected."
113
+
114
+ similarity = np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2))
115
+ matched = similarity > 0.55
116
 
117
+ return f"Similarity: {similarity:.3f} | Match: {matched}"
 
 
118
 
119
+ gr.Interface(
120
+ fn=gradio_ui,
121
+ inputs=[gr.Image(), gr.Image()],
122
+ outputs="text",
123
+ title="Face Match API"
124
+ ).launch()