深入理解Prewitt算子的原理、实现与应用
Prewitt算子是一种离散微分算子,用于计算图像亮度函数的梯度近似值,在图像处理中用于边缘检测,与Sobel算子类似但权重分布不同。
Prewitt算子通过计算图像在水平和垂直方向上的梯度来检测边缘。它使用两个3×3的卷积核分别计算x方向和y方向的梯度:
| 对比项 | Prewitt | Sobel |
|---|---|---|
| X 核权重 | [-1 0 1] 重复 | [-1 0 1] 中间加权 |
| 平滑效果 | 较弱 | 较强(中心加权) |
| 抗噪性 | 一般 | 较好 |
| 计算复杂度 | 低 | 低 |
x方向核 (Px):
[-1 0 +1]
[-1 0 +1]
[-1 0 +1]
y方向核 (Py):
[-1 -1 -1]
[ 0 0 0]
[+1 +1 +1]
边缘强度和方向的计算公式为:
Gradient Magnitude: G = |Gx| + |Gy| (或 G = sqrt(Gx2 + Gy2))
Edge Direction: theta = arctan(Gy / Gx)
import cv2
import numpy as np
import matplotlib.pyplot as plt
def prewitt_operator(image_path):
"""
实现Prewitt算子
:param image_path: 输入图像路径
:return: 原图和边缘检测结果
"""
# 读取图像
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 定义Prewitt核
prewitt_x = np.array([[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]], dtype=np.float32)
prewitt_y = np.array([[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1]], dtype=np.float32)
# 计算x和y方向的梯度
grad_x = cv2.filter2D(gray, cv2.CV_64F, prewitt_x)
grad_y = cv2.filter2D(gray, cv2.CV_64F, prewitt_y)
# 计算梯度幅值 (使用曼哈顿距离)
magnitude = np.abs(grad_x) + np.abs(grad_y)
# 归一化到0-255范围
magnitude = np.uint8(255 * magnitude / np.max(magnitude))
return img, magnitude
def manual_prewitt_operator(image_path):
"""
手动实现Prewitt算子
:param image_path: 输入图像路径
:return: Prewitt边缘检测结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
height, width = img.shape
# 定义Prewitt核
prewitt_x = np.array([[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]], dtype=np.float32)
prewitt_y = np.array([[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1]], dtype=np.float32)
# 创建输出图像
grad_x = np.zeros_like(img, dtype=np.float32)
grad_y = np.zeros_like(img, dtype=np.float32)
# 应用Prewitt核(卷积)
for i in range(1, height-1):
for j in range(1, width-1):
# 计算x方向梯度
grad_x[i, j] = np.sum(img[i-1:i+2, j-1:j+2] * prewitt_x)
# 计算y方向梯度
grad_y[i, j] = np.sum(img[i-1:i+2, j-1:j+2] * prewitt_y)
# 计算梯度幅值 (使用欧几里得距离)
magnitude = np.sqrt(grad_x**2 + grad_y**2)
# 归一化到0-255范围
magnitude = np.uint8(255 * magnitude / np.max(magnitude))
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), magnitude
def prewitt_with_direction(image_path):
"""
Prewitt算子计算边缘强度和方向
:param image_path: 输入图像路径
:return: 原图、边缘强度图和边缘方向图
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 定义Prewitt核
prewitt_x = np.array([[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]], dtype=np.float32)
prewitt_y = np.array([[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1]], dtype=np.float32)
# 计算x和y方向的梯度
grad_x = cv2.filter2D(img, cv2.CV_64F, prewitt_x)
grad_y = cv2.filter2D(img, cv2.CV_64F, prewitt_y)
# 计算梯度幅值
magnitude = np.sqrt(grad_x**2 + grad_y**2)
# 计算梯度方向
direction = np.arctan2(grad_y, grad_x)
# 归一化梯度幅值到0-255范围
magnitude = np.uint8(255 * magnitude / np.max(magnitude))
# 将方向转换为度数并映射到0-255范围
direction_deg = np.abs(direction * 180 / np.pi)
direction_normalized = np.uint8(255 * direction_deg / 180)
return (cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),
magnitude,
direction_normalized)
def prewitt_threshold(image_path, threshold=50):
"""
带阈值处理的Prewitt边缘检测
:param image_path: 输入图像路径
:param threshold: 阈值
:return: 原图和阈值处理后的边缘图
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 定义Prewitt核
prewitt_x = np.array([[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]], dtype=np.float32)
prewitt_y = np.array([[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1]], dtype=np.float32)
# 计算x和y方向的梯度
grad_x = cv2.filter2D(img, cv2.CV_64F, prewitt_x)
grad_y = cv2.filter2D(img, cv2.CV_64F, prewitt_y)
# 计算梯度幅值
magnitude = np.sqrt(grad_x**2 + grad_y**2)
# 应用阈值
_, thresholded = cv2.threshold(magnitude, threshold, 255, cv2.THRESH_BINARY)
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), np.uint8(thresholded)
def compare_sobel_prewitt(image_path):
"""
比较Sobel和Prewitt算子
:param image_path: 输入图像路径
:return: 原图、Sobel结果和Prewitt结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# Sobel算子
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
sobel_result = np.uint8(255 * sobel_magnitude / np.max(sobel_magnitude))
# Prewitt算子
prewitt_x = np.array([[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]], dtype=np.float32)
prewitt_y = np.array([[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1]], dtype=np.float32)
grad_x = cv2.filter2D(img, cv2.CV_64F, prewitt_x)
grad_y = cv2.filter2D(img, cv2.CV_64F, prewitt_y)
prewitt_magnitude = np.sqrt(grad_x**2 + grad_y**2)
prewitt_result = np.uint8(255 * prewitt_magnitude / np.max(prewitt_magnitude))
return (cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),
sobel_result,
prewitt_result)
def prewitt_extended_kernel(image_path):
"""
使用扩展的Prewitt核(5x5)
:param image_path: 输入图像路径
:return: 原图和扩展Prewitt核的边缘检测结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 定义扩展的Prewitt核 (5x5)
extended_prewitt_x = np.array([
[-2, -1, 0, 1, 2],
[-2, -1, 0, 1, 2],
[-2, -1, 0, 1, 2],
[-2, -1, 0, 1, 2],
[-2, -1, 0, 1, 2]
], dtype=np.float32)
extended_prewitt_y = np.array([
[-2, -2, -2, -2, -2],
[-1, -1, -1, -1, -1],
[ 0, 0, 0, 0, 0],
[ 1, 1, 1, 1, 1],
[ 2, 2, 2, 2, 2]
], dtype=np.float32)
# 应用扩展的Prewitt核
grad_x = cv2.filter2D(img, cv2.CV_64F, extended_prewitt_x)
grad_y = cv2.filter2D(img, cv2.CV_64F, extended_prewitt_y)
# 计算梯度幅值
magnitude = np.sqrt(grad_x**2 + grad_y**2)
# 归一化到0-255范围
magnitude = np.uint8(255 * magnitude / np.max(magnitude))
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), magnitude
# 使用示例
if __name__ == "__main__":
# 注意:需要提供实际的图像路径
# img, result = prewitt_operator('image.jpg')
# manual_img, manual_result = manual_prewitt_operator('image.jpg')
# dir_img, mag_result, dir_result = prewitt_with_direction('image.jpg')
# thresh_img, thresh_result = prewitt_threshold('image.jpg', 50)
# comp_img, sobel_res, prewitt_res = compare_sobel_prewitt('image.jpg')
# ext_img, ext_result = prewitt_extended_kernel('image.jpg')
pass
| 算子 | Prewitt X 核 | Prewitt Y 核 |
|---|---|---|
| 核矩阵 |
-1 0 1 -1 0 1 -1 0 1 |
-1 -1 -1 0 0 0 1 1 1 |
| 特点 | 对水平/垂直边缘敏感,抗噪性一般 | |