使用 OpenCV 检测图像上的矩形人像
Detect rectangular portraits of people on images with OpenCV
我有很多带有人物肖像的年鉴图片,我正在尝试构建一个算法来检测这些肖像。至少,检测正确的矩形肖像。 Example 1 Example 2
我正在尝试调查三个方向:
- 人脸检测
- 暗矩形检测(因为肖像通常是较亮背景上较暗的形状)
- 从 OCR 文本中提取人名
通过结合以上三种算法的结果,我希望得到一些适用于许多不同年鉴页面的方法。
对于矩形检测的任何帮助,我将不胜感激。
我从 Java 和 OpenCV 3.
开始
这是我申请的代码 an image:
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);
}
}
}
结果:
与轮廓一样,我仍然没有看到可以检测到的正确矩形。你能帮我一个正确的方向吗?也许有更简单的方法来执行此任务?
这不是一个完整的答案,但可能有用。
我用下面的代码得到了下面的图像。
要了解代码,您可以参考我在 http://answers.opencv.org/question/85884
的旧答案
如果它看起来很有希望,我们会一起努力改进它。
#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;
}
为了检测矩形肖像(爆头),我使用以下方法取得了一些成功。
- 矩形检测:
一个。转换为灰度
b。将图像边框颜色更改为背景颜色
c。二进制阈值
d.关闭形态变换
e。必要时反转图像
f。查找轮廓
克。根据纵横比和面积选择矩形轮廓
- 人脸检测:使用 Haar 级联查找包含头像的矩形轮廓。要成为一张爆头肖像,一个矩形应该只包含一张脸,脸的尺寸是根据矩形的大小指定的。例如,当使用 OpenCv CascadeClassifier.detectMultiScale 时,将 minSize=(0.4 * width, 0.3 * height) 设置为面部尺寸,其中高度和宽度是矩形的尺寸。
- 纵向验证:检查纵向矩形的网格结构。对于网格中缺失的矩形,检查面是否存在。对于现有的矩形,必要时使用网格结构来修正矩形尺寸。
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结果 (第一张图是反转后的)
示例 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 肖像验证代码
可以使用我在此 Whosebug question 中发布的代码找到网格结构。循环完成网格的结果。每个网格元素由 (x,y,w,h) 定义,其中 w 和 h 可以是上面找到的肖像的平均宽度和高度。使用 shapely.geometry 中的函数 box1.intersection(box2) 来确定是否有缺失或错误的肖像。如果相交区域很小或为零,则可能缺少人像,然后应使用人脸检测进行检查。如果有任何兴趣,我愿意提供更多详细信息。
我有很多带有人物肖像的年鉴图片,我正在尝试构建一个算法来检测这些肖像。至少,检测正确的矩形肖像。 Example 1 Example 2
我正在尝试调查三个方向:
- 人脸检测
- 暗矩形检测(因为肖像通常是较亮背景上较暗的形状)
- 从 OCR 文本中提取人名
通过结合以上三种算法的结果,我希望得到一些适用于许多不同年鉴页面的方法。
对于矩形检测的任何帮助,我将不胜感激。 我从 Java 和 OpenCV 3.
开始这是我申请的代码 an image:
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);
}
}
}
结果:
与轮廓一样,我仍然没有看到可以检测到的正确矩形。你能帮我一个正确的方向吗?也许有更简单的方法来执行此任务?
这不是一个完整的答案,但可能有用。
我用下面的代码得到了下面的图像。
要了解代码,您可以参考我在 http://answers.opencv.org/question/85884
的旧答案如果它看起来很有希望,我们会一起努力改进它。
#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;
}
为了检测矩形肖像(爆头),我使用以下方法取得了一些成功。
- 矩形检测:
一个。转换为灰度
b。将图像边框颜色更改为背景颜色
c。二进制阈值
d.关闭形态变换
e。必要时反转图像
f。查找轮廓
克。根据纵横比和面积选择矩形轮廓 - 人脸检测:使用 Haar 级联查找包含头像的矩形轮廓。要成为一张爆头肖像,一个矩形应该只包含一张脸,脸的尺寸是根据矩形的大小指定的。例如,当使用 OpenCv CascadeClassifier.detectMultiScale 时,将 minSize=(0.4 * width, 0.3 * height) 设置为面部尺寸,其中高度和宽度是矩形的尺寸。
- 纵向验证:检查纵向矩形的网格结构。对于网格中缺失的矩形,检查面是否存在。对于现有的矩形,必要时使用网格结构来修正矩形尺寸。
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结果 (第一张图是反转后的)
示例 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 肖像验证代码
可以使用我在此 Whosebug question 中发布的代码找到网格结构。循环完成网格的结果。每个网格元素由 (x,y,w,h) 定义,其中 w 和 h 可以是上面找到的肖像的平均宽度和高度。使用 shapely.geometry 中的函数 box1.intersection(box2) 来确定是否有缺失或错误的肖像。如果相交区域很小或为零,则可能缺少人像,然后应使用人脸检测进行检查。如果有任何兴趣,我愿意提供更多详细信息。