【问题标题】:Resizing non uniform images with precise face location使用精确的面部位置调整非均匀图像的大小
【发布时间】:2020-12-11 02:26:06
【问题描述】:

我在一家拍摄学校照片的工作室工作,我们正在尝试制作一个脚本,以消除将每张照片裁剪为模板的工作。我们使用的照片相当统一,但它们的分辨率和头部位置略有不同。我开始尝试编写脚本,但我的 Python 知识相当有限,并且通过大量的反复试验和在线资源,我认为我已经完成了大部分工作。

目前,我正在尝试找出从 NumPy 数组中裁剪图像的最佳方法,并将头部放在我想要的位置,但我找不到一个好的灵活解决方案。对于姿势 1 和姿势 2,头部的位置需要稍有不同,因此它需要易于动态更改(可能会实现某种简单的 GUI 来输入类似的东西,但现在我可以更改代码)。

我还需要能够更改照片的输出分辨率,以便它们都是统一的 (2000x2500)。有人有什么想法吗?

目前这是我当前的代码,它只是保存检测到的人脸正方形:

import cv2
import os.path
import glob

# Cascade path
cascPath = 'haarcascade_frontalface_default.xml'

# Create the haar cascade
faceCascade = cv2.CascadeClassifier(cascPath)

#Check for output folder and create if its not there
if not os.path.exists('output'):
    os.makedirs('output')

# Read Images
images = glob.glob('*.jpg')
for c, i in enumerate(images):
    image = cv2.imread(i, 1)

    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Find face(s) using cascade
    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,  # size of groups
        minNeighbors=5,  # How many groups around are detected as face for it to be valid
        minSize=(500, 500)  # Min size in pixels for face
    )

    # Outputs number of faces found in image
    print('Found {0} faces!'.format(len(faces)))

    # Places a rectangle on face
    for (x, y, w, h) in faces:
        imgCrop = image[y:y+h,x:x+w]

    if len(faces) > 0:
        #Saves Images to output folder with OG name
        cv2.imwrite('output/'+ i, imgCrop)

我可以像这样使用它:

    # Crop Padding
    left = 300
    right = 300
    top = 400
    bottom = 1000
    
    for (x, y, w, h) in faces:
        imgCrop = image[y-top:y+h+bottom, x-left:x+w+right]

但是输出相当随机的分辨率和基于图像分辨率的变化

