second commit the RaceRegWeb

This commit is contained in:
DiaosWang 2024-08-02 12:04:38 +08:00
parent f22e518c25
commit 6b0aeb816d
35 changed files with 139 additions and 56 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@
!/readme.md !/readme.md
!/componentdevelopment/ !/componentdevelopment/
!/FaceRegWeb/ !/FaceRegWeb/
!/FaceRegWeb5.2/

View File

@ -47,27 +47,30 @@ for i in range(NUM_WORKERS):
rw_lock = RedisReadWriteLock(redis_client, max_readers=max_readers) rw_lock = RedisReadWriteLock(redis_client, max_readers=max_readers)
ErrorMsg = { ErrorMsg = {
"101":"no face in the database",
"102":"invalid file path",
"103":"invaild file suffix",
"201":"input file type is not support, only support: base64, url, local-path", "30003": "no face found in the database",
"202":"this updateDB type is not support", "30004": "invalid file path",
"30005": "invalid file suffix",
"300":"deal the image successfully.", "30006": "unsupported input file type; only supports: base64, URL, local path",
"301":"no face in the image.", "30007": "unsupported type for database update",
"302":"too many face in the image.",
"303":"bad bright of face in the image.",
"3041":"face shifted left/right, partially not captured.",
"3042":"face shifted top/bottom, partially not captured.",
"3051":"rightFace in the image.",
"3052":"leftFace in the image.",
"3053":"upFace in the image.",
"3054":"downFace in the image.",
"306":"too small resolution of face in the image.",
"307":"bad clarity of face in the image.",
"401":"identity has exist. to pretect the db, reject opt of this time ", "30000": "facial recognition of a known individual",
"30001": "facial recognition of an unknown individual",
"30002": "successful image processing",
"30018": "no face detected in the image",
"30008": "multiple faces found in the image",
"30009": "poor bright conditions affecting facial quality in the image",
"30010": "face partially out of frame, shifted left or right",
"30011": "face partially out of frame, shifted top or bottom",
"30012": "face rotated to the right in the image",
"30013": "face rotated to the left in the image",
"30014": "face rotated upwards in the image",
"30015": "face rotated downwards in the image",
"30016": "low resolution of a face in the image",
"30017": "poor clarity of a face in the image",
"30019": "identity already exists; for database protection, operation rejected at this time"
} }
class FileError(Exception): class FileError(Exception):
@ -158,9 +161,9 @@ def load_img(img_path:str):
image = cv2.imdecode(image_array, cv2.IMREAD_COLOR) image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
elif img_path.startswith(("./","/","C:","D:","E:",".\\")) or os.path.isfile(img_path): # local-path elif img_path.startswith(("./","/","C:","D:","E:",".\\")) or os.path.isfile(img_path): # local-path
if not os.path.isfile(img_path): if not os.path.isfile(img_path):
raise FileError("102") # push: invalid file path raise FileError("30004") # push: invalid file path
elif not img_path.lower().endswith((".jpg",'.jpeg','.png')): elif not img_path.lower().endswith((".jpg",'.jpeg','.png')):
raise FileError("103") # push: invaild file suffix raise FileError("30005") # push: invaild file suffix
else: else:
image = cv2.imread(img_path) image = cv2.imread(img_path)
elif img_path.startswith("data:") and "base64" in img_path: # base64 elif img_path.startswith("data:") and "base64" in img_path: # base64
@ -172,7 +175,7 @@ def load_img(img_path:str):
nparr = np.fromstring(base64.b64decode(encoded_data), np.uint8) nparr = np.fromstring(base64.b64decode(encoded_data), np.uint8)
image = cv2.imdecode(nparr, cv2.IMREAD_COLOR) image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
else: else:
raise NotImpltError("201") # push: input file type is not support, only support: base64, url, local-path raise NotImpltError("30006") # push: input file type is not support, only support: base64, url, local-path
except Exception as e: except Exception as e:
logger.info(f"{e}") logger.info(f"{e}")
raise e raise e
@ -248,7 +251,7 @@ class FaceHelper:
try: try:
if self.db_embeddings is None: if self.db_embeddings is None:
raise FileError("101") # push: no face in the database raise FileError("30003") # push: no face in the database
image = load_img(img_path) # get bgr numpy image image = load_img(img_path) # get bgr numpy image
@ -257,14 +260,10 @@ class FaceHelper:
image = self.rotadjust(image) # 调整角度 image = self.rotadjust(image) # 调整角度
detect_result = self.fd.detect(image) detect_result = self.fd.detect(image)
detect_result = [(box.x1, box.y1, box.x2, box.y2) for box in detect_result] detect_result = [(box.x1, box.y1, box.x2, box.y2) for box in detect_result]
rett = {'code':" ", 'msg':" ", 'data':" "}
if len(detect_result) == 0: if len(detect_result) == 0:
logger.info(f"{img_path[:200]}: no face in the image") logger.info(f"{img_path[:200]}: no face in the image")
print(f"{img_path[:200]}: no face in the image") print(f"{img_path[:200]}: no face in the image")
raise FaceError("301") # push: no face in the image raise FaceError("30018") # push: no face in the image
elif len(detect_result) > 1:
rett['code'] = '302'
rett['data'] = {'code':" ", 'msg':" ", 'data':" "}
for facebox in detect_result: for facebox in detect_result:
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角 landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
# print("5点关键点",landmarks5) # print("5点关键点",landmarks5)
@ -291,12 +290,15 @@ class FaceHelper:
ret_data = [] ret_data = []
for i, (facebox, name) in enumerate(zip(detect_result, names)): for i, (facebox, name) in enumerate(zip(detect_result, names)):
if name != 'unknown': if name != 'unknown':
ret_data.append({'code':"300", 'msg': ErrorMsg["300"], 'data':name}) ret_data.append({'code':"30000", 'msg': ErrorMsg["30000"], 'data':[name,facebox]})
else: else:
code = self.check_face("None", image, facebox, prefix='facereg') code = self.check_face("None", image, facebox, prefix='facereg')
ret_data.append({'code':code, 'msg': ErrorMsg[code], 'data':name}) if code == "30002":
ret_data.append({'code':"30001", 'msg': ErrorMsg["30001"], 'data':[name,facebox]})
else:
ret_data.append({'code':code, 'msg': ErrorMsg[code], 'data':[name,facebox]})
if len(ret_data) != 1: if len(ret_data) != 1:
ret_data = {'code':"302", 'msg': ErrorMsg["302"], 'data': ret_data} ret_data = {'code':"30008", 'msg': ErrorMsg["30008"], 'data': ret_data}
else: else:
ret_data = ret_data[0] ret_data = ret_data[0]
@ -311,6 +313,49 @@ class FaceHelper:
return ret_data return ret_data
# return names, [ encoder_img2base64(det) for det in cropped_images] # return names, [ encoder_img2base64(det) for det in cropped_images]
# 只是提取人脸特征,不要对别的有任何影响
def featureDetect(self, img_path:str):
rw_lock.acquire_read()
try:
image = load_img(img_path) # get bgr numpy image
unknown_embeddings = []
image = self.rotadjust(image) # 调整角度
detect_result = self.fd.detect(image)
detect_result = [(box.x1, box.y1, box.x2, box.y2) for box in detect_result]
if len(detect_result) == 0:
logger.info(f"{img_path[:200]}: no face in the image")
print(f"{img_path[:200]}: no face in the image")
raise FaceError("30018") # push: no face in the image
for facebox in detect_result:
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
# print("5点关键点",landmarks5)
# 输入image 和5点特征点位置(基于原图image的位置) , return all cropped aligned face (裁剪后的对齐后的人脸部分图像, 简写为aligned_faces
landmarks5 = [ [ld5[0],ld5[1]] for ld5 in landmarks5]
cropped_face = self.fa.align(image, landmarks5=landmarks5)
# 输入aligned_faces return all features of aligned_faces
feature = self.fr.inference(cropped_face)
unknown_embeddings.append(feature)
ret_data = []
for _, (facebox, feature) in enumerate(zip(detect_result, unknown_embeddings)):
code = self.check_face("None", image, facebox, prefix='featuredetect')
ret_data.append({'code':code, 'msg': ErrorMsg[code], 'data': [f"{feature.tobytes()},{str(feature.dtype)}", facebox]})
if len(ret_data) != 1:
ret_data = {'code':"30008", 'msg': ErrorMsg["30008"], 'data': ret_data}
else:
ret_data = ret_data[0]
except Exception as e:
logger.info(f"{e}")
rw_lock.release_read()
raise e
else:
rw_lock.release_read()
return ret_data
# opt in ['add','delete','replace'] identity作为检索的标识符img_path只是提供文件路径 # opt in ['add','delete','replace'] identity作为检索的标识符img_path只是提供文件路径
# 写操作 # 写操作
def updateDB(self, img_path :str, opt :str, identity :str, Onlyrefresh=False): def updateDB(self, img_path :str, opt :str, identity :str, Onlyrefresh=False):
@ -333,14 +378,14 @@ class FaceHelper:
img = self.rotadjust(img) # 调整角度 img = self.rotadjust(img) # 调整角度
if opt in ["add","replace"]: if opt in ["add","replace"]:
if opt == "add" and self.db_identities is not None and identity in self.db_identities: if opt == "add" and self.db_identities is not None and identity in self.db_identities:
raise UpdatedbError("401") # push: identity has exist. to pretect the db, reject opt of this time raise UpdatedbError("30019") # push: identity has exist. to pretect the db, reject opt of this time
else: else:
detect_result = self.fd.detect(img) detect_result = self.fd.detect(img)
if len(detect_result) == 0: # no face if len(detect_result) == 0: # no face
logger.info(f"{img_path[:200]}: when update, no face in the image") logger.info(f"{img_path[:200]}: when update, no face in the image")
print(f"{img_path[:200]}: when update, no face in the image") print(f"{img_path[:200]}: when update, no face in the image")
raise FaceError("301") # push: no face in the image raise FaceError("30018") # push: no face in the image
else: # 获取最大的face,然后进行check else: # 获取最大的face,然后进行check
# H, W = img.shape[:2] # H, W = img.shape[:2]
areas = [ box.area() for box in detect_result] areas = [ box.area() for box in detect_result]
@ -348,7 +393,7 @@ class FaceHelper:
facebox = detect_result[max_idx] facebox = detect_result[max_idx]
facebox = (facebox.x1, facebox.y1, facebox.x2, facebox.y2) # top_left point, bottom_right point facebox = (facebox.x1, facebox.y1, facebox.x2, facebox.y2) # top_left point, bottom_right point
FaceError_number = self.check_face(img_path=img_path[:200], img=img, facebox=facebox, prefix='update') FaceError_number = self.check_face(img_path=img_path[:200], img=img, facebox=facebox, prefix='update')
if FaceError_number != "300": if FaceError_number != "30002":
raise FaceError(FaceError_number) raise FaceError(FaceError_number)
cv2.imwrite(os.path.join(self.db_dir, identity+'.jpg'),img,[cv2.IMWRITE_JPEG_QUALITY, 100]) # 如果file已经存在则会替换它 cv2.imwrite(os.path.join(self.db_dir, identity+'.jpg'),img,[cv2.IMWRITE_JPEG_QUALITY, 100]) # 如果file已经存在则会替换它
@ -359,7 +404,7 @@ class FaceHelper:
except FileNotFoundError: except FileNotFoundError:
pass pass
else: else:
raise NotImpltError("202") # push: this updateDB type is not support raise NotImpltError("30007") # push: this updateDB type is not support
print("end the updateDB") print("end the updateDB")
logger.info(f"end the updateDB") logger.info(f"end the updateDB")
@ -448,7 +493,7 @@ class FaceHelper:
if check: if check:
FaceError_number = self.check_face(img_path=new_image[:200], img=image, facebox=facebox, prefix='refreshdb') FaceError_number = self.check_face(img_path=new_image[:200], img=image, facebox=facebox, prefix='refreshdb')
if FaceError_number != "300": if FaceError_number != "30002":
continue continue
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角 landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
@ -489,14 +534,14 @@ class FaceHelper:
if len(representations) <= 0: if len(representations) <= 0:
self.db_embeddings = None self.db_embeddings = None
empty = True empty = True
# raise FileError("101") # push: no face in db # raise FileError("30003") # push: no face in db
else: else:
self.db_embeddings = np.array([rep["embedding"] for rep in representations], dtype=np.float32) self.db_embeddings = np.array([rep["embedding"] for rep in representations], dtype=np.float32)
self.db_identities = [os.path.splitext(os.path.basename(rep["identity"]))[0] for rep in representations] self.db_identities = [os.path.splitext(os.path.basename(rep["identity"]))[0] for rep in representations]
redis_client.set(f"worker_{self.pid}", 1) # 当前进程已更新 redis_client.set(f"worker_{self.pid}", 1) # 当前进程已更新
if empty: if empty:
logger.info("no face in the database") logger.info("no face in the database")
raise FileError("101") # push: no face in db raise FileError("30003") # push: no face in db
def rotadjust(self, img: np.ndarray): def rotadjust(self, img: np.ndarray):
image = img.copy() image = img.copy()
@ -560,29 +605,29 @@ class FaceHelper:
if facebox[0] < 0 or facebox[2] >= W: if facebox[0] < 0 or facebox[2] >= W:
logger.info(f"{img_path}: when {prefix}, face shifted left/right") logger.info(f"{img_path}: when {prefix}, face shifted left/right")
print(f"{img_path}: when {prefix}, face shifted left/right") print(f"{img_path}: when {prefix}, face shifted left/right")
return "3041" # face shifted left/right, partially not captured. return "30010" # face shifted left/right, partially not captured.
if facebox[1] < 0 or facebox[3] >= H: if facebox[1] < 0 or facebox[3] >= H:
logger.info(f"{img_path}: when {prefix}, face shifted top/bottom") logger.info(f"{img_path}: when {prefix}, face shifted top/bottom")
print(f"{img_path}: when {prefix}, face shifted top/bottom") print(f"{img_path}: when {prefix}, face shifted top/bottom")
return "3042" # face shifted top/bottom, partially not captured. return "30011" # face shifted top/bottom, partially not captured.
face_img = img[ max(0,int(facebox[1])):int(facebox[3]), max(0,int(facebox[0])):int(facebox[2]) ] face_img = img[ max(0,int(facebox[1])):int(facebox[3]), max(0,int(facebox[0])):int(facebox[2]) ]
if not self.qc.check_bright(face_img): if not self.qc.check_bright(face_img):
logger.info(f"{img_path}: when {prefix}, bad bright face in the image") logger.info(f"{img_path}: when {prefix}, bad bright face in the image")
print(f"{img_path}: when {prefix}, bad bright face in the image") print(f"{img_path}: when {prefix}, bad bright face in the image")
return "303" # bad bright face in the image return "30009" # bad bright face in the image
if not self.qc.check_resolution(face_img): if not self.qc.check_resolution(face_img):
logger.info(f"{img_path}: when {prefix}, too small resolution of face in the image") logger.info(f"{img_path}: when {prefix}, too small resolution of face in the image")
print(f"{img_path}: when {prefix}, too small resolution of face in the image") print(f"{img_path}: when {prefix}, too small resolution of face in the image")
return "306" # small face in the image return "30016" # small face in the image
pose = self.qpose.check(face_img) pose = self.qpose.check(face_img)
if pose != "frontFace": if pose != "frontFace":
logger.info(f"{img_path}: when {prefix}, {pose} in the image") logger.info(f"{img_path}: when {prefix}, {pose} in the image")
print(f"{img_path}: when {prefix}, {pose} in the image") print(f"{img_path}: when {prefix}, {pose} in the image")
dictt = {"rightFace": "3051", "leftFace": "3052", "upFace": "3053", "downFace": "3054"} dictt = {"rightFace": "30012", "leftFace": "30013", "upFace": "30014", "downFace": "30015"}
return dictt[pose] # pose of face in the image return dictt[pose] # pose of face in the image
if not self.qclarity.check(face_img): if not self.qclarity.check(face_img):
logger.info(f"{img_path}: when {prefix}, bad clarity of face in the image") logger.info(f"{img_path}: when {prefix}, bad clarity of face in the image")
print(f"{img_path}: when {prefix}, bad clarity of face in the image") print(f"{img_path}: when {prefix}, bad clarity of face in the image")
return "307" # bad clarity of face in the image return "30017" # poor clarity of face in the image
return "300" return "30002"

