计算机视觉
图像处理

OpenCV—反向投影直方图检测特定图像内容

文章目录

Opencv计算机视觉编程攻略(第二版)在第4.5节介绍了反向投影直方图检测图像中的特定内容。本文为学习笔记、示例程序的实现方法以及自己的一些体会。本例的工程共包含3个头文件和一个源文件。3个头文件分别定义了1维直方图Histogram1D和3维直方图ColorHistogram操作方法,以及直方图检测方法ContentFinder的类。源文件为主程序。

  1. #include “histogram.h”               //定义1维灰度直方图的获取与绘制方法类:class Histogram1D  
  2. #include “colorhistogram.h”          //定义3维彩色直方图的获取与绘制方法类:class ColorHistogram   
  3. #include “contentFinder.h”           //定义反向投影直方图检测方法的类:class ContentFinder  

本例中使用的图像如下:

程序使用的图像(来自网络)

1.直方图操作方法

(1)1维灰度直方图操作方法

头文件histogram.h中定义了操作1维灰度直方图的方法类class Histogram1D。histogram.h代码如下:

  1. #if !defined HISTOGRAM  
  2. #define HISTOGRAM  
  3.   
  4. #include <opencv2\core\core.hpp>  
  5. #include <opencv2\imgproc\imgproc.hpp>  
  6.   
  7. // To create histograms of gray-level images  
  8. class Histogram1D { //将算法封装进类  
  9.   
  10.   private:  
  11.   
  12.     int histSize[1];         // number of bins in histogram直方图中箱子(bin)个数,[1]表示只有1维  
  13.     float hranges[2];    // range of values值范围,min和max共2个值,因此定义2维浮点数组  
  14.     const float* ranges[1];  // pointer to the different value ranges值范围的指针  
  15.     int channels[1];         // channel number to be examined要检查的通道数量  
  16.   
  17.   public:  
  18.   
  19.     Histogram1D() {  
  20.   
  21.         // Prepare default arguments for 1D histogram  
  22.         histSize[0]= 256;   // 256 bins,只有1维,因此通过[0]来设置该维的箱子数  
  23.         hranges[0]= 0.0;    // from 0 (inclusive)直方图取值范围的min  
  24.         hranges[1]= 256.0;  // to 256 (exclusive)直方图取值范围的max  
  25.         ranges[0]= hranges;   
  26.         channels[0]= 0;     // we look at channel 0,1维直方图暂时只看0通道  
  27.     }  
  28.   
  29.     // Sets the channel on which histogram will be calculated.  
  30.     // By default it is channel 0.设置通道的方法  
  31.     void setChannel(int c) {  
  32.   
  33.         channels[0]= c;  
  34.     }  
  35.   
  36.     // Gets the channel used.获取通道的方法  
  37.     int getChannel() {       
  38.   
  39.         return channels[0];  
  40.     }  
  41.   
  42.     // Sets the range for the pixel values.设置直方图值的范围  
  43.     // By default it is [0,256]  
  44.     void setRange(float minValue, float maxValue) {  
  45.   
  46.         hranges[0]= minValue;  
  47.         hranges[1]= maxValue;  
  48.     }  
  49.   
  50.     // Gets the min pixel value.  
  51.     float getMinValue() {  
  52.   
  53.         return hranges[0];  
  54.     }  
  55.   
  56.     // Gets the max pixel value.  
  57.     float getMaxValue() {  
  58.   
  59.         return hranges[1];  
  60.     }  
  61.   
  62.     // Sets the number of bins in histogram.设置直方图箱子数(统计多少个灰度级)  
  63.     // By default it is 256.构造函数中默认设置为256  
  64.     void setNBins(int nbins) {  
  65.   
  66.         histSize[0]= nbins;  
  67.     }  
  68.   
  69.     // Gets the number of bins in histogram.  
  70.     int getNBins() {  
  71.   
  72.         return histSize[0];  
  73.     }  
  74.   
  75.     // Computes the 1D histogram.自编函数计算1维直方图  
  76.     cv::Mat getHistogram(const cv::Mat &image) {//输入图像image  
  77.   
  78.         cv::Mat hist;  
  79.   
  80.         // Compute histogram  
  81.         cv::calcHist(&image,   
  82.             1,      // histogram of 1 image only  
  83.             channels,   // the channel used  
  84.             cv::Mat(),  // no mask is used,不使用掩码  
  85.             hist,       // the resulting histogram  
  86.             1,      // it is a 1D histogram  
  87.             histSize,   // number of bins  
  88.             ranges      // pixel value range  
  89.         );  
  90.   
  91.         return hist;  
  92.     }  
  93.   
  94.     // Computes the 1D histogram and returns an image of it.直方图图像  
  95.     cv::Mat getHistogramImage(const cv::Mat &image, int zoom = 1){  
  96.   
  97.         // Compute histogram first  
  98.         cv::Mat hist = getHistogram(image);  
  99.   
  100.         // Creates image  
  101.         return Histogram1D::getImageOfHistogram(hist, zoom);  
  102.     }  
  103.   
  104.     // Stretches the source image using min number of count in bins.  
  105.     cv::Mat stretch(const cv::Mat &image, int minValue = 0) {  
  106.   
  107.         // Compute histogram first  
  108.         cv::Mat hist = getHistogram(image);  
  109.   
  110.         // find left extremity of the histogram  
  111.         int imin = 0;  
  112.         for (; imin < histSize[0]; imin++) {  
  113.             // ignore bins with less than minValue entries  
  114.             if (hist.at<float>(imin) > minValue)  
  115.                 break;  
  116.         }  
  117.   
  118.         // find right extremity of the histogram  
  119.         int imax = histSize[0] – 1;  
  120.         for (; imax >= 0; imax–) {  
  121.   
  122.             // ignore bins with less than minValue entries  
  123.             if (hist.at<float>(imax) > minValue)  
  124.                 break;  
  125.         }  
  126.   
  127.         // Create lookup table  
  128.         int dims[1] = { 256 };  
  129.         cv::Mat lookup(1, dims, CV_8U);  
  130.   
  131.         for (int i = 0; i<256; i++) {  
  132.   
  133.             if (i < imin) lookup.at<uchar>(i) = 0;  
  134.             else if (i > imax) lookup.at<uchar>(i) = 255;  
  135.             else lookup.at<uchar>(i) = cvRound(255.0*(i – imin) / (imax – imin));  
  136.         }  
  137.   
  138.         // Apply lookup table  
  139.         cv::Mat result;  
  140.         result = applyLookUp(image, lookup);  
  141.   
  142.         return result;  
  143.     }  
  144.   
  145.     // Stretches the source image using percentile.  
  146.     cv::Mat stretch(const cv::Mat &image, float percentile) {  
  147.   
  148.         // number of pixels in percentile  
  149.         float number= image.total()*percentile;  
  150.   
  151.         // Compute histogram first  
  152.         cv::Mat hist = getHistogram(image);  
  153.   
  154.         // find left extremity of the histogram  
  155.         int imin = 0;  
  156.         for (float count=0.0; imin < histSize[0]; imin++) {  
  157.             // number of pixel at imin and below must be > number  
  158.             if ((count+=hist.at<float>(imin)) >= number)  
  159.                 break;  
  160.         }  
  161.   
  162.         // find right extremity of the histogram  
  163.         int imax = histSize[0] – 1;  
  164.         for (float count=0.0; imax >= 0; imax–) {  
  165.             // number of pixel at imax and below must be > number  
  166.             if ((count += hist.at<float>(imax)) >= number)  
  167.                 break;  
  168.         }  
  169.   
  170.         // Create lookup table  
  171.         int dims[1] = { 256 };  
  172.         cv::Mat lookup(1, dims, CV_8U);  
  173.   
  174.         for (int i = 0; i<256; i++) {  
  175.   
  176.             if (i < imin) lookup.at<uchar>(i) = 0;  
  177.             else if (i > imax) lookup.at<uchar>(i) = 255;  
  178.             else lookup.at<uchar>(i) = cvRound(255.0*(i – imin) / (imax – imin));  
  179.         }  
  180.   
  181.         // Apply lookup table  
  182.         cv::Mat result;  
  183.         result = applyLookUp(image, lookup);  
  184.   
  185.         return result;  
  186.     }  
  187.   
  188.     // static methods  
  189.   
  190.     // Create an image representing a histogram  
  191.     static cv::Mat getImageOfHistogram(const cv::Mat &hist, int zoom) {  
  192.   
  193.         // Get min and max bin values  
  194.         double maxVal = 0;  
  195.         double minVal = 0;  
  196.         cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);  
  197.   
  198.         // get histogram size  
  199.         int histSize = hist.rows;  
  200.   
  201.         // Square image on which to display histogram  
  202.         cv::Mat histImg(histSize*zoom, histSize*zoom, CV_8U, cv::Scalar(255));  
  203.   
  204.         // set highest point at 90% of nbins (i.e. image height)  
  205.         int hpt = static_cast<int>(0.9*histSize);  
  206.   
  207.         // Draw vertical line for each bin  
  208.         for (int h = 0; h < histSize; h++) {  
  209.   
  210.             float binVal = hist.at<float>(h);  
  211.             if (binVal>0) {  
  212.                 int intensity = static_cast<int>(binVal*hpt / maxVal);  
  213.                 cv::line(histImg, cv::Point(h*zoom, histSize*zoom),  
  214.                     cv::Point(h*zoom, (histSize – intensity)*zoom), cv::Scalar(0), zoom);  
  215.             }  
  216.         }  
  217.   
  218.         return histImg;  
  219.     }  
  220.   
  221.     // Equalizes the source image.  
  222.     static cv::Mat equalize(const cv::Mat &image) {  
  223.   
  224.         cv::Mat result;  
  225.         cv::equalizeHist(image,result);  
  226.   
  227.         return result;  
  228.     }  
  229.   
  230.     // Applies a lookup table transforming an input image into a 1-channel image  
  231.     static cv::Mat applyLookUp(const cv::Mat& image, // input image  
  232.       const cv::Mat& lookup) { // 1×256 uchar matrix  
  233.   
  234.       // the output image  
  235.       cv::Mat result;  
  236.   
  237.       // apply lookup table  
  238.       cv::LUT(image,lookup,result);  
  239.   
  240.       return result;  
  241.     }  
  242. };  
  243.   
  244. #endif  

