jjunyuongv commited on
Commit
7db31a5
·
verified ·
1 Parent(s): 8f2a110
Files changed (4) hide show
  1. README.md +108 -1
  2. handler.py +231 -0
  3. insightface-endpoint.zip +3 -0
  4. requirements.txt +6 -0
README.md CHANGED
@@ -1,3 +1,110 @@
1
  ---
2
- license: apache-2.0
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ license: mit
3
+ tags:
4
+ - face-recognition
5
+ - face-embedding
6
+ - face-similarity
7
+ - insightface
8
+ - computer-vision
9
+ pipeline_tag: feature-extraction
10
  ---
11
+
12
+ # InsightFace Endpoint Model
13
+
14
+ InsightFace를 사용한 얼굴 임베딩 추출 및 유사도 계산 모델입니다.
15
+
16
+ ## 모델 설명
17
+
18
+ 이 모델은 다음 두 가지 주요 기능을 제공합니다:
19
+
20
+ 1. **얼굴 감지 및 임베딩 추출 (Face Detection & Embedding)**: `buffalo_l` 모델을 사용하여 이미지에서 얼굴을 감지하고 특징 벡터(임베딩)를 추출합니다.
21
+ 2. **얼굴 유사도 계산 (Face Similarity)**: 두 얼굴의 임베딩 벡터 간 코사인 유사도를 계산하여 얼굴이 같은 사람인지 판단합니다.
22
+
23
+ ## 모델 파일
24
+
25
+ - `buffalo_l/`: 얼굴 감지 및 임베딩 추출 모델 폴더
26
+ - `det_10g.onnx`: 얼굴 감지 모델
27
+ - `genderage.onnx`: 성별/나이 추정 모델
28
+ - `w600k_r50.onnx`: 얼굴 랜드마크 및 임베딩 추출 모델
29
+
30
+ ## 사용 방법
31
+
32
+ ### Inference Endpoint API
33
+
34
+ 이 모델은 HuggingFace Inference Endpoint로 배포되어 사용됩니다.
35
+
36
+ #### 얼굴 감지 및 임베딩 추출
37
+
38
+ ```python
39
+ import requests
40
+
41
+ response = requests.post(
42
+ "https://your-endpoint-url.hf.space",
43
+ headers={"Authorization": "Bearer YOUR_TOKEN"},
44
+ json={
45
+ "task": "face-detect",
46
+ "image": "data:image/png;base64,..."
47
+ }
48
+ )
49
+
50
+ # 응답 형식:
51
+ # {
52
+ # "faces": [
53
+ # {
54
+ # "bbox": [x1, y1, x2, y2],
55
+ # "embedding": [0.123, 0.456, ...], # 512차원 벡터
56
+ # "det_score": 0.99,
57
+ # "gender": 0, # 0: 여성, 1: 남성
58
+ # "age": 25
59
+ # }
60
+ # ]
61
+ # }
62
+ ```
63
+
64
+ #### 얼굴 유사도 계산
65
+
66
+ ```python
67
+ response = requests.post(
68
+ "https://your-endpoint-url.hf.space",
69
+ headers={"Authorization": "Bearer YOUR_TOKEN"},
70
+ json={
71
+ "task": "face-similarity",
72
+ "image1": "data:image/png;base64,...",
73
+ "image2": "data:image/png;base64,...",
74
+ "face_index1": 0,
75
+ "face_index2": 0
76
+ }
77
+ )
78
+
79
+ # 응답 형식:
80
+ # {
81
+ # "similarity": 0.85, # 0.0 ~ 1.0 (1.0이 가장 유사)
82
+ # "embedding1": [0.123, 0.456, ...],
83
+ # "embedding2": [0.234, 0.567, ...],
84
+ # "face1_info": {"bbox": [...], "det_score": 0.99, ...},
85
+ # "face2_info": {"bbox": [...], "det_score": 0.98, ...}
86
+ # }
87
+ ```
88
+
89
+ ## 요구사항
90
+
91
+ - Python 3.8+
92
+ - ONNX Runtime
93
+ - InsightFace 라이브러리
94
+
95
+ ## 라이선스
96
+
97
+ 이 모델은 원본 InsightFace 모델의 라이선스를 따릅니다.
98
+
99
+ ## 사용 사례
100
+
101
+ - 얼굴 인증 및 검증
102
+ - 동일인 판별
103
+ - 얼굴 기반 검색
104
+ - 출입 통제 시스템
105
+
106
+ ## 참고 자료
107
+
108
+ - [InsightFace 공식 사이트](https://www.insightface.ai/)
109
+ - [InsightFace GitHub](https://github.com/deepinsight/insightface)
110
+
handler.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace Inference Endpoint용 InsightFace 핸들러
3
+ 얼굴 임베딩 추출 및 유사도 계산용
4
+ """
5
+ import base64
6
+ import io
7
+ from typing import Dict, Any, Optional
8
+ from PIL import Image
9
+ import numpy as np
10
+ import insightface
11
+
12
+
13
+ class EndpointHandler:
14
+ def __init__(self, path=""):
15
+ """모델 초기화"""
16
+ self.face_analyzer = None
17
+ self._load_models(path)
18
+
19
+ def _load_models(self, path: str):
20
+ """모델 로드"""
21
+ try:
22
+ # InsightFace FaceAnalysis 초기화 (얼굴 감지 및 임베딩 추출용)
23
+ self.face_analyzer = insightface.app.FaceAnalysis(
24
+ name='buffalo_l',
25
+ root=path, # 모델 경로
26
+ providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
27
+ )
28
+ self.face_analyzer.prepare(ctx_id=0, det_size=(640, 640))
29
+
30
+ print("✅ InsightFace 모델 로드 완료 (얼굴 임베딩 추출용)")
31
+ except Exception as e:
32
+ print(f"❌ 모델 로드 실패: {e}")
33
+ raise
34
+
35
+ def _base64_to_image(self, base64_str: str) -> Image.Image:
36
+ """base64 문자열을 이미지로 변환"""
37
+ if "," in base64_str:
38
+ base64_str = base64_str.split(",")[1]
39
+
40
+ img_bytes = base64.b64decode(base64_str)
41
+ return Image.open(io.BytesIO(img_bytes))
42
+
43
+ def _image_to_base64(self, image: Image.Image) -> str:
44
+ """이미지를 base64 문자열로 변환"""
45
+ buffer = io.BytesIO()
46
+ image.save(buffer, format="PNG")
47
+ img_bytes = buffer.getvalue()
48
+ return base64.b64encode(img_bytes).decode("utf-8")
49
+
50
+ def _pil_to_numpy(self, image: Image.Image) -> np.ndarray:
51
+ """PIL Image를 BGR numpy 배열로 변환"""
52
+ return np.array(image.convert('RGB'))[:, :, ::-1]
53
+
54
+ def _numpy_to_pil(self, image: np.ndarray) -> Image.Image:
55
+ """BGR numpy 배열을 PIL Image로 변환"""
56
+ rgb = image[:, :, ::-1] # BGR -> RGB
57
+ return Image.fromarray(rgb)
58
+
59
+ def face_detect(self, data: Dict[str, Any]) -> Dict[str, Any]:
60
+ """
61
+ 얼굴 감지
62
+
63
+ Args:
64
+ data: {"image": "data:image/png;base64,..."}
65
+
66
+ Returns:
67
+ {"faces": [{"bbox": [x1, y1, x2, y2], "embedding": [...], ...}]}
68
+ """
69
+ try:
70
+ image_b64 = data.get("image", "")
71
+ if not image_b64:
72
+ return {"error": "이미지가 제공되지 않았습니다"}
73
+
74
+ # 이미지 변환
75
+ image = self._base64_to_image(image_b64)
76
+ image_np = self._pil_to_numpy(image)
77
+
78
+ # 얼굴 감지
79
+ faces = self.face_analyzer.get(image_np)
80
+
81
+ # 결과 변환
82
+ faces_data = []
83
+ for face in faces:
84
+ face_data = {
85
+ "bbox": face.bbox.tolist(),
86
+ "kps": face.kps.tolist() if hasattr(face, 'kps') and face.kps is not None else None,
87
+ "embedding": face.embedding.tolist() if hasattr(face, 'embedding') and face.embedding is not None else None,
88
+ "det_score": float(face.det_score) if hasattr(face, 'det_score') else None,
89
+ "gender": int(face.gender) if hasattr(face, 'gender') else None,
90
+ "age": int(face.age) if hasattr(face, 'age') else None,
91
+ }
92
+ faces_data.append(face_data)
93
+
94
+ return {"faces": faces_data}
95
+
96
+ except Exception as e:
97
+ return {"error": str(e)}
98
+
99
+ def face_similarity(self, data: Dict[str, Any]) -> Dict[str, Any]:
100
+ """
101
+ 두 얼굴 간 유사도 계산 (코사인 유사도)
102
+
103
+ Args:
104
+ data: {
105
+ "image1": "data:image/png;base64,...",
106
+ "image2": "data:image/png;base64,...",
107
+ "face_index1": 0, # image1에서 사용할 얼굴 인덱스
108
+ "face_index2": 0 # image2에서 사용할 얼굴 인덱스
109
+ }
110
+
111
+ Returns:
112
+ {
113
+ "similarity": float, # 0.0 ~ 1.0 (1.0이 가장 유사)
114
+ "embedding1": [...], # 첫 번째 얼굴 임베딩
115
+ "embedding2": [...], # 두 번째 얼굴 임베딩
116
+ "face1_info": {...}, # 첫 번째 얼굴 정보
117
+ "face2_info": {...} # 두 번째 얼굴 정보
118
+ }
119
+ """
120
+ try:
121
+ image1_b64 = data.get("image1", "")
122
+ image2_b64 = data.get("image2", "")
123
+ face_index1 = data.get("face_index1", 0)
124
+ face_index2 = data.get("face_index2", 0)
125
+
126
+ if not image1_b64 or not image2_b64:
127
+ return {"error": "이미지1 또는 이미지2가 제��되지 않았습니다"}
128
+
129
+ # 이미지 변환
130
+ image1 = self._base64_to_image(image1_b64)
131
+ image2 = self._base64_to_image(image2_b64)
132
+
133
+ image1_np = self._pil_to_numpy(image1)
134
+ image2_np = self._pil_to_numpy(image2)
135
+
136
+ # 얼굴 감지
137
+ faces1 = self.face_analyzer.get(image1_np)
138
+ faces2 = self.face_analyzer.get(image2_np)
139
+
140
+ if len(faces1) == 0:
141
+ return {"error": "이미지1에서 얼굴을 찾을 수 없습니다"}
142
+
143
+ if len(faces2) == 0:
144
+ return {"error": "이미지2에서 얼굴을 찾을 수 없습니다"}
145
+
146
+ # 얼굴 선택
147
+ if face_index1 >= len(faces1):
148
+ face_index1 = 0
149
+ if face_index2 >= len(faces2):
150
+ face_index2 = 0
151
+
152
+ face1 = faces1[face_index1]
153
+ face2 = faces2[face_index2]
154
+
155
+ # 임베딩 추출
156
+ if not hasattr(face1, 'embedding') or face1.embedding is None:
157
+ return {"error": "이미지1의 얼굴에서 임베딩을 추출할 수 없습니다"}
158
+
159
+ if not hasattr(face2, 'embedding') or face2.embedding is None:
160
+ return {"error": "이미지2의 얼굴에서 임베딩을 추출할 수 없습니다"}
161
+
162
+ emb1 = face1.embedding
163
+ emb2 = face2.embedding
164
+
165
+ # 코사인 유사도 계산
166
+ # similarity = dot(emb1, emb2) / (norm(emb1) * norm(emb2))
167
+ emb1_norm = np.linalg.norm(emb1)
168
+ emb2_norm = np.linalg.norm(emb2)
169
+
170
+ if emb1_norm == 0 or emb2_norm == 0:
171
+ return {"error": "임베딩 벡터의 크기가 0입니다"}
172
+
173
+ # 코사인 유사도 계산
174
+ # InsightFace 임베딩은 이미 L2 정규화되어 있으므로 내적만으로 유사도 계산 가능
175
+ similarity = np.dot(emb1, emb2) / (emb1_norm * emb2_norm)
176
+ # 코사인 유사도는 -1 ~ 1 범위이지만, 얼굴 임베딩은 보통 0 ~ 1 범위
177
+ # 음수 값이 나올 수 있으므로 0 ~ 1로 정규화 (선택사항)
178
+ # 실제로는 InsightFace 임베딩이 정규화되어 있어 0 ~ 1 범위가 일반적
179
+ similarity = max(0.0, min(1.0, similarity))
180
+
181
+ # 얼굴 정보 추출
182
+ face1_info = {
183
+ "bbox": face1.bbox.tolist(),
184
+ "det_score": float(face1.det_score) if hasattr(face1, 'det_score') else None,
185
+ "gender": int(face1.gender) if hasattr(face1, 'gender') else None,
186
+ "age": int(face1.age) if hasattr(face1, 'age') else None,
187
+ }
188
+
189
+ face2_info = {
190
+ "bbox": face2.bbox.tolist(),
191
+ "det_score": float(face2.det_score) if hasattr(face2, 'det_score') else None,
192
+ "gender": int(face2.gender) if hasattr(face2, 'gender') else None,
193
+ "age": int(face2.age) if hasattr(face2, 'age') else None,
194
+ }
195
+
196
+ return {
197
+ "similarity": float(similarity),
198
+ "embedding1": emb1.tolist(),
199
+ "embedding2": emb2.tolist(),
200
+ "face1_info": face1_info,
201
+ "face2_info": face2_info
202
+ }
203
+
204
+ except Exception as e:
205
+ return {"error": str(e)}
206
+
207
+ def __call__(self, data: Dict[str, Any]) -> Dict[str, Any]:
208
+ """
209
+ 메인 엔드포인트
210
+
211
+ Args:
212
+ data: {
213
+ "task": "face-detect" | "face-similarity",
214
+ "image": "data:image/png;base64,..." (face-detect용),
215
+ "image1": "data:image/png;base64,..." (face-similarity용),
216
+ "image2": "data:image/png;base64,..." (face-similarity용),
217
+ ...
218
+ }
219
+
220
+ Returns:
221
+ 작업 결과
222
+ """
223
+ task = data.get("task", "")
224
+
225
+ if task == "face-detect":
226
+ return self.face_detect(data)
227
+ elif task == "face-similarity":
228
+ return self.face_similarity(data)
229
+ else:
230
+ return {"error": f"알 수 없는 작업: {task}. 지원 작업: 'face-detect', 'face-similarity'"}
231
+
insightface-endpoint.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d5f26902433c1f6d1f9f9a027fa48bd1ae691bd991feb7b8a15186db695ee02c
3
+ size 177212354
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ insightface>=0.7.3
2
+ onnxruntime>=1.16.0
3
+ pillow>=10.0.0
4
+ numpy>=1.24.0
5
+ opencv-python>=4.8.0
6
+