一、canny算法原理
- 灰度化
- 高斯濾波
- 通過對待濾波像素及其相鄰點進行加權均值計算,可以去掉圖像上的噪點
- 提取邊緣
- 非極大值抑制
- 梯度值較大的點可能是真正的邊緣像素點,也可能是由顏色變化引起或噪點,想要過濾掉非邊緣可以使用非極大值抑制法
- 像素點和沿梯度方向上相鄰的兩個像素點進行比較,如果此像素點是三個點中梯度值最大的,則保留,否則置為0
- 雙閾值法
- 給定兩個閾值,如果像素點的梯度值高於高閾值,則為強邊緣,如果低於低閾值,則不是邊緣,介於低閾值和高閾值之間的為弱邊緣,對於弱邊緣又分兩種情況:一種是真實邊緣附近的點,一種孤立的點,不是真實邊緣
- 對於弱邊緣,需要判斷此點與其相鄰的八個點中是否有強邊緣,如果有,保留此點,如果沒有,則刪除
二、canny算法實現
#coding=utf-8 import numpy as np import cv2 class Canny(): def img2gray(self, img): b = img[:, :, 0].copy() g = img[:, :, 1].copy() r = img[:, :, 2].copy() out = b*0.0722 + g*0.7152 + r*0.2126 out = out.astype(np.uint8) return out def gussian_filter(self, img, ksize=3, sigma=1.4): H, W= img.shape pad = ksize // 2 out = np.zeros([H+2*pad, W+2*pad], dtype=np.float) out[pad:pad+H, pad:pad+W] = img.copy().astype(np.float) K = np.zeros((ksize, ksize), dtype=np.float) for x in range(-pad, -pad+ksize): for y in range(-pad, -pad+ksize): K[y+pad, x+pad] = np.exp(-(x**2 + y**2)/2*sigma*sigma) K /= (2 * np.pi*sigma*sigma) K /= K.sum() tmp = out.copy() for y in range(H): for x in range(W): out[y + pad, x + pad] = np.sum(K*tmp[y: y+ksize, x :x+ksize]) out = np.clip(out, 0, 255) out = out[pad: H+pad, pad: W+pad] out = out.astype(np.uint8) return out def sobel_filter(self, img, ksize=3): pad = ksize // 2 H, W= img.shape out = np.zeros([H + pad * 2, W + pad * 2], dtype=np.float) out[pad: pad + H, pad: pad + W] = img.copy().astype(np.float) tmp = out.copy() out_y = out.copy() out_x = out.copy() kx = [[1., 0., -1.], [2., 0., -2.], [1., 0., -1.]] ky = [[1., 2., 1.], [0., 0., 0.], [-1., -2., -1.]] for y in range(H): for x in range(W): out_y[pad+y, pad+x] = np.sum(ky*tmp[y:y+ksize, x:x+ksize]) out_x[pad+y, pad+x] = np.sum(kx*tmp[y:y+ksize, x:x+ksize]) out_x = np.clip(out_x, 0, 255) out_x = out_x[pad:H+pad, pad:W+pad] out_x = out_x.astype(np.uint8) out_y = np.clip(out_y, 0, 255) out_y = out_y[pad:H + pad, pad:W + pad] out_y = out_y.astype(np.uint8) return out_x, out_y def get_angle(self, out_x, out_y): edge = np.sqrt(np.power(out_x.astype(np.float32), 2) + np.power(out_y.astype(np.float32), 2)) edge = np.clip(edge, 0, 255) out_x = np.maximum(out_x, 1e-10) angle = np.arctan(out_y/out_x) return edge, angle def angle_handle(self, angle): angle = angle / np.pi * 180 angle[angle < -22.5] = 180 + angle[angle < -22.5] new_angle = np.zeros_like(angle, dtype=np.uint8) new_angle[np.where(angle <= 22.5)] = 0 new_angle[np.where((angle > 22.5) & (angle <= 67.5))] = 45 new_angle[np.where((angle > 67.5) & (angle <= 112.5))] = 90 new_angle[np.where((angle > 112.5) & (angle <= 157.5))] = 135 return new_angle def non_max_sus(self, edge, angle): H, W = edge.shape new_edge = edge.copy() for y in range(0, H): for x in range(0, W): if angle[y, x] == 0: x1, y1, x2, y2 = -1, 0, 1, 0 elif angle[y, x] == 45: x1, y1, x2, y2 = -1, -1, 1, 1 elif angle[y, x] == 90: x1, y1, x2, y2 = 0, -1, 0, 1 elif angle[y, x] == 135: x1, y1, x2, y2 = -1, 1, 1, -1, if x == 0: x1 = max(x1, 0) x2 = max(x2, 0) if y == 0: y1 = max(y1, 0) y2 = max(y2, 0) if x == W-1: x1 = min(x1, 0) x2 = min(x2, 0) if y == H - 1: y1 = min(y1, 0) y2 = min(y2, 0) if max(max(edge[y, x], edge[y + y1, x + x1]), edge[y + y2, x + x2]) != edge[y, x]: new_edge[y, x] = 0 return new_edge def hysterisis(self, edge, HT = 100, LT = 10): H, W = edge.shape edge[np.where(edge >= HT)] = 255 edge[np.where(edge <= LT)] = 0 new_edge = np.zeros((H + 2, W + 2), dtype=np.float32) new_edge[1: H+1, 1: W+1] = edge.copy() K = np.array(((1, 1, 1),(1, 0, 1),(1, 1, 1))) for y in range(0, H + 2): for x in range(0, W + 2): if new_edge[y, x] < LT or new_edge[y, x ]> HT: continue if np.max(new_edge[y-1: y+2, x - 1: x + 2] * K) >= HT: new_edge[y, x] = 255 else: new_edge[y, x] = 0 edge = new_edge[1: H:1, 1: W+1] return edge def execute_func(self, img): gray = self.img2gray(img) gaussian = self.gussian_filter(gray) x, y = self.sobel_filter(gaussian) edge, angle = self.get_angle(x, y) new_angle = self.angle_handle(angle) new_edge = self.non_max_sus(edge, new_angle) edge_out = self.hysterisis(new_edge) return edge_out test = Canny() img = cv2.imread('lenna.png') edge = test.execute_func(img) edge = edge.astype(np.uint8) cv2.imshow('img', edge) cv2.waitKey(0) cv2.destroyAllWindows()