plant-raspberrypi3のブログ

ラズベリーパイ3とPythonに挑戦して、植物を愛でたり画像を加工したりします。最近はscikit-imageの勉強してます。

scikit-imageのお勉強 第4回 transformモジュール ~拡大縮小・回転・アフィン変換~

scikit-imageの便利機能の備忘録シリーズ。

これまでの回はコチラ。

第1回 dataモジュールとioモジュール ~画像の入出力~

第2回 colorモジュール ~色空間~

第3回 drawモジュール ~図形の描画~

今回は第4回です。transformモジュールについてメモしていきます。

transformモジュール

回転などの変換に関するモジュールです。

公式ページ:Module: transform — skimage v0.14.0 docs

よく使いそうな機能は以下の4種類。

リサイズ・リスケール

  • transform.resize
  • transform.rescale

回転

  • transform.rotate

スワール(渦巻き化)

  • transform.swirl

アフィン変換

  • transform.AffineTransform + transform.warp

他に画像ピラミッド、Hough変換(特徴抽出の一種)やradon変換(断層画像の変換)なども利用できます。 この中で、今回は画像ピラミッドのみ取り上げます。

画像ピラミッド

  • transform.pyramid_reduce
  • transform.pyramid_expand
  • transform.pyramid_gaussian

いつも通り、テスト画像をdataモジュールで読み込んでおきます。
今回、拡大縮小が見やすいようにimage_show関数をちょっと改変しました。

・code 

%matplotlib inline
import matplotlib.pyplot as plt

from skimage import transform, data

def image_show(img):
    h,w,c = img.shape
    plt.figure(figsize=[float(w/100),float(h/100)])
    plt.title(f'w={w}, h={h}')
    plt.imshow(img)
    plt.xticks([])
    plt.yticks([])
    plt.show()

rocket = data.rocket()
image_show(rocket)

f:id:plant-raspberrypi3:20180904144416p:plain

リサイズ・リスケール

transform.resizeで指定の画像サイズにリサイズできます。

第1引数:画像データ
第2引数:リサイズ後の画像サイズ(タプル)
第3以降の引数:mode, anti_aliasingなど

・code

# 元画像表示
image_show(rocket)

# 画像の縮小
# mode='reflect'、anti_aliasing=True にする
resize_img = transform.resize(rocket,(200,500),mode='reflect',anti_aliasing=True)

image_show(resize_img)

# 画像の拡大
# modeはどれでも適用可
resize_img = transform.resize(rocket,(800,800),mode='constant',anti_aliasing=True)

image_show(resize_img)

・元画像

f:id:plant-raspberrypi3:20180904144537p:plain

・縮小画像

f:id:plant-raspberrypi3:20180904144543p:plain

・拡大画像

f:id:plant-raspberrypi3:20180904144556p:plain


transform.rescaleは特定の倍率に画像を拡大・縮小するための関数です。

第1引数:画像データ
第2引数:倍率(拡大なら>1、縮小なら<1)
第3以降の引数:mode, anti_aliasingなど

・code

# 元画像表示
image_show(rocket)

# 画像の縮小
# mode='reflect'、anti_aliasing=Trueにする
# multichannelは明示(True/False)
rescale_img = transform.rescale(rocket,0.2,mode='reflect',anti_aliasing=True,multichannel=True)

image_show(rescale_img)

# 画像の拡大
# modeはどれでも適用可
# multichannelは明示(True/False)
rescale_img = transform.rescale(rocket,1.5,mode='constant',anti_aliasing=True,multichannel=True)

image_show(rescale_img)

・元画像

f:id:plant-raspberrypi3:20180904144638p:plain

・縮小画像

f:id:plant-raspberrypi3:20180904144646p:plain

・拡大画像

f:id:plant-raspberrypi3:20180904144654p:plain

回転

画像の回転はtransform.rotateで行います。回転の際にリサイズするかどうかを選べます。

第1引数:画像データ
第2引数:角度(時計回り方向)
第3以降の引数:resizeなど

・code

# 元画像の表示
image_show(rocket)

# リサイズなしの回転
rotate_img = transform.rotate(rocket,90,resize=False)

image_show(rotate_img)

# リサイズありの回転
rotate_img = transform.rotate(rocket,90,resize=True)

image_show(rotate_img)

・元画像

f:id:plant-raspberrypi3:20180904144721p:plain

・リサイズなしの回転

f:id:plant-raspberrypi3:20180904144732p:plain

・リサイズありの回転

f:id:plant-raspberrypi3:20180904144744p:plain

スワール(渦巻き化)

画像に渦巻き加工を入れる関数としてtransform.swirlというものがあります。

第1引数:画像データ
第2以降の引数:mode, strengthなど

strengthを変えることで渦の強さを調節できます。

・code

for i in range(1,11,3):
    print(f'strength={i}')
    swirl_img = transform.swirl(rocket,mode='reflect',strength=i)
    image_show(swirl_img)

・元画像

f:id:plant-raspberrypi3:20180904144537p:plain

