【OpenCV】显著区域检测

写在前面

最近在做毕设,题目是行人再识别。目前只是用行人区域的颜色直方图进行匹配,效果比较不理想。老板推荐加入显著性检测,然后就搞了搞。

环境

  • VS2015
  • OpenCV3.2

代码

Saliency_h

// Saliency.h: interface for the Saliency class.
//
//////////////////////////////////////////////////////////////////////
//===========================================================================
//  Copyright (c) 2009 Radhakrishna Achanta [EPFL] 
//===========================================================================

#if !defined(_SALIENCY_H_INCLUDED_)
#define _SALIENCY_H_INCLUDED_

#include 
#include 
using namespace std;


class Saliency
{
public:
    Saliency();
    virtual ~Saliency();

public:

    void GetSaliencyMap(
        const vector<unsigned int>&             inputimg,//INPUT: ARGB buffer in row-major order
        const int&                      width,
        const int&                      height,
        vector<double>&                 salmap,//OUTPUT: Floating point buffer in row-major order
        const bool&                     normalizeflag = true);//false if normalization is not needed


private:

    void RGB2LAB(
        const vector<unsigned int>&             ubuff,
        vector<double>&                 lvec,
        vector<double>&                 avec,
        vector<double>&                 bvec);

    void GaussianSmooth(
        const vector<double>&           inputImg,
        const int&                      width,
        const int&                      height,
        const vector<double>&           kernel,
        vector<double>&                 smoothImg);

    //==============================================================================
    /// Normalize
    //==============================================================================
    void Normalize(
        const vector<double>&           input,
        const int&                      width,
        const int&                      height,
        vector<double>&                 output,
        const int&                      normrange = 255)
    {
        double maxval(0);
        double minval(DBL_MAX);
        {int i(0);
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                if (maxval < input[i]) maxval = input[i];
                if (minval > input[i]) minval = input[i];
                i++;
            }
        }}
        double range = maxval - minval;
        if (0 == range) range = 1;
        int i(0);
        output.clear();
        output.resize(width*height);
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                output[i] = ((normrange*(input[i] - minval)) / range);
                i++;
            }
        }
    }
};

#endif // !defined(_SALIENCY_H_INCLUDED_)

Saliency_cpp

// Saliency.cpp: implementation of the Saliency class.
//
//////////////////////////////////////////////////////////////////////
//===========================================================================
//  Copyright (c) 2009 Radhakrishna Achanta [EPFL] 
//===========================================================================


#include "stdafx.h"
#include "Saliency.h"
#include 


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Saliency::Saliency()
{

}

Saliency::~Saliency()
{

}

//===========================================================================
/// RGB2LAB
//===========================================================================
void Saliency::RGB2LAB(
    const vector<unsigned int>&             ubuff,
    vector<double>&                 lvec,
    vector<double>&                 avec,
    vector<double>&                 bvec)
{
    int sz = int(ubuff.size());
    lvec.resize(sz);
    avec.resize(sz);
    bvec.resize(sz);

    for (int j = 0; j < sz; j++)
    {
        int r = (ubuff[j] >> 16) & 0xFF;
        int g = (ubuff[j] >> 8) & 0xFF;
        int b = (ubuff[j]) & 0xFF;

        double xval = 0.412453 * r + 0.357580 * g + 0.180423 * b;
        double yval = 0.212671 * r + 0.715160 * g + 0.072169 * b;
        double zVal = 0.019334 * r + 0.119193 * g + 0.950227 * b;

        xval /= (255.0 * 0.950456);
        yval /= 255.0;
        zVal /= (255.0 * 1.088754);

        double fX, fY, fZ;
        double lval, aval, bval;

        if (yval > 0.008856)
        {
            fY = pow(yval, 1.0 / 3.0);
            lval = 116.0 * fY - 16.0;
        }
        else
        {
            fY = 7.787 * yval + 16.0 / 116.0;
            lval = 903.3 * yval;
        }

        if (xval > 0.008856)
            fX = pow(xval, 1.0 / 3.0);
        else
            fX = 7.787 * xval + 16.0 / 116.0;

        if (zVal > 0.008856)
            fZ = pow(zVal, 1.0 / 3.0);
        else
            fZ = 7.787 * zVal + 16.0 / 116.0;

        aval = 500.0 * (fX - fY) + 128.0;
        bval = 200.0 * (fY - fZ) + 128.0;

        lvec[j] = lval;
        avec[j] = aval;
        bvec[j] = bval;
    }
}

//==============================================================================
/// GaussianSmooth
///
/// Blur an image with a separable binomial kernel passed in.
//==============================================================================
void Saliency::GaussianSmooth(
    const vector<double>&           inputImg,
    const int&                      width,
    const int&                      height,
    const vector<double>&           kernel,
    vector<double>&                 smoothImg)
{
    int center = int(kernel.size()) / 2;

    int sz = width*height;
    smoothImg.clear();
    smoothImg.resize(sz);
    vector<double> tempim(sz);
    int rows = height;
    int cols = width;
    //--------------------------------------------------------------------------
    // Blur in the x direction.
    //---------------------------------------------------------------------------
    {int index(0);
    for (int r = 0; r < rows; r++)
    {
        for (int c = 0; c < cols; c++)
        {
            double kernelsum(0);
            double sum(0);
            for (int cc = (-center); cc <= center; cc++)
            {
                if (((c + cc) >= 0) && ((c + cc) < cols))
                {
                    sum += inputImg[r*cols + (c + cc)] * kernel[center + cc];
                    kernelsum += kernel[center + cc];
                }
            }
            tempim[index] = sum / kernelsum;
            index++;
        }
    }}

    //--------------------------------------------------------------------------
    // Blur in the y direction.
    //---------------------------------------------------------------------------
    {int index = 0;
    for (int r = 0; r < rows; r++)
    {
        for (int c = 0; c < cols; c++)
        {
            double kernelsum(0);
            double sum(0);
            for (int rr = (-center); rr <= center; rr++)
            {
                if (((r + rr) >= 0) && ((r + rr) < rows))
                {
                    sum += tempim[(r + rr)*cols + c] * kernel[center + rr];
                    kernelsum += kernel[center + rr];
                }
            }
            smoothImg[index] = sum / kernelsum;
            index++;
        }
    }}
}

