OpenCV如何实现背投(58)

embedded/2024/9/24 6:02:11/

 返回: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 programwaitKey();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 programwaitKey();

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.ppmy.cn/embedded/33421.html

相关文章

kali linux系统安装及修改中文设置(VMware虚拟机安装)手把手教安装

1. 官网下载镜像 https://www.kali.org/get-kali 点击下载就好 2. VMware创建虚拟机 默认不动下一步 选择稍后安装然后下一步 选择Debian10x64位 编辑虚拟机名称和安装位置 CPU核数 分配虚拟机内存 选择网络 I/O控制器 磁盘类型 选择磁盘 磁盘容量 点击完成 3. 安装kaili 选择…

Flutter笔记:Widgets Easier组件库(11)- 使用提示吐丝

Flutter笔记 Widgets Easier组件库&#xff08;11&#xff09;使用提示吐丝 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this …

JavaEE >> Spring MVC(2)

接上文 本文介绍如何使用 Spring Boot/MVC 项目将程序执行业务逻辑之后的结果返回给用户&#xff0c;以及一些相关内容进行分析解释。 返回静态页面 要返回一个静态页面&#xff0c;首先需要在 resource 中的 static 目录下面创建一个静态页面&#xff0c;下面将创建一个静态…

配电室智能巡检机器人

近年来&#xff0c;生产过程高度自动化&#xff0c;各工矿企业关键场所需定期巡检维护。但目前巡检主要靠人工&#xff0c;既耗时费力效率又低&#xff0c;且受环境等因素影响&#xff0c;巡检难以全面规范&#xff0c;隐患或问题易被忽视。在此情况下&#xff0c;如何利用现有…

大模型下的Agent、AIGC的商业案例集合

算是一份摘录 1 AIGC 对影楼的影响 https://mp.weixin.qq.com/s/3j-6FAxZEEvXUZ1q6by2uw 2 出海Talkie &#xff1a;情感智能体 https://mp.weixin.qq.com/s/KHPmfuVvywxxcI2rqoOghA Talkie 为每条消息提供 3 个免费灵感&#xff0c;如果用户需要更多 AI 生成的灵感选项&…

Stacking算法:集成学习的终极武器

Stacking算法&#xff1a;集成学习的终极武器 在机器学习的竞技场中&#xff0c;集成学习方法以其卓越的性能而闻名。其中&#xff0c;Stacking&#xff08;堆叠泛化&#xff09;作为一种高级集成技术&#xff0c;更是被誉为“集成学习的终极武器”。本文将带你深入了解Stacki…

Redisson 分布式锁和同步器

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 redisson 是基于redis的扩展库,使得redis除了应用于缓存以外,还能做队列…

SpringBoot+vue实现token认证登录

目录 后端(Spring Boot) 1. 创建用户实体和数据库表 2. 用户注册和登录接口 3. JWT Token生成 4. JWT Token验证 前端(Vue.js) 1. 用户界面 2. 发送登录请求 3. 接收并存储Token