OpenCV后投影

2018-09-21 11:31 更新

目標(biāo)

在本教程中,您將學(xué)習(xí):

  • 什么是反投影和為什么它是有用的
  • 如何使用OpenCV函數(shù)cv :: calcBackProject來(lái)計(jì)算Back Projection
  • 如何通過(guò)使用OpenCV函數(shù)cv :: mixChannels來(lái)混合圖像的不同通道

理論

什么是后投影?

  • 后投影是一種記錄給定圖像的像素適合直方圖模型中像素分布的方式。
  • 為了使其更簡(jiǎn)單:對(duì)于Back Projection,您可以計(jì)算特征的直方圖模型,然后使用它來(lái)在圖像中查找此功能。
  • 應(yīng)用示例:如果您具有肉色直方圖(例如,色相飽和度直方圖),則可以使用它來(lái)查找圖像中的肉色區(qū)域:

它是如何工作的?

  • 我們通過(guò)使用皮膚示例來(lái)解釋這一點(diǎn):
  • 假設(shè)您已經(jīng)根據(jù)下面的圖像獲得了皮膚直方圖(色相飽和度)。除此之外的直方圖將是我們的模型直方圖(我們知道的是皮膚色調(diào)的樣本)。您應(yīng)用了一些掩模僅捕獲皮膚區(qū)域的直方圖:

OpenCV后投影

T0

OpenCV后投影

T1

  • 現(xiàn)在,我們假設(shè)你會(huì)得到另一只手圖像(Test Image),如下所示:(帶有各自的直方圖):

OpenCV后投影

OpenCV后投影

  • 我們想要做的是使用我們的模型直方圖(我們知道代表皮膚色調(diào))來(lái)檢測(cè)我們的測(cè)試圖像中的皮膚區(qū)域。以下是步驟
  1. 在我們的測(cè)試圖像的每個(gè)像素 (i.e. p(i,j) )中,收集數(shù)據(jù)并找到該像素的對(duì)應(yīng)位置(i.e. (hi,j,si,j) )。
  2. 在對(duì)應(yīng)的 bin - (hi,j,si,j) 中查找模型直方圖,并讀取bin值。
  3. 將此bin值存儲(chǔ)在新圖像中(BackProjection)。此外,您可以考慮先對(duì)模型直方圖進(jìn)行歸一化,因此可以為您顯示測(cè)試圖像的輸出。
  4. 應(yīng)用上述步驟,我們將為我們的測(cè)試圖像獲得以下BackProjection圖像:

OpenCV后投影

   5. 在統(tǒng)計(jì)方面,基于我們使用的模型直方圖,BackProjection中存儲(chǔ)的值表示測(cè)試圖像中的像素屬于皮膚區(qū)域的概率。例如在我們的測(cè)試圖像中,較亮的區(qū)域更可能是皮膚區(qū)域(實(shí)際上是這樣),而較暗的區(qū)域的概率較低(注意這些“黑暗”區(qū)域?qū)儆诰哂幸恍╆幱暗谋砻?,反過(guò)來(lái)影響檢測(cè))。

  • 這個(gè)程序是做什么的?
  1. 加載圖像
  2. 將原始轉(zhuǎn)換為HSV格式,并將僅用于直方圖的色相通道分開(kāi)(使用OpenCV函數(shù)cv :: mixChannels
  3. 讓用戶在計(jì)算直方圖時(shí)輸入要使用的分組數(shù)。
  4. 計(jì)算直方圖(如果紙箱更改則更新)和相同圖像的反投影。
  5. 在窗口中顯示反投影和直方圖。
  • 可下載的代碼:
  1. 點(diǎn)擊這里查看基本版本(在本教程中解釋?zhuān)?/li>
  2. 對(duì)于稍微優(yōu)點(diǎn)的東西(使用HS直方圖和floodFill來(lái)定義皮膚區(qū)域的面具),您可以檢查改進(jìn)的演示
  3. ...或者您可以隨時(shí)查看樣品中的經(jīng)典camshiftdemo
  • 代碼一覽:

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat src; Mat hsv; Mat hue;
int bins = 25;
void Hist_and_Backproj(int, void* );
int main( int, char** argv )
{
  src = imread( argv[1], IMREAD_COLOR );
  if( src.empty() )
    { cout<<"Usage: ./calcBackProject_Demo1 <path_to_image>"<<endl;
      return -1;
    }
  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, WINDOW_AUTOSIZE );
  createTrackbar("* Hue  bins: ", window_image, &bins, 180, Hist_and_Backproj );
  Hist_and_Backproj(0, 0);
  imshow( window_image, src );
  waitKey(0);
  return 0;
}
void Hist_and_Backproj(int, void* )
{
  MatND hist;
  int histSize = MAX( bins, 2 );
  float hue_range[] = { 0, 180 };
  const float* ranges = { hue_range };
  calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
  normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
  MatND backproj;
  calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
  imshow( "BackProj", backproj );
  int w = 400; int h = 400;
  int bin_w = cvRound( (double) w / histSize );
  Mat histImg = Mat::zeros( w, h, 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 ), -1 ); }
  imshow( "Histogram", histImg );
}

