主頁 >  其他 > OpenCV----簡單物件分類

OpenCV----簡單物件分類

2021-12-27 08:55:57 其他

題目要求:在上一章OpenCV----簡單目標提取和分割中嘗試使用opencv連通性方法獲取了目標的面積和輪廓資訊,本章節將嘗試對這些特征進行整合,使用opencv中ml庫(machine learning)訓練一個目標分類器,給定一種輸入圖片,預測影像中目標的類別,
最新代碼庫已更新:opencv4_cpp

分析:
1)了解常用的分類模型,參見opencv ml API OpenCV namespace ml,首先建議一個簡單的SVM分類器,然后設計其他的分類器如樸素貝葉斯模型等;
2)設計命令列設定輸入圖片和模型選擇等,支持默認模型選擇;
3)考慮模板類進行分類模型選擇的通用能力;
4)支持模型引數手動設定;
5)進代碼設計成便于管理,解釋性強的工程,設計通用的工程模板;

  • 檔案工程目錄
    ·bin ------------------生成可執行檔案目錄
    ·build ----------------檔案編譯的中間結果
    ·data -----------------訓練測驗資料(影像)
    ·include -------------頭檔案目錄和inline檔案(用于模板方法實作)
    ·lib --------------------用于生成頭檔案的lib檔案
    ·model --------------不同模型訓練生成的xml檔案
    ·src -------------------源檔案和源檔案編譯命令CMakeLists.txt
    ·CMakeLists.txt —外層檔案編譯命令
    請添加圖片描述

  • outer CMakeLists.txt

cmake_minimum_required (VERSION 3.0)

PROJECT(ch6)

set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++17 -Wall")
set(CMAKE_CXX_FLAGS_RELEASE  "-std=c++17 -O3 -fopenmp -pthread")

IF(EXISTS ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
    include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
    conan_basic_setup()
ENDIF()

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
include_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/lib)
add_subdirectory(${PROJECT_SOURCE_DIR}/src)
  • inner CMakeLists.txt
cmake_minimum_required (VERSION 3.0)

set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++17 -Wall")
set(CMAKE_CXX_FLAGS_RELEASE  "-std=c++17 -O3 -fopenmp -pthread")

include_directories(${PROJECT_SOURSE_DIR}/include)

# Requires OpenCV
find_package(OpenCV  REQUIRED)

message("OpenCV version : ${OpenCV_VERSION}")

include_directories(${OpenCV_INCLUDE_DIRS})
link_directories(${OpenCV_LIB_DIR})

add_library(utils utils.cpp)
add_library(mwindow mwindow.cpp)
add_executable(main main.cpp)
target_link_libraries(main ${OpenCV_LIBS} utils mwindow -lopencv_ml)
  • 結果演示
 ./bin/main data/test.pgm  data/pattern.pgm bayes

請添加圖片描述

./bin/main data/test.pgm  data/pattern.pgm (svm)

請添加圖片描述

./bin/main data/test.pgm  data/pattern.pgm boost

請添加圖片描述

  • 代碼示例
    1)更新之前的多視窗類MWindow Class
/*
@File          :mwindow.hpp
@Description:  :
@Date          :2021/12/25 09:23:14
@Author        :xieyin
@version       :1.0
*/
#pragma once

#include<iostream>
#include<string>
#include<vector>
using namespace std;

#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
using namespace cv;

class MWindow{
    public:
        // consturtor
        MWindow(string windowTitle, int rows, int cols, int height=700, int width=1200, int flags=WINDOW_AUTOSIZE);
        // add image into canvas
        int addImage(string title, Mat img, bool render = false);
        // remove image from canvas
        void removeImage(int pos);
        // adjust all image size in canvas
        void render();
    private:
        string mWindowTitle;
        int mRows;
        int mCols;
        Mat mCanvas;
        vector<string> mSubTitles;
        vector<Mat> mSubImages;     
};

/*
@File          :mwindow.cpp
@Description:  :
@Date          :2021/12/25 09:23:22
@Author        :xieyin
@version       :1.0
*/

#include<iostream>
#include<string>
#include<vector>
using namespace std;

#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc.hpp>
using namespace cv;

