opencv基础篇 ——(七)边缘检测和图像锐化

        锐化和边缘检测是图像处理中常用的两种技术,它们可以用来增强图像的特征以及检测图像中的边缘

锐化: 锐化是一种增强图像中细节和边缘的技术,它使图像中的过渡区域更加明显,从而提高图像的清晰度和对比度。常见的锐化方法包括拉普拉斯算子和高斯滤波器等。

边缘检测: 边缘检测是一种寻找图像中突变区域的技术,它可以帮助我们找到图像中不同区域之间的边界。常见的边缘检测算法包括 Sobel、Canny 和 Scharr 等。

拉普拉斯算子laplacian 

        cv::laplacian 是 OpenCV 库中用于计算图像拉普拉斯算子(Laplacian operator)的函数,常用于边缘检测和图像锐化。拉普拉斯算子是一个二阶微分算子,可以捕捉图像中灰度值的快速变化,即边缘和细节变化。

函数声明:

void cv::laplacian(
    InputArray src,
    OutputArray dst,
    int ddepth,
    int ksize = 1,
    double scale = 1,
    double delta = 0,
    int borderType = BORDER_DEFAULT);

参数说明:

  • InputArray src: 输入图像,可以是单通道 8-bit、16-bit 或 32-bit 浮点型图像,或者是多通道图像(在这种情况下,每个通道分别计算拉普拉斯响应,然后组合成输出图像)。

  • OutputArray dst: 输出图像,存储计算得到的拉普拉斯响应。数据类型取决于 ddepth 参数。

  • int ddepth: 输出图像的数据深度。可以选择如下值:

    • -1:表示与输入图像 src 同样的深度。
    • CV_8UCV_16UCV_16SCV_32F 或 CV_64F:指定输出图像的具体数据类型。
  • int ksize: 拉普拉斯滤波器的大小,必须为奇数且大于等于 1。较大的 ksize 可以平滑噪声,但可能会降低边缘定位的精度;较小的 ksize 对噪声敏感,但能更好地保留边缘细节。

  • double scale: 用于调整拉普拉斯响应的尺度因子。计算出的拉普拉斯值乘以 scale 后再存储到输出图像中。默认值为 1

  • double delta: 加到拉普拉斯响应上的偏置值。通常用于避免由于计算导致的负值或值过小的问题,有助于保持结果的动态范围。默认值为 0

  • int borderType: 边界填充类型,定义了如何处理图像边缘以外的像素。可选值包括:

    • BORDER_CONSTANT: 使用指定的 borderValue 填充边缘。
    • BORDER_REPLICATE: 复制边缘像素值。
    • BORDER_REFLECT: 反射边缘像素。
    • BORDER_WRAP: 包裹边缘像素。
    • 其他边界模式,具体参见 OpenCV 文档。

    默认值为 BORDER_DEFAULT,相当于 BORDER_REFLECT_101,即边缘像素以一种类似镜面反射的方式扩展。

工作原理与应用:

cv::laplacian 函数计算输入图像 src 上的拉普拉斯算子响应。拉普拉斯算子本质上是对图像在二维空间上进行二次微分,其数学表达式为:

其中,( f(x, y) ) 表示图像在坐标 ( (x, y) )处的灰度值。通过计算图像在水平和垂直方向上的二阶导数之和,拉普拉斯算子能够检测到灰度值的过零点(zero-crossings),这些点通常对应图像中的边缘位置。

在实际计算过程中,cv::laplacian 函数使用一个离散化的模板(卷积核)来近似拉普拉斯算子。模板的大小由 ksize 参数指定,常见的如 3x35x5 等。模板内的系数构成一个类似于: 

的对角线权重分布,中心像素与周围像素的差异被放大,从而凸显边缘。

应用示例:

// 读取输入图像
cv::Mat src = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);

// 计算拉普拉斯响应,输出图像为 CV_8U 类型
cv::Mat laplacianImage;
cv::Laplacian(src, laplacianImage, CV_8U, 3);

