Python 備忘録

Python-ver: 3.6.3

Pythonで類似度検出③ 特徴点マッチング

久しぶりの更新です。
最近仕事が忙しいからかなかなか時間が取れません。


余談ですが、自分は月に一冊ぐらいのペースで技術書を買って読むのですが、「達人プログラマー
という本が非常に面白かったです。プログラマなら読んで損はない本でした。

新装版 達人プログラマー 職人から名匠への道

新装版 達人プログラマー 職人から名匠への道

今回は特徴点を使って類似度を検出したいと思います。

画像ファイル

今回、テストに使うのはこちらの画像です。
f:id:sh0122:20180204162245j:plain:h300:w400
アインシュタイン einstein.jpg
f:id:sh0122:20180204162249j:plain:h300:w400
女性 girl.jpg
f:id:sh0122:20180204162252j:plain:h300:w400
男性 man.jpg
f:id:sh0122:20180204162255j:plain:h300:w400
紳士 man2.jpg
f:id:sh0122:20180204162256j:plain:h300:w400
おばあさん old_woman.jpg

アインシュタインを比較画像にして、アインシュタインに似ている画像をプログラムで見つけます。
この中では年齢と外見からおばあさんの画像が一番近いかと思います。

ソース

# -*- coding: UTF-8 -*-

"""
	pattern_matching.py

	-version: 3.6.3
	-library: opencv
"""

import cv2
import os

def main():
	TARGET_FILE = 'einstein.jpg'
	IMG_DIR = '../images/'

	bf = cv2.BFMatcher(cv2.NORM_HAMMING)
	# 特徴点算出のアルゴリズムを決定(コメントアウトで調整して切り替え)
	# detector = cv2.ORB_create()
	detector = cv2.AKAZE_create()
	(target_kp, target_des) = calc_kp_and_des(IMG_DIR + TARGET_FILE, detector)

	print('TARGET_FILE: %s' % (TARGET_FILE))

	files = os.listdir(IMG_DIR)
	for file in files:
	    if file == '.DS_Store' or file == TARGET_FILE:
	        continue

	    comparing_img_path = IMG_DIR + file
	    try:
	        (comparing_kp, comparing_des) = calc_kp_and_des(comparing_img_path, detector)
			#画像同士をマッチング
	        matches = bf.match(target_des, comparing_des)
	        dist = [m.distance for m in matches]
			#類似度を計算する
	        ret = sum(dist) / len(dist)
	    except cv2.error:
	        ret = 100000

	    print(file, ret)

def calc_kp_and_des(img_path, detector):
	"""
		特徴点と識別子を計算する
		:param str img_path: イメージのディレクトリパス
		:param detector: 算出の際のアルゴリズム
		:return: keypoints
		:return: descriptor
	"""
	IMG_SIZE = (200, 200)
	img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
	img = cv2.resize(img, IMG_SIZE)
	return detector.detectAndCompute(img, None)

if __name__ == '__main__':
	main()

ソースはほぼまんまこちらを参考にさせていただきました。ありがとうございます。
Python + OpenCVで画像の類似度を求める - Qiita

今回のエラー

コードを試しているときに以下のエラーが出ました。

error: (-215) ssize.width > 0 && ssize.height > 0 in function cv::resize

これはどうやらイメージファイルのパスの指定が間違っていたため、読み込みがうまくできなかったエラーでした。
パスを修正してなんとか回避。

結果

出力は以下のようになりました。
AKAZEの場合

TARGET_FILE: einstein.jpg
girl.jpg 137.53623188405797
man.jpg 132.2753623188406
man2.jpg 154.91304347826087
old_woman.jpg 126.91304347826087

ORBの場合

TARGET_FILE: einstein.jpg
girl.jpg 71.87186629526462
man.jpg 70.72980501392757
man2.jpg 75.0
old_woman.jpg 70.76044568245125

ここで注意したいのは数字が低い方が類似度が高いということになります。
AKAZEの場合はうまいこといっているようです。
ORBの場合は、若干男性のほうが似ているというように出ています。
AKAZEのほうが精度がよいのでしょうか。

特徴点を結びつけたものがこちら
f:id:sh0122:20180204171201p:plain

表示させるコードはこちらです。表示させるだけなので大分雑です。
使う場合はリファクタリングしてください。

