使用open CV提取图像

img


此图为2值图,请教把液晶区域裁剪出来的代码,谢谢。 后续打算学习把符号小数点和数字裁剪出来,谢谢。

颜色翻转以下,然后进行中值滤波,最后求连通域,得到最大的连通域(现有图片中的最大黑色区域)。
对最大连通域进行闭运算,将连通域中的数字变成白色。
用连通域做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();
}


img

按照前面的代码,再优化以下流程,得到以下结果

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();
}

img

opencv里面直接有数字识别的包,你直接加载识别一下呢

中值滤波,最后求连通域,得到最大的连通域(现有图片中的最大黑色区域)。
对最大连通域进行闭运算,将连通域中的数字变成白色。

paddleocr可以直接识别数字

easyocr 了解一下