def get_person_image(personId): "生成并返回人物图像的filepath" conn = db.get_connection() try: sql = ("select face.id, photo.path, face.rotation, face.location" " from tbl_face face" " inner join tbl_photo photo on face.photo_id = photo.id" " where face.person_id = %s" " order by photo.taken_time desc" " limit 1") with conn.cursor() as cursor: cursor.execute(sql, (personId, )) row = cursor.fetchone() if row is None: return None faceId = row[0] photoPath = row[1] rotation = row[2] faceBox = row[3] # 截取人脸 image = cv2.imread(photoPath) logger = get_logger() logger.info(f"photoPath is {photoPath}") logger.info(f"rotation is {rotation} type {type(rotation)}") image = rotate_image(image, rotation) (top, right, bottom, left) = faceBox faceImage = image[top:bottom, left:right] # 保存到磁盘 os.makedirs(settings.PERSON_IMAGES_HOME, exist_ok=True) path = os.path.join(settings.PERSON_IMAGES_HOME, f"{faceId}.jpg") cv2.imwrite(path, faceImage) return path finally: db.put_connection(conn)
def count_photos(): conn = db.get_connection() try: with conn.cursor() as cursor: cursor.execute("SELECT count(id) from tbl_photo") row = cursor.fetchone() return row[0] finally: db.put_connection(conn)
def rename_person(personId, name): conn = db.get_connection() try: with conn.cursor() as cursor: cursor.execute("update tbl_person set name=%s where id=%s", (name, personId)) conn.commit() finally: db.put_connection(conn)
def list_photos(limit = 20, offset = 0): conn = db.get_connection() try: sql = f"SELECT id, digest from tbl_photo order by taken_time desc LIMIT {limit} OFFSET {offset}" with conn.cursor() as cursor: cursor.execute(sql) rows = cursor.fetchall() return [{"id": row[0], "digest": row[1]} for row in rows] finally: db.put_connection(conn)
def link_photos_to_person(photoIds, personId, newPersonId): "关联人物和照片, newPersonId可以为null" conn = db.get_connection() try: with conn.cursor() as cursor: ensure_hidden_person(cursor) cursor.execute( "update tbl_face set person_id=%s where person_id=%s and photo_id in %s", (newPersonId, personId, tuple(photoIds))) conn.commit() finally: db.put_connection(conn)
def create_person(name): "创建Person,返回person ID" conn = db.get_connection() try: with conn.cursor() as cursor: cursor.execute( "insert into tbl_person (name) values (%s) returning id", (name, )) row = cursor.fetchone() conn.commit() return row[0] finally: db.put_connection(conn)
def count_person_photos(personId): "返回包含某个人物的照片总数" conn = db.get_connection() try: sql = ("select count(DISTINCT photo.id)" " from tbl_photo photo" " inner join tbl_face face on photo.id = face.photo_id" " where face.person_id = %s") with conn.cursor() as cursor: cursor.execute(sql, (personId, )) row = cursor.fetchone() return row[0] finally: db.put_connection(conn)
def remove_persons(ids): conn = db.get_connection() try: with conn.cursor() as cursor: ensure_hidden_person(cursor) idsToRemove = tuple(ids) cursor.execute( "update tbl_face set person_id=-1 where person_id in %s", (idsToRemove, )) cursor.execute("delete from tbl_person where id in %s", (idsToRemove, )) conn.commit() finally: db.put_connection(conn)
def merge_persons(ids): conn = db.get_connection() try: with conn.cursor() as cursor: newId = ids[0] idsToRemove = tuple(ids[1:]) cursor.execute( "update tbl_face set person_id=%s where person_id in %s", (newId, idsToRemove)) cursor.execute("delete from tbl_person where id in %s", (idsToRemove, )) conn.commit() finally: db.put_connection(conn)
def test_grouping_result(): conn = db.get_connection() try: sql = ("select id, name from tbl_person where id != -1") with conn.cursor() as cursor: cursor.execute(sql) rows = cursor.fetchall() persons = [{"id": row[0], "name": row[1]} for row in rows] for person in persons: test_photos_in_person(cursor, person['id']) conn.commit() finally: db.put_connection(conn)
def reload_faces(): logger = get_logger() logger.info("=========== start reload_faces() ===========") conn = db.get_connection() try: with conn.cursor() as cursor: cursor.execute( "select id, path from tbl_photo where face_detect_done=false") rows = cursor.fetchall() logger.info("start to detect&encode faces") global time_detect_faces, time_encode_faces time_detect_faces = 0 time_encode_faces = 0 for (i, row) in enumerate(rows): photoId = row[0] photoPath = row[1] # 人脸检测、编码 encode_faces(photoId, photoPath, cursor) conn.commit() logger.info(f"encoding face {i}/{len(rows)}") logger.info(f"time_detect_faces: {time_detect_faces} seconds") logger.info(f"time_encode_faces: {time_encode_faces} seconds") logger.info("end detect&encode faces") # 自动估算最佳eps值 logger.info("start to calculate BestEps") start = time.time() global bestGuessEps bestGuessEps = select_cluster_param(cursor) end = time.time() logger.info(f"calculate BestEps takes {end-start} seconds") logger.info(f"DBSCAN param eps value: {bestGuessEps}") # 人脸分类 start = time.time() classify_faces(cursor) end = time.time() logger.info(f"classify_faces takes {end-start} seconds") conn.commit() # 人脸聚类 start = time.time() cluster_faces(cursor) end = time.time() logger.info(f"cluster_faces takes {end-start} seconds") conn.commit() finally: db.put_connection(conn) logger.info("end reload_faces()")
def list_person_photos(personId, limit=20, offset=0): "返回包含某个人物的照片列表" conn = db.get_connection() try: sql = ("select photo.id, photo.digest" " from tbl_photo photo" " inner join tbl_face face on photo.id = face.photo_id" " where face.person_id = %s" " group by photo.id" " order by photo.taken_time desc" " LIMIT %s OFFSET %s") with conn.cursor() as cursor: cursor.execute(sql, (personId, limit, offset)) rows = cursor.fetchall() return [{"id": row[0], "digest": row[1]} for row in rows] finally: db.put_connection(conn)
def list_persons(): conn = db.get_connection() try: sql = ( "select person.id, person.name, count(DISTINCT photo.id) photoCount" " from tbl_person person" " inner join tbl_face face on person.id = face.person_id" " inner join tbl_photo photo on face.photo_id = photo.id" " where person.id != -1" " group by person.id" " order by photoCount desc") with conn.cursor() as cursor: cursor.execute(sql) rows = cursor.fetchall() return [{ "id": row[0], "name": row[1], "photoCount": row[2] } for row in rows] finally: db.put_connection(conn)
def reload_photo(imagePath): logger = get_logger() conn = db.get_connection() try: with conn.cursor() as cursor: logger.info(f"reload image {imagePath}") if os.path.exists(imagePath): do_reload_photo(cursor, imagePath) else: # 文件被删除 cursor.execute("select id, digest from tbl_photo where path=%s", (imagePath,)) row = cursor.fetchone() if row: id = row[0] cursor.execute("delete from tbl_face where photo_id=%s", (id,)) cursor.execute("delete from tbl_photo where id=%s", (id,)) conn.commit() except Exception as e: logger.error(e) finally: db.put_connection(conn)
def get_photo_path(id): """获取照片preview的文件路径""" conn = db.get_connection() try: sql = "SELECT path,digest from tbl_photo where id=%(id)s" with conn.cursor() as cursor: cursor.execute(sql, {"id": id}) row = cursor.fetchone() if row is None: return None else: path = row[0] digest = row[1] img = cv2.imread(path) previewImg = preview_image(img, 800) previewPath = os.path.join(settings.SMALL_PHOTO_HOME, f"{digest}.jpg") os.makedirs(settings.SMALL_PHOTO_HOME, exist_ok=True) cv2.imwrite(previewPath, previewImg) return previewPath finally: db.put_connection(conn)
def do_reload_photos(): reloadStartTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) imagePaths = paths.list_images(settings.USER_PHOTO_HOME) conn = db.get_connection() try: with conn.cursor() as cursor: for imagePath in imagePaths: try: do_reload_photo(cursor, imagePath) conn.commit() except Exception as e: logger = get_logger() logger.error(e) # 删除文件不存在的photo记录及关联的face cursor.execute("select id from tbl_photo where last_scanned < TIMESTAMP %s", (reloadStartTime,)) rows = cursor.fetchall() for row in rows: id = row[0] cursor.execute("delete from tbl_face where photo_id=%s", (id,)) cursor.execute("delete from tbl_photo where id=%s", (id,)) conn.commit() finally: db.put_connection(conn)