为 svm::predict 准备 cv::mat

Posted

技术标签:

【中文标题】为 svm::predict 准备 cv::mat【英文标题】:cv::mat preparation for svm::predict 【发布时间】:2015-07-09 19:09:20 【问题描述】:

我是 OpenCv 的新手,正在从 OpenCv2.4 过渡到 OpenCv3... 我已经训练了存储在 *.xml 文件中的 SVM 分类器。

我在过渡期间的问题是如何正确准备 cv::Mat 以便它可以作为 cv::ml::SVM predict 的输入。

CvMat* NumExtractor::prepareDataSVM(cv::Mat & other)
      resize(other, other, Size(28,28));
      cv::Size s = other.size();
      int width = (int)s.width;
      int height = (int)s.height;
      cv::Mat ret = other.clone();
      CvMat* mat = cvCreateMat(width*height, 1, CV_32FC1);
      for(int i = 0; i < height; ++i)
      
          for(int j = 0; j < width; ++j)
          
             mat.data.fl[i*width+j] = (float)other.at<uchar>(i,j);
          
      
      return mat;
   

但我正在努力如何使用 cv::Mat 做到这一点。有人可以帮我吗?

编辑: 我想到了。我使用了错误的 API 来存储值。

【问题讨论】:

other 是什么?以及为什么要调整为 28x28? 其他是带有数字的输入图像,我想将其提供给 svm 进行分类。我调整它的大小是因为我在 28x28 样本上训练了 svm 那么,你的一维特征是什么? 嗯,有问题,或者它是......我需要将我的图像转换为 1d 并将其存储在 cv::mat 中,以便我可以提供给 svm。我不知道怎么把它放在垫子里,但我刚刚想通了。 :) 好吧,然后解决了.. :D 只是一些建议:返回一个Mat1f(没有指针),使other 一个Mat1b,创建mat 喜欢:Mat1f mat(width*height, 1, 0.f) , 将数据设置为mat like: mat(i*width+j) = float(other(i,j)). 【参考方案1】:

