反向投影直方图检测特定图像内容 - sumpig/OpenCV GitHub Wiki

首先装载彩色图像,定义ROI,然后计算经过缩减的色彩空间上的 3D 直方图,代码如下所示:

// 装载彩色图像
ColorHistogram hc;
cv::Mat color= cv::imread("waves.jpg");

// 提取ROI
imageROI= color(cv::Rect(0,0,100,45)); // 蓝色天空区域(感兴趣区域)

// 取得3D 颜色直方图(每个通道含8 个箱子)
hc.setSize(8); // 8×8×8
cv::Mat shist= hc.getHistogram(imageROI);

下一步是计算直方图,并用 find 方法检测图像中的天空区域:

// 创建内容搜寻器
ContentFinder finder;

// 设置用来反向投影的直方图
finder.setHistogram(shist);
finder.setThreshold(0.05f);

// 取得颜色直方图的反向投影
cv::Mat result = finder.find(color);
class ContentFinder {

    private:

        float hranges[2];
        const float* ranges[3];
        int channels[3];
        float threshold;           // 判断阈值
        cv::Mat histogram;         // 输入直方图

    public:

        ContentFinder() : threshold(0.1f) {

            ranges[0]= hranges;  
            ranges[1]= hranges; 
            ranges[2]= hranges; 
        }

        // 设置引用的直方图
        void setHistogram(const cv::Mat& h) {

            histogram= h;
            cv::normalize(histogram, histogram,1.0); // 归一化
        }

        void setThreshold(float t) {

            threshold= t;
	}

        // 使用全部通道,范围[0,256]
        cv::Mat find(const cv::Mat& image) {

            cv::Mat result;
            hranges[0]= 0.0; // 默认范围[0,256]
            hranges[1]= 256.0;
            channels[0]= 0; // 三个通道
            channels[1]= 1;
            channels[2]= 2;
            return find(image, hranges[0], hranges[1], channels);
        }

        // 查找属于直方图的像素
        cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels) {

            cv::Mat result;
            hranges[0]= minValue;
            hranges[1]= maxValue;

            // 直方图的维度数与通道列表一致
            for (int i=0; i<histogram.dims; i++)
                this->channels[i]= channels[i];

            cv::calcBackProject(&image, 1, // 只使用一幅图像
                                channels, // 通道
                                histogram, // 直方图
                                result, // 反向投影的图像
                                ranges, // 每个维度的值范围
                                255.0 // 选用的换算系数
                                // 把概率值从1 映射到255
            );
    

            // 对反向投影结果做阈值化,得到二值图像
            if (threshold>0.0)
                cv::threshold(result, result, 255.0*threshold, 255.0, cv::THRESH_BINARY);

            return result;
        }
};

原理

反向投影直方图的过程包括:从归一化后的直方图中读取概率值并把输入图像中的每个像素替换成与之对应的概率值。cv::calBackProject 函数可以完成这个任务。

cv::calBackProject 函数和 cv::calcHist 有些类似。一个像素的值对应直方图的一个箱子(可能是多维的)。但 cv::calBackProject 不会增加箱子的计数,而是把从箱子读取的值赋给反向投影图像中对应的像素。函数的第一个参数指明输入的图像(通常只有一个),接着需要指明使用的通道数量。这里传递给函数的直方图是一个输入参数,它的维度数要与通道列表数组的维度数一致。与cv::calcHist 函数一样,这里的 ranges 参数用数组形式指定了输入直方图的箱子边界。该数组以浮点数组为元素,每个数组元素表示一个通道的取值范围(最小值 和最大值)。

输出结果是一幅图像,包含计算得到的概率分布图。由于每个像素已经被替换成直方图中对应箱子处的值,因此输出图像的值范围是0.0~1.0(假定输入的是归一化直方图)。最后一个参数是换算系数,可用来重新缩放这些值。

本例中,计算稀疏直方图可以减少内存使用量。你可以使用 cv::SparseMat 重做该实验。另外,如果要寻找色彩鲜艳的物体,使用HSV 色彩空间的色调通道可能会更加有效。在其他情况下,最好使用感知上均匀的色彩空间(例如Lab*)的色度组件。