(2)3维彩色直方图操作方法

头文件ColorHistogram.h中定义了操作彩色直方图的方法类class ColorHistogram。colorhistogram.h代码如下:

  1. #if !defined COLHISTOGRAM  
  2. #define COLHISTOGRAM  
  3.   
  4. #include <opencv2\core\core.hpp>  
  5. #include <opencv2\imgproc\imgproc.hpp>  
  6.   
  7. class ColorHistogram {  
  8.   
  9.   private:  
  10.   
  11.     int histSize[3];         // size of each dimension  
  12.     float hranges[2];    // range of values  
  13.     const float* ranges[3];  // array of ranges for each dimension  
  14.     int channels[3];         // channel to be considered  
  15.   
  16.   public:  
  17.   
  18.     ColorHistogram() {  
  19.   
  20.         // Prepare default arguments for a color histogram  
  21.         // each dimension has equal size and range  
  22.         histSize[0]= histSize[1]= histSize[2]= 256;  
  23.         hranges[0]= 0.0;    // BRG range from 0 to 256  
  24.         hranges[1]= 256.0;  
  25.         ranges[0]= hranges; // in this class,    
  26.         ranges[1]= hranges; // all channels have the same range  
  27.         ranges[2]= hranges;   
  28.         channels[0]= 0;     // the three channels   
  29.         channels[1]= 1;   
  30.         channels[2]= 2;   
  31.     }  
  32.   
  33.     // set histogram size for each dimension  
  34.     void setSize(int size) {  
  35.   
  36.         // each dimension has equal size   
  37.         histSize[0]= histSize[1]= histSize[2]= size;  
  38.     }  
  39.   
  40.     // Computes the histogram.  
  41.     cv::Mat getHistogram(const cv::Mat &image) {  
  42.   
  43.         cv::Mat hist;  
  44.   
  45.         // BGR color histogram  
  46.         hranges[0]= 0.0;    // BRG range  
  47.         hranges[1]= 256.0;  
  48.         channels[0]= 0;     // the three channels   
  49.         channels[1]= 1;   
  50.         channels[2]= 2;   
  51.   
  52.         // Compute histogram  
  53.         cv::calcHist(&image,   
  54.             1,      // histogram of 1 image only  
  55.             channels,   // the channel used  
  56.             cv::Mat(),  // no mask is used  
  57.             hist,       // the resulting histogram  
  58.             3,      // it is a 3D histogram  
  59.             histSize,   // number of bins  
  60.             ranges      // pixel value range  
  61.         );  
  62.         return hist;  
  63.     }  
  64.   
  65.     // Computes the histogram.  
  66.     cv::SparseMat getSparseHistogram(const cv::Mat &image) {  
  67.   
  68.         cv::SparseMat hist(3,        // number of dimensions  
  69.                        histSize, // size of each dimension  
  70.                        CV_32F);  
  71.   
  72.         // BGR color histogram  
  73.         hranges[0]= 0.0;    // BRG range  
  74.         hranges[1]= 256.0;  
  75.         channels[0]= 0;     // the three channels   
  76.         channels[1]= 1;   
  77.         channels[2]= 2;   
  78.   
  79.         // Compute histogram  
  80.         cv::calcHist(&image,   
  81.             1,      // histogram of 1 image only  
  82.             channels,   // the channel used  
  83.             cv::Mat(),  // no mask is used  
  84.             hist,       // the resulting histogram  
  85.             3,      // it is a 3D histogram  
  86.             histSize,   // number of bins  
  87.             ranges      // pixel value range  
  88.         );  
  89.         return hist;  
  90.     }  
  91.   
  92.     // Computes the 1D Hue histogram with a mask.  
  93.     // BGR source image is converted to HSV  
  94.     // Pixels with low saturation are ignored  
  95.     cv::Mat getHueHistogram(const cv::Mat &image,   
  96.                              int minSaturation=0) {  
  97.         cv::Mat hist;  
  98.   
  99.         // Convert to HSV colour space  
  100.         cv::Mat hsv;  
  101.         cv::cvtColor(image, hsv, CV_BGR2HSV);  
  102.   
  103.         // Mask to be used (or not)  
  104.         cv::Mat mask;  
  105.   
  106.         if (minSaturation>0) {  
  107.             // Spliting the 3 channels into 3 images  
  108.             std::vector<cv::Mat> v;  
  109.             cv::split(hsv,v);  
  110.   
  111.             // Mask out the low saturated pixels  
  112.             cv::threshold(v[1],mask,minSaturation,255,  
  113.                                  cv::THRESH_BINARY);  
  114.         }  
  115.   
  116.         // Prepare arguments for a 1D hue histogram  
  117.         hranges[0]= 0.0;    // range is from 0 to 180  
  118.         hranges[1]= 180.0;  
  119.         channels[0]= 0;    // the hue channel   
  120.   
  121.         // Compute histogram  
  122.         cv::calcHist(&hsv,   
  123.             1,      // histogram of 1 image only  
  124.             channels,   // the channel used  
  125.             mask,       // binary mask  
  126.             hist,       // the resulting histogram  
  127.             1,      // it is a 1D histogram  
  128.             histSize,   // number of bins  
  129.             ranges      // pixel value range  
  130.         );  
  131.         return hist;  
  132.     }  
  133.   
  134.     // Computes the 2D ab histogram.  
  135.     // BGR source image is converted to Lab  
  136.     cv::Mat getabHistogram(const cv::Mat &image) {  
  137.   
  138.         cv::Mat hist;  
  139.   
  140.         // Convert to Lab color space  
  141.         cv::Mat lab;  
  142.         cv::cvtColor(image, lab, CV_BGR2Lab);  
  143.   
  144.         // Prepare arguments for a 2D color histogram  
  145.         hranges[0]= 0;  
  146.         hranges[1]= 256.0;  
  147.         channels[0]= 1; // the two channels used are ab   
  148.         channels[1]= 2;   
  149.   
  150.         // Compute histogram  
  151.         cv::calcHist(&lab,   
  152.             1,          // histogram of 1 image only  
  153.             channels,       // the channel used  
  154.             cv::Mat(),      // no mask is used  
  155.             hist,           // the resulting histogram  
  156.             2,          // it is a 2D histogram  
  157.             histSize,       // number of bins  
  158.             ranges          // pixel value range  
  159.         );  
  160.         return hist;  
  161.     }  
  162. };  
  163. #endif  

