ユーニックス総合研究所

  • home
  • archives
  • python-pillow-image-filter

PythonのPillowのImage.filterの使い方: 画像にフィルター(エフェクト)を適用する

  • 作成日: 2020-10-09
  • 更新日: 2023-12-24
  • カテゴリ: Python

PillowのImage.filterの使い方

Pythonの画像処理ライブラリであるPillowには画像を管理するImageモジュールがあります。
Imageモジュールは画像に指定のフィルター(エフェクト)をかける関数Image.filter()を持っています。

また、ImageFilterというフィルターを管理するモジュールもあり、このモジュールのフィルターをImage.filter()に指定することで多種多様なエフェクトを画像に適用することが出来るようになります。

Pillowには沢山のフィルターが備わっているため、このフィルターを使えば大抵のエフェクトには対応できます。
フィルターにはガウシアンブラーやメディアンフィルタ、エンボスなどがあります。
これらのフィルターはC言語で記述され、動作も早く、またPythonがバインディングしているので使い勝手もいいです。
まさにPillowのフィルターはC言語の早さとPythonの便利さを兼ね備えているわけですが、フィルターにかぎらずPillowのモジュールはたいていこの形式が取られています。
Pythonの速度の遅さをC言語でカバーしているわけですね。

Image.filter()の構造

Image.filter()は↓のような構造になっています。
Image.filter()は引数を1つ取り、返り値を1つ返す関数です。
引数のfilterImageFilterのフィルターオブジェクトを渡すことで機能します。

Image.filter(filter)  

filter(第1引数)

フィルターのカーネルです。
これはImageFilterに列挙されているオブジェクトです。
ImageFilter()はフィルター実行時にこのオブジェクトをインスタンス化してフィルターを実行します。

返り値

返り値はフィルター適用後の画像(Imageオブジェクト)です。

ImageFilter

ImageFilterは画像へ適用するフィルターが列挙されているモジュールです。
開発者はこのImageFilterから使いたいフィルターを選び、そのフィルターをImage.filter()に渡して、フィルターを実行するというのが一般的な流れになります。

使用できるフィルター

ImageFilterから選択できるフィルターは以下の通りです。
以下の定数的フィルタークラス(組み込みフィルター)があります。

  • BLUR
  • CONTOUR
  • DETAIL
  • EDGE_ENHANCE
  • EDGE_ENHANCE_MORE
  • EMBOSS
  • FIND_EDGES
  • SHARPEN
  • SMOOTH
  • SMOOTH_MORE

こちらのフィルターはインスタンス化の必要もなく定数のようにImage.filter()に指定すれば使えます。
それから以下のフィルタークラス(ランクフィルター、マルチバンドフィルターなど)があります。

  • Kernel
  • RankFilter
  • MedianFilter
  • MinFilter
  • MaxFilter
  • ModeFilter
  • GaussianBlur
  • BoxBlur
  • UnsharpMask
  • Color3DLUT

こちらのフィルターはインスタンス化してパラメーターを指定して使います。

使用する画像

今回のImage.filter()の検証には↓の画像を使います。
この画像はRGBAで横幅500px, 高さ500pxPNG画像です。

画像にブラー(BLUR)をかける

画像にブラーをかけます。ブラーとは輪郭をぼやけさせるフィルターです。
まずImage.open()に画像のパスを指定して画像をImageオブジェクトとして開きます。
それからそのオブジェクトのメソッドImage.filter()を、ImageFilter.BLUEを指定して実行します。
すると返り値としてフィルター適用後のオブジェクトが返ってきますので、Image.save()に保存先の画像のパスを指定してこのオブジェクトを画像として保存します。

from PIL import Image, ImageFilter  

im = Image.open('img/a.png')  # 画像を開く  
filter_im = im.filter(filter=ImageFilter.BLUR)  # ブラーを適用  
filter_im.save('dst/out1.png')  # フィルターを適用した画像を保存  

保存したdst/out1.pngを開くと↓のように表示されます。

画像にエンボス(EMBOSS)をかける

