遍历图像像素时NumPy数组切片shape意外变化问题
NumPy切片shape不符合预期问题排查
问题背景
修改边缘检测代码,通过自定义kernel实现航拍照片的边缘检测。所用测试图像初始shape为(876, 921, 3),实际读入后打印的shape为(611, 1400, 3)。
异常表现
调整行切片范围时,返回的数组shape不符合NumPy左闭右开切片规则的预期,测试结果如下:
horizontal_local_pixels = img[row-1:row+5, col-1:col+4, -1] # 实际返回shape =(5,6) horizontal_local_pixels = img[row-1:row+6, col-1:col+4, -1] # 实际返回shape = (5,6) horizontal_local_pixels = img[row-1:row+7, col-1:col+4, -1] # 实际返回shape = (5,8)
理论上row-1:row+6切片应返回shape为(7,5)的数组,但实际返回结果和row-1:row+5切片完全一致,均为(5,6)。
复现代码
import matplotlib.pyplot as plt import numpy as np # 定义垂直方向滤波器 vertical_filter = np.array([[-4, -3, -2, 0, 2, 3, 4], [-3, -2, -1, 0, 1, 2, 3], [-2, -1, -0.5, 0, 0.5, 1, 2], [-3, -2, -1, 0, 1, 2, 3], [-4, -3, -2, 0, 2, 3, 4]]) # 定义水平方向滤波器 horizontal_filter = np.array([[-4, -3, -2, -3, -4], [-3, -2, -1, -2, -3], [-1, -0.5, -0.25, -0.5, -1], [0, 0, 0, 0, 0], [1, 0.5, 0.25, 0.5, 1], [3, 2, 1, 2, 3], [4, 3, 2, 3, 4]]) # 读入测试图像 img = plt.imread('test.png') # 获取图像维度 n,m,d = img.shape # 初始化边缘检测结果图像 edges_img = img.copy() # 遍历图像所有像素 for row in range(3, n-2): for col in range(3, m-2): # 提取水平滤波用局部像素块 horizontal_local_pixels = img[row-1:row+6, col-1:col+4, 0] # 提取垂直滤波用局部像素块 vertical_local_pixels = img[row-1:row+4, col-1:col+6, 0] # 应用垂直滤波器计算得分 vertical_transformed_pixels = vertical_local_pixels.transpose().dot(vertical_filter) vertical_score = vertical_transformed_pixels.sum()/4 # 应用水平滤波器计算得分 horizontal_transformed_pixels = horizontal_local_pixels.transpose().dot(horizontal_filter) horizontal_score = horizontal_transformed_pixels.sum()/4 # 合并两个方向的边缘得分 edge_score = (vertical_score**2 + horizontal_score**2)**.5 # 将得分写入结果图像 edges_img[row, col] = [edge_score]*3 # 将结果值重映射到0-1区间,避免数值越界 edges_img = edges_img/edges_img.max()
异常原因
- NumPy切片的边界截断机制:和Python原生列表切片逻辑一致,当切片的起始索引小于0,或结束索引大于数组最大索引时,不会抛出索引错误,会自动将切片范围截断到数组的实际起止位置。
- 循环遍历范围没有给卷积核预留足够padding:7行5列的水平卷积核,需要提取的局部块是7行5列,对应切片
img[row-1:row+6, col-1:col+4, :],要求row-1 >= 0且row+5 <= n-1、col-1 >=0且col+3 <=m-1,推导可得合法的row范围是1 <= row <=n-6,合法的col范围是1<=col <=m-4。现有代码写的遍历范围是range(3, n-2),row最大值为n-3,此时row+5 = n+2,远大于图像最大行索引n-1,切片会被直接截断到图像末尾,返回的行数自然达不到预期的7。列维度shape异常也是同样的截断逻辑导致。
修复方案
- 调整循环的遍历范围,给卷积核预留足够的边缘padding,确保每次切片都不会超出图像边界:行遍历范围修改为
range(3, n-5),列遍历范围修改为range(3, m-3)。 - 如果需要处理图像边缘的像素,可以先用
np.pad对原图做边缘填充,再执行卷积遍历。
内容的提问来源于stack exchange,提问作者John Milburn
相关产品推荐
相关产品推荐

