Downscaling here means downscaling in terms of image sampling.
Here, we will talk about grayscale images. Generally, a grayscale image is composed of a collection of "1 channel" pixels such as 8-bit, 16-bit, 32-bit, etc. (Color images such as RGB have R, G, and B channels, and these are integrated into an image. This is important. In other words, RGB images have three 8-bit grayscale images. Strictly speaking, there may be an α component (a component that specifies transparency), but the α component is also like an 8-bit grayscale image increased by one.)
16-bit images are often used in medical images. 16-bit images can hold numbers between 0-65535 as pixel values. On the other hand, 8-bit images can only hold numbers from 0 to 255. (Signed and unsigned are not mentioned here.)
In image processing and machine learning, high-bit images are downscaled and converted to 8-bit images in order to simplify calculations or speed up processing. Please note that if you make a mistake during this downscale, the image will be strange.
I will borrow a chest X-ray from JIRA. The one with LEE means uncompressed image. Use this. Sample image
First, use pydicom to load the image and treat it as a numpy ndarray.
!pip install pydicom
Load the data.
import pydicom
import numpy as np
ds = pydicom.dcmread('CR_LEE_IR87a.dcm')#Specify the path
#Extract pixels (treated as ndarray)
pixels = ds.pixel_array
#Since it is flipped left and right, return it
pixels = np.fliplr(pixels)
#Display as an image
import matplotlib.pyplot as plt
plt.imshow(pixels,cmap="gray")#Both are required
plt.show()
To be on the safe side, make sure this image is not 8-bit. You can see it from the DICOM tag, but you can also see it by looking directly at the pixels.
#Take a look at the pixels in this image
print (pixels[512,512]) # 3186
You can see that the pixel value at pixel (x, y) (512,512) is 3186. In other words, it is 16-bit because it is not between 0 and 255 and it is not a real number.
Now you are ready.
numpy
I will simply convert it to 8bit.
#Try 8 bit conversion with numpy
np_img = pixels.astype('uint8')
plt.imshow(np_img,cmap="gray")
plt.show()
It's different.
Pillow
#Use pillow
from PIL import Image, ImageOps
pil_img = Image.fromarray(pixels)
pil_img = pil_img.convert('L')#L:grayscale
plt.imshow(pil_img,cmap="gray")#Both are required
plt.show()
print(np.asarray(pil_img)[512,512]) #255,It feels completely out of range.
This is also different.
OpenCV
#use opencv
#Since it cannot be converted by the following method, astype from numpy in step 1()You may only be able to use the same method as the conversion method with.
#Perhaps my usage is wrong. .. Refer to it as a failure example.
import cv2
cv_img16 = cv2.cvtColor(pixels, cv2.COLOR_BAYER_GR2GRAY)
cv_img8 = cv2.cvtColor(pixels, cv2.COLOR_BAYER_GR2GRAY) #For copying and conversion.
cv2.convertScaleAbs(cv_img16, cv_img8, 1/255,)#Not converted
plt.imshow(cv_img8,cmap="gray")
plt.show()
print(cv_img8[512,512])#Confirm that it is not converted.
Scikit-image
#Use skimage
import skimage
sk_img = skimage.img_as_ubyte(pixels)
plt.imshow(sk_img,cmap="gray")#Both are required
plt.show()
Oh! I think this is a deception, and if you look closely, the pixels are crushed.
I implemented ImageJ downscale. Normalize with maximum, minimum and scaling factors.
#Normalization borrowed from ImageJ
amin=np.amin(pixels)
amax=np.amax(pixels)
scale = 255.0/(amax-amin) # 256.0/(amax-amin+1)
pixelsByte = np.copy(pixels)
# pixelsByte = pixelsByte - amin #It feels like the minimum value does not have to be different.
pixelsByte = pixelsByte*scale # 0-Rescale to the range of 255
# np.clip(pixelsByte,0,255) #It was redundant
pixelsByte = np.uint8(pixelsByte) #Convert to unsigned bytes
print(np.asarray(pixelsByte)[512,512])
plt.imshow(pixelsByte,cmap="gray")
plt.show()
This is this. This is the image I was looking for.
Visionary Imaging Services, Inc.