def show_imgs_match():
	img1 = cv2.imread('../images2/einstein.jpg')
	img2 = cv2.imread('../images2/man3.jpg')
	gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
	gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
	akaze = cv2.ORB_create()
	kp1, des1 = akaze.detectAndCompute(gray1, None)
	kp2, des2 = akaze.detectAndCompute(gray2, None)

	bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
	matches = bf.match(des1, des2)
	matches = sorted(matches, key = lambda x:x.distance)
	img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2)
	plt.imshow(cv2.cvtColor(img3, cv2.COLOR_BGR2RGB))
	plt.show()

Pythonで類似度検出② ヒストグラム比較

はじめに

類似度検出の第一歩としてヒストグラム検出から見ていきたいと思います。
いずれはディープラーニングなんかも駆使して認識とか類似度検出ができるといいのですが。

今回、基礎に使う画像はこちら。適当に選んだ東京の夜景です。
f:id:sh0122:20171210170947j:plain

こちらと似た画像をヒストグラム比較で比べてみたいと思います。

ヒストグラム比較

まず、こちらの画像から比較してみます。
アメリカのサンフランシスコです。
f:id:sh0122:20171210171550j:plain

まずは、わかりやすいようにヒストグラムを見比べてみます。
ソースは以下の通り。

import cv2
import matplotlib.pyplot as plt

target_img_path = 'ファイルのパス'
img = cv2.imread(target_img_path)
plt.hist(img.ravel(),256,[0,256])
plt.show()

こちらが東京の夜景のヒストグラム
f:id:sh0122:20171210172706p:plain

こちらがサンフランシスコの夜景のヒストグラム
f:id:sh0122:20171210172911p:plain

いずれも左側が突き出た形になっているのがわかります。
こちらを比較してみます。

ソース

ヒストグラムを比較するだけなのでプログラムは大分雑です。

# -*- coding: UTF-8 -*-

import cv2
import matplotlib.pyplot as plt

def compare_by_hist():
	TARGET_FILE = 'tokyo.jpg'
	COMPARING_FILE = 'san-francisco.jpg'
	IMG_DIR = '../images/'
	IMG_SIZE = (200, 200)

	#比較するイメージファイルを読み込み、ヒストグラムを計算
	target_img_path = IMG_DIR + TARGET_FILE
	target_img = cv2.imread(target_img_path)
	target_img = cv2.resize(target_img, IMG_SIZE)
	target_hist = cv2.calcHist([target_img], [0], None, [256], [0, 256])

	#比較されるイメージファイルを読み込み、ヒストグラムを計算
	comparing_img_path = IMG_DIR + COMPARING_FILE
	comparing_img = cv2.imread(comparing_img_path)
	comparing_img = cv2.resize(comparing_img, IMG_SIZE)
	comparing_hist = cv2.calcHist([comparing_img], [0], None, [256], [0, 256])

	#ヒストグラムを比較する
	result = cv2.compareHist(target_hist, comparing_hist, 0)
	print(result)

if __name__ == '__main__':
	compare_by_hist()

結果

0.33651142534396467

この数字が類似度ということになります。まったく同じ画像だと1.0になります。
つまり、この画像の類似度は約33%ということになります。
思ったより低かったです。

他の画像と比較

東京の夜景と他の画像を比べてみました。
ダウンタウン
f:id:sh0122:20171210175209j:plain

0.6971260649928982

別の東京の写真
f:id:sh0122:20171210175459j:plain

0.6402893447411667

草原
f:id:sh0122:20171210175735j:plain

0.12629908970839823

雪原
f:id:sh0122:20171210180202j:plain

-0.43809553907956367

ダウンタウンや東京の夜景は60%の数値が出ましたが、まったく関係のない画像は低い数値が出ました。
ある程度は信用できるかと思いますが、まだまだ課題が多そうです。

まとめ

ヒストグラムを比較する手法では、色の割合を比較する。

人物を比較しなかったのは人物だと年齢や場所によって服や肌の色が変わってくるためです。
他の手法と併用して使えばある程度使えるのではないでしょうか。

Pythonで類似度検出① 検出の三つの手法

久しぶりの更新です。
自分でカスケード分類器を作ろうと思い画像収集のクローラーを作っていましたが、
思った以上に厄介なので、ひとまず置いときます。また今度に上げようかと思います。

最近仕事で、顔認証ができないかと類似度を検出する必要性に迫られています。
なので、今回から類似度検出をやっていきたいと思います。

