OpenCV各模块函数使用实例(9)--直方图和目标检测(Histograms and Object Detection)

本节介绍opencv中利用直方图进行图像分类的函数和方法。其中包括直方图的计算和比较操作。在彩色图像中使用hsv格式对图像进行变换,排除因为光照产生的像素值偏差。使用hsv格式能真正体现出图像的像素灰度分布,当把灰度直方图看成图像的概率分布时,利用概率分类函数和算法可以对图像进行统计意义上的分类研究。

(1)、void cv::calcHist(const Mat * images, int nimages, const int * channels, InputArray mask,

                                       OutputArray hist, int dims, const int * histSize, const float ** ranges,

                                        bool uniform = true,  bool accumulate = false)

             void cv::calcHist (const Mat *images, int nimages, const int *channels, 

                                        InputArray mask, SparseMat &hist, int dims, const int *histSize,

                                        const float **ranges, bool uniform=true, bool accumulate=false)

void cv::calcHist (InputArrayOfArrays images, const std::vector< int > &channels,

                            InputArray mask, OutputArray hist, const std::vector< int > &histSize,

                            const std::vector< float > &ranges, bool accumulate=false)

计算数组集的直方图。函数cv::calcHist 计算一个或多个数组的直方图。用于增加直方图组矩(histogram bin)的元组元素是从对应的输入数组集的同一个数组中取得的。下面的示例给出了怎样计算2D彩色图像的色调-饱和度(Hue-Saturation)直方图的方法:

#include <opencv2/imgproc.hpp>

#include <opencv2/highgui.hpp>

using namespace cv;

int main( int argc, char** argv )

{

        Mat src, hsv;

        if( argc != 2 || !(src=imread(argv[1], 1)).data )

                return -1;

        cvtColor(src, hsv, COLOR_BGR2HSV); //转换图像格式

        //量化色调(hue)为30级,饱和度(saturation)为32

        int hbins = 30, sbins = 32;

        int histSize[] = {hbins, sbins};//形成直方图轴坐标标度

        //色调范围是0 179,参见cvtColor,彩色格式设定的标度范围,组矩则是将这个范

        //围进行划分,本例划分为30

        float hranges[] = { 0, 180 };

        //饱和度范围0(--)255(纯光照色),饱和度格式设定的标度范围,设定的组矩

        //将这个范围划分为32段,每段为8,即直方图坐标轴s的标度为0,8,16,24,32

        float sranges[] = { 0, 256 };

        const float* ranges[] = { hranges, sranges };

        MatND hist;

        //从第0和第1通道计算直方图

        int channels[] = {0, 1};

        calcHist( &hsv, 1, channels, Mat(), // 不使用屏蔽

                        hist, 2, histSize, ranges,

                        true, // 直方图归一化

                        false );

        double maxVal=0;

        minMaxLoc(hist, 0, &maxVal, 0, 0);

        int scale = 10;

        Mat histImg = Mat::zeros(sbins*scale, hbins*10, CV_8UC3);

        for( int h = 0; h < hbins; h++ )

                for( int s = 0; s < sbins; s++ ){

                        float binVal = hist.at<float>(h, s);

                        int intensity = cvRound(binVal*255/maxVal);

                        rectangle( histImg, Point(h*scale, s*scale),

                                       Point((h+1)*scale - 1, (s+1)*scale - 1),

                                        Scalar::all(intensity), -1 );

                }

        namedWindow( "Source", 1 );

        imshow( "Source", src );

        namedWindow( "H-S Histogram", 1 );

        imshow( "H-S Histogram", histImg );

        waitKey();

}

参数

images

源图像集。应该有相同的深度,CV_8U,CV_16U或CV_32F,以及相同的尺寸。每一个数组都可以有不同的通道数。

nimages

源图像数(第一个参数中包含的图像个数)。

channels

数组通道数列表,用于计算直方图。第一个数组的通道数是从0到 images[0].channels()-1,第二个数组的通道数则从images[0].channels()到images[0].channels() + images[1].channels()-1,以此类推。

mask

可选的屏蔽数组。如果这个矩阵不空,它必须是一个8-bit数组且与images[i]同尺寸。这个数组的非0屏蔽元素标记对应数组元素在直方图中的计数。

hist

输出的直方图,它是一个致密或稀松的多维数组。

dims

直方图的维数,必须为正值且不大于CV_MAX_DIMS(当前opencv版本下等于32)。

histSize

每一维上数组直方图的尺寸(histogram bin,组矩)。

ranges

