openCV中meanshift算法查找目标的实现

目录
  • 一、简介
  • 二、实现过程
    • 1、设定感兴趣的区域
    • 2、获取脸部直方图并做归一化
    • 3、反向投影,用meanshift查找目标
  • 三、其他实验结果
  • 四、部分原理补充
  • 五、完整代码

一、简介

图像直方图的反向投影是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。当我们已知图像中某个物体的大体位置时,可以通过概率分布图找到物体在另一张图像中的准确位置。我们可以设定一个初始位置,在其周围反复移动来提高局部匹配概率,从而找到物体的准确位置,这个实现过程叫做均值平移算法。

二、实现过程

因为人物的面部特征相对于其他位置更明显,本次实验主要应用于人物的面部识别。

1、设定感兴趣的区域

感兴趣区域的设定有两种方式,一种是已知图片人物脸部位置的像素坐标,通过设定矩形框来定位到人物脸部位置,另一种是使用opencv自带的selectROI函数,手动框选自己感兴趣的位置。

2、获取脸部直方图并做归一化

设置一个ColorHistogram类增加一个获取色调直方图的函数getHueHistogram。此函数包含将图像转换成HSV色彩空间,屏蔽低饱和度的像素(可能用到,也可能用不到),计算图像直方图。

cv::Mat getHueHistogram(const cv::Mat &image2, int minSaturation = 0)
	{
		cv::Mat hist;

		//转换成HSV色彩空间
		cv::Mat hsv;
		cv::cvtColor(image2, hsv, CV_BGR2HSV);
		//cv::imshow("hsv", hsv);

		//掩码(可能用的到也可能用不到)
		cv::Mat mask;
		if (minSaturation > 0) {
			std::vector<cv::Mat>v;
			cv::split(hsv, v);  //将3个通道分割进3幅图像

			cv::threshold(v[1], mask, minSaturation, 255, cv::THRESH_BINARY);//屏蔽低饱和度的像素
		}

		//准备一维色调直方图的参数
		hranges[0] = 0.0;
		hranges[1] = 180.0;  //范围是0~180
		channels[0] = 0;   //色调通道

		//计算直方图
		cv::calcHist(&hsv, 1,   //仅为一幅图像的直方图
			channels,             //使用的通道
			mask,                 //二值掩码
			hist,                 //作为结果的直方图
			1,                    //这是一维的直方图
			histSize,             //箱子数量
			ranges);              //像素值的范围
		return hist;
	}

然后,对获取的直方图做归一化。

void setHistogram(const cv::Mat& h) {
		histogram = h;
		cv::normalize(histogram, histogram, 1.0);
	}

3、反向投影,用meanshift查找目标

打开第二张图像,并将其转换成HSV色彩空间(代码中对输入的图像做了resize,避免有些图像尺寸过大,显示不全),然后对第一幅图像的直方图做反向投影。下面result是反向投影的结果,目前是框选了路飞的脸部作为感兴趣区域,如果框选路飞的帽子,反向投影会有不一样的效果,大家可以自己尝试。

//打开第二幅图像,并转换成HSV,对第一幅图像的直方图做反向投影
	image = cv::imread("lufei2.JPG");
	resize(image, image3, cv::Size(500, 700));
	cv::cvtColor(image3, hsv, CV_BGR2HSV); //转换成HSV色彩空间
	int ch[1] = { 0 };
	cv::Mat result = finder.find(hsv, 0.0f, 180.0f, ch);

使用openCV的meanshift算法可以将初始矩形区域修改成图像人物脸部的新位置。

cv::TermCriteria criteria(
		cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
		10, // 最多迭代10 次
		1); // 或者重心移动距离小于1 个像素
	cv::meanShift(result, rect, criteria);

至此,就找到了另一张图像中人物的脸部。

三、其他实验结果

除了进行从单人图像找另一个单人图像的实验,还做了从单人图像找多人合影的图像,下面是对NBA球星做的一个实验。

四、部分原理补充

本实验为了突出感兴趣目标特征,使用了HSV色彩空间的色调分量,使用CV_BGR2HSV标志转换图像后,得到的第一个通道就是色调分量。这是一个8位分量,值范围为0~180(如果使用cv::cvtColor,转换后的图像与原始图像的类型就会是相同的)。为了提取色调图像,cv::split 函数把三通道的 HSV 图像分割成三个单通道图像。这三幅图像存放在一个 std::vector 实例中,并且色调图像是向量的第一个入口(即索引为 0)。