2.计算反向投影直方图的方法

计算反向投影直方图的过程:

  • 从ROI区域的归一化的直方图中读取概率值
  • 把输入图像(代检测图)中的每一个像素替换成与之对应的归一化中方图中的概率值
  • 把替换成概率值(0~1)的像素值再从0~1映射到0~255
  • 灰度值越大的像素越有可能是ROI的成分。

本例中,将反向投影直方图封装成一个类ContentFinder,contentfinder.h代码如下:

  1. #if !defined OFINDER  
  2. #define OFINDER  
  3.   
  4. #include <opencv2\core\core.hpp>  
  5. #include <opencv2\imgproc\imgproc.hpp>  
  6.   
  7. class ContentFinder {  
  8.   
  9.   private:  
  10.   
  11.     // histogram parameters  
  12.     float hranges[2];  
  13.     const float* ranges[3];  
  14.     int channels[3];  
  15.   
  16.     float threshold;           // decision threshold  
  17.     cv::Mat histogram;         // histogram can be sparse 输入直方图  
  18.     cv::SparseMat shistogram;  // or not  
  19.     bool isSparse;  
  20.   
  21.   public:  
  22.   
  23.     ContentFinder() : threshold(0.1f), isSparse(false) {  
  24.   
  25.         // in this class,  
  26.         // all channels have the same range  
  27.         ranges[0]= hranges;    
  28.         ranges[1]= hranges;   
  29.         ranges[2]= hranges;   
  30.     }  
  31.      
  32.     // Sets the threshold on histogram values [0,1]  
  33.     void setThreshold(float t) {  
  34.   
  35.         threshold= t;  
  36.     }  
  37.   
  38.     // Gets the threshold  
  39.     float getThreshold() {  
  40.   
  41.         return threshold;  
  42.     }  
  43.   
  44.     // Sets the reference histogram  
  45.     void setHistogram(const cv::Mat& h) {  
  46.   
  47.         isSparse= false;  
  48.         cv::normalize(h,histogram,1.0);  
  49.     }  
  50.   
  51.     // Sets the reference histogram  
  52.     void setHistogram(const cv::SparseMat& h) {  
  53.   
  54.         isSparse= true;  
  55.         cv::normalize(h,shistogram,1.0,cv::NORM_L2);  
  56.     }  
  57.   
  58.     // All channels used, with range [0,256]  
  59.     cv::Mat find(const cv::Mat& image) {  
  60.   
  61.         cv::Mat result;  
  62.   
  63.         hranges[0]= 0.0;    // default range [0,256]  
  64.         hranges[1]= 256.0;  
  65.         channels[0]= 0;     // the three channels   
  66.         channels[1]= 1;   
  67.         channels[2]= 2;   
  68.   
  69.         return find(image, hranges[0], hranges[1], channels);  
  70.     }  
  71.   
  72.     // Finds the pixels belonging to the histogram  
  73.     cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels) {  
  74.   
  75.         cv::Mat result;  
  76.   
  77.         hranges[0]= minValue;  
  78.         hranges[1]= maxValue;  
  79.   
  80.         if (isSparse) { // call the right function based on histogram type  
  81.   
  82.            for (int i=0; i<shistogram.dims(); i++)  
  83.               this->channels[i]= channels[i];  
  84.   
  85.            cv::calcBackProject(&image,  
  86.                       1,            // we only use one image at a time  
  87.                       channels,     // vector specifying what histogram dimensions belong to what image channels  
  88.                       shistogram,   // the histogram we are using  
  89.                       result,       // the resulting back projection image  
  90.                       ranges,       // the range of values, for each dimension  
  91.                       255.0         // the scaling factor is chosen such that a histogram value of 1 maps to 255  
  92.            );  
  93.   
  94.         } else {  
  95.   
  96.            for (int i=0; i<histogram.dims; i++)  
  97.               this->channels[i]= channels[i];  
  98. //某对象的this指针,指向被调用函数所在的对象,此处对象为ContentFinder类  
  99.            //this->channels[i]即ContentFinder类的私有成员channels[3]  
  100.            //对ContentFinder类各成员的访问均通过this进行  
  101.            cv::calcBackProject(&image,  
  102.                       1,            // we only use one image at a time  
  103.                       channels,     // 向量表示哪个直方图维度属于哪个图像通道  
  104.                       histogram,    // 用到的直方图  
  105.                       result,       // 反向投影的图像  
  106.                       ranges,       // 每个维度值的范围  
  107.                       255.0         // 选用的换算系数  
  108.            );  
  109.         }  
  110.         // Threshold back projection to obtain a binary image阈值分割反向投影图像得到二值图  
  111.         if (threshold>0.0)// 设置的阈值>0时,才进行阈值分割  
  112.             cv::threshold(result, result, 255.0*threshold, 255.0, cv::THRESH_BINARY);  
  113.         return result;  
  114.     }  
  115. };  
  116. #endif  

