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 \n" << 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 \n" << 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 \n" << 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 << "\n" << 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;
}

代碼粘貼下去可以直接使用;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM