PythonでOpenCVを使ってみよう01(画像の読み込みとハフ変換による円検出)

PythonでOpenCVを使ってみよう01(画像の読み込みとハフ変換による円検出)

Python/OpenCVのキホン的な使い方を覚えよう




画像データの読み込みとハフ変換による円検出

ナノネ、今回はPythonでOpenCVを使ってみよう。

OpenCVって、たしか画像処理とかできるやつだよね。

うん。画像の加工や解析など、いろんなことができるみたい。機械学習で使う画像データの切り出しにも活用できそうなので、今後のためにも使い方を少しずつ覚えていこう。今回はキホン的な画像ファイルの読み込みと円検出をやってみるよ。

ラジャー。

まずは必要なパッケージをインポート。Pythonの実行環境は、今回もGoogle Colabolatoryを使うことにするね。

In [0]:
import cv2
import matplotlib.pyplot as plt
import numpy as np
In [2]:
print(cv2.__version__)
3.4.3
In [0]:
# ********************************
# 動作テスト環境 @2019/06
# ********************************
# Google Colaboratory
# Ubuntu 18.04.2 LTS
# Python 3.6.7
# :: matplotlib 3.0.3
# :: numpy 1.16.3
# :: opencv-python 3.4.5.20
# :: opencv-contrib-python 3.4.3.18

Google Colaboratoryの環境だと、OpenCVはデフォルトでインストール済みだよ。今現在、OpenCVのバージョンは3.4.3で3系だね。少し前にOepneCVの4系バージョンがリリースされたみたいだけど、今回はデフォルトで入っていた3系を使うことにするよ。もし違う環境で動かす場合は、利用環境に応じた手順でOpenCVが使えるように準備してね。

OpenCVで画像ファイルを読み込んでみよう

画像ファイルはGitHubにあるOpenCVのサンプルデータを使ってみよう。

In [4]:
# Opencv-logo サンプル画像準備
import urllib.request as req
url = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/opencv-logo.png'
req.urlretrieve(url, "opencv-logo.png") # ローカル保存
Out[4]:
('opencv-logo.png', <http.client.HTTPMessage at 0x7f51889cca58>)

ときどき見かけるOpenCVのロゴ画像だ。画像ファイルはurllib.requestで読み込んでローカルに保存したのね。

画像ファイルの読み込み方法はいくつもあるけど、OpenCVで画像ファイルを読み込む場合は、imread(画像ファイル名)を使うよ。ファイルパスは画像ファイルの保存場所に応じて適宜指定してね。今回はカレントに画像ファイルを保存したので、ファイル名だけでOK。

OpenCVはcv2という名前でインポートしているので、imreadを使うときはcv2.imreadととしてね(以降のコードでもOpenCVの機能を利用するときは、cv2.〇〇〇で)

それから、読み込んだ画像データを画面に表示する方法もいくつかあるんだけど、今回はMatplotlibを使うよ。

In [5]:
logo_bgr = cv2.imread("opencv-logo.png") # 画像ファイル読み込み
plt.imshow(logo_bgr) # Matplotlibで画像を表示
plt.show()

読み込んだ画像データは、Matplotlibのimshowで表示できるんだね。あれ?、色がチョット変かも。赤と青が逆?

そうなんだよ。OpenCVは画像データの色の並びがBGR形式(青、緑、赤の順)、MatplotlibはRGB形式(赤、緑、青の順)に対応しているから、そのまま表示すると色がヘンテコになっちゃうんだ。

色の違いがもっと分かりやすいように、サンプル画像のLenaさんでも試してみよう。

In [6]:
# lena-san サンプル画像準備
import urllib.request as req # URL指定でファイルをダウンロードするのに利用
url = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/lena.jpg'
req.urlretrieve(url, "lena.jpg") # ローカル保存
Out[6]:
('lena.jpg', <http.client.HTTPMessage at 0x7f5188514898>)

In [7]:
lena_bgr = cv2.imread("lena.jpg") # 画像ファイル読み込み
plt.imshow(lena_bgr)
plt.show()

Matplotlibで画像を表示したら、Lenaさんが真っ青になっちゃた。

色の変換はOpenCVで簡単にできるから大丈夫。色を変換するにはcvtColor(画像データ, 色の変換方法)を使えばOK。BGR形式をRGB形式に変換する場合は、色の変換方法にCOLOR_BGR2RGBを指定してね。

In [8]:
# BGR(opencv default)
fig = plt.figure()
fig.add_subplot(121)
plt.imshow(lena_bgr)
plt.title('BGR')

# RGB(COLOR_BGR2RGB)
lena_rgb = cv2.cvtColor(lena_bgr, cv2.COLOR_BGR2RGB) # BGR -> RGB変換
fig.add_subplot(122)
plt.imshow(lena_rgb)
plt.title('RGB')

plt.show()

RGB形式に変換すれば、オリジナルと同じ色になるんだね。最初のロゴ画像でも同じようにやってみる。

