C++ opencv 识别数字编号

利用opencv的KNN识别数字,可以用在很多编码扫描上。第一次写c++ 边试边写的 很糙

结果:

  

效果还可以 但是对裁剪的准确性要求较高。

需要配置好opencv的环境

step 1 :切分训练数据

int split_data()
{
    Mat src, dst;
    src = imread("D:/Works/KNN-letters/my.png");
    if (src.empty())
    {
        std::cout << "can not load image 
" << std::endl;
        return -1;
    }
    imshow("input", src);
    dst = src.clone();
    cvtColor(src, src, COLOR_BGR2GRAY);
    Mat bin = src.clone();
    Mat ROI = src(Rect(0, 0, src.size().width, src.size().height));
    blur(ROI, ROI, Size(5, 5));
    imshow("blur", ROI);
    threshold(ROI, ROI, 220, 255, THRESH_BINARY);
    Canny(ROI, ROI, 20, 80, 3, false);

    std::vector<std::vector<Point>> contours;
    std::vector<Vec4i>hierarchy;
    imshow("ROI", ROI);

    findContours(ROI, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
    std::cout << contours.size() << std::endl;
    RNG rng(0);

    std::vector<RotatedRect> minRects(contours.size());
    std::vector<float> height, width;
    for (int i = 0; i < contours.size(); i++)
    {
        Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
        Rect rect = boundingRect(contours[i]);
        height.push_back(rect.height);
        width.push_back(rect.width);
    }
    //获取字符外接矩形宽高的最大值
    std::vector<float>::iterator h = std::max_element(std::begin(height), std::end(height));
    std::vector<float>::iterator w = std::max_element(std::begin(width), std::end(width));
    //获得轮廓的外接矩形
    std::vector<Rect>rects;
    //将轮廓外接矩形按纵坐标排序
    std::vector<float>sequence;

    for (int i = 0; i < contours.size(); i++)
    {
        Scalar color = Scalar(0, rng.uniform(0, 255), rng.uniform(0, 255));
        Rect rect = boundingRect(contours[i]);
        Rect reRect = Rect(Point(rect.x + rect.width / 2.0 - *w / 2.0, rect.y + rect.height / 2.0 - *h / 2.0 ), Point(rect.x + rect.width / 2.0 + *w / 2.0, rect.y + rect.height / 2.0 + *h / 2.0));
        rectangle(dst, reRect, color, 2);
        rects.push_back(reRect);
        sequence.push_back(reRect.y);
    }
    //按纵坐标排序
    sort(sequence.begin(), sequence.end());
    
    threshold(bin, bin, 0, 255, THRESH_BINARY | THRESH_OTSU);
    std::cout << rects.size() << std::endl; 
    int n = 0;
    for (int i = 0; i < rects.size(); i++)
    {
        for (int j = 0; j < 10; j++)
        {
            std::string outPath = "D:/Works/KNN-letters/letters3/";
            char label = '0';
            char temp[256];
            if ((rects[i].y > sequence[j * 6] - *h / 2.0) && (rects[i].y < sequence[j * 6] + *h / 2.0))
            {
                label = label + j;
                sprintf_s(temp, "%d", n);
                outPath = outPath + label + "/" + temp + ".jpg";
                std::cout << outPath << std::endl;
                //imwrite(outPath, bin(rects[i]));
                n++;
            }
        }
    }
    imshow("output", dst);
    waitKey();

    waitKey();
    return 0;
}

注意分类文件夹寻妖自己创建

   

step 2 训练KNN 

int trian_my2()
{    
    //split_data();
    ////===============================读取训练数据===============================////
    //图片共有10类
    const int classSum = 10;
    //每类共50张图片
    const int imagesSum = 6;
    //图片尺寸
    const int imageRows = 17;
    const int imageCols = 12;
    //每一行一个训练图片
    float trainingData[classSum * imagesSum][imageRows * imageCols] = { {0} };
    //训练样本标签
    float labels[classSum * imagesSum] = { 0 };

    for (int i = 0; i < classSum; i++)
    {
        //目标文件夹路径
        std::string inPath = "D:/Works/KNN-letters/letters3/";
        char label = '0';
        int k = 0;
        label = label + i;
        inPath = inPath + label + "/*.jpg";
        std::cout << inPath << std::endl;
        //用于查找的句柄
        _int64 handle;
        struct _finddata_t fileinfo;
        int r;
        //第一次查找
        handle = _findfirst(inPath.c_str(), &fileinfo);
        if (handle == -1)
            return -1;
        do
        {
            //找到的文件的文件名
            std::string imgname = "D:/Works/KNN-letters/letters3/";
            imgname = imgname + label + "/" + fileinfo.name;
            std::cout<<imgname<<std::endl;
            Mat src = imread(imgname, 0);
            if (src.empty())
            {
                std::cout << "can not load image 
" << std::endl;
                return -1;
            }
            //序列化后放入作为样本矩阵的一行
            for (int j = 0; j < imageRows * imageCols; j++)
            {
                trainingData[i * imagesSum + k][j] = (float)src.data[j];
            }
            // 设置样本标签 
            labels[i * imagesSum + k] = label;
            k++;
            std::cout << label << std::endl;
        } while (!_findnext(handle, &fileinfo));
        _findclose(handle);
        
    }
    Mat trainingDataMat(classSum * imagesSum, imageRows * imageCols, CV_32FC1, trainingData);
    Mat labelsMat(classSum * imagesSum, 1, CV_32FC1, labels);
    //std::cout<<trainingDataMat<<std::endl;
    //std::cout<<labelsMat<<std::endl;

    ////===============================创建KNN模型===============================////
    Ptr<ml::KNearest> model = ml::KNearest::create();

    model->setDefaultK(3);
    model->setIsClassifier(true);
    Ptr<TrainData>trainData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);

    model->train(trainData);

    model->save("D:/Works/KNN-letters/KNN_NUM.xml"); 
    ////===============================预测部分===============================////
    
    //Ptr<ml::KNearest> model = StatModel::load<KNearest>("D:/Works/KNN-letters/KNN_NUM.xml");
    Mat src, dst;
    src = imread("D:/Works/KNN-letters/my.png");
    if (src.empty())
    {
        std::cout << "can not load image 
" << std::endl;
        return -1;
    }
    dst = src.clone();
    //创建感兴趣区域,选取右侧10列作为预测数据
    cvtColor(src, src, COLOR_BGR2GRAY);
    blur(src, src, Size(5, 5));
    threshold(src, src, 230, 255, THRESH_BINARY);
    Canny(src, src, 20, 80, 3, false);
    std::vector<std::vector<Point>> contours;
    std::vector<Vec4i>hierarchy;
    findContours(src, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));

    for (int i = 0; i < contours.size(); i++)
    {
        Rect rect = boundingRect(contours[i]);
        //以矩形中心及指定的宽高作为字符区域
        Rect reRect = Rect(Point(rect.x + rect.width / 2.0 - imageCols / 2.0, rect.y + rect.height / 2.0 - imageRows / 2.0), Point(rect.x + rect.width / 2.0 + imageCols / 2.0, rect.y + rect.height / 2.0 + imageRows / 2.0));
        Mat sampleImg;
        cvtColor(dst, sampleImg, COLOR_BGR2GRAY);
        threshold(sampleImg, sampleImg, 0, 255, THRESH_BINARY | THRESH_OTSU);
        Mat sample = Mat::zeros(Size(imageCols, imageRows), sampleImg.type());

        float sampleData[imageRows * imageCols];
        int nub = 0;
        for (int r = 0; r < imageRows; r++)
        {
            for (int c = 0; c < imageCols; c++)
            {
                sampleData[nub] = sampleImg.at<uchar>(reRect.y + r, reRect.x + c);
                nub++;
            }
        }

        Mat sampleDataMat(1, imageRows * imageCols, CV_32FC1, sampleData);
        char f;
        f = model->predict(sampleDataMat);
        char temp[256];
        sprintf_s(temp, "%c", f);
        std::cout << temp << "
" << std::endl;
        std::string text(temp);
        RNG rng(f);
        Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
        putText(dst, text, Point(reRect.x, reRect.y + reRect.height), 1, 1.5, color, 2);
    }
    imshow("output", dst);
    waitKey();
    return 0;
}

代码粘贴下去可以直接使用;

原文地址:https://www.cnblogs.com/luofeel/p/12857247.html