数字图像处理系列(四)—图像边缘检测
随着课程的一步步学习,我们已经学过了对于图像进行的图像的点增强(log,grammar,直方图均匀化)和图像的去噪即模板化运算(均值滤波,中值滤波,高斯滤波),以及如何产生噪声(椒盐噪声和高斯噪声),其中我们最需要注意到是其中的模板化运算的概念。而这次我要说的就是如何进行对于图像中的目标物进行分割提取,从而为以后的进行分析特征和性质作为铺垫
1.图像分割的基本概念
1.边缘的定义
边缘定义为图像局部特性的不连续性(相邻区域之交界),比如说图像中个人和环境会在个人的轮廓和环境之间有一定的像素变化。
2.边缘的特点
- 不连续性
- 边缘位置的微分性
- 幅度(相差的大小)和方向性(对于图像是垂直方向的边缘还是水平方向)
3.边缘的信息
- 边缘的不确定性最大
- 边缘的信息最集中
4.边缘检测的思路
边缘检测算法的基本思想:计算
局部微分算子
可分成两步:1.对图像中每一个像素施以检测算子 2.根据事先确定的准则对检测算子的输出进行判定,确定该像素点是否为边缘点。
2.边缘检测算法
参见的边缘检测算法有:
1)梯度法
1.梯度的计算(采用微分的方法,因为是离散的像素,所以使用的是差分方程)
- 方向:在函数f(x, y)最大变化率的方向上
- 幅度:G[f(x,y)] = ( ∂ f ∂ x \frac{\partial f}{\partial x} ∂x∂f^2+ ∂ f ∂ y \frac{\partial f}{\partial y} ∂y∂f^2) ^1/2
2.梯度的三种表示
- 欧氏距离:( ∂ f ∂ x \frac{\partial f}{\partial x} ∂x∂f^2+ ∂ f ∂ y \frac{\partial f}{\partial y} ∂y∂f^2) ^1/2
- 城区距离(模为1) : ∂ f ∂ x \frac{\partial f}{\partial x} ∂x∂f的绝对值+ ∂ f ∂ y \frac{\partial f}{\partial y} ∂y∂f的绝对值
- 棋盘距离: max( ∂ f ∂ x \frac{\partial f}{\partial x} ∂x∂f, ∂ f ∂ y \frac{\partial f}{\partial y} ∂y∂f)
3.梯度的计算
4.差分计算的方法:
- 水平垂直差分法:px(x,y) = f(x+1,y)-f(x,y);py(x,y)=f(x,y+1)-f(x,y)
- 罗伯特梯度法:px(x,y) = f(x+1,y+1)-f(x,y);py(x,y)=f(x+1,y)-f(x,y+1)
3.常见算子
1.SOBEL算子
- 离散微分算子,用来计算图像灰度的近似梯度
- 引入了平均因素, 对图像中的随机噪声有一定的平滑作用
- 相隔两行或两列之差分, 边缘两侧元素得到了增强,边缘显得粗而亮
- 又称为一阶微分算子,求导算子,在水平和垂直方向上求导,得到图像x方向和y方向的方向梯度图像
卷积模板:
2.其他常见算子
3.拉普拉斯算子(常用的边缘增强的二阶算子)
- 公式: Δ 2 f \Delta^2 f Δ2f = ∂ 2 f ∂ x 2 \frac{\partial^2 f}{\partial x^2} ∂x2∂2f+ ∂ 2 f ∂ y 2 \frac{\partial^2 f}{\partial y^2} ∂y2∂2f = 4*f(x,y)-f(x-1,y)-f(x+1,y)-f(x,y-1)-f(x,y+1)
卷积模板:
4.课后作业
1.使用SOBEL算子进行边缘检测
/***********************************************************
*作业: 使用SOBEL算子进行边缘检测
*自定义函数采用G=sqrt(Gx*Gx+Gy*Gy),系统函数采用G=abs(Gx)*0.5+abs(Gy)*0.5
*
*作者: ***
*日期: 2020/04/03
*
***********************************************************/
# include<opencv2/opencv.hpp>
# include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
void sobel_x(Mat& src, Mat& dst); //水平方向的相减
void sobel_y(Mat& src, Mat& dst); //垂直方向的相减
void sobel(Mat& src1, Mat& src2,Mat& dst); //进行整合
int main()
{
Mat image = imread("d:/texting.jpg", 0);
if (image.empty())
{
cout << "未找到文件,请检查目录" << endl;
return -1;
}
//自定义的函数
Mat sobelx_image = Mat::zeros(image.size(), CV_8UC1);
sobel_x(image, sobelx_image);
Mat sobely_image = Mat::zeros(image.size(), CV_8UC1);
sobel_y(image, sobely_image);
Mat sobel_image = Mat::zeros(image.size(), CV_8UC1);
sobel(sobelx_image, sobely_image, sobel_image);
//系统的库函数
Mat sobelx = Mat::zeros(image.size(), CV_8UC1);
Sobel(image, sobelx, CV_16S, 1, 0, 3); //dx=1,dy=0,表示计算X方向的导数
Mat sobely = Mat::zeros(image.size(), CV_8UC1);
Sobel(image, sobely, CV_16S, 0, 1, 3); //dx=0,dy=1,表示计算y方向的导数
Mat abs_sobelx, abs_sobely,sobels;
convertScaleAbs(sobelx, abs_sobelx,1,0); //第三个参数1表示乘法,第四个参数表示偏移量即 abs_sobelx = abs(1*sobelx+0)
convertScaleAbs(sobely, abs_sobely,1,0);
addWeighted(abs_sobelx, 0.5, abs_sobely, 0.5, 0, sobels); //使用权重和偏移量的方法
imshow("image", image);
imshow("slobel_x", sobelx_image);
imshow("slobel_y", sobely_image);
imshow("slobel_image", sobel_image);
imshow("slobel1x", sobels);
waitKey(0);
return 0;
}
void sobel_x(Mat& src, Mat& dst)
{
int width = src.rows;
int height = src.cols;
for (int row = 0; row < width; ++row)
{
for (int col = 0; col < height; ++col)
{
if (row >= 1 && row < width - 1 && col >= 1 && col < height - 1)//保证图像边缘去掉
{
//生成变量,到时进行赋值给中心的那个数
int x = 0;
//根据sobel算子进行加减
x -= src.at<uchar>(row - 1, col - 1);
x += src.at<uchar>(row - 1, col + 1);
x -= src.at<uchar>(row , col - 1)*2;
x += src.at<uchar>(row - 1, col + 1)*2;
x -= src.at<uchar>(row + 1, col - 1);
x += src.at<uchar>(row + 1, col + 1);
//判断是否超出0-255
if (x < 0) x = 0;
if (x > 255) x = 255;
//进行赋值给输出的图像
dst.at<uchar>(row,col) = x;
}
}
}
}
void sobel_y(Mat& src, Mat& dst)
{
int width = src.rows;
int height = src.cols;
for (int row = 0; row < width; ++row)
{
for (int col = 0; col < height; ++col)
{
if (row >= 1 && row < width - 1 && col >= 1 && col < height - 1)//保证图像边缘去掉
{
//生成变量,到时进行赋值给中心的那个数
int x = 0;
//根据sobel算子进行加减
x -= src.at<uchar>(row - 1, col - 1);
x -= src.at<uchar>(row - 1, col ) * 2;
x -= src.at<uchar>(row - 1, col + 1) ;
x += src.at<uchar>(row + 1, col - 1) ;
x += src.at<uchar>(row + 1, col ) * 2;
x += src.at<uchar>(row + 1, col + 1);
//判断是否超出0-255
if (x < 0) x = 0;
if (x > 255) x = 255;
//进行赋值给输出的图像
dst.at<uchar>(row, col) = x;
}
}
}
}
void sobel(Mat& src1,Mat & src2, Mat& dst)
{
int width = src1.rows;
int height = src1.cols;
for (int row = 0; row < width; ++row)
{
for (int col = 0; col < height; ++col)
{
if (row >= 1 && row < width - 1 && col >= 1 && col < height - 1)//保证图像边缘去掉
{
int x = 0;
x = int(sqrt(src1.at<uchar>(row, col) * src1.at<uchar>(row, col) + src2.at<uchar>(row, col) * src2.at<uchar>(row, col)))+2;
//判断是否超出0-255
if (x < 0) x = 0;
if (x > 255) x = 255;
//进行赋值给输出的图像
dst.at<uchar>(row, col) = x;
}
}
}
}
通过这节课学会了边缘检测的方法以及常用的算子,在下面进行的就是图像分割的另外一种方法区域增长。