每一维上直方图组矩范围的维度数组。在直方图归一化的时候(uniform =true),对每一个维度i ,它足够指定下界(包含)L0为0-th直方图组矩及上界(不包含)UhistSize[i]−1为最后一个直方图组矩histSize[i]-1。即,在归一化直方图时,每一个ranges[i]都是一个2元素的数组。在直方图非归一化(uniform=false)时,每一个ranges[i]包含有histSize[i]+1个元素:L0,U0=L1,U1=L2,…,UhistSize[i]−2=LhistSize[i]−1,UhistSize[i]−1。没有落在L0和UhistSize[i]−1之间的数组元素不被计数到直方图。

uniform

标志,表示直方图是否要归一化(参见上面)。

accumulate

累加标志。如果设置,开始分配的直方图不被清空。这个特征允许在几个数组集上计算单一直方图,或随时更新直方图。

(2)、void cv::calcBackProject (const Mat *images, int nimages, const int *channels, 

                                        InputArray hist, OutputArray backProject, const float **ranges,

                                        double scale=1, bool uniform=true)

             void cv::calcBackProject (const Mat *images, int nimages, const int *channels,

                                        const SparseMat &hist, OutputArray backProject, const float **ranges,

                                        double scale=1, bool uniform=true)

             void cv::calcBackProject (InputArrayOfArrays images,

                                        const std::vector<int> &channels, InputArray hist, OutputArray dst,

                                        const std::vector< float > &ranges, double scale)

计算直方图的反映射。函数cv::calcBackProject计算直方图的反映射。即,类似于calcHist,在输入图像的每个像素位置(x, y)函数都采集选中通道的值,并查找对应的直方图组矩。不是增加组矩的值,而是相反,函数读取组矩的值,使用scale参数变比,然后存储到backProject(x,y)。从统计意义上讲,这个函数用直方图表示的实际概率分布计算每一个像素值的概率。参见示例,可以看到追踪场景中亮色目标的用途:

  • 追踪前,将目标显示到照相机,使其差不多覆盖整个帧。计算色调(Hue)的直方图。这个直方图可能由极强的最大值对应于目标的彩色区域。
  • 追踪过程中,使用预先计算的直方图计算每一个输入视频帧的色调(hue)平面反映射。阈值操作抑制弱彩色。这也可能同时抑制场景中的不充分的色彩饱和度像素和太暗或太亮的像素。
  • 在结果图象中查找并选择连通域,如,选择最大联通域。.

这是一种相机移动中彩色目标跟踪的粗略算法。

参数

images

源数组。它们全部都应该有相同的深度,CV_8U,CV_16U或CV_32F,以及相同的尺寸。每一个都可以有不同的通道数。

nimages

源图像个数。

channels

用于计算反映射的通道列表。通道数必须与直方图的维度匹配。第一个数组的通道计数从0开始到images[0].channels()-1,第二数组的通道计数从images[0].channels()到images[0].channels() + images[1].channels()-1,于此类推。

hist

输入的直方图,可以是致密或稀松的。

backProject

目的反映射数组,可以是与images[0]有相同尺寸和深度的单通道数组。

ranges

直方图每一维度上的组矩界数组的列表。见calcHist的说明。

scale

可选的输出反映射比例因子。

uniform

标志,表示直方图是否是归一化的。

参见

calcHistcompareHist

(3)、double cv::compareHist (InputArray H1, InputArray H2, int method)

                       double cv::compareHist (const SparseMat &H1, const SparseMat &H2, int method)

比较两个直方图。函数cv::compareHist使用指定算法比较两个致密或稀松直方图,并返回 d(H1,H2) 。对于1-, 2-, 3-维致密直方图这个函数能工作的很好,但是对于高维稀松直方图,它就不太适合了。对于这样的直方图,由于混叠和采样问题,非零直方图组矩可能稍稍有点移位。要比较这样的直方图或通常的稀松结构权重点集,应考虑使用EMD函数。

参数

H1

要比较的第一个直方图。

H2

第二个比较直方图,与第一个H1有相同的尺寸。

Method

比较算法,参见HistCompMethods

(4)、Ptr<CLAHE> cv::createCLAHE (double clipLimit=40.0, Size tileGridSize=Size(8, 8))

建立一个指向cv::CLAHE类的智能指针,并初始化这个类。

参数

clipLimit

对比度限制阈值。

tileGridSize

均衡直方图的栅格尺寸。输入图像将被划分成等尺寸的矩形瓦块。tileGridSize定义了行列的瓦块数。

注:CLAHE是限制对比度适配直方图均衡化类( Contrast Limited Adaptive Histogram Equalization)。