//===========================================================================
/// GetSaliencyMap
///
/// Outputs a saliency map with a value assigned per pixel. The values are
/// normalized in the interval [0,255] if normflag is set true (default value).
//===========================================================================
void Saliency::GetSaliencyMap(
    const vector<unsigned int>&     inputimg,
    const int&                      width,
    const int&                      height,
    vector<double>&                 salmap,
    const bool&                     normflag)
{
    int sz = width*height;
    salmap.clear();
    salmap.resize(sz);

    vector<double> lvec(0), avec(0), bvec(0);
    RGB2LAB(inputimg, lvec, avec, bvec);
    //--------------------------
    // Obtain Lab average values
    //--------------------------
    double avgl(0), avga(0), avgb(0);
    {for (int i = 0; i < sz; i++)
    {
        avgl += lvec[i];
        avga += avec[i];
        avgb += bvec[i];
    }}
    avgl /= sz;
    avga /= sz;
    avgb /= sz;

    vector<double> slvec(0), savec(0), sbvec(0);

    //----------------------------------------------------
    // The kernel can be [1 2 1] or [1 4 6 4 1] as needed.
    // The code below show usage of [1 2 1] kernel.
    //----------------------------------------------------
    vector<double> kernel(0);
    kernel.push_back(1.0);
    kernel.push_back(2.0);
    kernel.push_back(1.0);

    GaussianSmooth(lvec, width, height, kernel, slvec);
    GaussianSmooth(avec, width, height, kernel, savec);
    GaussianSmooth(bvec, width, height, kernel, sbvec);

    {for (int i = 0; i < sz; i++)
    {
        salmap[i] = (slvec[i] - avgl)*(slvec[i] - avgl) +
            (savec[i] - avga)*(savec[i] - avga) +
            (sbvec[i] - avgb)*(sbvec[i] - avgb);
    }}

    if (true == normflag)
    {
        vector<double> normalized(0);
        Normalize(salmap, width, height, normalized);
        swap(salmap, normalized);
    }
}

以上两个代码不需要改动,直接加入工程文件即可。

Main_cpp

// Main.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "Saliency.h"  

#include 
#include 

#include   

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    Saliency sal;

    Mat src = imread("./xr.jpg");

    if (src.empty()) return -1;

    vector<unsigned int >imgInput;
    vector<double> imgSal;

    //Mat to vector
    int nr = src.rows; // number of rows  
    int nc = src.cols; // total number of elements per line  
    if (src.isContinuous()) {
        // then no padded pixels  
        nc = nc*nr;
        nr = 1;  // it is now a 1D array  
    }

    for (int j = 0; j(j);
        for (int i = 0; iunsigned int t = 0;
            t += *data++;
            t <<= 8;
            t += *data++;
            t <<= 8;
            t += *data++;
            imgInput.push_back(t);

        }                
    }

    sal.GetSaliencyMap(imgInput, src.cols, src.rows, imgSal);

    //vector to Mat
    int index0 = 0;
    Mat imgout(src.size(), CV_64FC1);
    for (int h = 0; h < src.rows; h++) {
        double* p = imgout.ptr<double>(h);
        for (int w = 0; w < src.cols; w++) {
            *p++ = imgSal[index0++];
        }
    }
    normalize(imgout, imgout, 0, 1, NORM_MINMAX);

    imshow("原图像", src);
    imshow("显著性图像", imgout);

    waitKey(0);

    return 0;
}

说明

  1. 其中最重要的函数就是GetSaliencyMap,其输入和输出都为vector,所以重点是要将Mat转为vector
  2. 在原程序中有一段说明:
    // Assume we already have an unsigned integer buffer inputImg of
    // inputWidth and inputHeight (in row-major order).
    // Each unsigned integer has 32 bits and contains pixel data in ARGB
    // format. I.e. From left to right, the first 8 bits contain alpha
    // channel value and are not used in our case. The next 8 bits
    // contain R channel value; the next 8 bits contain G channel value;
    // the last 8 bits contain the B channel value.
    按照这段话,把Mat中的数据转成vector
  3. 但是我有一点不理解的地方:在使用了isContinuous()函数之后,注释说就变成了一维的数据??

结果

原图像

【OpenCV】显著区域检测_第1张图片

显著性区域

【OpenCV】显著区域检测_第2张图片

参考

  1. CSDN 国外的一个图像显著区域检测代码及其效果图 saliency region detection
    • 我的程序是照着这个程序改的,非常感谢原作者。因为我不想用IplImage类,所以沿用了该程序的思想,改成了Mat类。
  2. CSDN 访问Mat中每个像素的值(新)
    • 在由IplImage改为Mat的过程中,涉及很多基础知识,这个博主讲的非常详细。
  3. Github 我的工程代码
    • 工程文件。

你可能感兴趣的:(OpenCV)