KCF代码分析

来源:互联网 发布:淘宝首页设计怎么设计 编辑:程序博客网 时间:2024/06/11 16:51

很多都是参考网上的大神分析

KCF代码具体分析

KCF知乎专栏


放几个KCF中主要的cpp和hpp文件,回去接着看代码

fhog.cpp

#include "fhog.hpp"#ifdef HAVE_TBB#include <tbb/tbb.h>#include "tbb/parallel_for.h"#include "tbb/blocked_range.h"#endif#ifndef max#define max(a,b)            (((a) > (b)) ? (a) : (b))#endif#ifndef min#define min(a,b)            (((a) < (b)) ? (a) : (b))#endif/**********************************************************************************函数功能:计算image的hog特征,结果在map结构中的map大小为sizeX*sizeY*NUM_SECTOR*3(Getting feature map for the selected subimage)函数参数:选中的子图,cell的大小,返回的特征图RESULT:Error status***********************************************************************************/int getFeatureMaps( const IplImage* image, const int k, CvLSVMFeatureMapCaskade **map ){//总体过程是://1.计算每个像素的水平梯度dx和垂直梯度dy//2.计算每个像素的通道间最大梯度大小r及其最邻近梯度方向的索引值//3.计算每个block(2+4+2)*(2+4+2)的梯度直方图(分为9和18bin)存于map中//每个block的特征是以一个cell为中心,根据像素的位置以及像素点的梯度强度进行加权获得的int sizeX, sizeY;int p, px, stringSize;int height, width, numChannels;int i, j, kk, c, ii, jj, d;float  * datadx, *datady;int   ch;float magnitude, x, y, tx, ty;IplImage * dx, *dy;int *nearest;float *w, a_x, b_x;// 横向和纵向的3长度{-1,0,1}矩阵 float kernel[3] = { -1.f, 0.f, 1.f };CvMat kernel_dx = cvMat( 1, 3, CV_32F, kernel );          // 1*3的矩阵CvMat kernel_dy = cvMat( 3, 1, CV_32F, kernel );          // 3*1的矩阵float * r;//记录每个像素点的每个通道的最大梯度int   * alfa;//记录每个像素的梯度方向的索引值,分别为9份时的索引值和18份时的索引值。float boundary_x[NUM_SECTOR + 1];                        // boundary_x[10]float boundary_y[NUM_SECTOR + 1];float max, dotProd;int   maxi;height = image->height;width = image->width;numChannels = image->nChannels;// 采样图像大小的Ipl图像dx = cvCreateImage( cvSize( image->width, image->height ),IPL_DEPTH_32F, 3 );dy = cvCreateImage( cvSize( image->width, image->height ),IPL_DEPTH_32F, 3 );// 向下取整的(边界大小/4),k = cell_sizesizeX = width / k;// 将图像分割成多个元胞(cell),x方向上cell的个数sizeY = height / k;// y方向上cell的个数px = 3 * NUM_SECTOR;// 三通道?NUM_SECTOR=9 Hog特征中的9个角度范围p = px;// p=27stringSize = sizeX * p;// stringSize = 27*sizeXallocFeatureMapObject( map, sizeX, sizeY, p );// 为map初始化内存sizeX*sizeY*p=sizeY*stringSize// image:输入图像.  // dx:输出图像.  // kernel_dx:卷积核, 单通道浮点矩阵. 如果想要应用不同的核于不同的通道,先用 cvSplit 函数分解图像到单个色彩通道上,然后单独处理。  // cvPoint(-1, 0):核的锚点表示一个被滤波的点在核内的位置。 锚点应该处于核内部。缺省值 (-1,-1) 表示锚点在核中心。  // 函数 cvFilter2D 对图像进行线性滤波,支持 In-place 操作。当核运算部分超出输入图像时,函数从最近邻的图像内部象素差值得到边界外面的象素值。cvFilter2D( image, dx, &kernel_dx, cvPoint( -1, 0 ) );      // 起点在(x-1,y),按x方向滤波cvFilter2D( image, dy, &kernel_dy, cvPoint( 0, -1 ) );      // 起点在(x,y-1),按y方向滤波 // 初始化cos和sin函数float arg_vector;// 计算梯度角的边界,并存储在boundary__y中for( i = 0; i <= NUM_SECTOR; i++ ){arg_vector = ( ( float ) i ) * ( ( float ) ( PI ) / ( float ) ( NUM_SECTOR ) );// 每个角的角度boundary_x[i] = cosf( arg_vector );// 每个角度对应的余弦值boundary_y[i] = sinf( arg_vector );// 每个角度对应的正弦值}/*for(i = 0; i <= NUM_SECTOR; i++) */r = ( float * ) malloc( sizeof( float ) * ( width * height ) );alfa = ( int   * ) malloc( sizeof( int ) * ( width * height * 2 ) );//2.for( j = 1; j < height - 1; j++ ){// 记录每一行的首地址datadx = ( float* ) ( dx->imageData + dx->widthStep * j );datady = ( float* ) ( dy->imageData + dy->widthStep * j );for( i = 1; i < width - 1; i++ )// 遍历一行中的非边界像素{c = 0;// 第一颜色通道x = ( datadx[i * numChannels + c] );y = ( datady[i * numChannels + c] );r[j * width + i] = sqrtf( x * x + y * y );// 计算0通道的梯度大小// 使用向量大小最大的通道替代储存值for( ch = 1; ch < numChannels; ch++ )// 计算其他两个通道{tx = ( datadx[i * numChannels + ch] );ty = ( datady[i * numChannels + ch] );magnitude = sqrtf( tx * tx + ty * ty );// 计算幅值if( magnitude > r[j * width + i] )// 找出每个像素点的梯度的最大值(有三个颜色空间对应的梯度),并记录通道数以及水平梯度以及垂直梯度{r[j * width + i] = magnitude;// r表示最大幅值c = ch;// c表示这个幅值来自的通道序号x = tx;// x表示这个幅值对应的坐标处的x梯度y = ty;// y表示这个幅值对应的坐标处的y梯度}}/*for(ch = 1; ch < numChannels; ch++)*/// 使用sqrt(cos*x*cos*x+sin*y*sin*y)最大的替换掉max = boundary_x[0] * x + boundary_y[0] * y;maxi = 0;// 假设像素点的梯度方向为a,梯度方向为t,梯度大小为r,则dotProd=r*cosa*cost+r*sina*sint=r*cos(a-t)for( kk = 0; kk < NUM_SECTOR; kk++ )// 遍历9个HOG划分的角度范围{dotProd = boundary_x[kk] * x + boundary_y[kk] * y;// 计算两个向量的点乘// 若dotProd最大,则说明t最接近aif( dotProd > max ){max = dotProd;maxi = kk;}// 若-dotProd最大,则说明t最接近a+pielse{if( -dotProd > max ){max = -dotProd;// 取相反数maxi = kk + NUM_SECTOR;// ?}}}// 看起来有点像储存cos和sin的周期值alfa[j * width * 2 + i * 2] = maxi % NUM_SECTOR;// alfa[j * width * 2 + i * 2 + 1] = maxi;}/*for(i = 0; i < width; i++)*/}/*for(j = 0; j < height; j++)*/// nearest=[-1,-1,1,1];nearest = ( int  * ) malloc( sizeof( int ) *  k );w= ( float* ) malloc( sizeof( float ) * ( k * 2 ) );// 给nearest初始化,为了方便以后利用相邻的cell的特征计算block(8*8,每个block以一个cell为中心,以半个cell为边界厚度)的属性for( i = 0; i < k / 2; i++ ){nearest[i] = -1;}/*for(i = 0; i < k / 2; i++)*/for( i = k / 2; i < k; i++ ){nearest[i] = 1;}/*for(i = k / 2; i < k; i++)*///给w初始化?不明白w的作用,可能是cell(4*4)中每个像素贡献给直方图的权值(1/8+3/8+5/8+7/8+7/8+5/8+3/8+1/8)*(1/8+3/8+5/8+7/8+7/8+5/8+3/8+1/8)=4*4for( j = 0; j < k / 2; j++ ){b_x = k / 2 + j + 0.5f;a_x = k / 2 - j - 0.5f;w[j * 2] = 1.0f / a_x * ( ( a_x * b_x ) / ( a_x + b_x ) );w[j * 2 + 1] = 1.0f / b_x * ( ( a_x * b_x ) / ( a_x + b_x ) );}/*for(j = 0; j < k / 2; j++)*/for( j = k / 2; j < k; j++ ){a_x = j - k / 2 + 0.5f;b_x = -j + k / 2 - 0.5f + k;w[j * 2] = 1.0f / a_x * ( ( a_x * b_x ) / ( a_x + b_x ) );w[j * 2 + 1] = 1.0f / b_x * ( ( a_x * b_x ) / ( a_x + b_x ) );}/*for(j = k / 2; j < k; j++)*///3.for( i = 0; i < sizeY; i++ ){for( j = 0; j < sizeX; j++ ){for( ii = 0; ii < k; ii++ ){for( jj = 0; jj < k; jj++ ){//第i行的第j个cell的第ii行第jj个像素if( ( i * k + ii > 0 ) &&( i * k + ii < height - 1 ) &&( j * k + jj > 0 ) &&( j * k + jj < width - 1 ) )//要跳过厚度为1的边界像素,因为边界的梯度值不准确,但这样会导致含有边界的cell统计不完整{d = ( k * i + ii ) * width + ( j * k + jj );( *map )->map[i * stringSize + j * ( *map )->numFeatures + alfa[d * 2]] +=r[d] * w[ii * 2] * w[jj * 2];//第i行第j个cell的第alfa[d * 2]个梯度方向(0-8)( *map )->map[i * stringSize + j * ( *map )->numFeatures + alfa[d * 2 + 1] + NUM_SECTOR] +=r[d] * w[ii * 2] * w[jj * 2];//第i行第j个cell的第alfa[d * 2+1]个梯度方向(9-26)if( ( i + nearest[ii] >= 0 ) &&( i + nearest[ii] <= sizeY - 1 ) ){( *map )->map[( i + nearest[ii] ) * stringSize + j * ( *map )->numFeatures + alfa[d * 2]] +=r[d] * w[ii * 2 + 1] * w[jj * 2];( *map )->map[( i + nearest[ii] ) * stringSize + j * ( *map )->numFeatures + alfa[d * 2 + 1] + NUM_SECTOR] +=r[d] * w[ii * 2 + 1] * w[jj * 2];}if( ( j + nearest[jj] >= 0 ) &&( j + nearest[jj] <= sizeX - 1 ) ){( *map )->map[i * stringSize + ( j + nearest[jj] ) * ( *map )->numFeatures + alfa[d * 2]] +=r[d] * w[ii * 2] * w[jj * 2 + 1];( *map )->map[i * stringSize + ( j + nearest[jj] ) * ( *map )->numFeatures + alfa[d * 2 + 1] + NUM_SECTOR] +=r[d] * w[ii * 2] * w[jj * 2 + 1];}if( ( i + nearest[ii] >= 0 ) &&( i + nearest[ii] <= sizeY - 1 ) &&( j + nearest[jj] >= 0 ) &&( j + nearest[jj] <= sizeX - 1 ) ){( *map )->map[( i + nearest[ii] ) * stringSize + ( j + nearest[jj] ) * ( *map )->numFeatures + alfa[d * 2]] +=r[d] * w[ii * 2 + 1] * w[jj * 2 + 1];( *map )->map[( i + nearest[ii] ) * stringSize + ( j + nearest[jj] ) * ( *map )->numFeatures + alfa[d * 2 + 1] + NUM_SECTOR] +=r[d] * w[ii * 2 + 1] * w[jj * 2 + 1];}}}/*for(jj = 0; jj < k; jj++)*/}/*for(ii = 0; ii < k; ii++)*/}/*for(j = 1; j < sizeX - 1; j++)*/}/*for(i = 1; i < sizeY - 1; i++)*/// 释放变量cvReleaseImage( &dx );cvReleaseImage( &dy );free( w );free( nearest );free( r );free( alfa );return LATENT_SVM_OK;}/*****************************************************************************函数功能:特征图标准化与截断(Feature map Normalization and Truncation)函数参数:特征图,截断阈值函数输出:标准化与截断之后的特征图RESULT:Error status*****************************************************************************/int normalizeAndTruncate( CvLSVMFeatureMapCaskade *map, const float alfa ){//计算步骤://1.分别计算每个block(除去边界)的9分特性的9个特性的平方和//2.分别计算每个block在各个方向上的9分特性的2范数//3.用各个属性(共27个)除以各个方向上的2范数,得到归一化的27*4个属性int i, j, ii;int sizeX, sizeY, p, pos, pp, xp, pos1, pos2;float * partOfNorm; // norm of C(i, j)float * newData;float   valOfNorm;//大小为block的总数,计算每个block的前九个特征的2范数sizeX = map->sizeX;sizeY = map->sizeY;partOfNorm = ( float * ) malloc( sizeof( float ) * ( sizeX * sizeY ) );p = NUM_SECTOR;//每个cell的bin的数目xp = NUM_SECTOR * 3;//每个block的总特征数(9+18)pp = NUM_SECTOR * 12;for( i = 0; i < sizeX * sizeY; i++ ){valOfNorm = 0.0f;pos = i * map->numFeatures;//第i个block的第一个特征点索引号for( j = 0; j < p; j++ ){valOfNorm += map->map[pos + j] * map->map[pos + j];//计算第i个block的前9个特征的平方和}/*for(j = 0; j < p; j++)*/partOfNorm[i] = valOfNorm;}/*for(i = 0; i < sizeX * sizeY; i++)*/sizeX -= 2;//去掉第一列和最后一列的blocksizeY -= 2;//去掉一第行和最后一行的blocknewData = ( float * ) malloc( sizeof( float ) * ( sizeX * sizeY * pp ) );//normalizationfor( i = 1; i <= sizeY; i++ ){for( j = 1; j <= sizeX; j++ ){//右下valOfNorm = sqrtf(partOfNorm[( i ) *( sizeX + 2 ) + ( j )] +partOfNorm[( i ) *( sizeX + 2 ) + ( j + 1 )] +partOfNorm[( i + 1 )*( sizeX + 2 ) + ( j )] +partOfNorm[( i + 1 )*( sizeX + 2 ) + ( j + 1 )] ) + FLT_EPSILON;//计算该block右下四个block的9分属性的2范数pos1 = ( i ) * ( sizeX + 2 ) * xp + ( j ) * xp;//第i行第j列的block的属性的第一个值的索引值pos2 = ( i - 1 ) * ( sizeX ) * pp + ( j - 1 ) * pp;//除掉边框后的第i-1行第j-列的block的newdata的首地址for( ii = 0; ii < p; ii++ ){newData[pos2 + ii] = map->map[pos1 + ii] / valOfNorm;}/*for(ii = 0; ii < p; ii++)*/for( ii = 0; ii < 2 * p; ii++ ){newData[pos2 + ii + p * 4] = map->map[pos1 + ii + p] / valOfNorm;}/*for(ii = 0; ii < 2 * p; ii++)*///右上valOfNorm = sqrtf(partOfNorm[( i ) *( sizeX + 2 ) + ( j )] +partOfNorm[( i ) *( sizeX + 2 ) + ( j + 1 )] +partOfNorm[( i - 1 )*( sizeX + 2 ) + ( j )] +partOfNorm[( i - 1 )*( sizeX + 2 ) + ( j + 1 )] ) + FLT_EPSILON;for( ii = 0; ii < p; ii++ ){newData[pos2 + ii + p] = map->map[pos1 + ii] / valOfNorm;}/*for(ii = 0; ii < p; ii++)*/for( ii = 0; ii < 2 * p; ii++ ){newData[pos2 + ii + p * 6] = map->map[pos1 + ii + p] / valOfNorm;}/*for(ii = 0; ii < 2 * p; ii++)*///左下valOfNorm = sqrtf(partOfNorm[( i ) *( sizeX + 2 ) + ( j )] +partOfNorm[( i ) *( sizeX + 2 ) + ( j - 1 )] +partOfNorm[( i + 1 )*( sizeX + 2 ) + ( j )] +partOfNorm[( i + 1 )*( sizeX + 2 ) + ( j - 1 )] ) + FLT_EPSILON;for( ii = 0; ii < p; ii++ ){newData[pos2 + ii + p * 2] = map->map[pos1 + ii] / valOfNorm;}/*for(ii = 0; ii < p; ii++)*/for( ii = 0; ii < 2 * p; ii++ ){newData[pos2 + ii + p * 8] = map->map[pos1 + ii + p] / valOfNorm;}/*for(ii = 0; ii < 2 * p; ii++)*///左上valOfNorm = sqrtf(partOfNorm[( i ) *( sizeX + 2 ) + ( j )] +partOfNorm[( i ) *( sizeX + 2 ) + ( j - 1 )] +partOfNorm[( i - 1 )*( sizeX + 2 ) + ( j )] +partOfNorm[( i - 1 )*( sizeX + 2 ) + ( j - 1 )] ) + FLT_EPSILON;for( ii = 0; ii < p; ii++ ){newData[pos2 + ii + p * 3] = map->map[pos1 + ii] / valOfNorm;}/*for(ii = 0; ii < p; ii++)*/for( ii = 0; ii < 2 * p; ii++ ){newData[pos2 + ii + p * 10] = map->map[pos1 + ii + p] / valOfNorm;}/*for(ii = 0; ii < 2 * p; ii++)*/}/*for(j = 1; j <= sizeX; j++)*/}/*for(i = 1; i <= sizeY; i++)*///truncationfor( i = 0; i < sizeX * sizeY * pp; i++ ){if( newData[i] > alfa ) newData[i] = alfa;}/*for(i = 0; i < sizeX * sizeY * pp; i++)*///swop datamap->numFeatures = pp;map->sizeX = sizeX;map->sizeY = sizeY;free( map->map );free( partOfNorm );map->map = newData;return LATENT_SVM_OK;}/*****************************************************************************函数功能:特征图降维(Feature map reduction)In each cell we reduce dimension of the feature vector according to original paper special procedure函数参数:特征图函数输出:特征图RESULT:Error status*****************************************************************************/int PCAFeatureMaps( CvLSVMFeatureMapCaskade *map ){//步骤://1.计算每个18分属性在4个方向上的和;//2.计算每个9分属性在4个方向上的和//3.计算4个方向上18分属性的和int i, j, ii, jj, k;int sizeX, sizeY, p, pp, xp, yp, pos1, pos2;float * newData;float val;float nx, ny;sizeX = map->sizeX;sizeY = map->sizeY;p = map->numFeatures;pp = NUM_SECTOR * 3 + 4;yp = 4;xp = NUM_SECTOR;nx = 1.0f / sqrtf( ( float ) ( xp * 2 ) );ny = 1.0f / sqrtf( ( float ) ( yp ) );newData = ( float * ) malloc( sizeof( float ) * ( sizeX * sizeY * pp ) );for( i = 0; i < sizeY; i++ ){for( j = 0; j < sizeX; j++ ){pos1 = ( ( i ) *sizeX + j )*p;//去掉边界后的第i行第j列的block的的第一个属性值的索引值pos2 = ( ( i ) *sizeX + j )*pp;//newData关于第i行第j列的block的的第一个属性值的索引值k = 0;for( jj = 0; jj < xp * 2; jj++ )//18分属性{val = 0;for( ii = 0; ii < yp; ii++ ){val += map->map[pos1 + yp * xp + ii * xp * 2 + jj];//计算每个block的18分属性在四个方向的和}/*for(ii = 0; ii < yp; ii++)*/newData[pos2 + k] = val * ny;k++;}/*for(jj = 0; jj < xp * 2; jj++)*/for( jj = 0; jj < xp; jj++ )//9分属性{val = 0;for( ii = 0; ii < yp; ii++ ){val += map->map[pos1 + ii * xp + jj];}/*for(ii = 0; ii < yp; ii++)*/newData[pos2 + k] = val * ny;k++;}/*for(jj = 0; jj < xp; jj++)*/for( ii = 0; ii < yp; ii++ ){val = 0;for( jj = 0; jj < 2 * xp; jj++ ){val += map->map[pos1 + yp * xp + ii * xp * 2 + jj];//计算每个block的18分属性在一个方向上的和,}/*for(jj = 0; jj < xp; jj++)*/newData[pos2 + k] = val * nx;k++;} /*for(ii = 0; ii < yp; ii++)*/}/*for(j = 0; j < sizeX; j++)*/}/*for(i = 0; i < sizeY; i++)*///swop datamap->numFeatures = pp;free( map->map );map->map = newData;return LATENT_SVM_OK;}//modified from "lsvmc_routine.cpp"int allocFeatureMapObject( CvLSVMFeatureMapCaskade **obj, const int sizeX,const int sizeY, const int numFeatures ){int i;( *obj ) = ( CvLSVMFeatureMapCaskade * ) malloc( sizeof( CvLSVMFeatureMapCaskade ) );( *obj )->sizeX = sizeX;( *obj )->sizeY = sizeY;( *obj )->numFeatures = numFeatures;( *obj )->map = ( float * ) malloc( sizeof( float ) *( sizeX * sizeY  * numFeatures ) );for( i = 0; i < sizeX * sizeY * numFeatures; i++ ){( *obj )->map[i] = 0.0f;}return LATENT_SVM_OK;}int freeFeatureMapObject( CvLSVMFeatureMapCaskade **obj ){if( *obj == NULL ) return LATENT_SVM_MEM_NULL;free( ( *obj )->map );free( *obj );( *obj ) = NULL;return LATENT_SVM_OK;}