#include"mwindow.hpp"

MWindow::MWindow(string windowTitle, int rows, int cols, int height, int width, int flags):mWindowTitle(windowTitle), mRows(rows), mCols(cols){
    /*
    @description  : MWindow constructor
    @param  : 
        windowTitle : whole window title
        rows : sub window rows
        cols : sub window cols
        flags : namedWindow flags (eg, WINDOW_AUTOSIZE)
    @Returns  : 
    */
   // create canvas
    namedWindow(mWindowTitle, flags);
    mCanvas = Mat(height, width, CV_8UC3);
    imshow(mWindowTitle, mCanvas);
}

int MWindow::addImage(string title, Mat img, bool render){
    /*
    @description  : add title and image into canvas
    @param  : 
        title : sub image title
        img : image to be added
        render : render(flag) whether need to adjust the image for canvas
    @Returns  : 
        index : sub image index in total mRows * mCols
    */
   int index=-1;
    for(int i=0; i<mSubTitles.size(); i++){
        string t=this->mSubTitles[i];
        if(t.compare(title)==0){
            index=i;
            break;
        }
    }
    if(index==-1){
        mSubTitles.push_back(title);
        mSubImages.push_back(img);
    }else{
        mSubImages[index]= img;
    }
    if(render){
        MWindow::render();
    }
    return mSubImages.size() - 1;
}


void MWindow::removeImage(int pos){
    /*
    @description  : remove image from canvas based on index
    @param  : 
        pos : sub image index in total mRows * mCols
    @Returns  : 
        None
    */
    mSubTitles.erase(mSubTitles.begin() + pos);
    mSubImages.erase(mSubImages.begin() + pos);
}

void MWindow::render(){
    /*
    @description  : fill title and image into canvas in suitable way
    @param  : 
        None
    @Returns  :
        None 
    */
    mCanvas.setTo(Scalar(20, 20, 20));
    // get sub canvas size
    int cellH = mCanvas.rows / mRows;
    int cellW = mCanvas.cols / mCols;
    // set total number of images to load
    int n = mSubImages.size();
    int numImgs = n > mRows * mCols ? mRows * mCols : n;
    for(int i = 0; i < numImgs; i++){
        // get title
        string title = mSubTitles[i];
        // get sub canvas top left location
        int cellX = (cellW) * ((i) % mCols);
        int cellY = (cellH) * floor( (i) / (float) mCols);
        Rect mask(cellX, cellY, cellW, cellH);
        // set subcanvas size
        rectangle(mCanvas, Rect(cellX, cellY, cellW, cellH), Scalar(200, 200, 200), 1);
        Mat cell(mCanvas, mask);
        Mat imgResz;
        // get cell aspect
        double cellAspect = (double) cellW / (double) cellH;
        // get image
        Mat img = mSubImages[i];
        // get image aspect
        double imgAspect = (double) img.cols / (double) img.cols;
        double wAspect = (double) cellW / (double) img.cols;
        double hAspect = (double) cellH / (double) img.rows;
        // get suitable aspect and resize image
        double aspect = cellAspect < imgAspect ? wAspect : hAspect;
        resize(img, imgResz, Size(0, 0), aspect, aspect);
        // if gray image, convert to BGR
        if(imgResz.channels() == 1){
            cvtColor(imgResz, imgResz, COLOR_GRAY2BGR);
        }

        Mat subCell(mCanvas, Rect(cellX, cellY, imgResz.cols, imgResz.rows));
        imgResz.copyTo(subCell);
        putText(cell, title, Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(255, 0, 0));
    }
    // show total canvas
    imshow(mWindowTitle, mCanvas);

}
2)設計通用輔助函式utils.hpp, utills.cpp和utils.inl
/*
@File          :utils.hpp
@Description:  :
@Date          :2021/12/25 09:23:48
@Author        :xieyin
@version       :1.0
*/
#pragma once
#include<string>
#include<cmath>
#include<memory>
using namespace std;

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/ml.hpp>
using namespace cv;
using namespace cv::ml;

#include"mwindow.hpp"

extern shared_ptr<MWindow> myWin;

// generate randow color basd on randow number generator
Scalar randColor(RNG& rng);

