手动实现Canny算法 完成图像边缘提取 python实现

创作很累,如果您觉得对您有帮助,请点赞支持,感谢!


一. 总的算法流程:

① 使用高斯滤波器滤波

② 使用 Sobel 滤波器滤波获得在 x 和 y 方向上的输出,在此基础上求出边缘的强度和边缘的角度

edge 为边缘强度,tan 为边缘角度 ↑

③ 对边缘角度进行量化处理

对边缘角度进行量化处理算法 ↑

④ 根据边缘角度对边缘强度进行非极大值抑制(Non-maximum suppression),使图像边缘变得更细

非极大值抑制算法:0°时取(x,y)、(x+1,y)、(x-1,y) 中的最大值,其它角度类似 ↑

⑤ 使用滞后阈值对图像进行二值化处理,优化图像显示效果

算法如上所示 ↑

⑥ 输出图像边缘提取效果


二. 使用python手动实现 Canny 算法,完成图像边缘提取

# writer:wojianxinygcl@163.com

# date  :2020.3.20

import cv2

import numpy as np

import matplotlib.pyplot as plt

def Canny(img):

    # Gray scale

    def BGR2GRAY(img):

        b = img[:, :, 0].copy()

        g = img[:, :, 1].copy()

        r = img[:, :, 2].copy()

        # Gray scale

        out = 0.2126 * r + 0.7152 * g + 0.0722 * b

        out = out.astype(np.uint8)

        return out

    # Gaussian filter for grayscale

    def gaussian_filter(img, K_size=3, sigma=1.3):

        if len(img.shape) == 3:

            H, W, C = img.shape

            gray = False

        else:

            img = np.expand_dims(img, axis=-1)

            H, W, C = img.shape

            gray = True

        ## Zero padding

        pad = K_size // 2

        out = np.zeros([H + pad * 2, W + pad * 2, C], dtype=np.float)

        out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float)

        ## prepare Kernel

        K = np.zeros((K_size, K_size), dtype=np.float)

        for x in range(-pad, -pad + K_size):

            for y in range(-pad, -pad + K_size):

                K[y + pad, x + pad] = np.exp( - (x ** 2 + y ** 2) / (2 * sigma * sigma))

        #K /= (sigma * np.sqrt(2 * np.pi))

        K /= (2 * np.pi * sigma * sigma)

        K /= K.sum()

        tmp = out.copy()

        # filtering

        for y in range(H):

            for x in range(W):

                for c in range(C):

                    out[pad + y, pad + x, c] = np.sum(K * tmp[y : y + K_size, x : x + K_size, c])

        out = np.clip(out, 0, 255)

        out = out[pad : pad + H, pad : pad + W]

        out = out.astype(np.uint8)

        if gray:

            out = out[..., 0]

        return out

    # sobel filter

    def sobel_filter(img, K_size=3):

        if len(img.shape) == 3:

            H, W, C = img.shape

        else:

            H, W = img.shape

        # Zero padding

        pad = K_size // 2

        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_v = out.copy()

        out_h = out.copy()

        ## Sobel vertical

        Kv = [[1., 2., 1.],[0., 0., 0.], [-1., -2., -1.]]

        ## Sobel horizontal

        Kh = [[1., 0., -1.],[2., 0., -2.],[1., 0., -1.]]

        # filtering

        for y in range(H):

            for x in range(W):

                out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y : y + K_size, x : x + K_size]))

                out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y : y + K_size, x : x + K_size]))

        out_v = np.clip(out_v, 0, 255)

        out_h = np.clip(out_h, 0, 255)

        out_v = out_v[pad : pad + H, pad : pad + W]

        out_v = out_v.astype(np.uint8)

        out_h = out_h[pad : pad + H, pad : pad + W]

        out_h = out_h.astype(np.uint8)

        return out_v, out_h

    # get edge strength and edge angle

    def get_edge_angle(fx, fy):

        # get edge strength

        edge = np.sqrt(np.power(fx.astype(np.float32), 2) + np.power(fy.astype(np.float32), 2))

        edge = np.clip(edge, 0, 255)

        # make sure the denominator is not 0

        fx = np.maximum(fx, 1e-10)

        #fx[np.abs(fx) <= 1e-5] = 1e-5

        # get edge angle

        angle = np.arctan(fy / fx)

        return edge, angle


    # 将角度量化为0°、45°、90°、135°

    def angle_quantization(angle):

        angle = angle / np.pi * 180

        angle[angle < -22.5] = 180 + angle[angle < -22.5]

        _angle = np.zeros_like(angle, dtype=np.uint8)

        _angle[np.where(angle <= 22.5)] = 0

        _angle[np.where((angle > 22.5) & (angle <= 67.5))] = 45

        _angle[np.where((angle > 67.5) & (angle <= 112.5))] = 90

        _angle[np.where((angle > 112.5) & (angle <= 157.5))] = 135

        return _angle

    def non_maximum_suppression(angle, edge):

        H, W = angle.shape

        _edge = edge.copy()


        for y in range(H):

            for x in range(W):

                    if angle[y, x] == 0:

                            dx1, dy1, dx2, dy2 = -1, 0, 1, 0

                    elif angle[y, x] == 45:

                            dx1, dy1, dx2, dy2 = -1, 1, 1, -1

                    elif angle[y, x] == 90:

                            dx1, dy1, dx2, dy2 = 0, -1, 0, 1

                    elif angle[y, x] == 135:

                            dx1, dy1, dx2, dy2 = -1, -1, 1, 1

                    # 边界处理

                    if x == 0:

                            dx1 = max(dx1, 0)

                            dx2 = max(dx2, 0)

                    if x == W-1:

                            dx1 = min(dx1, 0)

                            dx2 = min(dx2, 0)

                    if y == 0:

                            dy1 = max(dy1, 0)

                            dy2 = max(dy2, 0)

                    if y == H-1:

                            dy1 = min(dy1, 0)

                            dy2 = min(dy2, 0)

                    # 如果不是最大值,则将这个位置像素值置为0

                    if max(max(edge[y, x], edge[y + dy1, x + dx1]), edge[y + dy2, x + dx2]) != edge[y, x]:

                            _edge[y, x] = 0

        return _edge

    # 滞后阈值处理二值化图像

    # > HT 的设为255,< LT 的设置0,介于它们两个中间的值,使用8邻域判断法

    def hysterisis(edge, HT=100, LT=30):

        H, W = edge.shape

        # Histeresis threshold

        edge[edge >= HT] = 255

        edge[edge <= LT] = 0

        _edge = np.zeros((H + 2, W + 2), dtype=np.float32)

        _edge[1 : H + 1, 1 : W + 1] = edge

        ## 8 - Nearest neighbor

        nn = np.array(((1., 1., 1.), (1., 0., 1.), (1., 1., 1.)), dtype=np.float32)

        for y in range(1, H+2):

                for x in range(1, W+2):

                        if _edge[y, x] < LT or _edge[y, x] > HT:

                                continue

                        if np.max(_edge[y-1:y+2, x-1:x+2] * nn) >= HT:

                                _edge[y, x] = 255

                        else:

                                _edge[y, x] = 0

        edge = _edge[1:H+1, 1:W+1]


        return edge

    # grayscale

    gray = BGR2GRAY(img)

    # gaussian filtering

    gaussian = gaussian_filter(gray, K_size=5, sigma=1.4)

    # sobel filtering

    fy, fx = sobel_filter(gaussian, K_size=3)

    # get edge strength, angle

    edge, angle = get_edge_angle(fx, fy)

    # angle quantization

    angle = angle_quantization(angle)

    # non maximum suppression

    edge = non_maximum_suppression(angle, edge)

    # hysterisis threshold

    out = hysterisis(edge, 80, 20)

    return out

if __name__ == '__main__':

    # Read image

    img = cv2.imread("../paojie.jpg").astype(np.float32)

    # Canny

    edge = Canny(img)

    out = edge.astype(np.uint8)

    # Save result

    cv2.imwrite("out.jpg", out)

    cv2.imshow("result", out)

    cv2.waitKey(0)

    cv2.destroyAllWindows()


三. 实验结果:

原图 ↑
Canny 算法 提取图像边缘结果 ↑

        可以看到,我的代码如愿以偿地提取了图像边缘,而且效果很好!


四. 参考内容:

        ① https://www.cnblogs.com/wojianxin/p/12533526.html

        ② https://blog.csdn.net/Ibelievesunshine/article/details/104996058


五. 版权声明:

        未经作者允许,请勿随意转载抄袭,抄袭情节严重者,作者将考虑追究其法律责任,创作不易,感谢您的理解和配合!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容