OpenCV如何实现背投

 返回:OpenCV系列文章目录(持续更新中......)

上一篇:OpenCV直方图比较
下一篇 :OpenCV系列文章目录(持续更新中......)

目标

在本教程中,您将学习:

  • 什么是背投以及它为什么有用
  • 如何使用 OpenCV 函数 cv::calcBackProject 计算背投
  • 如何使用 OpenCV 函数 cv::mixChannels 混合图像的不同通道

理论

什么是背投?

  • 背投是一种记录给定图像的像素与直方图模型中像素分布的拟合程度的方法。
  • 为简化起见:对于“背投”,您可以计算要素的直方图模型,然后使用它在影像中查找此要素。
  • 应用示例:如果您有肉色直方图(例如,色相饱和度直方图),则可以使用它来查找图像中的肉色区域:

它是如何工作的?

  • 我们通过使用皮肤示例来解释这一点:
  • 假设您已经根据下图获得了皮肤直方图(色相饱和度)。此外,直方图将是我们的模型直方图(我们知道它代表了肤色的样本)。您应用了一些蒙版来仅捕获皮肤区域的直方图
  •  

  • 现在,让我们想象一下,你得到另一个手部图像(测试图像),如下所示:(及其各自的直方图):

     

  • 我们想要做的是使用我们的模型直方图(我们知道它代表皮肤色调)来检测测试图像中的皮肤区域。步骤如下
    1. 在我们的测试图像的每个像素即p(i,j)中,收集数据并找到该像素的相应箱位置即 h{i,j}, s{i,j} )。
    2. 在相应的 bin 中查找模型直方图  h{i,j}, s{i,j}  - 并读取 bin 值。
    3. 将此图柱值存储在新图像 (BackProjection) 中。此外,您可以考虑先对模型直方图进行归一化,以便您可以看到测试图像的输出。
    4. 应用上述步骤,我们得到以下测试图像的 BackProjection 图像:

  1. 在统计方面,存储在 BackProjection 中的值表示测试图像中的像素属于皮肤区域的概率,基于我们使用的模型直方图。例如,在我们的测试图像中,较亮的区域更有可能是皮肤区域(实际上确实如此),而较暗区域的可能性较小(请注意,这些“黑暗”区域属于带有一些阴影的表面,这反过来又会影响检测)。

C++代码
 

  • 这个程序是做什么的?
    • 加载图像
    • 将原始格式转换为 HSV 格式,并仅分离用于直方图的 Hue 通道(使用 OpenCV 函数 cv::mixChannels )
    • 让用户输入用于计算直方图的箱数。
    • 计算直方图(并在条柱更改时更新它)和同一图像的背投。
    • 在窗口中显示背投和直方图。

  • 可下载代码
    • 单击此处获取基本版本(在本教程中解释)。
    • 对于稍微花哨的东西(使用 H-S 直方图和 floodFill 为皮肤区域定义蒙版),您可以查看改进的演示
    • ...或者,您可以随时查看示例中的经典 CamshiftDemo。
  • 代码一览:
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
 
#include <iostream>
 
using namespace cv;
using namespace std;
 
Mat hue;
int bins = 25;
 
void Hist_and_Backproj(int, void* );
 
int main( int argc, char* argv[] )
{
 CommandLineParser parser( argc, argv, "{@input |Back_Projection_Theory0.jpg| input image}" );
 samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/histograms/back_projection/images");
 Mat src = imread(samples::findFile(parser.get<String>( "@input" )) );
 if( src.empty() )
 {
 cout << "Could not open or find the image!\n" << endl;
 cout << "Usage: " << argv[0] << " <Input image>" << endl;
 return -1;
 }
 
 Mat hsv;
 cvtColor( src, hsv, COLOR_BGR2HSV );
 
 hue.create(hsv.size(), hsv.depth());
 int ch[] = { 0, 0 };
 mixChannels( &hsv, 1, &hue, 1, ch, 1 );
 
 const char* window_image = "Source image";
 namedWindow( window_image );
 createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
 Hist_and_Backproj(0, 0);
 
 imshow( window_image, src );
 // Wait until user exits the program
 waitKey();
 
 return 0;
}
 
