旋转图片检测到人脸后计算旋转前的图片的人脸中心点

This commit is contained in:
Yuukiy
2021-12-25 19:32:45 +08:00
parent f4fd67a420
commit 2753ec991b
+51 -22
View File
@@ -1,12 +1,13 @@
"""使用Open CV识别图像"""
import os
import cv2
import cv2 as cv
import sys
import math
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from core.datatype import mei_path
face_cascade = cv2.CascadeClassifier(mei_path('data/haarcascade_frontalface_default.xml'))
face_cascade = cv.CascadeClassifier(mei_path('data/haarcascade_frontalface_default.xml'))
# https://github.com/PyImageSearch/imutils/blob/master/imutils/convenience.py#L41
@@ -17,7 +18,7 @@ def rotate_bound(image, angle):
# grab the rotation matrix (applying the negative of the angle to rotate clockwise),
# then grab the sine and cosine (i.e., the rotation components of the matrix)
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
M = cv.getRotationMatrix2D((cX, cY), -angle, 1.0)
cosine = abs(M[0, 0])
sine = abs(M[0, 1])
@@ -30,7 +31,7 @@ def rotate_bound(image, angle):
M[1, 2] += (nH / 2) - cY
# perform the actual rotation and return the image
return cv2.warpAffine(image, M, (nW, nH))
return cv.warpAffine(image, M, (nW, nH))
def isValidArea(area):
@@ -43,40 +44,68 @@ def isValidArea(area):
return False
def calc_origin_area(img, area, rotate, ori_size):
"""计算图片中指定区域的中心点在旋转前的位置"""
# 计算area的中心点以图片中心点为坐标原点的新坐标
(h, w) = img.shape[:2]
Ox, Oy = (h//2, w//2)
new_x = area[0] + area[2]//2 - Ox
new_y = area[1] + area[3]//2 - Oy
# 计算area的中心点做反向旋转后的坐标
anti_rotate = math.radians(-rotate)
anti_x = new_x * math.cos(anti_rotate) - new_y * math.sin(anti_rotate)
anti_y = new_x * math.sin(anti_rotate) + new_y * math.cos(anti_rotate)
# 计算area的中心点在原图坐标系中的坐标
(ori_h, ori_w) = ori_size
origin_x = int(ori_w // 2 + anti_x)
origin_y = int(ori_h // 2 + anti_y)
return (origin_x, origin_y)
def detect_faces(img_path, debug=False):
img = cv2.imread(img_path)
faces = []
"""检测图片中的人脸并返回第一个有效的人脸区域"""
img = cv.imread(img_path)
# OpenCV的检测效果似乎受图像角度影响很大,因此要尝试旋转图片
rotate_angles = (0, 10, -10, 20, -20, 30, -30, 40, -40, 50, -50, 60, -60)
for angle in rotate_angles:
rotated = rotate_bound(img, angle)
grey = cv2.cvtColor(rotated, cv2.COLOR_BGR2GRAY)
grey = cv.cvtColor(rotated, cv.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(grey, 1.1, 4)
for area in faces:
if isValidArea(area) and (not debug):
return area
ori_pos = calc_origin_area(rotated, area, angle, img.shape[:2])
return ori_pos
# 有效区域用绿框标记,无效区域用红框标记
color = (0, 255, 0) if isValidArea(area) else (0, 0, 255)
(x, y, w, h) = area
cv2.rectangle(rotated, (x, y), (x+w, y+h), color, 2)
cv.rectangle(rotated, (x, y), (x+w, y+h), color, 2)
recog_info = f'({x},{y}), {w}x{h}'
(h2, w2) = rotated.shape[:2]
pos = (min(w2-170, x), max(y-10, 12)) # 避免信息显示到绘图区外
cv2.putText(rotated, recog_info, pos, cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
cv.putText(rotated, recog_info, pos, cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
if debug:
win_name = "Actress detection"
cv2.imshow(win_name, rotated)
cv2.setWindowTitle(win_name, win_name + f" with ratotion {angle} degrees")
cv2.waitKey(0)
return faces
cv.imshow(win_name, rotated)
cv.setWindowTitle(win_name, win_name + f" with ratotion {angle} degrees")
cv.waitKey(0)
def crop_by_face(img_path, debug=False):
"""根据指定的人脸区域裁剪图片"""
face_center = detect_faces(img_path)
if debug:
if not os.path.exists('debug'):
os.mkdir('debug')
img = cv.imread(img_path)
cv.circle(img, face_center, 100, (0,255,0), 1)
filepath, ext = os.path.splitext(img_path)
output = 'debug/' + filepath + '_opencv' + ext
cv.imwrite(output, img)
return face_center
if __name__ == "__main__":
import argparse
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to the image file")
args = vars(ap.parse_args(['-i', 'YPPx.jpg']))
detect_faces(args['image'], True)
if len(sys.argv) < 2:
sys.argv.append('siro-4701.jpg')
for img_path in sys.argv[1:]:
crop_by_face(img_path, True)