Opencv HOG



很好的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);
    }
}





發佈了39 篇原創文章 · 獲贊 4 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章