canny算法原理及實現


一、canny算法原理

  1. 灰度化
  2. 高斯濾波
  •  通過對待濾波像素及其相鄰點進行加權均值計算,可以去掉圖像上的噪點 
  1. 提取邊緣
  2. 非極大值抑制
  • 梯度值較大的點可能是真正的邊緣像素點,也可能是由顏色變化引起或噪點,想要過濾掉非邊緣可以使用非極大值抑制法
  • 像素點和沿梯度方向上相鄰的兩個像素點進行比較,如果此像素點是三個點中梯度值最大的,則保留,否則置為0
  1. 雙閾值法
  • 給定兩個閾值,如果像素點的梯度值高於高閾值,則為強邊緣,如果低於低閾值,則不是邊緣,介於低閾值和高閾值之間的為弱邊緣,對於弱邊緣又分兩種情況:一種是真實邊緣附近的點,一種孤立的點,不是真實邊緣
  • 對於弱邊緣,需要判斷此點與其相鄰的八個點中是否有強邊緣,如果有,保留此點,如果沒有,則刪除

二、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()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM