颜色翻转以下,然后进行中值滤波,最后求连通域,得到最大的连通域(现有图片中的最大黑色区域)。
对最大连通域进行闭运算,将连通域中的数字变成白色。
用连通域做mask,得到目标图。
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
//修改自以下链接,博主添加了pad,使两列图像边多了空格
//https ://blog.csdn.net/Beking17113/article/details/122304671
void multipleImage(vector<Mat> imgVector, Mat& dst, int imgCols, int MAX_PIXEL = 600)
{
//两列图像间的空白区域
int pad = 10;
int imgNum = imgVector.size();
//选择图片最大的一边 将最大的边按比例变为300像素
Size imgOriSize = imgVector[0].size();
int imgMaxPixel = max(imgOriSize.height, imgOriSize.width);
//获取最大像素变为MAX_PIXEL的比例因子
double prop = imgMaxPixel < MAX_PIXEL ? (double)imgMaxPixel / MAX_PIXEL : MAX_PIXEL / (double)imgMaxPixel;
Size imgStdSize(imgOriSize.width * prop, imgOriSize.height * prop); //窗口显示的标准图像的Size
Mat imgStd; //标准图片
Point2i location(0, 0); //坐标点(从0,0开始)
//Mat imgWindow(imgStdSize.height * ((imgNum - 1) / imgCols + 1), imgStdSize.width * imgCols+ pad * imgCols-pad, imgVector[0].type());
int imgRows = (imgNum - 1) / imgCols + 1;
Mat imgWindow = Mat::zeros(imgStdSize.height * imgRows + pad * imgRows - pad, imgStdSize.width * imgCols + pad * imgCols - pad, imgVector[0].type());
for (int i = 0; i < imgNum; i++)
{
location.x = (i % imgCols) * (imgStdSize.width + pad);
location.y = (i / imgCols) * imgStdSize.height;
resize(imgVector[i], imgStd, imgStdSize, prop, prop, INTER_LINEAR); //设置为标准大小
//将imgStd复制到imgWindow的指定区域中
imgStd.copyTo(imgWindow(Rect(location, imgStdSize)));
}
dst = imgWindow;
}
void imshows( string title, vector<Mat> imgVector, int imgCols = -1) {
Mat dst;
if (imgCols == -1) {
imgCols = imgVector.size();
}
multipleImage(imgVector, dst, imgCols);
namedWindow(title);
imshow(title, dst);
imwrite(title + ".png", dst);
}
Mat readImg(string path) {
Mat img = imread(path, 0);//左侧:图片路径
resize(img, img, { 512,512 });
int thres_ = 128;
cv::threshold(img, img, thres_, 255, THRESH_BINARY);//大于阈值返回 最大值 255 小于返回0
cv::medianBlur(img, img, 3);
return img;
}
Mat findLargesrArea(Mat srcImage)
{
Mat temp;
Mat labels;
srcImage.copyTo(temp);
//1. 标记连通域
int n_comps = connectedComponents(temp, labels, 4, CV_16U);
vector<int> histogram_of_labels;
for (int i = 0; i < n_comps; i++)//初始化labels的个数为0
{
histogram_of_labels.push_back(0);
}
int rows = labels.rows;
int cols = labels.cols;
for (int row = 0; row < rows; row++) //计算每个labels的个数
{
for (int col = 0; col < cols; col++)
{
histogram_of_labels.at(labels.at<unsigned short>(row, col)) += 1;
}
}
histogram_of_labels.at(0) = 0; //将背景的labels个数设置为0
//2. 计算最大的连通域labels索引
int maximum = 0;
int max_idx = 0;
for (int i = 0; i < n_comps; i++)
{
if (histogram_of_labels.at(i) > maximum)
{
maximum = histogram_of_labels.at(i);
max_idx = i;
}
}
//3. 将最大连通域标记为1
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if (labels.at<unsigned short>(row, col) == max_idx)
{
labels.at<unsigned short>(row, col) = 255;
}
else
{
labels.at<unsigned short>(row, col) = 0;
}
}
}
//4. 将图像更改为CV_8U格式
labels.convertTo(labels, CV_8U);
return labels;
}
int main(int argc, char* argv[])
{
string p1 = "D:\\test.png";
Mat im1 = readImg(p1);
Mat inv = 255 - im1;
Mat element = getStructuringElement(MORPH_RECT, Size(9, 9));
morphologyEx(inv, inv, MORPH_OPEN, element);//消除毛刺,删除部分连通域
//获取最大的连通域
Mat maxareo = findLargesrArea(inv);
//闭合连通域中的数字
Mat element2 = getStructuringElement(MORPH_RECT, Size(71, 71));
Mat maxareo_close, maxareo_close_erode,result;
morphologyEx(maxareo, maxareo_close, MORPH_CLOSE, element2);//消除毛刺,删除部分连通域
Mat element3 = getStructuringElement(MORPH_RECT, Size(31, 31));
morphologyEx(maxareo_close, maxareo_close_erode, MORPH_ERODE, element3);//因为图像边缘也被连接了,这里做删除
result = maxareo_close_erode.mul(im1);
imshows("二值图", {im1,inv ,maxareo_close_erode,result },2);
//imshow( inv);
waitKey();
}
按照前面的代码,再优化以下流程,得到以下结果
int main(int argc, char* argv[])
{
string p1 = "D:\\test.png";
Mat im1 = readImg(p1);
Mat inv = 255 - im1;
Mat element = getStructuringElement(MORPH_RECT, Size(9, 9));
morphologyEx(inv, inv, MORPH_OPEN, element);//消除毛刺,删除部分连通域
//获取最大的连通域
Mat maxareo = findLargesrArea(inv);
//闭合连通域中的数字
Mat element2 = getStructuringElement(MORPH_RECT, Size(71, 71));
Mat maxareo_close, maxareo_close_big_erode,result;
morphologyEx(maxareo, maxareo_close, MORPH_CLOSE, element2);//消除毛刺,删除部分连通域
Mat maxareo_close_big = Mat::zeros({ im1.rows+40,im1.cols+40}, im1.type());
maxareo_close.copyTo(maxareo_close_big(Rect({20,20}, im1.size())));
Mat element3 = getStructuringElement(MORPH_RECT, Size(39, 39));
morphologyEx(maxareo_close_big, maxareo_close_big_erode, MORPH_ERODE, element3);//因为图像边缘也被连接了,这里做删除
Mat maxareo_close_erode = maxareo_close_big_erode(Rect({ 20,20 }, im1.size()));
result = maxareo_close_erode.mul(im1);
imshows("二值图", {im1,inv ,maxareo_close_erode,result },2);
//imshow( inv);
waitKey();
}
opencv里面直接有数字识别的包,你直接加载识别一下呢
中值滤波,最后求连通域,得到最大的连通域(现有图片中的最大黑色区域)。
对最大连通域进行闭运算,将连通域中的数字变成白色。
paddleocr可以直接识别数字
easyocr 了解一下