[OpenCV+VS2015]Meanshift学习+算法代码

[OpenCV+VS2015]meanshift算法

1 meanshift介绍

这里有很多大神介绍,小弟我也不再造轮子了。参考文献

1.1 meanshift自己的一点理解

我个人认为是(纯属个人观点,如有不同,欢迎交流)
如果从数学角度看,是让一个圆形的中心向着圆形的重心移动,即圆心一直往数据集密度最大的方向移动;

如果从物理的角度看,是力的合成与物体的运动。每次迭代通过求取力的合成向量,然后让圆心沿着力的合成方向,移动到新的平衡位置;

如果从图像的角度来看,是让从人眼看起来相似的像素聚类合并

1.2 meanshift个人简化理解

感觉论文中的公式比较高深,我根据理解总结了一个别的公式:
在这里插入图片描述
理解到这里就可以按着下面思想撸程序了
在这里插入图片描述

2 meanshift算法

2.1 OpencvAPI

opencv有自带的API处理,函数为pyrMeanShiftFiltering()

2.2 编写meanshift

opencv中的meanshift不是圆形窗口而是方形窗口,然后用的好像不是高斯函数,嗯记不得了。
然后这里不仅要计算窗口每个点到中心的距离,还要考虑每个点和中心点的颜色距离!!

比方说:A、B喊我吃饭,
A说吃火锅,B说吃烧烤,那我看A先喊我B后喊我,那我就决定吃火锅了

但是如果考虑B跟我交情好

那我还得考虑交情的情况下,我决定吃烧烤去了

但是吧,A又先喊的我,我也总得考虑吧,hhh那我就和A、B去吃大排档(烧烤火锅都有)这就是meanshift中往均值方向走

2.2.1 LUV色彩空间

什么要转换到LUV空间之后再做meanshift呢,因为人眼看到的颜色近似度和RGB近似度是不一样的

举个例子(0,0,0)白色、(10,0,0)红色、(10,10,10)灰色 RGB下
那人眼看肯定灰色和白色感觉相近吧,白色和红色肯定色差大。
!!!!!但是!!但是!!从rgb数据上看红色到白色的欧式距离短呀,

那这肯定不符合人眼规律喽。
所以————

我这里直接上图了
在这里插入图片描述

首先要把0~255的RGB数据归一化,然后用上面公式转到sRGB下,其中有一个gamma校正。

然后转到XYZ下。
在这里插入图片描述

然后在XYZ坐标下转换到LUV空间下
在这里插入图片描述
引图

这里有个D65/2°下的基准白95.047,100.000,108.883

void RGBtoLUV( Color & color, Color & color_LUV)
	{
		Color WhiteReference_XYZ = { 95.047,100.000,108.883 };
		Color color_XYZ;
		double r = color[0] / 255.0, g = color[1] / 255.0, b = color[2] / 255.0;
		r = (r > 0.04045 ? pow((r + 0.055) / 1.055, 2.4) : r / 12.92) * 100.0;
		g = (g > 0.04045 ? pow((g + 0.055) / 1.055, 2.4) : g / 12.92) * 100.0;
		b = (b > 0.04045 ? pow((b + 0.055) / 1.055, 2.4) : b / 12.92) * 100.0;

		// Observer. = 2°, Illuminant = D65
		color_XYZ[0] = r * 0.4124 + g * 0.3576 + b * 0.1805;
		color_XYZ[1] = r * 0.2126 + g * 0.7152 + b * 0.0722;
		color_XYZ[2] = r * 0.0193 + g * 0.1192 + b * 0.9505;

		const double Epsilon = 0.008856; // Intent is 216/24389
		const double Kappa = 903.3; // Intent is 24389/27
		double y = color_XYZ[1] / WhiteReference_XYZ[1];
		color_LUV[0] = (y > Epsilon ? 116.0 * pow(y, 1.0 / 3.0) - 16.0 : Kappa * y);

		double targetDenominator = color_XYZ[0] + color_XYZ[1] * 15.0 + color_XYZ[2] * 3.0;
		double referenceDenominator = WhiteReference_XYZ[0] + WhiteReference_XYZ[1] * 15.0 + WhiteReference_XYZ[2] * 3.0;
		// ReSharper disable CompareOfFloatsByEqualityOperator
		double xTarget = targetDenominator == 0 ? 0 : ((4.0 * color_XYZ[0] / targetDenominator) - (4.0 * WhiteReference_XYZ[0] / referenceDenominator));
		double yTarget = targetDenominator == 0 ? 0 : ((9.0 * color_XYZ[1] / targetDenominator) - (9.0 * WhiteReference_XYZ[1] / referenceDenominator));
		// ReSharper restore CompareOfFloatsByEqualityOperator

		color_LUV[1] = (13.0 * color_LUV[0] * xTarget);
		color_LUV[2] = (13.0 * color_LUV[0] * yTarget);
	}
2.2.2 meanshift平滑

1、这里用的是容器vector和自己定义的class去导入图片数据,

