From 155ee065867522e11d37804e928dbd80cc1460db Mon Sep 17 00:00:00 2001 From: pm <1002639516@qq.com> Date: Wed, 4 Sep 2019 09:54:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BF=98=E8=AE=B0=E4=B8=8A=E4=BC=A0=E9=99=A4?= =?UTF-8?q?=E7=A9=BF=E5=AD=94=E4=BB=A5=E5=8F=8A=E5=B7=A5=E5=85=B7=E7=B1=BB?= =?UTF-8?q?=EF=BC=88=E8=A1=A5=E4=B8=8A=E4=BC=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hugaotwainds/ImageMultiOutput.cpp | 60 ++++++ hugaotwainds/ImageMultiOutput.h | 15 ++ hugaotwainds/ImageOutHole.cpp | 310 ++++++++++++++++++++++++++++++ hugaotwainds/ImageOutHole.h | 26 +++ hugaotwainds/filetools.h | 65 +++++++ 5 files changed, 476 insertions(+) create mode 100644 hugaotwainds/ImageMultiOutput.cpp create mode 100644 hugaotwainds/ImageMultiOutput.h create mode 100644 hugaotwainds/ImageOutHole.cpp create mode 100644 hugaotwainds/ImageOutHole.h create mode 100644 hugaotwainds/filetools.h diff --git a/hugaotwainds/ImageMultiOutput.cpp b/hugaotwainds/ImageMultiOutput.cpp new file mode 100644 index 0000000..44dc8cf --- /dev/null +++ b/hugaotwainds/ImageMultiOutput.cpp @@ -0,0 +1,60 @@ +#include "StdAfx.h" +#include "ImageMultiOutput.h" + + +ImageMultiOutput::ImageMultiOutput(void) +{ +} + + +ImageMultiOutput::~ImageMultiOutput(void) +{ +} + +//void ImageMultiOutput::apply(cv::Mat& pDib,int side) +//{ +// //throw std::logic_error("The method or operation is not implemented."); +//} + +cv::Mat ImageMultiOutput::GetMultiFilterMat(cv::Mat &src,int channel) +{ + return FilterColor(src,channel); +} + +cv::Mat ImageMultiOutput::FilterColor(cv::Mat image,short channel) +{ + cv::Mat dstImage(image.rows,image.cols,CV_8UC1); + + //int pixelSize = image.depth(); + int channels = image.channels(); + if(channel > channels -1){ + return dstImage; + } + if ( ( channel == 3 ) && ( channels != 4 ) && ( channels != 8 )) + { + return dstImage; + } + if ( channels <= 4 ) + { + int srcOffset = image.step - image.cols* channels ; + int dstOffset = dstImage.step - dstImage.cols; + unsigned char* src = image.data; + unsigned char* dst = dstImage.data; + src += channel; + + for ( int y = 0; y < image.rows; y++ ) + { + for ( int x = 0; x < image.cols; x++, src += channels , dst++ ) + { + unsigned short pix = *src; + if(pix >=130){ + pix = 255; + } + *dst = pix; + } + src += srcOffset; + dst += dstOffset; + } + } + return dstImage; +} diff --git a/hugaotwainds/ImageMultiOutput.h b/hugaotwainds/ImageMultiOutput.h new file mode 100644 index 0000000..a7e699d --- /dev/null +++ b/hugaotwainds/ImageMultiOutput.h @@ -0,0 +1,15 @@ +#pragma once +#include "ImageApply.h" + +class ImageMultiOutput +{ +public: + ImageMultiOutput(void); + ~ImageMultiOutput(void); + + cv::Mat GetMultiFilterMat(cv::Mat &src,int channel); +private: + cv::Mat FilterColor(cv::Mat image,short channel); + +}; + diff --git a/hugaotwainds/ImageOutHole.cpp b/hugaotwainds/ImageOutHole.cpp new file mode 100644 index 0000000..f6d07ec --- /dev/null +++ b/hugaotwainds/ImageOutHole.cpp @@ -0,0 +1,310 @@ +#include "StdAfx.h" +#include "ImageOutHole.h" +#include +#include +#include "opencv2/opencv.hpp" +#include "opencv/cv.h" +#include "opencv2/core/core.hpp" + +ImageOutHole::ImageOutHole(void) +{ +} + + +ImageOutHole::~ImageOutHole(void) +{ +} + +void ImageOutHole::puncture(Mat& front, Mat& back, double threshold, float edgeScale, double areaThreshold) +{ + //二值化正反面图像 + 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> contours_front, contours_back; + vector 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 maxContour_front = getMaxContour(contours_front, b1_front); + vector 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 vertices_point = getVertices(mask_rotatedRect); + + polylines(mask, vertices_point, true, Scalar(255), 3); //绘制纸张矩形边缘 + + //二值图像重叠图像颜色取反,并提取轮廓 + vector> contours_mask; + vector 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 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 +} + +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>& contours, vector& 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 all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage)); + int total = (int)all_contours.size(); + contours.resize(total); + + SeqIterator 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 ImageOutHole::getMaxContour(const vector>& contours, const vector& hierarchy, int areaThreshold /*= 20000*/) +{ + vector 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 ImageOutHole::getVertices(RotatedRect rect) +{ + vector 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>& 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 vertices_roi1 = getVertices(roi); + vector vertices_roi2 = getVertices(roi2); + + vector contour; + for (size_t i = 0, length = contours.size(); i < length; i++) + { + //contour = /*Mat_*/(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); //判断是否在纸张内 1:内;0:上;-1:外 + double temp2 = pointPolygonTest(vertices_roi2, p, false); //判断是否在边缘区域内 1:内;0:上;-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); +} + diff --git a/hugaotwainds/ImageOutHole.h b/hugaotwainds/ImageOutHole.h new file mode 100644 index 0000000..2218e23 --- /dev/null +++ b/hugaotwainds/ImageOutHole.h @@ -0,0 +1,26 @@ +#pragma once +#include "ImageApply.h" +#include +#include +#include +#include +using namespace cv; + +class ImageOutHole +{ +public: + ImageOutHole(void); + ~ImageOutHole(void); +public: + void puncture(Mat& front, Mat& back, double threshold, float edgeScale, double areaThreshold); +private: + cv::Mat threshold_mat(const Mat& src, double threshold); + void findContours22(const Mat& src, vector>& contours, vector& hierarchy, int retr = RETR_CCOMP, int method = CHAIN_APPROX_SIMPLE, Point offset = Point(0, 0)); + vector getMaxContour(const vector>& contours, const vector& hierarchy, int areaThreshold = 20000); + void getRoi(RotatedRect rrect_front, RotatedRect rrect_back, Size srcSize, Rect& roi_front, Rect& roi_back, RotatedRect& mask_rotatedRect); + Point rotatedPoint(Point p, Point center, double angle); + vector getVertices(RotatedRect rect); + void filterPoly(const Mat& mask, vector>& contours, RotatedRect roi, float edgeScale, double areaThreshold); + void fillPuncture(Mat& src, const Mat& mask, Rect roi); +}; + diff --git a/hugaotwainds/filetools.h b/hugaotwainds/filetools.h new file mode 100644 index 0000000..4f040e6 --- /dev/null +++ b/hugaotwainds/filetools.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include +#include + +class FileTools +{ +public: + static std::vector getFiles(std::string path) + { + std::vector files; + getFiles(path, files); + return files; + } + + static void write_log(std::string filepath, std::string log) + { + std::ofstream ofs(filepath, std::ios::app); + ofs << log << std::endl; + } + +private: + static void getFiles(std::string path, std::vector& files) + { + //文件句柄 + long hFile = 0; + //文件信息 + struct _finddata_t fileinfo; + std::string p; + if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo))!=-1) + { + do + { + //如果是目录,迭代之 + //如果不是,加入列表 + if ((fileinfo.attrib & _A_SUBDIR)) + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) + getFiles(p.assign(path).append("\\").append(fileinfo.name), files); + } + else + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name)); + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } + } + +}; + +class StopWatch +{ +public: + StopWatch() { valEnd = valStart = clock(); } + void start() { valStart = clock(); } + void stop() { valEnd =clock(); } + double time_run() { return (double)(valEnd - valStart)/CLOCKS_PER_SEC; } + +private: + clock_t valStart; + clock_t valEnd; +}; +