opencv2對讀書筆記——使用均值漂移算法查找物體

一些小概念ios


1.反投影直方圖的結果是一個機率映射,體現了已知圖像內容出現在圖像中特定位置的機率。算法

2.機率映射可以找到最初的位置,從最初的位置開始並且迭代移動,便可以找到精確的位置,這就是均值漂移算法作的事情。ide

3.均值漂移算法是以迭代的方式鎖定函數的局部最大值的。函數


關於均值漂移算法的過程(opencv)ui


事實上均值漂移算法就是尋找提早定義尋找區域中數據點的重心,或者說加權平均值。將尋找區域中心移動到數據點的重心處,並反覆這個過程直到尋找區域重心收斂到一個穩定點。spa

OpenCV中定義了兩種終止條件:迭代最大次數以及窗體重心的位移值(低於該值即以爲算法收斂)。code

在OpenCV中實現這個過程的是meanshift函數,其源碼例如如下:orm


#include "_cv.h"

CV_IMPL int
cvMeanShift( const void* imgProb, CvRect windowIn,
             CvTermCriteria criteria, CvConnectedComp* comp )
{
    //CvMoments用來計算矩形的重心,面積等形狀特徵
    CvMoments moments;
    int    i = 0, eps;
    CvMat  stub, *mat = (CvMat*)imgProb;
    CvMat  cur_win;
    CvRect cur_rect = windowIn;

    CV_FUNCNAME( "cvMeanShift" );

    //初始化跟蹤窗體
    if( comp )
        comp->rect = windowIn;

    //把0階矩和1階矩先初始化置零
    moments.m00 = moments.m10 = moments.m01 = 0;

    __BEGIN__;

    CV_CALL( mat = cvGetMat( mat, &stub ));

    //各類輸入變量不符合要求時顯示錯誤信息
    if( CV_MAT_CN( mat->type ) > 1 )
        CV_ERROR( CV_BadNumChannels, cvUnsupportedFormat );
    if( windowIn.height <= 0 || windowIn.width <= 0 )
        CV_ERROR( CV_StsBadArg, "Input window has non-positive sizes" );
    if( windowIn.x < 0 || windowIn.x + windowIn.width > mat->cols ||
        windowIn.y < 0 || windowIn.y + windowIn.height > mat->rows )
        CV_ERROR( CV_StsBadArg, "Initial window is not inside the image ROI" );

    //迭代的標準,精度=1.0,迭代次數=100
    CV_CALL( criteria = cvCheckTermCriteria( criteria, 1., 100 ));

    //精度eps=1
    eps = cvRound( criteria.epsilon * criteria.epsilon );

    //最大循環次數=最大迭代次數criteria.max_iter=100
    for( i = 0; i < criteria.max_iter; i++ )
    {
        int dx, dy, nx, ny;
        double inv_m00;

        //選取搜索區域,對該矩形區域計算它的0,1階矩
        CV_CALL( cvGetSubRect( mat, &cur_win, cur_rect )); 
        CV_CALL( cvMoments( &cur_win, &moments ));

        /* Calculating center of mass */
        if( fabs(moments.m00) < DBL_EPSILON )
            break;

        //搜索區域的質量m00
        inv_m00 = moments.inv_sqrt_m00*moments.inv_sqrt_m00;
        //搜索區域的水平重心偏移dx
        dx = cvRound( moments.m10 * inv_m00 - windowIn.width*0.5 );
        //搜索區域的垂直重心偏移dy
        dy = cvRound( moments.m01 * inv_m00 - windowIn.height*0.5 );

        //搜索區域的重心座標(nx,ny)
        nx = cur_rect.x + dx;
        ny = cur_rect.y + dy;

        //跟蹤目標處於圖像邊緣時進行一些對應的處理
        if( nx < 0 )
            nx = 0;
        else if( nx + cur_rect.width > mat->cols )
            nx = mat->cols - cur_rect.width;

        if( ny < 0 )
            ny = 0;
        else if( ny + cur_rect.height > mat->rows )
            ny = mat->rows - cur_rect.height;

        dx = nx - cur_rect.x;
        dy = ny - cur_rect.y;
        cur_rect.x = nx;
        cur_rect.y = ny;

        /* Check for coverage centers mass & window */
        //精度達到要求時就能夠退出循環
        if( dx*dx + dy*dy < eps )
            break;
    }

    __END__;

    //對meanshift函數的返回值賦值
    if( comp )
    {
        comp->rect = cur_rect;
        comp->area = (float)moments.m00;
    }

    return i;
}