在使用颜色的色调分量时,要把它的饱和度考虑在内(饱和度是向量的第二个入口),当颜色的饱和度很低时,它的色调信息就会变得不稳定且不可靠。这是因为低饱和度颜色的 B、G 和 R 分量几乎是相等的,这导致很难确定它所表示的准确颜色。因此,在 getHueHistogram 方法中使用 minSat 参数屏蔽掉饱和度低于此阈值的像素,不把它们统计进直方图中。

均值偏移算法是一个迭代过程,用于定位概率函数的局部最大值,方法是寻找预定义窗口内部数据点的重心或加权平均值。然后,把窗口移动到重心的位置,并重复该过程,直到窗口中心收敛到一个稳定的点。OpenCV 实现该算法时定义了两个停止条件:迭代次数达到最大值 (MAX_ITER);窗口中心的偏移值小于某个限值(EPS),可认为该位置收敛到一个稳定点。这两个条件存储在一个 cv::TermCriteria 实例中。

五、完整代码

#include <iostream>
#include<Windows.h>
#include<opencv2/core.hpp>    //图像数据结构的核心
#include<opencv2/highgui.hpp> //所有图形接口函数
#include<opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//获得色调直方图
class ColorHistogram
{
private:
	int histSize[3]; // 每个维度的大小
	float hranges[2]; // 值的范围(三个维度用同一个值)
	const float* ranges[3]; // 每个维度的范围
	int channels[3]; // 需要处理的通道

public:
	ColorHistogram() {
		// 准备用于彩色图像的默认参数
		// 每个维度的大小和范围是相等的
		histSize[0] = histSize[1] = histSize[2] = 256;
		hranges[0] = 0.0; // BGR 范围为0~256
		hranges[1] = 256.0;
		ranges[0] = hranges; // 这个类中
		ranges[1] = hranges; // 所有通道的范围都相等
		ranges[2] = hranges;
		channels[0] = 0; // 三个通道:B
		channels[1] = 1; // G
		channels[2] = 2; // R
	}

	//计算一维直方图,BGR的原图转换成HSV,忽略低饱和度的像素
	cv::Mat getHueHistogram(const cv::Mat &image2, int minSaturation = 0)
	{
		cv::Mat hist;

		//转换成HSV色彩空间
		cv::Mat hsv;
		cv::cvtColor(image2, hsv, CV_BGR2HSV);
		//cv::imshow("hsv", hsv);

		//掩码(可能用的到也可能用不到)
		cv::Mat mask;
		if (minSaturation > 0) {
			std::vector<cv::Mat>v;
			cv::split(hsv, v);  //将3个通道分割进3幅图像

			cv::threshold(v[1], mask, minSaturation, 255, cv::THRESH_BINARY);//屏蔽低饱和度的像素
		}

		//准备一维色调直方图的参数
		hranges[0] = 0.0;
		hranges[1] = 180.0;  //范围是0~180
		channels[0] = 0;   //色调通道

		//计算直方图
		cv::calcHist(&hsv, 1,   //仅为一幅图像的直方图
			channels,             //使用的通道
			mask,                 //二值掩码
			hist,                 //作为结果的直方图
			1,                    //这是一维的直方图
			histSize,             //箱子数量
			ranges);              //像素值的范围
		return hist;
	}

};

class ContentFinder {
private:
	// 直方图参数
	float hranges[2];
	const float* ranges[3];
	int channels[3];
	float threshold; // 判断阈值
	cv::Mat histogram; // 输入直方图
public:
	ContentFinder() : threshold(0.1f) {
		// 本类中所有通道的范围相同
		ranges[0] = hranges;
		ranges[1] = hranges;
		ranges[2] = hranges;
	}
	// 对直方图做归一化
	void setHistogram(const cv::Mat& h) {
		histogram = h;
		cv::normalize(histogram, histogram, 1.0);
	}

	// 查找属于直方图的像素
	cv::Mat find(const cv::Mat& image, float minValue, float maxValue,
		int *channels) {
		cv::Mat result;
		hranges[0] = minValue;
		hranges[1] = maxValue;
		// 直方图的维度数与通道列表一致
		for (int i = 0; i < histogram.dims; i++)
			this->channels[i] = channels[i];
		cv::calcBackProject(&image, 1, // 只使用一幅图像
			channels, // 通道
			histogram, // 直方图
			result, // 反向投影的图像
			ranges, // 每个维度的值范围
			255.0 // 选用的换算系数
			// 把概率值从1 映射到255
		);
		cv::imshow("result", result);
		return result;
	}
};

