OpenCV实战——基于均值漂移算法检测图像内容

news2024/11/18 22:43:48

OpenCV实战——基于均值漂移算法检测图像内容

    • 0. 前言
    • 1. 均值漂移算法
    • 2. 检测图像内容
    • 3. 完整代码
    • 相关链接

0. 前言

直方图反投影的结果是一个概率图,表示在特定图像位置找到给定图像内容的概率。假设我们现在知道一个物体在图像中的大概位置;概率图可用于找到对象的确切位置。目标对象最有可能的位置是在给定窗口内概率最大化的像素。因此,如果我们从初始位置开始并迭代移动,应该可以找到确切的对象位置,这就是均值漂移算法的核心思想。均值偏移算法被广泛应用于视觉跟踪相关应用中。

1. 均值漂移算法

均值漂移算法是一种迭代算法,用于定位概率函数的局部最大值,通过在预定义窗口内查找数据点的质心或加权平均值进行定位。然后算法将窗口中心移动到质心位置并重复该过程直到窗口中心收敛到一个稳定点。OpenCV 可以两种迭代停止标准:最大迭代次数和窗口中心位移值(低于给定值则认为位置已收敛到稳定点),这两个条件存储在 cv::TermCriteria 实例中。cv::meanShift 函数返回已执行的迭代次数,显然,算法执行的结果质量取决于在给定初始位置上提供的概率图的质量。在本节中,我们使用颜色直方图来表示图像,但我们也可以使用其他特征直方图来表示对象,例如,边缘方向直方图等。

2. 检测图像内容

(1) 假设我们已经确定了一个感兴趣的对象——人物面部,如下图所示:

感兴趣区域

(2) 使用 HSV 颜色空间的色调通道来检测目标对象。这意味着我们需要将图像转换为 HSV 图像,然后提取色调通道并计算定义的 (Region of Interest, ROI) 的一维色调直方图:

// 读取参考图像
cv::Mat image = cv::imread("4.png");
// 初始位置
cv::Rect rect(340, 260, 35, 40);
cv::rectangle(image, rect, cv::Scalar(0, 0, 255));
// 面部 ROI
cv::Mat imageROI = image(rect);
// 获取面部 ROI 的 Hue 直方图
int minSat = 65;
ColorHistogram hc;
cv::Mat colorhist = hc.getHueHistogram(imageROI, minSat);

(3) 色调直方图是使用我们添加到 ColorHistogram 类中的 getHueHistogram 方法获得的:

// 计算 1D Hue 直方图
cv::Mat getHueHistogram(const cv::Mat& image, int minSaturation=0) {
    cv::Mat hist;
    // 转换为 HSV 色彩空间
    cv::Mat hsv;
    cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
    cv::Mat mask;
    if (minSaturation>0) {
        std::vector<cv::Mat> v;
        cv::split(hsv, v);
        cv::threshold(v[1], mask, minSaturation, 255, cv::THRESH_BINARY);
    }
    hranges[0] = 0.0;
    hranges[1] = 180.0;
    channels[0] = 0;
    // 计算直方图
    cv::calcHist(
        &hsv,
        1,
        channels,
        mask,
        hist,
        1,
        histSize,
        ranges
    );
    return hist;
}

(4) 然后将生成的直方图传递给我们的 ContentFinder 类实例:

ContentFinder finder;
finder.setHistogram(colorhist);

(5) 打开第二张图片,在新的图像中定位人脸的新位置。图像同样需要首先转换到 HSV 空间,然后反向投影第一张图像的直方图:

// 第二张图像
image = cv::imread("5.png");
// 转化到 HSV 色彩空间
cv::Mat hsv;
cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
// 获取反向投影
int ch[1] = {0};
finder.setThreshold(-1.0f);
cv::Mat result = finder.find(hsv, 0.0f, 180.0f, ch);

(6) 现在,从初始矩形区域(即第一张图像中人脸的位置)开始,OpenCVcv::meanShift 算法将人脸新位置处的 rect 对象:

