I am trying to load DICOM files into python using the dicom library. I have done the following
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
This gives me values that appear to be 12 bit, since the highest value obtained was around 3047 and lowest value was 0. Then i made my own mapping function to bring it to the range 0-255. I used the following code:
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)
After doing this I realized that the array img_array was of type uint16. I wanted it as uint8, so i used the following line to convert to uint8:
cvuint8 = cv2.convertScaleAbs(correct_range_image)
Then I displayed the resulting image. But i received an image that didn't represent the original image very well. I have posted pictures of the original image and the converted image. How can I make the conversion better so that I get a better representation of the original image?? Code I used to display is here :
cv2.imwrite('1.jpeg', cvuint8 )
cv2.imshow('image',cvuint8 )[enter image description here][1]
cv2.waitKey(0)
IMAGES
[1] Converted Image: https://i.stack.imgur.com/wdGCo.jpg [2] Original Image: https://i.stack.imgur.com/JyfYI.jpg
I figured out the solution for my problem.As mentioned above by Ahmed, DICOM plays around rescale slope, intercept and window level/width for proper display. After going through lot of documents, here is the way to render DICOM in Python using OpenCV,numpy and pydicom libraries which make all work easy
Code: 1.Read the image
ds=dicom.read_file("image_path")
# store the raw image data
img = ds.pixel_array
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.Use window level and width information from the image header to display in the proper range.
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.Finally, with final image as wanted
scaled_img = cv2.convertScaleAbs(scaled_img)
cv2.imshow('image',scaled_img)
cv2.imwrite("hem.jpg",scaled_img)
cv2.waitKey(0)
please refer to http://dicom.nema.org/DICOM/2013/output/chtml/part04/sect_N.2.html to see how the medical image pixel data is rendered before its displayed not all mentioned concepts is applied but i assume your missing the importance of Window level and window Center Values . read about Windowing equation here Window width and center calculation of DICOM image
So if you are trying to downgrade the bitDepth of the Image before its correctly rendered (losing least important data) , for sure you will get a bad image . consider applying windowing on original data before conversion , you can play with imageJ tool to see image operations (WL , Downgrading Depth) in action before writing the code for it
Can follow this: https://raw.githubusercontent.com/VolumeRC/AtlasConversionScripts/master/src/convertDICOM.py
where in simple way they have mentioned DICOM rendering using window level/width, rescale slope/intercept
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)])
来源:https://stackoverflow.com/questions/44924335/converting-12-bit-dicom-image-to-8-bit-jpeg