int main()
{
	/************均值检测meanshift***********/
	cv::Mat image = cv::imread("ZMS1.jpg");
	cv::Mat image2;
	cv::Mat image3;
	cv::Mat hsv;
	resize(image, image2, cv::Size(500, 700));

	cv::Rect rect;
	rect = cv::selectROI("image", image2, false, false);
	cv::Mat imageROI = image2(rect).clone();//手动框选

	/*cv::Rect rect(227, 108, 108, 104);
	cv::Mat imageROI = image2(rect);*///手动设置矩形框选范围

	cv::rectangle(image2, rect, cv::Scalar(255, 0, 0), 1, cv::LINE_8, 0);

	cv::imshow("image2", image2);
	//得到人脸直方图
	int minsat = 65;  //最小饱和度
	ColorHistogram hc;
	cv::Mat colorhist = hc.getHueHistogram(imageROI, minsat);

	//把直方图传给ContentFinder类
	ContentFinder finder;
	finder.setHistogram(colorhist);//对直方图做归一化

	//打开第二幅图像,并转换成HSV,对第一幅图像的直方图做反向投影
	image = cv::imread("ZMS2.JPG");
	resize(image, image3, cv::Size(500, 700));
	cv::cvtColor(image3, hsv, CV_BGR2HSV); //转换成HSV色彩空间
	int ch[1] = { 0 };
	cv::Mat result = finder.find(hsv, 0.0f, 180.0f, ch);

	cv::TermCriteria criteria(
		cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
		10, // 最多迭代10 次
		1); // 或者重心移动距离小于1 个像素
	cv::meanShift(result, rect, criteria);
	cv::rectangle(image3, rect, cv::Scalar(0, 255, 0), 1, cv::LINE_8, 0);
	cv::imshow("image3", image3);
	waitKey(0);
}