// calculate given img's light pattert with large kernel's Blur operation
Mat calLigthPattern(Mat img);

// use 2 tpyes of light removal method, 0 diff, 1 div, defalut is 0
Mat removeLight(Mat img, Mat pattern, int methodLight=0);

// packed opencv lib connectedComponents function
Mat connectedComponents(Mat img_thr);

// packed opencv lib connectedComponentsWithStats function
Mat connectedComponentsWithStats(Mat img_thr);

// packed opencv lib findContours function
Mat findContours(Mat img_thr);

// helper function for trainAndTest, readFolderAndExtractFeatures
bool readFolderAndExtractFeatures(string filePath, int label, int numTest, 
    vector<float>& trainingData, vector<int>& trainResponses, vector<float>& testData, vector<int>& testResponses);

// helper function for trainAndTest, ploat data error
void plotData(Mat trainingDataMat, Mat trainResponsesMat, string mode="svm", float* error=NULL);

// define svm parameters
void defineSVM(Ptr<SVM>& svm);

// the train and test process for mechain learning
template<typename T>
void trainAndTest(string mode="svm");

// train svm model
void trainSVM();

// predict features extracted from imgOut, and put text in left top position
template<typename T>
void predict(vector<vector<float>> features, vector<int> posLeft, vector<int> posTop, string mode, Mat& imgOut);

// preprocess test image
Mat preProcess(Mat img);

// extract feature from preprocess image and get left top location
vector<vector<float>> extractFeatures(Mat img, vector<int>* posLeft=NULL, vector<int>* posTop=NULL);

#include"utils.inl"


/*
@File          :utils.cpp
@Description:  :
@Date          :2021/12/25 09:23:38
@Author        :xieyin
@version       :1.0
*/
#include<string>
#include<cmath>
#include<memory>
#include<iostream>
#include<vector>
using namespace std;

#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include<opencv2/ml.hpp>
using namespace cv;
using namespace cv::ml;

#include"utils.hpp"
#include"mwindow.hpp"

Scalar randColor(RNG& rng){
    /*
    @description  : generate randow color
    @param  : 
        rng : random number generator object
    @Returns  : 
        Sacalar() : BGR scalar
    */
    auto iColor = (unsigned)rng;
    return Scalar(iColor&255, (iColor >> 8)&255, (iColor >> 16)&255);
}

Mat calLigthPattern(Mat img){
    /*
    @description  : get source image's light pattern 
    @param  : 
        img : source BGR image or Gray image
    @Returns  : 
        pattern : the light pattern
    */
    Mat pattern;
    blur(img, pattern, Size(img.cols / 3, img.cols / 3));
    return pattern;
}

Mat removeLight(Mat img, Mat pattern, int methodLight){
    /*
    @description  : remove light between img and pattern based on method light
    @param  : 
        img : source BGR/Gray image
        pattern : pattern BGR/Gray image
        methodLight : choise options: 0 difference, 1 div
    @Returns  : 
        aux : light removed BGR/Gray image
    */
    Mat aux;
    if(methodLight == 1){
        // div operation in float 32 format CV_32F
        Mat img32, pattern32;
        img.convertTo(img32, 5);
        pattern.convertTo(pattern32, 5);
        aux = 1.0 - (img32 / pattern32);
        // covert to CV_8U and clip
        aux.convertTo(aux, 0, 255);
    }
    else{
        // difference
        aux = pattern - img;
    }
    return aux;
}


Mat connectedComponents(Mat img_thr){
    /*
    @description  : opencv connnected components
    @param  : 
        img : threshold image
    @Returns  : 
        None
    */
   Mat labels;
   auto num_objs = connectedComponents(img_thr, labels);
   Mat res;
   if(num_objs < 2){
       cout << "no object is detected. " << endl;
       return res;
   }
   res = Mat::zeros(img_thr.rows, img_thr.cols, CV_8UC3);
   RNG rng(0xFFFFFFFF);
   for(auto i = 1; i < num_objs; i++){
       Mat mask = labels == i;
       res.setTo(randColor(rng), mask);
   }
   return res;
}

