边缘提取——Prewitt算子和Sobel算子

Prewitt算子和Sobel算子

理论介绍

Prewitt算子和Sobel算子也是基于一阶导数的算子。
Sobel算子为距离近的元素设计了了更高的权重。

算子 卷积核
Prewitt算子 [[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]]

[[-1, -1, -1],
[0, 0, 0],
[1, 1, 1]]
Sobel算子 [[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]]

[[-1, -2,-1],
[ 0, 0, 0],
[ 1, 2, 1]]

所以Prewitt算子和Sobel算子计算方式相同。

编码实现(Python)

# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt


def convolution(matrix, c_kernel):
    # Prewitt算子的卷积核是3*3,所以卷积后少一圈
    rows, columns = matrix.shape
    # 这里提前补上了
    result_matrix = matrix.copy().astype(np.int16)
    for i in range(1, rows-1):
        for j in range(1, columns-1):
            result_matrix[i][j] = (matrix[i-1][j-1]*c_kernel[0][0] +
                                   matrix[i-1][j]*c_kernel[0][1] +
                                   matrix[i-1][j+1]*c_kernel[0][2] +
                                   matrix[i][j-1]*c_kernel[1][0] +
                                   matrix[i][j]*c_kernel[1][1] +
                                   matrix[i][j+1]*c_kernel[1][2] +
                                   matrix[i+1][j-1]*c_kernel[2][0] +
                                   matrix[i+1][j]*c_kernel[2][1] +
                                   matrix[i+1][j+1]*c_kernel[2][2]
                                   )
    return result_matrix


def Prewitt_Edge_Detection(source_img):
    # 输入灰度图,输出边缘提取后的灰度图
    rows, columns = source_img.shape

    img = source_img.copy()
    # Prewitt算子
    Prewitt_1 = np.array([[-1, 0, 1],
                          [-1, 0, 1],
                          [-1, 0, 1]], dtype=int)

    Prewitt_2 = np.array([[-1, -1, -1],
                          [0,  0, 0],
                          [1,  1, 1]], dtype=int)
	'''
    # Sobel算子
    Sobel_1 = np.array([[-1, 0, 1], 
                        [-2, 0, 2], 
                        [-1, 0, 1]], dtype=int)

    Sobel_2 = np.array([[-1, -2,-1], 
                        [ 0,  0, 0], 
                        [ 1,  2, 1]], dtype=int)
	# 用Sobel时直接更换。
	'''

    # step2 卷积
    x = convolution(img, Prewitt_1)
    y = convolution(img, Prewitt_2)
    # 也可以调用cv2的filter2D函数。
    # x = cv2.filter2D(img, cv2.CV_16S, Prewitt_1)
    # y = cv2.filter2D(img, cv2.CV_16S, Prewitt_2)

    # # 转换卷积后的图片深度,转uint8
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)

    Prewitt_img = np.zeros((rows, columns), dtype=np.uint8)
    # 或 Prewitt_img = np.array([[0]*columns]*rows).astype(np.uint8)

    for i in range(rows):
        for j in range(columns):
            Prewitt_img[i][j] = 0.5*absX[i][j] + 0.5*absY[i][j]
    # 或 Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

    return Prewitt_img


if __name__ == "__main__":
    # 用imread时需要注意图片路径不要包含中文,否则会can't open/read file: check file path/integrity
    # cv2.IMREAD_GRAYSCALE 读入灰度图
    source_img = cv2.imread('pictures\\source\\1.jpg', cv2.IMREAD_GRAYSCALE)
    # 文件中说In the case of color images, the decoded images will have the channels stored in **B G R** order.彩色图读入BGR

    result_img = Prewitt_Edge_Detection(source_img)

    # 用来正常显示中文标签
    plt.rcParams['font.sans-serif'] = ['SimHei']

    # plt.imshow()里添加参数cmap=“gray”,显示灰度图
    plt.subplot(121), plt.imshow(source_img, cmap="gray"), plt.title('source imge'), plt.axis('off')  # 坐标轴关闭
    plt.subplot(122), plt.imshow(result_img, cmap="gray"), plt.title('Prewitt operator imge'), plt.axis('off')
    plt.savefig('pictures\\result\\1_Prewitt operator result.jpg', bbox_inches='tight')
    plt.show()

debug过程

起初,卷积公式按照Roberts算子实现时的用result_matrix矩阵进行卷积,即

result_matrix[i-1][j-1]*c_kernel[0][0] +
result_matrix[i-1][j]*c_kernel[0][1] + (省略)

编译时出错:RuntimeWarning: overflow encountered in long_scalars

起初认为是np.int16表示范围不够大造成溢出,于是简单的将其改为np.int32最后甚至改为np.longlong,但还是溢出。这时应该是程序逻辑出错了,在出错地点及其周围检查后终于发现了问题。
在用Roberts算子进行卷积时,其 本次计算结果不会影响到下一次计算(因为计算结果仅与本身、右像素点、下像素点、右下像素点有关),所以用结果数组进行计算不会出错;而Prewitt算子和Sobel算子的卷积核为3*3,导致计算结果会影响下一次计算,所以最终导致程序可能将多次结果进行累计(互相影响),使得计算结果非常大,最终溢出。改为原始matrix后问题得以解决。

猜你喜欢

转载自blog.csdn.net/2201_75691823/article/details/129211538