修改代码与文档
This commit is contained in:
parent
7c63e11132
commit
3a1c18c29d
|
|
@ -2,7 +2,7 @@ import redis
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Redis config
|
# Redis config
|
||||||
redis_host = str(os.getenv("REDIS_HOST", 'localhost'))
|
redis_host = str(os.getenv("REDIS_HOST", '192.168.0.14'))
|
||||||
redis_port = int(os.getenv("REDIS_PORT", 2012))
|
redis_port = int(os.getenv("REDIS_PORT", 2012))
|
||||||
redis_password = str(os.getenv("REDIS_PASSWORD", 'Xjsfzb@Redis123!'))
|
redis_password = str(os.getenv("REDIS_PASSWORD", 'Xjsfzb@Redis123!'))
|
||||||
num_workers = int(os.getenv("NUM_WORKERS", 10))
|
num_workers = int(os.getenv("NUM_WORKERS", 10))
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,16 @@ from models import FaceRecoger, FaceBoxesV2, Landmark5er, FaceAlign, QualityOfCl
|
||||||
so = onnxruntime.SessionOptions()
|
so = onnxruntime.SessionOptions()
|
||||||
so.log_severity_level = 3 # 0=VERBOSE, 1=INFO, 2=WARNING, 3=ERROR, 4=FATAL
|
so.log_severity_level = 3 # 0=VERBOSE, 1=INFO, 2=WARNING, 3=ERROR, 4=FATAL
|
||||||
|
|
||||||
|
# # 获取workers
|
||||||
# 获取workers
|
# if "NUM_WORKERS" not in os.environ:
|
||||||
if "NUM_WORKERS" not in os.environ:
|
# raise RuntimeError("Environment variable NUM_WORKERS is required but not set.")
|
||||||
raise RuntimeError("Environment variable NUM_WORKERS is required but not set.")
|
|
||||||
NUM_WORKERS = int(os.getenv("NUM_WORKERS", 10))
|
NUM_WORKERS = int(os.getenv("NUM_WORKERS", 10))
|
||||||
|
|
||||||
# max readers
|
# max readers
|
||||||
max_readers = int(os.getenv("MAX_READERS",60))
|
max_readers = int(os.getenv("MAX_READERS", 60))
|
||||||
|
|
||||||
# 连接到 Redis
|
# 连接到 Redis
|
||||||
redis_host = str(os.getenv("REDIS_HOST", 'localhost'))
|
redis_host = str(os.getenv("REDIS_HOST", '192.168.0.14'))
|
||||||
redis_port = int(os.getenv("REDIS_PORT", 2012))
|
redis_port = int(os.getenv("REDIS_PORT", 2012))
|
||||||
redis_password = str(os.getenv("REDIS_PASSWORD", 'Xjsfzb@Redis123!'))
|
redis_password = str(os.getenv("REDIS_PASSWORD", 'Xjsfzb@Redis123!'))
|
||||||
# connected
|
# connected
|
||||||
|
|
@ -73,26 +72,31 @@ ErrorMsg = {
|
||||||
"30019": "identity already exists; for database protection, operation rejected at this time"
|
"30019": "identity already exists; for database protection, operation rejected at this time"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class FileError(Exception):
|
class FileError(Exception):
|
||||||
def __init__(self, arg:str):
|
def __init__(self, arg: str):
|
||||||
self.code = arg
|
self.code = arg
|
||||||
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
||||||
|
|
||||||
|
|
||||||
class NotImpltError(Exception):
|
class NotImpltError(Exception):
|
||||||
def __init__(self, arg:str):
|
def __init__(self, arg: str):
|
||||||
self.code = arg
|
self.code = arg
|
||||||
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
||||||
|
|
||||||
|
|
||||||
class FaceError(Exception):
|
class FaceError(Exception):
|
||||||
def __init__(self, arg:str):
|
def __init__(self, arg: str):
|
||||||
self.code = arg
|
self.code = arg
|
||||||
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
||||||
|
|
||||||
|
|
||||||
class UpdatedbError(Exception):
|
class UpdatedbError(Exception):
|
||||||
def __init__(self, arg:str):
|
def __init__(self, arg: str):
|
||||||
self.code = arg
|
self.code = arg
|
||||||
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
self.args = [f"{str(self.__class__.__name__)} {str(arg)}: {ErrorMsg[arg]}"]
|
||||||
|
|
||||||
|
|
||||||
# setting Logger
|
# setting Logger
|
||||||
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
log_dir = f"{os.path.dirname(os.path.abspath(__file__))}/log"
|
log_dir = f"{os.path.dirname(os.path.abspath(__file__))}/log"
|
||||||
|
|
@ -102,6 +106,7 @@ logging.basicConfig(filename=f'{log_dir}/{current_time}.log', level=logging.INFO
|
||||||
logger = logging.getLogger(__name__) # @@@@
|
logger = logging.getLogger(__name__) # @@@@
|
||||||
print(log_dir)
|
print(log_dir)
|
||||||
|
|
||||||
|
|
||||||
def list_images(path: str):
|
def list_images(path: str):
|
||||||
"""
|
"""
|
||||||
List images in a given path
|
List images in a given path
|
||||||
|
|
@ -150,19 +155,20 @@ def find_image_hash(file_path: str) -> str:
|
||||||
hasher.update(properties.encode("utf-8"))
|
hasher.update(properties.encode("utf-8"))
|
||||||
return hasher.hexdigest()
|
return hasher.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
# 支持base64 local-path url 等多种检索图片的方式,返回 numpy
|
# 支持base64 local-path url 等多种检索图片的方式,返回 numpy
|
||||||
def load_img(img_path:str):
|
def load_img(img_path: str):
|
||||||
image = None
|
image = None
|
||||||
try:
|
try:
|
||||||
if img_path.startswith(("http","www")): # url
|
if img_path.startswith(("http", "www")): # url
|
||||||
response = requests.get(url=img_path, stream=True, timeout=60, proxies={"http": None, "https": None})
|
response = requests.get(url=img_path, stream=True, timeout=60, proxies={"http": None, "https": None})
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
image_array = np.asarray(bytearray(response.raw.read()), dtype=np.uint8)
|
image_array = np.asarray(bytearray(response.raw.read()), dtype=np.uint8)
|
||||||
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("30004") # 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("30005") # push: invaild file suffix
|
raise FileError("30005") # push: invaild file suffix
|
||||||
else:
|
else:
|
||||||
image = cv2.imread(img_path)
|
image = cv2.imread(img_path)
|
||||||
|
|
@ -170,7 +176,7 @@ def load_img(img_path:str):
|
||||||
encoded_data_parts = img_path.split(",")
|
encoded_data_parts = img_path.split(",")
|
||||||
if len(encoded_data_parts) <= 0:
|
if len(encoded_data_parts) <= 0:
|
||||||
raise FileError("104") # push: base64 is empty
|
raise FileError("104") # push: base64 is empty
|
||||||
print( "base64 is empty" )
|
print("base64 is empty")
|
||||||
encoded_data = encoded_data_parts[-1]
|
encoded_data = encoded_data_parts[-1]
|
||||||
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)
|
||||||
|
|
@ -184,7 +190,7 @@ def load_img(img_path:str):
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
def encoder_img2base64(img:np.ndarray):
|
def encoder_img2base64(img: np.ndarray):
|
||||||
success, encoded_img = cv2.imencode('.png', img)
|
success, encoded_img = cv2.imencode('.png', img)
|
||||||
if success:
|
if success:
|
||||||
img_base64 = base64.b64encode(encoded_img).decode("utf-8")
|
img_base64 = base64.b64encode(encoded_img).decode("utf-8")
|
||||||
|
|
@ -195,7 +201,7 @@ def encoder_img2base64(img:np.ndarray):
|
||||||
# from seetaface.api import *
|
# from seetaface.api import *
|
||||||
class FaceHelper:
|
class FaceHelper:
|
||||||
|
|
||||||
def __init__(self, db_dir, config_path = './config.yaml'):
|
def __init__(self, db_dir, config_path='./config.yaml'):
|
||||||
self.db_dir = os.path.abspath(db_dir)
|
self.db_dir = os.path.abspath(db_dir)
|
||||||
self.pid = PID_id
|
self.pid = PID_id
|
||||||
self.db_embeddings = None
|
self.db_embeddings = None
|
||||||
|
|
@ -206,25 +212,31 @@ class FaceHelper:
|
||||||
config = yaml.safe_load(f)
|
config = yaml.safe_load(f)
|
||||||
|
|
||||||
self.sim_threshold = config['faceReg']['sim_threshold'] # 0.7
|
self.sim_threshold = config['faceReg']['sim_threshold'] # 0.7
|
||||||
self.rotclsifer = onnxruntime.InferenceSession( config['ck_paths']['rotifer'], so) # "./checkpoints/model_gray_mobilenetv2_rotcls.onnx"
|
self.rotclsifer = onnxruntime.InferenceSession(config['ck_paths']['rotifer'],
|
||||||
self.db_path = os.path.join( db_dir, "seetaface6.pkl" ).lower()
|
so) # "./checkpoints/model_gray_mobilenetv2_rotcls.onnx"
|
||||||
|
self.db_path = os.path.join(db_dir, "seetaface6.pkl").lower()
|
||||||
|
|
||||||
self.fd = FaceBoxesV2(config['ck_paths']['FcBx'], config['ck_paths']['num_threads'] ) # r"./checkpoints/faceboxesv2-640x640.onnx" 4
|
self.fd = FaceBoxesV2(config['ck_paths']['FcBx'],
|
||||||
self.ld5er = Landmark5er( onnx_path1 = config['ck_paths']['landmk1'], # "./checkpoints/face_landmarker_pts5_net1.onnx",
|
config['ck_paths']['num_threads']) # r"./checkpoints/faceboxesv2-640x640.onnx" 4
|
||||||
onnx_path2 = config['ck_paths']['landmk2'], # "./checkpoints/face_landmarker_pts5_net2.onnx",
|
self.ld5er = Landmark5er(onnx_path1=config['ck_paths']['landmk1'],
|
||||||
|
# "./checkpoints/face_landmarker_pts5_net1.onnx",
|
||||||
|
onnx_path2=config['ck_paths']['landmk2'],
|
||||||
|
# "./checkpoints/face_landmarker_pts5_net2.onnx",
|
||||||
num_threads=config['ck_paths']['num_threads'] # 4
|
num_threads=config['ck_paths']['num_threads'] # 4
|
||||||
)
|
)
|
||||||
self.fa = FaceAlign()
|
self.fa = FaceAlign()
|
||||||
self.fr = FaceRecoger(onnx_path = config['ck_paths']['FcReg'], num_threads= config['ck_paths']['num_threads'] ) # "./checkpoints/face_recognizer.onnx" 4
|
self.fr = FaceRecoger(onnx_path=config['ck_paths']['FcReg'],
|
||||||
|
num_threads=config['ck_paths']['num_threads']) # "./checkpoints/face_recognizer.onnx" 4
|
||||||
self.qc = QualityChecker(config['brightness']['v0'], config['brightness']['v1'],
|
self.qc = QualityChecker(config['brightness']['v0'], config['brightness']['v1'],
|
||||||
config['brightness']['v2'], config['brightness']['v3'],
|
config['brightness']['v2'], config['brightness']['v3'],
|
||||||
hw = (config['resolution']['height'], config['resolution']['width'])
|
hw=(config['resolution']['height'], config['resolution']['width'])
|
||||||
) # v0=70.0, v1=100.0, v2=210.0, v3=230.0
|
) # v0=70.0, v1=100.0, v2=210.0, v3=230.0
|
||||||
self.qpose = QualityOfPose(yaw_thrd=config['pose']['yaw_thrd'], pitch_thrd=config['pose']['pitch_thrd'],
|
self.qpose = QualityOfPose(yaw_thrd=config['pose']['yaw_thrd'], pitch_thrd=config['pose']['pitch_thrd'],
|
||||||
var_onnx_path = config['pose']['var_onnx_path'], # './checkpoints/fsanet-var.onnx',
|
var_onnx_path=config['pose']['var_onnx_path'], # './checkpoints/fsanet-var.onnx',
|
||||||
conv_onnx_path = config['pose']['conv_onnx_path'], # './checkpoints/fsanet-conv.onnx'
|
conv_onnx_path=config['pose']['conv_onnx_path'], # './checkpoints/fsanet-conv.onnx'
|
||||||
)
|
)
|
||||||
self.qclarity = QualityOfClarity(low_thresh=config['clarity']['low_thrd'], high_thresh=config['clarity']['high_thrd'])
|
self.qclarity = QualityOfClarity(low_thresh=config['clarity']['low_thrd'],
|
||||||
|
high_thresh=config['clarity']['high_thrd'])
|
||||||
|
|
||||||
# refresh the db
|
# refresh the db
|
||||||
try:
|
try:
|
||||||
|
|
@ -237,7 +249,7 @@ class FaceHelper:
|
||||||
logger.info(f"db_dir: {self.db_dir} ; PID: {self.pid}")
|
logger.info(f"db_dir: {self.db_dir} ; PID: {self.pid}")
|
||||||
|
|
||||||
# 读操作
|
# 读操作
|
||||||
def faceRecognition(self, img_path:str):
|
def faceRecognition(self, img_path: str):
|
||||||
rw_lock.acquire_read()
|
rw_lock.acquire_read()
|
||||||
if int(redis_client.get(f"worker_{self.pid}")) == 0: # 说明self中的db和磁盘中的db不同步
|
if int(redis_client.get(f"worker_{self.pid}")) == 0: # 说明self中的db和磁盘中的db不同步
|
||||||
with open(self.db_path, "rb") as f:
|
with open(self.db_path, "rb") as f:
|
||||||
|
|
@ -268,7 +280,7 @@ class FaceHelper:
|
||||||
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
|
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
|
||||||
# print("5点关键点:",landmarks5)
|
# print("5点关键点:",landmarks5)
|
||||||
# 输入image 和5点特征点位置(基于原图image的位置) , return all cropped aligned face (裁剪后的对齐后的人脸部分图像, 简写为aligned_faces)
|
# 输入image 和5点特征点位置(基于原图image的位置) , return all cropped aligned face (裁剪后的对齐后的人脸部分图像, 简写为aligned_faces)
|
||||||
landmarks5 = [ [ld5[0],ld5[1]] for ld5 in landmarks5]
|
landmarks5 = [[ld5[0], ld5[1]] for ld5 in landmarks5]
|
||||||
cropped_face = self.fa.align(image, landmarks5=landmarks5)
|
cropped_face = self.fa.align(image, landmarks5=landmarks5)
|
||||||
# 输入aligned_faces ,return all features of aligned_faces
|
# 输入aligned_faces ,return all features of aligned_faces
|
||||||
feature = self.fr.inference(cropped_face)
|
feature = self.fr.inference(cropped_face)
|
||||||
|
|
@ -278,8 +290,8 @@ class FaceHelper:
|
||||||
unknown_embeddings = np.vstack(unknown_embeddings)
|
unknown_embeddings = np.vstack(unknown_embeddings)
|
||||||
results = np.dot(unknown_embeddings, self.db_embeddings.T)
|
results = np.dot(unknown_embeddings, self.db_embeddings.T)
|
||||||
|
|
||||||
max_values = np.max(results,axis=1)
|
max_values = np.max(results, axis=1)
|
||||||
max_idxs = np.argmax(results,axis=1)
|
max_idxs = np.argmax(results, axis=1)
|
||||||
|
|
||||||
for i, (idx, value) in enumerate(zip(max_idxs, max_values)):
|
for i, (idx, value) in enumerate(zip(max_idxs, max_values)):
|
||||||
name = "unknown"
|
name = "unknown"
|
||||||
|
|
@ -290,15 +302,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':"30000", 'msg': ErrorMsg["30000"], 'data':[name,facebox]})
|
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')
|
||||||
if code == "30002":
|
if code == "30002":
|
||||||
ret_data.append({'code':"30001", 'msg': ErrorMsg["30001"], 'data':[name,facebox]})
|
ret_data.append({'code': "30001", 'msg': ErrorMsg["30001"], 'data': [name, facebox]})
|
||||||
else:
|
else:
|
||||||
ret_data.append({'code':code, 'msg': ErrorMsg[code], 'data':[name,facebox]})
|
ret_data.append({'code': code, 'msg': ErrorMsg[code], 'data': [name, facebox]})
|
||||||
if len(ret_data) != 1:
|
if len(ret_data) != 1:
|
||||||
ret_data = {'code':"30008", 'msg': ErrorMsg["30008"], '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]
|
||||||
|
|
||||||
|
|
@ -314,12 +326,10 @@ class FaceHelper:
|
||||||
# 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):
|
def featureDetect(self, img_path: str):
|
||||||
rw_lock.acquire_read()
|
rw_lock.acquire_read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image = load_img(img_path) # get bgr numpy image
|
image = load_img(img_path) # get bgr numpy image
|
||||||
|
|
||||||
unknown_embeddings = []
|
unknown_embeddings = []
|
||||||
image = self.rotadjust(image) # 调整角度
|
image = self.rotadjust(image) # 调整角度
|
||||||
detect_result = self.fd.detect(image)
|
detect_result = self.fd.detect(image)
|
||||||
|
|
@ -332,7 +342,7 @@ class FaceHelper:
|
||||||
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
|
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
|
||||||
# print("5点关键点:",landmarks5)
|
# print("5点关键点:",landmarks5)
|
||||||
# 输入image 和5点特征点位置(基于原图image的位置) , return all cropped aligned face (裁剪后的对齐后的人脸部分图像, 简写为aligned_faces)
|
# 输入image 和5点特征点位置(基于原图image的位置) , return all cropped aligned face (裁剪后的对齐后的人脸部分图像, 简写为aligned_faces)
|
||||||
landmarks5 = [ [ld5[0],ld5[1]] for ld5 in landmarks5]
|
landmarks5 = [[ld5[0], ld5[1]] for ld5 in landmarks5]
|
||||||
cropped_face = self.fa.align(image, landmarks5=landmarks5)
|
cropped_face = self.fa.align(image, landmarks5=landmarks5)
|
||||||
# 输入aligned_faces ,return all features of aligned_faces
|
# 输入aligned_faces ,return all features of aligned_faces
|
||||||
feature = self.fr.inference(cropped_face)
|
feature = self.fr.inference(cropped_face)
|
||||||
|
|
@ -341,10 +351,10 @@ class FaceHelper:
|
||||||
ret_data = []
|
ret_data = []
|
||||||
for _, (facebox, feature) in enumerate(zip(detect_result, unknown_embeddings)):
|
for _, (facebox, feature) in enumerate(zip(detect_result, unknown_embeddings)):
|
||||||
code = self.check_face("None", image, facebox, prefix='featuredetect')
|
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]})
|
ret_data.append({'code': code, 'msg': ErrorMsg[code], 'data': [{str(num) for num in feature}, facebox]})
|
||||||
|
|
||||||
if len(ret_data) != 1:
|
if len(ret_data) != 1:
|
||||||
ret_data = {'code':"30008", 'msg': ErrorMsg["30008"], '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]
|
||||||
|
|
||||||
|
|
@ -358,7 +368,7 @@ class FaceHelper:
|
||||||
|
|
||||||
# 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):
|
||||||
global rw_lock
|
global rw_lock
|
||||||
rw_lock.acquire_write() # 写锁定
|
rw_lock.acquire_write() # 写锁定
|
||||||
print("come in the updatedb")
|
print("come in the updatedb")
|
||||||
|
|
@ -371,14 +381,16 @@ class FaceHelper:
|
||||||
self.db_embeddings, self.db_identities = None, None
|
self.db_embeddings, self.db_identities = None, None
|
||||||
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) # 同步完毕
|
||||||
|
|
||||||
img = load_img(img_path)
|
img = load_img(img_path)
|
||||||
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("30019") # 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)
|
||||||
|
|
@ -388,28 +400,30 @@ class FaceHelper:
|
||||||
raise FaceError("30018") # 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]
|
||||||
max_idx = areas.index(max(areas))
|
max_idx = areas.index(max(areas))
|
||||||
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 = (
|
||||||
FaceError_number = self.check_face(img_path=img_path[:200], img=img, facebox=facebox, prefix='update')
|
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')
|
||||||
if FaceError_number != "30002":
|
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已经存在,则会替换它
|
||||||
|
|
||||||
elif opt == "delete":
|
elif opt == "delete":
|
||||||
try:
|
try:
|
||||||
os.remove(os.path.join(self.db_dir, identity+'.jpg'))
|
os.remove(os.path.join(self.db_dir, identity + '.jpg'))
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise NotImpltError("30007") # 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")
|
||||||
|
|
||||||
self.refresh_database(check = Onlyrefresh) # 结束时刷新下db, 并通知别的进程,dirty
|
self.refresh_database(check=Onlyrefresh) # 结束时刷新下db, 并通知别的进程,dirty
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(f"{e}")
|
logger.info(f"{e}")
|
||||||
rw_lock.release_write()
|
rw_lock.release_write()
|
||||||
|
|
@ -418,7 +432,7 @@ class FaceHelper:
|
||||||
rw_lock.release_write()
|
rw_lock.release_write()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def refresh_database(self, check = True):
|
def refresh_database(self, check=True):
|
||||||
# ensure db exist
|
# ensure db exist
|
||||||
os.makedirs(self.db_dir, exist_ok=True)
|
os.makedirs(self.db_dir, exist_ok=True)
|
||||||
if not os.path.exists(self.db_path):
|
if not os.path.exists(self.db_path):
|
||||||
|
|
@ -442,11 +456,13 @@ class FaceHelper:
|
||||||
if ext == '.jpg':
|
if ext == '.jpg':
|
||||||
continue
|
continue
|
||||||
iimg = cv2.imread(img_path)
|
iimg = cv2.imread(img_path)
|
||||||
cv2.imwrite(base_path+'.jpg', iimg, [cv2.IMWRITE_JPEG_QUALITY, 100])
|
cv2.imwrite(base_path + '.jpg', iimg, [cv2.IMWRITE_JPEG_QUALITY, 100])
|
||||||
storage_images[idx] = base_path+'.jpg'
|
storage_images[idx] = base_path + '.jpg'
|
||||||
|
|
||||||
must_save_pickle = False
|
must_save_pickle = False
|
||||||
new_images = []; old_images = []; replaced_images = []
|
new_images = [];
|
||||||
|
old_images = [];
|
||||||
|
replaced_images = []
|
||||||
|
|
||||||
new_images = list(set(storage_images) - set(pickle_images))
|
new_images = list(set(storage_images) - set(pickle_images))
|
||||||
old_images = list(set(pickle_images) - set(storage_images))
|
old_images = list(set(pickle_images) - set(storage_images))
|
||||||
|
|
@ -486,18 +502,19 @@ class FaceHelper:
|
||||||
else:
|
else:
|
||||||
logger.info(f"{new_image}: find one face, perfect!")
|
logger.info(f"{new_image}: find one face, perfect!")
|
||||||
|
|
||||||
areas = [ box.area() for box in detect_result]
|
areas = [box.area() for box in detect_result]
|
||||||
max_idx = areas.index(max(areas))
|
max_idx = areas.index(max(areas))
|
||||||
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
|
||||||
|
|
||||||
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 != "30002":
|
if FaceError_number != "30002":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
|
landmarks5 = self.ld5er.inference(image, facebox) # return: [(),(),(),(),()] 左眼 右眼 鼻子 左嘴角 右嘴角
|
||||||
landmarks5 = [ [ld5[0],ld5[1]] for ld5 in landmarks5]
|
landmarks5 = [[ld5[0], ld5[1]] for ld5 in landmarks5]
|
||||||
cropped_face = self.fa.align(image, landmarks5=landmarks5)
|
cropped_face = self.fa.align(image, landmarks5=landmarks5)
|
||||||
feature = self.fr.inference(cropped_face)
|
feature = self.fr.inference(cropped_face)
|
||||||
|
|
||||||
|
|
@ -550,10 +567,10 @@ class FaceHelper:
|
||||||
|
|
||||||
# 中心裁剪到 224x224
|
# 中心裁剪到 224x224
|
||||||
start = (256 - 224) // 2
|
start = (256 - 224) // 2
|
||||||
image = image[start:start+224, start:start+224]
|
image = image[start:start + 224, start:start + 224]
|
||||||
|
|
||||||
# 将单通道灰度图像转换为三通道
|
# 将单通道灰度图像转换为三通道
|
||||||
image = np.stack((image,)*3, axis=-1)
|
image = np.stack((image,) * 3, axis=-1)
|
||||||
|
|
||||||
# 转换为符合 ONNX 需要的格式
|
# 转换为符合 ONNX 需要的格式
|
||||||
image = image.astype(np.float32) / 255.0 # 归一化
|
image = image.astype(np.float32) / 255.0 # 归一化
|
||||||
|
|
@ -582,11 +599,11 @@ class FaceHelper:
|
||||||
def get_feature(self, img: np.ndarray):
|
def get_feature(self, img: np.ndarray):
|
||||||
time.sleep(0.08)
|
time.sleep(0.08)
|
||||||
# assert img.shape[0] == img.shape[1] and img.shape[0] == 256 and img.shape[2] == 3
|
# assert img.shape[0] == img.shape[1] and img.shape[0] == 256 and img.shape[2] == 3
|
||||||
img = cv2.resize( img, (256,256) )
|
img = cv2.resize(img, (256, 256))
|
||||||
input_feed = {}
|
input_feed = {}
|
||||||
# crop_img = cv2.resize(img,(248,248))
|
# crop_img = cv2.resize(img,(248,248))
|
||||||
# crop_img = crop_img[...,::-1]
|
# crop_img = crop_img[...,::-1]
|
||||||
crop_img = img[4:252, 4:252, :][...,::-1] # 注意要考虑 长或宽 < 248的情况
|
crop_img = img[4:252, 4:252, :][..., ::-1] # 注意要考虑 长或宽 < 248的情况
|
||||||
input_data = crop_img.transpose((2, 0, 1))
|
input_data = crop_img.transpose((2, 0, 1))
|
||||||
# resize_img = cv2.resize(img, (248, 248))
|
# resize_img = cv2.resize(img, (248, 248))
|
||||||
# input_data = resize_img.transpose((2, 0, 1))
|
# input_data = resize_img.transpose((2, 0, 1))
|
||||||
|
|
@ -610,7 +627,7 @@ class FaceHelper:
|
||||||
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 "30011" # 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")
|
||||||
|
|
@ -631,3 +648,70 @@ class FaceHelper:
|
||||||
return "30017" # poor clarity of face in the image
|
return "30017" # poor clarity of face in the image
|
||||||
|
|
||||||
return "30002"
|
return "30002"
|
||||||
|
|
||||||
|
def imageComparison(self, firstImage, secondImage):
|
||||||
|
rw_lock.acquire_read()
|
||||||
|
try:
|
||||||
|
# Load and adjust images
|
||||||
|
imageFirst = self.rotadjust(load_img(firstImage))
|
||||||
|
imageSecond = self.rotadjust(load_img(secondImage))
|
||||||
|
print("1")
|
||||||
|
# Detect faces
|
||||||
|
detect_result_first = self.fd.detect(imageFirst)
|
||||||
|
detect_result_second = self.fd.detect(imageSecond)
|
||||||
|
detect_result_first = [(box.x1, box.y1, box.x2, box.y2) for box in detect_result_first]
|
||||||
|
detect_result_second = [(box.x1, box.y1, box.x2, box.y2) for box in detect_result_second]
|
||||||
|
if len(detect_result_first) < 1:
|
||||||
|
raise FaceError("30018")
|
||||||
|
if len(detect_result_second) < 1:
|
||||||
|
raise FaceError("30018")
|
||||||
|
if len(detect_result_first) > 1:
|
||||||
|
raise FaceError("30008")
|
||||||
|
if len(detect_result_second) > 1:
|
||||||
|
raise FaceError("30008")
|
||||||
|
|
||||||
|
# Validate detected faces
|
||||||
|
self._validate_face_detection(detect_result_first, "firstImage")
|
||||||
|
self._validate_face_detection(detect_result_second, "secondImage")
|
||||||
|
# 提取人脸特征
|
||||||
|
features_first = self._extract_single_feature(imageFirst, detect_result_first)
|
||||||
|
features_second = self._extract_single_feature(imageSecond, detect_result_second)
|
||||||
|
# Calculate the Euclidean distance between features
|
||||||
|
distance = calculate_euclidean_distance(features_first, features_second)
|
||||||
|
# Compare distance with threshold
|
||||||
|
return distance >= 0.75
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error occurred: {e}")
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
rw_lock.release_read()
|
||||||
|
|
||||||
|
def _validate_face_detection(self, detect_result, image_name):
|
||||||
|
if not detect_result:
|
||||||
|
logger.warning(f"No face detected in the image: {image_name}")
|
||||||
|
raise FaceError("30018") # no face in the image
|
||||||
|
|
||||||
|
def extract_features(self, image, detect_result):
|
||||||
|
return [
|
||||||
|
self._extract_single_feature(image, facebox)
|
||||||
|
for facebox in detect_result
|
||||||
|
]
|
||||||
|
|
||||||
|
def _extract_single_feature(self, image, detect_result):
|
||||||
|
for facebox in detect_result:
|
||||||
|
landmarks5 = self.ld5er.inference(image, facebox)
|
||||||
|
landmarks5 = np.array([[ld5[0], ld5[1]] for ld5 in landmarks5])
|
||||||
|
cropped_face = self.fa.align(image, landmarks5=landmarks5)
|
||||||
|
return self.fr.inference(cropped_face)
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_euclidean_distance(features1, features2):
|
||||||
|
if len(features1) != len(features2):
|
||||||
|
raise ValueError("Feature arrays must have the same length")
|
||||||
|
# Calculate the Euclidean distance
|
||||||
|
total = 0
|
||||||
|
for f1, f2 in zip(features1, features2):
|
||||||
|
total += f1 * f2
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
rem 从 redis.yaml 文件中读取 Redis 相关配置信息
|
||||||
|
set "REDIS_CONFIG="
|
||||||
|
for /f "delims=" %%i in (redis.yaml) do (
|
||||||
|
set "REDIS_CONFIG=!REDIS_CONFIG! %%i"
|
||||||
|
)
|
||||||
|
|
||||||
|
rem 工作进程数 workers
|
||||||
|
set "NUM_WORKERS=%3"
|
||||||
|
|
||||||
|
rem 提取 Redis 配置信息
|
||||||
|
for %%i in (!REDIS_CONFIG!) do (
|
||||||
|
for /f "tokens=1,* delims==" %%j in (%%i) do (
|
||||||
|
if "%%j"=="REDIS_HOST" set "REDIS_HOST=%%k"
|
||||||
|
if "%%j"=="REDIS_PORT" set "REDIS_PORT=%%k"
|
||||||
|
if "%%j"=="REDIS_PASSWORD" set "REDIS_PASSWORD=%%k"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo num_workers: %NUM_WORKERS%
|
||||||
|
echo redis_host: %REDIS_HOST%; redis_port: %REDIS_PORT%
|
||||||
|
|
||||||
|
rem 运行 Python 脚本
|
||||||
|
set REDIS_HOST=%REDIS_HOST%
|
||||||
|
set REDIS_PORT=%REDIS_PORT%
|
||||||
|
set REDIS_PASSWORD=%REDIS_PASSWORD%
|
||||||
|
set NUM_WORKERS=%NUM_WORKERS%
|
||||||
|
|
||||||
|
rem 确保 Python 脚本能够正确读取环境变量
|
||||||
|
python flushredis.py
|
||||||
|
|
||||||
|
rem 启动 uvicorn
|
||||||
|
set MAX_READERS=%4%
|
||||||
|
uvicorn webmain:app --host %1 --port %2 --workers %NUM_WORKERS%
|
||||||
|
|
@ -1,83 +1,154 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from process import FaceHelper, FileError, ErrorMsg
|
from process import FaceHelper, FileError, ErrorMsg
|
||||||
import threading
|
import threading
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
# 创建 FastAPI 实例
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
# 创建线程锁,确保在多线程环境中对共享资源的访问是线程安全的
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
|
# 全局变量 facehelper,稍后将用于管理人脸识别相关操作
|
||||||
|
facehelper = None
|
||||||
|
|
||||||
facehelper = FaceHelper(
|
|
||||||
db_dir="./dbface",
|
|
||||||
)
|
|
||||||
|
|
||||||
class face(BaseModel):
|
def get_facehelper():
|
||||||
img:str
|
"""
|
||||||
|
懒加载模式初始化 facehelper 对象。
|
||||||
|
只有在第一次调用时才会创建 FaceHelper 实例。
|
||||||
|
使用线程锁以确保 facehelper 实例在多线程环境下的安全初始化。
|
||||||
|
"""
|
||||||
|
global facehelper
|
||||||
|
if facehelper is None:
|
||||||
|
with lock:
|
||||||
|
if facehelper is None:
|
||||||
|
facehelper = FaceHelper(db_dir="./dbface")
|
||||||
|
return facehelper
|
||||||
|
|
||||||
|
|
||||||
|
# 定义请求模型,用于接收前端传递的图像数据
|
||||||
|
class FaceRequest(BaseModel):
|
||||||
|
img: str
|
||||||
|
|
||||||
|
|
||||||
|
# 定义请求模型,用于接收前端传递的数据库操作数据
|
||||||
|
class DBFaceRequest(BaseModel):
|
||||||
|
img: str
|
||||||
|
optMode: str
|
||||||
|
uniqueKey: str
|
||||||
|
|
||||||
|
|
||||||
|
# 接两图片对比的参数
|
||||||
|
class ImageComparison(BaseModel):
|
||||||
|
firstImage: str
|
||||||
|
secondImage: str
|
||||||
|
|
||||||
class dbface(BaseModel):
|
|
||||||
img:str
|
|
||||||
optMode:str
|
|
||||||
uniqueKey:str
|
|
||||||
|
|
||||||
@app.post("/refreshdb")
|
@app.post("/refreshdb")
|
||||||
@app.post("/refreshdb/")
|
|
||||||
def refresh():
|
def refresh():
|
||||||
global facehelper
|
"""
|
||||||
|
刷新人脸数据库的接口。
|
||||||
|
该接口将调用 facehelper 的 updateDB 方法,刷新数据库内容。
|
||||||
|
"""
|
||||||
|
facehelper = get_facehelper()
|
||||||
try:
|
try:
|
||||||
|
# 加锁确保数据库更新操作的线程安全性
|
||||||
with lock:
|
with lock:
|
||||||
facehelper.updateDB(None, None, None, Onlyrefresh=True)
|
facehelper.updateDB(None, None, None, Onlyrefresh=True)
|
||||||
except FileError as e:
|
except FileError as e:
|
||||||
# return {"status":e.code, "detail":f"{e}"}
|
# 处理文件错误,返回相应的错误代码和信息
|
||||||
return {'code': e.code, 'msg': f"{e}", 'data': 'None'}
|
return {'code': e.code, 'msg': str(e), 'data': None}
|
||||||
else:
|
else:
|
||||||
return {'code': "30002", 'msg': ErrorMsg['30002'], 'data': 'None'}
|
# 成功刷新数据库后返回相应的状态码和消息
|
||||||
|
return {'code': "30002", 'msg': ErrorMsg['30002'], 'data': None}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/facerecognition")
|
@app.post("/facerecognition")
|
||||||
@app.post("/facerecognition/")
|
def faceRecognition(input: FaceRequest):
|
||||||
def faceRecognition(input:face):
|
"""
|
||||||
start = time.time()
|
进行人脸识别的接口。
|
||||||
global facehelper
|
接收前端传递的图像数据,并使用 facehelper 进行人脸识别。
|
||||||
|
返回识别结果和运行时间。
|
||||||
|
"""
|
||||||
|
facehelper = get_facehelper()
|
||||||
|
start = time.time() # 记录开始时间
|
||||||
try:
|
try:
|
||||||
|
# 调用 facehelper 的 faceRecognition 方法进行人脸识别
|
||||||
ret_data = facehelper.faceRecognition(input.img)
|
ret_data = facehelper.faceRecognition(input.img)
|
||||||
print("finished recognition...")
|
|
||||||
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 {"status":f"{e.code}", "detail":f"{e}"}
|
raise HTTPException(status_code=400, detail={'code': e.code, 'msg': str(e), 'data': None})
|
||||||
else:
|
finally:
|
||||||
|
end = time.time() # 记录结束时间
|
||||||
|
# 打印运行时间
|
||||||
|
print(f"Recognition finished. Runtime: {end - start:.2f} seconds")
|
||||||
|
|
||||||
|
# 返回识别结果
|
||||||
return ret_data
|
return ret_data
|
||||||
return {"status":1, "name":identity, "resImg":res_img_base64}
|
|
||||||
|
|
||||||
@app.post("/featuredetect")
|
@app.post("/featuredetect")
|
||||||
@app.post("/featuredetect/")
|
def featureDetect(input: FaceRequest):
|
||||||
def featureDetect(input:face):
|
"""
|
||||||
start = time.time()
|
特征检测接口。
|
||||||
global facehelper
|
接收前端传递的图像数据,并使用 facehelper 进行特征检测。
|
||||||
|
返回检测结果和运行时间。
|
||||||
|
"""
|
||||||
|
facehelper = get_facehelper()
|
||||||
|
start = time.time() # 记录开始时间
|
||||||
try:
|
try:
|
||||||
|
# 调用 facehelper 的 featureDetect 方法进行特征检测
|
||||||
ret_data = facehelper.featureDetect(input.img)
|
ret_data = facehelper.featureDetect(input.img)
|
||||||
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 {"status":f"{e.code}", "detail":f"{e}"}
|
raise HTTPException(status_code=400, detail={'code': e.code, 'msg': str(e), 'data': None})
|
||||||
else:
|
finally:
|
||||||
|
end = time.time() # 记录结束时间
|
||||||
|
# 打印运行时间
|
||||||
|
print(f"Feature detection finished. Runtime: {end - start:.2f} seconds")
|
||||||
|
|
||||||
|
# 返回检测结果
|
||||||
return ret_data
|
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]
|
|
||||||
|
|
||||||
|
@app.post("/updatedb")
|
||||||
|
def updateDB(input: DBFaceRequest):
|
||||||
|
"""
|
||||||
|
更新人脸数据库的接口。
|
||||||
|
接收前端传递的数据库操作数据,并使用 facehelper 更新数据库。
|
||||||
|
"""
|
||||||
|
facehelper = get_facehelper()
|
||||||
try:
|
try:
|
||||||
|
# 加锁确保数据库更新操作的线程安全性
|
||||||
with lock:
|
with lock:
|
||||||
facehelper.updateDB(input.img, input.optMode, input.uniqueKey)
|
facehelper.updateDB(input.img, input.optMode, input.uniqueKey)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {'code': e.code, 'msg': f"{e}", 'data': 'None'}
|
# 处理异常,返回相应的错误代码和信息
|
||||||
# return {"status":f"{e.code}", "detail":f"{e}"}
|
raise HTTPException(status_code=400, detail={'code': e.code, 'msg': str(e), 'data': None})
|
||||||
else:
|
else:
|
||||||
return {'code': "30002", 'msg': ErrorMsg['30002'], 'data': 'None'}
|
# 成功更新数据库后返回相应的状态码和消息
|
||||||
|
return {'code': "30002", 'msg': ErrorMsg['30002'], 'data': None}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/imageComparison")
|
||||||
|
def imageComparison(input: ImageComparison):
|
||||||
|
"""
|
||||||
|
更新人脸数据库的接口。
|
||||||
|
接收前端传递的数据库操作数据,并使用 facehelper 更新数据库。
|
||||||
|
"""
|
||||||
|
facehelper = get_facehelper()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 加锁确保数据库更新操作的线程安全性
|
||||||
|
with lock:
|
||||||
|
state = facehelper.imageComparison(input.firstImage, input.secondImage)
|
||||||
|
except Exception as e:
|
||||||
|
# 处理异常,返回相应的错误代码和信息
|
||||||
|
raise HTTPException(status_code=400, detail={'code': e.code, 'msg': str(e), 'data': None})
|
||||||
|
else:
|
||||||
|
# 成功更新数据库后返回相应的状态码和消息
|
||||||
|
return {'code': "30002", 'msg': ErrorMsg['30002'], 'data': bool(state)}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue