OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)

        在opencv的imgproc模块中,其他图像变换(Miscellaneous Image transformations)模块中包含了一些图像变换的小工具。比如阈值变换,线性融合,漫灌式充填等,这些工具对于处理图像的细节可以起到意想不到的效果。本节介绍这些函数的用法实例,有些来自opencv的帮助资料(翻译),实例为实际图像处理效果分析。程序实现为vs 2010 c++。使用opencv2.4.9版和4.3.0以前版本的源码移植。文章主要针对有一定图像处理经验的读者。

(1)、void cv::adaptiveThreshold(InputArray src,OutputArray dst,

                                        double maxValue,int adaptiveMethod,

                                        int thresholdType,int blockSize,double C);

用适当的阈值作用于数组。这个函数依据下面公式转换灰度图像到二值图像:

  • THRESH_BINARY

  • THRESH_BINARY_INV

         此处T(x,y) 是针对每一个像素分别计算的阈值(见adaptiveMethod参数)。这个函数不改变源图像,即,支持源图像直接操作。

参数

Src

8-位单通道源图像

dst

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

maxValue

赋给满足条件像素的非0值

adaptiveMethod

函数使用的适配阈值算法,参见AdaptiveThresholdTypes. BORDER_REPLICATE|BORDER_ISOLATED 用于处理边界点

thresholdType

阈值类型,必须是 THRESH_BINARY 或THRESH_BINARY_INV之一,见ThresholdTypes

blockSize

像素邻域的尺寸,用于计算像素阈值:3, 5, 7, 等

C

从平均值或权重平均值中减去的常量值。正常情况下,它是正值但也可以是0或负值(应该是阈值的偏移值)。

参见threshold, blur, GaussianBlur

(2)、double cv::threshold(InputArray src, OutputArray dst, double thresh,

                                                double maxval, int type )

用固定阈值作用于数组元素。这个函数用固定阈值作用于多通道数组。用于由灰度图像生成二值图像(也可以用于图像比较的目的) 或消除噪声,即,过滤太小或太大值。这个函数支持几种典型的阈值,由输入的参数确定。

指定值THRESH_OTSU 或 THRESH_TRIANGLE 可以与典型阈值组合,此时,这个函数使用Otsu's或三角算法确定最优阈值,并使用最优阈值来替代指定的阈值。

注:

        当前,实现的Otsu's 和三角算法仅适用于8-位单通道图像

参数

src

输入数组(多通道,8-位或32-位浮点)

dst

输出数组,与源同尺寸和类型并且同通道数

thresh

阈值

maxval

用于THRESH_BINARY 和 THRESH_BINARY_INV 阈值类型的最大值

type

阈值类型(见ThresholdTypes).

返回

在使用Otsu's 或三角算法时,计算给出的阈值

参见

adaptiveThreshold, findContours, compare, min, max

(3)、void cv::blendLinear(InputArray src1,  InputArray src2,  InputArray weights1, 

                                                InputArray weights2,  OutputArray dst)

线性混合操作是一种典型的二元(两个输入)的像素操作:

通过在范围0--1内改变 ,这个操可以用来对两幅图像或两段视频产生时间上的画面叠化(cross-dissolve)效果,就像在幻灯片放映和电影制作中那样。在幻灯片翻页时可以设置为前后页缓慢过渡以产生叠加效果,电影中经常在情节过渡时出现画面叠加效果。

(4)、void cv::distanceTransform(InputArray src,OutputArray dst,

                                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​       OutputArray labels, int distanceType,

                                                               int maskSize,int labelType = DIST_LABEL_CCOMP);

             void cv::distanceTransform(InputArray src,OutputArray dst,

                                                               int distanceType,int maskSize,int dstType = CV_32F );

这个函数计算图像中每个像素最靠近0像素点的距离。原图像是二值图像,0像素为背景像素。函数 cv::distanceTransform 计算二值图像中每个像素到最近0像素点的距离。对于0像素点,这个距离值显然是0。