// 初始化窗口位置
cv::rectangle(image, rect, cv::Scalar(0, 0, 255));
// 使用均值漂移检索对象
cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
                10,     // 最大迭代次数
                1);     // 或质心位置的变化小于1px
cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;

初始(红色)和新(绿色)人脸位置如下图所示:

内容检测

在以上代码中,使用 HSV 颜色空间的色调通道来检测我们的目标对象。使用色调通道是因为人脸具有独特的色彩,因此,使用像素的色调能够使人脸易于识别。基于以上考虑,首先将图像转换为 HSV 颜色空间,当使用 COLOR_BGR2HSV 标志时,色调分量是结果图像的第一个通道,该分量的取值范围为 0180 (使用 cv::cvtColor,转换后的图像与源图像的类型相同)。为了提取色调分量,使用 cv::split 函数将三通道 HSV 图像拆分为三个单通道图像,并被放入 std::vector 数组中,色调图像在其中的索引为 0
当使用颜色的色调分量时,考虑图像的饱和度(向量中的第 2 个元素)同样重要。事实上,当颜色的饱和度过低时,色调信息将变得不再可靠,这是因为对于低饱和度的颜色,BGR 分量几乎相等,这将很难确定确切颜色。因此,我们需要忽略低饱和度颜色的色调分量,也就是说,它们不计入直方图中,可以通过在 getHueHistogram 方法中使用 minSat 参数忽略饱和度低于此阈值的像素来做到这一点。

3. 完整代码

完整代码包含两个头文件和一个主函数文件,头文件 colorhistogram.hcontentFinder.h 可以参考反向投影直方图,主函数文件 (finder.cpp) 如下所示:

#include <iostream>
#include <vector>
using namespace std;

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/tracking.hpp>

#include "contentFinder.h"
#include "colorhistogram.h"

int main() {
    // 读取参考图像
    cv::Mat image = cv::imread("4.png");
    if (!image.data) return 0;
    // 初始位置
    cv::Rect rect(340, 260, 35, 40);
    cv::rectangle(image, rect, cv::Scalar(0, 0, 255));
    // 面部 ROI
    cv::Mat imageROI = image(rect);
    cv::namedWindow("Image 1");
    cv::imshow("Image 1", image);
    // 获取面部 ROI 的 Hue 直方图
    int minSat = 65;
    ColorHistogram hc;
    cv::Mat colorhist = hc.getHueHistogram(imageROI, minSat);
    ContentFinder finder;
    finder.setHistogram(colorhist);
    finder.setThreshold(0.2f);
    // 转化到 HSV 色彩空间
    cv::Mat hsv;
    cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
    // 分割图像通道
	vector<cv::Mat> v;
	cv::split(hsv,v);
	// 消除饱和度低的像素
	cv::threshold(v[1], v[1], minSat, 255, cv::THRESH_BINARY);
	cv::namedWindow("Saturation mask");
	cv::imshow("Saturation mask",v[1]);
    // 第二张图像
    image = cv::imread("5.png");
    cv::namedWindow("Image 2");
    cv::imshow("Image 2", image);
    cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
    // 获取反向投影
    int ch[1] = {0};
    finder.setThreshold(-1.0f);
    cv::Mat result = finder.find(hsv, 0.0f, 180.0f, ch);
    // 显示反向投影结果
    cv::namedWindow("Backprojection on second image");
	cv::imshow("Backprojection on second image",result);
    // 初始化窗口位置
    cv::rectangle(image, rect, cv::Scalar(0, 0, 255));
    // 使用均值漂移检索对象
    cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
                    10,     // 最大迭代次数
                    1);     // 或质心位置的变化小于1px
    cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;
    // 绘制结果窗口
    cv::rectangle(image, rect, cv::Scalar(0, 255, 0));
    cv::namedWindow("Image 2 result");
    cv::imshow("Image 2 result", image);
    cv::waitKey();
    return 0;
}

相关链接

