camshift.cpp

#include "_cvaux.h"

CvCamShiftTracker::CvCamShiftTracker()
{
    int i;
    
    memset( &m_box, 0, sizeof(m_box));
    memset( &m_comp, 0, sizeof(m_comp));
    memset( m_color_planes, 0, sizeof(m_color_planes));
    m_threshold = 0;

    for( i = 0; i < CV_MAX_DIM; i++ )
    {
        m_min_ch_val[i] = 0;
        m_max_ch_val[i] = 255;
        m_hist_ranges[i] = m_hist_ranges_data[i];
        m_hist_ranges[i][0] = 0.f;
        m_hist_ranges[i][1] = 256.f;
    }

    m_hist = 0;
    m_back_project = 0;
    m_temp = 0;
    m_mask = 0;
}


CvCamShiftTracker::~CvCamShiftTracker()
{
    int i;
    
    cvReleaseHist( &m_hist );
    for( i = 0; i < CV_MAX_DIM; i++ )
        cvReleaseImage( &m_color_planes[i] );
    cvReleaseImage( &m_back_project );
    cvReleaseImage( &m_temp );
    cvReleaseImage( &m_mask );
}


void
CvCamShiftTracker::color_transform( const IplImage* image )
{
    CvSize size = cvGetSize(image);
    uchar* color_data = 0, *mask = 0;
    uchar* planes[CV_MAX_DIM];
    int x, color_step = 0, plane_step = 0, mask_step;
    int dims[CV_MAX_DIM];
    int i, n = get_hist_dims(dims);

    assert( image->nChannels == 3 && m_hist != 0 );
    
    if( !m_temp || !m_mask || !m_color_planes[0] || !m_color_planes[n-1] || !m_back_project ||
        m_temp->width != size.width || m_temp->height != size.height ||
        m_temp->nChannels != 3 )
    {
        cvReleaseImage( &m_temp );
        m_temp = cvCreateImage( size, IPL_DEPTH_8U, 3 );
        cvReleaseImage( &m_mask );
        m_mask = cvCreateImage( size, IPL_DEPTH_8U, 1 );
        cvReleaseImage( &m_back_project );
        m_back_project = cvCreateImage( size, IPL_DEPTH_8U, 1 );
        for( i = 0; i < CV_MAX_DIM; i++ )
        {
            cvReleaseImage( &m_color_planes[i] );
            if( i < n )
                m_color_planes[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );
        }
    }

    cvCvtColor( image, m_temp, CV_BGR2HSV );
    cvGetRawData( m_temp, &color_data, &color_step, &size );
    cvGetRawData( m_mask, &mask, &mask_step, &size );
    
    for( i = 0; i < n; i++ )
        cvGetRawData( m_color_planes[i], &planes[i], &plane_step, &size );

    for( ; size.height--; color_data += color_step, mask += mask_step )
    {
        for( x = 0; x < size.width; x++ )
        {
            int val0 = color_data[x*3];
            int val1 = color_data[x*3+1];
            int val2 = color_data[x*3+2];
            if( m_min_ch_val[0] <= val0 && val0 <= m_max_ch_val[0] &&
                m_min_ch_val[1] <= val1 && val1 <= m_max_ch_val[1] &&
                m_min_ch_val[2] <= val2 && val2 <= m_max_ch_val[2] )
            {
                // hue is written to the 0-th plane, saturation - to the 1-st one,
                // so 1d histogram will automagically correspond to hue-based tracking,
                // 2d histogram - to saturation-based tracking.
                planes[0][x] = (uchar)val0;
                if( n > 1 )
                    planes[1][x] = (uchar)val1;
                if( n > 2 )
                    planes[2][x] = (uchar)val2;

                mask[x] = (uchar)255;
            }
            else
            {
                planes[0][x] = 0;
                if( n > 1 )
                    planes[1][x] = 0;
                if( n > 2 )
                    planes[2][x] = 0;
                mask[x] = 0;
            }
        }
        for( i = 0; i < n; i++ )
            planes[i] += plane_step;
    }
}


bool
CvCamShiftTracker::update_histogram( const IplImage* cur_frame )
{
    float max_val = 0;
    int i, dims;

    if( m_comp.rect.width == 0 || m_comp.rect.height == 0 ||
        m_hist == 0 )
    {
        assert(0);
        return false;
    }

    color_transform(cur_frame);

    dims = cvGetDims( m_hist->bins );
    for( i = 0; i < dims; i++ )
        cvSetImageROI( m_color_planes[i], m_comp.rect );
    cvSetImageROI( m_mask, m_comp.rect );

    cvSetHistBinRanges( m_hist, m_hist_ranges, 1 );
    cvCalcHist( m_color_planes, m_hist, 0, m_mask );

    for( i = 0; i < dims; i++ )
        cvSetImageROI( m_color_planes[i], m_comp.rect );

    for( i = 0; i < dims; i++ )
        cvResetImageROI( m_color_planes[i] );
    cvResetImageROI( m_mask );

    cvGetMinMaxHistValue( m_hist, 0, &max_val );
    cvScale( m_hist->bins, m_hist->bins, max_val ? 255. / max_val : 0. );

    return max_val != 0;
}


