文章目录


算子详情:

​卷积核——Roberts、Prewitt、Sobel、Lapacian、DoG、LoG算子​

一、Sobel算子

  • 离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度
  • Soble算子功能集合高斯平滑和微分求导
  • 一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方法与Y方向梯度图像

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_opencv

Scharr算子

由于Sobel算子求取导数的近似值,kernel=3时不是很准确,图像中较弱的边缘提取效果较差,OpenCV使用改进版本Scharr函数,算子如上右图:

例如一个 3 * 3 的 Sobel 算子,在梯度角度水平或垂直方向时,其不精确性就非常明显。Scharr 算子是对 Sobel 算子差异性的增强,两者之间的在检测图像边缘的原理和使用方式上相同。

而 Scharr 算子的主要思路是通过将模版中的权重系数放大来增大像素值间的差异,也是计算 x 或 y 方向上的图像差分。

sobel,Scharr算子等此类算子都是:输出图像深度 >= 输入图像深度

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_opencv_02

Sobel() 与 convertScaleAbs()


void Sobel( InputArray src,     输入图像(单通道)

OutputArray dst,     输出图像


int ddepth,       输出图像深度 (>= 输入图像深度)


int dx,         x梯度(几阶导数)一般为0、1、2,其中0表示这个方向上没有求导


int dy,         y梯度(几阶导数)


int ksize = 3,      滤波窗口大小(3,5,7…)


double scale = 1,    是否缩放,默认=1.0


double delta = 0,     偏移常数


int borderType = BORDER_DEFAULT )  图像边缘处理方式。默认值=cv2.BORDER_DEFAULT


在经过处理后,需要用 convertScaleAbs() 函数将其转回原来的uint8形式,否则将无法显示图像,而只是一副灰色的窗口。


void convertScaleAbs( InputArray src,       输入图像

OutputArray dst,    输出uint8类型图片


double alpha = 1,     伸缩系数


double beta = 0     偏移值:加到结果上的一个值


)


由于Sobel算子是在两个方向计算的,最后还需要用cv::addWeighted(src1, alpha, src2, beta, gamma, dst)函数将其组合起来。

头文件 quick_opencv.h:声明类与公共函数

#pragma once
#include <opencv2\opencv.hpp>
using namespace cv;

class QuickDemo {
public:
...
void edge_extract_Demo(Mat& image1);
void laplance_Demo(Mat& image1);
void canny_Demo(Mat& image1);
};

主函数调用该类的公共成员函数

#include <opencv2\opencv.hpp>
#include <quick_opencv.h>
#include <iostream>
using namespace cv;


int main(int argc, char** argv) {
Mat src = imread("D:\\Desktop\\jianbian.png");
if (src.empty()) {
printf("Could not load images...\n");
return -1;
}
QuickDemo qk;
qk.edge_extract_Demo(src1);
qk.laplance_Demo(src1);
qk.canny_Demo(src1);
waitKey(0);
destroyAllWindows();
return 0;
}

sobel 演示

源文件 quick_demo.cpp:实现类与公共函数

void QuickDemo::edge_extract_Demo(Mat& image) {
Mat gau_dst, gray_dst, xgrad, ygrad;
GaussianBlur(image, gau_dst, Size(3, 3), 10);
cvtColor(gau_dst, gray_dst, COLOR_BGR2GRAY);

//Sobel(gray_dst, xgrad, -1, 1, 0, 3);
//Sobel(gray_dst, ygrad, -1, 0, 1, 3);

//Sobel(gray_dst, xgrad, CV_16S, 1, 0, 3);
//Sobel(gray_dst, ygrad, CV_16S, 0, 1, 3);

Scharr(gray_dst, xgrad, CV_16S, 1, 0, 3);
Scharr(gray_dst, ygrad, CV_16S, 0, 1, 3);
convertScaleAbs(xgrad, xgrad);
convertScaleAbs(ygrad, ygrad);

Mat xygrad;
addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);
imshow("xgrad", xgrad);
imshow("ygrad", ygrad);
imshow("xygrad", xygrad);

