OpenCV如何实现背投(58)

news2024/11/20 20:31:06

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

上一篇:OpenCV直方图比较(57)
下一篇:OpenCV如何模板匹配(59)

目标

在本教程中,您将学习:

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

cv::mixChannels 和 cv::calcBackProject 都是 OpenCV 库中常用的图像处理函数,主要应用于通道处理、直方图反向投影等操作。

cv::mixChannels 是一个通道处理函数,它可以在不同的图像通道之间进行拷贝、转换和简单操作等处理。该函数的主要思路是,定义一个通道映射表来指定源和目标图像之间的通道拷贝关系,并在映射表中指定每个通道的源图像和目标图像之间的通道位置和通道数。通过 mixChannels 函数,我们可以实现在不同通道间进行像素值的传递和处理等图像操作。

cv::calcBackProject 是一个直方图反向投影函数,它可以用于将某个模型区域的像素在目标图像中得到的直方图投影回去,以便进行目标检测和跟踪等操作。该函数主要思路是,首先使用 calcHist 函数计算模型区域的直方图,然后在目标图像中使用 calcBackProject 函数计算每个像素点在该直方图上的投影分布,并生成反向投影图像。利用反向投影图像,我们可以定位目标区域,并识别出图像中存在的目标区域。

因此,cv::mixChannels 和 cv::calcBackProject 函数通常会一起使用。通过 mixChannels 函数进行通道拷贝和转换处理,然后利用 calcBackProject 函数生成反向投影图像,可以实现更加精确的目标区域定位和识别。这些函数广泛用于计算机视觉和图像处理等领域中的特定应用和算法中。

理论

什么是背投?

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

它是如何工作的?

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

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

     

  • 我们想要做的是使用我们的模型直方图(我们知道它代表皮肤色调)来检测测试图像中的皮肤区域。步骤如下
    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.coloradmin.cn/o/1636372.html

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

相关文章

Mac好用又好看的终端iTerm2 + oh-my-zsh

Mac好用又好看的终端iTerm2 1. iTerm2的下载安装2. oh-my-zsh的安装2.1 官网安装方式2.2 国内镜像源安装方式 3. oh-my-zsh配置3.1 存放主题的路径3.2 存放插件的路径3.3 配置文件路径 1. iTerm2的下载安装 官网下载&#xff1a; iTerm2 2. oh-my-zsh的安装 oh-my-zsh是一…

设备能源数据采集新篇章

在当今这个信息化、智能化的时代&#xff0c;设备能源数据的采集已经成为企业高效运营、绿色发展的重要基石。而今天&#xff0c;我们要向大家介绍的就是一款颠覆传统、引领未来的设备能源数据采集神器——HiWoo Box网关&#xff01; 一、HiWoo Box网关&#xff1a;一站式解决…

C++11:shared_ptr循环引用问题

一、shared_ptr的弊端 struct Listnode {int _val;std::shared_ptr<Listnode> _prev;std::shared_ptr<Listnode> _next;Listnode(int val ):_val(val),_prev(nullptr),_next(nullptr){}~Listnode(){cout << "~Listnode()" << endl;} }; in…

探索未来,开启元宇宙之旅!

一、什么是元宇宙 元宇宙&#xff0c;这个词汇逐渐进入了公众的视野&#xff0c;引发了人们无尽的想象。 首先&#xff0c;元宇宙是什么&#xff1f;元宇宙&#xff0c;顾名思义&#xff0c;是一个虚拟现实的世界&#xff0c;一个融合了数字、物理和社交空间的全息图。它不仅…

【数据结构】位图与布隆过滤器

目录 前言 位图的概念 经典面试题目 位图的模拟实现 set() reset() test() 位图整体代码 位图的应用 位图的优缺点 布隆过滤器 布隆过滤器的概念 哈希函数的个数与布隆过滤器长度的关系 布隆过滤器的模拟实现 插入 查找 删除 布隆过滤器整体代码 前言 哈希本质…

nginx缓存清理

背景 昨天打开我的gpt镜像网站&#xff0c;意外发现静态图片资源全都无法获取了 CoCo-AI 一番排查下来&#xff0c;发现是引用的cdn链接失效了 且cdn源是属于七牛云的&#xff0c;且不再维护&#xff0c;于是果断切换到cloudflare export function getEmojiUrl(unified: str…

iBarcoder for Mac:一站式条形码生成软件

在数字化时代&#xff0c;条形码的应用越来越广泛。iBarcoder for Mac作为一款专业的条形码生成软件&#xff0c;为用户提供了一站式的解决方案。无论是零售、出版还是物流等行业&#xff0c;iBarcoder都能轻松应对&#xff0c;助力用户实现高效管理。 iBarcoder for Mac v3.14…

Android4.4真机移植过程笔记(一)