在裏面咱們可以很是easy的看出迭代過程

對這個算法想細緻研究的同窗可以參考一下Dorin Comaniciu 等人2002年寫的:blog

《Mean Shift:A Robust Approach Toward Feature Space Analysis》圖片


實際樣例


樣例代碼


#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\video\tracking.hpp>

#include <iostream>
#include <vector>
using namespace std;

#include "objectFinder.h"
#include "colorhistogram.h"

int main()
{
	//讀取參考圖像
	cv::Mat image= cv::imread("../3.jpg");
	if (!image.data)
		return 0; 

	//定義查找物體
	cv::Mat imageROI= image(cv::Rect(85,200,64,64));
	cv::rectangle(image, cv::Rect(85,200,64,64),cv::Scalar(0,0,255));

	//顯示參考圖像
	cv::namedWindow("第一張圖片,標記籃球位置");
	cv::imshow("第一張圖片,標記籃球位置",image);

	//得到色度直方圖
	ColorHistogram hc;
	cv::MatND colorhist= hc.getHueHistogram(imageROI);

	//讀入目標圖像
	image= cv::imread("../4.jpg");

	//顯示目標圖像
	cv::namedWindow("第二張圖片");
	cv::imshow("第二張圖片",image);

	//將RGB圖像圖像轉換爲HSV圖像
	cv::Mat hsv;
	cv::cvtColor(image, hsv, CV_BGR2HSV);

	//分離圖像通道
	vector<cv::Mat> v;
	cv::split(hsv,v);

	//消除飽和度較低的像素點
	int minSat=65;
	cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
	cv::namedWindow("第二張圖片消除飽和度較低的像素點");
	cv::imshow("第二張圖片消除飽和度較低的像素點",v[1]);

	//進行直方圖反投影
	ObjectFinder finder;
	finder.setHistogram(colorhist);
	finder.setThreshold(0.3f);
	int ch[1]={0};
	cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);

	cv::namedWindow("第二張圖片進行直方圖反投影");
	cv::imshow("第二張圖片進行直方圖反投影",result);

	//利用位運算消除低飽和度像素
	cv::bitwise_and(result,v[1],result);
	cv::namedWindow("第二張圖片利用位運算進一步消除低飽和度像素點");
	cv::imshow("第二張圖片利用位運算進一步消除低飽和度像素點",result);

	// 獲得反投影直方圖機率圖像
	finder.setThreshold(-1.0f);
	result= finder.find(hsv,0.0f,180.0f,ch,1);
	cv::bitwise_and(result,v[1],result);
	cv::namedWindow("第二張圖片處理後的二值圖像");
	cv::imshow("第二張圖片處理後的二值圖像",result);

	cv::Rect rect(85,200,64,64);
	cv::rectangle(image, rect, cv::Scalar(0,0,255));

	cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
	cout << "均值漂移迭代次數 = " << cv::meanShift(result,rect,criteria) << endl;

	cv::rectangle(image, rect, cv::Scalar(0,255,0));

	//展現結果圖
	cv::namedWindow("查找結果,紅框爲第一幅圖中籃球位置,綠框爲現位置");
	cv::imshow("查找結果,紅框爲第一幅圖中籃球位置,綠框爲現位置",image);

	cv::waitKey();
	return 0;
}

輸出結果
















                                  -END-

相關文章
相關標籤/搜索