如何将12位DICOM图像转换为8位JPEG?

3
我正在尝试使用DICOM库将DICOM文件加载到Python中。我已经完成了以下步骤。
ds=dicom.read_file(r"C:\Users\Z003SPFR.AD005\ML\GLCM AND SVM\data\NECT\1.IMA")
#    # store the raw image data
DicomImage = ds.pixel_array

这给了我看起来像是12位的值,因为获得的最高值约为3047,最低值为0。然后我编写了自己的映射函数将其转换到0-255的范围内。我使用了以下代码:

leftMin = 0
leftMax = np.amax(DicomImage)

rightMin = 0
rightMax = 255



def translate(value, leftMin, leftMax, rightMin, rightMax):
    # Figure out how 'wide' each range is
    leftSpan = leftMax - leftMin
    rightSpan = rightMax - rightMin

    # Convert the left range into a 0-1 range (float)
    valueScaled = float(value - leftMin) / float(leftSpan)

    # Convert the 0-1 range into a value in the right range.
    return rightMin + (valueScaled * rightSpan)

#print(translate(value, leftMin, leftMax, rightMin, rightMax))

       

def int12_to_int8(img):
    img_array = []

    for eachRow in img:
        for eachPix in eachRow:
            img_array.append(translate(eachPix,leftMin, leftMax, rightMin, rightMax))
    img_array = np.array(img_array)
    img_array = img_array.reshape(512,512)  
    return img_array

correct_range_image = int12_to_int8(DicomImage)

做完这个之后,我意识到数组 img_arrayuint16 类型的。我想要它是 uint8 类型的。所以我使用了以下代码将其转换为 uint8

cvuint8 = cv2.convertScaleAbs(correct_range_image)

然后我展示了生成的图像。但是我收到的图像并没有很好地代表原始图像。我已经发布了原始图像和转换后的图像的图片。如何使转换更好,以便获得更好的原始图像表示?我用来显示的代码在此处:

cv2.imwrite('1.jpeg', cvuint8 )
cv2.imshow('image',cvuint8 )[enter image description here][1]
cv2.waitKey(0)

图片

转换后的图片
转换后的图片

原始图片
原始图片


可能有帮助:https://stackoverflow.com/q/73034604/5779732 - Amit Joshi
3个回答

2
我找到了解决我的问题的方法。如Ahmed所述,DICOM在适当显示时会涉及重新缩放斜率、截距和窗口水平/宽度。经过阅读大量文档,以下是使用OpenCV、numpy和pydicom库在Python中呈现DICOM的方法,这些库使所有工作变得容易。
代码: 1.读取图像
ds=dicom.read_file("image_path")
# store the raw image data
img = ds.pixel_array
  1. Use rescale slope and intercept information from the image header and translate it.

    rescale_slope=1 rescale_intercept=-1024

    def translate(value,rescale_slope,rescale_intercept):
    
    return (value*rescale_slope)+rescale_intercept 
    
    def int12_to_int8(DicomImage):
        img_array = []
    
    for eachRow in DicomImage:
        for eachPix in eachRow:
            img_array.append(translate(eachPix,rescale_slope,rescale_intercept))
    img_array = np.array(img_array)
    img_array = img_array.reshape(512,512)  
    return img_array
    
    img_1 = int12_to_int8(img)
    

3. 使用图像头部的窗位和窗宽信息来在正确范围内显示。

def get_LUT_value(data, window, level)

    return np.piecewise(data, 
        [data <= (level - 0.5 - (window-1)/2),
            data > (level - 0.5 + (window-1)/2)],
            [0, 255, lambda data: ((data - (level - 0.5))/(window-1) + 0.5)*(255-0)])

level=200
window=800

scaled_img=get_LUT_value(img, window, level)

4.最后,以期望的最终图像为准。

scaled_img = cv2.convertScaleAbs(scaled_img)
cv2.imshow('image',scaled_img)
cv2.imwrite("hem.jpg",scaled_img)
cv2.waitKey(0)

0
请参考http://dicom.nema.org/DICOM/2013/output/chtml/part04/sect_N.2.html,了解医学图像像素数据在显示之前如何呈现。 并非所有提到的概念都适用,但我认为您可能忽略了窗位和窗宽值的重要性。 在此阅读有关窗位和窗宽计算的方程式DICOM图像的窗宽和窗位计算
因此,如果您尝试在正确呈现图像之前降低图像的位深度(丢失最不重要的数据),那么肯定会得到糟糕的图像。 在转换之前考虑对原始数据应用窗位和窗宽,您可以使用imageJ工具来查看图像操作(WL,降级深度)的实际效果,然后再编写代码。

0
可以按照以下步骤操作: https://raw.githubusercontent.com/VolumeRC/AtlasConversionScripts/master/src/convertDICOM.py 他们简单地介绍了如何使用窗位/宽度、重缩放坡度/截距来进行DICOM渲染。
def get_LUT_value(data, window, level,rescaleSlope,rescaleIntercept):
    return np.piecewise(data,
                        [((data * rescaleSlope) + rescaleIntercept) <= (level - 0.5 - (window - 1) / 2),
                         ((data * rescaleSlope) + rescaleIntercept) > (level - 0.5 + (window - 1) / 2)],
                        [0, 255, lambda VAL: ((((VAL * rescaleSlope) + rescaleIntercept) - (level - 0.5)) / (
                        window - 1) + 0.5) * (255 - 0)])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接