1、RK源码编译 获取内核源码&#xff1a; git clone git172.28.1.172:rk3188_kernel -b xtc_ok1000 内核编译环境&#xff1a; 从172.28.1.132编译服务器的/data1/ZouZhiPing目录下拷贝toolchain.tar.gz&#xff08;交叉编译工具链&#xff09;并解压到与rk3188_kernel同级目…

计算机英文论文常见错误写作习惯2

目录 第一部分 非常长的句子 在一个句子的主要概念的前面&#xff0c;首先说明目的、地点或原因 将表示时间的短语放在句首的倾向 将最重要的主语放在句首&#xff0c;以示强调 ‘In this paper’, ‘in this study’ 第一部分 非常长的句子 由于作者经常直接从中文翻译…

通过ESXi主机和专业工具导出或导入虚拟机

关于导出虚拟机的用户场景 导出ESXi虚拟机是VMware内置功能之一&#xff0c;可用于数据迁移或作为ESXi备份解决方案。通常情况下&#xff0c;您可以将ESXi中的虚拟机导出为OVF模板&#xff0c;该模板可捕获虚拟机或虚拟设备的状态并存储在一个自包含的包中&#xff0c;其中磁盘…

使 Elasticsearch 和 Lucene 成为最佳向量数据库:速度提高 8 倍,效率提高 32 倍

作者&#xff1a;来自 Elastic Mayya Sharipova, Benjamin Trent, Jim Ferenczi Elasticsearch 和 Lucene 成绩单&#xff1a;值得注意的速度和效率投资 我们 Elastic 的使命是将 Apache Lucene 打造成最佳的向量数据库&#xff0c;并继续提升 Elasticsearch 作为搜索和 RAG&a…

启发式搜索算法4 -遗传算法实战:吊死鬼游戏

相关文章: 启发式搜索算法1 – 最佳优先搜索算法 启发式搜索算法2 – A*算法 启发式搜索算法2 – 遗传算法 有一个小游戏叫吊死鬼游戏&#xff08;hangman&#xff09;&#xff0c;在学习英语的时候&#xff0c;大家有可能在课堂上玩过。老师给定一个英文单词&#xff0c;同学们…

Python人脸识别全面教程

目录 第一部分&#xff1a;人脸识别基础 1.1 人脸检测 1.2 人脸识别算法 1.3 深度学习在人脸识别中的应用 1.4 人脸识别库 第二部分&#xff1a;人脸识别高级技术 2.1 特征提取与人脸编码 人脸编码示例 2.2 人脸识别流程 人脸识别流程示例 2.3 多人脸识别与跟踪 多…

LabVIEW航空发动机主轴承试验器数据采集与监测

LabVIEW航空发动机主轴承试验器数据采集与监测 随着航空技术的迅速发展&#xff0c;对航空发动机性能的测试与监测提出了更高的要求。传统的数据采集与监测方法已难以满足当前高精度和高可靠性的需求&#xff0c;特别是在主轴承试验方面。基于LabVIEW的航空发动机主轴承试验器…

翻译《The Old New Thing》 - How do I cover the taskbar with a fullscreen window?

How do I cover the taskbar with a fullscreen window? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20050505-04/?p35703 Raymond Chen 2005年5月5日 如何用全屏窗口覆盖任务栏&#xff1f; 很多时候&#xff0c;人们总是想得太多。…

[1688]jsp工资投放管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 工资投放管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0…

【数据结构】为了节省空间,对于特殊矩阵我们可以这样做……

特殊矩阵的压缩存储 导读一、数组与矩阵1.1 数组1.2 数组与线性表1.3 数组的存储结构1.4 矩阵在数组中的存储1.4.1 行优先存储1.4.2 列优先存储 二、特殊矩阵及其压缩存储三、对称矩阵及其存储3.1 方阵与对称矩阵3.2 对称矩阵的存储3.3 压缩存储的手动实现3.3.1 行优先存储3.3.…

虹科Pico汽车示波器 | 免拆诊断案例 | 起动机免拆诊断故障 2 例

电磁开关、换向器烧蚀及炭刷磨损均会导致起动机偶尔不工作&#xff0c;使发动机偶尔无法起动。由于故障是偶发的&#xff0c;且没有故障代码&#xff0c;这往往会让维修人员无从下手&#xff0c;而用Pico示波器测量起动电流&#xff0c;就会让这些“亚健康状态”一目了然。 案例…

一个好用的MQTT客户端软件

软件功能如下&#xff0c;实现的协议版本是 3.1.1 仅实现了常用的 CONNECT , PUBLISH , SUBSCRIBE 及相应的应答报文。支持以 Hex 格式显示接收的原始报文&#xff08;方便初学者学习&#xff09;。支持所有字段的自定义配置。支持保存与加载配置文件。 软件界面如下所示&…

刷代码随想录有感(50):路径总和

题干&#xff1a; 代码; class Solution { public:bool traversal(TreeNode* node, int count){if(node NULL)return false;if(!node -> left && !node -> right && count 0)return true;if(!node -> left && !node -> right &&…