Mat connectedComponentsWithStats(Mat img_thr){
    /*
    @description  : connnected components with stats
    @param  : 
        img : threshold image
    @Returns  : 
        None
    */
    Mat labels, stats, centroids;
    auto num_objs = connectedComponentsWithStats(img_thr, labels, stats, centroids);
    Mat res;
    if(num_objs < 2){
       cout << "no object is detected. " << endl;
       return res;
   }
   res = Mat::zeros(img_thr.rows, img_thr.cols, CV_8UC3);
   RNG rng(0xFFFFFFFF);
   for(auto i = 1; i < num_objs; i++){
       Mat mask = labels == i;
       res.setTo(randColor(rng), mask);
       stringstream ss;
       ss << "area: " << stats.at<int>(i, CC_STAT_AREA);
       // add text info
       putText(res, ss.str(), centroids.at<Point2d>(i), FONT_HERSHEY_SIMPLEX, 0.3, Scalar(0, 255, 0));
   }
   return res;
}


Mat findContours(Mat img_thr){
    /*
    @description  : find contours and put text
    @param  : 
        img : threshold image
    @Returns  : 
        None
    */
    vector<vector<Point>> contours;
    findContours(img_thr, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    Mat res;
    if(contours.size() == 0){
        cout << "no contours are found ." << endl;
        return res;
    }
    RNG rng(0xFFFFFFFF);
    res = Mat::zeros(img_thr.rows, img_thr.cols, CV_8UC3);
    // calculate moments
	vector<Moments> mu(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mu[i] = moments(contours[i], false);
	}
	// calculate centroids
	vector<Point2f> mc(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
	}
    for(auto i = 0; i < contours.size(); i++){
        drawContours(res, contours, i, randColor(rng));
        putText(res, "*", Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 0.4, Scalar(255, 0, 255), 1);
    }
    return res;
}

// helper function for readFolderAndExtractFeatures, preprocess image to binary image
Mat preProcess(Mat img){
    /*
    @description  : preprocess img to denoise and remove light
    @param  : 
        img : image to process
    @Returns  : 
    */
    if(img.channels() == 3){
        cvtColor(img, img, COLOR_BGR2GRAY);
    }
    Mat imgOut, imgNoise, imgLight;
    medianBlur(img, imgNoise, 3);
    imgNoise.copyTo(imgLight);
    // read lightPat
    Mat lightPat = imread("data/pattern.pgm", 0);
    imgLight = removeLight(imgNoise, lightPat);
    threshold(imgLight, imgOut, 30, 255, THRESH_BINARY);
    return imgOut;
}

// helper function for trainAndTest, readFolderAndExtractFeatures
bool readFolderAndExtractFeatures(string filePath, int label, int numTest, 
    vector<float> &trainingData, vector<int> &trainResponses, vector<float> &testData, vector<int> &testResponses){
    /*
    @description  : read file data and extract area and aspect features
    @param  : 
        filePath : image file path
        label : image lable to classify
        numTest : number for test
        trainingData : trainingData feature: area, aspect
        trainResponses : trainingData label
        testData : testData feature: area, aspect
        testResponses : testData label
    @Returns  : 
        (ref return) : trainingData, trainResponses, testData, testResponses
    */
    vector<String> files;
    // get folder
    glob(filePath, files, true); 
    Mat frame;
    int imgIdx = 0;
    if(files.size() == 0){
        return false;
    }
    for(int i = 0; i < files.size(); i++){
        frame = imread(files[i]);
        // preprocess image
        Mat pre = preProcess(frame);
        // get n features pair for each image
        vector<vector<float>> features = extractFeatures(pre);
        for(int i = 0; i < features.size(); i++){
            // first numTest for model test
            if(imgIdx >= numTest){
                trainingData.push_back(features[i][0]);
                trainingData.push_back(features[i][1]);
                trainResponses.push_back(label);
            }else{
                testData.push_back(features[i][0]);
                testData.push_back(features[i][1]);
                testResponses.push_back(label);
            }
        }
        imgIdx++;
    }
    return true;
}

// helper function for trainAndTest, ploat data error
void plotData(Mat trainingDataMat, Mat trainResponsesMat, string mode, float* error){
    /*
    @description  : ploat train data feature (x: area, y: aspect) distributiion
    @param  : 
        trainingDataMat : trainingDataMat shape [N/2, 2], N is trainData vector size
        trainResponsesMat : trainResponsesMat shape [N, 1], N is trainData label vector size
        error : total error rate to display
    @Returns  : 
        None
    */
    float areaMax, areaMin, asMax, asMin;
    areaMax = asMax = 0.0;
    areaMin = asMin = 99999999;
    for(int i = 0; i < trainingDataMat.rows; i++){
        float area = trainingDataMat.at<float>(i, 0);
        float aspect = trainingDataMat.at<float>(i, 1);
        // get min, max value
        if(area > areaMax){
            areaMax = area;
        }
        if(aspect > asMax){
            asMax = aspect;
        }
        if(areaMin > area){
            areaMin = area;
        }
        if(asMin > area){
            asMin = aspect;
        }
    }
    // create image to display
    Mat fig = Mat::zeros(512, 512, CV_8UC3);
    for(int i = 0; i < trainingDataMat.rows; i++){
        float area = trainingDataMat.at<float>(i, 0);
        float aspect = trainingDataMat.at<float>(i, 1);
        // min-max norm [0~1] * 420 pixel
        int x = (int)(420.0f*((area - areaMin) / (areaMax - areaMin)));
        int y = (int)(420.0f*((aspect - asMin) / (asMax - asMin)));
        int label = trainResponsesMat.at<int>(i);
        Scalar color;
        if(label == 0){
            color = Scalar(255, 0, 0);
        }else if(label == 1){
            color = Scalar(0, 255, 0);
        }else if(label == 2){
            color = Scalar(0, 0, 255);
        }
        // cicle locate with start at(80, 80) to overcome border
        circle(fig, Point(x+80, y+80), 3, color, -1, 8);
    }
    if(error != NULL){
        stringstream ss;
        ss << mode << " error: " << *error << " \%";
        putText(fig, ss.str(), Point(20, 512-40), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(200, 200, 200), 1, LINE_AA);
    }
    myWin->addImage("Fig", fig);
}

void defineSVM(Ptr<SVM>& svm){
    /*
    @description  : define svm parameters
    @param  : 
        svm : svm model
    @Returns  : 
        (ref return) : svm with parameters
    */
    svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setNu(0.05);
    svm->setKernel(SVM::CHI2);
    svm->setDegree(1.0);
    svm->setGamma(2.0);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
}

void trainSVM(){
    /*
    @description  : train a svm model and test its error rate
    @param  : 
        mode : machine learning mode
    @Returns  : 
        None
    */
    vector<float> trainingData;
    vector<int> trainResponses;
    vector<float> testData;
    vector<int> testResponses;

    int numTest = 20;
    string nutPath = "data/nut";
    string ringPath = "data/ring";
    string screwPath = "data/screw";
    // read data path and extract feature
    readFolderAndExtractFeatures(nutPath, 0, numTest, trainingData, trainResponses, testData, testResponses);
    readFolderAndExtractFeatures(ringPath, 1, numTest, trainingData, trainResponses, testData, testResponses);
    readFolderAndExtractFeatures(screwPath, 2, numTest, trainingData, trainResponses, testData, testResponses);
    // cout << "Num of train samples: " << trainingData.size() << endl;
    // cout << "Num of test samples: " << testData.size() << endl;
    Mat trainingDataMat(trainingData.size() / 2, 2, CV_32FC1, &trainingData[0]);
    Mat trainResponsesMat(trainResponses.size(), 1, CV_32SC1, &trainResponses[0]);
    Mat testDataMat(testData.size() / 2, 2, CV_32FC1, &testData[0]);
    Mat testResponsesMat(testResponses.size(), 1, CV_32SC1, &testResponses[0]);
    // set row sample
    Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, trainResponsesMat);

    // select model
    Ptr<SVM> model = SVM::create();
    defineSVM(model);
    model->train(tData);
    model->save("model/svm.xml");

    if(testResponses.size() > 0){
        Mat testPredict;
        // predict
        model->predict(testDataMat, testPredict);
        testPredict.convertTo(testPredict, CV_32SC1);
        Mat errMat = testPredict != testResponsesMat;
        float error = 100.0f * countNonZero(errMat) / testResponses.size();
        cout << "svm" << " Error rate: " << error << "\%" << endl;
        plotData(trainingDataMat, trainResponsesMat, "svm", &error);
    }
    else{
        plotData(trainingDataMat, trainResponsesMat, "svm");
    }
}

