一、 二值化
threshold函数python版原型:
retval, dst = cv.threshold( src, thresh, maxval, type[, dst] )
参数说明:
src:原图像。
dst:结果图像。
thresh:当前阈值。
maxVal:最大阈值,一般为255.
thresholdType:阈值类型,主要有下面几种:
enum ThresholdTypes {
THRESH_BINARY = 0,
THRESH_BINARY_INV = 1,
THRESH_TRUNC = 2,
THRESH_TOZERO = 3,
THRESH_TOZERO_INV = 4,
THRESH_MASK = 7,
THRESH_OTSU = 8,
THRESH_TRIANGLE = 16
};
返回值:
retval: 与参数thresh一致
dst: 结果图像
编号 阈值类型枚举
注意
1 THRESH_BINARY
2 THRESH_BINARY_INV
3 THRESH_TRUNC
4 THRESH_TOZERO
5 THRESH_TOZERO_INV
6 THRESH_MASK
不支持
7 THRESH_OTSU
不支持32位
8 THRESH_TRIANGLE
不支持32位
具体说明如下:
注意:
THRESH_OTSU和THRESH_TRIANGLE是作为优化算法配合THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO以及THRESH_TOZERO_INV来使用的。
当使用了THRESH_OTSU和THRESH_TRIANGLE两个标志时,输入图像必须为单通道。
二、膨胀
2.1 结构元形状构造函数
getStructuringElement( int shape, Size ksize, Point anchor )
其参数解释如下:
shape:1)MORPH_RECT 表示产生矩形的结构元
2)MORPH_ELLIPSEM 表示产生椭圆形的结构元
3)MORPH_CROSS 表示产生十字交叉形的结构元
ksize:表示结构元的尺寸,即(宽,高),必须是奇数
anchor:表示结构元的锚点,即参考点。默认值Point(-1, -1)代表中心像素为锚点
2.2 相关的API:
void cvDilate( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 );
其参数解释如下:
src:表示输入矩阵
element:表示结构元,即 函数getStructuringElement( )的返回值
anchor:结构元的锚点,即参考点
iterations:膨胀操作的次数,默认为一次
borderType:边界扩充类型
borderValue:边界扩充值
三、腐蚀
腐蚀操作与膨胀操作类似,只是它取结构元所指定的领域内值的最小值作为该位置的输出灰度值。因为取每个位置领域内最小值,所以腐蚀后输出图像的总体亮度的平均值比起原图会有所降低,图像中比较亮的区域的面积会变小甚至消失,而较暗物体的尺寸会扩大。
OpenCV中提供给了erode()函数来完成膨胀操作,其函数声明如下:
其中参数解释如下:
src:表示输入矩阵
dst:表示输出矩阵
element:表示结构元,即 函数getStructuringElement( )的返回值
anchor:结构元的锚点,即参考点
iterations:腐蚀操作的次数,默认为一次
borderType:边界扩充类型
borderValue:边界扩充值
三、TrackBar
opencv提供了一种称为轨迹条或滑动条(Trackbar)的控件工具,能够直观的改变出现处理时的参数,实时看到更改这些参数时对于图像处理结果的影响。createTrackbar这个函数我们以后会经常用到,它创建一个可以调整数值的轨迹条,并将轨迹条附加到指定的窗口上,使用起来很方便。首先大家要记住,它往往会和一个回调函数配合起来使用。先看下他的函数声明:
int createTrackbar(conststring& trackbarname, conststring& winname, int* value, int count, TrackbarCallback onChange=0,void* userdata=0);
其中参数解释如下:
trackbarname,表示轨迹条的名字,用来代表我们创建的轨迹条。
winname,填窗口的名字,表示这个轨迹条会依附到哪个窗口上,即对应namedWindow()创建窗口时填的某一个窗口名。
value,一个指向整型的指针,表示滑块的位置。并且在创建时,滑块的初始位置就是该变量当前的值。
count,表示滑块可以达到的最大位置的值。滑块最小的位置的值始终为0。
onChange,首先注意他有默认值0。这是一个指向回调函数的指针,每次滑块位置改变时,这个函数都会进行回调。并且这个函数的原型必须为void XXXX(int,void*);其中第一个参数是轨迹条的位置,第二个参数是用户数据(看下面的参数)。如果回调是NULL指针,表示没有回调函数的调用,仅第三个参数value有变化。
userdata,他也有默认值0。这个参数是用户传给回调函数的数据,用来处理轨迹条事件。如果使用的第三个参数value实参是全局变量的话,完全可以不去管这个userdata参数。
这个createTrackbar函数,为我们创建一个具有特定名称和范围的轨迹条(Trackbar,或者说是滑块范围控制工具),指定一个和轨迹条位置同步的变量。而且要指定回调函数onChange(第五个参数),在轨迹条位置改变的时候来调用这个回调函数。并且我们知道,创建的轨迹条显示在指定的winname(第二个参数)所代表的窗口上
回调函数:
void CallBack_func(int, void*)
{
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1)); //创建结构元
dilate(src, dst, structureElement, Point(-1, -1), 1); //调用膨胀API
imshow("膨胀操作后:", dst);
}
四、图像的滤波
滤波处理分为两大类:线性滤波和非线性滤波。OpenCV里有这些滤波的函数,使用起来非常方便,现在简单介绍其使用方法。
线性滤波:方框滤波、均值滤波、高斯滤波
方框滤波
boxFilter(img, out, -1, Size(5, 5));//-1指原图深度
均值滤波
均值滤波就是方框滤波的一个特殊情况。
均值滤波的缺点就是不能很好地保护细节,在图像去燥的同时也破坏了图像的而细节部分,从而使图像变得模糊,不能很好的去除噪点。
blur(img, out,Size(5, 5));//-1指原图深度
高斯滤波
高斯滤波,可以消除高斯噪声,广泛应用于图像处理的减噪过程。
GaussianBlur(img, out, Size(3, 3), 0, 0);
非线性滤波:中值滤波、双边滤波
中值滤波
基本思想就是用像素点的领域灰度的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像的细节(不会出现边缘模糊的情况)。
中值滤波跟均值滤波的思想看起来很相似,只是一个取平均值,一个取中位数而已
medianBlur(img, out, 7);//第三个参数表示孔径的线性尺寸,它的值必须是大于1的奇数
现在说说中值滤波与均值滤波的比较:均值滤波中噪声成分会被加入到平均计算,所以输出是受到噪声的影响的。但是中值滤波中,由于噪声成分很难选上,所以基本不影响输出。当然好的性能也需要付出一点代价的,中值滤波花费的时间是均值滤波的5倍以上。
中值滤波一般采用奇数的卷积核。
中值滤波对一些细节多(特别是细、尖顶的)的图像不太适合。
双边滤波
双边滤波的最大特点就是做边缘保存
bilateralFilter(img, out, 25, 25 * 2, 25 / 2);
开运算:先腐蚀再膨胀,用来消除小物体
闭运算:先膨胀再腐蚀,用于排除小型黑洞
形态学梯度:就是膨胀图与俯视图之差,用于保留物体的边缘轮廓。
顶帽:原图像与开运算图之差,用于分离比邻近点亮一些的斑块。
黑帽:闭运算与原图像之差,用于分离比邻近点暗一些的斑块。
opencv里有一个很好的函数getStructuringElement,我们只要往这个函数传相应的处理参数,就可以进行相应的操作了,使用起来非常方便。
下面列举一下相应的操作宏定义。
对图像进行缩放的最简单方法当然是调用resize函数啦!
resize函数可以将源图像精确地转化为指定尺寸的目标图像。
要缩小图像,一般推荐使用CV_INETR_AREA来插值;若要放大图像,推荐使用CV_INTER_LINEAR。
现在说说调用方式
第一种,规定好你要图片的尺寸,就是你填入你要的图片的长和高。
Mat dst = Mat::zeros(512, 512, CV_8UC3); //我要转化为512*512大小的 resize(img, dst, dst.size());
第二种,填入你要缩小或者放大的比率。
Mat dst; resize(img, dst, Size(),0.5,0.5);//我长宽都变为原来的0.5倍
五、边缘检测的一般步骤:
滤波——消除噪声
增强——使边界轮廓更加明显
检测——选出边缘点
Canny算法
Canny边缘检测算法被很多人推崇为当今最优秀的边缘检测算法,所以我们第一个就介绍他。
opencv中提供了Canny函数。
C++ API:
void cv::Canny ( InputArray image, (输入图像:8-bit)
OutputArray edges, (输出边缘图像:单通道,8-bit,size与输入图像一致)
double threshold1, (阈值1)
double threshold2, (阈值2)
int apertureSize=3, (Sober算子大小)
bool L2gradient=false (是否采用更精确的方式计算图像梯度)
)
void cv::Canny ( InputArray dx, (输入图像在x方向的导数:16-bit(CV_16SC1或CV_16SC3))
InputArray dy, (输入图像在y方向的导数:16-bit(CV_16SC1或CV_16SC3))
OutputArray edges, (输出边缘图像:单通道,8-bit,size与输入图像一致)
double threshold1, (阈值1)
double threshold2, (阈值2)
bool L2gradient=false (是否采用更精确的方式计算图像梯度)
)
Sobel算法
//求x方向梯度 Sobel(img, grad_x, CV_16S, 1, 0, 3, 1, 1,BORDER_DEFAULT); convertScaleAbs(grad_x, abs_grad_x); imshow("x方向soble", abs_grad_x); //求y方向梯度 Sobel(img, grad_y,CV_16S,0, 1,3, 1, 1, BORDER_DEFAULT); convertScaleAbs(grad_y,abs_grad_y); imshow("y向soble", abs_grad_y); //合并梯度 addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst); imshow("整体方向soble", dst);
Laplacian算法
cvtColor(img, gray, COLOR_RGB2GRAY); //使用Laplace函数 //第三个参数:目标图像深度;第四个参数:滤波器孔径尺寸;第五个参数:比例因子;第六个参数:表示结果存入目标图 Laplacian(gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT);
六、轮廓提取
轮廓的提取,边缘检测就可以做到,不过得到的轮廓比较粗糙。
图像轮廓的提取先对图像二值化,再通过findContours()函数提取轮廓,最后通过drawContours()函数将轮廓绘制出来。在将轮廓提取的结果使用imwrite函数保存到本地时,总是写不了,查了半天没找出问题,刚开始文件名为con.bmp,最后把文件名改成cont.bmp就好了,玄学。。。
1、轮廓边缘关键点可通过findContours()函数来得到。findContours()函数的定义如下:
声明1:
findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point());
声明2:
findContours( InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point());
名字:轮廓查找函数
描述:用于查找边缘轮廓坐标点
参数:
第一个参数:image,8位单通道图像矩阵,可以是灰度图,但更常用的是二值图像,可以使从canny()得到的图像,也可以是threshhold()函数得到的图像。’
第二个参数:contours,定义为“vector contours”,是一个向量,并且是一个双重向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓。
第三个参数:hierarchy,表示层数,定义为“vector hierarchy”,这一输出将是一个数组(通常仍是标准模板库向量),每条轮廓对应一个数组中一个值,数组中的每个值都是一个四元数组。
第四个参数:int型的mode,定义轮廓的检索模式:
取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓, 所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围 内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
第五个参数:int型的method,定义轮廓的近似方法:
取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
drawContours()函数定义如下:
2、轮廓边缘的绘制可通过drawContours()函数来实现。drawContours()函数的定义如下:
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const cv::Scalar& color int thickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )
名字:轮廓查找函数
描述:用于查找边缘轮廓坐标点
参数:
第一个参数:image,表示目标图像,
第二个参数:contours,表示输入的轮廓组,每一组轮廓由点vector构成,
第三个参数:contourIdx,指明画第几个轮廓,如果该参数为负值(通常设为-1),则画全部轮廓,如果是一个正数,则对应的轮廓被绘制。
第四个参数;color,为轮廓的颜色,
第五个参数;thickness,为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
第六个参数;lineType,为线型,
第七个参数;为轮廓结构信息,
第八个参数;为maxLevel
轮廓提取主要代码如下:
Mat contImg = Mat ::zeros(grayImg.size(),CV_8UC3);//定义三通道轮廓提取图像
Mat binImg;
threshold(grayImg, binImg, 127, 255, THRESH_OTSU);//大津法进行图像二值化
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
//查找轮廓
findContours(binImg, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
//绘制查找到的轮廓
drawContours(contImg, contours, -1, Scalar(0,255,0));