void Hist_and_Backproj(int, void* )
{
 int histSize = MAX( bins, 2 );
 float hue_range[] = { 0, 180 };
 const float* ranges[] = { hue_range };
 
 Mat hist;
 calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, ranges, true, false );
 normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
 
 Mat backproj;
 calcBackProject( &hue, 1, 0, hist, backproj, ranges, 1, true );
 
 imshow( "BackProj", backproj );
 
 int w = 400, h = 400;
 int bin_w = cvRound( (double) w / histSize );
 Mat histImg = Mat::zeros( h, w, CV_8UC3 );
 
 for (int i = 0; i < bins; i++)
 {
 rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ),
 Scalar( 0, 0, 255 ), FILLED );
 }
 
 imshow( "Histogram", histImg );
}

解释

读取输入图像:

CommandLineParser parser( argc, argv, "{@input |Back_Projection_Theory0.jpg| input image}" );
 samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/histograms/back_projection/images");
 Mat src = imread(samples::findFile(parser.get<String>( "@input" )) );
 if( src.empty() )
 {
 cout << "Could not open or find the image!\n" << endl;
 cout << "Usage: " << argv[0] << " <Input image>" << endl;
 return -1;
 }

将其转换为 HSV 格式:

 Mat hsv;
 cvtColor( src, hsv, COLOR_BGR2HSV );

在本教程中,我们将仅将 Hue 值用于我们的一维直方图(如果您想使用更标准的 H-S 直方图,请查看上面链接中的更高级代码,这会产生更好的结果):

 hue.create(hsv.size(), hsv.depth());
 int ch[] = { 0, 0 };
 mixChannels( &hsv, 1, &hue, 1, ch, 1 );
  • 如您所见,我们使用函数 cv::mixChannels 仅从 hsv 图像中获取通道 0(色相)。它获取以下参数:
    • &HSV:将从中复制通道的源数组
    • 1:源数组的数量
    • 色相(&C):复制通道的目标数组
    • 1:目标数组的数量
    • ch[] = {0,0}:指示如何复制通道的索引对数组。在本例中,将 &hsv 的 Hue(0) 通道复制到 &hue 的 0 通道(1 通道)
    • 1:索引对数
  • 为用户创建用于输入图格值的跟踪栏。对 Trackbar 的任何更改都意味着对 Hist_and_Backproj回调函数的调用。

 const char* window_image = "Source image";
 namedWindow( window_image );
 createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
 Hist_and_Backproj(0, 0);

显示图像并等待用户退出程序:

 imshow( window_image, src );
 // Wait until user exits the program
 waitKey();

Hist_and_Backproj功能:初始化 cv::calcHist 所需的参数。条柱数量来自 Trackbar:

 int histSize = MAX( bins, 2 );
 float hue_range[] = { 0, 180 };
 const float* ranges[] = { hue_range };

计算直方图并将其归一化为范围 [0,255]

 Mat hist;
 calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, ranges, true, false );
 normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );

 通过调用函数 cv::calcBackProject 获取同一图像的反向投影

 Mat backproj;
 calcBackProject( &hue, 1, 0, hist, backproj, ranges, 1, true );
  • 所有参数都是已知的(与用于计算直方图的参数相同),只是我们添加了 BackProj 矩阵,它将存储源图像 (&hue) 的反向投影
  • 显示 backproj:

 imshow( "BackProj", backproj );

绘制图像的一维色相直方图:

 int w = 400, h = 400;
 int bin_w = cvRound( (double) w / histSize );
 Mat histImg = Mat::zeros( h, w, CV_8UC3 );
 
 for (int i = 0; i < bins; i++)
 {
 rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ),
 Scalar( 0, 0, 255 ), FILLED );
 }
 
 imshow( "Histogram", histImg );