(5)、float cv::EMD (InputArray signature1, InputArray signature2,

                                int distType, InputArray cost=noArray(), float *lowerBound=0,

                                OutputArray flow=noArray())

             float cv::wrapperEMD (InputArray signature1, InputArray signature2, int distType,

                                InputArray cost=noArray(), Ptr<float> lowerBound=Ptr<float>(),

                                OutputArray flow=noArray())

在两个权重点集结构之间计算最小工作距离(运输距离)。这个函数在两个权重点集结构之间计算移动的地表距离或下限距离。其应用之一是图像检索中的多维直方图比较。EMD本身是一个运输问题,用于求解下限成本的某些修正的简单算法。因而,其复杂性在最坏情况下是指数级的。但是,平均来讲,它是足够快的。 在实际操作中下界的计算甚至可以更快(使用线性时间算法),并且还可以用来大概确定是否两个标记集距离足够远,以致于它们所表示的对象不可能相关。

参数

signature1

第一个标记,size1×dims+1 浮点矩阵。每行存储点的权重和点的坐标。如果使用用户定义的cost矩阵,这个矩阵可以允许为单列形式(仅有权重)。权重必须是非负的并且至少一个非0值。

signature2

第二个标记,与signature1同格式,行数可以不同。总体权重可以是不同的。此时在signature1或signature2中添加假点。权重必须非负且至少有一个非0值。

distType

使用的标度。见 DistanceTypes.

cost

用户定义的size1×size2 成本矩阵。另,如果使用成本矩阵,下界参数lowerBound 就不能计算给出,因为这需要一个标尺。

lowerBound

可选的输入输出参数:在两个标记集之间的距离下界,这个距离是质心距离。如果使用用户定义的成本矩阵,或如果标记仅由权重组成(标记矩阵是单列的),这个下界就不能被计算给出,因为点集结构的总体权重是不相等的,必须自己初始化lowerBound。如果在质心之间计算的距离大于或等于lowerBound (也就是说标记集足够远),这个函数就不能计算EMD。在任何时候lowerBound都被设置成用来计算质心之间的距离的。因此,如果想要计算质心之间距离和EMD,lowerBound 就应该设置到0。

flow

合成的size1×size2 流矩阵:flowi,j 是一个从signature1的i 点流到signature2的j 点的流量。

(6)、void cv::equalizeHist (InputArray src, OutputArray dst)

均衡化灰度图像的直方图。这个函数使用如下算法均衡化输入图像的直方图:

  • 计算源图像的直方图H
  • 标准化直方图,使直方图组矩的和为255
  • 计算直方图的积分:

                ​​​​​​​        ​​​​​​​         

  • 使用变换图像,查表计算:

 这个算法归一化了亮度,并且增加了图像的对比度。

参数

src

源图像,8位单通道

dst

目的图像,与源图像同类型和尺寸

(7)、void cv::matchTemplate (InputArray image, InputArray templ,

                                        OutputArray result, int method, InputArray mask=noArray())

比较与模板重叠的图像区域。这个函数使模板滑过图像,使用指定的算法比较与模板重叠的尺寸为w×h 的图像区域,并存储结果到result中。这是比较的算法公式(I表示图像,T 模板,R 结果)。在模板和图像块上求和:x=0...w1,y=0...h1

函数完成比较后,最好的匹配就可以使用minMaxLoc 函数找出,全程最小(当使用TM_SQDIFF) 或最大(当时用TM_CCORR  TM_CCOEFF )。对于彩色图像,模板的和作为分子而总和作为分母在所有通道上执行操作,各自的均值用于各自的通道。即,这个函数可以操作彩色模板和彩色图像。其结果仍然是单通道的图像,这更易于图像分析。

参数

image

进行搜索的图像。必须是8位或32位浮点类型

templ

搜索模板。必须不大于源图像并且有相同数据类型

result

比较结果图。必须是32位单通道浮点类型。如果图像是W×H 而模板是w×h ,则结果为(W−w+1)×(H−h+1) 。

method

指定比较算法的参数,见TemplateMatchModes

mask

搜索模板的屏蔽。必须与模板templ有相同的数据类型和尺寸。默认是不需要设置的。当前,仅支持TM_SQDIFF 和TM_CCORR_NORMED 算法。

直方图和目标检测(Histograms and Object Detection)编程实例

图像的直方图反映的是图像色彩和饱和度(灰度)的分布情况,也称作灰度的概率分布,根据设定的组矩(histogram bin)对图像灰度分布进行统计。操作直方图然后回应射到源图,可获得一定色彩或亮度(灰度)范围内的图像属性,并过滤掉不相关的色彩灰度值,从而达到提取图像特征(特殊区域)对图像进行分类或运动跟踪的目的。也可以用于识别色彩相关的图像类。注意:直方图的组矩是对图像进行分类的关键。组矩可以理解为对图像灰度的分割方法。可以使用线性函数分割,也可以使用其他非线性函数。

