python 检测图像_如何用Python图像处理或其他技术识别试纸检测结果?

一个尝试,但不严密

原图和识别后:

勉强识别出来,但是很容易受到背景的噪声污染,如果是那种虎皮桌子的当背景,就完全识别不出来了。

然后我想要识别出来是以试纸集中区域的部分。

步骤

一:缩减尺寸

为了加快识别计算时间。最后得到坐标后,还原用到原图裁剪。

cv::Mat shrinkPic; cv::pyrDown(cvImage, shrinkPic);

二:灰度图

几乎所有的识别,都会用灰度图。为什么呢?我查了资料一句话就是:降维计算。

我们拿1个象素来说,如果只表示黑和白,那么0和1即可。

如果是 RBG 那么3个信道的值分别是256,3种组合的数量级就是:256 * 256 * 256 = 1600w+多种组合

如果加上 alpha 信道,那么一个象素的可能组合达到40亿。对于计算机来说,一张1024 x 1024的图,从这个数据里找规律,计算量是非常大的。

但如果是灰度图,那么只有 0-255 的灰度值,那么计算量下了很多倍。

cv::cvtColor(shrinkPic, greyPic, cv::COLOR_RGBA2GRAY);

三:sobel 算子补全形态

cv::Mat grabX, grabY; cv::Sobel(greyPic, grabX, CV_32F, 1, 0); cv::Sobel(greyPic, grabY, CV_32F, 0, 1); cv::subtract(grabX, grabY, sobPic); cv::convertScaleAbs(sobPic, sobPic);

四:增加对比度

因为补全的部分,虽然是完整的矩形形态,但是边缘还是相对弱,如果直接二值化,容易补全形态丢失。

cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(25, 25)); cv::morphologyEx(sobPic, enhancePic, cv::MORPH_CLOSE, kernel);

五:去噪和二值化

上一步,过滤了一部分偏小的颜色之后,还是会有不属于识别物的噪点存在。我们做一下过滤,最后转化成二值化的图。

二值化就是全图只前0和1。那么计算机识别速度又加快上百倍了。

cv::blur(sobPic, threshPic, cv::Size(5,5)); cv::threshold(threshPic, threshPic, 30, 255, cv::THRESH_BINARY);

六:找到最小外接矩形中最大的一个得到坐标

识别物和噪点区域,可能被识别成一个数组,交还给你,你需要找到面积最大的一个,那么就是我们的识别目标

// 找出轮廓区域 std::vector<:vector>> contours; std::vector<:vec4i> hierarchy; cv::findContours(threshPic, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE); // 求所有形状的最小外接矩形中最大的一个 cv::RotatedRect box; for( int i = 0; i < contours.size(); i++ ){ cv::RotatedRect rect = cv::minAreaRect( cv::Mat(contours[i]) ); if (box.size.width < rect.size.width) { box = rect; } }

七:剪裁未缩小后的图,使用放射变换

完整示例

我展示的代码,缺了对 UIIImage 做处理,在参考链接最后一条。

如果不处理,你拍到的图可能和 OpenCV 拿到的 Mat 图 orientation 不一致。

#import "OpenCVWrapper.h" #import #import #import //MatToUIImage、MatToUIImage用到 #import //cv::域名下的东西会用到 #import + (UIImage *)change:(UIImage *)image { cv::Mat cvImage; UIImageToMat(image, cvImage); if (cvImage.empty()) { return nil; } cv::Mat shrinkPic; cv::pyrDown(cvImage, shrinkPic); int shrinkCount = (image.size.width / 500); int multi = 2; if (shrinkCount > 1) { shrinkCount = shrinkCount / 2; multi = pow(2, shrinkCount + 1); for (int i = 0; i < shrinkCount; i++) { cv::pyrDown(shrinkPic, shrinkPic); } } cv::Mat greyPic, sobPic,enhancePic, threshPic; cv::cvtColor(shrinkPic, greyPic, cv::COLOR_RGBA2GRAY); // 边缘直方图法,采用sobel算子提取边缘线,然后水平,垂直分别做直方图 cv::Mat grabX, grabY; cv::Sobel(greyPic, grabX, CV_32F, 1, 0); cv::Sobel(greyPic, grabY, CV_32F, 0, 1); cv::subtract(grabX, grabY, sobPic); cv::convertScaleAbs(sobPic, sobPic); // 填充空白区域,增强对比度 cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(25, 25)); cv::morphologyEx(sobPic, enhancePic, cv::MORPH_CLOSE, kernel); // 去除噪声 cv::blur(sobPic, threshPic, cv::Size(5,5)); cv::threshold(threshPic, threshPic, 30, 255, cv::THRESH_BINARY);//90 // return MatToUIImage(threshPic); // 找出轮廓区域 std::vector<:vector>> contours; std::vector<:vec4i> hierarchy; cv::findContours(threshPic, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE); // 求所有形状的最小外接矩形中最大的一个 cv::RotatedRect box; for( int i = 0; i < contours.size(); i++ ){ cv::RotatedRect rect = cv::minAreaRect( cv::Mat(contours[i]) ); if (box.size.width < rect.size.width) { box = rect; } } { // 画出来矩形和4个点, 供调试。此部分代码可以不要 cv::Mat drawing = cv::Mat::zeros(threshPic.rows, threshPic.cols, CV_8UC3); cv::Scalar color = cv::Scalar( rand() & 255, rand() & 255, rand() & 255 ); cv::Point2f rect_points[4]; box.points( rect_points ); for ( int j = 0; j < 4; j++ ) { line( drawing, rect_points[j], rect_points[(j+1)%4], color ); circle(drawing, rect_points[j], 10, color, 2); } // return MatToUIImage(drawing); } // 仿射变换 cv::Point2f corners[4], canvas[4], tmp[4]; // 固定输出尺寸,可以由外部传入 cv::Size real_size = cv::Size(500, 40); canvas[0] = cv::Point2f(0, 0); canvas[1] = cv::Point2f(real_size.width, 0); canvas[2] = cv::Point2f(real_size.width, real_size.height); canvas[3] = cv::Point2f(0, real_size.height); box.points( tmp ); bool sorted = false; int n = 4; while (!sorted){ for (int i = 1; i < n; i++){ sorted = true; if (tmp[i-1].x > tmp[i].x){ swap(tmp[i-1], tmp[i]); sorted = false; } } n--; } if (tmp[0].y < tmp[1].y){ corners[0] = tmp[0]; corners[3] = tmp[1]; } else{ corners[0] = tmp[1]; corners[3] = tmp[0]; } if (tmp[2].y < tmp[3].y){ corners[1] = tmp[2]; corners[2] = tmp[3]; } else{ corners[1] = tmp[3]; corners[2] = tmp[2]; } for (int i = 0; i < 4; i++){ corners[i] = cv::Point2f(corners[i].x * multi, corners[i].y * multi); //恢复坐标到原图 } cv::Mat result; cv::Mat M = cv::getPerspectiveTransform(corners, canvas); cv::warpPerspective(cvImage, result, M, real_size); return MatToUIImage(result); }

(转载)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值