說(shuō)明

  • 聲明矩陣來(lái)存儲(chǔ)我們的圖像,并初始化我們的直方圖使用的數(shù)據(jù)塊數(shù)量:
Mat src; Mat hsv; Mat hue;
int bins = 25;
  • 讀取輸入圖像并將其轉(zhuǎn)換為HSV格式:
src = imread( argv[1], 1 );
cvtColor( src, hsv, COLOR_BGR2HSV );
  • 對(duì)于本教程,我們將僅使用Hue值作為我們的1-D直方圖(如果要使用更標(biāo)準(zhǔn)的H-S直方圖,可以獲得更好的結(jié)果,請(qǐng)查看以上鏈接中的fancier代碼):
hue.create(hsv.size(),hsv.depth());
int ch [] = {0,0};
mixChannels(&hsv,1,&hue,1,ch,1);

如你所見(jiàn),我們使用函數(shù)cv :: mixChannels從hsv圖像中只得到通道0(Hue)。它得到以下參數(shù):

  1. **&hsv:**要從中復(fù)制通道的源數(shù)組
  2. 1:源數(shù)組的數(shù)量
  3. **&hue:**復(fù)制的頻道的目標(biāo)數(shù)組
  4. 1:目標(biāo)數(shù)組的數(shù)量
  5. ch [] = {0,0}:指示通道如何復(fù)制的索引對(duì)數(shù)組。在這種情況下,&hsv的Hue(0)通道正被復(fù)制到0通道的&Hue(1通道),
  6. 1:索引對(duì)數(shù)
  • 創(chuàng)建一個(gè)跟蹤欄,供用戶輸入bin值。Trackbar上的任何更改意味著調(diào)用Hist_and_Backproj回調(diào)函數(shù)。
char* window_image = "Source image";
namedWindow( window_image, WINDOW_AUTOSIZE );
createTrackbar("* Hue  bins: ", window_image, &bins, 180, Hist_and_Backproj );
Hist_and_Backproj(0, 0);
  • 顯示圖像并等待用戶退出程序:
imshow( window_image, src );
waitKey(0);
return 0;
  • Hist_and_Backproj函數(shù):初始化cv :: calcHist所需的參數(shù)。箱子數(shù)量來(lái)自Trackbar:
void Hist_and_Backproj(int,void *)
{
  MatND hist
  int histSize = MAX(bin,2);
  float hue_range [] = {0,180};
  const  float * ranges = {hue_range};
  • 計(jì)算直方圖并將其歸一化到范圍[0,255]
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
MatND backproj
calcBackProject(&hue,1,0,hist,backproj,&ranges,1,true);

所有參數(shù)都是已知的(與用于計(jì)算直方圖相同),只有我們添加了backproj矩陣,這將保存源圖像(&hue)的反投影,

  • 顯示backproj:

imshow(“BackProj”,backproj);

  • 繪制圖像的1-D色相直方圖:

int w = 400; int h = 400;
int bin_w = cvRound( (double) w / histSize );
Mat histImg = Mat::zeros( w, h, 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 ), -1 ); }
imshow( "Histogram", histImg );

結(jié)果

這里是使用示例圖像的輸出(猜猜什么?另一只手)。您可以使用bin值,您將觀察它如何影響結(jié)果:

OpenCV后投影

R0

OpenCV后投影

R1

OpenCV后投影

R2

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)