一、引言
图像处理是计算机视觉领域中的基础,而边缘检测和轮廓分析则是其核心任务之一。OpenCV作为一个强大的计算机视觉库,提供了众多功能强大的函数,帮助开发者实现高效的图像处理。在本文中,我们将深入探索OpenCV中的七个重要函数:Sobel算子、Laplacian算子、Canny算子、findContours、drawContours、透视变换函数以及如何通过外接矩形框识别轮廓。通过这些函数的讲解和实例演示,您将能掌握图像边缘检测、轮廓分析和透视变换等常见技术,为后续的图像处理打下坚实的基础。
二、重要函数
1. Sobel算子
cv2.Sobel(src, ddepth, dx, dy, ksize=3, scale=1, delta=0, borderType=None)
- 功能:
Sobel算子用于计算图像的边缘,通过计算图像在水平方向(X轴)和垂直方向(Y轴)的梯度,帮助检测图像中的边缘。
- 参数:
- src:输入图像,通常是灰度图(单通道图像)。
- ddepth:通常,你可以使用 -1 来表示与输入图像相同的深度,或者使用如 cv2.CV_64F 等来指定特定的深度。
- dx:x 方向上的导数阶数。如果你想要计算 x 方向上的梯度,设置这个参数为 1;如果你不关心 x 方向上的梯度,设置这个参数为 0。
- dy:y 方向上的导数阶数。如果你想要计算 y 方向上的梯度,设置这个参数为 1;如果你不关心 y 方向上的梯度,设置这个参数为 0。通常,你不会同时设置 dx 和 dy 都为 0。
- ksize:Sobel 核的大小。它必须是 1、3、5、7 或 9 之一。这个参数决定了用于计算梯度的滤波器的大小。大小为 1 时表示使用最小的滤波器,但通常你会使用更大的滤波器来平滑梯度计算。
- scale:可选参数,表示计算梯度时的缩放因子。默认值为 1,表示不进行缩放。你可以通过调整这个参数来放大或缩小梯度的结果。
- delta:可选参数,表示在将结果存储到目标图像之前要添加到结果中的可选增量值。默认值为 0,表示不添加增量。
- borderType:像素外推方法,例如 cv2.BORDER_DEFAULT、cv2.BORDER_REFLECT 等。这个参数决定了在图像边界处如何处理像素外推。
- 返回值:
返回值是一个与输入图像大小相同的矩阵,包含了图像在x和y方向的梯度值。该矩阵表示图像的梯度信息,用于边缘检测。
- 应用:边缘检测的实际案例
代码:
import cv2
img =cv2.imread("./shudu.png")
# 使用soble算子
img_sobel = cv2.Sobel(img, -1, 0, 1, ksize=3)
img_sobel_2 = cv2.Sobel(img, -1, 1, 0, ksize=3)
cv2.imshow('image',img)
cv2.imshow('img_sobel',img_sobel)
cv2.imshow('img_sobel_2',img_sobel_2)
cv2.waitKey(0)
结果展示:
2. Laplacian算子
cv2.Laplacian(src, ddepth[, ksize[, scale[, delta[, borderType]]]])
- 功能:Laplacian算子通过计算图像的二阶导数,检测图像中的边缘。与Sobel算子不同,Laplacian算子一次性综合了水平方向和垂直方向的梯度变化,因此能快速得到边缘信息。
- 参数:
- src:输入图像,通常为灰度图。
- ddepth:输出图像的所需深度。这个参数决定了输出图像的深度(数据类型)。通常,你可以使用 -1 来表示与输入图像相同的深度,或者使用 cv2.CV_64F 等来指定特定的深度。
- ksize(可选):滤波核大小,默认为1(仅支持1、3、5、7等奇数值)
- scale(可选):表示计算拉普拉斯算子时的缩放因子。默认值为 1,表示不进行缩放。你可以通过调整这个参数来放大或缩小拉普拉斯算子的结果。
- delta(可选):表示在将结果存储到目标图像之前要添加到结果中的可选增量值。默认值为 0,表示不添加增量。
- borderType(可选):像素外推方法,例如 cv2.BORDER_DEFAULT、cv2.BORDER_REFLECT 等。这个参数决定了在图像边界处如何处理像素外推。当 ksize 大于 1 时,这个参数才有意义。
- 返回值:返回一个梯度图像(矩阵),其中每个像素的值表示该点的二阶导数值(边缘强度)。较大的值对应边缘区域。
- 应用:综合数独图像水平方向和垂直方向的梯度变化
代码:
import cv2
img =cv2.imread("./shudu.png")
# 使用拉普拉斯算子
img_lap = cv2.Laplacian(img, -1,ksize=5)
cv2.imshow('image',img)
cv2.imshow('img_lap',img_lap)
cv2.waitKey(0)
结果:
3. Canny算子
cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
- 功能:Canny算子用于检测图像中的边缘。它通过以下步骤实现:
(1)降噪:使用高斯滤波器平滑图像,减少噪声影响。
(2)计算梯度和方向:使用Sobel算子计算每个像素点的梯度幅值和方向。
(3)非极大值抑制:确保边缘的精确性,去掉梯度方向上非边缘点。
(4)双阈值检测筛选:确定强边缘、弱边缘以及非边缘。
- 参数:
-image:输入图像,它应该是一个灰度图像(单通道)。
-threshold1:第一个阈值,用于边缘检测的滞后过程。这个值较低,用于确定边缘的初始点。
-threshold2:第二个阈值,用于边缘检测的滞后过程。这个值较高,用于确定边缘的最终点。如果某个像素点的梯度值高于这个阈值,它被认为是边缘;如果低于这个值但高于threshold1,并且与高于threshold2的像素点相连,它也被认为是边缘。
-edges:输出图像,与输入图像大小相同,但通常是二值图像(即只包含边缘和非边缘的像素)。
-apertureSize(可选,默认为3):Sobel算子的大小,它决定了梯度计算的邻域大小。它必须是1、3、5或7之一。
-L2gradient(可选,默认为False):一个布尔值,指示是否使用更精确的L2范数进行梯度计算。如果为True,则使用L2范数(即欧几里得距离);如果为False,则使用L1范数(即曼哈顿距离)。L2范数通常更精确,但计算成本也更高。
- 返回值:返回一个与输入图像大小相同的二值图像,其中:
- 白色像素(255)表示边缘。
- 黑色像素(0)表示非边缘区域。
- 应用:精确边缘检测
代码:
import cv2
img = cv2.imread("./card.png")
# 灰度化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化
_, img_binary = cv2.threshold(img_gray,130,255,cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 进行高斯滤波
img_blur = cv2.GaussianBlur(img_binary, (3,3),3)
# 边缘检测
img_canny = cv2.Canny(img_blur, 30,70)
cv2.imshow('image',img)
cv2.imshow('img_gray',img_gray)
cv2.imshow('img_binary',img_binary)
cv2.imshow('img_canny',img_canny)
cv2.waitKey(0)
结果:
4. findContours函数
contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
- 功能:用于检测二值图像中的轮廓。
- 它可以将边界连续的白色区域识别为一个轮廓(通常是目标物体的形状)。
- 常用在处理二值化后的图像,例如经过阈值处理或 Canny 边缘检测后的结果。
- 参数:
-image:输入图像,必须是二值图像(非零像素被视为目标,零像素为背景)。
- mode:轮廓检索模式,用于决定提取的轮廓层级关系:
- cv2.RETR_EXTERNAL:只检索最外层轮廓。
- cv2.RETR_LIST:检索所有轮廓,不建立层级关系。
- cv2.RETR_CCOMP:检索所有轮廓,建立两级层次结构。
- cv2.RETR_TREE:检索所有轮廓,并建立完整的层次结构(父子关系)。
- method:轮廓近似方法,用于减少轮廓点数以节省计算量:
- cv2.CHAIN_APPROX_NONE:存储所有轮廓点。
- cv2.CHAIN_APPROX_SIMPLE:只存储拐点,从而压缩水平、垂直和对角线段的冗余点。
- contours(可选):存储轮廓点的列表(与返回值重复,较少用)。
- hierarchy(可选):存储轮廓层次关系的数组。
- offset(可选):可选的偏移量,用于调整轮廓坐标。
- 返回值:
- contours:轮廓列表,其中每个轮廓是一个点集的数组。例如:`contours[0]` 表示第一个轮廓,包含其所有点的坐标。
- hierarchy:层次关系数组,形状为 (轮廓数, 4),每行的四个值分别表示当前轮廓的下一轮廓、前一轮廓、子轮廓和父轮廓的索引。
5. drawContours函数
cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
- 功能:将指定的轮廓绘制在图像上,用于显示或标注目标区域的边界。
- 参数:
- image:目标图像,用于绘制轮廓,通常是彩色图像。
- contours:轮廓点列表,通常由 `cv2.findContours` 返回,每个轮廓是一个点集。
- contourIdx:要绘制的轮廓索引:
-1:绘制所有轮廓。
具体索引:绘制指定的轮廓(如第0个轮廓)。
color:轮廓颜色(如 `(0, 255, 0)` 表示绿色),使用 BGR 格式。
thickness(可选):轮廓线的厚度,默认为 1。
正值:设置具体厚度。
-1:填充轮廓内部。
- lineType(可选):线条类型,默认为 `cv2.LINE_8`。
`cv2.LINE_4`:4连接线。
`cv2.LINE_8`:8连接线。
`cv2.LINE_AA`:抗锯齿线。
- hierarchy(可选):层次结构数组,用于指定绘制哪些轮廓。
- maxLevel(可选):绘制轮廓的最大层次,仅在 `hierarchy` 不为空时有效。
- offset(可选):偏移量,用于调整绘制轮廓的坐标。
- 应用:轮廓可视化
代码:
import cv2
import numpy as np
img = cv2.imread("./31.png")
# 灰度化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化
_, img_binary = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 寻找轮廓
contours, _ = cv2.findContours(img_binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
img_draw = img.copy()
cv2.drawContours(img_draw,contours,-1,(0,0,255),3)
cv2.imshow('image',img)
cv2.imshow('image_draw',img_draw)
cv2.waitKey(0)
结果 :
6. 透视变换函数
可跳转另一篇博客阅读
图像矫正小白指南:从理论到实践的简单实现-CSDN博客
7. 轮廓的外接边界框
(1)外接矩形
x, y, w, h = cv2.boundingRect(contour)
- 功能:外接矩形是包围轮廓的一个矩形,它的边缘与图像坐标轴平行
-参数:
- contour:输入的轮廓,是通过 `cv2.findContours()` 获取的一个轮廓。
-返回值
- x, y:外接矩形左上角的坐标。
- w, h:外接矩形的宽度和高度。
(2)最小外接矩形
box = cv2.minAreaRect(contour)
- 功能:最小外接矩形(也叫旋转矩形)是包围轮廓的一个矩形,但它可以根据轮廓的方向进行旋转,以最小化矩形的面积。这种矩形能够更紧凑地包围轮廓,适应有旋转的物体形状。
- 参数:
contour:输入的轮廓。
-返回值:
- box:返回一个最小外接矩形,包含:
- center:矩形中心点的坐标 (cx, cy)。
- size:矩形的宽度和高度 (w, h)。
- angle:矩形的旋转角度(相对于水平线的角度)。
对比:
外接矩形:适合简单的目标检测任务,尤其是当物体不旋转或旋转不明显时,计算简单且快速。
最小外接矩形:适合需要更精确描述轮廓,尤其是当物体存在旋转时,它能提供更加紧凑的包围矩形。
代码:
import cv2
import numpy as np
img = cv2.imread("./outline.png")
# 灰度化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
# 二值化
_, img_binary = cv2.threshold(img_gray,
127,
255,
cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# 寻找轮廓
contours, _ = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
img_draw = img.copy()
cv2.drawContours(img_draw, contours, -1, (0, 0, 255), 2)
# 给所有轮廓都绘制 外接
for i in contours:
# 第一种:调用外接矩形函数,获取当前轮廓点的左上角的坐标(x, y) 宽w 高h
x, y, w, h = cv2.boundingRect(i)
# 画矩形
cv2.rectangle(img_draw, [x, y], [x+w, y+h], (0, 255, 0), 2)
# 第二种:调用最小面积外接矩形函数,获取包含三个元素的元组(中心点坐标、长宽、旋转角度)
# ((center_x, center_y), (width, height), angle)
ret = cv2.minAreaRect(i)
# 调用cv2.boxPoints(ret)可以获取旋转矩阵的四个顶点
box = np.int32(cv2.boxPoints(ret))
# 绘制轮廓
cv2.drawContours(img_draw, [box], -1, (255, 255, 0), 3)
cv2.imshow('image', img)
cv2.imshow('image_draw', img_draw)
cv2.waitKey(0)
结果展示:
三、 结语
在本文中,我们探讨了多个图像处理技术,包括 Sobel 算子、Laplacian 算子、Canny 边缘检测、轮廓提取与绘制、以及透视变换等。通过这些基础方法,能够有效地处理图像中的边缘和形状,帮助我们从复杂的图像中提取出有用的信息。掌握这些技术,对于图像处理和计算机视觉的进一步学习与应用至关重要。希望本文能够为你深入理解这些常见操作提供一定的帮助,继续探索和应用这些工具来解决实际问题。