天天看點

opencv學習(三十九)之反向投影calcBackProject()

1.概述

反向投影是一種記錄給定圖像中的像素點如何适應直方圖模型像素分布的方式,簡單來講,反向投影就是首先計算某一特征的直方圖模型,然後使用模型去尋找圖像中存在的特征。反向投影在某一位置的值就是原圖對應位置像素值在原圖像中的總數目。

2.反向投影原理

原理采用OpenCV docs介紹!使用膚色直方圖來解釋反向投影的工作原理。假設我們已經獲得一個膚色直方圖(Hue-Staturation),旁邊的直方圖就是模型直方圖(代表手掌的膚色色調),可以通過掩碼操作來抓取手掌所在區域的直方圖:

opencv學習(三十九)之反向投影calcBackProject()
opencv學習(三十九)之反向投影calcBackProject()
opencv學習(三十九)之反向投影calcBackProject()
opencv學習(三十九)之反向投影calcBackProject()

我們需要做的就是使用模型直方圖(代表手掌的皮膚色調)來檢測測試圖像中的皮膚區域。以下是檢測步驟:

a.對測試圖像中的每個像素 p(i,j),擷取色調資料并找到該色調

opencv學習(三十九)之反向投影calcBackProject()

在直方圖中的bin位置

b.查詢模型直方圖中對應的bin-

opencv學習(三十九)之反向投影calcBackProject()

并讀取該bin的數值。

c.将此數值存儲在新的圖像中(BackProjection)。也可以先歸一化模型直方圖,這樣測試圖像的輸出就可以在螢幕上顯示了。

d.通過對測試中的圖像中的每個像素采用以上步驟,可以得到如下的BackProjection結果圖:

opencv學習(三十九)之反向投影calcBackProject()

e.使用統計學的語言,BackProjection中存儲的數值代表了測試圖像中該像素屬于皮膚區域的機率。以上圖為例,亮的區域是皮膚區域的可能性更大,而暗的區域則表示更低的可能性。

這個過程可以籠統的說與計算圖像直方圖相反,由圖像計算直方圖的過程比較容易了解,就是統計圖像中像素分布的機率,而反向投影正好反過來,是通過直方圖來形成圖像,其步驟有點類似于直方圖均衡化,隻不過直方圖均衡化是将圖像中的每個像素值由一個地方遷移到另外一個地方,而反向投影是直接去直方圖中的值,如某種像素值在直方圖中的值越大,在進行反向投影操作時其對應的像素值越大即月亮,反過來,如果某灰階值所占面積越小,其反向投影後像素值就會更小。

舉個簡單的例子來幫助了解這段話的意思。例如灰階圖像的像素值如下如下

grayImage

0 1 2 3

4 5 6 7

8 9 10 11

8 9 14 15

對圖像進行直方圖統計(bin指定的區間為[0,3),[4,7),[8,11),[12,16))如下所示:

Histogram=

4 4 6 2

也就是說在[0,3)這個區間的像素值有4個,其它含義相同

根據上述的直方圖進行反向投影,得到反向投影圖像像素值如下:

Back_Projection=

4 4 4 4

4 4 4 4

6 6 6 6

6 6 2 2

例如位置(0,0)上的像素值為0,對應的bin為[0,3),是以反向直方圖在該位置上的值這個bin的值4,而在位置(3,3)上的像素為15,其在直方圖中的統計為2,故其反向投影圖像中的像素為2

3.OpenCV提供API

calcBackProjection()函數共有三種形式,根據傳入參數的不同選擇不同的調用,為重載函數

void cv::calcBackProject    (   const Mat *     images,
        int     nimages,
        const int *     channels,
        InputArray      hist,
        OutputArray     backProject,
        const float **      ranges,
        double      scale = ,
        bool    uniform = true 
    )
           

參數解釋:

const Mat* images:輸入圖像,圖像深度必須位CV_8U,CV_16U或CV_32F中的一種,尺寸相同,每一幅圖像都可以有任意的通道數

int nimages:輸入圖像的數量

const int* channels:用于計算反向投影的通道清單,通道數必須與直方圖次元相比對,第一個數組的通道是從0到image[0].channels()-1,第二個數組通道從圖像image[0].channels()到image[0].channels()+image[1].channels()-1計數

InputArray hist:輸入的直方圖,直方圖的bin可以是密集(dense)或稀疏(sparse)

OutputArray backProject:目标反向投影輸出圖像,是一個單通道圖像,與原圖像有相同的尺寸和深度

const float ranges**:直方圖中每個次元bin的取值範圍

double scale=1:可選輸出反向投影的比例因子

bool uniform=true:直方圖是否均勻分布(uniform)的辨別符,有預設值true

另外兩種定義形式如下:

void cv::calcBackProject    (   const Mat *     images,
        int     nimages,
        const int *     channels,
        const SparseMat &   hist,
        OutputArray     backProject,
        const float **      ranges,
        double      scale = ,
        bool    uniform = true 
    )
           
void cv::calcBackProject    (   InputArrayOfArrays      images,
        const std::vector< int > &      channels,
        InputArray      hist,
        OutputArray     dst,
        const std::vector< float > &    ranges,
        double      scale 
    )   
           

4.示例代碼

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

//定義全局變量
Mat srcImage, hsvImage,hueImage;
const int hueBinMaxValue = ;
int hueBinValue=;

//聲明回調函數
void Hist_and_Backprojection(int, void*);

int main()
{
    srcImage=imread("Back_Projection_Theory2.jpg");

    //判斷圖像是否加載成功
    if(srcImage.empty())
    {
        cout << "圖像加載失敗" << endl;
        return -;
    }
    else
        cout << "圖像加載成功..." << endl << endl;

    //将圖像轉化為HSV圖像
    cvtColor(srcImage, hsvImage, CV_BGR2HSV);

    //隻使用圖像的H參數
    hueImage.create(hsvImage.size(), hsvImage.depth());
    int ch[]={,};
    mixChannels(&hsvImage, , &hueImage, , ch, );

    //軌迹條參數設定
    char trackBarName[];
    sprintf(trackBarName,"Hue bin:%d",hueBinMaxValue);
    namedWindow("SourceImage",WINDOW_AUTOSIZE);

    //建立軌迹條并調用回調函數
    createTrackbar(trackBarName, "SourceImage", &hueBinValue, hueBinMaxValue, Hist_and_Backprojection);
    Hist_and_Backprojection(hueBinValue, );

    imshow("SourceImage", srcImage);

    waitKey();

    return ;
}

void Hist_and_Backprojection(int, void*)
{
    MatND hist;
    int histsize=MAX(hueBinValue, );
    float hue_range[]={,};
    const float* ranges={hue_range};

    //計算圖像直方圖并歸一化處理
    calcHist(&hueImage, , , Mat(), hist, , &histsize, &ranges, true, false);
    normalize(hist, hist, , , NORM_MINMAX, -, Mat());

    //擷取反向投影
    MatND backProjection;
    calcBackProject(&hueImage, , , hist, backProjection, &ranges, , true);

    //輸出反向投影
    imshow("BackProjection", backProjection);

    //繪制圖像直方圖
    int w=;
    int h=;
    int bin_w = cvRound((double)w/histsize);
    Mat histImage = Mat::zeros(w, h, CV_8UC3);
    for(int i=; i < hueBinValue; i++)
    {
        rectangle(histImage, Point(i*bin_w, h), Point((i+)*bin_w, h-cvRound(hist.at<float>(i)*h/)), Scalar(,,), -);
    }
    imshow("HistImage", histImage);
}
           

程式運作結果:

opencv學習(三十九)之反向投影calcBackProject()