当maskSize == DIST_MASK_PRECISE 并且distanceType == DIST_L2 时,这个函数运行TBB库中的并行算法。其他情况,这个函数查找最近0像素的最短路径使用基本的位移算法:水平,垂直,对角或马步移动(适用于5x5屏蔽矩阵下),总距离作为这些基本距离的和。距离函数应该是对称的,所有水平和垂直位移都有相同的成本(表示为a),所有对角位移也有相同的成本(表示为b),而马步移动的成本(表示为c)。对于DIST_C 和DIST_L1类型的距离,可以精确进行计算,然而对于DIST_L2 (欧几里得距离),则有相对误差(5×5 屏蔽可给出精确结果)。对于a,b,c,OpenCV使用下面建议的值:

  • DIST_L1: a = 1, b = 2
  • DIST_L2:
    • 3 x 3a=0.955, b=1.3693
    • 5 x 5a=1, b=1.4, c=2.1969
  • DIST_C: a = 1, b = 1

典型地,为了快速,在粗糙的距离精度DIST_L2下,使用3×3屏蔽就够了。 对于更精确的距离精度DIST_L2,应该使用5×5 屏蔽或精确算法。注意,无论是精确还是近似算法对像素而言都应该是线性的。

这个函数的变异功能不仅仅是计算每个像素(x,y)的最小距离而且还标识0像素(labelType==DIST_LABEL_CCOMP)或最近0像素(labelType==DIST_LABEL_PIXEL)组成的最近连通域。连通域的索引存储在labels(x, y)中。当labelType==DIST_LABEL_CCOMP时,函数自动在输入图像中查找0像素组成的连通域并且用不同的标签标记。当labelType==DIST_LABEL_PIXEL,函数扫描输入图像并用不同的标签标记所有0像素域。

在变异模式下,复杂性仍然是线性的。即,这个函数提供一种非常快的方法来计算二值图像的维诺图。当前,第二个变异功能仅仅可用于近似的距离变换算法,即,不支持maskSize=DIST_MASK_PRECISE。

参数

Src

8-位,单通道(二值)源图像

Dst

具有计算距离的输出图像。它是8-位或32-位浮点单通道图像,与源图同尺寸

Labels

输出的2D 标签数组(离散的维诺图)。有CV_32SC1类型且与源图同尺寸

distanceType

距离类型,见DistanceTypes

maskSize

距离变换的屏蔽尺寸 见DistanceTransformMasks,DIST_MASK_PRECISE 不被变异功能支持。在DIST_L1 或DIST_C 距离类型下,这个参数强制为3,因为3×3 屏蔽给出与5×5 或任何更大滤镜相同的结果

labelType

要建立的标签数组的类型,见DistanceTransformLabelTypes

(5)、int cv::floodFill(InputOutputArray image,Point seedPoint,Scalar newVal,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        Rect * rect = 0Scalar loDiff = Scalar()

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        Scalar upDiff = Scalar()int  flags = 4);

        int cv::floodFill(InputOutputArray image,InputOutputArray mask

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​​​​​​​​​​​​Point seedPoint,Scalar newVal,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​Rect * rect = 0Scalar loDiff = Scalar()

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        Scalar upDiff = Scalar()int  flags = 4);

使用给定的颜色充填连通域。函数cv::floodFill 从种子点开始用给定的颜色充填连通域,连通性由与邻域像素的颜色/亮度靠近程度所确定。像素(x,y) 属于重绘区域,如果:

  • 在灰度图像下浮点范围满足

 在灰度图像下定点范围满足

 在彩色图像下浮点范围满足

 和

 在彩色图像下定点范围满足

 和

 此处src(x′,y′) 是已知的属于连通域的像素邻域的一个值。即,为了加入连通域,像素的颜色/亮度应该足够靠近:

  • 一个它的邻域的颜色/亮度,这个邻域已经属于浮点范围的连通域
  • 种子点的颜色/亮度,如果是定点范围。

使用这个函数既可以使用指定颜色直接标记连通域,也可以建立屏蔽然后抽取轮廓,拷贝区域到另一个图像。

参数

image

输入输出的1- 或 3-通道,8-位,或浮点图像,它由函数修改,除非指定FLOODFILL_MASK_ONLY 标志设置第二变异功能,见下面详细描述

mask

操作屏蔽,应该是单通道8位图像,比原图宽2个像素,高2个像素。因为这即是输入也是输出参数,因此必须进行初始化,漫灌充填不能穿过输入屏蔽的非零像素,例如,边缘检测输出可以用做屏蔽来终止对边缘的充填。在输出上,屏蔽对应充填像素的位置要设置成1或一个指定的标志值,此外,这个函数使用简单的内部处理充填屏蔽的边框,因而可以在多次调用中使用相同的屏蔽,来保证充填区域不会重叠

seedPoint

充填开始点

newVal

重绘区域像素的新值

loDiff

亮度/颜色的最大下差。在当前像素和连通域邻域像素之间,或联通域种子像素之间,亮度/颜色的最大下差

upDiff

亮度/颜色的最大上差。在当前像素和连通域邻域像素之间,或联通域种子像素之间,亮度/颜色的最大上差

rect

选择输出参数,由函数给出的重绘区域的最小外接矩形

flags

操作标志。头8位含有连通性值,默认为4,意指仅4方向最近邻域(共享边)被关注。连通性值8表示8方向最近领域像素(共享角点)被关注。后8位(8-16) 包含1到255值,用这个值充填屏蔽像素(默认值是1)。例如,4 | ( 255 << 8 ) 是4方向最近邻域和屏蔽充填值为255。下面的附加选择占用高位,因而可以使用(|)与联通性作进一步的组合,见FloodFillFlags

注意

由于屏蔽图大于被充填图像,因此图像中(x,y)像素对应屏蔽图中的(x+1,y+1)像素

参见

findContours

(6)void cv::grabCut(InputArray img,InputOutputArray mask,

        ​​​​​​​        ​​​​​​​        Rect rect,InputOutputArray bgdModel,InputOutputArray fgdModel,

                        int iterCount,int mode = GC_EVAL )

运行GrapCut算法进行图像分割。这个函数实现了GrabCut图像分割算法(类似于抠图的算法)。

参数

img

输入的8-位3-通道图像

mask

输入输出的8-位单通道屏蔽。这个屏蔽由函数在模式设置为GC_INIT_WITH_RECT时初始化,其元素可以是GrabCutClasses类之一

rect

ROI 包含分割对象的矩形区域。在ROI之外的像素被显式地标注为背景。这个参数仅仅用作mode==GC_INIT_WITH_RECT 情况

bgdModel

临时的背景模式数组。在处理相同图像时不要修改这个数组

fgdModel

临时的前景模式数组。在处理相同图像时不要修改这个数组

iterCount

在返回结果之前算法迭代的次数,注意,通过使用mode==GC_INIT_WITH_MASK or mode==GC_EVAL调用函数, 结果可以被进一步精炼

mode

操作模式,可以是GrabCutModes之一

(7)void cv::integral(InputArray src,OutputArray sum,int sdepth = -1 )

        void cv::integral(InputArray src,OutputArray sum,OutputArray sqsum,

                                        int sdepth = -1,int sqdepth = -1 )

        void cv::integral(InputArray src,OutputArray sum,OutputArray sqsum,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        OutputArray tilted,int sdepth = -1,int sqdepth = -1 )

计算图像的积分。这个函数计算源图像的一个或多个积分图像,如下:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第1张图片

使用积分图像可以快速计算图像的指定正矩形或转角矩形区域的和,平均值,标准差等,例如:

        ​​​​​​​        

 这就使得快速模糊滤波处理和变窗口尺寸的快速块矫正成为可能,例如,在多通道图像中,每一个通道的和分别进行累加。

作为特例,下图显示了正矩形Rect(3,3,3,2) 和斜矩形Rect(5,1,2,3) 的积分计算过程,图中显示了原始图像中选中的像素,以及相关的积分图像中的像素和斜矩形像素。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第2张图片

                                                                 积分计算的例

参数

src

输入图像,具有W×H,8-位或浮点(32f或64f)

sum

积分图像,具有(W+1)×(H+1),32位整数或浮点(32f或64f).

sqsum

平方和像素值的积分图像,具有(W+1)×(H+1),双精度浮点(64f)数组

tilted

转动45度的积分图像,具有(W+1)×(H+1)的数组,与sum同数据类型

sdepth

期望的积分和斜积分图像的深度,CV_32S,CV_32F,或 CV_64F

sqdepth

期望的平方和像素值积分图像的深度,CV_32F或CV_64F

(8)、void cv::watershed(InputArray image,InputOutputArray markers )

执行一种基于标记的图像分割方法,这种方法使用的是分水岭算法。这个函数实现的是一种变异的分水岭算法,非参数化的,基于标记的图像分割算法。

在把图像传输给函数之前,首先要用正标识(>0)粗略地框出图像标记的期望区域。以使每一个区域都表示成具有像素值1,2,3等等的一个或多个连通区域。这些标记可以从二进制屏蔽图中使用findContours 和 drawContours 生成(见watershed.cpp示例)。这些标记作为进一步图像分区的种子,在标记区域中的所有其他像素,相对于框出的区域是未知的,并且是由算法确定,因此应该被设置到0。在函数的输出中,每一个标记区域的像素都设置为种子区域的值,或-1,如果它在区域之间的边界上。