OpenCV实战(1)——OpenCV与图像处理基础
OpenCV实战(2)——OpenCV核心数据结构
OpenCV实战(3)——图像感兴趣区域
OpenCV实战(4)——像素操作
OpenCV实战(5)——图像运算详解
OpenCV实战(6)——OpenCV策略设计模式
OpenCV实战(7)——OpenCV色彩空间转换
OpenCV实战(8)——直方图详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/195048.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在 Navicat Monitor for MySQL/MariaDB 中配置实例

Navicat Monitor for MySQL/MariaDB 是一个无代理的远程服务器监控工具&#xff0c;它包含的功能可以使监控数据库&#xff08;DB&#xff09;实例发挥最大效用和更轻松。此外&#xff0c;基于服务器的架构使其可以通过网页浏览器从任何地方访问&#xff0c;从而为你提供无障碍…

DaVinci 项目设置:图像缩放调整

项目设置/图像缩放调整Project Settings/Image Scaling图像缩放调整 Image Scaling选项卡可用于设置片段在输入、输出时的缩放及相应的插值算法。图像缩放调整Image Scaling主要用于选择缩放处理的插值方法&#xff0c;也可用于载入输入、输出缩放调整的预设。缩放过滤器Resize…

特别提醒|2023年考PMP需关注的5大问题

目前知道的是2023年考试时间为3月、5月、8月、11月&#xff0c;但是3月不给新报名&#xff0c;需要报名的话&#xff0c;就是报5月的考试了。当然有的伙伴会有一些小问题&#xff0c;这里给大家整理了一些基本的问题给大家回答一下&#xff0c;大家如果还有其他的问题可以评论提…

[Flink] 容错机制与状态一致性机制

文章目录1.状态一致性1.1 状态一致性分类2.一致性检查点 checkpoint3.端到端&#xff08;end-to-end&#xff09;状态一致性4. 端到端的精确一次&#xff08;exactly-once&#xff09;保证4.1 幂等写入4.2 事务写入5.FlinkKafka 端到端状态一致性的保证5.1 Exactly-once 两阶段…

常见智力题汇总(建议收藏)

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a; 智力题 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我最…

Web 前端开发技术 —— JavaScript

Web 前端开发技术 —— JavaScript 总结 JavaScript 内容&#xff01; 文章目录Web 前端开发技术 —— JavaScript一、js 的引用方式与执行顺序1、引用方式在标签中直接写 js 代码复用 js 代码通过 import 方式2、执行顺序3、html、css、js 三者之间的关系二、变量与运算符变量…

C++之函数重载

文章目录前言一、函数重载二、如何支持函数重载&#xff08;C支持函数重载的原理--名字修饰(name Mangling)&#xff09;三、参数有什么区别才能构成函数重载1.参数个数不同2.参数类型不同3.参数顺序不同四、返回值类型不同是否可以构成函数重载总结前言 我们知道在使用C语言进…

jmh的一些作用

目录说明说明 jmh可以用来java基准测试&#xff0c;性能测试用这个测比较标准&#xff0c;可以设置预热、迭代次数&#xff0c;对某块代码精准测试&#xff0c;耗时时间单位有毫秒、纳秒等。 就先说到这\color{#008B8B}{ 就先说到这}就先说到这 在下Apollo\color{#008B8B}{在下…

AcWing 323. 战略游戏(树形DP + 状态机DP)

AcWing 323. 战略游戏&#xff08;树形DP 状态机DP&#xff09;一、问题二、分析1、思路分析2、状态表示3、状态转移4、循环设计5、初末状态三、代码一、问题 二、分析 1、思路分析 这道题最后问的其实就是&#xff0c;在一棵树中&#xff0c;每个边至少选择一个端点的条件下…

【FLASH存储器系列十五】NAND Flash究竟能不能随机读写到某个字节的数据?

网上有很多文章写道&#xff0c;nand flash的读写操作是以page为单位&#xff0c;还有文章说些nand flash时必须按page0、page1、page2…的顺序写&#xff0c;必须先写完前面的page才能写后面的page。难道nandflash就不能随机读到某个字节吗&#xff1f;只能一次性读一页&#…

