エッジ抽出 - Prewitt 演算子と Sobel 演算子

Prewitt 演算子と Sobel 演算子

理論的紹介

Prewitt 演算子と Sobel 演算子も、一次導関数に基づく演算子です。
Sobel オペレーターは、互いに近い要素に高い重みを与えるように設計されています。

オペレーター コンボリューションカーネル
プレウィット演算子 [[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]]
および
[[-1, -1, -1],
[0, 0, 0],
[ 1、1、1]]
ソーベル演算子 [[-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()

デバッグプロセス

最初に、Roberts 演算子を実装するときに、畳み込み式は result_matrix 行列と畳み込まれます。

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

コンパイル中のエラー: RuntimeWarning: long_scalars でオーバーフローが発生しました

最初は、np.int16 の範囲が十分に大きくなくてオーバーフローが発生するのではないかと思い、単純に np.int32 に変更し、最終的には np.longlong に変更しましたが、それでもオーバーフローしました。このとき、プログラムロジックに誤りがあるはずで、誤り箇所とその周辺を確認したところ、ようやく問題が判明しました。
畳み込みに Roberts 演算子を使用する場合、この計算の結果は次の計算に影響しません (計算結果はそれ自体、右のピクセル、下のピクセル、および右下のピクセルにのみ関連するため)。そのため、結果の配列は次のようになります。計算は失敗しませんが、Prewitt 演算子と Sobel 演算子の畳み込みカーネルは 3*3 であり、計算結果が次の計算に影響を与えるため、最終的にプログラムは複数の結果を蓄積する可能性があります (相互に影響し合う) )、計算結果が非常に大きくなり、最終的にはオーバーフローしてしまいます。元のマトリックスに変更すると問題は解決しました。

おすすめ

転載: blog.csdn.net/2201_75691823/article/details/129211538