注意

任何两个相邻的联通区域,都必须是由分水岭边界(-1像素)分隔的。尽管它们可以在传递给函数的初始标记图像上相互接触

参数

image

输入的8-位3-通道图像

markers

输入输出的32-位单通道标记图像。与原图像同尺寸

参见

findContours

其他图像变换(Miscellaneous Image Transformations)实例

         这里给出的图像变换函数示例采用的是opencv2.4.9版本的开源源码,在vs2010上实现。其中有些函数是高版本出现的,因此在处理中移植了一些高版本的函数源码到opencv2.4.9中。关于实例中的一些说明是针对有一定图像处理基础的读者的,读者应该至少知道关于颜色空间,图像通道,等基本概念。

枚举AdaptiveThresholdTypes

         ADAPTIVE_THRESH_MEAN_C //阈值T(x,y)为(x,y)邻域blkSize×blkSize平均值减去C

        ADAPTIVE_THRESH_GAUSSIAN_C //阈值T(x,y)是(x,y)邻域blkSize×blkSize的权重和(与高斯窗口关联的)减去C。默认的sigma (标准差)用于指定的blkSize,见getGaussianKernel

枚举DistanceTransformLabelTypes

    DIST_LABEL_CCOMP // 原图中每一个0联通域(以及所有靠近这个连通域的非零像素)都将赋值为相同标签

    DIST_LABEL_PIXEL  // 每一个0像素(和所有靠近的非0像素) 都有它自己的标签

void cv::adaptiveThreshold(InputArray src,OutputArray dst,

                                        double maxValue,int adaptiveMethod,

                                        int thresholdType,int blockSize,double C);

计算适合图像的阈值,对图像进行二值化处理。注意,此处的阈值是对于每一个像素进行计算的,像素在邻域内的阈值与blocksize有关,并且使用了大于(>)或小于(<),因此对于二值图像(或近似的二值图像),函数只对边缘像素检测有效,如图:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第3张图片

 左边为灰度图,右边为滤波图,函数返回的二值图如下:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第4张图片

左边为灰度图返回的二值图,右边为滤波图返回的二值图。这就是在邻域中计算MEAN值T(x,y),然后根据大于/小于等于来判断像素的背景/前景。如果使用大于等于代替大于判定,则背景的邻域像素也都会变为前景,只有边缘部分被标记出来(可以使用c偏移值测试)。这个函数的阈值计算方法(领域平均值法或领域高斯法)注定会产生上图这样的效果。

对于一般图像,其结果如下图:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第5张图片

左边是灰度图及其二值图,右边是高斯滤波图及其二值图。

double cv::threshold(InputArray src, OutputArray dst, double thresh,

                                        double maxval, int type )

计算适合图像的阈值,对图像进行二值化处理。注意,此处的计算阈值不是邻域的阈值,而是针对整个图计算的阈值,因此可以生成真正的连通区域二值图像。使用OTSU算法,则自动计算最适合图像的threshold值,此时设置的threshold无效。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第6张图片

这是使用了THRESH_BINARY 和 THRESH_BINARY_INV 模式,threshold=100,以及组合OTSU的情况。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第7张图片

上两个图使用THRESH_TOZERO 和 THRESH_TOZERO_INV方法,threshold = 100,下两个使用OTSU进行组合,计算的threshold = 129。此处主要说明OTSU算法的效果。

void cv::blendLinear(InputArray src1,  InputArray src2,  InputArray weights1, 

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        InputArray weights2,  OutputArray dst)

叠加两幅图像,函数根据参数指定的权重对两幅图像进行融合,生成类似于水印形式的融合图像。对于运动图像(视频),可以形成重叠形式的前景慢过渡(逐渐改变权重)。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第8张图片

两张图融合,weight0=1,weight1=0.3,显然,weight1=0.3的米粒图作为背景了。而源图weight0=1保持不变。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第9张图片

还是上面两张图,此时weight0=2,weight1=0.6,此时前景图更明亮了(亮度增强了),背景也亮度增强了。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第10张图片

这里weight0=3,weight1=1,前景图颜色已经变了,与源图有很大差异,背景图则保持了原来的本色(没有变化)。这说明在线性融合过程中(opencv2.4.9版本下使用的是addweighted()函数)权重改变图片的亮度,前景亮度大于背景亮度(如果背景权重大,背景可变为前景)。