1void cv::calcHist(const Mat * images, int nimages, const int * channels,

                                InputArray mask, OutputArray hist, int dims,

                                const int * histSize, const float ** ranges,

                                bool uniform = true,  bool accumulate = false)

     void cv::calcHist (const Mat *images, int nimages, const int *channels, 

                                InputArray mask, SparseMat &hist, int dims,

                                const int *histSize, const float **ranges,

                                bool uniform=true, bool accumulate=false)

      void cv::calcHist (InputArrayOfArrays images, const std::vector< int > &channels,

                                InputArray mask, OutputArray hist, const std::vector< int > &histSize,

                                const std::vector< float > &ranges, bool accumulate=false)

计算给定图像设定参数的直方图。其中给定图像是一个图像列表,可以是一个图像或多个图像的。输出为全部图像统计的直方图,根据通道、维数和尺寸范围,计算图像指定通道的像素值统计数。参数nimages指定输入的图像数,channels指定各图像使用的通道号,dims指定直方图的维数(使用图像的通道数,每一个计算直方图的图像都是用相同的通道个数,可以是不同的通道),histSizeranges指定了直方图中每一维的尺寸和取值范围(histSize为组矩histogram bin的个数,ranges为划分组矩的数据范围,如0--255灰度、0--179色度等,ranges被线性等分成histSize份,或非线性划分成histSize,每一个组矩bin指定一个数据区间,在此数据区间内的像素值都属于同一个组矩)。对于变通道或非线性组矩,使用重载的矢量函数指定各参数。下面图像显示rgb图的直方图和hsv图的直方图(一维方式):

 

源图和灰度图。

 

RGB各通道的直方图,组矩为32,范围256。

 HSV格式各通道直方图,其中h通道组矩为32,范围为180,其它通道组矩为32,范围256。

对于二维直方图则是组合两个通道后的图像计算直方图。

上三为RGB图像的RG、RB、GB组合的二维直方图,下三为HSV图像的HS、HV、SV组合的二维直方图(坐标为2维,灰度值为组矩值)。对于3维直方图(RGB或HSV)也可以计算,只是不能直接图像表述(空间位置的统计值是4维)。

2void cv::calcBackProject (const Mat *images, int nimages, const int *channels, 

                                InputArray hist, OutputArray backProject, const float **ranges,

                                double scale=1, bool uniform=true)

      void cv::calcBackProject (const Mat *images, int nimages, const int *channels,

                                const SparseMat &hist, OutputArray backProject, const float **ranges,

                                double scale=1, bool uniform=true)

      void cv::calcBackProject (InputArrayOfArrays images,

                                const std::vector<int> &channels, InputArray hist, OutputArray dst,

                                const std::vector< float > &ranges, double scale)

用指定的直方图反映射到图像,以直方图中的组矩值过滤图像素点,找出与直方图指定的分布概率像素区域作为活动区域。

 用于定位快递图标的反映射,左图为源图,中间为反映射图,右侧为快递图标的截图。使用图标截图生成直方图,使用此直方图对源图进行反映射,然后根据图标模板定位图标窗口位置。反映射指定屏蔽参数h[10,80],s[0,256],v[0,256]。其中h为hue参数,使用图像的颜色值过滤图像。在视频跟踪时,对目标生成一个直方图后,可直接在后续的视频帧中用于反映射获取目标在视频帧中的位置。要想定位准确,一是对目标颜色进行精准设定H[L,U],一是设定目标的矩形范围,直方图反映射是根据颜色值过滤图像,然后用目标矩形范围检索映射图像,找到在矩形范围内像素个数的最大值框位置,计算转角矩形。如图:

 上图中分别是:源图,定位图(结果图),直方图反映射图,直方图及采集直方图使用的图,反映射结果的源图和根据HSV格式限定值生成的屏蔽图(mask)。反映射结果图是反映射源图“与”上屏蔽图获得。根据反映射结果图,使用跟踪窗(设定矩形)使用函数CamShift()

获得,将返回的转角矩形的外切椭圆绘制到源图。使用同一个直方图数据,可以对同类型的多张图像(帧)进行处理。

3double cv::compareHist (InputArray H1, InputArray H2, int method)

                       double cv::compareHist (const SparseMat &H1, const SparseMat &H2, int method)