3.提取感兴趣区域ROI的直方图

以上定义了直方图操作方法和反向投影直方图检测方法。下面将在main函数中调用上述方法来做反向投影直方图检测的实验。

要查找图像中特定的内容(例如在下图中检测出天空中的云彩),首先要选择一个包括所需样本的兴趣区域,即画一个矩形框选出ROI。

(1)读取图像

补充:imread(const string& filename, int flags=1)函数读取图像色彩空间参数flags:

  1. enum  
  2. {  
  3.     // 8bit, color or not  
  4.     IMREAD_UNCHANGED  =-1,  
  5.     // 8bit, gray  
  6.     IMREAD_GRAYSCALE  =0,  
  7.     // color  
  8.     IMREAD_COLOR      =1,  
  9.     // any depth,  
  10.     IMREAD_ANYDEPTH   =2,  
  11.     // any color  
  12.     IMREAD_ANYCOLOR   =4  
  13. };  

在main函数中运行程序主干。以灰度图的形式读取图像。

  1. // Read input image  
  2.     cv::Mat image= cv::imread(“f:\\images\\waves.jpg”,0);// /灰度图方式读取图像  
  3.     if (!image.data)  
  4.         return 0;   

(2)设置ROI

设置读取图像中的云层区域为ROI。

  1. // define image ROI  
  2. cv::Mat imageROI;  
  3. imageROI= image(cv::Rect(406,146,30,24)); // Cloud  region  