void cv::distanceTransform(InputArray src,OutputArray dst,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        OutputArray labels, int distanceType,

                                        int maskSize,int labelType = DIST_LABEL_CCOMP);

void cv::distanceTransform(InputArray src,OutputArray dst,

                                        int distanceType,int maskSize,int dstType = CV_32F );

距离变换是针对二值图像的一种变换,是计算并标识空间点(对目标点)距离的过程。最终把二值图像变换为距离标注的灰度图像。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第11张图片

这是采用distancetype=DIST_L1,masksize=3/5,给出的距离变换图像。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第12张图片

distancetype=DIST_L1,masksize=3时的变换图像。可以使用距离数值对粘连图像进行分割,也可以使用距离图分离出目标骨架(如图中最亮的部分)。

int cv::floodFill(InputOutputArray image,Point seedPoint,Scalar newVal,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        Rect * rect = 0Scalar loDiff = Scalar()

        ​​​​​​​        ​​​​​​​        ​​​​​​​        Scalar upDiff = Scalar()int  flags = 4);

int cv::floodFill(InputOutputArray image,InputOutputArray mask

        ​​​​​​​        ​​​​​​​        ​​​​​​​        Point seedPoint,Scalar newVal,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        Rect * rect = 0Scalar loDiff = Scalar()

        ​​​​​​​        ​​​​​​​        ​​​​​​​        Scalar upDiff = Scalar()int  flags = 4);

对mask指定的区域(由非0点划分,如contour划分)中,指定seed点的连通区域进行充填,充填从seed点开始漫灌方式进行,每次充填一个seed点区域(根据loDiff和upDiff区间灰值)。返回该区域的最小外接矩形。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第13张图片

这是在lodiff=2,updiff=2时fioodfill函数充填结果。左图为源图,右图为结果图,黄点为seed点,根据contours的最小面积矩形获得。当diff值为5和10时,如下图:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第14张图片

 OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第15张图片

 可以看出,误差值包含了底色,最外层的黄色矩形说明了填充区域。这说明正确选择diff值对充填区域有较大影响。下面是使用mask的的情况。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第16张图片

同样是diff=10,由于mask的屏蔽,并没有联通底色区域。但是,当diff=20时,也出现了mask没有拦住充填过程的现象。这是因为在flag中使用了8路联通检测,对于contours中的斜线,8路检测是可以轻松穿过的。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第17张图片

使用4路联通检测,则情况就不同了:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第18张图片

 同样的diff=20,mask,结果如上图。继续调整diff=50,仍然可以在mask范围内充填,这就是mask对充填范围的限制作用。对于使用FLOODFILL_FIXED_RANGE,只是计算误差过程,是使用seed点还是邻域点。对于FLOODFILL_MASK_ONLY,则填充在源图上进行还是填充到mask图上。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第19张图片

左图是mask图,右图为源图,程序给出的seed点和填充返回的矩形框。

void cv::grabCut(InputArray img,InputOutputArray mask,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        Rect rect,InputOutputArray bgdModel,InputOutputArray fgdModel,

                                int iterCount,int mode = GC_EVAL )

   GrabCut是微软研究院的一个课题,主要功能是分割,抠图和背景虚化。交互式设定一个前景提取的矩形,其中包含有需要提取的前景目标,设定提取模式和迭代次数,函数返回mask为提取目标的屏蔽图像,使用“与”操作,可以获得目标图像。

算法流程

  1. 在图片中定义含有(一个或多个)物体的矩形,矩形外的区域被自动认为是背景。
  2. 对于用户定义的矩形区域,可用背景中数据来区分是前景还是背景。
  3. 用高斯混合模型(GMM)来对背景和前景检验,并将未定义的像素标记为可能的前景或背景。
  4. 图像中的每一个像素都被看作通过虚拟边与周围像素连接,而每条边都有一个属于前景或背景的概率这基于它和周围像素颜色上的相似性。
  5. 每一个像素(即算法中的节点)会与前一个前景或背景节点连接。
  6. 在节点连接完成后,用图论中最大流最小割的方法来分割。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第20张图片

 获取车牌图像。计算操作有点慢。关于参数的选取,mode = GC_INIT_WITH_RECT/ GC_INIT_WITH_MASK,前者使用给定的矩形初始化mask(由函数完成),后者需要自己使用GC_BGD/GC_FGD/GC_PR_BGD/GC_PR_FGD初始化mask(一般采用矩形边及以外点为GC_BGD,其他点为GC_PR_FGD)。此时效果与GC_INIT_WITH_RECT相同。也可以交互式选择背景模式,能更精确分割图像。将分割出的图像对背景进行模糊(Blur)处理后反贴回去可达到背景模糊效果。注意,由于grapcut函数是依靠色彩进行变换的,所以对于灰度图像转换成的三通道图像效果不是很好。

