【Python】画像を平均化しノイズを除去してから二値化する

【Python】画像を平均化しノイズを除去してから二値化する

前回、【OpenCV】画像を二値化して表示するで画像の二値化について解説しました。実は、普通に画像を二値化するよりも、平均化という処理を予め加えることで、画像のノイズを取り除く事ができ、普通に二値化するよりも解析がしやすい画像となります。この平均化によってノイズを除くことも、Pythonだったら数行のコードで簡単に実現することが出来ます。そこで今回は、PythonのOpenCVを用いて画像を平均化してノイズ除去してから二値化する方法について紹介します。
*本記事は、前回の記事を読んでないという方でも理解できるように書かれています。

動作検証済み環境
macOS Catalina(10.15.7), python3.7.10, Jupyter Notebook

*本記事では、Pythonを実行する環境としてJupyter Notebookを用いています。Jupyter Notebookのインストールがお済み出ない方は、こちらの記事を参考にインストールをお願いします。インストールは5分程度で完了します。

画像の平均化と二値化処理とは?

「画像の平均化」とは簡単に言うと画像をぼかしてノイズを除く処理のことです。「平滑化」とも呼ばれたりもします。画像内の隣り合うピクセル値の差を低減することで、滑らかな画像を作ることが出来ます。平均化することによって画像中に存在するノイズを低減させられるメリットがあります。

「画像の二値化処理」とは、分析対象の画像を白と黒の2色のみに変換する画像処理を指します。二値化処理は画像と背景の境界を明確にするため、物体の検出が用意になることやデータ容量が小さくして画像処理の高速化させられるなどの利点があります。

以下に同じ画像に対してそれぞれの処理をした画像を貼ります。実際に、平均化してから二値化を行った画像にはぼかしが入り、細かなノイズが少なくなっていることが分かると思います。

処理なし

平均化処理

二値化処理
平均化して二値化処理

Pythonで画像の平均化する方法(サンプルコードあり)

まずは画像を平均化する処理を実装します。画像の平均化は、PythonのライブラリであるOpenCVを使うと簡単にできます。実際に画像の平均化をするには、以下のようなコードを書きます。

import cv2  #OpenCVのインポート

img = "stone.jpeg" #平均化したい画像ファイル名

grayscale_img = cv2.imread(img, cv2.IMREAD_GRAYSCALE) #カラー画像を白黒画像で読み出しオブジェクトgrayscale_imgに代入
averaged_img = cv2.blur(grayscale_img,(9,9)) #第一引数で指定したオブジェクトgrayscale_imgを輝度で平均化処理する。第二引数は平均化するピクセル数で、今回の場合は9,9は9x9ピクセルの計81ピクセル

cv2.imwrite("stone_averaged.jpeg", averaged_img) #"stone_averaged.jpeg"として平均化された画像を保存

実際に平均化するコードを実行してみよう

上で記述したコードはaveraged_and_threshold_image.ipynbという名前のjupyter notebookファイルとして、Desktop/Python/LabCode/averaged_and_threshold_imageに保存します。

*コードを実行する前に
コードを実行する前に、お使いのJupyter notebook環境にOpenCVのインストールを行う必要があります。OpenCVのインストール方法については、こちらの記事に写真付きで詳しく解説してありますので、御覧ください。2分程度でOpenCVのインストールができると思います。

実行結果

今回はこちらのカラー画像(stone.jpeg)を平均化していこうと思います。

stone.jpeg

stone.jpegaveraged_and_threshold_image.ipynbと同じ階層(以下の画像だと、averaged_and_threshold_imageフォルダの内)に保存しておきます。

上記のコードを実行すると、Desktop/LabCode/Pythonのフォルダ内にstone_averaged.jpegが出来ていると思います。

コード実行結果

stone_averaged.jpeg

画像にきちんと平均化処理がかかっていますね!

Pythonで画像を平均化してノイズ除去してから二値化する方法(サンプルコードあり)

いよいよ次は、画像を平均化してノイズを除去してから二値化する処理です。こちらでも引き続きOpenCVを使用していきますので、コードの実行前にOpenCVのインストールをお忘れないようお願いします。(OpenCVインストール方法)

画像を平均化してノイズを除去してから二値化するには、以下のようなコードになります。

#平均化して二値化

import cv2  #OpenCVのインポート

img = "stone.jpeg" #平均化して二値化したい画像ファイル名
threshold = 100 #平均化した後に二値化したい閾値

grayscale_img = cv2.imread(img, cv2.IMREAD_GRAYSCALE) #カラー画像を白黒画像で読み出しオブジェクトgrayscale_imgに代入
averaged_img = cv2.blur(grayscale_img,(9,9)) #第一引数で指定したオブジェクトgrayscale_imgを輝度で平均化処理する。第二引数は平均化するピクセル数で、今回の場合は9,9は9x9ピクセルの計81ピクセル