void
CvCamShiftTracker::reset_histogram()
{
    if( m_hist )
        cvClearHist( m_hist );
}


bool
CvCamShiftTracker::track_object( const IplImage* cur_frame )
{
    CvRect rect;
    CvSize bp_size;
    
    union
    {
        void** arr;
        IplImage** img;
    } u;
    
    if( m_comp.rect.width == 0 || m_comp.rect.height == 0 ||
        m_hist == 0 )
    {
        return false;
    }
    
    color_transform( cur_frame );
    u.img = m_color_planes;
    cvCalcArrBackProject( u.arr, m_back_project, m_hist );
    cvAnd( m_back_project, m_mask, m_back_project );

    rect = m_comp.rect;
    bp_size = cvGetSize( m_back_project );
    if( rect.x < 0 )
        rect.x = 0;
    if( rect.x + rect.width > bp_size.width )
        rect.width = bp_size.width - rect.x;
    if( rect.y < 0 )
        rect.y = 0;
    if( rect.y + rect.height > bp_size.height )
        rect.height = bp_size.height - rect.y;

    cvCamShift( m_back_project, rect,
                cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
                &m_comp, &m_box );

    if( m_comp.rect.width == 0 || m_comp.rect.height == 0 )
        m_comp.rect = rect; // do not allow tracker to loose the object

    return m_comp.rect.width != 0 && m_comp.rect.height != 0;
}


bool
CvCamShiftTracker::set_hist_dims( int c_dims, int *dims )
{
    if( (unsigned)(c_dims-1) >= (unsigned)CV_MAX_DIM || dims == 0 )
        return false;

    if( m_hist )
    {
        int dims2[CV_MAX_DIM];
        int c_dims2 = cvGetDims( m_hist->bins, dims2 );

        if( c_dims2 == c_dims && memcmp( dims, dims2, c_dims*sizeof(dims[0])) == 0 )
            return true;

        cvReleaseHist( &m_hist );
    }

    m_hist = cvCreateHist( c_dims, dims, CV_HIST_ARRAY, 0, 0 );

    return true;
}


bool
CvCamShiftTracker::set_hist_bin_range( int channel, int min_val, int max_val )
{
    if( (unsigned)channel >= (unsigned)CV_MAX_DIM ||
        min_val >= max_val || min_val < 0 || max_val > 256 )
    {
        assert(0);
        return false;
    }

    m_hist_ranges[channel][0] = (float)min_val;
    m_hist_ranges[channel][1] = (float)max_val;

    return true;
}