vector<vector<float>> extractFeatures(Mat img, vector<int>* posLeft, vector<int>* posTop){
    /*
    @description  : extract image features and get left top loation
    @param  : 
        img : image to get feature
        postLeft : left top_left location
        postTop : top top_left location
    @Returns  : 
        features: extracted feature
    */
    vector<vector<float>> features;
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    Mat temp = img.clone();
    // find contours
    findContours(temp, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    if(contours.size() == 0){
        return features;
    }
    for(int i = 0; i < contours.size(); i++){
        Mat mask = Mat::zeros(img.rows, img.cols, CV_8UC1);
        // draw contours
        drawContours(mask, contours, i, Scalar(1), FILLED, LINE_8, hierarchy, 1);
        // get area value
        Scalar areaSum = sum(mask);
        float area = areaSum[0];
        if(area > 500){
            // calculate aspect for area is larger than 500
            RotatedRect r = minAreaRect(contours[i]);
            float w = r.size.width;
            float h = r.size.height;
            float aspect = w < h ? h / w : w / h;
            vector<float> row;
            // load calculated feature
            row.push_back(area);
            row.push_back(aspect);
            features.push_back(row);
            // load top_left location
            if(posLeft != NULL){
                posLeft->push_back((int)r.center.x);
            }
            if(posTop != NULL){
                posTop->push_back((int)r.center.y);
            }
            myWin->addImage("Extracted Feature", mask * 255);
            myWin->render();
            waitKey(10);
        }
    }
    return features;
}


/*
@File          :utils.inl
@Description:  :
@Date          :2021/12/25 20:29:26
@Author        :xieyin
@version       :1.0
*/
template<typename T>
void trainAndTest(string mode){
    /*
    @description  : train a svm model and test its error rate
    @param  : 
        mode : machine learning mode
    @Returns  : 
        None
    */
    vector<float> trainingData;
    vector<int> trainResponses;
    vector<float> testData;
    vector<int> testResponses;

    int numTest = 20;
    string nutPath = "data/nut";
    string ringPath = "data/ring";
    string screwPath = "data/screw";
    // read data path and extract feature
    readFolderAndExtractFeatures(nutPath, 0, numTest, trainingData, trainResponses, testData, testResponses);
    readFolderAndExtractFeatures(ringPath, 1, numTest, trainingData, trainResponses, testData, testResponses);
    readFolderAndExtractFeatures(screwPath, 2, numTest, trainingData, trainResponses, testData, testResponses);
    // cout << "Num of train samples: " << trainingData.size() << endl;
    // cout << "Num of test samples: " << testData.size() << endl;
    Mat trainingDataMat(trainingData.size() / 2, 2, CV_32FC1, &trainingData[0]);
    Mat trainResponsesMat(trainResponses.size(), 1, CV_32SC1, &trainResponses[0]);
    Mat testDataMat(testData.size() / 2, 2, CV_32FC1, &testData[0]);
    Mat testResponsesMat(testResponses.size(), 1, CV_32SC1, &testResponses[0]);
    // set row sample
    Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, trainResponsesMat);

    // select model
    Ptr<T> model = T::create();
    model->train(tData);
    model->save("model/" + mode + ".xml");

    if(testResponses.size() > 0){
        Mat testPredict;
        // predict
        model->predict(testDataMat, testPredict);
        testPredict.convertTo(testPredict, CV_32SC1);
        Mat errMat = testPredict != testResponsesMat;
        float error = 100.0f * countNonZero(errMat) / testResponses.size();
        cout << mode << " Error rate: " << error << "\%" << endl;
        plotData(trainingDataMat, trainResponsesMat, mode, &error);
    }
    else{
        plotData(trainingDataMat, trainResponsesMat, mode);
    }
}

