I am Charmie

メモとログ

skimage.transform.rescale with color image

skimage.transform.rescale関数を使った時にハマったので,備忘録.

色々試行錯誤したコードはページの下の方に載せている. カラー画像とグレースケール画像をで変換する時の色々を載せる.

原画像

以下のように,両画像が8bitの画像として読み込まれる.

  color image
    shape: (512, 512, 3)
    dtype: uint8
    min(): 0
    max(): 255
    mean(): 114.59900410970052
  gray-scale image
    shape: (512, 512)
    dtype: uint8
    min(): 0
    max(): 255
    mean(): 118.31400299072266

rescale()

rescale()に原画像とスケールファクタのみを与えると,予期せぬ変換も施される.
最適解と言えないかもしれないが,色々試した結果を如何に示していく.

  1. カラー画像は色相が3チャンネルから6チャンネルになる
  2. 数値の範囲: [0, 255]から[0.0, 1.0]になる
  3. データ型: uint8からfloat64になる
# コードの一部抜粋
    img_rescaled = rescale(img, scale_factor)
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = rescale(img_gray, scale_factor)
    print_image_info(img_rescaled, '  gray-scale image', 4)
# 実行結果
  color image
    shape: (1024, 1024, 6)
    dtype: float64
    min(): 0.0
    max(): 1.0
    mean(): 0.44507563581653625
  gray-scale image
    shape: (1024, 1024)
    dtype: float64
    min(): 0.005147058823529662
    max(): 0.9975490196078434
    mean(): 0.4639770320817536

カラー画像の6チャンネル化を防ぐ

rescale()のオプション引数multichannelをTrueにする. ただし,グレースケール画像を入力する時にTrueにすると,画像の縦方向のみrescaleされることになるので避ける必要がある.

  • multichannelをTrueにすると,画像の最後の次元をrescaleしない
  • カラー画像は最後の次元である色相が3のまま変化しなくなった
  • グレースケール画像は最後の次元である横幅が512のまま変化しなくなった
# コードの一部抜粋
    img_rescaled = rescale(img, scale_factor, multichannel=True)
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = rescale(img_gray, scale_factor, multichannel=True)
    print_image_info(img_rescaled, '  gray-scale image', 4)
# 実行結果
  color image
    shape: (1024, 1024, 3)
    dtype: float64
    min(): 0.0
    max(): 1.0
    mean(): 0.4494087048605384
  gray-scale image
    shape: (1024, 512)
    dtype: float64
    min(): 6.520274516386231e-15
    max(): 0.9999999999999966
    mean(): 0.46397646735696224

数値の範囲を保持したい

rescale()のオプション引数preserve_rangeをTrueにする.
画像のデータ型と数値の範囲についてはここに書いてある.

  • preserve_rangeをTrueにすると,出力画像の数値の範囲が保持されている
  • グレースケール画像の最小値・最大値が変化してる理由はrescale時の補間処理に原因がありそう
# コードの一部抜粋
# カラー画像は6チャンネル画像にならないように,multichannel=Trueとしている
    img_rescaled = rescale(img, scale_factor, preserve_range=True, multichannel=True)
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = rescale(img_gray, scale_factor, preserve_range=True)
    print_image_info(img_rescaled, '  gray-scale image', 4)
# 実行結果
  color image
    shape: (1024, 1024, 3)
    dtype: float64
    min(): 0.0
    max(): 255.0
    mean(): 114.5992197394371
  gray-scale image
    shape: (1024, 1024)
    dtype: float64
    min(): 1.312500000000064
    max(): 254.37500000000006
    mean(): 118.31414318084717

データ型をuint8のままで

img_as_ubyte()によってrescale()の出力をuint8に変換 (当然数値の範囲も[0.0, 1.0]から[0, 255]に変換)

# コードの一部抜粋
# カラー画像は6チャンネル画像にならないように,multichannel=Trueとしている
    img_rescaled = img_as_ubyte(rescale(img, scale_factor, multichannel=True))
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = img_as_ubyte(rescale(img_gray, scale_factor))
    print_image_info(img_rescaled, '  gray-scale image', 4)
# 実行結果
  color image
    shape: (1024, 1024, 3)
    dtype: uint8
    min(): 0
    max(): 255
    mean(): 114.59968185424805
  gray-scale image
    shape: (1024, 1024)
    dtype: uint8
    min(): 1
    max(): 254
    mean(): 118.31463718414307

コード

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import numpy as np

import skimage.data
from skimage.transform import rescale
from skimage.util import img_as_ubyte


def print_image_info(img, message=None, spaces=None):
    assert isinstance(img, np.ndarray)
    assert len(img.shape) in [2, 3]

    if message is not None:
        print(message)
    if spaces is None:
        spaces = ""
    else:
        spaces = spaces * " "
    for arg in ['shape', 'dtype', 'min()', 'max()', 'mean()']:
        exec("print('%s%s:', img.%s)" % (spaces, arg, arg))


def main():
    print('Original image')
    img = skimage.data.astronaut()
    print_image_info(img, '  color image', 4)
    img_gray = skimage.data.camera()
    print_image_info(img_gray, '  gray-scale image', 4)

    scale_factor = 2.0
    print('\nrescale()')
    img_rescaled = rescale(img, scale_factor)
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = rescale(img_gray, scale_factor)
    print_image_info(img_rescaled, '  gray-scale image', 4)

    print('\nrescale() with multichannel=True')
    img_rescaled = rescale(img, scale_factor, multichannel=True)
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = rescale(img_gray, scale_factor, multichannel=True)
    print_image_info(img_rescaled, '  gray-scale image', 4)

    print('\nrescale() with preserve_range=True')
    img_rescaled = rescale(img, scale_factor, preserve_range=True, multichannel=True)
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = rescale(img_gray, scale_factor, preserve_range=True)
    print_image_info(img_rescaled, '  gray-scale image', 4)

    print('\nimg_as_ubyte(rescale())')
    img_rescaled = img_as_ubyte(rescale(img, scale_factor, multichannel=True))
    print_image_info(img_rescaled, '  color image', 4)
    img_rescaled = img_as_ubyte(rescale(img_gray, scale_factor))
    print_image_info(img_rescaled, '  gray-scale image', 4)


if __name__ == '__main__':
    main()