边缘检测的基本方法有很多:
一阶的有Roberts Cross算子,Prewitt算子,Sobel算子,Canny算子,Krisch算子,罗盘算子;
二阶的还有Laplacian算子、Marr-Hildreth。
Roberts算子边缘检测
import cv2
import numpy as np
from scipy import signal
def roberts(I, _boundary='fill', _fillvalue=0):
# 图像的高,宽
H1, W1 = I.shape[0:2]
# 卷积核的尺寸
H2, W2 = 2, 2
# 卷积核1 和 锚点的位置
R1 = np.array([[1, 0], [0, -1]], np.float32)
kr1, kc1 = 0, 0
# 计算full卷积
IconR1 =signal.convolve2d(I,R1,mode='full', boundary=_boundary, fillvalue=_fillvalue)
IconR1 = IconR1[H2 - kr1 - 1:H1 + H2 - kr1 - 1, W2 - kc1 - 1:W1 + W2 - kc1 - 1]
# 卷积核2 和 锚点的位置
R2 = np.array([[0, 1], [-1, 0]], np.float32)
kr2, kc2 = 0, 1
# 再计算full卷积
IconR2=signal.convolve2d(I,R2, mode='full', boundary=_boundary, fillvalue=_fillvalue)
IconR2 = IconR2[H2 - kr2 - 1:H1 + H2 - kr2 - 1, W2 - kc2 - 1:W1 + W2 - kc2 - 1]
return (IconR1, IconR2)
if __name__ == '__main__':
I = cv2.imread('./cat/cat1_noise.jpg', cv2.IMREAD_GRAYSCALE)
# 显示原图
cv2.imshow('origin', I)
# 卷积,注意边界一般扩充采用的symm
IconR1, IconR2 = roberts(I, 'symm')
# 45度方向上的边缘强度的灰度级显示
IconR1 = np.abs(IconR1)
edge45 = IconR1.astype(np.uint8)
cv2.imshow('edge45', edge45)
# 135度方向上的边缘强度的灰度级显示
IconR2 = np.abs(IconR2)
edge135 = IconR2.astype(np.uint8)
cv2.imshow('edge135', edge135)
# 用平方和的开方来衡量最后输出的边缘
edge = np.sqrt(np.power(IconR1, 2.0) + np.power(IconR2, 2.0))
edge = np.round(edge)
edge[edge > 255] = 255
edge = edge.astype(np.uint8)
# 显示边缘
cv2.imshow('edge', edge)
cv2.waitKey(0)
cv2.destroyAllWindows()
Prewitt算子
# -*- coding: utf-8 -*-
# By:Eastmount
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('./cat/cat1.png')
lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Prewitt算子
kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
# 转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示图形
titles = ['原始图像', 'Prewitt算子']
images = [lenna_img, Prewitt]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
Sobel算子
import cv2
img = cv2.imread("./cat/cat1.png")
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# -------------------Sobel边缘检测------------------------
x = cv2.Sobel(gray_img, cv2.CV_16S, 1, 0)
y = cv2.Sobel(gray_img, cv2.CV_16S, 0, 1)
# cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
# 可选参数alpha是伸缩系数,beta是加到结果上的一个值,结果返回uint类型的图像
Scale_absX = cv2.convertScaleAbs(x) # convert 转换 scale 缩放
Scale_absY = cv2.convertScaleAbs(y)
result = cv2.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
# ----------------------显示结果----------------------------
cv2.imshow('img', gray_img)
cv2.imshow('Scale_absX', Scale_absX)
cv2.imshow('Scale_absY', Scale_absY)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Canny算子
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('./cat/cat1.png',0)
edges = cv.Canny(img, 100, 200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
Laplacian(拉普拉斯)算子
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('./cat/cat1.png')
lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 拉普拉斯算法
dst = cv2.Laplacian(grayImage, cv2.CV_16S, ksize=3)
Laplacian = cv2.convertScaleAbs(dst)
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示图形
titles = ['原始图像', 'Laplacian算子']
images = [lenna_img, Laplacian]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
Marr-Hildreth边缘检测
import numpy as np
import matplotlib.pyplot as plt
import cv2
def edgesMarrHildreth(img, sigma):
# 根据输入的高斯标准差确定窗口大小,3 * sigma占99.7%
size = int(2 * (np.ceil(3 * sigma)) + 1)
# 生成(-size / 2 + 1, size / 2 )的网格
x, y = np.meshgrid(np.arange(-size / 2 + 1, size / 2 + 1), np.arange(-size / 2 + 1, size / 2 + 1))
# 计算LoG核
kernel = ((x ** 2 + y ** 2 - (2.0 * sigma ** 2)) / sigma ** 4) * np.exp(
-(x ** 2 + y ** 2) / (2.0 * sigma ** 2)) # LoG filter
kern_size = kernel.shape[0]
# 生成与输入图像相同大小的全零矩阵log
log = np.zeros_like(img, dtype=float)
# 应用LoG核
for i in range(img.shape[0] - (kern_size - 1)):
for j in range(img.shape[1] - (kern_size - 1)):
window = img[i:i + kern_size, j:j + kern_size] * kernel
log[i, j] = np.sum(window)
# 将log由float转换为int64
log = log.astype(np.int64, copy=False)
# 生成与log相同大小的全零矩阵zero_crossing
zero_crossing = np.zeros_like(log)
# 判断零交叉点
for i in range(log.shape[0] - (kern_size - 1)):
for j in range(log.shape[1] - (kern_size - 1)):
if log[i][j] == 0:
if (log[i][j - 1] < 0 and log[i][j + 1] > 0) or (log[i][j - 1] < 0 and log[i][j + 1] < 0) or (
log[i - 1][j] < 0 and log[i + 1][j] > 0) or (log[i - 1][j] > 0 and log[i + 1][j] < 0):
zero_crossing[i][j] = 255
if log[i][j] < 0:
if (log[i][j - 1] > 0) or (log[i][j + 1] > 0) or (log[i - 1][j] > 0) or (log[i + 1][j] > 0):
zero_crossing[i][j] = 255