類似度検出とは二つの画像を比べて、どれだけ似ているかを検出します。
最終的には、人物が特定できるような、顔認証まで進めたらいいなと思います。

類似度検出の三つの手法

今回は用語の整理に終わると思います。
画像認識には例のごとくOpenCVを使っていきます。

類似度検出には、主に三つの手法があります。

以上の三つです。ほかにもあるかもしれませんが、門外漢の私には全然わかりません。
一つ一つ確認していきましょう。

ヒストグラム比較

ヒストグラムとは、

ヒストグラム(英: histogram[1])とは、縦軸に度数、横軸に階級をとった統計グラフの一種で、データの分布状況を視覚的に認識するために主に統計学や数学、画像処理等で用いられる。

https://ja.wikipedia.org/wiki/%E3%83%92%E3%82%B9%E3%83%88%E3%82%B0%E3%83%A9%E3%83%A0

つまり、こういうグラフのことです。
f:id:sh0122:20171123170216p:plain:h400
https://ja.wikipedia.org/wiki/%E3%83%92%E3%82%B9%E3%83%88%E3%82%B0%E3%83%A9%E3%83%A0#/media/File:Histogram_of_Japanese_wikipedia_article_%22Histogram%22_page_views.png

ヒストグラム比較とは、まず画像の色がどのように分布しているかのヒストグラムを作成します。
そして、二つの画像のヒストグラムを比較して、同じような分布であれば、二つの画像は「似ている」ということになります。

色で判断するやり方ですね。

特徴点マッチング

特徴点マッチングとは、画像同士の特徴点の距離を測り、近ければ類似度が高いとみなす手法です。

特徴点とは何か。検索してもあまり明快な解答は得られませんでしたが、要は画像のマッチングをする上で重要になる部分、らしい。
人で言えば、輪郭や目鼻立ち、眼の大きさなどなど。人物の類似度を比較する上では一番重要になりそうな手法です。

特徴点を抽出する方法、アルゴリズムは主に二つあり、

  • ORB(Oriented FAST and Rotated BRIEF)
  • AKAZE(Accelerated KAZE)

の二つみたいです。

ORBは、特徴点、特徴量を抽出するアルゴリズムで、移動、回転、ズームのどれにも*ロバストネスがあるアルゴリズムです。
もともとSIFTというアルゴリズムが、移動、回転に加えてズーム対するロバストネスを獲得しました。ただSIFTは計算量が多く、低速だったため、速度を改良したSURFというアルゴリズムがでてきました。ただし、SIFTもSURFも特許で守られているため、使用するためには特許料を払う必要があります。
そこで、移動、回転、ズームの3つに対してロバストネスをもち、計算速度も速く、フリーで使うことができるORBというアルゴリズムが2011年に開発されました。

またOpenCV 3.0からは、2013年に開発されたAKAZEというアルゴリズムがサポートされています。AKAZEは、計算量がORBよりもやや多いものの、低周波領域、高周波領域の抽出精度がORBよりも優れていて、ハイスピードカメラのトラッキングでその威力を発揮するとされています。AKAZEとORBの比較動画はこちら(youtube)。


引用注 ロバストネス:「1 堅牢(けんろう)性。強靭(きょうじん)性。頑強性。ロバストネス。」
https://kotobank.jp/word/%E3%83%AD%E3%83%90%E3%82%B9%E3%83%88%E6%80%A7-1742511#E3.83.87.E3.82.B8.E3.82.BF.E3.83.AB.E5.A4.A7.E8.BE.9E.E6.B3.89

https://qiita.com/hitomatagi/items/62989573a30ec1d8180b

詳しいことはこちらに書いてありますので参考に。文字の強調はこちらで付けさせていただきました。

実際に特徴点を算出した画像がこちら。アルゴリズムはORBです。

画像はこちらから拝借しました。
無料の写真: 男性の顔, 目, Manとシリンダー - Pixabayの無料画像 - 2732206

f:id:sh0122:20171123172820j:plain:h400

要するに二つの画像でこちらのように特徴点を算出し、一つ一つの特徴点を比べあって一番距離が近い(似ている)特徴点同士を線で結び、比較する手法です。

テンプレートマッチング

テンプレートマッチングとは、