以下代码帮助您开始使用 cv::ml::SVM 的 train() 和 predict() 函数;这是代码的解释(代码在opencv4.2中运行良好)。 输入图像是来自数据集的水下图像(可在此处获得基准https://li-chongyi.github.io/proj_benchmark.html)。代码的第一部分处理图像的红色通道补偿,以改善图像的色彩偏差。 对于红色通道补偿,使用了论文“Color Correction Based on CFA and Enhancement Based on Retinex With Dense Pixels for Underwater Images”的公式8。红色通道补偿后的图像在代码中存储在 image2 中。

然后,选择图像中仅包含水像素的矩形部分作为训练数据。训练数据被分类成一个 3 列矩阵,每列分别保存红色、绿色和蓝色强度值(例如 mx3)。

由于我们需要两副眼镜,因此选择图像中包含非水像素的矩形部分作为数据的另一部分。

然后,将纯水数据和非水数据连接起来形成训练数据。标签矩阵的排列方式使其为水像素保持 + 1 和 - 1 水像素。 然后 SVM 被管理以使用线性内核运行,但伽马也设置了一个值以便使用 RBF 内核运行。 input image

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


int main()

    std::cout << "Hello World!\n";
    //read an image
    Mat image = imread("9554.png", 1);
    //check for existance of data
    if (!image.data) 
     printf("no image data.\n"); return -1; 
    //planes is a vector for holding rgb channels separately
    //std::vector<Mat> planes;
    Mat planes[3];

    //split the image into channels
    //planes[2] is the red channel
    split(image, planes);

    // converting planes from uchar to double
    planes[0].convertTo(planes[0], CV_64FC1);
    planes[1].convertTo(planes[1], CV_64FC1);
    planes[2].convertTo(planes[2], CV_64FC1);

    // defining coefficients of green and blue channel for blending
    double a = 0.05, b = 0.95;
    
    //sum_im stores pixelwise sum of Red, Green and Blue planes
    Mat imBlendNormal_B_G, sum_im;

    //converting to double
    imBlendNormal_B_G.convertTo(imBlendNormal_B_G, CV_64FC1);
    sum_im.convertTo(sum_im, CV_64FC1);

    //blending green and blue planes with a and b coefficients
    // and 0.0 offset(or gamma)
    addWeighted(planes[1], a, planes[0], b, 0.0, imBlendNormal_B_G);

    // sum of red, green and blue pixel in two addWeighted calls
    addWeighted(planes[2], 1.0, planes[1], 1.0, 0.0, sum_im);
    addWeighted(planes[0], 1.0, sum_im, 1.0, 0.0, sum_im);

    //dividing blended green and blue image to total RGB sum
    divide(imBlendNormal_B_G, sum_im, imBlendNormal_B_G);

    //defining average kernel 3x3
    Mat avg3x3_kernel = (Mat_<double>(3, 3) << 1.0 / 9.0, 1.0 / 9.0, 1.0 / 9.0, 1.0 / 9.0, 1.0 / 9.0, 1.0 / 9.0, 1.0 / 9.0, 1.0 / 9.0, 1.0 / 9.0);
    
    //defining matrices for storing 3x3 average of blue and green planes
    Mat blueAverage, greenAverage;
    // converting to double type
    blueAverage.convertTo(blueAverage, CV_64FC1);
    greenAverage.convertTo(greenAverage, CV_64FC1);

    // taking 3x3 average
    filter2D(planes[0], blueAverage, planes[0].depth(), avg3x3_kernel);
    filter2D(planes[1], greenAverage, planes[1].depth(), avg3x3_kernel);

    //imBlendAverage_B_G_R: for blending of averaged green and blue channels
    Mat imBlendAverage_B_G_R; 
    //convert to double
    imBlendAverage_B_G_R.convertTo(imBlendAverage_B_G_R, CV_64FC1);

    //blend averaged green and blue with a and b coeffs
    addWeighted(greenAverage, a, blueAverage, b, 0.0, imBlendAverage_B_G_R);
    
    //differentiate red values
    addWeighted(imBlendAverage_B_G_R, 1.0, planes[2], -1.0, 0.0, imBlendAverage_B_G_R);

    //CompensationTermRed: storing finally compensated red channel intensities
    Mat CompensationTermRed; 
    //coverting to double
    CompensationTermRed.convertTo(CompensationTermRed, CV_64FC1);

    //multiplication term
    CompensationTermRed = imBlendAverage_B_G_R.mul(imBlendNormal_B_G);

    //final add term
    addWeighted(CompensationTermRed, 1.0, planes[2], 1.0, 0.0, CompensationTermRed);
    
    // assign new red channel values to planes[2]
    planes[2] = CompensationTermRed;

    Mat image2;
    cv::merge(planes, 3, image2); 
    image2.convertTo(image2, CV_8UC3);
    imshow("merge",image2);
    printf("\ndims of image2 (merge): %d  %d\n", image2.rows, image2.cols);
    //defining rectangle of coordination
    Rect waterrect(5,5,365,135);
    
    // water only image 
    Mat WaterOnlyImage = image(waterrect);
    imshow("water only image", WaterOnlyImage);    
    
    // separating WaterOnlyImage planes
    Mat WaterOnlyPlanes[3];
    split(WaterOnlyImage, WaterOnlyPlanes);
    printf("size of WaterOnlyPlanes[0]: %d  %d  %d\n", WaterOnlyPlanes[0].rows,WaterOnlyPlanes[0].cols, WaterOnlyPlanes[0].channels());

    // vector of each plane
    Mat WaterOnlyRedVector, WaterOnlyGreenVector, WaterOnlyBlueVector;

    // reshape each plane into vector separately
    // column vector
    WaterOnlyRedVector = WaterOnlyPlanes[2].reshape(0, 1).t();
    WaterOnlyGreenVector = WaterOnlyPlanes[1].reshape(0, 1).t();
    WaterOnlyBlueVector = WaterOnlyPlanes[0].reshape(0, 1).t();
    printf("WaterOnlyRedVector size: %d  %d  %d\n", WaterOnlyRedVector.rows, WaterOnlyRedVector.cols, WaterOnlyRedVector.channels());
    
    // (row*com)x3 vector holding all RGB pixels of water
    // (WaterOnlyRedVector.rows, 3);
    Mat WaterOnlyRGBVector;

    // concatenation of three vectors values using hconcat
    // hconcate: horizontal concatenation
    hconcat(WaterOnlyRedVector, WaterOnlyGreenVector, WaterOnlyRGBVector);
    hconcat(WaterOnlyRGBVector, WaterOnlyBlueVector, WaterOnlyRGBVector);
    printf("WaterOnlyRGBVector dims is: %d  %d  %d\n", WaterOnlyRGBVector.rows, WaterOnlyRGBVector.cols, WaterOnlyRGBVector.channels());
    
    // label vector for water pixels, all preset one
    Mat WaterOnlyLabelVector_float = Mat::ones(WaterOnlyRGBVector.rows, 1, CV_32SC1);
    printf("WaterOnlyLabelVector_float dims: %d\t%d\t%d\t\n", WaterOnlyLabelVector_float.rows, WaterOnlyLabelVector_float.cols, WaterOnlyLabelVector_float.channels());
    //std::cout << WaterOnlyLabelVector_float << "\n";

    // defining non water coordination
    Rect nonWaterRect(1, 400, 640, 320);
    //non water image
    Mat NonWaterImage = image(nonWaterRect);
    imshow("non water image", NonWaterImage);

    //holding plited nonwater image planes
    Mat NonWaterPlanes[3];

    //split nonwater image sample
    split(NonWaterImage, NonWaterPlanes);
    printf("NonWaterPlanes[0] dims: %d\t%d\t%d \n", NonWaterPlanes[0].rows, NonWaterPlanes[0].cols, NonWaterPlanes[0].channels());

    // 3 column vector for each of rgb planes
    Mat NonWaterRedVector, NonWaterGreenVector, NonWaterBlueVector;

    // reshaping each plane to get a column vector Mx1
    NonWaterRedVector = NonWaterPlanes[2].reshape(0, 1).t();//red 1st
    NonWaterGreenVector = NonWaterPlanes[1].reshape(0, 1).t();//green 2nd
    NonWaterBlueVector = NonWaterPlanes[0].reshape(0, 1).t();//blue
    printf("NonWaterGreenVector size is: %d\t%d\t%d\t\n", NonWaterGreenVector.rows, NonWaterGreenVector.cols, NonWaterGreenVector.channels());
    
    // Mx3 vector holding all RGB pixels of NonWater
    Mat NonWaterRGBVector;

    // concatenate 3 column vec into one place (Mx3)
    cv::hconcat(NonWaterRedVector, NonWaterGreenVector, NonWaterRGBVector);
    cv::hconcat(NonWaterRGBVector, NonWaterBlueVector, NonWaterRGBVector);
    printf("NonWaterRGBVector dims: %d\t%d\t%d\t\n", NonWaterRGBVector.rows, NonWaterRGBVector.cols, NonWaterRGBVector.channels());

    // label vector for NonWater pixels
    Mat NonWaterLabelVector_float = cv::Mat::zeros(NonWaterRGBVector.rows, 1, CV_32SC1) - 1.0;
    printf("NonWaterLabelVector_float dims: %d\t%d\t%d\t\n", NonWaterLabelVector_float.rows, NonWaterLabelVector_float.cols, NonWaterLabelVector_float.channels());
    //cout << NonWaterLabelVector_float << "\n";
    
    // label matrix for all data
    Mat AllLabelVector_float;
    cv::vconcat(WaterOnlyLabelVector_float, NonWaterLabelVector_float, AllLabelVector_float);
    printf("AllLabelVector_float dims:  %d\t%d\t%d\t\n", AllLabelVector_float.rows, AllLabelVector_float.cols, AllLabelVector_float.channels());

    //concatenation of all training data
    // uchar mat
    Mat AllTrainingDataVector_uchar;
    cv::vconcat(WaterOnlyRGBVector, NonWaterRGBVector, AllTrainingDataVector_uchar);
    // convert to double
    Mat AllTrainingDataVector_float;
    AllTrainingDataVector_uchar.convertTo(AllTrainingDataVector_float, CV_32F);
    printf("AllTrainingDataVector_float dims: %d\t%d\t%d\t\n", AllTrainingDataVector_float.rows, AllTrainingDataVector_float.cols, AllTrainingDataVector_float.channels());
    //std::cout << AllTrainingDataVector_float << "\n";
    
    // define support vector machine
    Ptr<ml::SVM> WaterSVMParams = ml::SVM::create();
    WaterSVMParams->setType(cv::ml::SVM::C_SVC);
    WaterSVMParams->setKernel(cv::ml::SVM::RBF);
    WaterSVMParams->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER,100,1e-6));
    WaterSVMParams->setGamma(0.000000001);
    // train data
    WaterSVMParams->train(AllTrainingDataVector_float, ROW_SAMPLE, AllLabelVector_float);

    // prediction responses image
    Mat WaterPredictionImage = Mat::zeros(image.rows, image.cols, CV_8UC3);

    // blue for water and green for nonwater
    Vec3b green(0, 255, 0), blue(255, 0, 0);

    // loop to fill the response image with prediction colors
    for (int i = 0; i < image.rows; i++)
    
        for (int j = 0; j < image.cols; j++) 
            // getting one pixel at (i,j)
            Vec3b oneTestPixel = image.at<Vec3b>(i, j);
            // getting rgb vals
            float oneTestPixelRed = (float)oneTestPixel.val[2];
            float oneTestPixelGreen = (float)oneTestPixel.val[1];
            float oneTestPixelBlue = (float)oneTestPixel.val[0];

            Mat samplePixelMat = (Mat_<float>(1, 3) << oneTestPixelRed, oneTestPixelGreen, oneTestPixelBlue);
            //cout <<"curret test pixel:\t" << samplePixelMat << "\n";
            float responseSVM = WaterSVMParams->predict(samplePixelMat);
            if (responseSVM == 1.0) 
                WaterPredictionImage.at<Vec3b>(i, j) = blue;
            
            if (responseSVM == -1.0) 
                WaterPredictionImage.at<Vec3b>(i, j) = green;
            
        //end of for j

    //end of for i
    
    imshow("WaterPredictionImage-Linear Kernel", WaterPredictionImage);
        

    waitKey(0);
    return 0;


【讨论】:

以上是关于为 svm::predict 准备 cv::mat的主要内容,如果未能解决你的问题,请参考以下文章

为 const 数据创建 cv::Mat 标头

将一行 cv::Mat 转换为 std::vector

将 Magick::Image 转换为 cv::Mat

cv::Mat 转换为特征矩阵并返回

将 cv::Mat 转换为 vector<int>

如何从 c++ 接口 cv::Mat 转换为 c IplImage?