资讯专栏INFORMATION COLUMN

openCV中meanshift算法查找目标

vpants / 3250人阅读

摘要:因此,在方法中使用参数屏蔽掉饱和度低于此阈值的像素,不把它们统计进直方图中。

一、简介

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

二、实现过程

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

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::vectorv;			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 #include#include    //图像数据结构的核心#include //所有图形接口函数#include#include #include#includeusing 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::vectorv;			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的一些知识,做的笔记,也许会有许多不足,欢迎大家前来指正!有问题可以随时和我交流。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/123167.html

相关文章

  • opencv python Meanshift 和 Camshift

    摘要:算法的基本原理简单假设我们有一堆点,和一个小的圆形窗口,算法就是不断移动小圆形窗口,直到找到圆形区域内最大灰度密度处为止初始窗口以蓝色圆圈显示,名称为其原始中心标有蓝色矩形,名为但是,这个窗口当中所有点的点集构成的质心在蓝色圆形点处圆环的型 Meanshift and Camshift Meanshift Meanshift 算法的基本原理简单,假设我们有一堆点,和一个小的圆形窗口,...

    KitorinZero 评论0 收藏0
  • 基于卷积神经网络的奶牛个体身份识别

    摘要:将躯干图像灰度化后经插值运算和归一化变换为大小的矩阵,作为结构的卷积神经网络的输入进行个体识别。卷积神经网络构建为减少数据量并保证输入图像的细节信息,将奶牛躯干图像灰度化后通过插值计算变化为的图像,并除以归一化后作为输入数据。 最近看了一个有趣的人工智能应用,给大家分享一下~这是一个人工智能与农业的结合,在农业中我们经常需要给个体动物做标记,目的是对奶牛做身份识别,然后可以对动物做养殖和繁殖...

    MyFaith 评论0 收藏0
  • 女朋友嫌我拍的照片有雾,连夜用OpenCV写出❤️去雾算法❤️逃过一劫(收藏保命)

    ❤️欢迎订阅《从实战学python》专栏,用python实现爬虫、办公自动化、数据可视化、人工智能等各个方向的实战案例,有趣又有用!❤️ 更多精品专栏简介点这里 治愈生活的良方 就是保持对生活的热爱 前言 哈喽,大家好,我是一条。 每次和女朋友出去玩,拍照是必须的,天气好还行,天气要是不好,加上我这破手机,那拍的简直惨不忍睹,自己都不过去。 但是没什么能难倒程序员的,为了不挨骂,连夜写出去雾...

    DTeam 评论0 收藏0
  • OpenCV实战 | 八种目标跟踪算法

    摘要:目标追踪首先,我们会大致介绍八种建立在上的目标跟踪算法。词典包含了种的目标追踪器行。它将目标追踪器的命令行参数字符串映射到实际的追踪器函数上。其中行里的目的是根据追踪器命令行参数以及从得来的相关重要信息。 虽然我们熟知的的质心追踪器表现得很好,但它需要我们在输入的视频上的每一帧运行一个目标探测器。对大多数环境来说,在每帧上进行检测非常耗费计算力。所以,我们想应用一种一次性的目标检测方法,然后...

    shevy 评论0 收藏0
  • 边缘和轮廓检测——计算机视觉的应用

    摘要:它是图像处理图像模式识别和计算机视觉技术的基本步骤之一。轮廓检测可能是一种用于形状分析和对象检测和识别的有用技术。对整个图像进行边缘检测,而仅对图像内的对象进行轮廓检测。 计算机视觉的重点是从计算机中的视频和图像中提取有意义的信息。在本文中,我们将从初学者开始探索一个使用 OpenCV ...

    mo0n1andin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<