kcftracker.cpp

#ifndef _KCFTRACKER_HEADERS#include "kcftracker.hpp"#include "ffttools.hpp"#include "recttools.hpp"#include "fhog.hpp"#include "labdata.hpp"#endif// Constructor/*根据配置选项初始化一些参数*/KCFTracker::KCFTracker( bool hog, bool fixed_window, bool multiscale, bool lab ){// Parameters equal in all caseslambda = 0.0001;//regularizationpadding = 2.5;//horizontal area surrounding the target, relative to its size//output_sigma_factor = 0.1;output_sigma_factor = 0.125;//bandwidth of gaussian targetif( hog ){    // HOG// VOTinterp_factor = 0.012;// linear interpolation factor for adaptationsigma = 0.6;//gaussian kernel bandwidth// TPAMI//interp_factor = 0.02;//sigma = 0.5; cell_size = 4;//HOG cell size_hogfeatures = true;if( lab ){interp_factor = 0.005;sigma = 0.4;//output_sigma_factor = 0.025;output_sigma_factor = 0.1;_labfeatures = true;_labCentroids = cv::Mat( nClusters, 3, CV_32FC1, &data );cell_sizeQ = cell_size*cell_size;}else{_labfeatures = false;}}else{   // RAWinterp_factor = 0.075;sigma = 0.2;cell_size = 1;_hogfeatures = false;if( lab ){printf( "Lab features are only used with HOG features.\n" );_labfeatures = false;}}if( multiscale ){ // multiscaletemplate_size = 96;//template_size = 100;scale_step = 1.05;scale_weight = 0.95;if( !fixed_window ){//printf("Multiscale does not support non-fixed window.\n");fixed_window = true;}}else if( fixed_window ){  // fit correction without multiscaletemplate_size = 96;//template_size = 100;scale_step = 1;}else{template_size = 1;scale_step = 1;}}/******************************************************************************函数功能:初始化跟踪器,包括回归参数的计算,变量的初始化(Initialize tracker)*函数参数:目标初始框的引用,初始帧、*****************************************************************************/void KCFTracker::init( const cv::Rect &roi, cv::Mat image ){_roi = roi;//_roi是基类Tracker的protected成员变量assert( roi.width >= 0 && roi.height >= 0 );_tmpl = getFeatures( image, 1 );// 获取特征,在train里面每帧修改_prob = createGaussianPeak( size_patch[0], size_patch[1] );     // 这个不修改了,只初始化一次   24 14_alphaf = cv::Mat( size_patch[0], size_patch[1], CV_32FC2, float( 0 ) );  // 获取特征,在train里面每帧修改//_num = cv::Mat(size_patch[0], size_patch[1], CV_32FC2, float(0));//_den = cv::Mat(size_patch[0], size_patch[1], CV_32FC2, float(0));train( _tmpl, 1.0 ); // train with initial frame}/******************************************************************************函数功能:获取当前帧的目标位置以及尺度(Update position based on the new frame)*函数参数:当前帧的整幅图像*****************************************************************************/cv::Rect KCFTracker::update( cv::Mat image ){if( _roi.x + _roi.width <= 0 ) _roi.x = -_roi.width + 1;//如果越界,就让框框保持在开始越界的地方if( _roi.y + _roi.height <= 0 ) _roi.y = -_roi.height + 1;if( _roi.x >= image.cols - 1 ) _roi.x = image.cols - 2;if( _roi.y >= image.rows - 1 ) _roi.y = image.rows - 2;//跟踪框中心float cx = _roi.x + _roi.width / 2.0f;float cy = _roi.y + _roi.height / 2.0f;float peak_value;    // 尺度不变时检测峰值结果cv::Point2f res = detect( _tmpl, getFeatures( image, 0, 1.0f ), peak_value );// 略大尺度和略小尺度进行检测if( scale_step != 1 ){// Test at a smaller _scale  使用较小的尺度进行检测float new_peak_value;cv::Point2f new_res = detect( _tmpl, getFeatures( image, 0, 1.0f / scale_step ), new_peak_value );// 做减益还比同尺度大就认为是目标 if( scale_weight * new_peak_value > peak_value ){res = new_res;peak_value = new_peak_value;_scale /= scale_step;_roi.width /= scale_step;_roi.height /= scale_step;}// Test at a bigger _scale 使用较大的尺度进行检测new_res = detect( _tmpl, getFeatures( image, 0, scale_step ), new_peak_value );if( scale_weight * new_peak_value > peak_value ){res = new_res;peak_value = new_peak_value;_scale *= scale_step;_roi.width *= scale_step;_roi.height *= scale_step;}}// Adjust by cell size and _scale_roi.x = cx - _roi.width / 2.0f + ( ( float ) res.x * cell_size * _scale );_roi.y = cy - _roi.height / 2.0f + ( ( float ) res.y * cell_size * _scale );if( _roi.x >= image.cols - 1 ) _roi.x = image.cols - 1;if( _roi.y >= image.rows - 1 ) _roi.y = image.rows - 1;if( _roi.x + _roi.width <= 0 ) _roi.x = -_roi.width + 2;if( _roi.y + _roi.height <= 0 ) _roi.y = -_roi.height + 2;assert( _roi.width >= 0 && _roi.height >= 0 );cv::Mat x = getFeatures( image, 0 );train( x, interp_factor );return _roi;}/******************************************************************************函数功能:根据上一帧结果计算当前帧的目标位置(Detect object in the current frame)*函数参数:之前训练(初始化)的结果,当前的特征图,当前最高得分(引用)*****************************************************************************/cv::Point2f KCFTracker::detect( cv::Mat z, cv::Mat x, float &peak_value ){using namespace FFTTools;// 做变换得到计算结果rescv::Mat k = gaussianCorrelation( x, z );// 计算x和z之间的高斯相关核(公式)cv::Mat res = ( real( fftd( complexMultiplication( _alphaf, fftd( k ) ), true ) ) );  // 计算目标得分(公式)// minMaxLoc only accepts doubles for the peak, and integer points for the coordinates// 使用opencv的minMaxLoc来定位峰值坐标位置cv::Point2i pi;double pv;cv::minMaxLoc( res, NULL, &pv, NULL, &pi );peak_value = ( float ) pv;// subpixel peak estimation, coordinates will be non-integer// 子像素峰值检测,坐标是非整形的cv::Point2f p( ( float ) pi.x, ( float ) pi.y );if( pi.x > 0 && pi.x < res.cols - 1 ){p.x += subPixelPeak( res.at<float>( pi.y, pi.x - 1 ), peak_value, res.at<float>( pi.y, pi.x + 1 ) );}if( pi.y > 0 && pi.y < res.rows - 1 ){p.y += subPixelPeak( res.at<float>( pi.y - 1, pi.x ), peak_value, res.at<float>( pi.y + 1, pi.x ) );}p.x -= ( res.cols ) / 2;p.y -= ( res.rows ) / 2;return p;}/******************************************************************************函数功能:根据每一帧的结果训练样本并更新模板(train tracker with a single image)*函数参数:新的目标图像,训练因子train_interp_factor是interp_factor*****************************************************************************/void KCFTracker::train( cv::Mat x, float train_interp_factor ){using namespace FFTTools;cv::Mat k = gaussianCorrelation( x, x );cv::Mat alphaf = complexDivision( _prob, ( fftd( k ) + lambda ) );  //计算岭回归系数(公式)// 更新模板的特征_tmpl = ( 1 - train_interp_factor ) * _tmpl + ( train_interp_factor ) * x;// 公式// 更新岭回归系数的值_alphaf = ( 1 - train_interp_factor ) * _alphaf + ( train_interp_factor ) * alphaf;    // 公式/*cv::Mat kf = fftd(gaussianCorrelation(x, x));cv::Mat num = complexMultiplication(kf, _prob);cv::Mat den = complexMultiplication(kf, kf + lambda);_tmpl = (1 - train_interp_factor) * _tmpl + (train_interp_factor) * x;_num = (1 - train_interp_factor) * _num + (train_interp_factor) * num;_den = (1 - train_interp_factor) * _den + (train_interp_factor) * den;_alphaf = complexDivision(_num, _den);*/}/******************************************************************************函数功能:使用带宽SIGMA计算高斯卷积核以用于所有图像X和Y之间的相对位移必须都是MxN大小。二者必须都是周期的(即,通过一个cos窗口进行预处理)Evaluates a Gaussian kernel with bandwidth SIGMA for all relative shifts between input images X and Y, which must both be MxN. They must also be periodic (ie., pre-processed with a cosine window)*函数参数:高斯核的两个参数*****************************************************************************/cv::Mat KCFTracker::gaussianCorrelation( cv::Mat x1, cv::Mat x2 ){using namespace FFTTools;cv::Mat c = cv::Mat( cv::Size( size_patch[1], size_patch[0] ), CV_32F, cv::Scalar( 0 ) );// HOG featuresif( _hogfeatures ){cv::Mat caux;cv::Mat x1aux;cv::Mat x2aux;for( int i = 0; i < size_patch[2]; i++ ){x1aux = x1.row( i );// Procedure do deal with cv::Mat multichannel bugx1aux = x1aux.reshape( 1, size_patch[0] );// 将第i个属性排列成原来cell的排列形式x2aux = x2.row( i ).reshape( 1, size_patch[0] );// 两个傅立叶频谱的每个元素的乘法 相乘-频谱// 输入数组1、输入数组2、输出数组(和输入数组有相同的类型和大小)cv::mulSpectrums( fftd( x1aux ), fftd( x2aux ), caux, 0, true );     // 核相关性公式caux = fftd( caux, true );rearrange( caux );caux.convertTo( caux, CV_32F );c = c + real( caux );}}else     // Gray features{cv::mulSpectrums( fftd( x1 ), fftd( x2 ), c, 0, true );c = fftd( c, true );rearrange( c );c = real( c );}cv::Mat d;cv::Mat testx1 = x1.mul( x1 );cv::Mat testx2 = x2.mul( x2 );cv::Scalar ax1 = cv::sum( testx1 );cv::Scalar ax2 = cv::sum( testx2 );cv::max( ( ( cv::sum( x1.mul( x1 ) )[0] + cv::sum( x2.mul( x2 ) )[0] ) - 2. * c ) / ( size_patch[0] * size_patch[1] * size_patch[2] ), 0, d );cv::Mat k;cv::exp( ( -d / ( sigma * sigma ) ), k );return k;}/******************************************************************************函数功能:创建高斯峰函数,仅在第一帧时被执行*Create Gaussian Peak. Function called only in the first frame*函数参数:二维高斯峰的X、Y的大小*****************************************************************************/cv::Mat KCFTracker::createGaussianPeak( int sizey, int sizex ){cv::Mat_<float> res( sizey, sizex );int syh = ( sizey ) / 2;int sxh = ( sizex ) / 2;float output_sigma = std::sqrt( ( float ) sizex * sizey ) / padding * output_sigma_factor;//?float mult = -0.5 / ( output_sigma * output_sigma );//?for( int i = 0; i < sizey; i++ ){for( int j = 0; j < sizex; j++ ){int ih = i - syh;int jh = j - sxh;res( i, j ) = std::exp( mult * ( float ) ( ih * ih + jh * jh ) );}}return FFTTools::fftd( res );}/******************************************************************************函数功能:提取目标窗口的特征(Obtain sub-window from image, with replication-padding and extract features)*函数参数:图像,是否使用汉宁窗,尺度调整因子*****************************************************************************/cv::Mat KCFTracker::getFeatures( const cv::Mat & image, bool inithann, float scale_adjust ){//步骤://1.根据给定的框框找到合适的框框//2.提取HOG特征//3.对特征进行归一化和截断//4.对特征进行降维//5.获取Lab特征,并将结果与hog特征进行连接//6.创建一个常数阵,对所有特征根据cell的位置进行加权?cv::Rect extracted_roi;//center point of ROIfloat cx = _roi.x + _roi.width / 2;float cy = _roi.y + _roi.height / 2;// 初始化hanning窗, 其实只执行一次,只在第一帧的时候inithann=1if( inithann ){int padded_w = _roi.width * padding;int padded_h = _roi.height * padding;if( template_size > 1 )                  // template_size=96{  // Fit largest dimension to the given template size// 按照长宽比例修改_tmpl长宽大小,保证比较大的边为template_size大小96if( padded_w >= padded_h ){//fit to width_scale = padded_w / ( float ) template_size;}else{_scale = padded_h / ( float ) template_size;}_tmpl_sz.width = padded_w / _scale;_tmpl_sz.height = padded_h / _scale;}else{  //No template size given, use ROI size_tmpl_sz.width = padded_w;_tmpl_sz.height = padded_h;_scale = 1;// original code from paper:/*if (sqrt(padded_w * padded_h) >= 100) {   //Normal size_tmpl_sz.width = padded_w;_tmpl_sz.height = padded_h;_scale = 1;}else {   //ROI is too big, track at half size_tmpl_sz.width = padded_w / 2;_tmpl_sz.height = padded_h / 2;_scale = 2;}*/}// 设置_tmpl_sz的长宽:向上取原来长宽的最小2*cell_size倍  // 其中,较大边长为104 if( _hogfeatures ){// Round to cell size and also make it even_tmpl_sz.width = ( ( ( int ) ( _tmpl_sz.width / ( 2 * cell_size ) ) ) * 2 * cell_size ) + cell_size * 2;_tmpl_sz.height = ( ( ( int ) ( _tmpl_sz.height / ( 2 * cell_size ) ) ) * 2 * cell_size ) + cell_size * 2;}else{  // Make number of pixels even (helps with some logic involving half-dimensions)_tmpl_sz.width = ( _tmpl_sz.width / 2 ) * 2;_tmpl_sz.height = ( _tmpl_sz.height / 2 ) * 2;}}// 以上都是调整_tmpl_sz的大小为了各种适应// 检测区域大小extracted_roi.width = scale_adjust * _scale * _tmpl_sz.width;extracted_roi.height = scale_adjust * _scale * _tmpl_sz.height;// center roi with new size 检测区域的左上角坐标 extracted_roi.x = cx - extracted_roi.width / 2;extracted_roi.y = cy - extracted_roi.height / 2;// 提取目标区域像素,超边界则做填充cv::Mat FeaturesMap;cv::Mat z = RectTools::subwindow( image, extracted_roi, cv::BORDER_REPLICATE );//检验extracted_roi似乎否在image范围内,若有超出部分,通过边界补全// 按照比例缩小边界大小if( z.cols != _tmpl_sz.width || z.rows != _tmpl_sz.height ){cv::resize( z, z, _tmpl_sz );}// HOG features 提取HOG特征点if( _hogfeatures ){IplImage z_ipl = z;                             // 之前的图像类CvLSVMFeatureMapCaskade *map;// 申请指针getFeatureMaps( &z_ipl, cell_size, &map );// 对map赋值,获取hog特征map为sizeX*sizeY*3*NUM_SECTOR  normalizeAndTruncate( map, 0.2f );// 对hog特征进行归一化和截断;结果由map指向,大小sizeX*sizeY*3*NUM_SECTOR*4,但是此时的sizeX和sizezY均比之前少2.PCAFeatureMaps( map );// 对HOG特征进行降维size_patch[0] = map->sizeY;// HOG特征的sizeYsize_patch[1] = map->sizeX;// HOG特征的sizeXsize_patch[2] = map->numFeatures;// HOG特征的特征个数FeaturesMap = cv::Mat( cv::Size( map->numFeatures, map->sizeX*map->sizeY ), CV_32F, map->map );  // Procedure do deal with cv::Mat multichannel bugFeaturesMap = FeaturesMap.t( );    // 288*31freeFeatureMapObject( &map );// Lab features// 我测试结果,带有Lab特征在一些跟踪环节效果并不好if( _labfeatures ){cv::Mat imgLab;cvtColor( z, imgLab, CV_BGR2Lab );unsigned char *input = ( unsigned char* ) ( imgLab.data );// Sparse output vectorcv::Mat outputLab = cv::Mat( _labCentroids.rows, size_patch[0] * size_patch[1], CV_32F, float( 0 ) );//每一列是一个cell的nClusters个属性int cntCell = 0;//代表的是除边界以外的cell的索引号// Iterate through each cellfor( int cY = cell_size; cY < z.rows - cell_size; cY += cell_size ){for( int cX = cell_size; cX < z.cols - cell_size; cX += cell_size ){//遍历除边界以外的cell,第cy行第cx列的cell// Iterate through each pixel of cell (cX,cY)//对每个cell的每个像素的lab值进行根据_labCentroids进行分类,分类标准:欧氏距离的平方for( int y = cY; y < cY + cell_size; ++y ){for( int x = cX; x < cX + cell_size; ++x ){//遍历cell中的每一个像素,第y行第x列的像素// Lab components for each pixelfloat l = ( float ) input[( z.cols * y + x ) * 3];//三个通道,分别代表LAB空间的三个值float a = ( float ) input[( z.cols * y + x ) * 3 + 1];float b = ( float ) input[( z.cols * y + x ) * 3 + 2];// Iterate trough each centroid(质心,矩心)float minDist = FLT_MAX;int minIdx = 0;float *inputCentroid = ( float* ) ( _labCentroids.data );for( int k = 0; k < _labCentroids.rows; ++k ){float dist = ( ( l - inputCentroid[3 * k] ) * ( l - inputCentroid[3 * k] ) )+ ( ( a - inputCentroid[3 * k + 1] ) * ( a - inputCentroid[3 * k + 1] ) )+ ( ( b - inputCentroid[3 * k + 2] ) * ( b - inputCentroid[3 * k + 2] ) );//好像是求的lab和某一个特定lab的距离if( dist < minDist ){minDist = dist;minIdx = k;}}// Store result at outputoutputLab.at<float>( minIdx, cntCell ) += 1.0 / cell_sizeQ;//((float*) outputLab.data)[minIdx * (size_patch[0]*size_patch[1]) + cntCell] += 1.0 / cell_sizeQ; }}cntCell++;}}// Update size_patch[2] and add features to FeaturesMapsize_patch[2] += _labCentroids.rows;FeaturesMap.push_back( outputLab );//将根据lab空间计算得到的结果和通过hog特征计算的结果进行合并}}else           //if not hog{FeaturesMap = RectTools::getGrayImage( z );FeaturesMap -= ( float ) 0.5;// In Paper;size_patch[0] = z.rows;size_patch[1] = z.cols;size_patch[2] = 1;}if( inithann ){createHanningMats( );// 创建了一个和FeatureMap大小相关的常数Mat(sizeX*sizezY)*size_patch[2]}FeaturesMap = hann.mul( FeaturesMap );// 点乘return FeaturesMap;}/******************************************************************************函数功能:初始化cosine window,仅在第一帧调用(Initialize Hanning window. Function called only in the first frame.)*函数参数:无*****************************************************************************/void KCFTracker::createHanningMats( ){cv::Mat hann1t = cv::Mat( cv::Size( size_patch[1], 1 ), CV_32F, cv::Scalar( 0 ) );cv::Mat hann2t = cv::Mat( cv::Size( 1, size_patch[0] ), CV_32F, cv::Scalar( 0 ) );for( int i = 0; i < hann1t.cols; i++ )hann1t.at<float >( 0, i ) = 0.5 * ( 1 - std::cos( 2 * 3.14159265358979323846 * i / ( hann1t.cols - 1 ) ) );for( int i = 0; i < hann2t.rows; i++ )hann2t.at<float >( i, 0 ) = 0.5 * ( 1 - std::cos( 2 * 3.14159265358979323846 * i / ( hann2t.rows - 1 ) ) );cv::Mat hann2d = hann2t * hann1t;// HOG featuresif( _hogfeatures ){cv::Mat hann1d = hann2d.reshape( 1, 1 ); // Procedure do deal with cv::Mat multichannel bughann = cv::Mat( cv::Size( size_patch[0] * size_patch[1], size_patch[2] ), CV_32F, cv::Scalar( 0 ) );for( int i = 0; i < size_patch[2]; i++ ){for( int j = 0; j < size_patch[0] * size_patch[1]; j++ ){hann.at<float>( i, j ) = hann1d.at<float>( 0, j );}}}// Gray featureselse{hann = hann2d;}}/******************************************************************************函数功能:对目标的位置插值,提高精度,计算一维亚像素峰值*使用幅值做差来定位峰值的位置,返回的是需要改变的偏移量大小*Calculate sub-pixel peak for one dimension*函数参数:无*****************************************************************************/float KCFTracker::subPixelPeak( float left, float center, float right ){float divisor = 2 * center - right - left;if( divisor == 0 )return 0;return 0.5 * ( right - left ) / divisor;}