以灰度图方式读取原图与设置的ROI云彩区域

(3)获取ROI的直方图

调用Histogram1D类中的.getHistogram( )方法获取ROI的1维直方图

  1. // Find histogram of reference  
  2.     Histogram1D h;  
  3.     cv::Mat hist= h.getHistogram(imageROI);  
  4.     cv::namedWindow(“Reference Hist”);  
  5.     cv::imshow(“Reference Hist”,h.getHistogramImage(imageROI));  
  6.     waitKey(0);  

ROI区域直方图

(5)归一化ROI区域直方图

通过归一化直方图得到一个函数,该函数为特定强度的像素属于这个区域的概率:

cv::normalize(histogram,histogram,1.0);

实际上,归一化在反向计算投影类ContentFinder中进行,此处单独说明一下。归一化直方图后,直方图每个bin位置的值之和为1。因此,可以将直方图在灰度级n位置的值视为灰度级为n的像素属于此ROI区域的概率。例如,假设下图为某一区域的归一化的直方图,当进行直方图反向投影时,如果图像中有一个像素灰度值为31,那么这个像素是该感兴趣区域的概率为0.1;如果图像中有一个像素灰度值为33,那么这个像素是该感兴趣区域的概率为0.5;如果图像中有像素灰度值为31~34以外的值,那么这些像素是该感兴趣区域的概率为0,因为该ROI区域的归一化直方图只在31~34位置有不为0的值。