View File

@ -3,6 +3,7 @@
项目细节详见docx文档. 项目细节详见docx文档.
# requirements # requirements
```
fastapi 0.103.2 fastapi 0.103.2
uvicorn[standard] 0.22.0 uvicorn[standard] 0.22.0
opencv-python 4.9.0.80 opencv-python 4.9.0.80
@ -10,10 +11,11 @@ requests 2.31.0
Pillow 9.4.0 Pillow 9.4.0
redis 5.0.6 redis 5.0.6
onnxruntime 1.18.1 onnxruntime 1.18.1
```
推荐使用 `pip install` 安装上述库,可以不遵循其版本号(这里写出具体版本是为了复现原运行环境). 推荐使用 `pip install` 安装上述库,可以不遵循其版本号(这里写出具体版本是为了复现原运行环境).
# file struct # file struct
```
./ ./
checkpoints 储存模型的目录 checkpoints 储存模型的目录
models 方法函数目录 models 方法函数目录
@ -24,8 +26,9 @@ onnxruntime 1.18.1
redis.yaml redis的配置文件 redis.yaml redis的配置文件
config.yaml 项目的配置文件 config.yaml 项目的配置文件
setup.sh 启动项目的脚本(linux版本) setup.sh 启动项目的脚本(linux版本)
reflushdb.sh 手动刷新数据库(usage: ./reflushdb.sh ip:port)
readme.md 本文件 readme.md 本文件
```
# setup # setup
`bash setup.sh [ip] [port] [进程数] [并发工作线程数]` `bash setup.sh [ip] [port] [进程数] [并发工作线程数]`
e.g.: `bash setup.sh 0.0.0.0 8017 1 60` e.g.: `bash setup.sh 0.0.0.0 8017 1 60`

