使用GPU加速的nvImageCodec推进医学图像解码
使用GPU加速的nvImageCodec推进医学图像解码
本文深入探讨了使用nvJPEG2000库在AWS HealthImaging中解码DICOM医学图像的功能。文章将引导读者了解图像解码的复杂性,介绍AWS HealthImaging,并探索GPU加速解码解决方案带来的进步。通过GPU加速的nvJPEG2000库开始在AWS HealthImaging中提高吞吐量并降低解读医学图像的成本,这代表着云环境中运营效率的重大进步。这些创新有望节省大量成本,预计此类工作负载的潜在成本削减总计达数亿美元。
JPEG 2000
正确实施JPEG 2000会带来极大的复杂性,早期遇到的互操作性问题阻碍了不同系统之间的无缝集成。这种复杂性成为广泛采用的障碍。然而,高吞吐量JPEG 2000(HTJ2K)编码系统的推出标志着图像压缩技术的显着进步。JPEG 2000标准第15部分中概述的HTJ2K通过用更高效的FBCOT(优化截断快速块编码)替换原始块编码算法EBCOT(优化截断嵌入式块编码)来提高吞吐量。
这一新标准解决了解码速度限制,并为JPEG 2000在医学成像领域的更广泛采用打开了大门。HTJ2K支持无损和有损压缩,在保留关键医疗细节和实现高效存储之间提供平衡。具有任意宽度和高度的灰度和彩色图像,以及每通道高达16位的支持,展示了HTJ2K的适应性。新标准对分解级别没有限制,提供了广泛的选择。
nvJPEG2000库
随着通过nvJPEG2000等库进行GPU加速的出现,HTJ2K的解码性能达到了新的高度。这一进步释放了JPEG 2000在医学图像处理方面的真正潜力,使其成为医疗保健提供者、研究人员和开发人员可行且高效的解决方案。nvJPEG2000提供C API,包括用于解码单个图像的nvjpeg2kDecode
和用于解码图像中特定图块的nvjpeg2kDecodeTile
等函数。图书馆提供:
- 统一API接口
nvImageCodec
:开源库与Python无缝集成,为开发人员提供了便捷的接口。 - 解码性能分析:HTJ2K与传统JPEG 2000之间的解码性能对比分析,包括对GPU加速的见解。
为了确保可用性、高性能和生产就绪性,本文探讨了HTJ2K解码与MONAI(专为医学图像分析而设计的框架)的集成。MONAI Deploy App SDK提供高性能功能,并促进医学成像AI应用程序的调试。它还深入研究了使用AWS HealthImaging、MONAI和nvJPEG2000进行医学图像处理的相关成本优势。
AWS HealthImaging
在本演练中,我们展示了AWS HealthImaging的使用。我们演示了利用GPU加速接口使用SageMaker多模型端点进行图像解码的过程。
第1步:暂存您的DICOM图像
首先将您的DICOM图像暂存在Amazon S3存储桶中。AWS HealthImaging与推出的合作伙伴产品集成,提供最适合您的工作流程的各种工具,并在指定的S3存储桶中上传和组织您的DICOM图像数据。您还可以探索AWS开放数据计划。公共S3存储桶中存在包含合成医学成像数据的开放数据集,例如Synthea Coherent。
步骤2:调用DICOM数据导入API
将DICOM图像暂存在S3存储桶后,下一步涉及调用本机API将DICOM数据导入AWS HealthImaging。此托管API促进了平稳且自动化的流程,确保您的医学图像数据得到有效传输并为进一步优化做好准备。
步骤3:在数据湖中索引DICOM标头
成功导入后,从AWS HealthImaging检索DICOM标头、解压缩数据blob,并将这些JSON对象写入数据湖S3存储桶。从那里,您可以利用AWS数据湖分析工具,例如Amazon Glue生成数据目录、Amazon Athena执行即席SQL查询以及Amazon QuickSight构建数据可视化仪表板。您还可以将图像元数据与其他健康数据模式相结合来执行多模式数据分析。
第4步:访问您的医学图像数据
借助托管API,访问将成为一种无缝体验。AWS HealthImaging以亚秒级的速度提供对成像数据的高性能和精细访问。
在云上构建PACS查看器和VNA解决方案的AWS合作伙伴可以将其图像查看应用程序与AWS HealthImaging集成。这些应用程序经过优化,可为大规模查看和分析医学图像提供用户友好且高效的体验。AWS合作伙伴PACS的示例包括Allina Health案例研究、Visage Imaging和Visage AWS。
科学家和研究人员可以利用Amazon SageMaker执行AI和ML建模,以解锁高级见解并自动执行审阅和注释任务。Amazon Sagemaker可与MONAI结合使用来开发强大的AI模型。使用Amazon SageMaker笔记本,用户可以从AWS HealthImaing检索像素帧,使用itkwidget等开源工具可视化医学图像,并创建SageMaker托管训练作业或模型托管终端节点。
作为一项符合HIPAA要求的服务,AWS HealthImaging可以灵活地向远程用户授予和审核对医疗图像数据的安全访问权限。访问控制由Amazon Identity and Access Management管理,确保授权用户可以对ImageSet数据进行精细访问。Amazon CloudTrail还可以监控访问活动,以跟踪谁在什么时间访问了哪些数据。
步骤5:支持GPU的HTJ2K解码
在典型的AI或ML工作流程(CPU解码路径)中,HTJ2K编码的像素帧将加载到CPU内存中,然后在CPU中解码并转换为张量。这些可以由GPU复制和处理。nvJPEG2000可以从AWS HealthImaging获取编码像素并将其直接解码到GPU内存中(GPU解码路径),MONAI具有内置函数,可将图像数据转换为张量,以供深度学习模型使用。与CPU解码方法相比,它的路径更短,如下图所示。
此外,nvJPEG2000的GPU加速显着提高了解码性能,减少了延迟并增强了整体响应能力。该库与Python无缝集成,为开发人员提供了熟悉且强大的图像解码任务环境。
在Amazon SageMaker上运行的演示notebook展示了如何以可扩展且高效的方式集成和利用GPU加速图像解码的强大功能。在我们的实验中,SageMaker g4dn.2xlarge实例上的GPU解码速度比SageMaker m5.2xlarge实例上的CPU解码速度快约7倍(下图)。
在本实验中,我们使用了Synthea Coherent数据集中的合成大脑MRI图像。GPU加速对于不同大小的数据集表现出类似的加速因子。上面标记的图像集包含脑MRI和像素帧。这些像素帧代表DICOM MRI图像,并以压缩的HTJ2K数据格式进行编码。
成本效益分析
结合先进的图像解码技术,AWS HealthImaging不仅提高了效率,还为医疗保健组织提供了经济高效的解决方案。所提出的解决方案的端到端成本效益是巨大的,特别是考虑到通过GPU加速实现的令人印象深刻的吞吐量加速。
EC2 G4实例上的单个NVIDIA T4 GPU的加速比CPU基准提高了大约5倍,而EC2 G6实例上的新L4 GPU上的这一改进进一步增强到了令人印象深刻的12倍。通过多个GPU实例进行扩展,性能表现出近乎线性的可扩展性,在四个NVIDIA T4 GPU和四个NVIDIA L4 GPU上分别达到约19倍和48倍。
在解码性能方面,我们与OpenJPEG进行了对比分析。对于CT1 16位512×512灰度图像,我们注意到不同GPU配置的速度显着提高了2.5倍。此外,对于尺寸为3064×4774的较大MG1 16位灰度图像,我们在各种GPU设置上实现了令人印象深刻的8倍速度提升。
为了全面评估年度云成本和能源使用情况,我们的计算基于标准分段工作负载。此工作负载涉及每分钟将500个DICOM文件上传到MONAI服务器平台。目前我们的成本估算仅关注T4 GPU,预计未来还会有L4 GPU。我们假设Amazon EC2 G4实例的保留定价为一年。
在这些条件下,在单个T4 GPU上处理DICOM工作负载的年度成本估计约为7400万美元,而与CPU管道相关的成本为3.454亿美元。这意味着云支出显着减少,预测表明此类医院工作负载可能节省数亿美元。
在单个T4 GPU上,端到端吞吐量加速比CPU基准快大约5倍。这种加速在新的L4 GPU上进一步增强,速度提高了约12倍。当使用多个GPU实例时,性能几乎呈线性扩展。例如,使用四个T4 GPU时,加速比可达到约19倍,而使用四个L4 GPU时,速度可提升至约48倍。
考虑到环境影响,能源效率是处理大量工作负载的数据中心的关键因素。我们的计算表明,使用相应的基于GPU的硬件时,每年的能耗(以GWh为单位)会大大降低。具体来说,单个L4系统的能耗约为CPU服务器的十二分之一。
对于类似于示例DICOM视频场景的工作负载(每分钟500小时的视频),每年的节能估计约为数百GWh。这些节能不仅具有经济效益,而且对环境也具有重要意义。温室气体排放量的相应减少量是巨大的,类似于避免每年行驶数万辆客车的排放,每辆客车每年行驶约11,000英里。
为什么选择nvImageCodec?
NVIDIA/nvImageCodec库为开发人员提供了强大且高效的图像解码任务解决方案。nvImageCodec利用NVIDIA GPU的强大功能,提供加速的解码性能,非常适合需要高吞吐量和低延迟的应用程序。
主要特性
- GPU加速:nvImageCodec的突出特点之一是其GPU加速功能。通过利用NVIDIA GPU的计算能力,nvImageCodec显着加快了图像解码过程,从而可以更快地处理大型数据集。
- 无缝集成:nvImageCodec与Python无缝集成,为开发人员的图像处理工作流程提供熟悉的环境。借助用户友好的API,将nvImageCodec集成到现有的Python项目中非常简单。
- 高性能:凭借优化的算法和并行处理,即使在处理复杂的图像解码任务时,nvImageCodec也能提供卓越的性能。无论您是解码JPEG、JPEG 2000、TIFF还是其他图像格式,nvImageCodec都能确保快速高效的处理。
- 多功能性:从医学成像到计算机视觉应用,nvImageCodec支持广泛的用例。无论您处理的是灰度图像还是彩色图像,nvImageCodec都能提供多功能性和灵活性来满足您的图像解码需求。
用例
- 医学成像:在医学成像领域,快速、准确的图像解码对于及时诊断和治疗至关重要。借助nvImageCodec,医疗保健专业人员可以快速、精确地解码医学图像,从而加快决策速度并改善患者治疗效果。
- 计算机视觉:在计算机视觉应用中,图像解码速度在对象检测和图像分类等实时处理任务中起着至关重要的作用。通过利用nvImageCodec的GPU加速,开发人员可以实现高性能图像解码,从而增强应用程序的响应能力。
- 遥感:在遥感应用中,快速有效地解码大型卫星图像对于环境监测和灾害管理等各种任务至关重要。借助nvImageCodec,研究人员和分析人员可以轻松解码卫星图像,从而实现及时分析和决策。
如何获取nvImageCodec
获取nvImageCodec很简单。您可以从多个来源下载它,例如PyPI、NVIDIA开发者专区,或直接从GitHub存储库下载。下载后,您可以开始尝试编码和解码示例,以提高图像编解码器管道的效率。
如何批量解码高通量JPEG 2000医学图像
下面是一个Python示例,演示使用nvImageCodec库进行批量图像解码。此示例说明如何使用nvImageCodec批量解码HTJ2K图像。指定文件夹内的所有图像均以无损HTJ2K格式压缩,精度为uint16位。输出确认所有医学图像均已成功解码,且质量没有任何损失(下图)。
import os
import os.path
from matplotlib import pyplot as plt
from nvidia import nvimgcodec
dir = "htj2k_lossless"
image_paths = [os.path.join(dir, filename) for filename in os.listdir(dir)]
decode_params = nvimgcodec.DecodeParams(allow_any_depth=True, color_spec=nvimgcodec.ColorSpec.UNCHANGED)
nv_imgs = nvimgcodec.Decoder().read(image_paths, decode_params)
cols = 4
rows = (len(nv_imgs) + cols - 1) // cols
fig, axes = plt.subplots(rows, cols)
fig.set_figheight(2 * rows)
fig.set_figwidth(10)
for i in range(len(nv_imgs)):
axes[i // cols][i % cols].set_title("%ix%i : %s" % (nv_imgs[i].height, nv_imgs[i].width, nv_imgs[i].dtype))
axes[i // cols][i % cols].set_axis_off()
axes[i // cols][i % cols].imshow(nv_imgs[i].cpu(), cmap='gray')
如何批量解码多个JPEG 2000图块
下面是一个Python示例,展示了使用nvImageCodec库对大图像进行基于图块的图像解码。这演示了使用nvImageCodec解码大尺寸JPEG 2000压缩图像的过程。每个图块代表一个感兴趣区域(ROI),尺寸为512 x 512像素。
解码过程包括将图像分割成图块,确定区域总数,然后使用nvImageCodec根据各个图块的索引对其进行解码,提供特定的图块解码信息。生成的输出显示与不同图块相关的信息。
from matplotlib import pyplot as plt
import numpy as np
import random
random.seed(654321)
from nvidia import nvimgcodec
jp2_stream = nvimgcodec.CodeStream('./B_37_FB3-SL_570-ST_NISL-SE_1708_lossless.jp2')
def get_region_grid(stream, roi_height, roi_width):
regions = []
num_regions_y = int(np.ceil(stream.height / roi_height))
num_regions_x = int(np.ceil(stream.width / roi_width))
for tile_y in range(num_regions_y):
for tile_x in range(num_regions_x):
tile_start = (tile_y * roi_height, tile_x * roi_width)
tile_end = (np.clip((tile_y + 1) * roi_height, 0, stream.height), np.clip((tile_x + 1) * roi_width, 0, stream.width))
regions.append(nvimgcodec.Region(start=tile_start, end=tile_end))
print(f"{len(regions)} {roi_height}x{roi_width} regions in total")
return regions
regions_native_tiles = get_region_grid(jp2_stream, jp2_stream.tile_height, jp2_stream.tile_width) # 512x512 tiles
dec_srcs = [nvimgcodec.DecodeSource(jp2_stream, region=regions_native_tiles[random.randint(0, len(regions_native_tiles) - 1)]) for k in range(16)]
imgs = nvimgcodec.Decoder().decode(dec_srcs)
fig, axes = plt.subplots(4, 4)
fig.set_figheight(15)
fig.set_figwidth(15)
i = 0
for ax0 in axes:
for ax1 in ax0:
ax1.imshow(np.array(imgs[i].cpu()))
i = i + 1