前提
ここでいうダウンスケーリングは、画像の標本化でいうところのダウンスケールを意味します。
画像のダウンスケーリング
ここでは、グレースケール画像を対象に話をします。
一般的に、グレースケール画像は8-bit, 16-bit, 32-bit等、「1チャンネル」の画素の集まりで構成されています。
(RGBなどのカラー画像は、R、G、Bそれぞれのチャンネルがあり、これらを統合して画像にしています。この点は重要です。言い換えれば、RGB画像は、8-bit grayscale画像が3枚分あると言えます。厳密にはα成分(透明度を指定する成分)がある場合もありますが、α成分も8-bit grayscale画像が一枚分増えたようなものです。)
よく医用画像で使われるのが、16-bit画像です。
16-bit画像は、0-65535の間の数値をピクセル値として保持できます。
一方で、8-bit画像は、0-255までの数値しか保持できません。
(符号付き、符号なしについてはここでは敢えて触れません。)
画像処理や機械学習などでは、計算を簡便にするため、あるいは処理を高速にするために、高いビットの画像を8-bit画像にダウンスケールして変換する処理を行います。
このダウンスケールの際にお作法を間違えると、画像がおかしなことになるので注意が必要です。
サンプル画像
JIRAの胸部レントゲンをお借りします。
LEEとついているものは非圧縮画像という意味です。これを使います。
サンプル画像
ダウンスケール例(16-bitから8-bit)
まずは、画像をロードしてnumpyのndarrayとして扱うために、pydicomを使って行きます。
!pip install pydicom
データをロードします。
import pydicom
import numpy as np
ds = pydicom.dcmread('CR_LEE_IR87a.dcm')#パスを指定する
#ピクセルを取り出す(ndarrayとして扱っています)
pixels = ds.pixel_array
# 左右反転しているので戻す
pixels = np.fliplr(pixels)
#画像として表示
import matplotlib.pyplot as plt
plt.imshow(pixels,cmap="gray")#2つとも必要
plt.show()
念のため、この画像が8-bitでないことを確認します。
DICOMタグからもわかりますが、直接ピクセルをみてもわかります。
# この画像の画素を見てみる
print (pixels[512,512]) # 3186
画素(x,y)(512,512)にあるピクセル値は3186であることが確認できます。
つまり、0-255の間にない、かつ、実数ではないので16-bitであることがわかります。
これで準備が出来ました。
numpy
単純に8bitに変換してみます。
# numpyで8 bit変換してみる
np_img = pixels.astype('uint8')
plt.imshow(np_img,cmap="gray")
plt.show()
違うなあ。
Pillow
# 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")#2つとも必要
plt.show()
print(np.asarray(pil_img)[512,512]) #255, 完全にアウトオブレンジな感じです。
これも違うなあ。
OpenCV
# opencvを使う
# 変換可能
# pixels は16 bit配列の変数とする。
import cv2
cv_img8 = np.zeros(pixels.shape) # dummy変数
cv_img8 = cv2.convertScaleAbs(pixels-np.min(pixels), cv_img8, 255/(np.max(pixels)-np.min(pixels)), beta=0) # 2021/6/22修正
cv_img8 = cv_img8.astype(np.uint8)
plt.imshow(cv_img8, cmap="gray")
plt.show()
print(cv_img8[512,512])
Scikit-image
# skimageを使う
import skimage
sk_img = skimage.img_as_ubyte(pixels)
plt.imshow(sk_img,cmap="gray")#2つとも必要
plt.show()
お!と思うが、これはまやかしで、よくみると画素が潰れている。
ImageJのアルゴリズムを用いたダウンスケール
ImageJのダウンスケールを実装してみました。
最大値、最小値と、スケーリングファクタで正規化します。
# ImageJから拝借したノーマリゼーション
pixels_ = np.copy(pixels)
amin=np.amin(pixels_)
amax=np.amax(pixels_)
pixelsByte = ((pixels_-amin)/(amax-amin))*255
# pixelsByte = ((pixels_-amin)/(amax-amin+1))*256
pixelsByte = np.clip(pixelsByte,0,255)
pixelsByte = np.uint8(pixelsByte) # 符号なしバイトに変換
print(np.asarray(pixelsByte)[512,512])
plt.imshow(pixelsByte,cmap="gray")
plt.show()
これこれ。求めてた画像です。
Visionary Imaging Services, Inc.