Mat xy_grad = Mat::zeros(xgrad.size(),xgrad.type());
int width = xgrad.cols;
int height = xgrad.rows;
for (int h = 0; h < height; h++) {
uchar* current_x = xgrad.ptr<uchar>(h);
uchar* current_y = ygrad.ptr<uchar>(h);
uchar* current_d = xy_grad.ptr<uchar>(h);
for (int w = 0; w < width; w++) {
*current_d++ = saturate_cast<uchar>(*current_x++ + *current_y++);
}
}
imshow("xy_grad", xy_grad);
}

输出图像深度:-1(与原图保持一致)

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_边缘提取_03


输出图像深度:CV_16S 方向梯度细节更多了。

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_边缘提取_04


使用自定义函数直接相加结果(右): (使用addWeighted函数,各方向梯度0.5的权重组合发虚:左)

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_opencv_05

测试scharr()效果:细节更加丰富

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_#include_06

二、laplance算子

在二阶导数的时候,最大变化处的值为零即边缘是零值。通过二阶导数计算,依据此理论我们可以计算图像二阶导数,提取边缘。

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_opencv_07


void Laplacian( InputArray src,      输入图像(单通道)


OutputArray

dst,      输出图像


int

ddepth,        输出图像深度 (>= 输入图像深度)


int

ksize = 1,       滤波窗口大小(3,5,7…)


double

scale = 1,     是否缩放,默认=1.0


double

delta = 0,     偏移常数


int

borderType = BORDER_DEFAULT   图像边缘处理方式


)


一般处理流程

  • 高斯模糊 – 去噪声GaussianBlur()
  • 转换为灰度图像cvtColor()
  • 拉普拉斯 – 二阶导数计算Laplacian()
  • 取绝对值convertScaleAbs()
  • 显示结果

源文件 quick_demo.cpp:实现类与公共函数

void QuickDemo::laplance_Demo(Mat& image) {
Mat gau_dst, gray_dst, laplance_dst, ygrad;

GaussianBlur(image, gau_dst, Size(3, 3), 10);
cvtColor(gau_dst, gray_dst, COLOR_BGR2GRAY);

Laplacian(gray_dst, laplance_dst, CV_16S, 3, 1.0, 0);
convertScaleAbs(laplance_dst, laplance_dst);

threshold(laplance_dst, laplance_dst, 0, 255, THRESH_OTSU | THRESH_BINARY);
imshow("laplance_dst", laplance_dst);
}

演示效果:

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_窗口大小_08

三、canny 边缘提取

Canny算法介绍 - 非最大信号抑制

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_#include_09


T1, T2为阈值,凡是高于T2的都保留,凡是小于T1都丢弃,从高于T2的像素出发,凡是大于T1而且相互连接的,都保留。最终得到一个输出二值图像。

推荐的高低阈值比值为 T2: T1 = 3:1/2:1其中T2为高阈值,T1为低阈值 InputArray

image,        输入图像(单通道)

OutputArray edges,     输出图像

double threshold1,      阈值T1

double threshold2,     阈值T2 一般为 2*T1

int apertureSize = 3,     Soble算子的size窗口大小 

bool L2gradient = false   默认false L1归一化,Ture则为L2归一化

);

一般处理流程

  1. 高斯模糊 - GaussianBlur
  2. 灰度转换 - cvtColor
  3. 计算梯度 – Sobel/Scharr
  4. 非最大信号抑制
  5. 高低阈值输出二值图像
void QuickDemo::canny_Demo(Mat& image) {
Mat gau_dst, gray_dst, grad_dst;

GaussianBlur(image, gau_dst, Size(3, 3), 10);
cvtColor(gau_dst, gray_dst, COLOR_BGR2GRAY);

Canny(gray_dst, grad_dst, 50.0, 100.0, 3, false);
imshow("grad_dst", grad_dst);
}

OpenCV + CPP 系列(廿一)图像卷积二 (sobel、laplance、canny)边缘提取_#include_10