void cv::integral(InputArray src,OutputArray sum,int sdepth = -1 )

void cv::integral(InputArray src,OutputArray sum,OutputArray sqsum,

                                int sdepth = -1,int sqdepth = -1 )

void cv::integral(InputArray src,OutputArray sum,OutputArray sqsum,

        ​​​​​​​        ​​​​​​​        ​​​​​​​        OutputArray tilted,int sdepth = -1,int sqdepth = -1 )

对图像做积分操作,计算每通道每像素的积分,以该像素为右下角矩形的面积灰度值的和(0,0,x,y)。输出为该像素的积分值。返回积分值构成的矩阵图像。注意,tilted矩阵的计算为斜45度的计算方式:

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

如图所示:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第21张图片

 P0的积分为白色块,P1的积分黄色 + 白色块,P2的积分为橙色 + 白色,P3的积分为绿色 + 黄色 + 橙色 + 白色。都是以斜45度的方式计算点的积分值。如图:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第22张图片

 这分别是源图,灰度图,滤波图(高斯滤波)canny图和二值图,下面是对应的积分图:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第23张图片

 这是原图的积分,分别是sum,sqsum和tiltsum

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第24张图片

这是灰度的积分,分别是sum,sqsum和tiltsum,滤波和canny与之相似。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第25张图片

这是二值图的积分,分别是sum,sqsum和tiltsum。从这些积分图中可以看出,sum和sqsum是从左上角到右下角逐渐增加的,右下角是积分和的最大值(最亮),而tiltsum虽然也是从左上角到右下角进行计算的,但是由于算法不同,其最大值在下边缘的中点上达到最大,这是因为tiltsum计算的是斜45度的积分域。左下角和右下角的积分只是对角线上半部分倍计算,因此,积分值在下边缘的中点达到最大。

void cv::watershed(InputArray image,InputOutputArray markers )

分水岭算法主要用于图像的分割。这里使用的是标记域markers,不象grabcut使用矩形区域。标记域可以由contours划分,因此在使用watershed时,应首先生成contours,作为标记markers,然后再调用watershed获得输出的markers,结合原图的图像操作后获得分割图像。注意,所谓contours给出的是对标记图中的标记给出的contours,即,标记的contour。例如:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第26张图片

在图像上做标记(交互式)。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第27张图片

标记图及其contours结果,以及watershed算法结果。叠加源图,标记contours显示:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第28张图片

说明标记区域可以很好地被watershed算法分割。并且可以提取指定区域(比如车牌区域)。对源图的分割区域进行再处理就能够获得希望的结果。算法也可以直接使用图像算法自动获得标记contours,然后对图像进行分割,如图:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第29张图片

这是原图和经过灰度处理,高斯滤波后由findcontours获得的contours图。使用这个contours作为标记,得到如下watershed算法的结果图:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第30张图片

此时的分割并没有如预期的那样,这是因为标记图超出了目标范围,算法根据特定的参数计算出了目标的影响范围。从contours图中也可以看出,contours比目标大了一圈,这是因为使用了高斯滤波,目标的边缘被淡化了,而findcontours则在淡化了的边缘外侧检测到了目标的边缘。此时如果使用质心作为标记则有如下结果:

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第31张图片

watershed算法取得了较好的结果,但是要注意,其中有一个目标被watershed算法作为了底色(紫色,仅有一个外围线的,每一个目标都有两个外围线,一个是contour线,一个是watershed算法的边界线,仅有一条线的紫色目标其watershed算法的边界线为图像边界)。这说明在watershed算法中,总有一个标记为底色标记或者多个目标分割底色。如果仅有一个标记,则提取的图像全部为底色,使用自动加标记的方式可以避免这种情况发生。

OpenCV各模块函数使用实例(7)--其他图像变换(Miscellaneous Image Transformations)_第32张图片

在左上角添加一个标记,其势力范围被当做底色。

你可能感兴趣的:(软件编程,VC++,imgproc,opencv,图像分析,c++,模式匹配)