赞
踩
作者:Simon Song
1.KMEANS:是一种聚类算法,主要过程:
流程图:
参数k–> 初始化中心点–>根据每个样本与中心的距离,分配聚类编号–>对编号相同的样本,计算新的中心位置–>当距离(D)小于阈值(T)或迭代(Iteration)次数大于迭代次数(C)->条件成立退出;条件不成立,从第三个步骤开始。
初始化中心的方法有:KMEANS_PP_CENTERS 中心初始化算法
KMEANS_RANDOM_CENTERS随机初始化
KMEANS_USE_INTIAL_LABEL用户指定标签
2.GMM高斯混合模型是基于高斯混合期望最大,是概率的方法。gmm分割,效果不如kmeans,有边缘颜色处理不好的情况。
找到这种方式的高斯函数,各个系数,各个值。找到了之后,不断迭代逼近最真实的情况。按真实的情况进行直方图分类,得到直方图属于哪个高斯核和分类。至此完成分类功能。
vs2015,OpenCV341,C++
#include<iostream> #include<opencv2/opencv.hpp> #include<math.h> using namespace std; using namespace cv; using namespace cv::ml;//机器学习模块 Mat Mat_to_sample(Mat&image);//数据组装函数 int main(int argc, char** argv) { //读取图 Mat src = imread("G:/opencv_trainning/vs2015/5/kmeanstest/tx.png"); //判空 if(src.empty()){ cout << "open fault" << endl; return -1; } //显示 imshow("src",src); //数据组装, n行3列单通道 Mat points=Mat_to_sample(src); //kmeans分割 //定义相关变量 int k = 4;//聚类数 Mat labels;//标签,n*1单通道,整形数据 Mat centers;//中心,m行2列,整形单通道 TermCriteria criteria(TermCriteria::EPS+TermCriteria::COUNT,10,0.1);//条件,迭代10次,最小量0.1 //参数:(32F数据矩阵,分类个数,标签矩阵,条件,不同初始化分类尝试次数,聚类中心初始化标记,聚类中心) kmeans(points,k,labels, criteria,3,KMEANS_PP_CENTERS,centers); /* //gmm分割,效果不如kmeans,有边缘颜色处理不好 Ptr<EM> em_cluster = EM::create();//创建 em_cluster->setClustersNumber(4);// 设置聚类数 em_cluster->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);//设置协方差矩阵类型 em_cluster->setTermCriteria(TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,100,0.1));//设置检测标准 Mat labels;//标签矩阵 em_cluster->trainEM(points,noArray(),labels,noArray());//训练, 参数:(数据矩阵,noArray(), 标签矩阵,noArray()) */ //去背景+遮罩生成 Mat mask = Mat::zeros(src.size(),CV_8UC1);//定义遮罩矩阵 int index = src.cols * 2 + 2;//矩阵(2,2)位置的索引位置 int bg_label = labels.at<int>(index, 0);//获得背景标签 //更改背景以及赋值mask矩阵 int width = src.cols;//宽 int height = src.rows;//高 Mat dst = Mat::zeros(src.size(),src.type());//定义一张图用于效果显示 for (size_t row = 0; row < src.rows;row++) {//行 for (size_t col = 0; col < src.cols;col++) {//列 //获得位置索引 int index = width*row + col; //获得标签 int label = labels.at<int>(index, 0); //比较,与背景相等时 if (label==bg_label){ //mask矩阵位置赋值0 mask.at<uchar>(row, col) = 0; //更改bgr通道数据为0 dst.at<Vec3b>(row, col)[0] = 0;//b dst.at<Vec3b>(row, col)[1] = 0;//g dst.at<Vec3b>(row, col)[2] = 0;//r } else {//否则 //mask矩阵位置赋值255 mask.at<uchar>(row, col) = 255; //获取原图值 Vec3b bgr = src.at<Vec3b>(row, col); //更改bgr通道数据为原图值 dst.at<Vec3b>(row, col) = bgr;//bgr } } } imshow("kmeans",dst); imshow("mask",mask); //腐蚀+高斯模糊 //获得结构元素,参数:(形状,核尺寸,锚点) Mat kernel = getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1)); //腐蚀,(单通道原图,单通道目标图,核元素),其他参数默认 erode(mask,mask, kernel); imshow("erode",mask); //高斯滤波,参数:(原图,目标图,核尺寸,x标准差,y标准差),其他参数默认 GaussianBlur(mask,mask,Size(3,3),0,0); imshow("gaussianBlur",mask); //通道混合输出 //定义随机颜色 Vec3b color(theRNG().uniform(0,255), theRNG().uniform(0, 255), theRNG().uniform(0, 255));//定义颜色 Mat result = Mat::zeros(src.size(),src.type());//定义结果图 for (size_t row = 0; row < height; row++) {//行 for (size_t col = 0; col < width; col++) {//列 int mc = mask.at<uchar>(row, col);//获取mask颜色 //判断mask颜色 if(mc == 255){//为白色时,给原值 result.at<Vec3b>(row, col) = src.at<Vec3b>(row,col); } else if (mc==0) {//为黑色时,给背景色 result.at<Vec3b>(row, col) = color;//给随机颜色 } else {//否则按权重比例给值 double w = mc / 255;//获得权重 int b1 = src.at<Vec3b>(row,col)[0];//获得原图值b int g1 = src.at<Vec3b>(row, col)[1];//获得原图值g int r1 = src.at<Vec3b>(row, col)[2];//获得原图r int b2 = color[0];//获得随机颜色b int g2 = color[1];//获得随机颜色g int r2 = color[2];//获得随机颜色r //计算bgr值 int b = b1*w + b2*(1 - w);//b int g = g1*w + g2*(1 - w);//g int r = r1*w + r2*(1 - w);//r result.at<Vec3b>(row, col)=Vec3b((uchar)b,(uchar)g,(uchar)r);//赋值 } } } imshow("result",result); waitKey(0);//按键等待 return 0; } //数据组装函数 Mat Mat_to_sample(Mat&image) { //定义必要的变量 int w = image.cols;//宽 int h = image.rows;//高 int dims = image.channels();//维度 int sampleCount = w*h;//取样数量 Mat points(sampleCount,dims,CV_32F,Scalar(10));//定义取样点,32FC1,m*3 for (size_t row = 0; row < image.rows;row++) {//行 for (size_t col = 0; col < image.cols;col++) {//列 //获得index位置 int index = w*row + col;// //获取点颜色 Vec3b bgr = image.at<Vec3b>(row, col); //赋值到points points.at<float>(index, 0) = static_cast<int>(bgr[0]);//b points.at<float>(index, 1) = static_cast<int>(bgr[1]);//g points.at<float>(index, 2) = static_cast<int>(bgr[2]);//r } } return points;//返回点数据 }
1.整理图片为N行3列的单通道数据,为分割做准备。
1.kmeans/gmm分割:获得聚类标签矩阵labels(n行1列),kmeans有中心矩阵(m 行2列);
2.去背景+生成遮罩:获得有用的数据区域(mask和dst两张图);
3.腐蚀+高斯模糊:将边缘以高斯分布方式,获得不同的中间颜色,后面会对边缘部分进行权重混合处理。注意腐蚀和高斯模糊的核尺寸要一致。
4.混合处理:将背景部分替换为指定颜色,将前景图的每个像素赋值到当前位置;对于边缘部分(非0非255),按权重混合前景色和指定色。
到此,我们完成了图片背景的替换,实现过程稍显复杂,习惯就好了。
我是Simon,在这里期待与您的交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。