归一化直方图示例

4.调用反向投影直方图检测灰度图像

前文定义了1维直方图的操作方法Histogram1D类,定义了计算反向投影直方图的方法ContentFinder类,在main函数中读取了图像、设置好ROI,获取了ROI直方图后,就可以调用ContentFinder类来计算反向投影了。

注意为了能将处理过程的中间结果显示出来,设置阈值为-0.1<0,用ContentFinder类在find()方法中当设置的阈值<0时不进行阈值分割,因此输出的结果为灰度图像,便于显示观察。下面的代码先使用find()获取反向投影结果result1,然后使用result1.convertTo(tmp,CV_8U,-1.0,255.0);将result1转换为反向投影图像tmp。

  1. // Create the content finder  
  2.     ContentFinder finder;  
  3.   
  4.     // set histogram to be back-projected  
  5.     finder.setHistogram(hist);//将ROI直方图hist传入反向投影计算类ContentFinder finder  
  6.     finder.setThreshold(-1.0f);// /设置阈值为-0.1,float型。阈值<0,不使用find方法中的阈值分割,输出result为灰度图   
  7.   
  8.     // Get back-projection  
  9.     cv::Mat result1;  
  10.     result1= finder.find(image);  
  11.   
  12.     // Create negative image and display result  
  13.     cv::Mat tmp;  
  14.     result1.convertTo(tmp,CV_8U,-1.0,255.0);  
  15.     cv::namedWindow(“Backprojection result”);  
  16.     cv::imshow(“Backprojection result”,tmp);  

