在图像/视频中裁剪出人脸地区

[复制链接]
发表于 2025-10-13 12:22:30 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1. 在图像中裁剪人脸地区
  1. import face_alignment
  2. import skimage.io
  3. import numpy
  4. from argparse import ArgumentParser
  5. from skimage import img_as_ubyte
  6. from skimage.transform import resize
  7. from tqdm import tqdm
  8. import os
  9. import numpy as np
  10. import warnings
  11. warnings.filterwarnings("ignore")
  12. # =================================================================================================
  13. # Kun(20250306): 输出裁剪图像的命令
  14. # =================================================================================================
  15. def extract_bbox(frame, fa):
  16.     if max(frame.shape[0], frame.shape[1]) > 640:
  17.         scale_factor =  max(frame.shape[0], frame.shape[1]) / 640.0
  18.         frame = resize(frame, (int(frame.shape[0] / scale_factor), int(frame.shape[1] / scale_factor)))
  19.         frame = img_as_ubyte(frame)
  20.     else:
  21.         scale_factor = 1
  22.     frame = frame[..., :3]
  23.     bboxes = fa.face_detector.detect_from_image(frame[..., ::-1])
  24.     if len(bboxes) == 0:
  25.         return []
  26.     return np.array(bboxes)[:, :-1] * scale_factor
  27. def bb_intersection_over_union(boxA, boxB):
  28.     xA = max(boxA[0], boxB[0])
  29.     yA = max(boxA[1], boxB[1])
  30.     xB = min(boxA[2], boxB[2])
  31.     yB = min(boxA[3], boxB[3])
  32.     interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)
  33.     boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)
  34.     boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)
  35.     iou = interArea / float(boxAArea + boxBArea - interArea)
  36.     return iou
  37. def join(tube_bbox, bbox):
  38.     xA = min(tube_bbox[0], bbox[0])
  39.     yA = min(tube_bbox[1], bbox[1])
  40.     xB = max(tube_bbox[2], bbox[2])
  41.     yB = max(tube_bbox[3], bbox[3])
  42.     return (xA, yA, xB, yB)
  43. def compute_bbox(tube_bbox, frame_shape, inp, image_shape, increase_area=0.1):
  44.     left, top, right, bot = tube_bbox
  45.     width = right - left
  46.     height = bot - top
  47.     #Computing aspect preserving bbox
  48.     width_increase = max(increase_area, ((1 + 2 * increase_area) * height - width) / (2 * width))
  49.     height_increase = max(increase_area, ((1 + 2 * increase_area) * width - height) / (2 * height))
  50.     left = int(left - width_increase * width)
  51.     top = int(top - height_increase * height)
  52.     right = int(right + width_increase * width)
  53.     bot = int(bot + height_increase * height)
  54.     top, bot, left, right = max(0, top), min(bot, frame_shape[0]), max(0, left), min(right, frame_shape[1])
  55.     h, w = bot - top, right - left
  56.     scale = f'{image_shape[0]}:{image_shape[1]}'
  57.     return f'ffmpeg -i {inp} -filter:v "crop={w}:{h}:{left}:{top}, scale={scale}" crop.png'
  58. def process_image(args):
  59.     device = 'cpu' if args.cpu else 'cuda'
  60.     fa = face_alignment.FaceAlignment(face_alignment.LandmarksType.TWO_D, flip_input=False, device=device)
  61.     frame = skimage.io.imread(args.inp)
  62.     frame_shape = frame.shape
  63.     bboxes = extract_bbox(frame, fa)
  64.     if len(bboxes) == 0:
  65.         print("No faces detected.")
  66.         return []
  67.     tube_bbox = bboxes[0]  # Assuming we take the first detected face
  68.     command = compute_bbox(tube_bbox, frame_shape, inp=args.inp, image_shape=args.image_shape, increase_area=args.increase)
  69.     return [command]
  70. if __name__ == "__main__":
  71.     parser = ArgumentParser()
  72.     parser.add_argument("--image_shape", default=(256, 256), type=lambda x: tuple(map(int, x.split(','))),
  73.                         help="Image shape")
  74.     parser.add_argument("--increase", default=0.1, type=float, help='Increase bbox by this amount')
  75.     parser.add_argument("--iou_with_initial", type=float, default=0.25, help="The minimal allowed iou with inital bbox")
  76.     parser.add_argument("--inp", required=True, help='Input image')
  77.     parser.add_argument("--cpu", dest="cpu", action="store_true", help="cpu mode.")
  78.     args = parser.parse_args()
  79.     commands = process_image(args)
  80.     for command in commands:
  81.         print(command)
