Pythonによるデータ分析・機械学習ブログ

Pythonによるデータの前処理・グラフ化、機械学習、統計解析、画像処理、Webスクレイピング、自然言語処理の基礎について紹介していきます。

MENU

【第13回】OpenCVを用いた画像処理による物体の輪郭描画:サーロインの輪郭描画

はじめに

OpenCVとはインテルが開発・公開したオープンソースのコンピュータビジョン向けライブラリで、画像処理・画像解析および機械学習等の機能を持つC/C++JavaPythonMATLAB用ライブラリとなっています。

OpenCVには以下のような機能があります。

  • グレースケール化
  • 2値化
  • 輪郭の検出
  • 顔検出
  • ノイズ除去
  • テンプレートマッチング

画像内での物体の位置を把握する方法の一つに輪郭検出があります。OpenCVを用いて輪郭検出を行うには findContours() およびcv2.polylines()などが使用されます。

本記事では、OpenCVのcv2.threshold()を用いて二値化した画像からcv2.findContours() およびcv2.polylines()を用いて輪郭を描画する方法を紹介します。

SAMURAI TERAKOYA

 

1. 本記事で使用する画像

本記事で使用する画像はお肉のフリー素材サイトoniku imagesの画像から以下のサーロインの画像を使用させていただきます。



2. 物体の輪郭検出と描画

それでは、上記のサーロインの画像を用いて以下の手順で輪郭検出していきます。

  1. HSV変換して明度の濃淡画像を取得
  2. 明度の濃淡画像を二値化処理
  3. 二値化画像から輪郭の描画

2.1 HSV変換と明度の濃淡画像の取得(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))

HSV色空間は、H:色相(Hue)、S:彩度(Saturation)、V:明度(Value)を指しています。明度の濃淡画像を取得するには各成分の画像に分離します。HSV色空間への変換はcv2.cvtColor(img, cv2.COLOR_BGR2HSV)を用い、各成分の分離にはcv2.split()を用います。HSV変換についてはこちらの記事をご覧ください。

HSV変換後に、明度の濃淡画像を取得するコードは以下の様になります。

出力結果は以下の様になります。明度の濃淡画像は白っぽい部分と黒っぽい部分がはっきり分離しているのがわかると思います。

HSV変換後の画像】

【色相の濃淡画像】

【彩度の濃淡画像】

【明度の濃淡画像】

 

2.2 二値化処理(cv2.threshold())

上記の明度の濃淡画像は白っぽい部分と黒っぽい部分がはっきり分離しているのがわかると思います。こちらの明度の濃淡画像を用いて二値化処理をしていきます、二値化処理には閾値処理のcv2.threshold()を用います。用いる際には以下の様になります。

img = cv2.threshold(img, num1, num2, thresholdtype)

引数は以下の様になります。

引数 内容
img グレースケールの画像
num1 画素値の閾値
num2 閾値条件を満たした際の最大値
thresholdtype 閾値処理の種類

 

閾値処理の種類は以下の様になります。ここで画像がグレースケールで、画素値が0の場合は黒色、255の場合は白色になります。

閾値処理の種類 処理内容
cv2.THRESH_BINARY 画素値が閾値(num1)以上の場合は最大値(num2)に、閾値以下の場合は0に変更
cv2.THRESH_BINARY_INV 画素値が閾値(num1)以上の場合は0に、閾値以下の場合は最大値(num2)に変更
cv2.THRESH_TRUNC 画素値が閾値(num1)以上の場合は最大値(num2)に変更し、閾値以下の場合は元の画素値のまま
cv2.THRESH_TOZERO 画素値が閾値(num1)以上の場合は元の画素値のままで、閾値以下の場合は0に変更。※ここで最大値num2は意味がありません。
cv2.THRESH_TOZERO_INV 画素値が閾値(num1)以上の場合は0に変更し、閾値以下の場合は元の画素値のまま。※ここで最大値num2は意味がありません。

 

それでは、二値化処理をしたいと思います。以下のコードでは明度の濃淡画像(img_v.jpg)を用いて、閾値150以上の画素値であれば画素値を255にし、閾値以下であれば画素値を0にする二値化処理をしています。

出力結果は以下の様になります。肉の部分だけ白色(画素値255)、それ以外の部分は黒色(画素値0)の画像が得られました。

 

2.3 輪郭に外接する長方形の描画(cv2.boundingRect()、cv2.rectangle())

 輪郭描画の前に、輪郭に外接する長方形の描画をしてみたいと思います。まずはcv2.findContours()を用いて二値化画像の白の領域を囲む輪郭を取得します。しかし、この段階では、白い部分は小さいものもいくつかあることから誤検出をしてしまいます。そこで、面積を一定数値以上のものだけにして、肉部分の輪郭情報を取得します。さらに、cv2.boundingRect()を用いることで外接矩形の座標情報を取得することができます。その外接矩形の情報をcv2.rectangle()の引数にして長方形の描画を行います。

 

 二値化画像の白の領域を囲む輪郭を取得するのに用いるcv2.findContours()の内容は以下のようになります。戻り値は検出された輪郭の座標情報contoursと輪郭の階層構造を表すhierarchyです。

