Linux 检测人脸然后自动裁剪图片

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13211745/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 17:41:33  来源:igfitidea点击:

Detect face then autocrop pictures

pythonlinuxopencvcropface-detection

提问by AisIceEyes

I am trying to find an app that can detect faces in my pictures, make the detected face centered and crop 720 x 720 pixels of the picture. It is rather very time consuming & meticulous to edit around hundreds of pictures I plan to do that.

我试图找到一个应用程序,它可以检测我照片中的人脸,使检测到的人脸居中并裁剪 720 x 720 像素的图片。编辑我打算这样做的数百张图片相当耗时且一丝不苟。

I have tried doing this using python opencv mentioned herebut I think it is outdated. I've also tried using thisbut it's also giving me an error in my system. Also tried using face detection plugin for GIMP but it is designed for GIMP 2.6 but I am using 2.8 on a regular basis. I also tried doing what was posted at ultrahigh blog but it is very outdated (since I'm using a Precise derivative of Ubuntu, while the blogpost was made way back when it was still Hardy). Also tried using Phatch but there is no face detection so some cropped pictures have their face cut right off.

我曾尝试使用此处提到的 python opencv执行此操作但我认为它已过时。我也试过使用它,但它也在我的系统中给我一个错误。还尝试使用 GIMP 的人脸检测插件,但它是为 GIMP 2.6 设计的,但我经常使用 2.8。我也尝试过在 ultrahigh 博客上发布的内容,但它已经过时了(因为我使用的是 Ubuntu 的 Precise 衍生版,而该博文是在它还是 Hardy 的时候制作的)。还尝试使用 Phatch,但没有面部检测,因此一些裁剪后的照片的面部被直接剪掉了。

I have tried all of the above and wasted half a day trying to make any of the above do what I needed to do.

我已经尝试了上述所有方法,并浪费了半天时间试图使上述任何一个做我需要做的事情。

Do you guys have suggestion to achieve a goal to around 800 pictures I have.

你们有没有建议来实现我拥有的大约 800 张图片的目标。

My operating system is Linux Mint 13 MATE.

我的操作系统是 Linux Mint 13 MATE。

Note: I was going to add 2 more links but stackexchange prevented me to post two more links as I don't have much reputation yet.

注意:我打算再添加 2 个链接,但是 stackexchange 阻止我再发布两个链接,因为我还没有多少声誉。

采纳答案by Onlyjus

I have managed to grab bits of code from various sources and stitch this together. It is still a work in progress. Also, do you have any example images?

我设法从各种来源抓取了一些代码并将它们拼接在一起。它仍在进行中。另外,你有任何示例图像吗?

'''
Sources:
http://pythonpath.wordpress.com/2012/05/08/pil-to-opencv-image/
http://www.lucaamore.com/?p=638
'''

#Python 2.7.2
#Opencv 2.4.2
#PIL 1.1.7

import cv
import Image

def DetectFace(image, faceCascade):
    #modified from: http://www.lucaamore.com/?p=638

    min_size = (20,20)
    image_scale = 1
    haar_scale = 1.1
    min_neighbors = 3
    haar_flags = 0

    # Allocate the temporary images
    smallImage = cv.CreateImage(
            (
                cv.Round(image.width / image_scale),
                cv.Round(image.height / image_scale)
            ), 8 ,1)

    # Scale input image for faster processing
    cv.Resize(image, smallImage, cv.CV_INTER_LINEAR)

    # Equalize the histogram
    cv.EqualizeHist(smallImage, smallImage)

    # Detect the faces
    faces = cv.HaarDetectObjects(
            smallImage, faceCascade, cv.CreateMemStorage(0),
            haar_scale, min_neighbors, haar_flags, min_size
        )

    # If faces are found
    if faces:
        for ((x, y, w, h), n) in faces:
            # the input to cv.HaarDetectObjects was resized, so scale the
            # bounding box of each face and convert it to two CvPoints
            pt1 = (int(x * image_scale), int(y * image_scale))
            pt2 = (int((x + w) * image_scale), int((y + h) * image_scale))
            cv.Rectangle(image, pt1, pt2, cv.RGB(255, 0, 0), 5, 8, 0)

    return image

def pil2cvGrey(pil_im):
    #from: http://pythonpath.wordpress.com/2012/05/08/pil-to-opencv-image/
    pil_im = pil_im.convert('L')
    cv_im = cv.CreateImageHeader(pil_im.size, cv.IPL_DEPTH_8U, 1)
    cv.SetData(cv_im, pil_im.tostring(), pil_im.size[0]  )
    return cv_im

def cv2pil(cv_im):
    return Image.fromstring("L", cv.GetSize(cv_im), cv_im.tostring())


pil_im=Image.open('testPics/faces.jpg')
cv_im=pil2cv(pil_im)
#the haarcascade files tells opencv what to look for.
faceCascade = cv.Load('C:/Python27/Lib/site-packages/opencv/haarcascade_frontalface_default.xml')
face=DetectFace(cv_im,faceCascade)
img=cv2pil(face)
img.show()

Testing on the first page of Google (Googled "faces"): enter image description here

在谷歌的第一页上测试(谷歌搜索的“面孔”): 在此处输入图片说明



Update

更新

This code should do exactly what you want. Let me know if you have questions. I tried to include lots of comments in the code:

此代码应该完全符合您的要求。如果您有任何疑问,请告诉我。我试图在代码中包含很多注释:

'''
Sources:
http://opencv.willowgarage.com/documentation/python/cookbook.html
http://www.lucaamore.com/?p=638
'''

#Python 2.7.2
#Opencv 2.4.2
#PIL 1.1.7

import cv #Opencv
import Image #Image from PIL
import glob
import os

def DetectFace(image, faceCascade, returnImage=False):
    # This function takes a grey scale cv image and finds
    # the patterns defined in the haarcascade function
    # modified from: http://www.lucaamore.com/?p=638

    #variables    
    min_size = (20,20)
    haar_scale = 1.1
    min_neighbors = 3
    haar_flags = 0

    # Equalize the histogram
    cv.EqualizeHist(image, image)

    # Detect the faces
    faces = cv.HaarDetectObjects(
            image, faceCascade, cv.CreateMemStorage(0),
            haar_scale, min_neighbors, haar_flags, min_size
        )

    # If faces are found
    if faces and returnImage:
        for ((x, y, w, h), n) in faces:
            # Convert bounding box to two CvPoints
            pt1 = (int(x), int(y))
            pt2 = (int(x + w), int(y + h))
            cv.Rectangle(image, pt1, pt2, cv.RGB(255, 0, 0), 5, 8, 0)

    if returnImage:
        return image
    else:
        return faces

def pil2cvGrey(pil_im):
    # Convert a PIL image to a greyscale cv image
    # from: http://pythonpath.wordpress.com/2012/05/08/pil-to-opencv-image/
    pil_im = pil_im.convert('L')
    cv_im = cv.CreateImageHeader(pil_im.size, cv.IPL_DEPTH_8U, 1)
    cv.SetData(cv_im, pil_im.tostring(), pil_im.size[0]  )
    return cv_im

def cv2pil(cv_im):
    # Convert the cv image to a PIL image
    return Image.fromstring("L", cv.GetSize(cv_im), cv_im.tostring())

def imgCrop(image, cropBox, boxScale=1):
    # Crop a PIL image with the provided box [x(left), y(upper), w(width), h(height)]

    # Calculate scale factors
    xDelta=max(cropBox[2]*(boxScale-1),0)
    yDelta=max(cropBox[3]*(boxScale-1),0)

    # Convert cv box to PIL box [left, upper, right, lower]
    PIL_box=[cropBox[0]-xDelta, cropBox[1]-yDelta, cropBox[0]+cropBox[2]+xDelta, cropBox[1]+cropBox[3]+yDelta]

    return image.crop(PIL_box)

def faceCrop(imagePattern,boxScale=1):
    # Select one of the haarcascade files:
    #   haarcascade_frontalface_alt.xml  <-- Best one?
    #   haarcascade_frontalface_alt2.xml
    #   haarcascade_frontalface_alt_tree.xml
    #   haarcascade_frontalface_default.xml
    #   haarcascade_profileface.xml
    faceCascade = cv.Load('haarcascade_frontalface_alt.xml')

    imgList=glob.glob(imagePattern)
    if len(imgList)<=0:
        print 'No Images Found'
        return

    for img in imgList:
        pil_im=Image.open(img)
        cv_im=pil2cvGrey(pil_im)
        faces=DetectFace(cv_im,faceCascade)
        if faces:
            n=1
            for face in faces:
                croppedImage=imgCrop(pil_im, face[0],boxScale=boxScale)
                fname,ext=os.path.splitext(img)
                croppedImage.save(fname+'_crop'+str(n)+ext)
                n+=1
        else:
            print 'No faces found:', img

def test(imageFilePath):
    pil_im=Image.open(imageFilePath)
    cv_im=pil2cvGrey(pil_im)
    # Select one of the haarcascade files:
    #   haarcascade_frontalface_alt.xml  <-- Best one?
    #   haarcascade_frontalface_alt2.xml
    #   haarcascade_frontalface_alt_tree.xml
    #   haarcascade_frontalface_default.xml
    #   haarcascade_profileface.xml
    faceCascade = cv.Load('haarcascade_frontalface_alt.xml')
    face_im=DetectFace(cv_im,faceCascade, returnImage=True)
    img=cv2pil(face_im)
    img.show()
    img.save('test.png')


# Test the algorithm on an image
#test('testPics/faces.jpg')

# Crop all jpegs in a folder. Note: the code uses glob which follows unix shell rules.
# Use the boxScale to scale the cropping area. 1=opencv box, 2=2x the width and height
faceCrop('testPics/*.jpg',boxScale=1)

Using the image above, this code extracts 52 out of the 59 faces, producing cropped files such as: enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here

使用上面的图片,这段代码从 59 张人脸中提取了 52 张,生成了裁剪的文件,例如: 在此处输入图片说明在此处输入图片说明在此处输入图片说明在此处输入图片说明在此处输入图片说明在此处输入图片说明在此处输入图片说明在此处输入图片说明

回答by ckoerner

This sounds like it might be a better question for one of the more (computer) technology focused exchanges.

这听起来对于更多(计算机)技术集中的交易所之一来说可能是一个更好的问题。

That said, have you looked into something like thisjquery face detection script? I don't know how savvy you are, but it is one option that is OS independent.

也就是说,您是否研究过类似jquery 人脸检测脚本的内容?我不知道你有多精明,但这是一种独立于操作系统的选择。

This solutionalso looks promising, but would require Windows.

这个解决方案看起来也很有前途,但需要 Windows。

回答by Lri

I used this shell command:

我使用了这个shell命令:

for f in *.jpg;do PYTHONPATH=/usr/local/lib/python2.7/site-packages python -c 'import cv2;import sys;rects=cv2.CascadeClassifier("/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml").detectMultiScale(cv2.cvtColor(cv2.imread(sys.argv[1]),cv2.COLOR_BGR2GRAY),1.3,5);print("\n".join([" ".join([str(item) for item in row])for row in rects]))' $f|while read x y w h;do convert $f -gravity NorthWest -crop ${w}x$h+$x+$y ${f%jpg}-$x-$y.png;done;done

for f in *.jpg;do PYTHONPATH=/usr/local/lib/python2.7/site-packages python -c 'import cv2;import sys;rects=cv2.CascadeClassifier("/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml").detectMultiScale(cv2.cvtColor(cv2.imread(sys.argv[1]),cv2.COLOR_BGR2GRAY),1.3,5);print("\n".join([" ".join([str(item) for item in row])for row in rects]))' $f|while read x y w h;do convert $f -gravity NorthWest -crop ${w}x$h+$x+$y ${f%jpg}-$x-$y.png;done;done

You can install opencvand imagemagickon OS X with brew install opencv imagemagick.

您可以安装opencv,并imagemagick在OS X上使用brew install opencv imagemagick

回答by Katerina

Another available option is dlib, which is based on machine learning approaches.

另一个可用选项是dlib,它基于机器学习方法。

import dlib
from PIL import Image
from skimage import io
import matplotlib.pyplot as plt


def detect_faces(image):

    # Create a face detector
    face_detector = dlib.get_frontal_face_detector()

    # Run detector and get bounding boxes of the faces on image.
    detected_faces = face_detector(image, 1)
    face_frames = [(x.left(), x.top(),
                    x.right(), x.bottom()) for x in detected_faces]

    return face_frames

# Load image
img_path = 'test.jpg'
image = io.imread(img_path)

# Detect faces
detected_faces = detect_faces(image)

# Crop faces and plot
for n, face_rect in enumerate(detected_faces):
    face = Image.fromarray(image).crop(face_rect)
    plt.subplot(1, len(detected_faces), n+1)
    plt.axis('off')
    plt.imshow(face)

enter image description hereenter image description here

在此处输入图片说明在此处输入图片说明

回答by Hernán Acosta

I think the best option is Google Vision API. It's updated, it uses machine learning and it improves with the time.

我认为最好的选择是 Google Vision API。它已经更新,它使用机器学习,并且随着时间的推移而改进。

You can check the documentation for examples: https://cloud.google.com/vision/docs/other-features

您可以查看文档以获取示例:https: //cloud.google.com/vision/docs/other-features

回答by Israel-abebe

the above codes work but this is recent implementation using OpenCV I was unable to run the above by the latest and found something that works (from various places)

上面的代码有效,但这是最近使用 OpenCV 的实现我无法通过最新的方式运行上面的代码并找到了一些有效的东西(来自不同的地方)

import cv2
import os

def facecrop(image):
    facedata = "haarcascade_frontalface_alt.xml"
    cascade = cv2.CascadeClassifier(facedata)

    img = cv2.imread(image)

    minisize = (img.shape[1],img.shape[0])
    miniframe = cv2.resize(img, minisize)

    faces = cascade.detectMultiScale(miniframe)

   for f in faces:
        x, y, w, h = [ v for v in f ]
        cv2.rectangle(img, (x,y), (x+w,y+h), (255,255,255))

        sub_face = img[y:y+h, x:x+w]
        fname, ext = os.path.splitext(image)
        cv2.imwrite(fname+"_cropped_"+ext, sub_face)



    return



facecrop("1.jpg")

回答by Abhishek Singh

Autocropworked out for me pretty well. It is as easy as autocrop -i pics -o crop -w 400 -H 400. You can get the usage in their readme file.

Autocrop对我来说效果很好。就这么简单autocrop -i pics -o crop -w 400 -H 400。您可以在他们的自述文件中获取用法。

usage: [-h] [-o OUTPUT] [-i INPUT] [-w WIDTH] [-H HEIGHT] [-v]

Automatically crops faces from batches of pictures

optional arguments:
  -h, --help            Show this help message and exit
  -o, --output, -p, --path
            Folder where cropped images will be placed.
            Default: current working directory
  -i, --input
            Folder where images to crop are located.
            Default: current working directory
  -w, --width
            Width of cropped files in px. Default=500
  -H, --height
            Height of cropped files in px. Default=500
  -v, --version         Show program's version number and exit

回答by Mrunal

Just adding to @Israel Abebe's version. If you add a counter before image extension the algorithm will give all the faces detected. Attaching the code, same as Israel Abebe's. Just adding a counter and accepting the cascade file as an argument. The algorithm works beautifully! Thanks @Israel Abebe for this!

只是添加到@Israel Abebe 的版本中。如果在图像扩展之前添加计数器,算法将给出所有检测到的人脸。附上代码,与 Israel Abebe 的相同。只需添加一个计数器并接受级联文件作为参数。该算法工作得很好!感谢@Israel Abebe 为此!

import cv2
import os
import sys

def facecrop(image):
facedata = sys.argv[1]
cascade = cv2.CascadeClassifier(facedata)

img = cv2.imread(image)

minisize = (img.shape[1],img.shape[0])
miniframe = cv2.resize(img, minisize)

faces = cascade.detectMultiScale(miniframe)
counter = 0
for f in faces:
    x, y, w, h = [ v for v in f ]
    cv2.rectangle(img, (x,y), (x+w,y+h), (255,255,255))

    sub_face = img[y:y+h, x:x+w]
    fname, ext = os.path.splitext(image)
    cv2.imwrite(fname+"_cropped_"+str(counter)+ext, sub_face)
    counter += 1
return

facecrop("Face_detect_1.jpg")

PS: Adding as answer. Was not able to add comment because of points issue.

PS:添加为答案。由于积分问题,无法添加评论。

回答by ashish bansal

Detect face and then crop and save the cropped image into folder ..

检测人脸,然后裁剪并将裁剪后的图像保存到文件夹中..

import numpy as np
import cv2 as cv
face_cascade = cv.CascadeClassifier('./haarcascade_frontalface_default.xml')
#eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml')
img = cv.imread('./face/nancy-Copy1.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
    cv.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]
    #eyes = eye_cascade.detectMultiScale(roi_gray)
    #for (ex,ey,ew,eh) in eyes:
     #   cv.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
    sub_face = img[y:y+h, x:x+w]
    face_file_name = "face/" + str(y) + ".jpg"
    plt.imsave(face_file_name, sub_face)
plt.imshow(sub_face)



回答by Anupam Bera

I have developed an application "Face-Recognition-with-Own-Data-Set" using the python package ‘face_recognition'and ‘opencv-python'.

我使用 python 包'face_recognition''opencv-python'开发了一个应用程序“Face-Recognition-with-Own-Data-Set” 。

The source code and installation guide is in the GitHub - Face-Recognition-with-Own-Data-Set

源码和安装指南在GitHub - Face-Recognition-with-Own-Data-Set

Or run the source -

或运行源 -

import face_recognition
import cv2
import numpy as np

import os
'''
    Get current working director and create a Data directory to store the faces
'''
currentDirectory = os.getcwd()
dirName = os.path.join(currentDirectory, 'Data')
print(dirName)
if not os.path.exists(dirName):
    try:
        os.makedirs(dirName)
    except:
        raise OSError("Can't create destination directory (%s)!" % (dirName))
'''
    For the given path, get the List of all files in the directory tree 
'''
def getListOfFiles(dirName):
    # create a list of file and sub directories
    # names in the given directory
    listOfFile = os.listdir(dirName)
    allFiles = list()
    # Iterate over all the entries
    for entry in listOfFile:
        # Create full path
        fullPath = os.path.join(dirName, entry)
        # If entry is a directory then get the list of files in this directory
        if os.path.isdir(fullPath):
            allFiles = allFiles + getListOfFiles(fullPath)
        else:
            allFiles.append(fullPath)

    return allFiles

def knownFaceEncoding(listOfFiles):
    known_face_encodings=list()
    known_face_names=list()
    for file_name in listOfFiles:
        # print(file_name)
        if(file_name.lower().endswith(('.png', '.jpg', '.jpeg'))):
            known_image = face_recognition.load_image_file(file_name)
            # known_face_locations = face_recognition.face_locations(known_image)
            # known_face_encoding = face_recognition.face_encodings(known_image,known_face_locations)
            face_encods = face_recognition.face_encodings(known_image)
            if face_encods:
                known_face_encoding = face_encods[0]
                known_face_encodings.append(known_face_encoding)
                known_face_names.append(os.path.basename(file_name[0:-4]))
    return known_face_encodings, known_face_names


# Get the list of all files in directory tree at given path
listOfFiles = getListOfFiles(dirName)
known_face_encodings, known_face_names = knownFaceEncoding(listOfFiles)

video_capture = cv2.VideoCapture(0)
cv2.namedWindow("Video", flags= cv2.WINDOW_NORMAL)
# cv2.namedWindow("Video")

cv2.resizeWindow('Video', 1024,640)
cv2.moveWindow('Video', 20,20)


# Initialize some variables
face_locations = []
face_encodings = []
face_names = []
process_this_frame = True


while True:
    # Grab a single frame of video
    ret, frame = video_capture.read()
    # print(ret)
    # Resize frame of video to 1/4 size for faster face recognition processing
    small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
    # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
    rgb_small_frame = small_frame[:, :, ::-1]


    k = cv2.waitKey(1)
    # Hit 'c' on capture the image!
    # Hit 'q' on the keyboard to quit!
    if k == ord('q'):
        break
    elif k== ord('c'):
        face_loc = face_recognition.face_locations(rgb_small_frame)
        if face_loc:
            print("Enter Name -")
            name = input()
            img_name = "{}/{}.png".format(dirName,name)
            (top, right, bottom, left)= face_loc[0]
            top *= 4
            right *= 4
            bottom *= 4
            left *= 4
            cv2.imwrite(img_name, frame[top - 5 :bottom + 5,left -5 :right + 5])
            listOfFiles = getListOfFiles(dirName)
            known_face_encodings, known_face_names = knownFaceEncoding(listOfFiles)

    # Only process every other frame of video to save time
    if process_this_frame:
        # Find all the faces and face encodings in the current frame of video
        face_locations = face_recognition.face_locations(rgb_small_frame)
        face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
        # print(face_locations)

        face_names = []

        for face_encoding,face_location in zip(face_encodings,face_locations):
            # See if the face is a match for the known face(s)
            matches = face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance= 0.55)
            name = "Unknown"
            distance = 0

            # use the known face with the smallest distance to the new face
            face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
            #print(face_distances)
            if len(face_distances) > 0:
                best_match_index = np.argmin(face_distances)
                if matches[best_match_index]:
                    name = known_face_names[best_match_index]
                    # distance = face_distances[best_match_index]
            #print(face_distances[best_match_index])
            # string_value = '{} {:.3f}'.format(name, distance)
            face_names.append(name)


    process_this_frame = not process_this_frame


    # Display the results
    for (top, right, bottom, left), name in zip(face_locations, face_names):
        # Scale back up face locations since the frame we detected in was scaled to 1/4 size
        top *= 4
        right *= 4
        bottom *= 4
        left *= 4

        # Draw a box around the face
        cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)

        # Draw a label with a name below the face
        cv2.rectangle(frame, (left, bottom + 46), (right, bottom+11), (0, 0, 155), cv2.FILLED)
        font = cv2.FONT_HERSHEY_DUPLEX
        cv2.putText(frame, name, (left + 6, bottom +40), font, 1.0, (255, 255, 255), 1)

    # Display the resulting image
    cv2.imshow('Video', frame)

# Release handle to the webcam
video_capture.release()
cv2.destroyAllWindows()

It will create a 'Data' directory in the current location even if this directory does not exist.

即使该目录不存在,它也会在当前位置创建一个“数据”目录。

When a face is marked with a rectangle, press 'c'to capture the image and in the command prompt, it will ask for the name of the face. Put the name of the image and enter. You can find this image in the 'Data'directory.

当用矩形标记人脸时,按“c”键捕获图像,在命令提示符下,它会询问人脸的名称。输入图像名称并输入。您可以在“数据”目录中找到此图像。