区区几行代码,就能全面实现 Python 自动探索性数据分析

探索性数据分析是数据科学模型开发和数据集研究的重要组成部分之一。在拿到一个新数据集时首先就需要花费大量时间进行EDA来研究数据集中内在的信息。自动化的EDA Python包可以用几行Python代码执行EDA。 在本文中整理了10个可以自动执行EDA并生成有关数据的见解的Python包&am…

C语言——二分查找与猜数字游戏

文章目录二分查找二分查找的思想二分查找的条件二分查找的实现过程代码举例猜数字游戏游戏说明猜数字游戏思想代码实现打印菜单打印主函数打印游戏函数整体代码演示二分查找 题目&#xff1a; 在一个有序数组中查找具体的某个数字n。 首先我们先定义一个110的数组 &#xff0c;…

immersive-translate(沉浸式双语网页翻译扩展),解决谷歌翻译无法使用问题

前言 谷歌停止了大陆的谷歌翻译服务&#xff0c;所以找到了immersive-translate 插件解决翻译问题。当然 最直接就是 换个浏览器比如 Edge\Firefox等等。 主要特性 智能识别网页主内容区&#xff0c;区别于同类插件翻译网页所有的区域&#xff0c;这可以极大增强译文的阅读…

【C++11】右值引用与移动构造、万能引用与完美转发

目录 一、右值引用 1.1 左值引用和右值引用 1.2 左值引用与右值引用比较 1.3 右值引用的使用场景和意义 二、移动构造 2.1 移动构造的实现 2.2 移动赋值 2.3 默认成员函数 2.4 default关键字 2.5 delete 关键字 2.6 STL中的移动构造 二、完美转发 2.1 模板中的万能…

利用剪枝降低bfs算法的时空复杂度(一道OJ题目)

作者&#xff1a;非妃是公主 专栏&#xff1a;《算法》《刷题笔记》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 《算法》专栏系列文章 算法设计与分析复习01&#xff1a;主方法求递归算法时间复杂度 算法设计与分析…

我写了一个脚本,实现了图片分类问题的全自动化训练

众所周知,图片分类问题属于计算机视觉中比较容易解决的问题之一 但 这几天被数据集的问题搞得焦头烂额, 照理说分类问题的数据集应该比较好制作 但 如果之前没有现成的数据集 也会变得比较麻烦 直到我偶然发现了一个HuggingFace的图片搜索API 无限次调用 而且不需要身份验证 真…

【手撕面试题】HTML+CSS(高频知识点一)

目录 面试官&#xff1a;给定一个元素&#xff0c;如何实现水平垂直居中&#xff1f; 面试官&#xff1a;padding与margin有什么不同&#xff1f; 面试官&#xff1a;vw和百分比有什么区别&#xff1f; 面试官&#xff1a;行内元素与块级元素有什么区别&#xff1f; 面试官…

MapReduce实现TopN

目录 1、先导知识 2、案例 2.1 需求 2.2 代码实现 FlowBean类 Mapper类 Reducer类 Driver类 3、总结 1、先导知识 TreeMap底层是根据红黑树的数据结构构建的&#xff0c;默认是根据key的自然排序来组织&#xff08;比如integer的大小&#xff0c;String的字典排序&…

一刷代码随想录——回溯算法

1.理论基础【1】本质回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。回溯是递归的副产品&#xff0c;只要有递归就会有回溯。因为回溯的本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案&#xff0c;如果想让回溯法高效一些&#xff0c;可以…

线性DP与真题

目录 一、前言 二、最长公共子序列&#xff08;lanqiaoOJ题号1189&#xff0c;类似于1054&#xff09; 三、最长递增子序列 1、蓝桥骑士&#xff08;lanqiaoOJ题号1188&#xff09; 四、编辑距离 1、字符串转换&#xff08;lanqiaoOJ题号1507&#xff09; 五、网络图上的…