twain2/ImageOutHole.cpp

316 lines
9.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "StdAfx.h"
#include "ImageOutHole.h"
#include <memory>
#include <math.h>
#include "opencv2/opencv.hpp"
#include "opencv/cv.h"
#include "opencv2/core/core.hpp"
using namespace cv;
using namespace std;
ImageOutHole::ImageOutHole(void)
{
}
ImageOutHole::~ImageOutHole(void)
{
}
void ImageOutHole::puncture(Mat& front, Mat& back, double threshold, float edgeScale, double areaThreshold)
{
//二值化正反面图像
//FileTools::write_log("D:\\1.txt", "enter ImageOutHole apply");
threshold = min(max(threshold, 1.0), 254.0);
Mat front_thre = threshold_mat(front, threshold);
Mat back_thre = threshold_mat(back, threshold);
//反面二值化图像水平翻转
flip(back_thre, back_thre, 1); //1:Horizontal
//正反面图像寻边
vector<vector<Point>> contours_front, contours_back;
vector<Vec4i> b1_front, b1_back;
findContours22(front_thre.clone(), contours_front, b1_front);
findContours22(back_thre.clone(), contours_back, b1_back);
#if 0
imwrite("front_thre.bmp", front_thre);
imwrite("back_thre.bmp", back_thre);
#endif
//提取正反面图像最大轮廓
vector<Point> maxContour_front = getMaxContour(contours_front, b1_front);
vector<Point> maxContour_back = getMaxContour(contours_back, b1_back);
RotatedRect rrect_front = minAreaRect(maxContour_front); //提取正面最大轮廓的最小外接矩形
RotatedRect rrect_back = minAreaRect(maxContour_back); //提取反面最大轮廓的最小外接矩形
//提取正反面图像重叠部分区域
Rect roi_front, roi_back;
RotatedRect mask_rotatedRect;
getRoi(rrect_front, rrect_back, Size(front.cols, front.rows), roi_front, roi_back, mask_rotatedRect);
Mat roiMat_front(front_thre, roi_front); //在正面二值图像中截取重叠部分
Mat roiMat_back(back_thre, roi_back); //在反面二值图像中截取重叠部分
//正反面二值图像做或运算真正镂空区域保留0其他地方填充为255
//为了避免孔洞彻底贯穿纸边,认为绘制纸张轮廓,确保所有孔洞为封闭图形,不会与背景粘连。
#if 0
imwrite("front_thre.bmp", roiMat_front);
imwrite("back_thre.bmp", roiMat_back);
#endif
Mat mask;
bitwise_or(roiMat_front, roiMat_back, mask); //或运算,正反面二值图像重叠
vector<Point> vertices_point = getVertices(mask_rotatedRect);
polylines(mask, vertices_point, true, Scalar(255), 3); //绘制纸张矩形边缘
//二值图像重叠图像颜色取反,并提取轮廓
vector<vector<Point>> contours_mask;
vector<Vec4i> b1_mask;
bitwise_not(mask, mask); //取反
findContours22(mask.clone(), contours_mask, b1_mask); //提取重叠图像轮廓
//过滤非孔洞的联通区域
filterPoly(mask, contours_mask, mask_rotatedRect, edgeScale, areaThreshold);
//膨胀算法,增大孔洞连通区域面积
dilate(mask, mask, Mat(), Point(-1, -1), 3, BORDER_DEFAULT, Scalar(255));
vector<Point> rect_poly;
rect_poly.push_back(Point(2, 2));
rect_poly.push_back(Point(2, mask.rows - 2));
rect_poly.push_back(Point(mask.cols - 2, mask.rows - 2));
rect_poly.push_back(Point(mask.cols - 2, 2));
polylines(mask, rect_poly, true, Scalar(0), 5); //把mask边缘涂黑
#if 0
imwrite("mask.bmp", mask);
#endif
//填充正面图像孔洞
Mat mask_flip;
flip(mask, mask_flip, 1); //因为之前反面图像翻转,所以现在要再翻转回去
roi_back.x = back.cols - roi_back.width - roi_back.x; //因为之前反面图像翻转所以现在ROI也要进行相应翻转
fillPuncture(front, mask, roi_front); //正面孔洞填充
fillPuncture(back, mask_flip, roi_back); //反面孔洞填充
#if 0
imwrite("111.jpg", front);
imwrite("222.jpg", back);
#endif
//FileTools::write_log("D:\\1.txt", "Exit ImageOutHole apply");
}
cv::Mat ImageOutHole::threshold_mat(const Mat& src, double threshold)
{
Mat dst(src.rows, src.cols, CV_8UC1, 1);
if (src.channels() == 3)
{
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
cv::threshold(gray, dst, threshold, 255, THRESH_BINARY);
}
else
{
cv::threshold(src, dst, threshold, 255, THRESH_BINARY);
}
return dst;
}
void ImageOutHole::findContours22(const Mat& src, vector<vector<Point>>& contours, vector<Vec4i>& hierarchy,
int retr/* = RETR_CCOMP*/, int method/* = CHAIN_APPROX_SIMPLE*/, Point offset /*= Point(0, 0)*/)
{
CvMat c_image = src;
MemStorage storage(cvCreateMemStorage());
CvSeq* _ccontours = nullptr;
cvFindContours(&c_image, storage, &_ccontours, sizeof(CvContour), retr, method, CvPoint(offset));
if (!_ccontours)
{
contours.clear();
return;
}
Seq<CvSeq*> all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage));
int total = (int)all_contours.size();
contours.resize(total);
SeqIterator<CvSeq*> it = all_contours.begin();
for (int i = 0; i < total; i++, ++it)
{
CvSeq* c = *it;
((CvContour*)c)->color = (int)i;
int count = (int)c->total;
int* data = new int[count * 2];
cvCvtSeqToArray(c, data);
for (int j = 0; j < count; j++)
{
contours[i].push_back(Point(data[j * 2], data[j * 2 + 1]));
}
delete[] data;
}
hierarchy.resize(total);
it = all_contours.begin();
for (int i = 0; i < total; i++, ++it)
{
CvSeq* c = *it;
int h_next = c->h_next ? ((CvContour*)c->h_next)->color : -1;
int h_prev = c->h_prev ? ((CvContour*)c->h_prev)->color : -1;
int v_next = c->v_next ? ((CvContour*)c->v_next)->color : -1;
int v_prev = c->v_prev ? ((CvContour*)c->v_prev)->color : -1;
hierarchy[i] = Vec4i(h_next, h_prev, v_next, v_prev);
}
}
vector<Point> ImageOutHole::getMaxContour(const vector<vector<Point>>& contours, const vector<Vec4i>& hierarchy, int areaThreshold /*= 20000*/)
{
vector<Point> maxContour;
for (size_t i = 0, length = hierarchy.size(); i < length; i++)
{
Vec4i h = hierarchy[i];
if (h[3] == -1)
{
if (contourArea(contours[i]) > areaThreshold)
{
maxContour = contours[i];
break;
}
}
}
return maxContour;
}
void ImageOutHole::getRoi(RotatedRect rrect_front, RotatedRect rrect_back, Size srcSize, Rect& roi_front, Rect& roi_back, RotatedRect& mask_rotatedRect)
{
Size size((rrect_front.size.width + rrect_back.size.width) / 2, (rrect_front.size.height + rrect_back.size.height) / 2);
float angle = (rrect_front.angle + rrect_back.angle) / 2;
rrect_front.size = rrect_back.size = size;
rrect_front.angle = rrect_back.angle = angle;
roi_front = rrect_front.boundingRect();
roi_back = rrect_back.boundingRect();
if (roi_front.width!=roi_back.width||roi_front.height!=roi_back.height)
{
roi_front.height=roi_back.height;
roi_front.width=roi_back.width;
}
Point offset(0, 0);
int top = min(roi_front.y, roi_back.y);
if (top < 0)
{
roi_front.y -= top;
roi_back.y -= top;
roi_front.height += top;
roi_back.height += top;
offset.y += top;
}
int left = min(roi_front.x, roi_back.x);
if (left < 0)
{
roi_front.x -= left;
roi_back.x -= left;
roi_front.width += left;
roi_back.width += left;
offset.x += left;
}
int right = max(roi_front.x + roi_front.width, roi_back.x + roi_back.width);
if (right >= srcSize.width)
{
roi_front.width -= (right - srcSize.width + 1);
roi_back.width -= (right - srcSize.width + 1);
}
int bottom = max(roi_front.y + roi_front.height, roi_back.y + roi_back.height);
if (bottom >= srcSize.height)
{
roi_front.height -= (bottom - srcSize.height + 1);
roi_back.height -= (bottom - srcSize.height + 1);
}
mask_rotatedRect.center = Point((roi_front.width + offset.x) / 2, (roi_front.height + offset.y) / 2);
mask_rotatedRect.size = size;
mask_rotatedRect.angle = angle;
}
Point ImageOutHole::rotatedPoint(Point p, Point center, double angle)
{
double cos_ = cos(angle / 180 * CV_PI);
double sin_ = sin(angle / 180 * CV_PI);
double x = (p.x - center.x) * cos_ - (p.y - center.y) * sin_ + center.x;
double y = (p.y - center.y) * cos_ + (p.x - center.x) * sin_ + center.y;
return Point(x, y);
}
vector<Point> ImageOutHole::getVertices(RotatedRect rect)
{
vector<Point> points;
Point leftTop(rect.center.x - rect.size.width / 2, rect.center.y - rect.size.height / 2);
Point leftBottom(rect.center.x - rect.size.width / 2, rect.center.y + rect.size.height / 2);
Point rightTop(rect.center.x + rect.size.width / 2, rect.center.y - rect.size.height / 2);
Point rigthBottom(rect.center.x + rect.size.width / 2, rect.center.y + rect.size.height / 2);
points.push_back(rotatedPoint(leftTop, rect.center, rect.angle));
points.push_back(rotatedPoint(leftBottom, rect.center, rect.angle));
points.push_back(rotatedPoint(rigthBottom, rect.center, rect.angle));
points.push_back(rotatedPoint(rightTop, rect.center, rect.angle));
return points;
}
void ImageOutHole::filterPoly(const Mat& mask, vector<vector<Point>>& contours, RotatedRect roi, float edgeScale, double areaThreshold)
{
edgeScale = min(0.49f, max(edgeScale, 0.0f));
RotatedRect roi2(roi.center, Size(roi.size.width * (1 - edgeScale * 2), roi.size.height * (1 - edgeScale * 2)), roi.angle);
vector<Point> vertices_roi1 = getVertices(roi);
vector<Point> vertices_roi2 = getVertices(roi2);
vector<Point> contour;
for (size_t i = 0, length = contours.size(); i < length; i++)
{
//contour = /*Mat_<Point>*/(contours[i]);
if (contourArea(contours[i]) < areaThreshold)
{
fillConvexPoly(mask, contours[i], Scalar(0));
continue;
}
for (int j = 0, count = contours[i].size(); j < count; j++)
{
Point p(contours[i][j]);
double temp1 = pointPolygonTest(vertices_roi1, p, false); //判断是否在纸张内 10-1
double temp2 = pointPolygonTest(vertices_roi2, p, false); //判断是否在边缘区域内 10-1
//如果在纸张外或者边缘内视为非孔洞填充为0
if (temp1 < 0 || temp2 > 0)
{
fillConvexPoly(mask, contours[i], Scalar(0));
break;
}
}
}
}
void ImageOutHole::fillPuncture(Mat& src, const Mat& mask, Rect roi)
{
Mat mask_temp;
if (src.channels() == 3)
{
cvtColor(mask, mask_temp, COLOR_GRAY2RGB);
}
else
{
mask_temp = mask;
}
Mat src_roi(src, roi);
bitwise_or(src_roi, mask_temp, src_roi);
}