详见《OpenCV3编程入门(毛星云、冷雪飞)》
void boxFilter(InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), bool normalize=true, int borderType=BORDER_DEFAULT)
参数1 InputArray类型的src,源图像,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F, CV_64F之一
参数2 OutputArray类型的dst,目标图像
参数3 int类型的ddepth,输出图像的深度,-1代表原图深度,即src.depth()
参数4 Size类型的ksize,内核大小,一般用Size(w,h)表示内核大小
参数5 Point类型的anchor,表示锚点(被平滑的那个点),有默认值Point(-1, -1),表示锚点在核的中心
参数6 bool类型的normalize,默认为true,表示内核是否被其区域归一化了,均值滤波是方框滤波normalized后的特殊情况
参数7 int类型的borderType,推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT)
参数1 InputArray类型的src,源图像,对通道独立处理,可以处理任意通道数的图像,CV_8U, CV_16U, CV_16S, CV_32F, CV_64F之一
参数2 OutputArray类型的dst,目标图像,和源图像有一样的尺寸。比如用Mat::Clone,以源图像为模板,初始化得到目标图
参数3 Size类型的ksize,内核大小,Size(w,h)
参数4 Point类型的anchor,表示锚点,有默认值Point(-1,-1)
参数5 int类型的borderType,用于推断图片外部像素的某种边界模式
高斯滤波:GaussianBlur函数
一类根据高斯函数形状来选择权值的线性平滑滤波器,对抑制服从正态分布的噪声非常有效
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT)
参数1 InputArray类型的src,源图像,是CV_8U, CV_16V, CV_16S, CV_32F, CV_64F
参数2 OutputArray类型的dst,目标图像,需和源图像有一样的尺寸和类型,可以用Mat::Clone以源图像为模板初始化得到目标图
参数3 Size类型的ksize,高斯内核大小
参数4 double类型的sigmaX,高斯在X方向的标准偏差
参数5 double类型的sigmaY,高斯在Y方向的标准偏差,若sigmaY为0就设为sigmaX
FilterEngine类:OpenCV图像滤波核心引擎
各种图像滤波函数都是在函数末尾处定义了一个Ptr类型的f,然后f->apply(src, dst)一下
Ptr<FilterEngine> f = createBoxFilter(src.type(), dst.type(), ksize, anchor, normalize, borderType);
f->apply(src, dst);
Ptr是用来动态分配的对象的智能指针模板类,尖括号里的模板参数是FilterEngine。FilterEngine类可以把几乎所有滤波操作施加到图像上,包含了所有必要的中间缓存器,很多和滤波相关的create系函数返回值就是Ptr,如
cv::createSeparableLinearFilter()
cv::createLinearFilter(), cv::createGaussianFilter(), cv::createDerivFilter()
cv::createBoxFilter()
cv::createMorphologyFilter()
需注意,如需要使用预先定义好的滤波操作,有cv::filter2D(), cv::erode()和cv::dilate()可以选择,不依赖于FilterEngine,在函数体内部实现了FilterEngine提供的功能;不像blur函数等依赖于FilterEngine引擎。
class CV_EXPORTS FilterEngine{}
线性滤波:每个像素的输出值是一些输入像素的加权和,易于从频谱响应角度来进行分析。
非线性滤波:噪声是散粒噪声而不是高斯噪声
中值滤波:medianBlur:用像素点邻域灰度值的中值代替该像素点灰度值
优势:消除孤立的噪声点,如斑点噪声和椒盐噪声;克服线性滤波器带来的图像细节模糊;滤除脉冲干扰及图像扫描噪声有效,常用于保护边缘信息
劣势:时间是均值滤波5倍以上,需要排列+选取中间值
void medianBlur(InputArray src, OutputArray dst, int ksize)
参数1 InputArray类型的src,填1、3或4通道的Mat类型图像;当ksize为3或5时,图像深度为CV_8U, CV_16U, CV_32F之一,对于较大孔径尺寸的图像只能是CV_8U
参数2 OutputArray类型的dst,目标图像,需和源图片有一样的尺寸和类型,可以用Mat::Clone以源图像为模板来初始化目标图
参数3 int类型的ksize,孔径的线性尺寸,必须为大于1的奇数
双边滤波:bilateralFilter:结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。
优势:比高斯滤波多了一个协方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近离得较远的像素不会对边缘上的像素影响太多。
劣势:保留过多高频信息,对于彩色图像里的高频噪声不能干净滤掉,智能对低频信息进行较好地滤波。
输出像素值取决于邻域像素值的加权值组合,加权系数w(i,j,k,l)取决于定义域核和值域核的乘积
void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)
参数1 InputArray类型的src,源图像,需要是8位或浮点型单通道、三通道的图像
参数2 OutputArray类型的dst,目标图像,需要和源图像有一样的尺寸和类型
参数3 int类型的d,滤波过程中每个像素邻域的直径,如果被设为非正数,那么OpenCV会从第五个参数sigmaSpace计算出来
参数4 double类型的sigmaColor,颜色空间滤波器的sigma值,参数值越大越表明该像素邻域内有越宽广的颜色被混合到一起,产生较大的半相等颜色区域
参数5 double类型的sigmaSpace,坐标空间中滤波器的sigma值,坐标空间的标注方差。数值越大,意味着越远的像素会相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色,当d>0时,d指定了邻域大小且与sigmaSpace无关。否则d正比于sigmaSpace
参数6 int类型的borderType,用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT
bilateralFilter(image, out, 25, 25*2, 25/2);
膨胀和腐蚀的功能:1、消除噪声;2、分割出独立的图像元素,在图像中连接相邻的元素;3、寻找图像中的明显极大值区域或极小值区域;4、求出图像的梯度
void cv::dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor, int iterations, int borderType, constScalar& borderValue){
morphOp(MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue);
}
dilate函数:
void dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue())
参数1 InputArray类型的src,源图像,图像深度为CV_8U, CV_16U, CV_16S, CV_32F或CV_64F之一
参数2 OutputArray类型的dst,目标图像,需要和源图像有一样的尺寸和类型
参数3 InputArray类型的kernel,膨胀操作的核,当为NULL时表示使用参考点位于中心3×3的核
使用getStructuringElement函数配合这个参数使用,返回指定形状和尺寸的结构元素,其中,getStructuringElement第一个参数表示内核形状,包括MORPH_RECT(矩形)、MORPH_CROSS(交叉形)、MORPH_ELLIPSE(椭圆形)。第二、三个参数分别是内核尺寸以及锚点位置。
int g_nStructElementSize=3;
Mat element=getStructuringElement(MORPH_RECT, Size(2*g_nStructElementSize+1, 2*g_nStructElementSize+1), Point(g_nStructElementSize, g_nStructElementSize));
参数4 Point类型的anchor,锚的位置,有默认值(-1, -1),锚位于中心
参数5 int类型的iterations,迭代使用erode()函数的次数,默认值为1
参数6 int类型的borderType,用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT
参数7 const Scalar&类型的borderValue,当边界为常数时的边界值有默认值morphologyDefaultBorderValue()
Mat image=imread("1.jpg");
Mat element = getStructuringElement(MORPH_RECT, Size(15,15));
Mat out;
dilate(image, out, element);
void cv::erode(InputArray src, OutputArray dst, InputArray kernel, Point anchor, int iterations, int borderType, constScalar& borderValue){
morphOp(MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue);
}
erode函数:
void erode(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue());
参数1 InputArray类型的src,源图像,图像深度为CV_8U, CV_16U, CV_16S, CV_32F或CV_64F之一
参数2 OutputArray类型的dst,目标图像,需要和源图像有一样的尺寸和类型
参数3 InputArray类型的kernel,腐蚀操作的核,当为NULL时表示使用参考点位于中心3×3的核。使用getStructuringElement函数配合这个参数使用,返回指定形状和尺寸的结构元素
参数4 Point类型的anchor,锚的位置,有默认值(-1, -1),锚位于中心
参数5 int类型的iterations,迭代使用erode()函数的次数,默认值为1
参数6 int类型的borderType,用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT
参数7 const Scalar&类型的borderValue,当边界为常数时的边界值有默认值morphologyDefaultBorderValue()
Mat image = imread("1.jpg");
Mat element = getStructuringElement(MORPH_RECT, Size(15,15));
Mat out;
erode(image, out, element);
开运算:先腐蚀后膨胀,可以用来消除小物体,在纤细点处分离物体,在平滑较大物体的边界同时不明显改变其面积
dst=open(src, element)=dilate(erode(src, element));
闭运算:先膨胀后腐蚀,能够排除小型黑洞
dst=close(src, element)=erode(dilate(src, element));
形态学梯度:膨胀图与腐蚀图之差,可以将团块边缘突出出来,可以用来保留物体边缘轮廓
顶帽/礼帽(Top Hat):原图与开运算的结果图之差,用来分离比临近点亮一些的斑块,在一副图像具有大幅背景,微小物品比较有规律的情况下,可以用顶帽运算进行背景提取
dst = tophat(src, element) = src - open(src, element)
黑帽(Black Hat):闭运算的结果与原图像之差,分离比临近点暗一些的斑块,效果图有很完美的轮廓
dst = blackhat(src, element) = close(src, element) - src
核心API函数:morphologyEx()
void morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue());
参数1 InputArray类型的src,源图像,图像位深为CV_8U, CV_16U, CV_16S, CV_32F, CV_64F之一
参数2 OutputArray类型的dst,目标图像,与源图像有一样的尺寸和类型
参数3 int类型的op,表示形态学运算的类型
参数4 InputArray类型的kernel,形态学操作的核,当为NULL时表示使用参考点位于中心3×3的核。使用getStructuringElement函数配合这个参数使用,返回指定形状和尺寸的结构元素
参数5 Point类型的anchor,锚的位置,有默认值(-1,-1),表示锚位于中心
参数6 int类型的iterations,迭代使用函数的次数,默认为1
参数7 int类型的borderType,用于推断图像外部像素的某种边界模式,有默认值BORDER_CONSTANT
参数8 const Scalar&类型的borderValue,有默认值morphologyDefaultBorderValue()
漫水填充:floodFill:用一种特定的颜色填充连通区域,设置可连通像素的上下限以及连通方式来达到不同的填充效果的方法。用指定的颜色从种子点开始填充一个连接域,经常被用来标记或分离图像的一部分,以便对其进行进一步分析处理,也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或只处理掩码指定的像素点,操作的结果总是某个连续的区域。
//版本1
int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(),int flags=4)
//版本2
int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4)
参数1 InputOutputArray类型的image,输入/输出1通道或3通道,8位或浮点图像
参数2 InputOutputArray类型的mask,第二个版本floodFill独享的参数,表示操作掩膜,应该为单通道8位,长和宽比输入图像image大两个像素点的图像。漫水填充不会填充mask的非零像素区域,例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘,同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。且mask会比需填充的图像大,所以mask中输入图像(x,y)像素点对应的点坐标为(x+1, y+1)
参数3 Point类型的seedPoint,漫水填充算法的起始点
参数4 Scalar类型的newVal,像素点被染色的值,在重绘区域像素的新值
参数5 Rect*类型的rect,有默认值0,可选的参数,用来设置floodFill函数将要重绘区域的最小边界矩形区域
参数6 Scalar类型的loDiff,有默认值Scalar(),表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之负差(lower brightness/color difference)的最大值
参数7 Scalar类型的upDiff,有默认值Scalar(),表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之正差(lower brightness/color difference)的最大值
参数8 int类型的flags,操作标识符,此参数包含3个部分
低8位(第0~7位)用于控制算法的连通区域,可取4或者8,如果为4表示填充算法只考虑当前像素水平方向和垂直方向的相邻点,如果为8表示除上述相邻点外,还会包含对角线方向的相邻点
高8位(第16~23位)可以为0或者两种选项标识符的组合——FLOODFILL_FIXED_RANGE(考虑当前像素与种子像素之间的差,否则就考虑当前像素与其相邻像素的差,这个范围是浮动的)、FLOODFILL_MASK_ONLY(函数不会去填充改变原始图像newVal,而是去填充掩模图像mask,这个标识符只对第二个版本的floodFill有用)
中间8位用于指定填充掩码图像的值,如果flags中间8位的值为0,则掩码会用1来填充
flags=8 | FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE | (38<<8)
图像金字塔:用于图像分割,以多分辨率来解释图像,通过梯次向下采样获得,直到达到某个终止条件才停止采样。层级越高,图像越小,分辨率越低。
高斯金字塔:用来下采样,最主要的图像金字塔
拉普拉斯金字塔:从金字塔底层图像重建上层未采样图像,用来预测残差,可以对图像进行最大程度的还原,向上采样来重建图像
PyrUp函数:对图像向上采样
void pyrUp(InputArray src, OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT)
参数1 InputArray类型的src,源图像
参数2 OutputArray类型的dst,输出图像
参数3 const Size&类型的dstsize,输出图像的大小,有默认值Size(),即由Size(src.cols2, src.rows2)计算,且需要满足
参数4 int类型的borderType,边界模式
PyDown函数:对图像向下采样
void pyrDown(InputArray src, OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT)
参数1 InputArray类型的src,源图像
参数2 OutputArray类型的dst,输出图像
参数3 const Size&类型的dstsize,输出图像大小,有默认值Size(),由Size((src.cols+1)/2, (src.rows+1)/2)计算,且需要满足
pyrDown函数执行了高斯金字塔下采样步骤,将源图像与如下内核做卷积运算,然后对图像偶数行和列插值进行线下采样操作
注意:PyrUp和PyDown函数是不可逆的,这里的向下与向上采样,是针对图像的尺寸而言,和金字塔的方向相反。
高斯金字塔:通过高斯平滑和亚采样获得一些列下采样图像,包含了一系列低通滤波器,其截止频率从上一层到下一层以因子2逐渐增加
对图像向下取样:为获取层级为Gi+1的金字塔图像,对图像Gi进行高斯内核卷积,并将所有偶数行和列去除,得到新的图像面积会变成源图像的四分之一,按上述过程对G0执行操作即可产生整个金字塔。会逐渐失去信息。
对图像向上取样:将图像在每个方向扩大为原来的两倍,新增的行和列以0填充,使用先前同样的内核与放大后的图像卷积,获得新增像素的近似值。
拉普拉斯金字塔:通过源图像减去先缩小再放大的图像的一系列图像构成的,可以理解为高斯金字塔的逆形式。Li=Gi-PyUp(Gi+1)
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)
参数1 InputArray类型的src,源图像
参数2 OutputArray类型的dst,输出图像,当其非0时有着dsize的尺寸,或由src.size()计算出来
参数3 Size类型的dsize,输出图像的大小,如果等于0则由dsize=Size(round(fxsrc.cols), round(fysrc.rows))计算,其中dsize, fx, fy都不能为0
参数4 double类型的fx,沿水平轴的缩放系数,有默认值0,如果等于0由(double)dsize.width/src.cols计算
参数5 double类型的fy,沿垂直轴的缩放系数,有默认值0,如果等于0由(double)dsize.height/src.rows计算
参数6 int类型的interpolation,用于指定插值方式,默认为INTER_LINEAR(线性插值),可选INTER_NEAREST(最近邻插值),INTER_AREA(区域插值,利用像素区域关系的重采样插值),INTER_CUBIC(三次样条插值,超过4×4像素邻域内的双三次插值),INTER_LANCZOS4(Lanczos插值,超过8×8像素邻域的Lanczos插值)
若缩小图像,使用CV_INTER_AREA插值,若放大图像,使用CV_INTER_CUBIC(慢)或CV_INTER_LINEAR(效率高,速度快)
Mat dstImage=Mat::zeros(512,512,CV_8UC3);
Mat srcImage=imread("1.jpg");
//显式指定dsize=dstImage.size(),那么fx和fy会计算出来,不用额外指定
resize(srcImage, dstImage, dstImage.size());
Mat dstImage;
Mat srcImage=imread("1.jpg");
//指定fx和fy,让函数计算出目标图像的大小
resize(srcImage,dstImage,Size(),0.5,0.5);
剔除一些低于或高于一定值的像素。
Threshold()函数:固定阈值操作:对灰度图像进行阈值操作得到二值图像,或者去除噪声,过滤很小或很大像素值的图像点
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
参数1 InputArray类型的src,输入单通道数组,8或32位浮点类型的Mat
参数2 OutputArray类型的dst,函数调用后的运算结果
参数3 double类型的thresh,阈值的具体值
参数4 double类型的maxval,当第5个参数阈值类型type取CV_THRESH_BINARY或CV_THRESH_BINARY_INV时阈值类型的最大值
参数5 int类型的type,阈值类型
自适应阈值操作:adaptiveThreshold()函数
void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)
参数1 InputArray类型的src,源图像,8位单通道浮点型图像
参数2 OutputArray类型的dst,函数调用后的运算结果
参数3 double类型的maxValue,给像素赋的满足条件的非0值
参数4 int类型的adaptiveMethod,用于指定要使用的自适应阈值算法,可取值为ADAPTIVE_THRESH_MEAN_C或ADAPTIVE_THRESH_GAUSSIAN_C
参数5 int类型的thresholdType,阈值类型,必须为THRESH_BINARY, THRESH_BINARY_INV之一
参数6 int类型的blockSize,用于计算阈值大小的一个像素的领域尺寸,取值为3、5、7等
参数7 double类型的C,减去平均或加权平均值后的常数值,通常为正数,少数情况下为0或负数
第一步:滤波(高斯滤波采用离散化的高斯函数产生一组归一化的高斯核,然后基于高斯核函数对图像灰度矩阵的每一点加权求和)
第二步:增强(可通过计算梯度幅值决定)
第三步:检测(通过阈值化方法对梯度值较大的点取舍)
三个评价标准:低错误率、高定位性、最小响应,Canny使用了变分法(一种寻找满足特定功能函数的方法),最优检测使用4个指数函数项的和表示。
【步骤1】 消除噪声
一般使用高斯平滑滤波器,例如,使用size=5的高斯内核
【步骤2】计算梯度幅值和方向
此处,用Sobel滤波器步骤操作
【步骤3】非极大值抑制
排除非边缘像素,仅保留一些细线条。
【步骤4】 滞后阈值
需要高阈值和低阈值,若某一像素位置幅值超过高阈值,被保留为边缘像素;若某一像素位置幅值小于低阈值,该像素被排除;若某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于高阈值的像素时被保留。
canny边缘检测:Canny()函数
void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)
参数1 InputArray类型的image,源图像
参数2 OutputArray类型的edges,输出的边缘图
参数3 double类型的threshold1,第一个滞后性阈值
参数4 double类型的threshold2,第二个滞后性阈值
参数5 int类型的apertureSize,应用Sobel算子的孔径大小,有默认值3
参数6 bool类型的L2gradient,一个计算图像梯度幅值的标识,有默认值false
阈值1,2的较小值用于边缘连接,较大的值用来控制边缘的初阶段,推荐的高低阈值比在2:1到3:1之间
简单的canny用法:
Canny(src,src,150,100,3);
高阶的canny用法:转成灰度图,降噪,用canny,再将得到的边缘作为掩码拷贝到效果图上,得到彩色的边缘图
Mat src1, dst, edge, gray;
dst.create(src1.size(), src1.type());
cvtColor(src1, gray, COLOR_BGR2GRAY);
blur(gray, edge, Size(3.3));
Canny(edge, edge, 3, 9, 3);
dst = Scalar::all(0);
src1.copyTo(dst, edge);
是一个用于边缘检测的离散微分算子,结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。计算过程:
1、分别在x和y两个方向求导
2、在图像的每一点结合以上两个结果求出近似梯度
使用Sobel算子:Sobel()函数
void Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT);
参数1 InputArray类型的src,输入图像
参数2 OutputArray类型的dst,目标图像
参数3 int类型的ddepth,输出图像的深度,支持如下src.depth()和ddepth的组合
若src.depth()=CV_8U,取ddepth=-1/CV_16S/CV_32F/CV_64F
若src.depth()=CV_16U/CV_16S,取ddepth=-1/CV_32F/CV_64F
若src.depth()=CV_32F,取ddepth=-1/CV_32F/CV_64F
若src.depth()=CV_64F,取ddepth=-1/CV_64F
参数4 int类型dx,x反向上的差分阶数
参数5 int类型dy,y反向上的差分阶数
参数6 int类型ksize,有默认值3,表示Sobel核的大小,必须取1、3、5、7之一
参数7 double类型的scale,计算导数值时可选的缩放因子,默认值为1
参数8 double类型的delta,表示在结果存入目标图之前可选的delta值,有默认值0
参数9 int类型的borderType,边界模式, 默认值为BORDER_DEFAULT
一般情况下使用ksize×ksize内核来计算导数,然而当ksize为1时使用3×1或1×3的内核,没有进行平滑操作。
补充1:内核大小为3时,opencv提供了scharr函数,运算比Sobel函数更精确
补充2:Sobel算子结合了高斯平滑和分化,结果会有更多抗噪性,取【xorder=1, yorder=0, ksize=3】计算图像X反向的导数
【xorder=0, yorder=1, ksize=3】计算图像Y反向的导数
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y, dst;
Mat src=imread("1.jpg");
//X方向梯度
Sobel(src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
//Y方向梯度
Sobel(src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
//合并梯度
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
Laplacian算子是n维欧几里得空间中的二阶微分算子,定义为梯度grad的散度div。如果f是二阶可微的实函数,则f的拉普拉斯算子是笛卡尔坐标系xi中所有非混合二阶偏导数求和。
Laplacian使用了图像梯度,内部代码调用了Sobel算子
注意:让一幅图像减去它的Laplacian算子可以增强对比度。
计算拉普拉斯变换:Laplacian()函数
void Laplacian(InputArray src, OutputArray dst, int depth, int ksize=1, double scale=1, double delta=0, intborderType=BORDER_DEFAULT)
参数1 InputArray类型的image,源图像
参数2 OutputArray类型的edges,输出的边缘图
参数3 int类型的ddepth,目标图像的深度
参数4 int类型的ksize,用于计算二阶导数的滤波器孔径尺寸,大小必须为正奇数,默认值1
参数5 double类型的scale,计算拉普拉斯值时可选的比例因子,默认值1
参数6 double类型的delta,表示在结果存入目标图之前可选的delta值,默认值0
参数7 int类型的borderType,边界模式,默认为BORDER_DEFAULT
ksize=1时,Laplacian()函数采用以下3×3孔径
计算图像差分:Scharr()函数
void Scharr(InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale=1, double delta=0, int borderType=BORDER_DEFAULT)
参数1 InputArray类型的src,源图像
参数2 OutputArray类型的dst,目标图像
参数3 int类型的ddepth,输出图像深度,支持如下src.depth()和ddepth组合
若src.depth()=CV_8U,取ddepth=-1/CV_16S/CV_32F/CV_64F
若src.depth()=CV_16U/CV_16S,取ddepth=-1/CV_32F/CV_64F
若src.depth()=CV_32F,取ddepth=-1/CV_32F/CV_64F
若src.depth()=CV_64F,取ddepth=-1/CV_64F
参数4 int类型dx,x方向的差分阶数
参数5 int类型dy,y方向的差分阶数
参数6 double类型的scale,计算导数值时可选的缩放因子,默认为1
参数7 double类型的delta,结果在存入目标图之前可选的delta值,默认为0
参数8 int类型的borderType,边界模式,默认为BORDER_DEFAULT
Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType);
等价于
Sobel(src, dst, ddepth, dx, dy, CV_SCHARR, scale, delta, borderType);
霍夫变换是图像处理中的一种特征提取技术,该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。
OpenCV支持三种线变换:标准霍夫变换(Standard Hough Transform, SHT)、多尺度霍夫变换(Multi-Scale Hough Transform, MSHT)和累计概率霍夫变换(Progressive Probabilistic Hough Transform, PPHT)。MSHT为SHT在多尺度下的一个变种,PPHT是SHT算法的一个改进,在一定范围内进行霍夫变换,计算单独线段的方向以及范围,从而减少计算量,缩短计算时间。
对于一个定点(x0, y0),在极坐标对极径极角平面绘出所有通过它的直线,得到一条正弦曲线
如果n个不同点进行上述操作得到的曲线在平面相交,就意味着他们通过同一条直线
可以通过设置直线上的点的阈值来定义多少曲线交于一点,这样认为检测到了一条直线。
标准霍夫变换:HoughLines()函数
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0);
参数1 InputArray类型的image,源图像,单通道二进制图像
参数2 InputArray类型的lines,经过调用HoughLines函数后存储了霍夫线变换检测到线条的输出矢量,每条线由两个元素的矢量(距离、角度)决定
参数3 double类型的rho,单位半径
参数4 double类型的theta,单位角度
参数5 int类型的threshold,大于threshold的线段才可以被检测通过并返回到结果中
参数6 double类型的srn,默认值0。对于多尺度霍夫变换,这是第三个参数rho的除数距离。粗略的累加器进步尺寸直接是rho,精确的累加器进步尺寸是rho/srn
参数7 double类型的stn,默认值0,对于多尺度霍夫变换srn表示第theta的除数距离。如果srn和stn同时为0,使用经典的霍夫变换,否则这两个参数都为正数。
#include
#include
using namespace cv;
using namespace std;
int main(){
Mat srcImage = imread("1.jpg");
Mat midImage,dstImage;
//进行边缘滤波,转化为灰度图,满足霍夫变换输入要求
Canny(srcImage, midImage, 50, 200,3);
cvtColor(midImage, dstImage, CV_GRAY2BGR);
//进行霍夫变换
vector<Vec2f> lines; //定义一个矢量结构Lines用于存放得到的线段矢量集合
HoughLines(midImage, lines, 1, CV_PI/180, 150, 0, 0);
//在图中绘制出每条线段
for(size_t i = 0; i < lines.size(); i++){
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt1.x = cvRound(x0 - 1000*(-b));
pt1.y = cvRound(y0 - 1000*(a));
line(dstImage, pt1, pt2, Scalar(55,100,195), 1, LINE_AA);
}
}
累计概率霍夫变换:HoughLinesP()函数
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0)
参数1 InputArray类型的image,源图像,8位单通道二进制图像
参数2 OutputArray类型的lines,经过调用HoughLinesP函数后存储了检测到的线条的输出矢量,每一条线由4个元素的矢量(x_1, y_1, x_2, y_2)表示,其中(x_1, y_1)和(x_2, y_2)是每个检测到的线段的结束点
参数3 double类型的rho,单位半径
参数4 double类型的theta,单位角度
参数5 int类型的threshold,大于threshold的线段才可以被检测通过并返回到结果中
参数6 double类型的minLineLength,默认值0,最低线段长度,比这个参数短的线段不能被显现
参数7 double类型的maxLineGap,默认值0,允许将同一行点与点之间连接起来的最大距离
#include
#include
using namespace cv;
using namespace std;
int main(){
Mat srcImage = imread("1.jpg");
Mat midImage,dstImage;
//进行边缘滤波,转化为灰度图,满足霍夫变换输入要求
Canny(srcImage, midImage, 50, 200,3);
cvtColor(midImage, dstImage, CV_GRAY2BGR);
//进行霍夫变换
vector<Vec4i> lines; //定义一个矢量结构Lines用于存放得到的线段矢量集合
HoughLinesP(midImage, lines, 1, CV_PI/180, 80, 50, 10);
//在图中绘制出每条线段
for(size_t i = 0; i < lines.size(); i++){
Vec4i l = lines[i];
line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, LINE_AA);
}
}
一条直线可以由极径极角(r, theta)表示,而圆需要用三个参数表示(x_center, y_center, r)
霍夫梯度法原理:
(1)对图像应用边缘检测,如canny检测;
(2)对边缘图像的每一个非0点,考虑其局部梯度,即Sobel()函数计算x和y方向的Sobel一阶导数得到的梯度;
(3)利用得到的梯度,由斜率指定的直线上的每一个点都在累加器中被累加,这里的斜率是从一个指定的最小值到指定的最大值的距离;
(4)同时,标记边缘图像中每一个非0像素的位置;
(5)从二维累加器中这些点中选择候选的中心,这些中心大于给定阈值并且大于所有近邻,这些候选中心按照累加值降序排列,以便于最支持像素的中心首先出现;
(6)接下来对每一个中心,考虑所有的非0像素;
(7)这些像素按照其与中心的距离排序,从到最大半径的最小距离算起,选择非0像素最支持的一条半径;
(8)如果一个中心收到边缘图像非0像素最充分的支持,并且到前期被选择的中心有足够的距离,那么它就会被保留下来。
霍夫梯度法缺点:
(1)使用Sobel导数计算局部梯度,它可以视作等同于一条局部切线,不是一个数值稳定的做法,可能产生一些噪声;
(2)在边缘图像中整个非0像素级被看做每个中心的候选部分,因此如果累加器的阈值设置偏低,算法会消耗较长时间。因为每一个中心只选择一个圆,如果有同心圆,只选择其中一个;
(3)中心按照其关联的累加器值升序排列的,如果新的中心过于接近之前已接受的中心,不会被保留下来;且当有许多同心圆或近似的同心圆时,霍夫梯度法倾向保留最大的一个圆。
霍夫圆变换:HoughCircles()函数
和HoughLines和HoughLinesP的区别:不需要源图是二值的
void HoughCircles(InputArray image, OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0)
参数1 InputArray类型的image,源图像,8位灰度单通道图像
参数2 InputArray类型的circles,经过调用HoughCircles函数后此参数存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x, y, radius)表示
参数3 int类型的method,霍夫梯度法的标识符为HOUGH_GRADIENT
参数4 double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,此参数允许创建一个比输入图像分辨率低的累加器,如果dp=1,累加器和输入图像具有相同的分辨率,如果dp=2,累加器有输入图像一半大的宽度和高度
参数5 double类型的minDist,为霍夫变换检测到的圆和圆心之间的最小距离,即让算法明显区分的两个不同圆之间的最小距离。如果太小,多个相邻的圆可能被错误地检测成一个重合的圆,反之,某些圆不能被检测出来
参数6 double类型的param1,有默认值100,是第三个参数method设置的检测方法的对应参数,对当前唯一方法霍夫梯度法CV_HOUGH_GRADIENT,表示传递给canny边缘检测算子的高阈值,低阈值为高阈值的一半
参数7 double类型的param2,有默认值100,是第三个参数method设置的检测方法的对应参数,对当前唯一方法霍夫梯度法CV_HOUTH_GRADIENT,表示在检测阶段圆心的累加器阈值,它越小越可以检测到更多不存在的圆,越大则通过检测的圆更加接近完美的圆形
参数8 int类型的minRadius,有默认值0,表示圆半径最小值
参数9 int类型的maxRadius,有默认值0,表示圆半径最大值
#include
#include
using namespace cv;
using namespace std;
int main(){
Mat srcImage = imread("1.jpg");
Mat midImage,dstImage;
//转化为灰度图并进行图像平滑
cvtColor(srcImage, midImage, COLOR_GRAY2BGR);
GaussianBlur(midImage, midImage, Size(9,9), 2, 2);
//进行霍夫变换
vector<Vec3f> circles;
HoughLinesP(midImage, circles, HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0);
//在图中绘制出每条线段
for(size_t i = 0; i < circles.size(); i++){
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
circle(srcImage, center, 3, Scalar(0,255,0), -1, 8, 0); //绘制圆心
circle(srcImage, center, radius, Scalar(155, 50, 255), 3, 8, 0);
}
}
重映射是把一幅图像中某位置的像素放置到另一个图片指定位置的过程
实现重映射:remap()函数
void remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
参数1 InputArray类型的src,源图像
参数2 OutputArray类型的dst,目标图像
参数3 InputArray类型的map1,有两种可能的表示对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2, CV_32FC1或CV_32FC2类型的X值
参数4 InputArray类型的map2,有两种可能的表示对象:(1)当map1表示点(x,y)时,这个参数不代表任何值;(2)表示CV_16UC1, CV_32FC1类型的Y值(第二个值)
参数5 int类型的interpolation,插值方式,INTER_NEAREST-最近邻,INTER_LINEAR-双线性插值,INTER_CUBIC-双三次样条插值(4×4),INTER_LANCZOS4-Lanczos插值(8×8)
参数6 int类型的borderMode,边界模式,默认值BORDER_CONSTANT,表示目标图像的离群点像素值不会被此函数修改
参数7 const Scalar&类型的borderValue,当有常数边界时使用的值,有默认值Scalar(),即默认值0
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
using namespace cv;
int main(){
Mat srcImage, dstImage;
Mat map_x, map_y;
srcImage=imread("1.jpg",1);
dstImage.create(srcImage.size(), srcImage.type());
map_x.create(srcImage.size(),CV_32FC1);
map_y.create(srcImage.size(),CV_32FC1);
for(int j = 0; j < srcImage.rows; j++){
for(int i = 0; i < srcImage.cols; j++){
map_x.at<float>(j,i)=static_cast<float>(i);
map_y.at<float>(j,i)=static_cast<float>(srcImage.rows - j);
}
}
remap(srcImage, dstImage, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0,0));
}
仿射变换:在几何中,一个向量空间进行一次线性变换并接上一个平移,变换成另一个向量空间的过程,保持了二维图形的“平直性”(直线变换后依然是直线)和“平行性”(二维图形之间的相对位置关系保持不变,平行线依然是平行线,直线上点的位置顺序不变)。
仿射变换的三种常见形式:旋转rotation,平移translation,缩放scale。可以使用2×3的矩阵表示仿射变换
使用矩阵A和B对二维向量X做变换,可表示为
仿射变换可从以下两种场景获得:1、已知X和T,知道他们是有联系的,下一步求出矩阵M;2、已知M和X,想求得T,只要应用T=M.X即可。
进行仿射变换:warpAffine()函数
void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
参数1 InputArray类型的src,源图像
参数2 OutputArray类型的dst,目标图像
参数3 InputArray类型的M,2×3变换矩阵
参数4 Size类型的dsize,输出图像的尺寸
参数5 int类型的flags,插值方法,默认值INTER_LINEAR
参数6 int类型的borderMode,边界像素模式,默认值BORDER_CONSTANT
参数7 const Scalar&类型的borderValue,恒定的边界情况下取的值,默认Scalar(),即0
WarpAffine函数与cvGetQuadrangleSubPixel()函数:类似但不完全相同:WarpAffine要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像图不太合适)且输出图像的部分可以保留不变,而cvGetQuadrangleSubPix可以精确地从8位图像中提取四边形到浮点数缓存中,具有比较小的系统开销,总是全部改变输出图像的内容
计算二维旋转变换矩阵:getRotationMatrix2D()函数
Mat getRotationMatrix2D(Point2f center, double angle, double scale)
参数1 Point2f类型的center,源图像的旋转中心
参数2 double类型的angle,旋转角度,正值表示向逆时针
参数3 double类型的scale,缩放系数
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
using namespace cv;
using namespace std;
int main(){
//定义两组点,代表两个三角形
Point2f srcTriangle[3];
Point2f dstTriangle[3];
//定义Mat变量
Mat rotMat(2,3,CV_32FC1);
Mat warpMat(2, 3, CV_32FC1);
Mat srcImage, dstImage_warp, dstImage_warp_rotate;
srcImage=imread("1.jpg");
dstImage_warp=Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
//设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0]=Point2f(0,0);
srcTriangle[1]=Point2f(static_cast<float>(srcImage.cols - 1), 0);
srcTriangle[2]=Point2f(0, static_cast<float>(srcImage.rows - 1));
dstTriangle[0]=Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.33));
dstTriangle[1]=Point2f(static_cast<float>(srcImage.cols*0.65), static_cast<float>(srcImage.rows*0.35));
dstTriangle[2]=Point2f(static_cast<float>(srcImage.cols*0.15), static_cast<float>(srcImage.rows*0.6));
//求得仿射变换
warpMat=getAffineTransform(srcTriangle, dstTriangle);
//对源图像应用求得的仿射变换
warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());
//对图像进行缩放后再旋转
Point center = Point(dstImage_warp.cols/2, dstImage_warp.rows/2);
double angle = -30.0;
double scale = 0.8;
rotMat = getRotationMatrix2D(center, angle, scale);
warpAffine(dstImage_warp, dstImage_warp_rotate, rotMat, dstImage_warp.size());
imshow(WINDOW_NAME1, srcImage);
imshow(WINDOW_NAME2, dstImage_warp);
imshow(WINDOW_NAME3, dstImage_warp_rotate);
}
直方图均衡化:扩大图像的动态范围。有效利用0~255的空间,图像表现力更出色,本质是扩大量化间隔,但量化等级减小了。
void equalizeHist(InputArray src, OutputArray dst)
参数1 InputArray类型的src,源图像,8位单通道图像
参数2 OutputArray类型的dst,目标图像
步骤:(1)计算输出图像的直方图H;(2)进行直方图归一化,直方图的组距的和为255;(3)计算直方图积分;(4)以H’作为查询表进行图像变换
寻找轮廓:findContours()函数
void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
参数1 InputArray类型的image,源图像,8位单通道二进制图像,可使用compare(), inrange
参数2 OutputArrayOfArrays类型的contours,检测到的轮廓、函数调用后的运算结果存在这里,每个轮廓存储为一个点向量,用point类型的vector表示
参数3 OutputArray类型的hierarchy,可选的输出向量,包括图像的拓扑信息。其作为轮廓数量的表示,包含了许多元素,每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0]~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,对应的hierarchy[i]设置为负数
参数4 int类型的mode,轮廓检索模式。RETR-EXTERNAL:只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1;RETR-LIST:提取所有轮廓,放置在list中,检测的轮廓不建立等级关系;RETR_CCOMP:提取所有轮廓,并且将其组织为双层结构(顶层为连通域的外围边界,次层为孔的内层边界);RETR-TREE:提取所有轮廓,并重新建立网状的轮廓结构
参数5 int类型的method,轮廓的近似方法。CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置不超过1,即max(abs(x1-x2),abs(y2-y1))==1;CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息;CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS:使用Teh-Chinl链逼近算法中的一个
参数6 Point类型的offset,每个轮廓点的可选偏移量,有默认值Point(),对ROI图像中找出的轮廓,并且在整个图像中进行分析时,使用该参数
绘制轮廓:drawContours()函数
void drawContours(InputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point())
参数1 InputArray类型的image,目标图像
参数2 InputArrayOfArrays类型的contours,所有输入轮廓,每个轮廓存储为一个点向量,用point类型的vector表示
参数3 int类型的contourIdx,轮廓绘制的指示变量,如果为负值,绘制所有轮廓
参数4 const Scalar&类型的color,轮廓的颜色
参数5 int类型的thickness,轮廓线条的粗细度,默认值1,如果其为负值,绘制在轮廓内部
参数6 int类型的lineType,线条的类型,默认值8(8连通线型),4(4连通线型),LINE_AA(抗锯齿线型)
参数7 InputArray类型的hierarchy,可选的层次结构信息,有默认值noArray()
参数8 int类型的maxLevel,用于绘制轮廓的最大等级,有默认值INT_MAX
参数9 Point类型的offset,可选的轮廓偏移参数,用指定的偏移量offset=(dx,dy)偏移需要绘制的轮廓,有默认值Point()
Mat result(image.size(),CV_8U, cv::Scalar(255));
drawContours(result, contours, -1, Scalar(0), 3);
# include
# include "opencv2/highgui/highgui.hpp"
# include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
int main(int argc, char** argv){
Mat srcImage=imread("1.jpg", 0);
Mat dstImage=Mat::zeros(srcImage.rows, srcImage.cols, CV_8UC3);
srcImage = srcImage > 119;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
//查找轮廓
findContours(srcImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
//遍历所有顶层轮廓,以随机燕娥绘制出每个连接组件颜色
int index=0;
for(; index >-0; index = hierarchy[index][0]){
Scalar color(rand()&255, rand()&255, rand()&255);
drawContours(dstImage, contours, index, color, FILLED, 8, hierarchy);
}
}
凸包是一个计算几何中常见的概念,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形。理解物体形状或轮廓的一种比较有用方法是计算一个物体的凸包,然后计算其凸缺陷(A~H),很多复杂物体的特性能很好被这种缺陷表现出来。
寻找凸包:convexHull()函数
void convexHull(InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true)
参数1 InputArray类型的points,输入的二维点集,可以填Mat类型或std::vector
参数2 OutputArray类型的hull,输出参数,函数调用后找到的凸包
参数3 bool类型的clockwise,操作方向标识符,当此标识符为真时,输出的凸包为顺时针方向,否则,为逆时针方向,且假定坐标系的x轴指向左,y轴指向上方
参数4 bool类型的returnPoints,操作标识符,默认值true,当标识符为真时,函数返回各凸包的各个点,否则,返回凸包各点的指数。当输出数组是std::vector时,此标志被忽略
# include "opencv2/imgproc/imgproc.hpp"
# include "opencv2/highgui/highgui.hpp"
# include
using namespace cv;
int main(){
Mat image(600, 600, CV_8UC3);
RNG& rng= theRNG();
while(1){
char key;
int count = (unsigned)rng % 100 + 3; //随机生成的点数量
vector<Point> points; //点值
//随机生成点坐标
for(int i = 0; i < count; i++){
Point point;
point.x = rng.uniform(image.cols/4, image.cols*3/4);
point.y = rng.uniform(image.rows/4, image.rows*3/4);
points.push_back(point);
}
//检测凸包
vector<int> hull;
convexHull(Mat(points), hull, true);
//绘制出随机颜色的点
image = Scalar::all(0);
for(int i = 0; i < count; i++)
circle(image, point[i], 3, Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)), FILLED, LINE AA);
//准备参数
int hullcount=(int)hull.size();//凸包的边数
Point point0 = points[hull[hullcount - 1]]; //连接凸包边的坐标点
//绘制凸包的边
for(int i = 0; i < hullcount; i++){
Point point = points[hull[i]];
line(image, point0, point, Scalar(255,255,255), 2, LINE_AA);
point0 = point;
}
imshow("凸包检测示例",image);
//ESC,Q或者q,程序退出
key = (char)waitKey();
if (key == 27 || key == 'q' || key == 'Q') break;
}
return 0;
}
返回外部矩形边界:boundingRect()函数:返回指定点集最外面的矩形边界
Rect boundingRect(InputArray points)
参数1 points可以为输入的二维点集,可以是std::vector或Mat类型
寻找最小包围矩形:minAreaRect()函数
RotatedRect minAreaRect(InputArray points)
参数1 输入的二维点集,寻找可旋转的最小面积的包围矩形
寻找最小包围圆形:minEnclosingCircle()函数
void minEnclosingCircle(InputArray points, Point2f& center, float& radius)
参数1 InputArray类型的points,输入的二维点集,可以为std::vector<>或Mat类型
参数2 Point2f&类型的center,圆的输出圆心
参数3 float&类型的radius,圆的输出半径
用椭圆拟合二维点集:fitEllipse()函数
RotatedRect fitEllipse(InputArray points)
参数1 输入的二维点集,可以为std::vector<>或Mat类型
逼近多边形:approxPolyDP()函数
void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
参数1 InputArray类型的curve,输入的二维点集
参数2 OutputArray类型的approxCurve,多边形逼近的结果,类型与输入的二维点集类型一致
参数3 double类型的epsilon,逼近的精度
参数4 bool类型的closed,如果为真,近似的曲线为封闭曲线,否则,近似的曲线不封闭
广泛应用于图像分析,如模式识别、目标分类、目标识别与方位估计、图像编码与重构。从一幅数字图形中计算出来的矩集,描述了该图像形状的全局特征,并提供了大量图像几何信息,如大小、位置、方向、形状,广泛应用于各种图像处理、计算机视觉和机器人技术领域的目标识别与方位估计中。
一阶矩:与形状有关
二阶矩:显示图像围绕直线平均值的拓展程度
三阶矩:关于平均值的对称性测量
由二阶矩、三阶矩可以导出一组共7个不变矩,是图像的统计特性,满足平移、伸缩、旋转均不变的不变性
矩的计算:moments()函数:计算多边形和光栅形状最高达三阶的所有矩,计算形状的重心、面积、主轴和其它形状特性,如7Hu不变量
Moments moments(InputArray array, bool binaryImage=false)
参数1 InputArray类型的array,输入参数,可以是光栅图像(单通道,8位或浮点的二维数组)或二维数组
参数2 bool类型的binaryImage,默认值false,若取true,则所有的非零像素为1,此参数仅对图像使用
计算轮廓面积:contourArea()函数
double contourArea(InputArray contour, bool oriented=false)
参数1 InputArray类型的coutour,输入向量,二维点,可以为std::vector或Mat类型
参数2 bool类型的oriented,面向区域标识符。若为true,该函数返回一个带符号的面积值,其正负取决于轮廓的方向是顺时针还是逆时针,根据这个特性可以根据面积的符号来确定轮廓的位置,这个参数有默认值false,表示以绝对值返回,不带符号。
vector<Point> contour;
contour.push_back(Point2f(0,0));
contour.push_back(Point2f(10,0));
contour.push_back(Point2f(10,10));
contour.push_back(Point2f(5,4));
double area0 = contourArea(contour);
vector<Point> approx;
approxPolyDP(contour, approx, 5, true);
double areal = contourArea(approx);
计算轮廓长度:arcLength()函数
double arcLength(InputArray curve, bool closed)
参数1 InputArray类型的curve,输入的二维点集,可以为std::vector或Mat类型
参数2 bool类型的closed,一个用于指示曲线是否封闭的标识符,有默认值closed,表示曲线封闭
例:查找和绘制图像轮廓矩
//使用Canny检测边缘
Canny(g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh*2, 3);
//找到轮廓
findContours(g_cannyMat_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0,0));
//计算矩
vector<Moments> mu(g_vContours.size());
for(unsigned int i = 0; i < g_vContours.size(); i++){
mu[i] = moments(g_vContours[i], false);
}
//计算中心矩
vector<Point2f> mc(g_vContours.size());
for(unsigned int i = 0; i < g_vContours.size(); i++){
mc[i] = Point2f(static_cast<float>(mu[i].m10/mu[i].m00), static_cast<float>(mu[i].m01)/mu[i].m00)
}
//绘制图
Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);
for (unsigned int i = 0; i < g_vContours.size(); i++){
Scalar color = Scalar(g_rng.uniform(0,255), g_rng.uniform(0,255), g_rng.uniform(0,255)); //随机生成颜色值
drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point()); //绘制外层和内层轮廓
circle(drawing, mc[i], 4, color, -1, 8, 0); //绘制图
}
分水岭算法:一种基于拓扑理论的数学形态学分割方法,基本思想是把图像看作测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,集水盆的边界形成分水岭。类似模拟浸入过程,在每个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
经典算法:(1)排序;(2)淹没。对每个像素的灰度级进行从低到高的排序,然后在从低到高淹没的过程中,对每一个局部极小值在h阶高度的影响域采用先进先出结构进行判断及标注。分水岭变换得到的是输入图像的集水盆图像,集水盆之间的边界点即为分水岭。
实现分水岭算法:watershed()函数
基于标记的分割算法中的一种,在把图像传给函数之前,大致勾画标记出图像中的期望进行分割的区域,他们被标记为正指数。每个区域被标记为1,2,3等,表示成为一个或多个连接组件,这些标志值可以用findContours()函数和drawContours()函数由二进制的掩码检索出来。这些标记就是即将绘制出来的分割区域的种子,而没被标记的区域被置为0,区域间的值被设置为-1。
void watershed(InputArray image, InputOutputArray markers)
参数1 InputArray类型的src,源图像,8位3通道彩色图像
参数2 InputOutputArray类型的markers,函数调用后的运算结果存在里面,输入/输出32位单通道图像的标记结果,和源图像有一样的尺寸和类型
实现图像修补:inpaint()函数:从扫描的照片中清除灰尘和划痕,或者从静态图像或视频中去除不需要的物体
void inpaint(InputArray src, InputArray inpaintMask, OuputArray dst, double inpaintRadius, int flags)
参数1 InputArray类型的src,源图像,8位单通道或3通道图像
参数2 InputArray类型的inpaintMask,修复掩膜,8位单通道图像,非0像素表示需要修补的区域
参数3 OutputArray类型的dst,函数调用后的运算结果,和源图像有一样的尺寸和类型
参数4 double类型的inpaintRadius,需要修补的每个点的圆形邻域,修复算法的参考半径
参数5 int类型的flags,修补方法的标识符
直方图意义:(1)图像中像素强度分布的图形表达方式;(2)统计了每一个强度值所具有的像素个数。直方图将统计结果分布于一系列预定义的bins中。
dims:需要统计的特征的数目,dims=1因为只统计了灰度值(灰度图像)
bins:每个特征空间子区段的数目,可翻译为“直条”或“组距”
range:每个特征空间的取值范围,range=[0, 255]
计算直方图:calcHist()函数
void 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)
参数1 const Mat类型的images,输入数组,需为相同的深度和相同的尺寸
参数2 int类型的nimages,输入数组的个数,代表几张图像
参数3 const int类型的channels,需要统计的通道(dim)索引,第一个数组通道从0到images[0].channels()-1,第二个数组通道从images[0].channels()计算到images[0].channels()+images[1].channels()-1
参数4 InputArray类型的mask,操作掩码,如果不为空,它必须为8位,并且与images[i]有同样的大小和尺寸,这里的非0掩码元素用于标记统计直方图的数组元素数据
参数5 OutputArray类型的hist,输出的目标直方图,二维数组
参数6 int类型的dims,需要计算的直方图维度,必须为正数,且不大于CV_MAX_DIMS
参数7 const int*类型的histSize,存放每个维度的直方图尺寸的数组
参数8 const float**类型的ranges,表示每一个维度数组(参数6)的每一维边界阵列,可以理解为每一维数值的取值范围
参数9 bool类型的uniform,指示直方图是否均匀的标识符,有默认值true
参数10 bool类型的accumulate,累计标识符,有默认值false。若为true,直方图在匹配阶段不会被清零,此功能主要是允许从多个阵列中计算单个直方图,或者用于在特定时间更新直方图
寻找最值:minMaxLoc()函数
void minMaxLoc(InputArray src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, InputArray mask = noArray())
参数1 InputArray类型的src,输入的单通道阵列
参数2 double类型的minVal,返回最小值的指针,若无须返回,此值置为NULL
参数3 double类型的maxVal,返回最大值的指针,若无须返回,此值置为NULL
参数4 Point类型的minLoc,返回最小位置的指针(二维情况下),若无须返回,此值置为NULL
参数5 Point类型的maxLoc,返回最大位置的指针(二维情况下),若无须返回,此值置为NULL
参数6 InputArray类型的mask,用于选择子阵列的可选掩膜
例:绘制H-S直方图
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
int main(){
Mat srcImage, hsvImage;
srcImage=imread("1.jpg");
cvtColor(srcImage, hsvImage, COLOR_BGR2HSV);
//将色调量化为30个等级,将饱和度量化为32个等级
int hueBinNum = 30; //色调的直方图直条数量
int saturationBinNum = 32; //饱和度的直方图直条数量
int histSize[]={hueBinNum, saturationBinNum};
//定义色调的变化范围为0~179
float hueRanges[]={0,180};
//定义饱和度的变化范围为0~255
float saturationRanges[]={0,256};
const float* ranges[]={hueRanges, saturationRanges};
MatND dstHist;
//参数准备,calcHist函数中将计算第0通道和第1通道的直方图
int channels[] = {0,1};
//调用calcHist,进行直方图计算
calcHist(&hsvImage, 1, channels, Mat(), dstHist, 2, histSize, ranges, true, false);
//为绘制直方图准备参数
double maxValue=0;
minMaxLoc(dstHist, 0, &maxValue, 0, 0);//查找数组和子数组的全局最小值和最大值存入maxValue中
int scale = 10;
Mat histImg = Mat::zeros(saturationBinNum*scale, hueBinNum*10, CV_8UC3);
//双层循环,进行直方图绘制
for (int hue = 0; hue < hueBinNum; hue++)
for (int saturation = 0; saturation < saturationBinNum; saturation++){
float binValue = dstHist.at<float>(hue, saturation); //直方图直条的值
int intensity = cvRound(binValue*255/maxValue);//强度
//绘制
rectangle(histImg, Point(hue*scale, saturation*scale), Point((hue+1)*scale - 1, (saturation + 1)*scale - 1), Scalr::all(intensity), FILLED);
}
}
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
using namespace cv;
using namespace std;
int main(){
Mat srcImage = imread("1.jpg", 0);
MatND dstHist;
int dims = 1;
float hranges[] = {0,255};
const float *range[] = {hranges}; //const类型
int size = 256;
int channels = 0;
//计算直方图
calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges);
int scale = 1;
Mat dstImage(size*scale, size, CV_8U, Scalar(0));
//获取最大值和最小值
double minValue = 0;
double maxValue = 0;
minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
//绘制直方图
int hpt = saturate_cast<int>(0.9*size);
for(int i = 0; i < 256; i++){
float binValue = dstHist.at<float>(i);
int realValue = saturate_cast<int>(binValue*hpt/maxValue);
rectangle(dstImage, Point(i*scale, size-1), Point((i+1)*scale - 1, size - reaLValue), Scalar(255));
}
}
#include
#include
using namespace cv;
int main(){
Mat srcImage;
srcImage = imread("1.jpg");
//参数准备
int bins = 256;
int hist_size[] = {bins};
float range[] = {0,256};
const float* ranges[] = {range};
MatND redHist, grayHist, blueHist;
int channels_r[] = {0};
//直方图计算(红色分量部分)
calcHist(&srcImage, 1, channels_r, Mat(), redHist, 1, hist_size, ranges, true, false);
//直方图计算(绿色分量部分)
int channels_g[] = {1}
calcHist(&srcImage, 1, channels_g, Mat(), grayHist, 1, hist_size, ranges, true, false);
//直方图计算(蓝色分量部分)
int channels_b[] = {2};
calcHist(&srcImage, 1, channels_b, Mat(), blueHist, 1, hist_size, ranges, true, false);
//绘制
//参数准备
double maxValue_red, maxValue_green, maxValue_blue;
minMaxLoc(redHist, 0, &maxValue_red, 0, 0);
minMaxLoc(grayHist, 0, &maxValue_green, 0, 0);
minMaxLoc(bludHist, 0, &maxValue_blue, 0, 0);
int scale;
int histHeight = 256;
Mat histImage = Mat::zeros(histHeight, bins*3, CV_8UC3);
//绘制
for(int i =0; i < bins; i++){
//参数准备
float binValue_red = redHist.at<float>(i);
float binValue_green = grayHist.at<float>(i);
float binValue_blue = blueHist.at<float>(i);
int intensity_red = cvRound(binValue_red*histHeight/maxValue_red); //绘制的高度
int intensity_green = cvRound(binValue_green*histHeight/maxValue_green);
int intensity_blue = cvRound(binValue_blue*histHeight/maxValue_blue);
//绘制红色分量
rectangle(histImage, Point(i*scale, histHeight-1), Point((i+1)*scale-1, histHeight - intensity_red), Scalr(255, 0, 0));
//绘制绿色分量
rectangle(histImage, Point((i+bins)*scale, histHeight-1), Point((i+bins+1)*scale-1, histHeight - intensity_green), Scalr(0, 255, 0));
//绘制蓝色分量
rectangle(histImage, Point((i+bins*2)*scale, histHeight-1), Point((i+bins*2+1)*scale-1, histHeight - intensity_blue), Scalr(0, 0,255));
}
}
对比直方图:compareHist()函数
double compareHist(InputArray H1, InputArray H2, int method)
double compareHist(const SparseMat& H1, const SparseMat& H2, int method)
参数1,2 要比较的大小相同的直方图
参数3 所选择的距离标准
method=CV_COMP_CORREL(相关,Correlation)
method=CV_COMP_CHISQR(卡方,Chi-Square)
method=CV_COMP_INTERSECT(直方图相交,Intersection)
method=CV_COMP_BHATTACHARYYA或method=CV_COMP_HELLINGER(Bhattacharyya距离或Hellinger距离)
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
int main(){
//图像声明
Mat srcImage_base, hsvImage_base;
Mat srcImage_test1, hsvImage_test1;
Mat srcImage_test2, hsvImage_test2;
Mat hsvImage_halfDown;
//图像载入
srcImage_base = imread("1.jpg", 1);
srcImage_test1 = imread("2.jpg", 1);
srcImage_test2 = imread("3.jpg", 1);
//RGB->HSV
cvtColor(srcImage_base, hsvImage_base, COLOR_BGR2HSV);
cvtColor(srcImage_test1, hsvImage_test1, COLOR_BGR2HSV);
cvtColor(srcImage_test2. hsvImage_test2. COLOR_BGG2HSV);
//创建包含基准图像下半部分的半身图像(HSV格式)
hsvImage_halfDown = hsvImage_base(Range(hsvImage_base.rows/2, hsvImage_base.rows-1), Range(0, hsvImage_base.cols - 1));
//初始化计算直方图需要的形参
int h_bins = 50; int s_bins = 60;
int histSize[] = {h_bins, s_bins};
//hue取值范围从0~255,saturation取值从0~180
float h_ranges[] = (0, 256);
float s_ranges[] = {0, 180};
const float* ranges[] = {h_ranges, s_ranges};
int channels[] = {0,1};
//创建存储直方图的MatND类的实例
MatND baseHist;
MatND halfDownHist;
MatND testHist1;
MatND testHist2;
//计算基准图像、两张测试图像、半身基准图像的HSV直方图
calcHist(&hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges, true, false);
normalize(baseHist, baseHist, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, histSize, ranges, true, false);
normalize(halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvImage_test1, 1, channels, Mat(), testHist1, 2, histSize, ranges, true, false);
normalize(testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvImage_test2, 1, channels, Mat(), testHist2, 2, histSize, ranges, true, false);
normalize(testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat());
//直方图对比
for(int i = 0; i < 4; i++){
int compare_method = i;
double base_base = compareHist(baseHist, baseHist, compare_method);
double base_half = compareHist(baseHist, halfDownHist, compare_method);
double base_test1 = compareHist(baseHist, testHist1, compare_method);
double base_test2 = compareHist(baseHist, testHist2, compare_method);
}
}
反向投影:首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征的方法
检测步骤:
(1)对测试图像中的每个像素,获取色调数据并找到该色调(h(i,j), s(i,j))在直方图中的bin位置;
(2)查询模型直方图中对应bin的数值;
(3)将此数值存储在新的反射投影图像中,也可以先归一化直方图数值到0~255范围,这样可以直接显示放射投影图像(单通道图像);
(4)通过对测试图像的每个像素采用以上步骤,可以得到最终的反射投影图像
(5)使用统计学语言分析,反向投影中储存的数值代表了测试图像中该像素属于皮肤区域的概率,亮起的区域是皮肤区域的概率更大,更暗的区域则是表示为皮肤的概率更低。手掌内部的边缘和阴影影响了检测的精度。
反向投影的作用:用于在输入图像(通常较大)中查找与特定图像(通常较小或仅1个像素,模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置
反向投影的结果:包含了以每个输入图像像素点为起点的直方图对比结果,可以把它看成二维的浮点型数组、二维矩阵,或者单通道的浮点型图像
计算反向投影:calcBackProject()函数
void calcBackProject(const Mat* images, int nimages, const int* channels, InputArray hist, OuputArray backProject, const float** ranges, double scale=1, bool uniform = true)
参数1 const Mat类型的images,输入的数组,它们须为相同的深度和相同的尺寸,而通道数可以任意
参数2 int类型的nimages,输入数组的个数,第一个参数中存放了多少图像
参数3 const int类型的channels,需要统计的通道(dim)索引,第一个数组通道从0到images[0].channels()-1,第二个数组通道从images[0].channels()计算到images[0].channels()+images[1].channels()-1
参数4 InputArray类型的hist,输入直方图
参数5 OuputArray类型的backProject,目标反向投影阵列,须为单通道,并且和images[0]有相同的大小和深度
参数6 const float**类型的ranges,表示每一个维度数组(第六个参数dims)的每一维的边界阵列,可以理解为每一维数值的取值范围
参数7 double scale,默认值1,输出的反向投影可选的缩放因子,默认值为1
参数8 bool类型的uniform,指示直方图是否均匀的标识符,默认值true
通道复制:mixChannels()函数
版本1:
void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs)
参数1 Mat类型的src,输入数组,所有矩阵必须有相同的尺寸和深度
参数2 size_t类型的nsrcs,参数1 src输入的矩阵数
参数3 Mat类型的dst,输出数组,所有的矩阵必须被初始化,大小和深度需与src[0]相同
参数4 size_t类型的ndsts,参数3输入的矩阵数
参数5 int*类型的fromTo,对指定通道进行复制的数组索引
参数6 size_t类型的npairs,参数5 fromTo的索引数
版本2:
void mixChannels(cosnt vector<Mat>& src, vector<Mat>& dst, const int* fromTo, size_t npairs)
参数1 vector&类型的src,输入矩阵向量
参数2 vector&类型的dst,输出矩阵向量
参数3 const int* 类型的fromTo,对指定的通道进行复制的数组索引
参数4 size_t类型的npairs,参数3 fromTo的索引数
例:将4通道RGBA图像转换为3通道BGR和一个单独的Alpha通道图像
Mat rgba(100,100,CV_8UC4,Scalar(1,2,3,4));
Mat bgr(rgba.rows, rgba.cols, CV_8UC3);
Mat alpha(rgba.rows, rgba.cols, CV_8UC1);
Mat out[] = {bgr, alpha};
int from_to[] = {0,2,1,1,2,0,3,3};
mixChannels(&rgba, 1, out, 2, from_to, 4);
例:反向投影
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using nammespace cv;
Mat g_srcImage; Mat g_hsvImage; Mat g_hueImage;
int g_bins = 30; //直方图组距
g_srcImage = imread("1.jpg");
cvtColor(g_srcImage, g_hsvImage, COLOR_BGR2HSV);
//分离hue通道
g_hueImage.create(g_hsvImage.size(), g_hsvImage.depth());
int ch[] = {0,0};
mixChannel(&g_hsvImage, 1, &g_hueImage, 1, ch, 1);
//参数准备
MatND hist;
int histSize = MAX(g_bins, 2);
float hue_range[] = {0,180};
const float* ranges = {hue_range};
//计算直方图并归一化
calcHist(&g_hueImage, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false);
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
//计算反向投影
MatND backproj;
calcBackProject(&g_hueImage, 1, 0, hist, backproj, &ranges, 1, true);
//绘制直方图的参数准备
int w = 400; int h = 400;
int bin_w = cvRound((double)w/histSize);
Mat histImg = Mat::zeros(w, h, CV_8UC3);
//绘制直方图
for(int i = 0; i < g_bins; i++){
rectangle(histImg, Point(i*bin_w, h), Point((i+1)*bin_w, h - cvRound((hist.at<float>(i)*h/255.0))), Scalar(100,123,255), -1);
}
模板匹配:在一幅图像中寻找与另一幅模板图像最匹配部分的技术
实现模板匹配:matchTemplate()函数
void matchTemplate(InputArray image, InputArray temp1, OuputArray result, int method)
参数1 InputArray类型的image,待搜索的图像,需为8位或32位浮点型图像
参数2 InputArray类型的temp1,搜索模板,需和源图像有一样的数据类型
参数3 OutputArray类型的result,比较结果的映射图像,必须为单通道、32位浮点型图像,如果图像尺寸是W×H而temp1尺寸是w×h,此参数result一定是(W-w+1)×(H-h+1)
参数4 int型的method,指定的匹配方法
method=TM_SQDIFF(平方差匹配法)
最好匹配为0,匹配越差,匹配值越大
method=TM_SQDIFF_NORMED(归一化平方差匹配法)
method=TM_CCORR(相关匹配法)
采用模板和图像间乘法,较大的数表示匹配程度较高
method=TM_CCORR_NORMED(归一化相关匹配法)
method=TM_CCOEFF(系数匹配法)
将模板对其均值的相对值与其图像对其均值的相关值进行匹配,1表示完美匹配,-1表示糟糕匹配,0表示没有相关性
method=TM_CCOEFF_NORMED(化相关系数匹配法)
例:模板匹配
g_srcImage = imread("1.jpg", 1);
g_templateImage = imread("2.jpg", 2);
int resultImage_cols = g_srcImage.cols - g_templateImage.cols + 1;
int resultImage_rows = g_srcImage.rows - g_templateImage.rows + 1;
g_resultImage.create(resultImage_cols, resultImage_rows, CV_32FC1);
//进行匹配和标准化
matchTemplate(g_srcImage, g_templateImage, g_resultImage, g_nMatchMethod);
normalize(g_resultImage, g_resultImage, 0, 1, NORM_MINMAX, -1, Mat());
//通过minMaxLoc定位最匹配的位置
double minValue; double maxValue; Point minLocation; Point maxLocation;
Point matchLocation;
minMaxLoc(g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat());
//对于SQDIFF和SQDIFF_NORMED,越小的数值有越高的匹配结果,其余方法,数值越大匹配效果越好
if (g_nMatchMethod == TM_SQDIFF || g_nMatchMethod == TM_SQDIFF_NORMED)
matchLocation = minLocation;
else
matchLocation = maxLocation;
//绘制矩形
rectangle(srcImage, matchLocation, Point(matchLocation.x + g_templateImage.cols, matchLocation.y + g_template.rows), Scalar(0,0,255), 2, 8, 0);
rectangle(g_resultImage, matchLocation, Point(matchLocation.x + g_templateImage.cols, matchLocation.y + g_template.rows), Scalar(0,0,255), 2, 8, 0);