template<typename T>
void predict(vector<vector<float>> features, vector<int> posLeft, vector<int> posTop, string mode, Mat& imgOut){
    /*
    @description  : predict features extracted from imgOut, and put text in left top position
    @param  : 
        features : extracted feature from imgOut
        posLeft : left_top left location
        posTop : left_top top location
        mode : machine learning mode
        imgOut : the img with text output
    @Returns  : 
        (ref return) : imgOut
    */
    for(int i = 0; i < features.size(); i++){
        Mat predDataMat(1, 2, CV_32FC1, &features[i][0]);
        Ptr<T> model = Algorithm::load<T>("model/" + mode + ".xml");
        float result = model->predict(predDataMat);
        cout << result << endl;
        stringstream ss;
        Scalar color;
        if(result == 0){
            color = Scalar(255, 0, 0);
            ss << "NUT";
        }
        else if(result == 1){
            color = Scalar(0, 255, 0);
            ss << "RING";
        }
        else if(result == 2){
            color = Scalar(0, 255, 0);
            ss << "SCREW";
        }
        putText(imgOut, ss.str(), Point2d(posLeft[i], posTop[i]), FONT_HERSHEY_SIMPLEX, 0.4, color);
    }
}

3)主函式
/*
@File          :main.cpp
@Description:  :
@Date          :2021/12/25 09:23:30
@Author        :xieyin
@version       :1.0
*/