为了搞清楚convertTo()的操作,我们用如下代码扫描图像像素的方法读取result1中一个15×15矩形区域的值在屏幕上输出:

  1. if(1)//for debug  
  2.     {  
  3.         cv::namedWindow(“Backprojection result1”);  
  4.         cv::imshow(“Backprojection result1”,result1);  
  5.         cout<<“pixel vaule of result1(421,141)~(436,156),15×15 region”<<endl<<endl;  
  6.         for ( int j = 140;j<155;j++)//从第j=101行开始  
  7.         {  
  8.             uchar* data= result1.ptr<uchar>(j);   
  9.             for (int i=420;i<435;i++)  
  10.             {  
  11.                 cout<<(int)data[i]<<” “;//打印输出第j行的所有数据  
  12.             }  
  13.             cout<<endl;//输出1行后换行  
  14.         }  
  15.     }  

result1图像该区域的像素值输出结果如下图所示

图像result1从点(421,141)到点(436,156)的矩形区域的像素值

使用使用上述方法输出tmp图像同样位置的15×15矩形区域的像素值如下图所示。比较转换前后的图像,发现对源图中的像素逐点I(x,y)做了反向操作I(x,y)=255-I(x,y).

图像tmp从点(421,141)到点(436,156)的矩形区域的像素值

