You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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>

火山引擎 最新活动