很好的Hog 博文 :http://www.cnblogs.com/tornadomeet/archive/2012/08/15/2640754.html
除了HOG,重點是滑動窗口和尺度變換兩部分
void HOGDescriptor::detectMultiScale(
const Mat& img, vector<Rect>& foundLocations,
double hitThreshold, Size winStride, Size padding,
double scale0, int groupThreshold) const
{
double scale = 1.;
foundLocations.clear();
int i, levels = 0;
const int maxLevels = 64;
int t, nthreads = getNumThreads();
vector<HOGThreadData> threadData(nthreads);
for( t = 0; t < nthreads; t++ )
threadData[t].smallerImgBuf.create(img.size(), img.type()); //爲什麼每個線程數據存放的img都是和圖像一樣大小呢?
vector<double> levelScale(maxLevels); // 記錄尺度縮放比例,這裏只是放大窗口,或者說縮小了圖像大小
for( levels = 0; levels < maxLevels; levels++ )
{
levelScale[levels] = scale;
if( cvRound(img.cols/scale) < winSize.width || // 等價於 img.cols < winSize.width*scale,這個條件是說,窗口大小大於了圖像大小。
cvRound(img.rows/scale) < winSize.height ||
scale0 <= 1 )
break;
scale *= scale0; // scale0 := 1.05 ,輸入值,按這個比例進行縮放
}
levels = std::max(levels, 1);
levelScale.resize(levels); // vector容器的一種操作
{// 用一組大括號比較好
#ifdef _OPENMP // 通過宏來控制更好,不僅僅要打開OpenMP,還需要宏的定義
#pragma omp parallel for num_threads(nthreads) schedule(dynamic)
#endif // _OPENMP
for( i = 0; i < levels; i++ )// 若是多線程,可按不同尺度並行計算,否則就是一個尺度一個尺度的計算
{
HOGThreadData& tdata = threadData[getThreadNum()]; // 獲取線程號;getNumThreads()爲獲取總線程數
double scale = levelScale[i];
Size sz(cvRound(img.cols/scale), cvRound(img.rows/scale)); // 圖像新的大小
Mat smallerImg(sz, img.type(), tdata.smallerImgBuf.data); // 暫且理解smallerImg與tdata.smallerImgBuf.data 共享同一個地址空間
if( sz == img.size() )
smallerImg = Mat(sz, img.type(), img.data, img.step); // 不用resize
else
resize(img, smallerImg, sz); // resize到指定的大小,這時就把img數據拷貝到了tdata.smallerImgBuf.data
detect(smallerImg, tdata.locations, hitThreshold, winStride, padding);
// 窗口大小映射到相應尺度下的窗口大小
Size scaledWinSize = Size(cvRound(winSize.width*scale), cvRound(winSize.height*scale));
// 記錄檢測到的矩形區域
for( size_t j = 0; j < tdata.locations.size(); j++ )
tdata.rectangles.push_back(Rect(
cvRound(tdata.locations[j].x*scale), // 左上角座標恢復到源大小圖像的位置
cvRound(tdata.locations[j].y*scale),
scaledWinSize.width, scaledWinSize.height));
}
}
// 將
for( t = 0; t < nthreads; t++ )
{
HOGThreadData& tdata = threadData[t];
std::copy(tdata.rectangles.begin(), tdata.rectangles.end(),//如果要把一個序列(sequence)拷貝到一個容器(container)中去,通常用std::copy算法
std::back_inserter(foundLocations)); // back_inserter是迭代器,將元素插入到容器尾部
}
groupRectangles(foundLocations, groupThreshold, 0.2);// 將重複的矩形框進行合併
}
//計算滑動窗口數量
Size HOGCache::windowsInImage(Size imageSize, Size winStride) const
{
return Size((imageSize.width - winSize.width)/winStride.width + 1,
(imageSize.height - winSize.height)/winStride.height + 1);
}
void HOGDescriptor::detect(const Mat& img,
vector<Point>& hits, double hitThreshold,
Size winStride, Size padding , const vector<Point>& locations/*默認創建一個空的vector*/) const
{
hits.clear();
if( svmDetector.empty() )
return;
if( winStride == Size() )
winStride = cellSize; // cellSize = 8*8 ,padding = 32*32
Size cacheStride(gcd(winStride.width, blockStride.width),
gcd(winStride.height, blockStride.height));
size_t nwindows = locations.size(); // nwindows = 0
padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); // 不理解。暫且理解爲擴充後的padding大小
padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height);
Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); // 擴充後的圖像大小
HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride);
if( !nwindows )
nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); // 這裏得到的就是滑動窗口的數量,具體windowsInImage函數;注意nWindows是size類型
const HOGCache::BlockData* blockData = &cache.blockData[0];
int nblocks = cache.nblocks.area();
int blockHistogramSize = cache.blockHistogramSize;
size_t dsize = getDescriptorSize();
double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0;
vector<float> blockHist(blockHistogramSize);
// 按每個滑動窗口進行計算
for( size_t i = 0; i < nwindows; i++ )
{
Point pt0;
if( !locations.empty() )
{
pt0 = locations[i];
if( pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width ||
pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height )
continue;
}
else
{
pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding);
CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0);
}
double s = rho;
const float* svmVec = &svmDetector[0];
int j, k;
for( j = 0; j < nblocks; j++, svmVec += blockHistogramSize )
{
const HOGCache::BlockData& bj = blockData[j];
Point pt = pt0 + bj.imgOffset;
const float* vec = cache.getBlock(pt, &blockHist[0]);
for( k = 0; k <= blockHistogramSize - 4; k += 4 )
s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] +
vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3];
for( ; k < blockHistogramSize; k++ )
s += vec[k]*svmVec[k];
}
if( s >= hitThreshold )
hits.push_back(pt0);
}
}