对两个图像的直方图做相似度比较,用以确定两个图像在像素统计意义上的相似度。比如图像的放大缩小后的图像,经过几何变换后的图像等。

 同一张图的直方图比较,可以看出各算法的比较结果,Correl(相关性比较),接近于1或-1则相似性好。Chisqr(卡方比较),接近于0则相似性好,Intersect(交叉比较),等于取直方图的最小值的和,相当于直方图最小值的积分,与源直方图积分之差便可说明相似度。Bhattacharyya(巴氏距离),在计算式中有一个1-x项,实际操作中只是用了x项,因此计算的巴氏距离接近于0最好(理论上在1-x下接近于1最好)。最后是HELLINGER等价于巴氏距离。

对仿射变换的图进行直方图比较,从比较数据中可以看出巴氏距离和相关度都说明两图的相似性,而卡方和交叉比较的数据则需要进一步判断。因为仿射后的图片有背景为黑的区域,因此直方图有较大差异(这也是在实际应用中需要考虑的问题)。二者的直方图:

源图的直方图左上角有背景黑表示的组矩(histogram bin)块。

同样的比较图片,添加了底色(黑)边,比较结果就不一样了。其对应直方图:

4Ptr<CLAHE> cv::createCLAHE (double clipLimit=40.0, Size tileGridSize=Size(8, 8))

建立一个对比度限定的自适应直方图均衡化类,用于对图像直方图均衡化后变换图像。

 

这是原图和灰度图。

这是tileGridSize=Size(8, 8)clipLimit=204080时的均衡直方图变换图。

这是clipLimit=40tileGridSize=Size(4, 4)Size(16, 16)时的均衡直方图变换。

5float cv::EMD (InputArray signature1, InputArray signature2,

                                int distType, InputArray cost=noArray(), float *lowerBound=0,

                                OutputArray flow=noArray())

      float cv::wrapperEMD (InputArray signature1, InputArray signature2, int distType,

                                InputArray cost=noArray(), Ptr<float> lowerBound=Ptr<float>(),

                                OutputArray flow=noArray())

这是一种计算权重标记序列的最小运动距离的算法,来源于多源/多目配送优化算法(earth mover’s distance)。用于计算直方图的相似度(复杂比较算法)。此算法可以粗略地确定两个权重序列距离的远近,因而也能判断出两个序列表示的目标是否相关。作为直方图相似的比较算法,能够更精确地给出比较图像是否相关。注意,该算法对组矩数敏感,在图像分类时,应先确定组矩数,越大耗时越长,当大于20时,有明显延迟,一般不大于12。

两张完全不相同的图片进行EMD操作,使用bin=12的HSV格式,计算结果为1.47881281。

两张有关联的图片经EMD操作,在bin = 12的HSV格式下距离为0.00167492。这说明距离的关联度越接近于0,也能说明图片的关联程度。需要指出的是图片的背景一定要相同,如下图:

黑色的背景区域增加了EMD的距离,相关联的图片不再关联。而下图:

则可看出背景的重要性,如果图片尺寸一致,其EMD的关联性可能会更好。以上说明了通过EMD计算图片的关联性一般需要注意的事项。

6void cv::equalizeHist (InputArray src, OutputArray dst)

直方图均衡化图像变换,对图像的直方图进行均衡化处理,使用处理后的直方图反映射到图像。有点类似于CLAHE类对图像进行均衡化处理。注意,仅对单通道图像操作。

 

上图是源图(灰度图),下图是均衡化后的图,可明显看出清晰度的增强。两个直方图,左侧为源图的直方图,右侧为均衡化后的直方图,有一定的拉伸效果。

7void cv::matchTemplate (InputArray image, InputArray templ,

                                OutputArray result, int method, InputArray mask=noArray())

使用模板图像与源图像重叠部分进行比较,模板逐点滑过源图像。根据指定比较算法计算比较结果,生成结果图像。使用minMaxLoc函数计算结果图像的最小最大值及其位置,确定最佳匹配位置。注意:结果图像比原图像小:

sorWid – tepWid = dstWid;

sorHige – tepHig = dstHig;

所以,对于原图相中部分包含的模板,本函数结果未知。

模板匹配用于检测图像中的特定目标(等大小目标),效果比较好。对于目标比例变化不大的也可以有粗略定位,但是对于目标变形或尺寸变化较大的则不能产生正确定位。

总结:

        图像直方图属性,是对像素颜色值的统计操作,给出像素值落在某一色彩空间点上的概率。依次通过统计分析手段对图像进行变换获得关于对比度的拉伸操作。

        关于模板匹配,是通过模板在图像上的滑动比较找到匹配距离最小的图像位置块。

ccc 于 2021-11-1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值