画像からテンプレート画像と一致する部分を検出するのがテンプレートマッチングです。テンプレートマッチングは、テンプレート画像を被検出画像上でスライドさせ、テンプレート画像と被検出画像の領域を比較し、類似度の高い領域を検出します。

http://www.tech-tech.xyz/archives/3065942.html#i1

つまり、画像の中から、類似度の高いある部分だけをマッチングさせる手法です。

こちらは、何らかのオブジェクトなどを検出するのに役立ちそうです。
たとえば、ゲームの中のコインの数や、街中の標識、信号機など、決まった形のものを検出するのに絶大な力を発揮してくれるでしょうか。

まとめ

画像の類似度を測るには主に三つの手法がある。

  • ヒストグラム比較
  • 色で比較するやり方。画像検索などに役立つ?
  • 特徴点マッチング
  • 画像の特徴を照らし合わせて比較するやり方。特徴点を算出するのにはORBとAKAZEという二つのアルゴリズムがある。 人物を特定するのに役立つか。
  • テンプレートマッチング
  • 画像のある部分のマッチングをかける方法。ある程度形が決まったものを特定するのに役立つか。

今回は用語の整理に終始してしまいましたが、次回からは手法を一つ一つ試していきます。
最終的には人物特定がしたいです。

Pythonで画像認識② 上半身と下半身認識

本日は画像から上半身と下半身を認識させてみようと思います。

といってもやることは顔認識となんら変わりません。

今回、認識させる画像はこちら。
f:id:sh0122:20171108223627j:plain
画像はこちらから拝借いたしました。
無料の写真: サッカー, 審判員, スポーツ, 一致, ゲーム, アービタ - Pixabayの無料画像 - 1301036

ソース

# -*- coding: utf-8 -*-
import cv2

def body_recognition(file_path):
    """上半身と下半身を検知するメソッド"""
    #画像読み込み グレースケール変換
    image = cv2.imread(file_path)
    image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

    #カスケード分類機読み込み
    upperbody_cascade = cv2.CascadeClassifier(r'../../opencv-3.3.1/data/haarcascades/haarcascade_upperbody.xml')
    lowerbody_cascade = cv2.CascadeClassifier(r'../../opencv-3.3.1/data/haarcascades/haarcascade_lowerbody.xml')

    #カスケード分類機から上半身、下半身それぞれの矩形を取得
    upperbody_rects = upperbody_cascade.detectMultiScale(image_gray, scaleFactor=1.2, minNeighbors=3, minSize=(10, 10))
    lowerbody_rects = lowerbody_cascade.detectMultiScale(image_gray, scaleFactor=1.2, minNeighbors=3, minSize=(10, 10))

    color = (255, 0, 0) #上半身は青
    for rect in upperbody_rects:
        cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)
        cv2.imwrite('../images/detected.jpg', image)

    color = (0, 0, 255) #下半身は赤
    for rect in lowerbody_rects:
        cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)
        cv2.imwrite('../images/detected.jpg', image)

def main():
    file_path = "../images/soccer.jpg"
    body_recognition(file_path)

if __name__ == '__main__':
    main()

解説

画像ファイルは上半身と下半身に関数を分けようかと思いましたが、一緒の関数に書きました。
ファイルも一緒にしました。

理由は、変更を加えようとすると二回離れたところを修正するので、修正し忘れが発生すること。
後は、検知した画像を見る手間が省けることなどです。

顔認識のところでは、haarcascade_frontalface_default.xmlを使いましたが、今回は一緒に同梱されている
haarcascade_upperbody.xmlとhaarcascade_lowerbody.xmlを使いました。

さらに、detectMultiScaleの引数の値を若干変えています。
minNeighborsというのが精度で、これを上げるとより高い認識をしてくれます。
つまり、色々認識された中でよりそれに近いものだけを認識してくれるようになるみたいです。

上半身や下半身がなかなかうまく認識してくれず、色々変えた結果です。

認識後の画像

f:id:sh0122:20171108223940j:plain

青が上半身、赤が下半身です。

認識してなかったり、足首が認識されていたりちょっと微妙ですね。

精度的にはどうかはわかりませんが、自分はあまりできませんでした。

真正面からとったものはあまり認識してくれず、さらに一人の画像だとあまり認識されませんでした。

カスケード分類機は自分でも作れるようなので、自分でも作ってみたいと思います。

Pythonで画像認識① 顔認識

久々の更新です。

本日は画像の顔認識をやっていきたいと思います。

