機械系エンジニアの備忘録

20代独身社会人。仕事では機械・機構の研究開発を行っているエンジニアが、自分の専門分野ではないpythonを扱って楽しむブログです。

MENU

PythonとOpenCVで簡単にKuwaharaフィルターを適用する方法

Kuwaharaフィルター(白黒)をPythonで自作する

上がKuwaharaフィルター適用後、下がオリジナル画像です。

 

1. はじめに

Kuwaharaフィルターはノイズを除去しながらエッジを保持することができるため、私が知っている例ではSEM画像の3値化処理などに使われている画像処理フィルターです。また油絵っぽくなるためすごく面白いフィルターです。Kuwaharaフィルターは画像の各画素に対して任意に設定した周囲の領域の平均値を計算し、その領域の標準偏差が最も小さい値を新しい画素値として適用することで画像の平滑化を行います。詳細はSection 3で説明します。

非常に面白い画像フィルターですがopencvにデフォルトで入っていないため、今回はPythonOpenCVを使ってKuwaharaフィルターを自作する方法を紹介します。pythoコードはコピペで使用OKです。

 

 2. コード

以下が、画像にKuwaharaフィルターを適用するPythonコードです。

import cv2
import numpy as np
import time

start = time.perf_counter()

# 画像の読み込み
img = cv2.imread('/Users/xxxxx/Desktop/haibiscas.jpg', 0)
img_kuwahara = img.copy()

# 画像の縦横の画素数を取得
height, width = img.shape
print(height, width)

# Kuwaharaフィルター
a = 5  # フィルターの直径
n = int((a - 1) / 2)

for i in range(n, height - n):
    for j in range(n, width - n):
        top_left = img[i - n:i, j - n:j]
        top_right = img[i:i + n, j - n:j]
        bottom_left = img[i - n:i, j:j + n]
        bottom_right = img[i:i + n, j:j + n]
        trim_mean = [top_left.mean(), top_right.mean(), bottom_left.mean(), bottom_right.mean()]
        trim_std = [np.std(top_left), np.std(top_right), np.std(bottom_left), np.std(bottom_right)]
        img_kuwahara[i, j] = trim_mean[trim_std.index(min(trim_std))]

end = time.perf_counter()
print('{:.2f}'.format((end - start) / 60))

cv2.startWindowThread()
cv2.imshow('test', img_kuwahara)
cv2.imshow("original", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)

 

3. コードの解説

ライブラリのインポート

最初に必要なライブラリをインポートします。cv2はOpenCVのライブラリで画像処理に使います。numpyは数値計算用、timeはプログラムの実行時間を測るために使います。

import cv2
import numpy as np
import time

 

開始時間の計測(任意)

プログラムの開始時間を記録します。ここは任意なのでプログラムの実行時間を測る必要がなければ飛ばして下さい。

start = time.perf_counter()

 

画像の読み込み

指定したパスから画像をグレースケールで読み込み、Kuwaharaフィルター適用後の画像のコピーを作成します。ファイルパスはお使いのPCに適した名前にしてください。

img = cv2.imread('/Users/xxxxx/Desktop/haibiscas.jpg', 0)
img_kuwahara = img.copy()

 

Kuwaharaフィルターの処理

フィルター径を設定し、各画素に対して四つの領域を定義します。それぞれの領域の平均値と標準偏差を計算し、標準偏差が最も小さい領域の平均値を新しい画素の値とします。

a = 5  # フィルターの直径
n = int((a - 1) / 2)

for i in range(n, height - n):
    for j in range(n, width - n):
        top_left = img[i - n:i, j - n:j]
        top_right = img[i:i + n, j - n:j]
        bottom_left = img[i - n:i, j:j + n]
        bottom_right = img[i:i + n, j:j + n]
        trim_mean = [top_left.mean(), top_right.mean(), bottom_left.mean(), bottom_right.mean()]
        trim_std = [np.std(top_left), np.std(top_right), np.std(bottom_left), np.std(bottom_right)]
        img_kuwahara[i, j] = trim_mean[trim_std.index(min(trim_std))]

詳細は以下の図になります。

例えば71のセルを中心にフィルター径3でKuwaharaフィルターを実施する場合、左上、右上、左下、右下の4つの9×9の四角形に注目します。

それぞれの四角形において分散を計算すると右下の紫色の四角が分散=18.21で他の3つの四角形より小さいです。よって中央の71は、右下の最も分散が小さい紫色の四角の平均41に書き換えられます。これを画像全体で繰り返し行うのがKuwaharaフィルターです。

実行時間の計測と表示(任意)

プログラムの終了時間を記録し、実行時間を分単位で表示します。

ここは任意ですので必要ない人は飛ばしてください。

end = time.perf_counter()
print('{:.2f}'.format((end - start) / 60))

 

画像の表示

OpenCVを使って、元の画像とフィルター適用後の画像を表示します。

cv2.startWindowThread()
cv2.imshow('test', img_kuwahara)
cv2.imshow("original", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)

 

4. おすすめ参考書

PyAutoGUIやOpenCVの使用方法が載っている書籍を紹介します。

この3つは非常に丁寧に書かれているのでお勧めです。

Pythonで始めるOpenCV入門

Pythonで始めるOpenCV入門

 

Pythonで始めるOpenCV入門」と 「PythonGUI自動化しよう」については、Amazonの月額読み放題サービス「Kindle unlimited」に加入していれば読み放題です。

 

 5. おわりに

このコードを使うことで簡単にKuwaharaフィルターを画像に適用することができます。今回は白黒画像に対してKuwaharaフィルターを適用する方法を紹介しました、次回はカラー画像でのKuwaharaフィルターについて紹介します。