OpenCV cv2.imwrite保存拼接图像异常:仅保存原图而非拼接后图像的问题排查
问题分析与解决方案
核心问题:通道数不兼容导致保存失败
你遇到的问题根源在于拼接后的图像通道格式不符合cv2.imwrite的保存规范:
- 你读取的原图
im是3通道的BGR格式(cv2.imread默认返回3通道),而处理后的梯度图mag最终是1通道的灰度图,两者在通道维度(axis=2)拼接后得到了4通道数组。 - JPEG格式不支持4通道图像,
cv2.imwrite在处理这种非标准格式时,会默认只保存前3通道(也就是原图),这就是为什么你只看到原图被保存。
另外,你的梯度图通道处理有大量冗余操作:先把灰度图转成RGB(3通道),又转回灰度(1通道),再扩展成1通道,完全是没必要的绕路。
修正步骤与代码改进
1. 修复基础依赖问题
首先,你的代码里用到了os模块的函数,但没有导入,先补上:
import os # 加上这个导入 import cv2 import numpy as np
2. 简化梯度图的通道处理
把冗余的通道转换去掉,直接将单通道梯度图转为3通道BGR格式(和原图通道数一致),方便后续拼接:
# 原冗余代码替换成这一行 mag = cv2.cvtColor(mag, cv2.COLOR_GRAY2BGR)
现在mag的形状和原图im完全一致,都是(高度, 宽度, 3)。
3. 选择正确的拼接方式(二选一)
方式一:左右/上下并排拼接(推荐,可视化对比常用)
如果你的需求是把原图和梯度图并排显示(而不是合并通道),在宽度或高度维度拼接:
# 左右拼接(axis=1,宽度方向) concat = np.concatenate([im, mag], axis=1) # 或者上下拼接(axis=0,高度方向) # concat = np.concatenate([im, mag], axis=0)
这样得到的是3通道图像,完全符合JPEG的保存要求,cv2.imwrite可以正常保存。
方式二:通道维度拼接(仅当你需要多通道数据时)
如果你确实要在通道维度合并(比如作为额外的特征通道),需要保存为支持4通道的格式(比如PNG),并且可以将梯度图作为Alpha通道:
# 保留梯度图为单通道,和原图拼接成4通道BGRA mag_single = np.expand_dims(mag, axis=2) concat = np.concatenate([im, mag_single], axis=2) # 保存为PNG格式(JPG不支持4通道) outfile = 'output%s.png' % (num) cv2.imwrite(outfile, concat)
注意:这种方式的图像在普通查看器中会把梯度图当作透明度显示,可能不是你想要的可视化效果。
完整修正后的代码
import os import cv2 import numpy as np def prepare_data_test(test_path): input_names = [] for dirname in test_path: for _, _, fnames in sorted(os.walk(dirname)): for fname in fnames: if is_image_file(fname): input_names.append(os.path.join(dirname, fname)) return input_names def is_image_file(filename): return any(filename.endswith(extension) for extension in IMG_EXTENSIONS) IMG_EXTENSIONS = [ '.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP', ] test_path = ["./figures/"] # ["./test_images/real/"] subtask = "dataset" # if you want to save different testset separately val_names = prepare_data_test(test_path) num=1 for val_path in val_names: im = cv2.imread(val_path)#3채 img = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) # no channel dx = cv2.Sobel(img, cv2.CV_32F, 1, 0) # float 형태의 미분값을 저장 dy = cv2.Sobel(img, cv2.CV_32F, 0, 1) mag = cv2.magnitude(dx, dy) # gradient magnitude , no channel mag = np.clip(mag, 0, 255).astype(np.uint8) # 255보다 커질 수 있으므로 saturate 연산 # 简化通道处理:转成3通道BGR,和原图格式匹配 mag = cv2.cvtColor(mag, cv2.COLOR_GRAY2BGR) # 左右拼接(推荐的可视化方式) concat = np.concatenate([im, mag], axis=1) outfile = 'output%s.jpg' % (num) cv2.imwrite(outfile, concat) num = num + 1 # 这里cv2.waitKey()和destroyAllWindows()其实不需要,因为没有创建窗口显示图像 # cv2.waitKey() # cv2.destroyAllWindows()
内容的提问来源于stack exchange,提问作者허니브레</think_never_used_51bce0c785ca2f68081bfa7d91973934>




