MeanShift和Camshift演算法實現 Opencv

來源:互聯網
上載者:User

 1--Back Projection


CamShift演算法,即"Continuously Apative Mean-Shift"演算法,是一種動作追蹤演算法。它主要通過視頻映像中運動物體的顏色資訊來達到跟蹤的目的。我把這個演算法分解成三個部分,便於理解:
1) Back Projection計算
2) Mean Shift演算法
3) CamShift演算法
首先討論Back Projection,在隨後繼續討論後面兩個演算法。

Back Projection
計算Back Projection的步驟是這樣的:


1. 計算被跟蹤目標的色彩長條圖。在各種色彩空間中,只有HSI空間(或與HSI類似的色彩空間)中的H分量可以表示顏色資訊。所以在具體的計算過程中,首先將其他的色彩空間的值轉化到HSI空間,然後會其中的H分量做1D長條圖計算。
2. 根據獲得的色彩長條圖將原始映像轉化成色彩機率分布圖像,這個過程就被稱作"Back Projection"。
在OpenCV中的長條圖函數中,包含Back Projection的函數,函數原型是:
void cvCalcBackProject(IplImage** img, CvArr** backproject, const CvHistogram* hist);


傳遞給這個函數的參數有三個:
1. IplImage** img:存放原始映像,輸入。
2. CvArr** backproject:存放Back Projection結果,輸出。
3. CvHistogram* hist:存放長條圖,輸入

下面就給出計算Back Projection的OpenCV代碼


1.準備一張只包含被跟蹤目標的圖片,將色彩空間轉化到HSI空間,獲得其中的H分量:
IplImage* target=cvLoadImage("target.bmp",-1); //裝載圖片
IplImage* target_hsv=cvCreateImage( cvGetSize(target), IPL_DEPTH_8U, 3 );
IplImage* target_hue=cvCreateImage( cvGetSize(target), IPL_DEPTH_8U, 3 );
cvCvtColor(target,target_hsv,CV_BGR2HSV); //轉化到HSV空間
cvSplit( target_hsv, target_hue, NULL, NULL, NULL ); //獲得H分量


2.計算H分量的長條圖,即1D長條圖:


IplImage* h_plane=cvCreateImage( cvGetSize(target_hsv),IPL_DEPTH_8U,1 );
int hist_size[]={255}; //將H分量的值量化到[0,255]
float* ranges[]={ {0,360} }; //H分量的取值範圍是[0,360)
CvHistogram* hist=cvCreateHist(1, hist_size, ranges, 1);
cvCalcHist(&target_hue, hist, 0, NULL);
在這裡需要考慮H分量的取值範圍的問題,H分量的取值範圍是[0,360),這個取值範圍的值不能用一個byte來表示,為了能用一個byte表示,需要將H值做適當的量化處理,在這裡我們將H分量的範圍量化到[0,255].


4.計算Back Projection:


IplImage* rawImage;
//----------------------------------------------
//get from video frame,unsigned byte,one channel
//----------------------------------------------
IplImage* result=cvCreateImage(cvGetSize(rawImage),IPL_DEPTH_8U,1);
cvCalcBackProject(&rawImage,result,hist);
5.結果:result即為我們需要的.

2--Mean Shift演算法
這裡來到了CamShift演算法,OpenCV實現的第二部分,這一次重點討論Mean Shift演算法。
在討論Mean Shift演算法之前,首先討論在2D機率分布圖像中,如何計算某個地區的重心(Mass Center)的問題,重心可以通過以下公式來計算:
1.計算地區內0階矩
for(int i=0;i< height;i++)
for(int j=0;j< width;j++)
M00+=I(i,j)
2.地區內1階矩:
for(int i=0;i< height;i++)
for(int j=0;j< width;j++)
{
M10+=i*I(i,j);
M01+=j*I(i,j);
}


3.則Mass Center為:
Xc=M10/M00; Yc=M01/M00