In [9]:
# BGR(opencv default)
fig = plt.figure()
fig.add_subplot(121)
plt.imshow(logo_bgr)
plt.title('BGR')

# RGB(COLOR_BGR2RGB)
logo_rgb = cv2.cvtColor(logo_bgr, cv2.COLOR_BGR2RGB) # BGR -> RGB変換
fig.add_subplot(122)
plt.imshow(logo_rgb)
plt.title('RGB')

plt.show()

うまくできたみたい。

色の変換ついでに、グレースケールに変換する方法も覚えておこう。グレースケールに変換する場合は、cvtColorの引数にCOLOR_BGR2GRAYを指定すればOK。

In [10]:
# BGR(opencv default)
fig = plt.figure()
fig.add_subplot(121)
plt.imshow(logo_bgr)
plt.title('BGR')

# GRAY(COLOR_BGR2GRAY)
logo_gray = cv2.cvtColor(logo_bgr, cv2.COLOR_BGR2GRAY) # BGR -> GRAY変換
fig.add_subplot(122)
plt.imshow(logo_gray)
plt.title('GRAY')

plt.show()

グレー表示にならないけど、変換に失敗した?背景が黄色とかまたヘンテコな色になってる。

---

画像データ自体はうまくグレースケールに変換されているんだけど。画像データのshapeを確認してもカラー3chだったのが、シングルになっているでしょ。

In [11]:
print('logo_bgr shape ',  logo_bgr.shape) # 3ch color 739, 600, 3
print('logo_gray shape ', logo_gray.shape) # gray scale 739, 600
logo_bgr shape  (739, 600, 3)
logo_gray shape  (739, 600)

グレースケール画像をMatplotlibで表示したとき、デフォルトでは明るさの階調に応じて自動で色付けがされちゃうみたい。もしグレー表示したいときは、imshowの引数でcmap='gray'を指定すればOKだよ。

In [12]:
# BGR(opencv default)
fig = plt.figure()
fig.add_subplot(121)
plt.imshow(logo_bgr)
plt.title('BGR')

# GRAY(COLOR_BGR2GRAY)
logo_gray = cv2.cvtColor(logo_bgr, cv2.COLOR_BGR2GRAY) # BGR -> GRAY変換
fig.add_subplot(122)
plt.imshow(logo_gray, cmap='gray') # グレー表示
plt.title('GRAY')

plt.show()

グレーの画像でうまく表示できた。

ちなみにカラー画像をグレースケールとして直接読み込みたい場合は、imreadの第2引数に0を指定するといいよ。こんな感じで。

In [13]:
# グレースケールで画像読み込み
logo_gray = cv2.imread('opencv-logo.png', 0) # グレースケールで読み込み
# logo_gray = cv2.imread('opencv-logo.png', cv2.IMREAD_GRAYSCALE) # 定数名でも指定可能

# imreadの第2引数
# 1 ; cv2.IMREAD_COLOR : カラー画像として読み込む、画像の透明度は無視される、デフォルト値
# 0 ; cv2.IMREAD_GRAYSCALE : グレースケール画像として読み込む
# -1; cv2.IMREAD_UNCHANGED : アルファチャンネルも含めた画像として読み込む

print(logo_gray.shape)
plt.imshow(logo_gray, cmap='gray')
plt.gray()
plt.show()
(739, 600)

つづいて、色を反転する方法も覚えておこう。bitwise_not(画像データ)で変換できるよ。こんな感じで。

In [14]:
logo_gray = cv2.imread('opencv-logo.png', 0)# グレースケールで読み込み
# 色反転
logo_invgray = cv2.bitwise_not(logo_gray) # 色反転
plt.imshow(logo_invgray, cmap='gray')
plt.gray()
plt.show()

色の反転はよく使うから、覚えておくよ。

次は画像をぼかす方法の一例。medianBlur(画像データ、ぼかしサイズ)で、ぼかし処理ができるよ。第2引数は奇数で指定、大きな値は指定すると強いぼかしがかかるよ。

In [15]:
# ぼかし弱
logo_invgray_blur5 = cv2.medianBlur(logo_invgray, 5) # ぼかしサイズ5
plt.imshow(logo_invgray_blur5, cmap='gray')
plt.show()
In [16]:
# ぼかし強
logo_invgray_blur15 = cv2.medianBlur(logo_invgray, 15) # ぼかしサイズ15
plt.imshow(logo_invgray_blur15, cmap='gray')
plt.show()

ぼかし処理の違いがわかるよに、画像を横に並べてみる。

In [17]:
fig = plt.figure(figsize=(12, 4))

# ぼかしなし
fig.add_subplot(131)
plt.imshow(logo_invgray, cmap='gray')
plt.title('no blur')

# ぼかし弱
fig.add_subplot(132)
plt.imshow(logo_invgray_blur5, cmap='gray')
plt.title('blur 5')

# ぼかし弱
fig.add_subplot(133)
plt.imshow(logo_invgray_blur15, cmap='gray')
plt.title('blur 15')

plt.show()

Lenaさんでも、ぼかし処理をやってみる。