显示出反向前后的全图如下2图所示。

反向前的result1图像
反向后的tmp图像
刚才我们观察了反向投影的处理过程中的图像,对上述图像进行阈值分割即可得到检测结果,如下图所示。

阈值分割后的反向投影检测结果

5.调用反向投影直方图检测彩色图像

从上图检测结果中可以看到,本来是检测云彩区域,但得到的检测结果还包括了底部许多沙滩区域和部分海水区域。一个改进的方法就是使用彩色图像的直方图来进行反向投影检测。使用彩色信息检测需要用到前面提到的3维彩色直方图操作ColorHistogram类。在main函数中通过如下代码来进行彩色图像的反向投影直方图检测。

  1. // Load color image  
  2.     ColorHistogram hc; //声明一个彩色直方图类hc用于下面获取ROI直方图的操作  
  3.     cv::Mat color= cv::imread(“f:\\images\\waves.jpg”);//这里要使用本机的图像路径  
  4.   
  5.     // extract region of interest  
  6.     imageROI= color(cv::Rect(406,154,30,24)); //设置检测云彩区域  
  7.   
  8.     // Get 3D colour histogram (8 bins per channel)  
  9.     hc.setSize(8); // 8x8x8 降低bin的数量使用8,16,64分别计算,结果如下图所示。  
  10.     cv::Mat shist= hc.getHistogram(imageROI);  
  11.   
  12.     // set histogram to be back-projected  
  13.     finder.setHistogram(shist);  
  14.     finder.setThreshold(0.05f);  
  15.   
  16.     // Get back-projection of color histogram  
  17.     result1= finder.find(color);  
  18.     cv::namedWindow(“Color Detection Result”);  
  19.     cv::imshow(“Color Detection Result”,result1);  

下图是输入的彩色图像与ROI检测区域

彩色图像与选择的ROI区域

在程序中,可以通过hc.setSize()来设置直方图中每个通道bin个数,本实验中,选择了8/16/64三个参数,检测结果如下图所示。从检测结果可以看出来,使用彩色直方图后,检测效果大大优于灰度直方图,并且连水中云的倒影都能检测出来。而设置的bin个数越多,检测精度越高。并且本例中使用16个bin得到的检测结果优于8和64个bin,因为精度过高将排除掉部分与ROI区域类似的云彩区域,所以bin不是越多越好,合适的bin的个数需要根据实际情况而定。

彩色图检测结果(8个bin,出现了少部分沙滩)

彩色图检测结果(16个bin,基本检测出了全部的云彩)

彩色图检测结果(64个bin,有部分云彩漏检)

计算稀疏直方图可以减少内存使用量。可以使用cv::SparseMat重做本实验。

如果将RGB色彩空间转换为Lab色彩空间或HSV色彩空间。如果寻找色彩鲜艳的物体,使用HSV色彩空间的色调通道可能会更有效。在其他情况下,最好使用感知上均匀的Lab色彩空间的色度组件。色彩空间转换代码如下:

  1. // Convert to Lab space  
  2.     cv::Mat lab;  
  3.     cv::cvtColor(color, lab, CV_BGR2Lab);  
  4.   
  5.     // Convert to HSV space  
  6.     cv::Mat hsv;  
  7.     cv::cvtColor(color, hsv, CV_BGR2HSV);  

检测结果如下图所示。

LAB彩色模式检测结果(8个bin)

HSV彩色模式检测结果(8个bin)

转载注明来源:CV视觉网 » OpenCV—反向投影直方图检测特定图像内容

分享到:更多 ()
扫描二维码,给作者 打赏
pay_weixinpay_weixin

请选择你看完该文章的感受:

0不错 0超赞 0无聊 0扯淡 0不解 0路过

评论 抢沙发

评论前必须登录!