Simple Python: Face-aware image crop

简介

Cropman是Github上一个开源的项目,基于opencv2,提供对图片的识别和裁剪的Python接口,项目地址.

项目主页给出了简单的使用方法,通过命令行接收需要处理的图片、裁剪的尺寸、需要保存的图片名,cropper.py为主文件。源程序首先识别图片检测图片中的人脸坐标,如果没有识别到人脸则返回原图片的中心点,如果检测到人脸,则根据返回的人脸坐标值(上左下右4个值)计算中心点,得到中心点后根据已提供的需要裁剪的尺寸计算实际的裁剪尺寸(以上一步得到中心点为中心点,给出的宽高为宽高,同时校验有效宽高),返回相对于原图的裁剪坐标。

hack

稍作修改如下:

def crop(self, img, target_width, target_height):
    original_height,  original_width = img.shape[:2]
    faces = self.detector.detect_faces(img)
    if len(faces) == 0:                         # 未识别到人脸
        #print 'No face'
        return None, None, None, None
    else:                                       # 识别到人脸
        top, left, bottom, right = self._bounding_rect(faces)
        #print 'Get face:', top, left, bottom, right
        return top, left, bottom, right

后续的程序中直接调用此方法来获取人脸识别后的坐标,因为人脸识别现阶段也没有100%有效的实现,此程序也只是对系统中图片处理中不足的一个补充,并不能完全依赖,需要结合实际的业务需求实现逻辑:

....
@classmethod
def crop(cls, size, scale, localname):
    original_width, original_height = size
    if scale == '4:3':
        sx, sy, sx_max, sy_max = 4, 3, 640, 480
    else:
        sx, sy, sx_max, sy_max = 16, 9, 640, 360
    cropper = Cropper()
    try:
        localname = cls.convert_img_to_jpg(localname)
        input_image = cv2.imread(localname)
    except IOError:
        input_image = None
    if input_image is None:
        return None
    top, left, bottom, right = cropper.crop(input_image, original_width, original_height)
    if top:
        for i in range(1000):
            suit_x, suit_y = i * sx, i * sy
            if suit_x > original_width or suit_y > original_height:
                suit_x, suit_y = suit_x - 4, suit_y - 3
                break
        bottom -= top/2
        x1 = int((original_width - suit_x)/2.0)
        y1 = int((bottom - suit_y)/2.0) if bottom > suit_y else 0
        x2 = original_width - x1
        y2 = (bottom - y1) if bottom > suit_y else suit_y
        input_image = input_image[y1:y2, x1:x2]
        new_localname = RandomName.randomname()
        try:
            cv2.imwrite(new_localname, input_image)
            return new_localname
        except IOError:
            return None
    else:
        for i in range(1000):
            suit_x, suit_y = i * sx, i * sy
            if suit_x > original_width or suit_y > original_height:
                suit_x, suit_y = suit_x - 4, suit_y - 3
                break
        x1 = int((original_width - suit_x)/2.0) if original_width > suit_x else 0
        y1 = 0
        x2 = original_width - x1 if original_width > suit_x else suit_x
        y2 = suit_y
        input_image = input_image[y1:y2, x1:x2]
        new_localname = RandomName.randomname()
        try:
            cv2.imwrite(new_localname, input_image)
            return new_localname
        except IOError:
            return None