In [18]:
lena_bgr = cv2.imread("lena.jpg")
lena_rgb = cv2.cvtColor(lena_bgr, cv2.COLOR_BGR2RGB) # BGR -> RGB変換
lena_rgb_blur5 = cv2.medianBlur(lena_rgb, 5) # ぼかし弱
lena_rgb_blur15 = cv2.medianBlur(lena_rgb, 15) # ぼかし強

fig = plt.figure(figsize=(12, 4))

# ぼかしなし
fig.add_subplot(131)
plt.imshow(lena_rgb)
plt.title('no blur')

# ぼかし弱
fig.add_subplot(132)
plt.imshow(lena_rgb_blur5)
plt.title('blur 5')

# ぼかし弱
fig.add_subplot(133)
plt.imshow(lena_rgb_blur15)
plt.title('blur 15')

plt.show()

並べて比較すると、処理効果の違いがわかりやすいね。

OpenCVでハフ変換による円検出をやってみよう

最後はチュートリアルを参考にして、HoughCirclesを使った円の検出をやってみよう。

HoughCirclesの使い方としては、グレースケールに変換した画像と円の検出条件をパラメータとして引数に渡すよ。すると検出された円情報がリスト形式で取得できるんだ。リストにはX座標、Y座標、半径が格納されているので、その情報を元にOpenCVの描画メソッドで、お好みのマーキング描画することができるよ。検出された円を線で囲んだり、中心をマーキングしたりできるね。

HoughCirclesの詳しい使い方はドキュメントにあるので調べてみてね。

In [19]:
# 円検出のテスト用画像
img = cv2.imread("opencv-logo.png", 0) # グレースケールで画像読み込み
img = cv2.medianBlur(img, 5) # ぼかし処理

cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) # 結果出力用の画像(マーカーを色付けするのカラー化)

# HoughCircles(グレースケール画像、円検出条件...を指定)
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20,
                           param1=50, param2=38, minRadius=0, maxRadius=0)

circles = np.uint16(np.around(circles)) # 円検出データを整数型に変換(16ビットの符号なし整数)

# マーキング描画
for i in circles[0,:]:
  cv2.circle(cimg,(i[0],i[1]),i[2],(0, 255, 0), 10) # 緑色で円をマーキング
  cv2.circle(cimg,(i[0],i[1]),2,(255, 0, 0), 10) # 赤色で円の中心を表示

plt.imshow(cimg)
plt.show()

print(circles.shape)
print(circles)
(1, 6, 3)
[[[142 414  54]
  [452 414 136]
  [300 140 143]
  [438 656  52]
  [ 48 646  61]
  [248 672  37]]]

6個の円が検出されたみたい。チュートリアルの結果とちょっと違うけど、使った画像が違うからかなぁ?色を反転してみる。

In [20]:
# 円検出のテスト用画像
img = cv2.imread("opencv-logo.png", 0)
img = cv2.bitwise_not(img) # 色反転
img = cv2.medianBlur(img, 5)

cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20,
                           param1=50, param2=38, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
  cv2.circle(cimg,(i[0],i[1]),i[2],(0, 255, 0), 10)
  cv2.circle(cimg,(i[0],i[1]),2,(255, 0, 0), 10)

plt.imshow(cimg)
plt.show()

print(circles.shape)
print(circles)
(1, 6, 3)
[[[142 414  54]
  [452 414 136]
  [300 140 143]
  [438 656  52]
  [ 48 646  61]
  [248 672  37]]]

う~ん...。別のサンプルロゴ画像でも試してみる。

In [21]:
# Opencv-logo-white サンプル画像準備(白文字で背景透過png)
import urllib.request as req
url = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/opencv-logo-white.png'
req.urlretrieve(url, "opencv-logo-white.png") # ローカル保存
Out[21]:
('opencv-logo-white.png', <http.client.HTTPMessage at 0x7f51838fcdd8>)

見た目は文字(OpenCV)が見えないけど、透過PNGで白文字がある画像だった。

In [22]:
# 円検出のテスト用画像
img = cv2.imread("opencv-logo-white.png", 0)
img = cv2.medianBlur(img, 5)

cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20,
                           param1=50, param2=28, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
  cv2.circle(cimg,(i[0],i[1]),i[2],(0, 255, 0), 2)
  cv2.circle(cimg,(i[0],i[1]),2,(255, 0, 0), 3)

plt.imshow(cimg)
plt.show()

print(circles.shape)
print(circles)
(1, 5, 3)
[[[136 124  41]
  [ 42 126  41]
  [ 86  40  41]
  [ 16 196  16]
  [130 194  18]]]

微妙に違うけど、チュートリアルと似たような円の検出ができたみたい。

HoughCirclesの引数パラメータを調整すれば、円の検出感度を変更できるのでいろいろ試してみてね(思い通りの検出感度に調整するのが結構難しいけど)。

今回のOpenCV練習は以上で。今後いろんな画像処理や物体検出なども試していこう。

お疲れ様でした。またね!



スポンサーリンク

0 件のコメント :

コメントを投稿