结果

以下是使用示例图像的输出(你猜怎么着?另一只手)。您可以使用 bin 值,您将观察它如何影响结果:

参考文献:

1、《Back Projection》-----Ana Huamán

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GraspNet-1Billion 论文阅读

文章目录 GraspNet-1Billion总体数据集评价指标网络pointnet&#xff1a;Approach Network:Operation Network&#xff1a;Tolerance Network 摘要相关工作基于深度学习的抓取预测算法抓取数据集点云深度学习 GraspNet-1Billion CVPR2020 上海交大 论文和数据集地址&#xff1…

【漏洞复现】艺创科技智能营销路由器后台命令执行漏洞

漏洞描述&#xff1a; 成都艺创科技有限公司是一家专注于新型网络设备研发、生产、销售和服务的企业&#xff0c;在大数据和云时代&#xff0c;致力于为企业提供能够提升业绩的新型网络设备。 智能营销路由器存在后台命令执行漏洞&#xff0c;攻击者可利用漏洞获取路由器控制…

Android 开发工具使用

c调试 在NDK调试的时候&#xff0c;如果找不到 符号的话&#xff0c;我们可以在调试配置中添加符号地址的全路径一直到根目录&#xff1a;&#xff0c;xxx/armeabi-v7a&#xff1a; You must point the symbol search paths at the obj/local/ directory. This is also not a …

1146. 快照数组