【问题讨论】:

    标签: python python-3.x opencv crop


    【解决方案1】:

    TL;DR

    • 要使用尺寸设置新分辨率,您可以使用cv2.resize。可能会有像素损失,所以可以使用插值法。

    • 新调整大小的图像可能是BGR格式,因此您可能需要转换为RGB格式。

    cv2.resize(src=crop, dsize=(2000, 2500), interpolation=cv2.INTER_LANCZOS4)
    crop = cv2.cvtColor(crop, cv2.COLOR_BGR2RGB)  # Make sure the cropped image is in RGB format
    cv2.imwrite("image-1.png", crop)
    

    建议:

    • 一种方法是使用 python 的 face-recognition 库。

    • 该方法是使用两个样本图像进行训练。

    • 根据训练图像预测下一张图像。

    例如,以下是训练图像:

    我们要预测下图中的人脸:

    当我们得到训练图像的面部编码并应用于下一张图像时:

    import face_recognition
    import numpy as np
    import matplotlib.pyplot as plt
    from PIL import Image, ImageDraw
    
    # Load a sample picture and learn how to recognize it.
    first_image = face_recognition.load_image_file("images/ex.jpg")
    first_face_encoding = face_recognition.face_encodings(first_image)[0]
    
    # Load a second sample picture and learn how to recognize it.
    second_image = face_recognition.load_image_file("images/index.jpg")
    sec_face_encoding = face_recognition.face_encodings(second_image)[0]
    
    # Create arrays of known face encodings and their names
    known_face_encodings = [
        first_face_encoding,
        sec_face_encoding
    ]
    
    print('Learned encoding for', len(known_face_encodings), 'images.')
    
    # Load an image with an unknown face
    unknown_image = face_recognition.load_image_file("images/babes.jpg")
    
    # Find all the faces and face encodings in the unknown image
    face_locations = face_recognition.face_locations(unknown_image)
    face_encodings = face_recognition.face_encodings(unknown_image, face_locations)
    
    # Convert the image to a PIL-format image so that we can draw on top of it with the Pillow library
    # See http://pillow.readthedocs.io/ for more about PIL/Pillow
    pil_image = Image.fromarray(unknown_image)
    # Create a Pillow ImageDraw Draw instance to draw with
    draw = ImageDraw.Draw(pil_image)
    
    # Loop through each face found in the unknown image
    for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
        matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
        face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
        best_match_index = np.argmin(face_distances)
        draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255), width=5)
    
    # Remove the drawing library from memory as per the Pillow docs
    del draw
    
    # Display the resulting image
    plt.imshow(pil_image)
    plt.show()
    

    输出将是:

    以上是我的建议。当您使用当前图像创建新分辨率时,会出现像素损失。因此,您需要使用 interpolation 方法。

    例如:找到人脸位置后,选择原始图像中的坐标。

    # Add after draw.rectangle function.
    crop = unknown_image[top:bottom, left:right]
    

    使用 2000 x 2500 大小设置新分辨率并使用 CV2.INTERN_LANCZOS4 进行插值。

    可能的问题:为什么是CV2.INTERN_LANCZOS4

    当然,你可以选择任何你喜欢的,但建议in this postCV2.INTERN_LANCZOS4

    cv2.resize(src=crop, dsize=(2000, 2500), interpolation=cv2.INTER_LANCZOS4)
    

    保存图片

    crop = cv2.cvtColor(crop, cv2.COLOR_BGR2RGB)  # Make sure the cropped image is in RGB format
    cv2.imwrite("image-1.png", crop)
    

    输出约为 4.3 MB,因此我无法在此处显示。

    从最终结果中,我们清楚地看到并识别出人脸。该库精确地找到图像中的人脸。

    你可以做什么:

    • 您可以使用自己设置的训练图像,也可以使用上面的示例。

    • 对每张图像应用人脸识别功能,使用经过训练的人脸位置并将结果保存在目录中。

    【讨论】:

    • Haarcascade 特征是古老而强大的技术。这种技术可以提高您工作的准确性。我建议给一个机会。
    • 这似乎与我目前遇到的问题相同。输出最终是随机分辨率和纵横比。
    • @Tristan.Beer 我已经更新了我的答案。您可以查看 TL;DR 部分。
    • 好的,我有点喜欢这种方法更好的openCV检测。有没有办法在原始图像中获取人脸位置?所以如果它找到一张脸,它可以告诉我脸框的中心在哪里,以像素为单位?这样,如果我理解正确,我可以采用该值并可能相应地进行裁剪(例如,如果头部低于图像顶部的裁剪顶部然后底部以使中心位于正确的位置)。
    • 没关系,box 0, 0 存储在 x, y 变量中。我会试一试这个实现!
    【解决方案2】:

    这是我如何裁剪我想要的方式,它被添加到“输出面数”功能的正下方

    #Get the face postion and output values into variables, might not be needed but I did it
        for (x, y, w, h) in faces:
            xdis = x
            ydis = y
            w = w
            h = h
    
        #Get scale value by dividing wanted head hight by detected head hight
        ws = 600/w
        hs = 600/h
    
        #scale image to get head to right size, uses bilinear interpolation by default
        scale = cv2.resize(image,(0,0),fx=hs,fy=ws)
    
        #calculate head postion for given values
        sxdis = int(xdis*ws) #applying scale to x distance and turning it into a integer
        sydis = int(ydis*hs) #applying scale to y distance and turning it into a integer
        sycent = sydis+300 #adding half head hight to get center
        ystart = sycent-700 #subtract where you want the head center to be in pixels, this is for the vertical
        yend = ystart+2500 #Add whatever you want vertical resolution to be
        xcent = sxdis+300 #adding half head hight to get center
        xstart = xcent-1000 #subtract where you want the head center to be in pixels, this is for the horizontal
        xend = xstart+2000 #add whatever you want the horizontal resolution to be
    
        #Crop the image
        cropped = scale[ystart:yend, xstart:xend]
    

    它一团糟,但它完全按照我想要的方式工作。 由于速度原因,最终选择了 openCV 而不是切换到 python-Recognition,但如果我可以让多线程在 python-recognition 中工作,我可能会切换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-29
      • 2011-10-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多