多视图分类:自定义NumPy加载器调图像至224x224适配VGG16
解决纯NumPy下64x64图像缩放至224x224适配VGG16的问题
问题原因分析
关于Try(1)的报错
x_batch[i,j]是x_batch的数组视图,而非拥有独立内存的数组,调用.resize()方法时,由于视图不具备数据所有权,无法直接修改形状,因此抛出cannot resize this array: it does not own its data错误。- 单张图像的目标形状应为
(224,224,3),而非(1,224,224,3),多了一个不必要的batch维度。
关于Try(2)的图像失真
np.resize()的逻辑是通过重复或截断数组元素来匹配目标形状,完全忽略图像的空间结构,本质是对数组元素的重新排列,而非图像的插值缩放,因此必然导致图像严重失真。
纯NumPy实现图像双线性插值缩放
要实现符合视觉效果的图像缩放,需采用双线性插值(VGG16预训练时默认使用的缩放方式),以下是纯NumPy实现的双线性插值函数:
def bilinear_resize(image, target_size): # image: (H, W, 3) 输入图像数组 # target_size: (new_H, new_W) 目标尺寸 h, w, c = image.shape new_h, new_w = target_size # 计算坐标缩放比例 x_scale = (w - 1) / (new_w - 1) if new_w > 1 else 0 y_scale = (h - 1) / (new_h - 1) if new_h > 1 else 0 # 生成目标坐标网格 new_x = np.linspace(0, w-1, new_w) new_y = np.linspace(0, h-1, new_h) x_grid, y_grid = np.meshgrid(new_x, new_y) # 计算四个邻点的坐标 x0 = np.floor(x_grid).astype(int) x1 = np.minimum(x0 + 1, w - 1) y0 = np.floor(y_grid).astype(int) y1 = np.minimum(y0 + 1, h - 1) # 提取邻点的像素值 pixel00 = image[y0, x0, :] pixel01 = image[y0, x1, :] pixel10 = image[y1, x0, :] pixel11 = image[y1, x1, :] # 计算插值权重 dx = x_grid - x0 dy = y_grid - y0 # 执行双线性插值计算 interpolated = (1 - dx) * (1 - dy) * pixel00 + dx * (1 - dy) * pixel01 + \ (1 - dx) * dy * pixel10 + dx * dy * pixel11 return interpolated.astype(image.dtype)
修改数据加载器的正确方式
调整数据加载器逻辑,在读取图像后直接用双线性插值缩放至224x224,同时初始化对应尺寸的x_batch:
def data_loader(dataframe, classDict, basePath, batch_size=16): target_size = (224, 224) while True: # 初始化目标尺寸的batch数组 x_batch = np.zeros((batch_size, 4, *target_size, 3)) y_batch = np.zeros((batch_size, 20)) for i in range(batch_size): rndNumber = np.random.randint(len(dataframe)) *images, class_id = dataframe.iloc[rndNumber] for j in range(4): # 读取图像并归一化 img = plt.imread(os.path.join(basePath, images[j])) / 255. # 双线性插值缩放至目标尺寸 resized_img = bilinear_resize(img, target_size) x_batch[i,j] = resized_img class_id = classDict[class_id] y_batch[i, class_id] = 1.0 yield { 'image1': x_batch[:, 0], 'image2': x_batch[:, 1], 'image3': x_batch[:, 2], 'image4': x_batch[:, 3] }, {'class_out': y_batch} # 测试数据加载器 example, lbl= next(data_loader(df_train, classDictTrain, basePath)) print(example['image1'].shape) # 预期输出:(16, 224, 224, 3) print(lbl['class_out'].shape) # 预期输出:(16, 20)
关键说明
- 双线性插值会保留图像的空间结构,缩放后的图像不会失真,符合VGG16预训练时的输入要求。
- 提前初始化目标尺寸的
x_batch,避免后续形状调整的额外性能开销。 - 所有操作均基于纯NumPy实现,未使用keras或OpenCV的内置图像加载/缩放方法,满足需求。
内容的提问来源于stack exchange,提问作者stackersTech101
相关产品推荐
相关产品推荐