顔認識は知っての通り、画像の中の人間の顔をプログラムで検知すること。
今回の目標は画像の中の人間の顔に四角い枠を出してみることをしたいと思います。
imagesフォルダの中にwoman.jpgというjpgファイルを入れています。こちらのファイル。
f:id:sh0122:20171030185446j:plain:h500

今回はHaar-like特徴分類器という手法を使って顔認識をしたいと思います。
Haar-like特徴分類器とは

「Haar-like特徴を用いたブースティングされた分類器のカスケード」とは、
Haar-likeとよばれる明暗で構成される矩形のパターンを検出に使う手法です。

https://qiita.com/hitomatagi/items/04b1b26c1bc2e8081427

です。そして、haarcascadeはopencvに搭載されているらしいです。

ソースコード

さっそくコード。

# -*- coding: utf-8 -*-

import cv2

#HAAR分類器の顔検出用の特徴量
cascade_path = "/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml"

image_path = "../images/woman.jpg"

color = (255, 255, 255) #白
#color = (0, 0, 0) #黒

#ファイル読み込み
image = cv2.imread(image_path)
#グレースケール変換
image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

#カスケード分類器の特徴量を取得する
cascade = cv2.CascadeClassifier(cascade_path)

#物体認識(顔認識)の実行
facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))

print("face rectangle")
print(facerect)

if len(facerect) > 0:
    #検出した顔を囲む矩形の作成
    for rect in facerect:
        cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)

    #認識結果の保存
    cv2.imwrite("../images/detected.jpg", image)

コードはこちらを参考にさせていただきました。
python+OpenCVで顔認識をやってみる - Qiita

今回のエラー

だが、どうしてもエラーが出る。
まずシンタックスエラー。パスを指定している部分でエラーが出る。
どうも\の部分でencodeができていないらしい。このように直したらできました。

cascade_path = r"/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml"

"の前にrを足しました。こちらを直すとまたエラーが。

cv2.error: C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect.cpp:1698: error: (-215) !empty() in function cv::CascadeClassifier::detectMultiScale

最初の数十文字で検索をかけるとStackOverFlowにあった。
python 2.7 - error: (-215) !empty() in function detectMultiScale - Stack Overflow
どうやら、パスが間違っていて、読み込むファイルが見つからないエラーらしい。

パスが違うのは考えてみれば当たり前のことでした・・・。なんで気づかなかったのだろう。

エクスプローラーでhaarcascadeなどと検索。インストールされていればどこかにxmlファイルがあるはず。
だが、見つかりそうにない・・・。haarcascadeはopenCVに同梱されているらしいが。


しょうがないので、GitHubからhaarcascade_frontalface_default.xmlをダウンロードしてどこかのフォルダに保存。
opencv/data/haarcascades at master · opencv/opencv · GitHub
こちらから拝借しました。

フォルダを保存したパスで、cascade_pathを変えてみるが・・・。今度は別のエラーが。

cv2.error: C:\projects\opencv-python\opencv\modules\core\src\persistence.cpp:448
4: error: (-49) Input file is empty in function cvOpenFileStorage

これがよくわからない。

解決策

色々調べていると、OpenCVの公式サイトがあるらしいので、そこからダウンロードする。
こちら
OpenCV library

  • 上のタブからRELEASESをクリック。
  • 一番上の3.3.1のSourcesをクリック
  • zipファイルがダウンロードされるので、任意の場所に保存
  • 任意の場所に解凍
解凍したファイルのdata\haarcascadesというフォルダの中にhaarcascade_frontalface_default.xmlというファイルがあるので、
それをパスに指定。

すると、できました。
やっぱり公式から持ってくるのが一番でした。

出来上がったコード

試行錯誤して、出来上がったのがこちら。

# -*- coding: utf-8 -*-

import cv2

#HAAR分類器の顔検出用の特徴量
cascade_path = r'C:\projects\opencv-3.3.1\data\haarcascades\haarcascade_frontalface_default.xml'


image_path = "../images/woman.jpg"
color = (255, 255, 255) #白

#ファイル読み込み
image = cv2.imread(image_path)
#グレースケール変換
image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

#カスケード分類器の特徴量を取得する
cascade = cv2.CascadeClassifier(cascade_path)

facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))
print("face rectangle")
print(facerect)