#include<iostream>
#include<string>
#include<sstream>
#include<memory>
using namespace std;

#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include<opencv2/ml.hpp>
using namespace cv;
using namespace cv::ml;

#include"mwindow.hpp"
#include"utils.hpp"

const char* keys = {
    "{help h usage ? | | Print this message}"
    "{@image | | Image for test}"
    "{@lightPat | | light pattern for test image}"
    "{@mode | svm | machine learning mode, default svm}"
};

shared_ptr<MWindow> myWin;

int main(int argc, const char** argv){
    // command line parser
    CommandLineParser parser(argc, argv, keys);
    if(parser.has("help")){
        parser.printMessage();
        return 0;
    }
    if(!parser.check()){
        parser.printErrors();
        return 0;
    }

    // define mywin
    myWin = make_shared<MWindow>("Main Window", 2, 2, 700, 1000, 1);
    
    // get test image path
    String imgFile = parser.get<String>(0); 
    Mat img = imread(imgFile, 0);
    if(img.data == NULL){
        cout << "can not read image file." << endl;
        return 0;
    }

    // get light pattern image
    String ligPatFile = parser.get<String>(1);
    Mat lightPat = imread(ligPatFile, 0);
    if(lightPat.data == NULL){
        cout << "can not read image file." << endl;
        return 0;
    }
    // mdeianblur light pattern
    medianBlur(lightPat, lightPat, 3);

    // copy img to imgOut
    Mat imgOut = img.clone();
    cvtColor(imgOut, imgOut, COLOR_GRAY2BGR);

    // preprocess image
    Mat pre = preProcess(img);

    // get feature and top left location from image
    vector<int> posLeft, posTop;
    vector<vector<float>> features = extractFeatures(pre, &posLeft, &posTop);

    // get mode selection
    string mode = parser.get<string>(2);
    // train and predict model
    if (mode == "svm"){
        trainSVM();
        // trainAndTest<SVM>(mode);
        predict<SVM>(features, posLeft, posTop, mode, imgOut);
    }
    else if (mode == "bayes"){
        trainAndTest<NormalBayesClassifier>(mode);
        predict<NormalBayesClassifier>(features, posLeft, posTop, mode, imgOut);
    }
    else if(mode == "boost"){
        trainAndTest<Boost>(mode);
        predict<Boost>(features, posLeft, posTop, mode, imgOut);
    }
    else{
        cout << "not support model";
        return 0;
    }

    myWin->addImage("binary Image", pre);
    myWin->addImage("result", imgOut);
    myWin->render();
    waitKey(0);
    return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/394101.html

標籤:其他

上一篇:[YOLO專題-2]:總體-YOLO目標檢測的網路總體架構與核心概念

下一篇:視覺SLAM十四講 第5講 相機與影像

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more