ret, averaged_and_threshold_img = cv2.threshold(averaged_img, threshold, 255, cv2.THRESH_BINARY) #オブジェクトaveraged_imgを閾値threshold(127)で二値化しaveraged_and_threshold_imgに代入
cv2.imwrite("stone_averaged_and_threshold.png", averaged_and_threshold_img) #"stone_averaged_and_threshold.jpeg"として平均化し二値化された画像を保存

実際に画像を平均化してノイズを除去してから二値化する処理コードを実行してみよう

上で記述したコードはaveraged_and_threshold_image.ipynbという名前のjupyter notebookファイルとして、Desktop/Python/LabCode/averaged_and_threshold_imageに保存していきます。

実行結果

先程のカラー画像(stone.jpeg)を今回は、画像を平均化してノイズを除去してから二値化する処理を実行していこうと思います。

stone.jpeg

stone.jpegaveraged_and_threshold_image.ipynbと同じ階層(以下の画像だと、Pythonフォルダの内)に保存しておきます。

上記のコードを実行すると、Desktop/LabCode/Pythonのフォルダ内にstone_averaged_and_threshold.jpegが出来ていると思います。

コード実行結果

stone_averaged_and_threshold.jpeg

平均化でノイズが除かれ、きれいに二値化されていますね!

コードの解説

import cv2

OpenCVを呼び出しています。Pythonでは、ライブラリ名を最初に記述することで使用可能になります。

img = "stone.jpeg" 
threshold = 100 

平均化によるノイズ除去してから二値化したい画像ファイル(stone.jpeg)をオブジェクトimgに代入しています。
閾値として使いたい値(100)をオブジェクトthresholdに代入しています。

grayscale_img = cv2.imread(img, cv2.IMREAD_GRAYSCALE) 

cv2.imreadによってOpenCVのimreadメソッドを呼び出しています。imreadメソッドは画像を読み出すメソッドで、画像を読み出す際に特定の処理を加えることが出来ます。今回はカラー画像を白黒画像にしています。
cv2.imread は第一引数に指定した画像を第二引数で指定した処理を行います。ここでは、第一引数に指定した画像はimgで、第二引数で指定した処理はcv2.IMREAD_GRAYSCALEのことで、画像を白黒で読み込むOpenCVの機能となります。読みだした画像をオブジェクトgrayscale_imgに代入します。

averaged_img = cv2.blur(grayscale_img,(9,9))

cv2.blurが画像を平均化するためのOpenCVのメソッドとなります。第一引数に指定した画像を第二引数で指定した処理を行います。第二引数の(9,9)の意味ですが、これは各ピクセルに対してそのピクセルを中心にした9×9のピクセルの平均値を取得しています。そのため、(2,2)(5,5)でも平均化は可能で、取得した平均ピクセル値を指定範囲のピクセルに適用します。数字(指定するピクセル範囲)が大きくなるにつれて平均化によるぼかしは強くなり、ノイズが除かれます。

(2,2)
(5,5)
(9,9)

平均化は、この処理を全ピクセルに対して行います。 今回の場合は、grayscale_img(9,9)で平均化をかけています。

ret, averaged_and_threshold_img = cv2.threshold(averaged_img, threshold, 255, cv2.THRESH_BINARY) 
cv2.imwrite("stone_averaged_and_threshold.png", averaged_and_threshold_img) 

cv2.thresholdによってOpenCVのthresholdメソッドを呼び出しています。今回のthresholdメソッドの引数の解説は以下になります。

  • averaged_img: 二値化したい画像
  • threshold : 設定したい閾値。今回で言うと100になっている。数字で直接指定することも可能*
  • 255: Maxのピクセル値
  • cv2.THRESH_BINARY: thresholdTypeで2値化の方法の1つ

*各ピクセルを100という閾値でふるいにかけています。閾値より高い値は白(=255)になり、低い数値は黒(=0)になります。ピクセルや、画像データに関する基礎知識は、【Python】画像データを数値データとして扱うには?にて解説しておりますので、こちらもぜひご参照ください。

二値化処理した画像を、オブジェクトaveraged_and_threshold_imgに入れています。

retは説明すると難しいですが、簡単に言うとメソッドで作成した閾値の設定が入っていると思ってください。retが閾値の設定を受け取って、averaged_and_threshold_imgで画像データを受け取っています。なぜこのように分けて受け取る必要があるのかを説明すると長くなりますので、今回の記事での解説は割愛させていただきます。詳しく知りたい方は、こちらの記事にかかれている内容を参考にされてください。

画像のしきい値処理

cv2.imwrite("stone_averaged_and_threshold.png", averaged_and_threshold_img) 

cv2.imwriteはOpenCVの画像を書き出す機能となっております。第二引数で指定した画像を、第一引数の画像名で保存します。

ただ二値化するよりも平均化によるノイズの除去を実行してからのほうが、きれいに二値化ができます。ノイズがあると画像解析の結果がきれいに出てこないことがあるので、そういう場合は、平均化によるノイズの除去を検討してみましょう。