// 将拉普拉斯响应进行阈值处理以获得边缘图像
cv::threshold(laplacianImage, laplacianImage, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

// 显示结果
cv::imshow("Original Image", src);
cv::imshow("Laplacian Edges", laplacianImage);
cv::waitKey();

边缘检测算法Canny 

  cv::Canny 是 OpenCV 库中用于实现 Canny 边缘检测算法的函数。Canny 边缘检测是一种广泛应用的边缘检测方法,因其能够提供高质量、单像素宽且无断点的边缘,同时具备较好的抗噪性能而闻名。

函数声明:

void cv::Canny(
    InputArray image,
    OutputArray edges,
    double threshold1,
    double threshold2,
    int apertureSize = 3,
    bool L2gradient = false);

参数说明:

  • InputArray image: 输入图像,通常为单通道 8-bit 或浮点型灰度图像。为了得到最佳结果,建议先对彩色图像进行灰度化处理。

  • OutputArray edges: 输出边缘图像,与输入图像 image 同样大小,存储类型为 8-bit 单通道图像,其中边缘像素值为非零值(通常为 255),非边缘像素值为零。

  • double threshold1: 较低阈值。任何边缘强度高于此阈值的像素被认为是潜在边缘。

  • double threshold2: 较高阈值。只有那些连接到至少一个高于较高阈值的边缘像素的潜在边缘才会最终被保留为真正的边缘。

  • int apertureSize: Sobel 导数计算时使用的卷积核大小,一般取值为 3 或 5。较大的值可以提供更精确的边缘方向估计,但计算成本更高。

  • bool L2gradient: 是否使用 L2 范数(欧几里得范数)来计算图像梯度。默认值为 false,表示使用 L1 范数(绝对值和)。

工作原理与应用:

Canny 边缘检测算法通常包含以下四个步骤:

  1. 高斯滤波: 首先对输入图像进行高斯平滑,以减少噪声对边缘检测的影响。这一步通常使用高斯核进行卷积操作。

  2. 梯度计算: 计算平滑后的图像在水平和垂直方向上的一阶导数(梯度)。OpenCV 使用 Sobel 算子或者 Scharr 算子(如果指定了 L2gradient=true)来完成这一任务。计算得到的梯度包括幅值(表示边缘强度)和方向。

  3. 非极大值抑制: 在梯度方向上对每个像素进行局部比较,如果该像素的梯度幅值不是其梯度方向上邻域内的最大值(即非极大值),则将其梯度幅值置零,以消除边缘检测过程中产生的虚假响应。

  4. 双阈值检测: 应用两个阈值(threshold1 和 threshold2)来确定最终的边缘。所有梯度幅值高于较高阈值的像素被认为是边缘像素;对于幅值介于高低阈值之间的像素,如果它们与已确定的边缘像素(即幅值超过较高阈值的像素)相连,则也被认为是边缘像素。低于较低阈值的像素被舍弃。

应用示例:

// 读取输入图像并转换为灰度
cv::Mat src = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);

// 设置 Canny 边缘检测参数
double lowThreshold = 100.0;
double highThreshold = 200.0;

// 执行 Canny 边缘检测
cv::Mat edges;
cv::Canny(src, edges, lowThreshold, highThreshold, 3);

// 显示结果
cv::imshow("Original Image", src);
cv::imshow("Canny Edges", edges);
cv::waitKey();

索贝尔算子 Sobel

        Sobel 算子是一种常用的边缘检测算子,通过计算图像在特定方向上的一阶或二阶导数来检测图像中的边缘。

函数声明:

void cv::Sobel(
    InputArray src,
    OutputArray dst,
    int ddepth,
    int dx,
    int dy,
    int ksize = 3,
    double scale = 1,
    double delta = 0,
    int borderType = BORDER_DEFAULT);