/* End of file. */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
cv2.camshift() 是 OpenCV 中用于实现 CAMShift 算法的函数,其源代码可以在 OpenCV 的 GitHub 库中找到。 以下是 cv2.camshift() 的源代码: ``` CV_IMPL int cvCamShift( CvArr* imgProb, CvRect _window, CvTermCriteria criteria, CvConnectedComp* _comp, CvBox2D* box ) { const int TOLERANCE = 10; int k, row, col, pix, iter; int max_iter = criteria.max_iter; float hsize[] = {0.25f, 0.25f, 0.5f, 0.5f, 0.75f, 1.f, 1.f, 1.f}; float hranges[] = {-180, 180}; float vmin = 0, vmax = 180, smin = 0; float hist_thresh; CvSize sz; IplImage stub, *prob = (IplImage*)imgProb, *mask = 0; uchar *mask_row = 0; int mask_step = 0; CvHistogram* hist = 0; CvMoments m; CvPoint2D32f center, old_center; float a, b, c, xscale, yscale, max_val; CvBox2D box0; CV_FUNCNAME( "cvCamShift" ); __BEGIN__; if( !CV_IS_IMAGE( prob )) CV_ERROR( CV_StsBadArg, "The probability map is not a valid image" ); if( prob->depth != IPL_DEPTH_32F || prob->nChannels != 1 ) CV_ERROR( CV_StsUnsupportedFormat, "Only 32-bit floating-point, single-channel probability images are supported" ); if( !CV_ARE_SIZES_EQ( prob, &_window )) CV_ERROR( CV_StsUnmatchedSizes, "The probability map size differs from the tracking window size" ); if( criteria.type & CV_TERMCRIT_EPS ) criteria.epsilon *= criteria.epsilon; else criteria.epsilon = 0; criteria.epsilon = MAX( criteria.epsilon, 1e-8f ); criteria.max_iter = MAX( criteria.max_iter, 1 ); if( criteria.type & CV_TERMCRIT_ITER ) criteria.max_iter = MIN( criteria.max_iter, 100 ); else criteria.max_iter = 100; // allocate images sz = cvGetSize( prob ); mask = cvCreateImage( sz, IPL_DEPTH_8U, 1 ); cvRectangle( mask, cvPoint(0,0), cvPoint(sz.width,sz.height), CV_RGB(255,255,255), -1 ); cvSetImageROI( mask, _window ); cvSet( mask, cvScalar(0) ); cvResetImageROI( mask ); hist = cvCreateHist( 1, &hist_size, CV_HIST_ARRAY, &h_ranges, 1 ); cvCalcArrHist( &prob, hist, 0 ); cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 ); cvConvertScale( hist->bins, hist->bins, max_val? 255.0/max_val : 0, 0 ); // cam-shift iteration center.x = (_window.x + _window.width - 1)*0.5f; center.y = (_window.y + _window.height - 1)*0.5f; old_center = center; box0.center = center; box0.size.width = _window.width; box0.size.height = _window.height; box0.angle = 0; iter = 0; for(;;) { CvBox2D box1; float *row_ptr; float m00, m10, m01; if( center.x <= 0 || center.x >= sz.width-1 || center.y <= 0 || center.y >= sz.height-1 ) break; cvSetImageROI( prob, _window ); cvSetImageROI( mask, _window ); cvCalcArrHist( &prob, hist, 0 ); cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 ); hist_thresh = max_val * (1.f - criteria.epsilon); cvThreshHist( hist, hist_thresh ); cvNormalizeHist( hist, 1 ); // find the x and y gradients assert( CV_MAT_DEPTH( prob->type ) == CV_32F ); cvSobel( prob, dx, 1, 0, 1 ); cvSobel( prob, dy, 0, 1, 1 ); // initialize the transition matrix H = J'J m00 = m10 = m01 = 0; row_ptr = (float*)(dx->imageData + dx->widthStep); for( row = 1; row <= _window.height; row++, row_ptr += dx->widthStep ) for( col = 1; col <= _window.width; col++ ) { pix = cvRound(row_ptr[col]); m00 += pix*pix; m10 += (float)col*pix; m01 += (float)row*pix; } H[0] = m00; H[1] = m10; H[2] = m01; H[3] = m10; H[4] = (float)_window.width*_window.width; H[5] = 0; H[6] = m01; H[7] = 0; H[8] = (float)_window.height*_window.height; // calculate the update step cvSolve( &H, &dh, &dp, CV_LU ); if( fabs(dp.x) > (float)_window.width*TOLERANCE || fabs(dp.y) > (float)_window.height*TOLERANCE ) break; // update the window position center.x += dp.x; center.y += dp.y; _window.x = cvRound(center.x - _window.width*0.5f); _window.y = cvRound(center.y - _window.height*0.5f); if( iter >= max_iter ) break; if( _window.x < 0 || _window.x + _window.width >= sz.width || _window.y < 0 || _window.y + _window.height >= sz.height ) break; box1.center = center; box1.size.width = _window.width; box1.size.height = _window.height; box1.angle = 0; a = (float)fabs( box0.size.width - box1.size.width ); b = (float)fabs( box0.size.height - box1.size.height ); c = (float)fabs( box0.angle - box1.angle ); c = c >= 180 ? 360 - c : c; if( a < _window.width*0.05f && b < _window.height*0.05f && c < 2.0f ) break; box0 = box1; iter++; } if( _comp ) { memset( _comp, 0, sizeof(*_comp)); _comp->rect.x = _window.x; _comp->rect.y = _window.y; _comp->rect.width = _window.width; _comp->rect.height = _window.height; _comp->area = 0; mask_row = (uchar*)(mask->imageData + mask->widthStep*(int)_window.y); mask_step = mask->widthStep; } if( box ) { box->center = center; box->size.width = _window.width; box->size.height = _window.height; box->angle = 0; } if( _comp ) { for( row = 0; row < _window.height; row++, mask_row += mask_step ) for( col = 0; col < _window.width; col++ ) if( mask_row[col] ) _comp->area++; if( _comp->area > 0 ) { _comp->value = -1; _comp->rect.width--; _comp->rect.height--; cvCalcSubPixelMoment( &m, prob, &center ); _comp->centroid.x = m.m10/m.m00 + _window.x; _comp->centroid.y = m.m01/m.m00 + _window.y; } } __END__; cvReleaseImage( &mask ); cvReleaseHist( &hist ); cvReleaseImage( &dx ); cvReleaseImage( &dy ); return cvGetErrStatus(); } ``` 该函数的实现包括以下步骤: 1. 对输入的概率图像进行判断; 2. 分配内存; 3. 计算直方图; 4. CAMShift 迭代; 5. 返回结果。 在 CAMShift 迭代中,该函数会计算梯度,判断迭代是否终止,并更新窗口位置和大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值