java版本 class SnapshotArray {int id 0;List<int[]>[] snapshots;public SnapshotArray(int length) {snapshots new List[length];for (int i 0; i < length; i) {snapshots[i] new ArrayList<int[]>();}}public void set(int index, int val) {snapsho…

运算符重载(1)

1.加号运算符重载&#xff0c;这里用编译器统一的名称operator代替函数名 #include<iostream> using namespace std; //1.成员函数的加号重载 //2.全局函数的加号重载 class Person { public:Person() {};//1.成员函数的加号重载//Person operator(Person& p)//{// P…

E4980A是德科技E4980A精密LCR表

181/2461/8938产品概述&#xff1a; Keysight E4980A 精密 LCR 表为各种元件测量提供了精度、速度和多功能性的最佳组合。E4980A 在低阻抗和高阻抗范围内提供快速测量速度和出色的性能&#xff0c;是元件和材料的一般研发和制造测试的终极工具。LAN、USB 和 GPIB PC 连接可提高…

Springboot实现国际化以及部署Linux不生效问题

1、在application.properties 添加以下配置&#xff1a; #国际化配置 spring.messages.basenamei18n/messages/messages spring.messages.fallback-to-system-localefalse 2、添加配置文件在 resources目录下 如下图所示&#xff1a; 这个国际化文件命名有个坑&#xff0c;必须…

校车车载4G视频智能监控系统方案

一、项目背景 随着社会的快速发展&#xff0c;校车安全问题日益受到人们的关注。为了提高校车运营的安全性&#xff0c;保障学生的生命安全&#xff0c;我们提出了一套校车车载4G视频智能监控系统方案。该系统能够实时监控校车内部和外部环境&#xff0c;及时发现并处理潜在的…

多线程模型浅谈

优质博文&#xff1a;IT-BLOG-CN 笔者近期在维护的项目中发现了一些比较随机的问题&#xff0c;时有时无的&#xff0c;排查之后发现是使用多线程导致的&#xff0c;恍然之下研究了下多线程的底层模型相关知识&#xff0c;现不大家简要分享下。 一个程序进程可包含多个线程&am…

统计建模——模型——python为例

统计建模涵盖了众多数学模型和分析方法&#xff0c;这些模型和方法被广泛应用于数据分析、预测、推断、分类、聚类等任务中。下面列举了一些常见的统计建模方法及其具体应用方式&#xff1a; 目录 1.线性回归模型&#xff1a; ----python实现线性回归模型 -------使用NumPy…

牛客网刷题 | BC66 牛牛的通勤

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 牛牛的通勤路上有两…

yolov8旋转目标检测输出的角度转化为适合机械爪抓取的角度

1. 机械爪抓取时旋转的角度定义 以X轴正方向&#xff08;右&#xff09;为零度方向&#xff0c;角度取值范围[-90&#xff0c;90)。 确认角度的方法&#xff1a; 逆时针旋转X轴&#xff0c;X轴碰到矩形框长边时旋转过的角度记为angleX&#xff1a; 1.如果angleX小于90&#xf…

udp/tcp错误总结

udp tcp——多进程 tcp——多线程 tcp——线程池 tcp——守护进程 &#x1f386;udp  ✨pthread_create 错误总结  ✨LockGuard错误总结  ✨服务端需要写成多线程  ✨客户端也需要写成多线程  ✨多线程调试工具 &#x1f386;tcp  ✨tcp独有调试工具——telnet  ✨Threa…

Python的历史演变与作用

目录 1.概述 2.起源 3.发展阶段 4.Python 3的诞生 5.现状与未来 6.Python的作用 6.1.Web开发 6.2.数据科学与人工智能 ​​​​​​​6.3.自动化与脚本编程 ​​​​​​​6.4.教育与学习 ​​​​​​​6.5.其他领域 7.结语 1.概述 Python&#xff0c;一门富有表…

DSP开发实战教程-国产DSP替代进口TI DSP的使用技巧

1.替换CCS安装路径下的Flash.out文件 找到各自CCS的安装路径&#xff1a; D:\ti\ccs1230\ccs\ccs_base\c2000\flashAlgorithms 复制进芯电子国产DSP官网提供的配置文件 下载链接&#xff1a;https://mp.csdn.net/mp_download/manage/download/UpDetailed 2.替换原有文件 3.…

Thinkphp--in-sqlinjection

一、漏洞原理 在 Builder 类的 parseData 方法中&#xff0c;由于程序没有对数据进行很好的过滤&#xff0c;将数据拼接进 SQL 语句&#xff0c;导致 SQL注入漏洞 的产生。 影响版本 5.0.13<ThinkPHP<5.0.15 5.1.0<ThinkPHP<5.1.5 在相应的文件夹位置打开终端…

记录如何用php做一个网站访问计数器的方法

简介 创建一个简单的网站访问计数器涉及到几个步骤&#xff0c;包括创建一个用于存储访问次数的文件或数据库表&#xff0c;以及编写PHP脚本来增加计数和显示当前的访问次数。 方法 以下是使用文件存储访问次数的基本步骤&#xff1a; 创建一个文本文件来存储计数&#xff1a…

.NET 检测地址/主机/域名是否正常

&#x1f331;PING 地址/主机名/域名 /// <summary>/// PING/// </summary>/// <param name"ip">ip</param>/// <returns></returns>public static bool PingIp(string ip){System.Net.NetworkInformation.Ping p new System.N…

Codeforces Round 941 (Div. 1) E. Connected Cubes(构造)

题目 思路来源 官方题解 题解 可以看下官方题解的7张图&#xff0c;还是比较清晰的&#xff0c;这里直接粘贴一下 来源&#xff1a;Codeforces Round #941 (Div. 1, Div. 2) Editorial - Codeforces &#xff08;1&#xff09;原来的图 &#xff08;2&#xff09;对于偶数行…

牛客NC376 变回文串的最少插入次数【困难 动态规划,回文 C++/Java/Go/PHP 高频】力扣同一道题1312

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/bae2652b4db04a438368238498e4c13e https://leetcode.cn/problems/minimum-insertion-steps-to-make-a-string-palindrome/description/ 思路 参考答案C class Solution {public:/*** 代码中的类名、方法名、参…