当前位置:   article > 正文

OpenCV之图像分割(二) 高斯混合模型(GMM)方法 样本数据训练与预言&图像分割_cv2.ml.em_create

cv2.ml.em_create

基于高斯函数的算法,通过混合单个或多个高斯函数,计算对应像素中概率,哪个分类的概率最高的,则属于哪个类别

图解:

 

GMM算法概述

GMM方法跟K - Means相比较,属于软分类
实现方法 - 期望最大化(E - M)
停止条件 - 收敛,或规定的循环次数

 

代码:

  1. #include<opencv2\core\core.hpp>
  2. #include<opencv2\highgui\highgui.hpp>
  3. #include<opencv2\imgproc\imgproc.hpp>
  4. #include<opencv2\opencv.hpp>
  5. #include<iostream>
  6. using namespace std;
  7. using namespace cv;
  8. using namespace cv::ml;
  9. int main()
  10. {
  11. Mat img = Mat::zeros(500, 500, CV_8UC3);
  12. RNG rng;
  13. Scalar colorTab[] = {
  14. Scalar(0, 0, 255), // 红
  15. Scalar(0, 255, 0), // 绿
  16. Scalar(255, 0, 0), // 蓝
  17. Scalar(0, 255, 255), // 黄
  18. Scalar(255, 0, 255) // 品红
  19. };
  20. int numCluster = rng.uniform(2, 5);
  21. int numSample = rng.uniform(5, 1000);
  22. Mat points(numSample, 2, CV_32FC1); //两列,单通道,与KMeans不同
  23. Mat labels;
  24. //生成随机数
  25. for (int k = 0; k < numCluster; k++)
  26. {
  27. Point center;
  28. center.x = rng.uniform(0, img.cols);
  29. center.y = rng.uniform(0, img.rows);
  30. Mat pointChunk = points.rowRange(k*numSample / numCluster,
  31. k != numCluster - 1 ? (k + 1)*numSample / numCluster : numSample);
  32. rng.fill(pointChunk, RNG::NORMAL, Scalar(center.x, center.y), Scalar(img.cols*0.05, img.rows*0.05));
  33. }
  34. randShuffle(points, 1, &rng);
  35. Ptr<cv::ml::EM> em_model = cv::ml::EM::create();// 生成 EM 期望最大化
  36. em_model->setClustersNumber(numCluster); // 设置分类数
  37. em_model->setCovarianceMatrixType(cv::ml::EM::COV_MAT_SPHERICAL); // 协方差矩阵类型
  38. em_model->setTermCriteria(TermCriteria(TermCriteria::EPS +
  39. TermCriteria::COUNT, 100, 0.1)); // 迭代条件,EM训练比KMeans耗时,可能会不收敛,所以迭代次数设大点
  40. em_model->trainEM(points, noArray(), labels, noArray());
  41. // EM训练,获得分类结果,参数labels与KMeans的labels参数意思一样
  42. /*
  43. bool trainEM(InputArray samples, //输入的样本,一个单通道的矩阵。从这个样本中,进行高斯混和模型估计
  44. OutputArray logLikelihoods=noArray(),//可选项,输出一个矩阵,里面包含每个样本的似然对数值
  45. OutputArray labels=noArray(), // 可选项,输出每个样本对应的标注
  46. OutputArray probs=noArray() //可选项,输出一个矩阵,里面包含每个隐性变量的后验概率
  47. );
  48. */
  49. Mat sample(1, 2, CV_32FC1);
  50. for (int row = 0; row < img.rows; row++)
  51. {
  52. for (int col = 0; col < img.cols; col++)
  53. {
  54. sample.at<float>(0) = (float)col;
  55. sample.at<float>(1) = (float)row;
  56. Vec2d predict = em_model->predict2(sample, noArray());// 预言
  57. int response = cvRound(predict[1]);// response 就是给出的当前的分类
  58. circle(img, Point(col, row), 1, colorTab[response]*0.75, -1);// 以EM预言的分类结果,将img当前点用不同颜色绘制出来
  59. /*
  60. Vec2d predict2(InputArray sample,//待测样本
  61. OutputArray probs //返回一个Vec2d类型的数,包括两个元素的double向量,
  62. //第一个元素为样本的似然对数值,第二个元素为最大可能混和分量的索引值。
  63. )
  64. */
  65. }
  66. }
  67. for (int i = 0; i < numSample; i++)//绘制出样本中的数据点属于哪种分类
  68. {
  69. Point p(cvRound(points.at<float>(i, 0)), cvRound(points.at<float>(i, 1)));
  70. circle(img, p, 1, colorTab[labels.at<int>(i)], -1);// 用不同颜色在img上绘制上面随机产生的分类点
  71. }
  72. imshow("GMM-EM", img);
  73. waitKey(0);
  74. return 0;
  75. }

效果图:

代码:图像分割

  1. /*
  2. 准备数据,建立一个width*height的样本,将numSample样本在points中填满,
  3. 使用一维的存取BGR通道,points是(size(numSample,dims),CV_64FC1);
  4. 每一行的points存取img中的一个位置的BGR像素,points填满后。
  5. Ptr<EM> em_model = EM::create();设置分类数目,矩阵类型,迭代算法的终止条件
  6. 调用trainEM进行训练,将结果放置在labels中,根据labels给图像进行分类标记
  7. 预言:设置一样sample存储每一个像素位置的三个通道的值
  8. 将sample放入predict2函数中进行预言,将预言结果在图像上标记出来
  9. */
  10. #include<opencv2\core\core.hpp>
  11. #include<opencv2\highgui\highgui.hpp>
  12. #include<opencv2\imgproc\imgproc.hpp>
  13. #include<opencv2\opencv.hpp>
  14. #include<iostream>
  15. using namespace std;
  16. using namespace cv;
  17. using namespace cv::ml;
  18. int main()
  19. {
  20. Mat srcImg = imread("toux.jpg");
  21. if (!srcImg.data)
  22. {
  23. printf("could not load image...\n");
  24. return -1;
  25. }
  26. imshow("input image", srcImg);
  27. Scalar colors[] = {
  28. Scalar(255, 0, 0),
  29. Scalar(0, 255, 0),
  30. Scalar(0, 0, 255),
  31. Scalar(255, 255, 0)
  32. };
  33. int width = srcImg.cols;
  34. int height = srcImg.rows;
  35. int dims = srcImg.channels();
  36. int numCluster = 3;
  37. int numSample = width*height;
  38. Mat points(numSample, dims, CV_64FC1);
  39. Mat labels;
  40. // 图像RGB像素数据转换为样本数据
  41. int index = 0;
  42. for (int row = 0; row < height; row++) // 这里的步骤与KMeans是一样的
  43. {
  44. for (int col = 0; col < width; col++)
  45. {
  46. index = row*width + col;
  47. Vec3b bgr = srcImg.at<Vec3b>(row, col);
  48. points.at<double>(index, 0) = static_cast<int>(bgr[0]);
  49. points.at<double>(index, 1) = static_cast<int>(bgr[1]);
  50. points.at<double>(index, 2) = static_cast<int>(bgr[2]);
  51. }
  52. }
  53. double time0 = getTickCount();
  54. //EM Cluster Train
  55. Ptr<EM> em_model = EM::create(); // 生成 EM 期望最大化,其图像分割的方式是基于机器学习的方式
  56. em_model->setClustersNumber(numCluster); // 设置分类数
  57. em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL); // 协方差矩阵类型
  58. em_model->setTermCriteria(TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 100, 0.1)); // 迭代条件,EM训练比KMeans耗时,可能会不收敛,所以迭代次数设大点
  59. em_model->trainEM(points, noArray(), labels, noArray()); // EM训练,获得分类结果,参数labels与KMeans的labels参数意思一样,速度比KMeans要慢很多
  60. cout << "train time=" << (getTickCount() - time0) / getTickFrequency() << endl; // train time=10425.8 训练所需的时间很长
  61. // 对每个像素标记颜色与显示
  62. // 对每个像素标记颜色与显示
  63. Mat result_nopredict = Mat::zeros(srcImg.size(), CV_8UC3);
  64. Mat result_predict = Mat::zeros(srcImg.size(), CV_8UC3);
  65. Mat sample(dims, 1, CV_64FC1); // 也只能用 CV_64F
  66. time0 = getTickCount();
  67. int r = 0, g = 0, b = 0;
  68. for (int row = 0; row < height; row++)
  69. {
  70. for (int col = 0; col < width; col++)
  71. {
  72. // 获取训练的分类结果,放到 result_nopredict 中
  73. index = row*width + col;
  74. int label = labels.at<int>(index, 0);
  75. Scalar c = colors[label];
  76. result_nopredict.at<Vec3b>(row, col)[0] = c[0];
  77. result_nopredict.at<Vec3b>(row, col)[1] = c[1];
  78. result_nopredict.at<Vec3b>(row, col)[2] = c[2];
  79. // 通过预言获得分类结果,因为EM训练用的是src的颜色数据,所以用src的颜色数据做预言,得到的结果与 result_nopredict 是一模一样的
  80. b = srcImg.at<Vec3b>(row, col)[0];
  81. g = srcImg.at<Vec3b>(row, col)[1];
  82. r = srcImg.at<Vec3b>(row, col)[2];
  83. sample.at<double>(0) = b;
  84. sample.at<double>(1) = g;
  85. sample.at<double>(2) = r;
  86. Vec2d predict = em_model->predict2(sample, noArray()); // 预言,预言的时间是很短的
  87. int response = cvRound(predict[1]); // response 就是目标颜色数据在EM训练中预言的分类
  88. c = colors[response];
  89. result_predict.at<Vec3b>(row, col)[0] = c[0];
  90. result_predict.at<Vec3b>(row, col)[1] = c[1];
  91. result_predict.at<Vec3b>(row, col)[2] = c[2];
  92. }
  93. }
  94. printf("execution time(ms) : %.2f\n", (getTickCount() - time0) / getTickFrequency()); // execution time(ms) : 1600.31
  95. imshow("EM-Segmentation nopredict", result_nopredict); // 从效果看,KMeans更好些
  96. imshow("EM-Segmentation predict", result_predict);
  97. waitKey(0);
  98. return 0;
  99. }

效果图:

参考:https://blog.csdn.net/huanghuangjin/article/details/81452229

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/351121
推荐阅读
  

闽ICP备14008679号