画像にエンボスをかける場合はImage.EMBOSSを指定します。

from PIL import Image, ImageFilter  

im = Image.open('img/a.png')  # 画像を開く  
filter_im = im.filter(filter=ImageFilter.EMBOSS)  # エンボスを適用  
filter_im.save('dst/out2.png')  # フィルターを適用した画像を保存  

出力結果。

画像を収縮(MinFilter)させる

ImageFilter.MinFilterの使用例です。
これはインスタンス化してそのオブジェクトをImage.filter()に渡して使用します。

from PIL import Image, ImageFilter  

im = Image.open('img/a.png')  # 画像を開く  
filter_im = im.filter(filter=ImageFilter.MinFilter())  # ミンフィルターを適用  
filter_im.save('dst/out3.png')  # フィルターを適用した画像を保存  

出力結果。

ソースコードの解析

Image.filter()の実装は↓のようになっています。

内部的には引数のfilterがインスタンス化できる場合はインスタンス化しています。
また、画像のバンドが1つ、またはフィルターがマルチバンドフィルターの場合と、それ以外とで処理を分けています。
どちらの場合もインスタンス化したfilterのメソッドfilterを呼び出しています。
つまり、フィルターのインターフェースとしてはこのfilterメソッドを定義する必要があるわけですね。

filterメソッドの実装はフィルターによってまちまちです。
たとえばGussianBlurfilterメソッドの実装は↓のようになっています。

    def filter(self, image):  
        return image.gaussian_blur(self.radius)  

Imageオブジェクトのgaussian_blur()を呼び出しているだけです。
このgaussian_blur()は最終的にC言語で書かれたモジュールに辿り着きます。

↑の関数から

↑の関数が呼ばれます。
ImagingGaussianBlur()の実装を見ると内部ではImagingBoxBlur()にパラメーターを指定して処理を委譲しています。

ImagingBoxBlur()は以下のモード以外ではエラーを生成するのがわかります。

    if (!(strcmp(imIn->mode, "RGB") == 0 ||  
          strcmp(imIn->mode, "RGBA") == 0 ||  
          strcmp(imIn->mode, "RGBa") == 0 ||  
          strcmp(imIn->mode, "RGBX") == 0 ||  
          strcmp(imIn->mode, "CMYK") == 0 ||  
          strcmp(imIn->mode, "L") == 0 ||  
          strcmp(imIn->mode, "LA") == 0 ||  
          strcmp(imIn->mode, "La") == 0)) {  
        return ImagingError_ModeError();  
    }  

よって↓のコードのようにモード1の画像にImageFilter.GaussianBlurを適用するとValueErrorが生成されます。

from PIL import Image, ImageFilter  

im = Image.new('1', (500, 500), 0)  # モード1の画像を生成  
im.filter(filter=ImageFilter.GaussianBlur())  # ガウシアンブラーを適用  

出力結果。

Traceback (most recent call last):  
  File "sample.py", line 4, in <module>  
    im.filter(filter=ImageFilter.GaussianBlur())  # ガウシアンブラーを適用  
  File "C:\venv_win\lib\site-packages\PIL\Image.py", line 1208, in filter  
    return self._new(filter.filter(self.im))  
  File "C:\venv_win\lib\site-packages\PIL\ImageFilter.py", line 168, in filter  
    return image.gaussian_blur(self.radius)  
ValueError: image has wrong mode  

問題

Q1: 画像にブラーをかけたい場合に適当なフィルターを答えよ

  1. ImageFilter.BLUR
  2. ImageFilter.MinFilter
  3. ImageFilter.EMBOSS

Q2: Image.filter()の引数として適当なオブジェクトを答えよ

  1. convertメソッドが定義されたオブジェクト
  2. filterメソッドが定義されたオブジェクト
  3. cropメソッドが定義されたオブジェクト

Q3: Image.filter()の戻り値として適当なものを答えよ

  1. int
  2. list
  3. Image

問題の正解はこちら↓

Q1: 1
Q2: 2
Q3: 3