2、同时meanshift求值的时候要用双线性插值法去求移动后的点的像素值

	void meanshift_filter(const Image & src, Image & dst, const double sr = 6, const double tr = 8) {
		const unsigned long max_itr = 10;        //最大的迭代次数
		const double color_threshold = 0.05;
		assert(!src.empty());
		for (unsigned long i = 0; i < src.size(); i++)
		{
			assert(src[i].size() == src[0].size());
		}
		assert(sr > 0 && tr > 0);

		dst.clear();              //容器清空操作

		for (unsigned long i = 0; i < src.size(); i++)
		{
			Row dst_row;     
			for (unsigned long j = 0; j < src[i].size(); j++)
			{
				double cur_row = i;
				double cur_col = j;
				Color cur_color = src[i][j];
				for (unsigned long k = 0; k < max_itr; k++)
				{
					double ltr = (cur_row - sr - 1);// left top row.
					double ltc = (cur_col - sr - 1);// left top col.
					double rbr = (cur_row + sr + 1);// right bottom row.
					double rbc = (cur_col + sr + 1);// right bottom col.

					ltr = ltr < 0 ? 0 : ltr;
					ltc = ltc < 0 ? 0 : ltc;
					rbr = rbr > src.size() ? src.size() : rbr;
					rbc = rbc > src[i].size() ? src[i].size() : rbc; //确保在图片内

					double displacement_r = 0;
					double displacement_c = 0;
					double denominator = 0;
					for (unsigned long l = ltr; l < rbr; l++)
					{
						for (unsigned long m = ltc; m < rbc; m++)     //遍历窗口中的点
						{
							double temp = cur_row - l;
							double distant_sq = 0;
							distant_sq += (temp*temp);                 
							temp = cur_col - m;
							distant_sq += (temp*temp);                  //求取位置距离

							if (distant_sq <= sr*sr)                    //在窗口内(为圆形),那么就要求取像素色彩距
							{
								double color_distance = 0;
								for (int c = 0; c < channels; c++)
								{
									temp = src[l][m][c] - cur_color[c];   //窗口中的点减去中心点
									color_distance += (temp*temp);        //三个通道的色彩距   (0,0,0)(50,0,0)(50,50,50)RGB色彩空间
								}
								color_distance = sqrt(color_distance);     //求取像素色彩距离

								double weight = Guass(color_distance / tr);   //根据像素色彩距,做出权重
								displacement_r += (l - cur_row) * weight;   //进行替换
								displacement_c += (m - cur_col) * weight;
								denominator += weight;                      //用于求和归一
							}
						}
					}

					double dr = displacement_r / denominator;
					double dc = displacement_c / denominator;
					cur_row += dr;
					cur_col += dc;                                         //向量进行平移操作
					Color pre_color = cur_color;

					if (cur_row < 0 || cur_row >= src.size() - 1 || cur_col < 0 || cur_col >= src[i].size() - 1)
					{
						int cur_r = cur_row;
						int cur_c = cur_col;
						cur_r = cur_r < 0 ? 0 : cur_r;
						cur_c = cur_c < 0 ? 0 : cur_c;
						cur_r = cur_r >= src.size() ? src.size() - 1 : cur_r;
						cur_c = cur_c >= src[i].size() ? src[i].size() - 1 : cur_c;      
						cur_color[0] = src[cur_r][cur_c][0];
						cur_color[1] = src[cur_r][cur_c][1];
						cur_color[2] = src[cur_r][cur_c][2];
					}    //边界操作
					else
					{
						for (int c = 0; c < channels; c++)
						{
							int lt_r = cur_row, lt_c = cur_col;
							double temp1 = src[lt_r][lt_c][c] + (cur_col - lt_c)*(src[lt_r][lt_c + 1][c] - src[lt_r][lt_c][c]);
							double temp2 = src[lt_r + 1][lt_c][c] + (cur_col - lt_c)*(src[lt_r + 1][lt_c + 1][c] - src[lt_r + 1][lt_c][c]);
							cur_color[c] = temp1 + (cur_row - lt_r)*(temp2 - temp1);  //插入多少的像素值
						}
					}
					// 双线性插值

					/*循环迭代次数*/
					double color_distance = 0;
					for (int c = 0; c < channels; c++)
					{
						double temp = pre_color[c] - cur_color[c];
						color_distance += (temp*temp);
					}

					if ((abs(dr) < EQUAL_ERR && abs(dc) < EQUAL_ERR) || color_distance < color_threshold*color_threshold || k == max_itr - 1)
					{
						dst_row.push_back(cur_color);                      
						break;//用于退出迭代
					}
				}
			}
			dst.push_back(dst_row);
		}
	}

3 效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

电脑不行,跑个程序跑五六分钟hhh

然后就是meanshift的聚类以及分割还不太懂,邻接表,传递闭包还不太明白,后面有空再学习!

##4 补充一下自己学习做的笔记(5.13补记)
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值