View File

@ -0,0 +1,18 @@
#!/bin/bash
# keep input valid
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <server_url>"
exit 1
fi
# ip:host
SERVER=$1
ENDPOINT="/refreshdb"
# 使用 curl 调用接口
response=$(curl -s -X POST "$SERVER$ENDPOINT" -H "Content-Type: application/json")
# 打印响应
echo "Response from $ENDPOINT: $response"

View File

@ -18,9 +18,10 @@ class face(BaseModel):
class dbface(BaseModel): class dbface(BaseModel):
img:str img:str
optMode:str optMode:str
imgName:str uniqueKey:str
@app.post("/refreshdb") @app.post("/refreshdb")
@app.post("/refreshdb/")
def refresh(): def refresh():
global facehelper global facehelper
try: try:
@ -30,9 +31,9 @@ def refresh():
# return {"status":e.code, "detail":f"{e}"} # return {"status":e.code, "detail":f"{e}"}
return {'code': e.code, 'msg': f"{e}", 'data': 'None'} return {'code': e.code, 'msg': f"{e}", 'data': 'None'}
else: else:
return {'code': "300", 'msg': ErrorMsg['300'], 'data': 'None'} return {'code': "30002", 'msg': ErrorMsg['30002'], 'data': 'None'}
@app.post("/facerecognition")
@app.post("/facerecognition/") @app.post("/facerecognition/")
def faceRecognition(input:face): def faceRecognition(input:face):
start = time.time() start = time.time()
@ -49,19 +50,34 @@ def faceRecognition(input:face):
return ret_data return ret_data
return {"status":1, "name":identity, "resImg":res_img_base64} return {"status":1, "name":identity, "resImg":res_img_base64}
@app.post("/featuredetect")
@app.post("/updatedb/") @app.post("/featuredetect/")
def updateDB(input:dbface): def featureDetect(input:face):
start = time.time()
global facehelper global facehelper
# 在这儿加一句只取imgName中第一个`.`前面的部分str
input.imgName = os.path.splitext(os.path.basename(input.imgName))[0]
try: try:
with lock: ret_data = facehelper.featureDetect(input.img)
facehelper.updateDB(input.img, input.optMode, input.imgName) print("finished featuredetect...")
end = time.time()
print("runtime: ", end-start)
except Exception as e: except Exception as e:
return {'code': e.code, 'msg': f"{e}", 'data': 'None'} return {'code': e.code, 'msg': f"{e}", 'data': 'None'}
# return {"status":f"{e.code}", "detail":f"{e}"} # return {"status":f"{e.code}", "detail":f"{e}"}
else: else:
return {'code': "300", 'msg': ErrorMsg['300'], 'data': 'None'} return ret_data
@app.post("/updatedb")
@app.post("/updatedb/")
def updateDB(input:dbface):
global facehelper
# input.uniqueKey = os.path.splitext(os.path.basename(input.uniqueKey))[0]
try:
with lock:
facehelper.updateDB(input.img, input.optMode, input.uniqueKey)
except Exception as e:
return {'code': e.code, 'msg': f"{e}", 'data': 'None'}
# return {"status":f"{e.code}", "detail":f"{e}"}
else:
return {'code': "30002", 'msg': ErrorMsg['30002'], 'data': 'None'}

Binary file not shown.