接下來,討論Mean Shift演算法的具體步驟,Mean Shift演算法可以分為以下4步:
1.選擇窗的大小和初始位置.
2.計算此時視窗內的Mass Center.
3.調整視窗的中心到Mass Center.
4.重複2和3,直到視窗中心"會聚",即每次視窗移動的距離小於一定的閾值。

在OpenCV中,提供Mean Shift演算法的函數,函數的原型是:
int cvMeanShift(IplImage* imgprob,CvRect windowIn,
CvTermCriteria criteria,CvConnectedComp* out);

需要的參數為:
1.IplImage* imgprob:2D機率分布圖像,傳入;
2.CvRect windowIn:初始的視窗,傳入;
3.CvTermCriteria criteria:停止迭代的標準,傳入;
4.CvConnectedComp* out:查詢結果,傳出。
(註:構造CvTermCriteria變數需要三個參數,一個是類型,另一個是迭代的最大次數,最後一個表示特定的閾值。例如可以這樣構造criteria:criteria=cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,10,0.1)。)

返回的參數:
1.int:迭代的次數。

3--CamShift演算法

1.原理
在瞭解了MeanShift演算法以後,我們將MeanShift演算法擴充到連續映像序列(一般都是指視頻映像序列),這樣就形成了CamShift演算法。CamShift演算法的全稱是"Continuously Apaptive Mean-SHIFT",它的基本思想是視頻映像的所有幀作MeanShift運算,並將上一幀的結果(即Search Window的中心和大小)作為下一幀MeanShift演算法的Search Window的初始值,如此迭代下去,就可以實現對目標的跟蹤。整個演算法的具體步驟分5步:
Step 1:將整個映像設為搜尋地區。
Step 2:初始話Search Window的大小和位置。
Step 3:計算Search Window內的彩色機率分布,此地區的大小比Search Window要稍微大一點。
Step 4:運行MeanShift。獲得Search Window新的位置和大小。
Step 5:在下一幀視頻映像中,用Step 3獲得的值初始化Search Window的位置和大小。跳轉到Step 3繼續運行。

2.實現
在OpenCV中,有實現CamShift演算法的函數,此函數的原型是:
cvCamShift(IplImage* imgprob, CvRect windowIn,
CvTermCriteria criteria,
CvConnectedComp* out, CvBox2D* box=0);
其中:
imgprob:色彩機率分布圖像。
windowIn:Search Window的初始值。
Criteria:用來判斷搜尋是否停止的一個標準。
out:儲存運算結果,包括新的Search Window的位置和面積。
box:包含被跟蹤物體的最小矩形。

說明:
1.在OpenCV 4.0 beta的目錄中,有CamShift的例子。遺憾的是這個例子目標的跟蹤是半自動的,即需要人手工選定一個目標。我正在努力嘗試全自動的目標跟蹤,希望可以和大家能在這方面與大家交流。

5.
運動目標跟蹤與檢測的原始碼(CAMSHIFT 演算法)
From http://blog.csdn.net/hunnish/archive/2004/09/07/97049.aspx

採用 CAMSHIFT 演算法快速跟蹤和檢測運動目標的 C/C++ 原始碼,OPENCV BETA 4.0 版本在其 SAMPLE 中給出了這個例子。演算法的簡單描述如下(英文):

This application demonstrates a fast, simple color tracking algorithm that can be used to track faces, hands . The CAMSHIFT algorithm is a modification of the Meanshift algorithm which is a robust statistical method of finding the mode (top) of a probability
distribution. Both CAMSHIFT and Meanshift algorithms exist in the library. While it is a very fast and simple method of tracking, because CAMSHIFT tracks the center and size of the probability distribution of an object, it is only as good as the probability
distribution that you produce for the object. Typically the probability distribution is derived from color via a histogram, although it could be produced from correlation, recognition scores or bolstered by frame differencing or motion detection schemes, or
joint probabilities of different colors/motions etc.

In this application, we use only the most simplistic approach: A 1-D Hue histogram is sampled from the object in an HSV color space version of the image. To produce the probability image to track, histogram "back projection" (we replace image pixels by their
histogram hue value) is used.

演算法的詳細情況,請看論文:

http://www.assuredigit.com/incoming/camshift.pdf

關於OPENCV B4.0 庫的使用方法以及相關問題,請查閱下面的相關文章:

http://forum.assuredigit.com/display_topic_threads.asp?ForumID=11&TopicID=3471

運行檔案下載:

http://www.assuredigit.com/product_tech/Demo_Download_files/camshiftdemo.exe

該運行檔案在VC6.0環境下編譯通過,是一個 stand-alone 運行程式,不需要OPENCV的DLL庫支援。在運行之前,請先串連好USB介面的網路攝影機。然後可以用滑鼠選定欲跟蹤目標。

=====
#include "cv.h"
#include "highgui.h"
#include
#include
#endif

IplImage *image = 0, *hsv = 0, *hue = 0, *mask = 0, *backproject = 0, *histimg = 0;
CvHistogram *hist = 0;

int backproject_mode = 0;
int select_object = 0;
int track_object = 0;
int show_hist = 1;
CvPoint origin;
CvRect selection;
CvRect track_window;
CvBox2D track_box; // tracking 返回的地區 box,帶角度
CvConnectedComp track_comp;
int hdims = 48; // 劃分HIST的個數,越高越精確
float hranges_arr[] = {0,180};
float* hranges = hranges_arr;
int vmin = 10, vmax = 256, smin = 30;

void on_mouse( int event, int x, int y, int flags )
{
if( !image )
return;

if( image->origin )
y = image->height - y;

if( select_object )
{
selection.x = MIN(x,origin.x);
selection.y = MIN(y,origin.y);
selection.width = selection.x + CV_IABS(x - origin.x);
selection.height = selection.y + CV_IABS(y - origin.y);

selection.x = MAX( selection.x, 0 );
selection.y = MAX( selection.y, 0 );
selection.width = MIN( selection.width, image->width );
selection.height = MIN( selection.height, image->height );
selection.width -= selection.x;
selection.height -= selection.y;

}

switch( event )
{
case CV_EVENT_LBUTTONDOWN:
origin = cvPoint(x,y);
selection = cvRect(x,y,0,0);
select_object = 1;
break;
case CV_EVENT_LBUTTONUP:
select_object = 0;
if( selection.width > 0 && selection.height > 0 )
track_object = -1;
#ifdef _DEBUG
printf("/n # 滑鼠的選擇地區:");
printf("/n X = %d, Y = %d, Width = %d, Height = %d",
selection.x, selection.y, selection.width, selection.height);
#endif
break;
}
}

CvScalar hsv2rgb( float hue )
{
int rgb[3], p, sector;
static const int sector_data[][3]=
{{0,2,1}, {1,2,0}, {1,0,2}, {2,0,1}, {2,1,0}, {0,1,2}};
hue *= 0.033333333333333333333333333333333f;
sector = cvFloor(hue);
p = cvRound(255*(hue - sector));
p ^= sector & 1 ? 255 : 0;

rgb[sector_data[sector][0]] = 255;
rgb[sector_data[sector][1]] = 0;
rgb[sector_data[sector][2]] = p;

#ifdef _DEBUG
printf("/n # Convert HSV to RGB:");
printf("/n HUE = %f", hue);
printf("/n R = %d, G = %d, B = %d", rgb[0],rgb[1],rgb[2]);
#endif

return cvScalar(rgb[2], rgb[1], rgb[0],0);
}

int main( int argc, char** argv )
{
CvCapture* capture = 0;
IplImage* frame = 0;

if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 );
else if( argc == 2 )
capture = cvCaptureFromAVI( argv[1] );

if( !capture )
{
fprintf(stderr,"Could not initialize capturing.../n");
return -1;
}

printf( "Hot keys: /n"
"/tESC - quit the program/n"
"/tc - stop the tracking/n"
"/tb - switch to/from backprojection view/n"
"/th - show/hide object histogram/n"
"To initialize tracking, select the object with mouse/n" );