参数说明:

  • InputArray src: 输入图像,通常为单通道 8-bit 或浮点型灰度图像。为了得到最佳结果,建议先对彩色图像进行灰度化处理。

  • OutputArray dst: 输出图像,存储计算得到的 Sobel 算子响应。数据类型取决于 ddepth 参数。

  • int ddepth: 输出图像的数据深度。可以选择如下值:

    • -1:表示与输入图像 src 同样的深度。
    • CV_8UCV_16UCV_16SCV_32F 或 CV_64F:指定输出图像的具体数据类型。
  • int dx 和 int dy: 分别表示在 x 轴和 y 轴方向上的微分阶数。通常 dx 和 dy 取值为 1(表示一阶导数),用于计算图像在水平和垂直方向上的边缘。若两者均为 0,则 cv::Sobel 函数相当于进行图像平滑处理。也可以取值为 2(表示二阶导数),用于计算图像的拉普拉斯响应(边缘强度)。

  • int ksize: Sobel 核函数的大小,必须为奇数且大于等于 1。常见的取值有 357 等。较大的 ksize 可以平滑噪声,但可能会降低边缘定位的精度;较小的 ksize 对噪声敏感,但能更好地保留边缘细节。

  • double scale: 用于调整 Sobel 响应的尺度因子。计算出的 Sobel 值乘以 scale 后再存储到输出图像中。默认值为 1

  • double delta: 加到 Sobel 响应上的偏置值。通常用于避免由于计算导致的负值或值过小的问题,有助于保持结果的动态范围。默认值为 0

  • int borderType: 边界填充类型,定义了如何处理图像边缘以外的像素。可选值包括:

    • BORDER_CONSTANT: 使用指定的 borderValue 填充边缘。
    • BORDER_REPLICATE: 复制边缘像素值。
    • BORDER_REFLECT: 反射边缘像素。
    • BORDER_WRAP: 包裹边缘像素。
    • 其他边界模式,具体参见 OpenCV 文档。

    默认值为 BORDER_DEFAULT,相当于 BORDER_REFLECT_101,即边缘像素以一种类似镜面反射的方式扩展。

工作原理与应用:

cv::Sobel 函数计算输入图像 src 上的 Sobel 算子响应。Sobel 算子本质上是一种离散化的微分算子,用于估计图像在特定方向上的导数。对于一阶导数,Sobel 算子的核函数在 x 轴和 y 轴方向上分别为:

-1   0   1
-2   0   2
-1   0   1

-1  -2  -1
 0   0   0
 1   2   1

计算得到的响应可以反映图像在相应方向上灰度值的变化情况,即边缘信息。当 dx 和 dy 为 1 时,cv::Sobel 函数返回图像在 x 轴和 y 轴方向上的梯度;当两者均为 2 时,返回图像的拉普拉斯响应,即边缘强度。

在实际应用中,cv::Sobel 函数通常用于计算图像在 x 轴和 y 轴方向上的梯度幅值和方向,进而用于边缘检测、图像分析和计算机视觉任务。计算得到的 Sobel 响应可以进一步通过阈值处理、非极大值抑制等步骤提取出图像的边缘。

应用示例:

// 读取输入图像并转换为灰度
cv::Mat src = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);

// 计算 Sobel 响应在 x 轴和 y 轴方向上的响应
cv::Mat sobelX, sobelY;
cv::Sobel(src, sobelX, CV_32F, 1, 0, 3);
cv::Sobel(src, sobelY, CV_32F, 0, 1, 3);

// 计算梯度幅值和方向
cv::Mat gradientMag, gradientAngle;
cv::cartToPolar(sobelX, sobelY, gradientMag, gradientAngle, true);

// 应用阈值处理提取边缘
double edgeThresh = 50.0;
cv::Mat edges;
cv::threshold(gradientMag, edges, edgeThresh, 255, cv::THRESH_BINARY);

// 显示结果
cv::imshow("Original Image", src);
cv::imshow("Sobel Edges", edges);
cv::waitKey();

Scharr算子

        Scharr 算子是一种专门设计用于图像边缘检测的微分算子,它是 Sobel 算子的一种改进版本,特别是在处理较小的内核尺寸(如 3x3)时,Scharr 算子能提供更高的精度。

函数声明:

void cv::Scharr(
    InputArray src,
    OutputArray dst,
    int ddepth,
    int dx,
    int dy,
    double scale = 1,
    double delta = 0,
    int borderType = BORDER_DEFAULT);