・strength=1

f:id:plant-raspberrypi3:20180904144903p:plain

・strength=4

f:id:plant-raspberrypi3:20180904144910p:plain

・strength=7

f:id:plant-raspberrypi3:20180904144916p:plain

・strength=10

f:id:plant-raspberrypi3:20180904144923p:plain

アフィン変換

アフィン変換をマスターすると拡大縮小・平行移動・反転・回転・剪断変形などなんでもできて便利です。

zellij.hatenablog.com

「アフィン変換とは平行移動と線形変換を組み合わせた変換」のこと。(中略) 具体的には、線形変換(拡大縮小、剪断、回転)、平行移動があり、これらの組み合わせで表現される。

scikit-imageでのアフィン変換のやり方は、ここまで説明してきた変換とは手順が違うので注意が必要です。 

1. transform.AffineTransformクラスのインスタンスを作成
2. transform.warpで画像に上記インスタンスの効果を適用

  

それぞれのクラス・関数の引数は以下の通りです。

  • transform.AffineTransformの引数

scale:拡大縮小(縦・横のタプルで与える、マイナスで反転)
translation:平行移動(縦・横のタプルで与える)
rotation:回転
shear:剪断変形

反転・回転・剪断変形は画像が画面からはみ出ることが多いので、適宜translationを組み合わせて使います。

  • transform.warpの引数

第1引数:画像データ
第2引数: transform.AffineTransformクラス等のインスタンス

・code

# 元画像の表示
image_show(rocket)

# 拡大縮小のみ
at = transform.AffineTransform(scale=(1.2,1.2))
warp_img = transform.warp(rocket,at)
image_show(warp_img)

# 平行移動のみ
at = transform.AffineTransform(translation=(-30,-50))
warp_img = transform.warp(rocket,at)
image_show(warp_img)

#反転と平行移動
at = transform.AffineTransform(scale=(-1,1),translation=(640, 0))
warp_img = transform.warp(rocket,at)
image_show(warp_img)

# 回転と平行移動
at = transform.AffineTransform(rotation=0.5,translation=(300,-100))
warp_img = transform.warp(rocket,at)
image_show(warp_img)

# 剪断変形と拡大縮小、平行移動
at = transform.AffineTransform(shear=1,scale=(1.5,1.5),translation=(30,30))
warp_img = transform.warp(rocket,at)
image_show(warp_img)

・元画像

f:id:plant-raspberrypi3:20180904145046p:plain

・縮小

f:id:plant-raspberrypi3:20180904145052p:plain

・平行移動

f:id:plant-raspberrypi3:20180904145110p:plain

・反転と平行移動

f:id:plant-raspberrypi3:20180905104616p:plain

・回転と平行移動

f:id:plant-raspberrypi3:20180904145117p:plain

・剪断変形と拡大縮小、平行移動

f:id:plant-raspberrypi3:20180904145128p:plain

おまけ:画像ピラミッド

画像ピラミッドに関してはOpenCVのTutorialページがわかりやすいです。

画像ピラミッド — OpenCV-Python Tutorials 1 documentation

例えば画像中で何かを探すとき(例えば顔)、画像中にどのような大きさで現れるか分かりません。そのような状況では様々な解像度の画像を用意し、全画像に対して物体検出を試みます。これらの異なる解像度を持つ画像の集合を画像ピラミッド(最大解像度の画像を下に、最小解像度の画像を上に積むとピラミッドのようになるからです)と呼びます。

 
画像ピラミッドの関数には、transform.pyramid_reducetransform.pyramid_expandやジェネレーターとして機能するtransform.pyramid_gaussianなどがあります。

・code

#pyramidシリーズ

image_show(rocket)

pyramid_img = transform.pyramid_reduce(rocket,downscale=5,multichannel=True)

image_show(pyramid_img)

pyramid_img = transform.pyramid_expand(rocket,upscale=1.5,multichannel=True)

image_show(pyramid_img)

・元画像

f:id:plant-raspberrypi3:20180904145249p:plain

・transform.pyramid_reduce

f:id:plant-raspberrypi3:20180904145304p:plain

・transform.pyramid_expand

f:id:plant-raspberrypi3:20180904145313p:plain

これらは普通の拡大縮小とあまり変わらないですね。


次のようにジェネレーターを使うと画像ピラミッドっぽさが出ます。

・code

pyramid_gen = transform.pyramid_gaussian(rocket,downscale=2,multichannel=True)

for i in range(5):
    pyramid_img = next(pyramid_gen)

    image_show(pyramid_img)

・1回目

f:id:plant-raspberrypi3:20180904145408p:plain

・2回目

f:id:plant-raspberrypi3:20180904145414p:plain

・3回目

f:id:plant-raspberrypi3:20180904145421p:plain

・4回目

f:id:plant-raspberrypi3:20180904145429p:plain

・5回目

f:id:plant-raspberrypi3:20180904145437p:plain

今回はここまで。

2018/9/10訂正

アフィン変換の説明のところで引数に誤植があったため修正しました。