import SimpleITK as sitk import cv2 import numpy as np import matplotlib.pyplot as plt # 读取目标图像 target_img = cv2.imread('./plot_abdomenreg_arrow.jpg') # 读取目标图像 target_img = cv2.cvtColor(target_img, cv2.COLOR_BGR2RGB) # 转换颜色空间 # 读取分割图像 seg_data = sitk.GetArrayFromImage(sitk.ReadImage('./logs/abdomenreg/SegReg_edt/0029_0030_reg/seg_fixed.nii.gz')) # 提取第二维度第40层切片(注意维度顺序) # 根据seg_image.nii.gz的尺寸(96,80,128),应取中间维度 seg_slice = seg_data[:, 40, :] # 得到(96,128)的二维数组 seg_slice = np.flipud(seg_slice) # 上下翻转图像 # 调整分割图像的尺寸以匹配目标图像的尺寸 target_height, target_width, _ = target_img.shape seg_slice_resized = cv2.resize(seg_slice, (target_width, target_height), interpolation=cv2.INTER_NEAREST) # 创建空白轮廓图像 contour_img = np.zeros_like(target_img) # 设置颜色映射 labels = np.unique(seg_slice_resized) colors = plt.cm.jet(np.linspace(0, 1, len(labels))) # 绘制轮廓 for label, color in zip(labels, colors): if label == 0: continue mask = (seg_slice_resized == label).astype(np.uint8) contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours( contour_img, contours, -1, (int(color[0]*255), int(color[1]*255), int(color[2]*255)), 1 ) # 叠加显示 combined = cv2.addWeighted(target_img, 1, contour_img, 1, 0) # 保存结果 cv2.imwrite('contour_overlay.png', cv2.cvtColor(combined, cv2.COLOR_RGB2BGR)) cv2.imwrite('contour_seg.png', cv2.cvtColor(combined, cv2.COLOR_RGB2BGR))把这段代码改为plt作图
时间: 2025-06-10 16:03:06 AIGC 浏览: 42
<think>好的,我需要帮助用户将现有的基于OpenCV和SimpleITK的代码转换为使用matplotlib(plt)进行可视化。让我先仔细阅读用户提供的代码,理解其功能,然后思考如何用matplotlib实现相同的效果。
首先,原代码的主要步骤是:
1. 读取目标图像(一张JPEG图像)并转换颜色空间。
2. 读取分割结果(NIfTI格式),提取特定切片并进行调整。
3. 调整分割切片的尺寸以匹配目标图像。
4. 创建空白图像,为每个标签生成轮廓,叠加到目标图像上。
5. 保存结果。
用户希望改用matplotlib作图,这意味着需要将OpenCV的图像处理部分替换为matplotlib的函数,同时保持最终效果一致。
首先,分析各个步骤可能的变化点:
1. **图像读取和颜色空间转换**:原代码使用OpenCV的cv2.imread读取图像,并将BGR转换为RGB。而matplotlib的imread直接读取为RGB,所以这一步可以简化,用plt.imread代替,无需颜色转换。
2. **读取NIfTI文件**:这部分仍然需要使用SimpleITK,因为涉及医学图像格式的处理,matplotlib本身不支持。因此,sitk.ReadImage和sitk.GetArrayFromImage仍然需要保留。
3. **切片处理**:原代码使用numpy进行数组操作,这部分保持不变,但需要注意matplotlib显示时的坐标方向,可能需要调整转置或翻转操作。
4. **调整分割图像尺寸**:原代码使用cv2.resize,但matplotlib的显示尺寸可以通过调整图像插值或设置extent参数来控制。不过,为了精确匹配目标图像尺寸,可能仍然需要使用resize,但可以用skimage的resize代替,或者继续使用cv2.resize,因为这只是数组操作,不影响后续显示。
5. **生成轮廓**:原代码使用cv2.findContours和cv2.drawContours来绘制轮廓。在matplotlib中,需要找到替代方法。可能的方案是使用skimage的measure.find_contours函数来获取轮廓的坐标,然后用plt.plot来绘制这些轮廓。
6. **颜色映射**:原代码使用plt.cm.jet生成颜色,这部分可以保留,但需要注意颜色的应用方式。
7. **叠加显示和保存**:matplotlib的figure和axes可以用于叠加图像和轮廓,最后保存为图片。
接下来,分步骤思考如何转换每个部分:
**1. 读取目标图像**:
原代码:
target_img = cv2.imread('./plot_abdomenreg_arrow.jpg')
target_img = cv2.cvtColor(target_img, cv2.COLOR_BGR2RGB)
改为使用matplotlib:
target_img = plt.imread('./plot_abdomenreg_arrow.jpg')
注意:plt.imread读取的已经是RGB格式,无需转换。但需要确认图像路径是否正确,以及是否可能读取带有alpha通道的情况,可能需要处理为三通道。
**2. 读取分割图像**:
这部分保持不变,仍使用SimpleITK读取NIfTI文件。
**3. 切片处理**:
原代码提取中间维度的第40层切片,并进行翻转。需要注意的是,matplotlib的imshow默认原点在左上角,而医学图像可能原点在左下,因此翻转操作可能需要调整。原代码使用np.flipud,可能需要保留,但需要检查显示方向是否正确。
**4. 调整分割图像尺寸**:
原代码使用cv2.resize,这里可以继续使用,因为结果只是一个numpy数组,不影响后续处理。或者使用skimage.transform.resize,但需要注意插值方法。由于分割图是二值或标签图像,应使用最近邻插值(INTER_NEAREST),所以cv2.resize仍然合适。
**5. 创建轮廓**:
这是关键部分。原代码使用cv2.findContours找到轮廓,然后用drawContours绘制到空白图像上。在matplotlib中,需要找到每个轮廓的路径,然后用plot函数绘制。
skimage.measure.find_contours可以找到二值图像的轮廓线,返回轮廓的坐标。对于每个标签,生成对应的二值掩膜,然后找到轮廓,用plot绘制。
需要循环每个非零标签,生成对应的掩膜,找到轮廓,然后在同一张图上绘制。
颜色部分,原代码使用plt.cm.jet的颜色,转换为0-255的整数值。在matplotlib中,可以直接使用颜色元组,例如(color[0], color[1], color[2]),注意颜色值在0-1之间,或者转换为0-255但需要在plot中指定color参数时注意格式。
**6. 叠加显示**:
原代码使用cv2.addWeighted将轮廓叠加到目标图像上。在matplotlib中,可以通过先显示目标图像,然后在同一坐标系下绘制轮廓线来实现叠加效果。
**7. 保存结果**:
使用plt.savefig代替cv2.imwrite,注意保存前关闭坐标轴,调整边框等。
可能的代码结构:
- 创建figure和axes
- 显示目标图像
- 对每个标签生成轮廓,用plot绘制
- 调整坐标轴,去除刻度
- 保存图像
需要注意的问题:
- 坐标方向:matplotlib的imshow默认原点在上,而医学图像可能在下,可能需要调整extent参数或使用origin参数。
- 图像尺寸:确保调整后的分割图像与目标图像尺寸一致,否则轮廓位置会错位。
- 轮廓的坐标:skimage的find_contours返回的坐标是基于数组的行列索引,可能需要转置或调整以匹配图像显示。
- 颜色映射:正确应用颜色到每个轮廓。
- 图像保存的分辨率和质量:使用plt.savefig的dpi参数控制。
现在,逐步构建代码:
1. 导入所需库:
除了原代码的导入,还需要导入skimage.measure.find_contours:
import SimpleITK as sitk
import numpy as np
import matplotlib.pyplot as plt
from skimage.measure import find_contours
2. 读取目标图像:
target_img = plt.imread('./plot_abdomenreg_arrow.jpg') # 直接读取为RGB
3. 读取分割图像并处理切片:
seg_data = sitk.GetArrayFromImage(sitk.ReadImage('./logs/abdomenreg/SegReg_edt/0029_0030_reg/seg_fixed.nii.gz'))
seg_slice = seg_data[:, 40, :]
seg_slice = np.flipud(seg_slice)
4. 调整分割图像尺寸:
target_height, target_width = target_img.shape[0], target_img.shape[1]
seg_slice_resized = cv2.resize(seg_slice, (target_width, target_height), interpolation=cv2.INTER_NEAREST)
注意:这里可能需要保留cv2.resize,因为它处理数组的尺寸调整,不影响后续。或者使用skimage.transform.resize,但需要确保处理正确。
5. 创建figure和axes,显示目标图像:
fig, ax = plt.subplots(figsize=(target_width/100, target_height/100), dpi=100) # 根据实际尺寸调整
ax.imshow(target_img)
6. 生成颜色映射:
labels = np.unique(seg_slice_resized)
colors = plt.cm.jet(np.linspace(0, 1, len(labels)))
7. 对每个非零标签绘制轮廓:
for label, color in zip(labels, colors):
if label == 0:
continue
mask = (seg_slice_resized == label).astype(np.uint8)
contours = find_contours(mask, level=0.5) # 找到轮廓
for contour in contours:
ax.plot(contour[:, 1], contour[:, 0], linewidth=1, color=color[:3]) # 注意坐标顺序
注意:find_contours返回的轮廓坐标是(行,列),而imshow的x轴是列,y轴是行。因此,在plot时,x对应contour[:, 1],y对应contour[:, 0]。同时,可能需要调整坐标方向,例如如果图像显示上下颠倒,可能需要取反。
原代码中的seg_slice经过np.flipud处理,所以在调整大小后,seg_slice_resized的方向是否与target_img一致?可能需要确认是否需要额外的翻转。或者,在绘制轮廓时调整坐标。
例如,如果target_img在matplotlib中显示正确,而分割图经过flipud,那么在调整大小后,可能需要再次翻转,或者在绘制轮廓时调整y坐标。
可能需要进一步测试,但根据原代码,seg_slice_resized已经是调整后的,并且经过flipud,所以在matplotlib中显示时是否需要调整?
这可能涉及到坐标原点的匹配问题。例如,原代码使用cv2处理,而cv2的坐标原点在左上角,matplotlib默认的imshow原点在左上角(当origin未设置时)。因此,可能不需要额外调整。但需要验证。
如果发现轮廓位置上下颠倒,可能需要调整contour[:, 0]为 mask.shape[0] - contour[:, 0],或者在调整分割图时处理。
8. 隐藏坐标轴并保存图像:
ax.axis('off')
plt.subplots_adjust(left=0, right=1, top=1, bottom=0) # 去除白边
plt.savefig('contour_overlay_plt.png', bbox_inches='tight', pad_inches=0, dpi=100)
plt.close()
这样,应该能够生成叠加轮廓后的图像,并保存为png文件。
测试可能出现的问题:
- 轮廓坐标是否正确对应到目标图像的位置。
- 颜色是否正确应用。
- 图像保存时是否有白边,需要通过调整subplots_adjust和bbox_inches='tight'来处理。
- 图像尺寸是否与目标图像一致,可能需要设置figsize和dpi,使得保存的图像尺寸与原始target_img相同。
例如,如果target_img的尺寸是(height, width, 3),则设置figsize为(width/dpi, height/dpi),dpi根据需要调整,以确保保存的图像尺寸正确。
例如,如果dpi=100,则figsize=(width/100, height/100)。这样,保存的图像尺寸为 width x height 像素。
另外,可能需要关闭坐标轴和其他元素,确保保存的图像仅包含内容。
综合以上思考,最终的转换代码大致如下:</think>以下是使用matplotlib重构后的代码,实现了与OpenCV版本相同的轮廓叠加效果:
```python
import SimpleITK as sitk
import numpy as np
import matplotlib.pyplot as plt
from skimage.measure import find_contours
# 读取目标图像(自动转换为RGB格式)
target_img = plt.imread('./plot_abdomenreg_arrow.jpg')
# 读取分割数据并处理切片
seg_data = sitk.GetArrayFromImage(
sitk.ReadImage('./logs/abdomenreg/SegReg_edt/0029_0030_reg/seg_fixed.nii.gz')
)
seg_slice = seg_data[:, 40, :] # 提取中间维度第40层
seg_slice = np.flipud(seg_slice) # 上下翻转
# 调整分割图像尺寸与目标图像匹配
target_height, target_width = target_img.shape[:2]
seg_slice_resized = cv2.resize(seg_slice, (target_width, target_height),
interpolation=cv2.INTER_NEAREST)
# 创建画布
fig, ax = plt.subplots(figsize=(target_width/100, target_height/100),
dpi=100, frameon=False)
ax.imshow(target_img)
ax.axis('off') # 关闭坐标轴
# 设置颜色映射(排除背景0)
labels = np.unique(seg_slice_resized)
colors = plt.cm.jet(np.linspace(0, 1, len(labels)))
# 绘制所有轮廓
for label, color in zip(labels, colors):
if label == 0:
continue
mask = (seg_slice_resized == label).astype(np.uint8)
# 查找轮廓(返回轮廓点坐标)
contours = find_contours(mask, level=0.5)
# 绘制每个轮廓线段
for contour in contours:
ax.plot(contour[:, 1], # X坐标(列)
contour[:, 0], # Y坐标(行)
linewidth=1,
color=color[:3]) # 使用前三个颜色通道
# 保存结果(去除白边)
plt.subplots_adjust(left=0, right=1, top=1, bottom=0)
plt.savefig('contour_overlay_plt.png',
bbox_inches='tight',
pad_inches=0,
dpi=100)
plt.close()
```
**主要改进说明**:
1. **图像读取优化**:
- 直接使用`plt.imread()`读取图像,自动获得RGB格式数组
- 省去OpenCV的颜色空间转换步骤
2. **坐标系统适配**:
- 通过`find_contours`获取的轮廓点坐标直接对应数组行列索引
- 在`ax.plot()`中交换X/Y坐标以匹配图像显示方向
3. **画布尺寸控制**:
- 通过`figsize`和`dpi`参数精确控制输出图像分辨率
- 计算公式:`figsize=(width/100, height/100)`配合`dpi=100`可获得1:1像素比例
4. **可视化效果增强**:
- 使用`plt.cm.jet`颜色映射保持与OpenCV版本一致的配色方案
- 通过`frameon=False`和`subplots_adjust`彻底去除画布白边
**注意事项**:
1. 如果发现轮廓位置上下颠倒,可以尝试修改Y坐标计算为:
```python
ax.plot(contour[:, 1], mask.shape[0] - contour[:, 0], ...)
```
2. 对于需要更高精度的轮廓检测,可以调整`find_contours`的level参数:
```python
find_contours(mask, level=0.5) # 0-1之间的阈值,默认0.5
```
3. 输出图像分辨率可以通过调整dpi参数控制:
```python
dpi=300 # 输出更高分辨率的图像
```
该版本实现了与OpenCV版本相同的核心功能,但完全基于matplotlib的渲染管线,更适合科学可视化场景,同时保持与Jupyter等环境的良好兼容性。
阅读全文