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 であり、計算結果が次の計算に影響を与えるため、最終的にプログラムは複数の結果を蓄積する可能性があります (相互に影響し合う) )、計算結果が非常に大きくなり、最終的にはオーバーフローしてしまいます。元のマトリックスに変更すると問題は解決しました。