if len(facerect) > 0:
    #検出した顔を囲む矩形の作成
    rect = facerect[1]
    cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)

    #認識結果の保存
    cv2.imwrite("../images/detected.jpg", image)

こちらが認識後の画像。
f:id:sh0122:20171031213510j:plain:h500

Pythonで画像処理② マスキング

閾値

本日は閾値というものを使って、画像のマスキングをしてみたいと思います。

閾値(しきいち)とは

閾値とは、その値を境にして、動作や意味などが変わる値のことである。

http://www.weblio.jp/content/%E9%96%BE%E5%80%A4?edc=BINIT

今回はこの意味の方が正しいでしょう。

そして、マスキングとはある画像のある部分だけ隠すこと。

つまり、閾値という色の範囲を指定してその部分だけ画像を隠そうということです。

閾値を使ったマスキング

それでは実際にプラグラムを書いてみたいと思います。
使うのはまたこの画像

f:id:sh0122:20171015140811j:plain
こちらの画像の空の部分だけマスキングしたいと思います。

プログラムはこちらを参考にさせていただきました。変数など少し変えてます。
画像処理入門講座 : OpenCVとPythonで始める画像処理 | プログラミング | POSTD

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt

blue_min = np.array([140, 0, 0], np.uint8)
blue_max = np.array([255, 255, 255], np.uint8)

img = cv2.imread('../images/field.jpg')
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
plt.imshow(cv2.cvtColor(img_hsv, cv2.COLOR_HSV2RGB))

まずは、最初の部分から。ここからどんどん付け足す形で書いていきます。
blue_minは青色の最少値、blue_maxは青色の最大値で、ここで青色の色の範囲を指定してます。

そのあと、イメージを読んで、表示させています。
ここで画像をHSVに一回変換していますが、HSVとは

HSVとは,Hueが色相,Saturation(Chroma)が彩度,Value(Lightness)が明度である.それぞれHueは[0,179], Saturationは[0,255],Valueは[0,255]の範囲の値をとるが、使用するソフトウェアによっては値の範囲が異なるので,OpenCVで得られた値と別ソフトウェアで得られた値を比較する場合は,値の正規化をしなければならない.

http://lang.sist.chukyo-u.ac.jp/classes/OpenCV/py_tutorials/py_imgproc/py_colorspaces/py_colorspaces.html

のことだそうです。
ここでplt.show()で画像を表示させてみると

f:id:sh0122:20171015142015p:plain

普通に表示されます。

その次

# 青の領域のマスクを取得
mask_blue = cv2.inRange(img, blue_min, blue_max)

# 青の領域のマスクから青じゃない部分を取得
mask = cv2.bitwise_not(mask_blue)
plt.imshow(cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB))

cv2.inRangeで青の部分のマスクを取得しmask_blueにいれています。
そのあとにその青のマスクを利用して、青じゃない部分を取得しています。

この部分をRGBに変換して表示させます。
ここでplt.show()をしてみると。

f:id:sh0122:20171017215912p:plain

こうなります。

黒の部分が表示されない、部分。白の部分が表示されるマスクされた部分。
(余計な部分が少し消えてます。この部分はまた今度やりたいと思います。)

そして、最後

mask_rgb = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB)
# 青じゃない部分の領域をカット
masked_img = cv2.bitwise_and(img, mask_rgb)

# 切り取った部分を白で補完
masked_replace_white = cv2.addWeighted(masked_img, 1, \
                                       cv2.cvtColor(mask_blue, cv2.COLOR_GRAY2RGB), 1, 0)

plt.imshow(cv2.cvtColor(masked_replace_white, cv2.COLOR_BGR2RGB))

実際に元のデータをカットして、空の部分を消しています。
ここで表示させてみると。

f:id:sh0122:20171017220108p:plain

こんな感じ。道の部分が少し消えちゃってますね。
何か変換をかければよいのか。わかりませんが、調べてみようと思います。

プログラム全体

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt

blue_min = np.array([140, 0, 0], np.uint8)
blue_max = np.array([255, 255, 255], np.uint8)

img = cv2.imread('../images/field.jpg')
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
plt.imshow(cv2.cvtColor(img_hsv, cv2.COLOR_HSV2RGB))

# 青の領域のマスクを取得
mask_blue = cv2.inRange(img, blue_min, blue_max)

# 青の領域のマスクから青じゃない部分を取得
mask = cv2.bitwise_not(mask_blue)
plt.imshow(cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB))