到此这篇关于openCV中meanshift算法查找目标的实现的文章就介绍到这了,更多相关openCV meanshift查找目标内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python中OpenCV实现查找轮廓的实例

    本文将结合实例代码,介绍 OpenCV 如何查找轮廓.获取边界框. 代码: contours.py OpenCV 提供了 findContours 函数查找轮廓,需要以二值化图像作为输入.并指定些选项调用即可. 我们以下图作为示例: 二值化图像 代码工程 data/ 提供了小狗和红球的二值化掩膜图像: 其使用预训练好的实例分割模型来生成的,脚本可见 detectron2_seg_threshold.py.模型检出结果,如下: 模型用的 Mask R-CNN 已有预测边框.但其他模型会有只出预测掩

  • opencv 查找连通区域 最大面积实例

    今天在弄一个查找连通的最大面积的问题. 要把图像弄成黑底,白字,这样才可以正确找到. 然后调用下边的方法: RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界 #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> using namespace cv; using namespace std; int main( int a

  • 利用OpenCV和Python实现查找图片差异

    使用OpenCV和Python查找图片差异 flyfish 方法1 均方误差的算法(Mean Squared Error , MSE) 下面的一些表达与<TensorFlow - 协方差矩阵>式子表达式一样的 拟合 误差平方和( sum of squared errors) residual sum of squares (RSS), also known as the sum of squared residuals (SSR) or the sum of squared errors of

  • openCV中meanshift算法查找目标的实现

    目录 一.简介 二.实现过程 1.设定感兴趣的区域 2.获取脸部直方图并做归一化 3.反向投影,用meanshift查找目标 三.其他实验结果 四.部分原理补充 五.完整代码 一.简介 图像直方图的反向投影是一个概率分布图,表示一个指定图像片段出现在特定位置的概率.当我们已知图像中某个物体的大体位置时,可以通过概率分布图找到物体在另一张图像中的准确位置.我们可以设定一个初始位置,在其周围反复移动来提高局部匹配概率,从而找到物体的准确位置,这个实现过程叫做均值平移算法. 二.实现过程 因为人物的面

  • OpenCV机器学习MeanShift算法笔记分享

    MeanShift算法 Mean shift 是一种机器学习算法,并不仅仅局限于图像上的应用.关于 Mean shift 算法介绍的书和文章很多,这里就不多介绍了.简单的说,Meanshift 算法是一种迭代算法,需要给一个初始的区域,然后这个算法会反复的调整这个区域,使得这个区域最吻合我们期望的特征. OpenCV 中有两处用到了 Mean Shift .分别是: pyrMeanShiftFiltering meanShift 这里只介绍第二个函数的使用方法. 我们的原始图像还是上次那个向日葵

  • OpenCV中Grabcut算法的具体使用

    目录 Grabcut 算法的基本步骤: Grabcut的相关API: Grabcut 算法的代码示例: Grabcut 算法主要运用于计算机视觉中的前背景分割,立体视觉和抠图等.该算法利用了图像中的纹理(颜色)信息和边界(反差)信息,只要少量的用户交互操作即可得到比较好的分割结果. 1. Grabcut 的目标和背景的模型是RGB三通道的混合高斯模型GMM; 2. Grab Cut为一个不断进行分割估计和模型参数学习的交互迭代过程: 3. Grab Cut只需要提供背景区域的像素集就可以了.也就

  • Opencv基于CamShift算法实现目标跟踪

    CamShift算法全称是"Continuously Adaptive Mean-Shift"(连续的自适应MeanShift算法),是对MeanShift算法的改进算法,可以在跟踪的过程中随着目标大小的变化实时调整搜索窗口大小,对于视频序列中的每一帧还是采用MeanShift来寻找最优迭代结果,至于如何实现自动调整窗口大小的,可以查到的论述较少,我的理解是通过对MeanShift算法中零阶矩的判断实现的. 在MeanShift算法中寻找搜索窗口的质心用到窗口的零阶矩M00和一阶矩M1

  • Opencv中cv2.floodFill算法的使用

    目录 一. 泛洪算法--floodFill函数原型 二.简单应用 三.应用,结合minareaRect 一. 泛洪算法--floodFill函数原型 cv2.floodFill(img,mask,seed,newvalue(BGR),(loDiff1,loDiff2,loDiff3),(upDiff1,upDiff2,upDiff3),flag) img:为待使用泛洪算法的图像 mask:为掩码层,使用掩码可以规定是在哪个区域使用该算法,如果是对于完整图像都要使用,则掩码层大小为原图行数+2,列

  • python opencv之SIFT算法示例

    本文介绍了python opencv之SIFT算法示例,分享给大家,具体如下: 目标: 学习SIFT算法的概念 学习在图像中查找SIFT关键的和描述符 原理: (原理部分自己找了不少文章,内容中有不少自己理解和整理的东西,为了方便快速理解内容和能够快速理解原理,本文尽量不使用数学公式,仅仅使用文字来描述.本文中有很多引用别人文章的内容,仅供个人记录使用,若有错误,请指正出来,万分感谢) 之前的harris算法和Shi-Tomasi 算法,由于算法原理所致,具有旋转不变性,在目标图片发生旋转时依然

  • python opencv之SURF算法示例

    本文介绍了python opencv之SURF算法示例,分享给大家,具体如下: 目标: SURF算法基础 opencv总SURF算法的使用 原理: 上节课使用了SIFT算法,当时这种算法效率不高,需要更快速的算法.在06年有人提出了SURF算法"加速稳定特征",从名字上来看,他是SIFT算法的加速版本. (原文) 在SIFT算法当中使用高斯差分方程(Difference of Gaussian)对高斯拉普拉斯方程( Laplacian of Gaussian)进行近似.然而,SURF使

  • python opencv之分水岭算法示例

    本文介绍了python opencv之分水岭算法示例,分享给大家,具体如下: 目标 使用分水岭算法对基于标记的图像进行分割 使用函数cv2.watershed() 原理: 灰度图像可以被看成拓扑平面,灰度值高的区域可以看出山峰,灰度值低的区域可以看成是山谷.向每一个山谷当中灌不同颜色的水.水位升高,不同山谷的水会汇合,为防止不同山谷的水汇合,小在汇合处建立起堤坝.然后继续灌水,然后再建立堤坝,直到山峰都掩模.构建好的堤坝就是图像的分割. 此方法通常会得到过渡分割的结果,因为图像中的噪声以及其他因

  • 浅谈opencv自动光学检测、目标分割和检测(连通区域和findContours)

    步骤如下: 1.图片灰化: 2.中值滤波 去噪 3.求图片的光影(自动光学检测) 4.除法去光影 5.阈值操作 6.实现了三种目标检测方法 主要分两种连通区域和findContours 过程遇到了错误主要是图片忘了灰化处理,随机颜色的问题.下面代码都已经进行了解决 这是findContours的效果 下面是连通区域的结果 #include <opencv2\core\utility.hpp> #include <opencv2\imgproc.hpp> #include <o

随机推荐