kcftracker.hpp

#pragma once#include "tracker.h"#ifndef _OPENCV_KCFTRACKER_HPP_#define _OPENCV_KCFTRACKER_HPP_#endifclass KCFTracker : public Tracker{public:    // Constructor// Constructor  // 构造KCF跟踪器的类  KCFTracker( bool hog = true,                 // 使用hog特征  bool fixed_window = true,        // 使用固定窗口大小  bool multiscale = true,          // 使用多尺度  bool lab = true );               // 使用lab色空间特征    // Initialize tracker     virtual void init(const cv::Rect &roi, cv::Mat image);        // Update position based on the new frame    virtual cv::Rect update(cv::Mat image);    float interp_factor; // linear interpolation factor for adaptation 自适应的线性插值因子,会因为hog,lab的选择而变化    float sigma;         // gaussian kernel bandwidth 高斯(卷积)核带宽,会因为hog,lab的选择而变化    float lambda; // regularization 正则化 0.0001    int cell_size; // HOG cell size  HOG特征中元胞的大小 4    int cell_sizeQ; // cell size^2, to avoid repeated operations 元胞数组内像素数目,16,为了计算省事    float padding; // extra area surrounding the target  目标扩展区域,2.5    float output_sigma_factor; // bandwidth of gaussian target  高斯目标的带宽,不同hog,lab会不同    int template_size;   // template size 模板大小,在计算_tmpl_sz时,较大变成被归一成96,而较小边长按比例缩小    float scale_step;   // scale step for multi-scale estimation 多尺度估计的尺度步长    float scale_weight;   // to downweight detection scores of other scales for added stability 为了增加其他尺度检测时的稳定性,给检测结果峰值做一定衰减,为原来的0.95倍protected:    // Detect object in the current frame.    cv::Point2f detect(cv::Mat z, cv::Mat x, float &peak_value);    // train tracker with a single image    void train(cv::Mat x, float train_interp_factor);    // Evaluates a Gaussian kernel with bandwidth SIGMA for all relative shifts between input images X and Y, which must both be MxN. They must    also be periodic (ie., pre-processed with a cosine window).    cv::Mat gaussianCorrelation(cv::Mat x1, cv::Mat x2);    // Create Gaussian Peak. Function called only in the first frame.    cv::Mat createGaussianPeak(int sizey, int sizex);    // Obtain sub-window from image, with replication-padding and extract features    cv::Mat getFeatures(const cv::Mat & image, bool inithann, float scale_adjust = 1.0f);    // Initialize Hanning window. Function called only in the first frame.    void createHanningMats();    // Calculate sub-pixel peak for one dimension    float subPixelPeak(float left, float center, float right);cv::Mat _alphaf;            // 初始化/训练结果alphaf,用于检测部分中结果的计算  cv::Mat _prob;              // 初始化结果prob,不再更改,用于训练  cv::Mat _tmpl;              // 初始化/训练的结果,用于detect的z  cv::Mat _num;               // 貌似都被注释掉了  cv::Mat _den;               // 貌似都被注释掉了  cv::Mat _labCentroids;      // lab质心数组private:int size_patch[3];          // hog特征的sizeY,sizeX,numFeatures  cv::Mat hann;               // createHanningMats()的计算结果  cv::Size _tmpl_sz;          // hog元胞对应的数组大小  float _scale;               // 修正成_tmpl_sz后的尺度大小  int _gaussian_size;         // 未引用???  bool _hogfeatures;          // hog标志位  bool _labfeatures;          // lab标志位};