参数说明:

  • InputArray src: 输入图像,通常为单通道 8-bit 或浮点型灰度图像。为了得到最佳结果,建议先对彩色图像进行灰度化处理。

  • OutputArray dst: 输出图像,存储计算得到的 Scharr 算子响应。数据类型取决于 ddepth 参数。

  • int ddepth: 输出图像的数据深度。可以选择如下值:

    • -1:表示与输入图像 src 同样的深度。
    • CV_8UCV_16UCV_16SCV_32F 或 CV_64F:指定输出图像的具体数据类型。
  • int dx 和 int dy: 分别表示在 x 轴和 y 轴方向上的微分阶数。通常 dx 和 dy 取值为 1(表示一阶导数),用于计算图像在水平和垂直方向上的边缘。若两者均为 0,则 cv::Scharr 函数相当于进行图像平滑处理。

  • double scale: 用于调整 Scharr 响应的尺度因子。计算出的 Scharr 值乘以 scale 后再存储到输出图像中。默认值为 1

  • double delta: 加到 Scharr 响应上的偏置值。通常用于避免由于计算导致的负值或值过小的问题,有助于保持结果的动态范围。默认值为 0

  • int borderType: 边界填充类型,定义了如何处理图像边缘以外的像素。可选值包括:

    • BORDER_CONSTANT: 使用指定的 borderValue 填充边缘。
    • BORDER_REPLICATE: 复制边缘像素值。
    • BORDER_REFLECT: 反射边缘像素。
    • BORDER_WRAP: 包裹边缘像素。
    • 其他边界模式,具体参见 OpenCV 文档。

    默认值为 BORDER_DEFAULT,相当于 BORDER_REFLECT_101,即边缘像素以一种类似镜面反射的方式扩展。

工作原理与应用:

cv::Scharr 函数计算输入图像 src 上的 Scharr 算子响应。Scharr 算子是一种精确的一阶微分算子,其核函数在设计时考虑了像素间的距离权重,使得在小内核尺寸(如 3x3)下也能更精确地逼近图像的一阶导数。对于一阶导数,Scharr 算子的核函数在 x 轴和 y 轴方向上分别为:

 3   10   3
 0    0   0
-3  -10  -3

-3   0    3
-10   0   10
-3    0   3

相比于传统的 Sobel 算子,Scharr 算子的权重分配更接近于理论上的精确一阶导数计算,尤其是在接近图像边缘的像素处,这使得它在相同内核尺寸下能提供更精确的边缘检测结果。

在实际应用中,cv::Scharr 函数通常用于计算图像在 x 轴或 y 轴方向上的梯度幅值和方向,进而用于边缘检测、图像分析和计算机视觉任务。计算得到的 Scharr 响应可以进一步通过阈值处理、非极大值抑制等步骤提取出图像的边缘。

应用示例:

// 读取输入图像并转换为灰度
cv::Mat src = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);

// 计算 Scharr 响应在 x 轴和 y 轴方向上的响应
cv::Mat scharrX, scharrY;
cv::Scharr(src, scharrX, CV_32F, 1, 0);
cv::Scharr(src, scharrY, CV_32F, 0, 1);

// 计算梯度幅值和方向
cv::Mat gradientMag, gradientAngle;
cv::cartToPolar(scharrX, scharrY, gradientMag, gradientAngle, true);

// 应用阈值处理提取边缘
double edgeThresh = 50.0;
cv::Mat edges;
cv::threshold(gradientMag, edges, edgeThresh, 255, cv::THRESH_BINARY);

// 显示结果
cv::imshow("Original Image", src);
cv::imshow("Scharr Edges", edges);
cv::waitKey();

spatialGradient 算子

        cv::spatialGradient 是 OpenCV 库中用于计算图像的局部空间梯度(即 x 和 y 方向的一阶导数)的函数。该函数返回一个包含两个元素的 std::vector<cv::Mat>,分别表示图像在 x 轴和 y 轴方向上的梯度响应。

std::vector<cv::Mat> cv::spatialGradient(
    InputArray src,
    OutputArray dx,
    OutputArray dy,
    int ksize = 3,
    int borderType = BORDER_DEFAULT);

