首先应该区分边缘检测跟轮廓查找的区别,书里的章节把边缘检测放到了第七章:图像变换里,而把轮廓查找放到了第八章:图像轮廓与图像分割修复里。边缘检测算法仅是根据图像像素之间的差异,检测处轮廓边界的像素,但并未把轮廓当作一个整体。而轮廓查找可以将边缘变为一整个轮廓,并可以对其进行一系列其他操作,如矩的计算,轮廓面积计算,轮廓长度计算等。
边缘检测的一般步骤:-》滤波,一般为高斯核Size(5,5)-》增强,可通过计算梯度幅值来确定-》检测,一般通过阈值化的方法检测
Canny边缘检测算法的步骤为:
1:消除噪声。
2:计算梯度幅值和方向(曾强)
3:非极大值抑制
4:滞后阈值
滞后阈值有两个阈值,一个为高阈值,一个为低阈值。
若某一像素高于高阈值,则被保留
若低于低阈值,则被排除
若在两者之间,该像素仅在连接到一个高于高阈值的像素的时候被保留
Canny()函数:
void Canny(
InputArray ima, #输入图像
outputArray edges, #输出图像
double thresholdl, #高阈值
double thresholed2, #低阈值
int apertureSize=3, #Sobel算子大小
bool L2gradient = false#默认值
);
Canny()函数使用:
// 【1】将原图像转换为灰度图像
cvtColor( srcImage, grayImage, COLOR_BGR2GRAY );
// 【2】先用使用 3x3内核来降噪
blur( grayImage, edge, Size(3,3) );
// 【3】运行Canny算子
Canny( edge, edge, 3, 9,3 );
soble算法的步骤:
1:分别在X,Y方向对图像I进行求导,分别得到x,y方向的梯度Gx,Gy。
2:对图像每一点求近似梯度:G=Gx,Gy的平方和再开根号
Soble()函数:
void Soble(
InputArray src, #输入图像
OutputArray dst, #输出图像
int ddepth, #输出图像深度
int dx, #x方向的差分阶数,一般取1
int dy, #y方向的差分阶数,一般取1
int ksize=3, #Soble核大小,一般取3,必须为奇数
double scale=1, #默认值1
double delta=0, #默认值0
int borderType=BORDER_DEFAULT #边界模式
);
Soble()函数使用:
//【1】求 X方向梯度
Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );
imshow("【效果图】 X方向Sobel", abs_grad_x);
//【2】求Y方向梯度
Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );
imshow("【效果图】Y方向Sobel", abs_grad_y);
//【3】合并梯度(近似)
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );
imshow("【效果图】整体方向Sobel", dst);
函数一:findCountours()
void findCountours(
InputoutputArray image, #8位单通道图像
outputArrayOfArrays contours, #point类型的点向量
OutputArray hierarchy, #可选输出向量 hierarchy【0】hierarchy【1】...
int mode, #轮廓检索模式
int method. #轮廓的近似办法
Pointoffset=Point() #每个轮廓点的可选偏移量
);
找出轮廓后,紧接着自然是将轮廓画出来,就用到绘制轮廓函数
函数二:drawCountours()函数
void drawCountours(
InputOutArray image, #目标图像
InputArrayOfArrays contours, #point 类型的vector 存储所有输入的轮廓
int contourIdx, #轮廓绘制的指定变量,如果其为负值,则绘制所有轮廓
const Scalar& color, #轮廓颜色
int thickness, #轮廓线条粗度,默认值1
int lineType, #线条的类型,默认值8
InputArray hierarchy=noArray(), #可选层次结构信息
int maxLevel=INT_MAX, #用于绘制的最大等级
Point offset=Point() #可选的轮廓偏移参数
)
寻找轮廓的使用:
// 用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));
// 绘出轮廓
Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);
for (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());
}
思路就是,如果你直接使用findCountours()函数,效果并不会想象的那样,如下
只用轮廓查找:
我们可以看到效果,边缘其实并不是我们想的那个边缘,这是我们需要先对图像进行Canny()检测,将边缘检测出来,但这时检测出来的边缘并不是一个整体,就需要用边缘查找findContours()函数来查找并将边缘变为一个整体,从而方便对边缘进行进一步处理和运算:
加了Canny()的边缘检测:
我们可以看到边缘已经被画出来了,线条是用的随机颜色画出的。这时我们还可以对边缘进行一些额外的处理和计算了,会用到一些其他函数,如:
计算轮廓面积:contourArea()
计算轮廓长度:arcLength()
矩的计算: moments()
这些函数在一些特殊的地方会有妙用,比如判断,筛选,返回形状的重心,面积,主轴(moments())等。
在图像变换这一章中还有一部分用的很多,很重要的一节:霍夫变换。在图像处理和计算机视觉领域中,如何从当前的图像中提取算需要的特征信息是图像识别的关键所在。霍夫变换可以很好的检测直线和圆。
函数原型:
C++: void HoughLines(
InputArray image, #输入图像,需要8位单通道二进制图像,但可以将任意图像源加载进来,再由该函数转换为该格式
OutputArray lines, #输出矢量
double rho, #以像素为单位的距离精度
double theta, #以弧度为单位的角度精度
int threshold, #累加平面的阈值参数
double srn=0,
double stn=0
)
cvHoughCircles(
CvArr* image, #输入图像,8位单通道灰度图
void* circle_storage, #圆的输出矢量
int method, #使用的检测方法 标识符:HOUGH_GRADIENT
double dp, #1.5
double min_dist, #检测到的圆与圆之间的最小距离
double param1=100,
double param2=100, #这个值越大,检测到的圆越完美
int min_radius=0, #圆半径的最小值
int max_radius=0 #圆半径的最大值
);