contours, hierarchy = cv2.findContours(image, mode, method)

引数は以下の様になります。

引数 内容
image 二値化画像画像
mode 輪郭取得モード
method 輪郭近似法

 

輪郭取得モードと輪郭近似法についてはこちらの記事で詳しく書かれていたので、この記事を参照してください。

【OpenCV-Python】findContoursによる輪郭検出 | イメージングソリューション

 

 外接矩形を描画するのに必要な座標情報を取得するには、cv2.boundingRectを用います。

x, y, width, height = cv2.boundingRect(array) 

引数に輪郭位置情報arrayを渡し、戻り値として外接矩形の左上の位を(x, y),横と縦のサイズを(width, height)として取得できます。

 

 外接矩形を描画するには、長方形を描画するcv2.rectangle()を用います。cv2.rectangle()についてはこちらの記事をご覧ください。

 

 それでは外接矩形を描画していきたいと思います。以下が外接矩形を描画するコードになっています。

出力結果は以下の様になります。


2.4 輪郭の描画(cv2.drawContours()、cv2.polylines())

それでは本記事の目的である物体の輪郭描画を行いたいと思います。 輪郭描画にはcv2.drawContours()を用います。cv2.drawContours()は以下の様に用います。戻り値dstは輪郭が描画された画像です。

dst = cv2.rawContours(image, contours, contourIdx, color)

引数は以下の様になります。

引数 内容
image 輪郭を描画したい画像
contours 輪郭の位置情報
contourIdx 描画する輪郭を示すパラメータ。負の場合すべての輪郭を描画
color 線の色

 

それでは輪郭を描画したいと思います。まずはcv2.findContours()を用いて二値化画像の白の領域を囲む輪郭を取得します。しかし、この段階では、白い部分は小さいものもいくつかあることから誤検出をしてしまいます。そこで、面積を一定数値以上のものだけにして、肉部分の輪郭情報を取得します。そして、cv2.drawContours()を用いて輪郭を描画します。

出力結果は以下の様になります。きれいに輪郭に線を描画することができていますね。

 

上記とは別の方法で輪郭を描画することができます。それは多角形の描画手法を用いることです。多角形の描画については以下の記事をご覧ください。

chantastu.hatenablog.com

 

多角形の描画にはcv2.polylines()を用います。詳しくは上記記事をご覧ください。

それではcv2.polylines()を用いて輪郭の描画を行います。頂点の位置情報のデータ構造を変換しなければならないので、面倒ですね・・・。

出力結果は以下の様になります。

 

2.4 複数画像での動作確認

上記で示した内容を他の画像でも画像処理を行い、輪郭描画が正常に動作するか確認します。使用する画像はお肉のフリー素材サイトoniku imagesの画像から以下のサーロインの画像を使用させていただきます。

画像処理対象の画像は以下の画像です。複数のお肉の画像を結合しました。

 

それでは上記の画像に対して輪郭描画を行います。今回は肉の面積が減少したのでそれに合わせてcv2.contourArea()で用いる閾値を1000に修正しています。それ以外は上記のコードと一緒です。

出力結果は以下です。他の画像でも輪郭描画ができているのがわかりますね。右上の斜めから撮影された画像では、上面での輪郭検出になっています。影になっている部分は検出できていないですね・・・。

 

オススメ書籍

Pythonで始めるOpenCV4プログラミング

実践OpenCV4 for Python 画像映像情報処理と機械学習

 

スキル修得&転職

 近年、DXの進展に伴うデジタル人材の需要の高まりに追いついていない状況が続いていると経済産業省がホームページで記載している通り、DX人材、IT人材が不足しているのが現状です。さらにコンピュータの性能向上やAI技術の発展により、よりDX人材、IT人材の需要が高まってきます。さらには、今後の長期間安定して職がある業種とも考えられます。

以下の求人では、IT業界の転職や、IT未経験だけどIT人材を志望する就活を支援してくれます。無料で会員登録もできるので、まずは登録だけでもして様子を見てみてはいかがでしょうか?

▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼

▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲

 

おわりに

 本記事では、OpenCVのcv2.threshold()を用いて二値化した画像からcv2.findContours() およびcv2.polylines()を用いて輪郭を描画する方法を紹介しました。具体的にはcv2.cvtColor(img, cv2.COLOR_BGR2HSV)で画像をHSV変換して明度の濃淡画像を取得し、cv2.threshold()を用いて明度の濃淡画像を二値化後、v2.findContours() およびcv2.polylines()を用いて輪郭を描画しました。本記事では肉の画像でしたが、他の物体の場合には本記事の閾値などの調整や他の手法を用いて輪郭描画をすることになるかと思います。本記事の内容は一例だと思って、参考にしてください。