参数说明:

  • InputArray src: 输入图像,通常为单通道 8-bit 或浮点型灰度图像。为了得到最佳结果,建议先对彩色图像进行灰度化处理。

  • OutputArray dx 和 OutputArray dy: 可选的输出参数,分别用于存储图像在 x 轴和 y 轴方向上的梯度响应。如果不提供这两个参数,函数将返回一个包含两个元素的 std::vector<cv::Mat> 结果。

  • int ksize: 梯度计算所使用的卷积核大小,必须为奇数且大于等于 1。常见的取值有 357 等。较大的 ksize 可以平滑噪声,但可能会降低边缘定位的精度;较小的 ksize 对噪声敏感,但能更好地保留边缘细节。

  • int borderType: 边界填充类型,定义了如何处理图像边缘以外的像素。可选值包括:

    • BORDER_CONSTANT: 使用指定的 borderValue 填充边缘。
    • BORDER_REPLICATE: 复制边缘像素值。
    • BORDER_REFLECT: 反射边缘像素。
    • BORDER_WRAP: 包裹边缘像素。
    • 其他边界模式,具体参见 OpenCV 文档。

    默认值为 BORDER_DEFAULT,相当于 BORDER_REFLECT_101,即边缘像素以一种类似镜面反射的方式扩展。

工作原理与应用:

cv::spatialGradient 函数计算输入图像 src 上的局部空间梯度,即在 x 轴和 y 轴方向上的一阶导数。函数内部可能使用 Sobel 算子、Scharr 算子或其他适当的微分算子来计算梯度。计算得到的梯度响应反映了图像在相应方向上灰度值的变化情况,即边缘信息。

在实际应用中,cv::spatialGradient 函数通常用于计算图像在 x 轴和 y 轴方向上的梯度幅值和方向,进而用于边缘检测、图像分析和计算机视觉任务。计算得到的梯度响应可以进一步通过阈值处理、非极大值抑制等步骤提取出图像的边缘。

应用示例:

// 读取输入图像并转换为灰度
cv::Mat src = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);

// 计算图像的 x 和 y 方向梯度
cv::Mat dx, dy;
cv::spatialGradient(src, dx, dy, 3);

cv::Mat fdx, fdy;
dx.convertTo(fdx, CV_32F);
dy.convertTo(fdy, CV_32F);

// 计算梯度幅值和方向
cv::Mat gradientMag, gradientAngle;
cv::cartToPolar(fdx, fdy, gradientMag, gradientAngle, true);

// 应用阈值处理提取边缘
double edgeThresh = 50.0;
cv::Mat edges;
cv::threshold(gradientMag, edges, edgeThresh, 255, cv::THRESH_BINARY);

// 显示结果
cv::imshow("Original Image", src);
cv::imshow("Spatial Gradient Edges", edges);
cv::waitKey();

效果展示

laplacian、canny算子效果

sobel、scharr算子效果

spatialGradient 算子

  • 34
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在Python和OpenCV中实现直线检测,可以使用Hough变换来检测直线。Hough变换是一种常用的图像处理方法,可用于检测直线、圆等几何形状。 以下是一个简单的示例代码,使用Hough变换来检测直线并计算交点: ```python import cv2 import numpy as np # 读取图像 img = cv2.imread('test.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 边缘检测 edges = cv2.Canny(gray, 50, 150, apertureSize=3) # Hough变换检测直线 lines = cv2.HoughLines(edges, 1, np.pi/180, 200) # 计算交点 points = [] for i in range(len(lines)): for j in range(i+1, len(lines)): rho1, theta1 = lines[i][0] rho2, theta2 = lines[j][0] if abs(theta1 - theta2) < np.pi/4: continue a = np.array([[np.cos(theta1), np.sin(theta1)], [np.cos(theta2), np.sin(theta2)]]) b = np.array([rho1, rho2]) x0, y0 = np.linalg.solve(a, b) points.append((int(x0), int(y0))) # 绘制直线和交点 for line in lines: rho, theta = line[0] a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) cv2.line(img, (x1,y1), (x2,y2), (0,0,255), 2) for point in points: cv2.circle(img, point, 5, (0,255,0), -1) # 显示图像 cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在代码中,首先读取图像并进行灰度转换和边缘检测。然后使用Hough变换检测直线,并计算交点。最后绘制直线和交点,并显示图像。 需要注意的是,在计算交点时,需要将两条直线的极坐标表示转换为直角坐标表示,并使用线性方程组求解。 希望这个例子能够帮助到你实现直线检测并计算交点。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值