mask_rgb = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB)

# 青じゃない部分の領域をカット
masked_img = cv2.bitwise_and(img, mask_rgb)

# 切り取った部分を白で補完
masked_replace_white = cv2.addWeighted(masked_img, 1, \
                                       cv2.cvtColor(mask_blue, cv2.COLOR_GRAY2RGB), 1, 0)

plt.imshow(cv2.cvtColor(masked_replace_white, cv2.COLOR_BGR2RGB))

plt.show()

次は、画像認識に入ってゆきたいと思います。

Pythonで画像処理① 表示とグレースケール変換

OpenCvのインストール

今日はPythonで画像処理のほうに進んでいこうと思います。

その前にOpenCvという画像処理用のライブラリをインストールします。

OpenCvとは・・・

コンピュータで画像や動画を処理するのに必要なモジュールが搭載されたライブラリです。当初はインテル社が開発して、それをWillow Garageに引き継がれた後、Itseezが現在では開発を手掛けているそうです。

ライブラリをインストールしてばかりですが、Pythonにはものすごく多くのライブラリが用意されていますね。
そこが魅力でもあります。

まあインストールは簡単でした。いつもの通り、仮想環境をactivateで利用可能状態にして、

pip install opencv-python

と打ち込むだけ。

そのあと、pythonと打って、インタラクティブシェルを動かします。
インタラクティブシェルでimport cv2と打ってエンターを押して、何もエラーが表示されなければインストール完了です。

画像表示

ここらでただ単に画像を表示してみましょう。
いきなり画像認識に入っても全然わからないので。。。

こちらの画像を表示させてみたいと思います。

f:id:sh0122:20171015140811j:plain

画像はこちらから持ってきました。
無料の写真: フィールド, 離れた, 夏, 空, 雲, 車線, 風景 - Pixabayの無料画像 - 175959

まずは、defaultの直下にimagesフォルダを作ります。その中に名前をfield.jpgとして、画像を保存します。
そして、srcの中でpicture.pyを作成。名前は何でもよいですが。

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('../images/field.jpg')

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

plt.imshow(img)
plt.show()

picture.pyの中身はこんな感じ。これを動かしてみると、

f:id:sh0122:20171015142015p:plain
こんな感じのウィンドウが出てくる。

画像パスはsrcフォルダ、つまり、ソースコードがある場所を指しているようです。

ポイントはimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)です。
img = cv2.imread('../images/field.jpg')で画像を読み込んだ段階では、imgにはnumpy配列でRGB値が格納されています。

ためしに、イメージを読み込んだ後にprint(img)としてみると、

[[[174 153 138]
[171 150 135]
[167 148 133]
...,
[142 99 50]
[144 98 50]
[144 98 50]]

[[176 155 140]
[174 153 138]
[172 153 138]
...,
[143 100 51]
[142 99 50]
[142 99 50]]

[[175 156 141]
[174 155 140]
[175 156 141]
...,
[144 100 53]
[142 99 50]
[141 98 49]]

...,
[[ 8 37 28]
[ 0 20 9]
[ 0 33 19]
...,
[ 21 77 82]
[ 14 72 71]
[ 11 71 65]]

[[ 2 27 17]
[ 0 21 10]
[ 5 35 22]
...,
[ 37 92 97]
[ 40 97 96]
[ 12 70 65]]

[[ 0 16 7]
[ 0 16 6]
[ 7 33 20]
...,
[ 41 93 99]
[ 54 108 108]
[ 47 103 98]]]

こんなのが、出てきます。これがRGB値なのですが、img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)でRGBに変換しているわけですね。
それをplt.imgshow(img) plt.show()で表示させています。

ためしに、img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)の部分をコメントアウトするか、削除するかして実行してみてください。
凄い色合いの画像が表示されるはずです。

グレースケール変換

表示させるだけでは面白くないので、グレースケールに変換してみましょう。
といっても、グレースケール変換した画像をsrcフォルダに落とすだけの簡単なコードです。

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('../images/field.jpg')

img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

cv2.imwrite('gray.jpg', img)

このプログラムを実行するとgray.jpgという画像ファイルがsrcフォルダの中に作られます。
f:id:sh0122:20171015144230j:plain
こんな感じ。

今日はこれぐらいで終わります。
次は、画像の矩形を割り出すなど画像認識の初歩をやっていきたいと思います。