复制代码
2. 在视频中裁剪人脸地区
  1. import face_alignment
  2. import skimage.io
  3. import numpy
  4. from argparse import ArgumentParser
  5. from skimage import img_as_ubyte
  6. from skimage.transform import resize
  7. from tqdm import tqdm
  8. import os
  9. import imageio
  10. import numpy as np
  11. import warnings
  12. warnings.filterwarnings("ignore")
  13. # =================================================================================================
  14. # Kun(20250306): 输出裁剪视频的命令
  15. # =================================================================================================
  16. def extract_bbox(frame, fa):
  17.     if max(frame.shape[0], frame.shape[1]) > 640:
  18.         scale_factor =  max(frame.shape[0], frame.shape[1]) / 640.0
  19.         frame = resize(frame, (int(frame.shape[0] / scale_factor), int(frame.shape[1] / scale_factor)))
  20.         frame = img_as_ubyte(frame)
  21.     else:
  22.         scale_factor = 1
  23.     frame = frame[..., :3]
  24.     bboxes = fa.face_detector.detect_from_image(frame[..., ::-1])
  25.     if len(bboxes) == 0:
  26.         return []
  27.     return np.array(bboxes)[:, :-1] * scale_factor
  28. def bb_intersection_over_union(boxA, boxB):
  29.     xA = max(boxA[0], boxB[0])
  30.     yA = max(boxA[1], boxB[1])
  31.     xB = min(boxA[2], boxB[2])
  32.     yB = min(boxA[3], boxB[3])
  33.     interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)
  34.     boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)
  35.     boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)
  36.     iou = interArea / float(boxAArea + boxBArea - interArea)
  37.     return iou
  38. def join(tube_bbox, bbox):
  39.     xA = min(tube_bbox[0], bbox[0])
  40.     yA = min(tube_bbox[1], bbox[1])
  41.     xB = max(tube_bbox[2], bbox[2])
  42.     yB = max(tube_bbox[3], bbox[3])
  43.     return (xA, yA, xB, yB)
  44. def compute_bbox(start, end, fps, tube_bbox, frame_shape, inp, image_shape, increase_area=0.1):
  45.     left, top, right, bot = tube_bbox
  46.     width = right - left
  47.     height = bot - top
  48.     #Computing aspect preserving bbox
  49.     width_increase = max(increase_area, ((1 + 2 * increase_area) * height - width) / (2 * width))
  50.     height_increase = max(increase_area, ((1 + 2 * increase_area) * width - height) / (2 * height))
  51.     left = int(left - width_increase * width)
  52.     top = int(top - height_increase * height)
  53.     right = int(right + width_increase * width)
  54.     bot = int(bot + height_increase * height)
  55.     top, bot, left, right = max(0, top), min(bot, frame_shape[0]), max(0, left), min(right, frame_shape[1])
  56.     h, w = bot - top, right - left
  57.     start = start / fps
  58.     end = end / fps
  59.     time = end - start
  60.     scale = f'{image_shape[0]}:{image_shape[1]}'
  61.     return f'ffmpeg -i {inp} -ss {start} -t {time} -filter:v "crop={w}:{h}:{left}:{top}, scale={scale}" crop.mp4'
  62. def compute_bbox_trajectories(trajectories, fps, frame_shape, args):
  63.     commands = []
  64.     for i, (bbox, tube_bbox, start, end) in enumerate(trajectories):
  65.         if (end - start) > args.min_frames:
  66.             command = compute_bbox(start, end, fps, tube_bbox, frame_shape, inp=args.inp, image_shape=args.image_shape, increase_area=args.increase)
  67.             commands.append(command)
  68.     return commands
  69. def process_video(args):
  70.     device = 'cpu' if args.cpu else 'cuda'
  71.     fa = face_alignment.FaceAlignment(face_alignment.LandmarksType.TWO_D, flip_input=False, device=device)
  72.     video = imageio.get_reader(args.inp)
  73.     trajectories = []
  74.     previous_frame = None
  75.     fps = video.get_meta_data()['fps']
  76.     commands = []
  77.     try:
  78.         for i, frame in tqdm(enumerate(video)):
  79.             frame_shape = frame.shape
  80.             bboxes =  extract_bbox(frame, fa)
  81.             ## For each trajectory check the criterion
  82.             not_valid_trajectories = []
  83.             valid_trajectories = []
  84.             for trajectory in trajectories:
  85.                 tube_bbox = trajectory[0]
  86.                 intersection = 0
  87.                 for bbox in bboxes:
  88.                     intersection = max(intersection, bb_intersection_over_union(tube_bbox, bbox))
  89.                 if intersection > args.iou_with_initial:
  90.                     valid_trajectories.append(trajectory)
  91.                 else:
  92.                     not_valid_trajectories.append(trajectory)
  93.             commands += compute_bbox_trajectories(not_valid_trajectories, fps, frame_shape, args)
  94.             trajectories = valid_trajectories
  95.             ## Assign bbox to trajectories, create new trajectories
  96.             for bbox in bboxes:
  97.                 intersection = 0
  98.                 current_trajectory = None
  99.                 for trajectory in trajectories:
  100.                     tube_bbox = trajectory[0]
  101.                     current_intersection = bb_intersection_over_union(tube_bbox, bbox)
  102.                     if intersection < current_intersection and current_intersection > args.iou_with_initial:
  103.                         intersection = bb_intersection_over_union(tube_bbox, bbox)
  104.                         current_trajectory = trajectory
  105.                 ## Create new trajectory
  106.                 if current_trajectory is None:
  107.                     trajectories.append([bbox, bbox, i, i])
  108.                 else:
  109.                     current_trajectory[3] = i
  110.                     current_trajectory[1] = join(current_trajectory[1], bbox)
  111.     except IndexError as e:
  112.         raise (e)
  113.     commands += compute_bbox_trajectories(trajectories, fps, frame_shape, args)
  114.     return commands
  115. if __name__ == "__main__":
  116.     parser = ArgumentParser()
  117.     parser.add_argument("--image_shape", default=(256, 256), type=lambda x: tuple(map(int, x.split(','))),
  118.                         help="Image shape")
  119.     parser.add_argument("--increase", default=0.1, type=float, help='Increase bbox by this amount')
  120.     parser.add_argument("--iou_with_initial", type=float, default=0.25, help="The minimal allowed iou with inital bbox")
  121.     parser.add_argument("--inp", required=True, help='Input image or video')
  122.     parser.add_argument("--min_frames", type=int, default=150,  help='Minimum number of frames')
  123.     parser.add_argument("--cpu", dest="cpu", action="store_true", help="cpu mode.")
  124.     args = parser.parse_args()
  125.     commands = process_video(args)
  126.     for command in commands:
  127.         print (command)
  128.         
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表