//cvNamedWindow( "Histogram", 1 );
cvNamedWindow( "CamShiftDemo", 1 );
cvSetMouseCallback( "CamShiftDemo", on_mouse ); // on_mouse 自訂事件
cvCreateTrackbar( "Vmin", "CamShiftDemo", &vmin, 256, 0 );
cvCreateTrackbar( "Vmax", "CamShiftDemo", &vmax, 256, 0 );
cvCreateTrackbar( "Smin", "CamShiftDemo", &smin, 256, 0 );

for(;;)
{
int i, bin_w, c;

frame = cvQueryFrame( capture );
if( !frame )
break;

if( !image )
{

image = cvCreateImage( cvGetSize(frame), 8, 3 );
image->origin = frame->origin;
hsv = cvCreateImage( cvGetSize(frame), 8, 3 );
hue = cvCreateImage( cvGetSize(frame), 8, 1 );
mask = cvCreateImage( cvGetSize(frame), 8, 1 );
backproject = cvCreateImage( cvGetSize(frame), 8, 1 );
hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 ); // 計算長條圖
histimg = cvCreateImage( cvSize(320,200), 8, 3 );
cvZero( histimg );
}

cvCopy( frame, image, 0 );
cvCvtColor( image, hsv, CV_BGR2HSV ); // 彩色空間轉換 BGR to HSV

if( track_object )
{
int _vmin = vmin, _vmax = vmax;

cvInRangeS( hsv, cvScalar(0,smin,MIN(_vmin,_vmax),0),
cvScalar(180,256,MAX(_vmin,_vmax),0), mask ); // 得到二值的MASK
cvSplit( hsv, hue, 0, 0, 0 ); // 只提取 HUE 分量

if( track_object < 0 )
{
float max_val = 0.f;
cvSetImageROI( hue, selection ); // 得到選擇地區 for ROI
cvSetImageROI( mask, selection ); // 得到選擇地區 for mask
cvCalcHist( &hue, hist, 0, mask ); // 計算長條圖
cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 ); // 只找最大值
cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 ); // 縮放 bin 到區間 [0,255]
cvResetImageROI( hue ); // remove ROI
cvResetImageROI( mask );
track_window = selection;
track_object = 1;

cvZero( histimg );
bin_w = histimg->width / hdims; // hdims: 條的個數,則 bin_w 為條的寬度

// 畫長條圖
for( i = 0; i < hdims; i++ )
{
int val = cvRound( cvGetReal1D(hist->bins,i)*histimg->height/255 );
CvScalar color = hsv2rgb(i*180.f/hdims);
cvRectangle( histimg, cvPoint(i*bin_w,histimg->height),
cvPoint((i+1)*bin_w,histimg->height - val),
color, -1, 8, 0 );
}
}

cvCalcBackProject( &hue, backproject, hist ); // 使用 back project 方法
cvAnd( backproject, mask, backproject, 0 );

// calling CAMSHIFT 演算法模組
cvCamShift( backproject, track_window,
cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
&track_comp, &track_box );
track_window = track_comp.rect;

if( backproject_mode )
cvCvtColor( backproject, image, CV_GRAY2BGR ); // 使用backproject灰階映像
if( image->origin )
track_box.angle = -track_box.angle;
cvEllipseBox( image, track_box, CV_RGB(255,0,0), 3, CV_AA, 0 );
}

if( select_object && selection.width > 0 && selection.height > 0 )
{
cvSetImageROI( image, selection );
cvXorS( image, cvScalarAll(255), image, 0 );
cvResetImageROI( image );
}

cvShowImage( "CamShiftDemo", image );
cvShowImage( "Histogram", histimg );

c = cvWaitKey(10);
if( c == 27 )
break; // exit from for-loop
switch( c )
{
case 'b':
backproject_mode ^= 1;
break;
case 'c':
track_object = 0;
cvZero( histimg );
break;
case 'h':
show_hist ^= 1;
if( !show_hist )
cvDestroyWindow( "Histogram" );
else
cvNamedWindow( "Histogram", 1 );
break;
default:
;
}
}

cvReleaseCapture( &capture );
cvDestroyWindow("CamShiftDemo");

return 0;
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.