使用OpenCV检测图像中人的矩形头像。

19
我有许多年鉴人像图片,正在尝试构建一个算法来检测这些人像。至少要正确检测矩形人像。示例1 示例2 我正在探索三个方向:
  1. 人脸检测
  2. 黑色矩形检测(因为肖像通常是在较亮的背景上的较暗形状中)
  3. 从OCR文本中提取人名
通过结合以上三种算法的结果,我希望能得到一些方法,适用于许多不同的年鉴页面。
非常感谢任何关于矩形检测的帮助。 我开始使用Java和OpenCV 3编写代码。
以下是我应用于一张图片的代码:
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat source = Imgcodecs.imread("Path/to/image", Imgcodecs.CV_LOAD_IMAGE_ANYCOLOR);
Mat destination = new Mat(source.rows(), source.cols(), source.type());

Imgproc.cvtColor(source, destination, Imgproc.COLOR_RGB2GRAY);
Imgproc.GaussianBlur(destination, destination, new Size(5, 5), 0, 0, Core.BORDER_DEFAULT);

int threshold = 100;
Imgproc.Canny(destination, destination, 50, 100);
Imgproc.Canny(destination, destination, threshold, threshold*3);

在这一点上,我有如下结果: 输入图像描述 尝试从上面的边缘中找到轮廓:
    List<MatOfPoint> contourDetections = new ArrayList<>();
    Mat hierarchy = new Mat();

    // Find contours
    Imgproc.findContours(destination, contourDetections, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

    // Draw contours 
    Imgproc.drawContours(source, contours, -1, new Scalar(255,0,0), 2);

获得以下结果: 在此输入图像描述

但不确定如何从这些轮廓中提取矩形,因为许多线条是不完整的。

回到边缘并尝试使用HoughLinesP查找垂直和水平线:

    Mat lines = new Mat();
    int thre = 50;
    int minLineSize = 250;
    int lineGap = 80;

    int ignoreLinesShorter = 300;

    Imgproc.HoughLinesP(destination, lines, 1, Math.PI/180, thre, minLineSize, lineGap);

    for(int c = 0; c < lines.rows(); c++) {

        double[] vec = lines.get(c, 0);

        double  x1 = vec[0],
                y1 = vec[1],
                x2 = vec[2],
                y2 = vec[3];

        // Filtering only verticat and horizontal lines
        if(x1 == x2 || y1 == y2) {

            // Filtering out short lines
            if(Math.abs(x1 - x2) > ignoreLinesShorter || Math.abs(y1 - y2) > ignoreLinesShorter) {

              Point start = new Point(x1, y1);
              Point end = new Point(x2, y2);

              // Draw line
              Imgproc.line(source, start, end, new Scalar(0,0,255), 2);
            }
        }
    }

结果:

enter image description here

和等高线一样,我仍然没有看到正确的矩形可以检测。你能帮我指出正确的方向吗?也许有更简单的方法来执行这个任务吗?


1
轮廓不完整是因为边缘不完整。你试过在 Canny 中使用更低的阈值吗?此外,你可以使用“contourArea”按大小过滤掉较小的轮廓。 - alkasm
1
如何将轮廓的阈值增加,然后延伸所有垂直和水平线? - Top.Deck
你是否曾经能够开发出一个强大的算法来检测年鉴中的矩形肖像?我想用这个算法处理我母校的85本年鉴。我提供了一个比@sturkmen使用reduce()函数更好的答案,但它仍然不够强大。 - Jakub
2个回答

4

这不是一个完整的答案,但可能是有用的。

我使用以下代码获取下面的图像。

要理解代码,您可以参考我的旧回答,网址为http://answers.opencv.org/question/85884

如果看起来很有前途,我们将一起努力改进它。

enter image description here

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;

int main(int argc, char** argv)
{
    Mat img = imread("e:/test/twHVm.jpg");
    if (img.empty())
        return -1;

    Mat resized, gray, reduced_h, reduced_w;
    resize(img, resized, Size(), 1, 1);

    cvtColor(resized, gray, CV_BGR2GRAY);

    reduce(gray, reduced_h, 0, REDUCE_AVG);
    reduce(gray, reduced_w, 1, REDUCE_AVG);


    for (int i = 0; i < img.cols; i++)
    {
        if (reduced_h.at<uchar>(0, i) > 200) // this is experimental value
        line(resized, Point(i, 0), Point(i, img.rows), Scalar(0, 255, 0), 1);
    }

    for (int i = 0; i < img.rows; i++)
    {
        if (reduced_w.at<uchar>(i, 0) > 225) // this is experimental value
        line(resized, Point(0, i), Point(img.cols, i), Scalar(0, 255, 0), 1);
    }

    imshow("result", resized);
    waitKey(0);
    return 0;
}

嗨,我曾经在一个类似的项目上工作过。我有一些照片被旋转了,我必须让它们看起来正常(旋转到正常状态)。所以我设法检测它们是水平还是垂直,并将它们旋转。但无法检测它们是否镜像。 - Alexander Mladzhov
你可以提供一个样例图片和代码,我来看看能否帮助你。请只提供文本内容,不要进行解释。 - sturkmen
你的结果很好,谢谢。我会看一下你的代码。 - Max Chernopolsky

1
检测矩形肖像(头像)时,我已经成功地使用以下方法:
  1. 矩形检测:
    a. 转换为灰度
    b. 将图像边框颜色更改为背景颜色
    c. 二值阈值处理
    d. 闭形态变换
    e. 如有必要,反转图像
    f. 查找轮廓
    g. 基于长宽比和面积选择矩形轮廓
  2. 人脸检测:使用Haar级联查找包含头像的矩形轮廓。对于头像肖像,矩形应仅包含一个面部,其尺寸相对于矩形的大小指定。例如,在使用OpenCv CascadeClassifier.detectMultiScale时,将minSize =(0.4 * width,0.3 * height)设置为面部尺寸,其中height和width是矩形的尺寸。
  3. 肖像验证:检查肖像矩形的网格结构。对于缺失的矩形网格,检查是否存在面部。对于现有的矩形,使用网格结构在必要时更正矩形尺寸。

1. 矩形检测的Python代码(可以轻松转换为Java)

img = cv2.imread('example.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# Remove black border by cropping
bw = 6 # border width
ht, wd = img.shape[:2] # height, width
gray = gray[bw:ht-bw, bw:wd-bw]

# HISTOGRAM -- Put histogram function here to determine the following:
bg_color = (235,235,235) # background color
thresh_value = 220

# Add back border with background color
gray = cv2.copyMakeBorder(gray, bw, bw, bw, bw, cv2.BORDER_CONSTANT, value=bg_color)

# Binary Threshold
thresh = cv2.threshold(gray, thresh_value, 255, cv2.THRESH_BINARY)[1] # orig: 235

# Closing Morphological Transformation
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# Invert Image
closing = np.invert(closing)

# Find contours
cnts = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

# Find portraits by specifying range of sizes and aspect ratios
img_area = ht * wd
for cnt in cnts:
    x,y,w,h = cv2.boundingRect(cnt)
    if w*h < 0.005*img_area or w*h > 0.16*img_area or h/w < 0.95 or h/w > 1.55:
        continue
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

cv2.imshow('Result', img)
cv2.waitKey(0)

示例1结果(第一张图片是反转后的。) 示例1

示例2结果 示例2


2. 人脸检测的 Python 代码

def is_headshot(cnt_img):
    gray = cv2.cvtColor(cnt_img, cv2.COLOR_BGR2GRAY)
    height, width = cnt_img.shape[:2]
    min_size = int(max(0.4*width, 0.3*height))
    faces = face_cascade.detectMultiScale(gray, 
                                          scaleFactor=1.3, 
                                          minNeighbors=3, 
                                          minSize=(min_size, min_size))
    if len(faces) == 1:
        return True
    else:
        return False

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
x,y,w,h = cv2.boundingRect(cnt) # bounding rectangle of contour found in code above
if is_headshot(img[y:y+h, x:x+w]):
    cv2.imwrite('headshot.jpg', img[y:y+h, x:x+w])

3. 人像验证的Python代码
可以使用我在stackoverflow question中发布的代码找到网格结构。循环遍历完成的网格结果。每个网格元素由(x,y,w,h)定义,其中w和h可以是上面发现的肖像的平均宽度和高度。使用shapely.geometry中的函数box1.intersection(box2)确定是否缺少或大小不合适的人像。如果交集区域很小或为零,则可能会有一个缺失的人像,然后应该使用人脸检测进行检查。如果有兴趣,我可以提供更多细节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接