月の写真から欠けの程度を定量化する
満月や三日月など満ち欠けの異なる月の写真をopencvで輪郭検出し明るい部分の面積を求めるとともに、外接円の面積を求め、その比から月のかけ具合を定量化します。
やること
- 月の写真を読み出し二値化、領域検出し月の面積を求める
- 検出した領域に対する外接円に面積を求める
- 月の面積、外接円の面積、それらの比を表示する
- 二値化画像と領域・外接円を重ね書きしたカラー画像を縦に連結しサイズを半分にして表示する
使った関数
- cv2.imread : 画像ファイルの読み出し
- cv2.threshold : 画像の二値化
- cv2.findContours : 領域の検出
- cv2.drawContours : 領域の描画
- cv2.minEnclosingCircle : 領域に対して外接円を求める
- cv2.circle : 円を描画
- cv2.cvtColor : 色空間の変換
- cv2.vconcat : 画像を縦に連結
- cv2.resize : 画像のサイズ変更
- cv2.imshow : 画像の表示
環境
準備
無料の写真素材・Pexelsから月の写真を入手しjupyter notebookファイル(***.ipynb)と同じディレクトリに保存しました。
moon_1.jpg
moon_2.jpg
moon_3.jpg
コード
※cv2.findContoursの戻り値はこのブログで使っているOpenCV 4では2つですが、前バージョンOpenCV 3では3つのため書式が異なります
import cv2 #OpenCVのインポート fname="moon_3.jpg" #開く画像ファイル名 threshold=45 #二値化閾値 img_color= cv2.imread(fname) #画像を読み出しオブジェクトimg_colorに代入 img_gray = cv2.imread(fname,cv2.IMREAD_GRAYSCALE) #画像をグレースケールで読み出しオブジェクトimg_grayに代入 img_blur = cv2.blur(img_gray,(11,11)) #img_grayを平均化領域11x11で平均化処理しimg_blurに代入 ret, img_binary= cv2.threshold(img_blur, threshold, 255, cv2.THRESH_BINARY) #オブジェクトimg_blurを閾値threshold(127)で二値化しimg_binaryに代入 contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #img_binaryを領域抽出 cnt=contours[0] #一つ目の領域をcntに代入 cv2.drawContours(img_color, cnt, -1, (0,255,0), 2) #領域cntを緑色でimg_colorに重ね書き cnt_area = cv2.contourArea(cnt) #領域cntの面積をcnt_areaに代入 (x,y),radius = cv2.minEnclosingCircle(cnt) #領域cntに対して外接円を求め中心座標x,yと半径radiusを取得 center = (int(x),int(y)) #外接円を描画するために中心座標x,yを整数変換し中心座標centerとする radius = int(radius) #外接円を描画するためにradiusを整数変換し半径とする img_color = cv2.circle(img_color,center,radius,(0,0,255),2) #img_color上に外接円を赤で描画する circle_area =radius*radius*3.14 #外接円面積を求めcircle_areaに代入 area_ratio=cnt_area/circle_area #cnt_area/circle_areをarea_ratioに代入 img_binary_bgr = cv2.cvtColor(img_binary, cv2.COLOR_GRAY2BGR) #二値化画像img_binaryをカラー画像(BGR)に変換しimg_binary_bgrとする img_result=cv2.vconcat([img_binary_bgr,img_color]) #img_binary_bgrとimg_colorを縦に連結しimg_resultとする height = img_result.shape[0] #img_resultの高さをheightに width = img_result.shape[1] #img_resultの幅をwidthに img_result_resize = cv2.resize(img_result , (int(width*0.5), int(height*0.5))) #img_resultの高さ幅を半分にしてimg_result_resizeとする print('月の面積 : ',cnt_area) #cnt_areaの表示 print('外接円の面積 : ',circle_area) #circle_areaの表示 print('月の面積/外接円の面積 : ',area_ratio) #cnt_area/circle_areaの表示 cv2.imshow("result",img_result_resize) #img_result_resizeの表示 cv2.waitKey(0) #キー入力待ち cv2.destroyAllWindows() #ウインドウを閉じる
実行結果
画像ファイル'moon_1.jpg'の場合
月の面積 : 39340.5
外接円の面積 : 40094.66
月の面積/外接円の面積 : 0.9811905126518095
画像ファイル'moon_2.jpg'の場合
月の面積 : 16615.0
外接円の面積 : 45972.740000000005
月の面積/外接円の面積 : 0.361409826779957
画像ファイル'moon_3.jpg'の場合
月の面積 : 32727.0
外接円の面積 : 45216.0
月の面積/外接円の面積 : 0.7237924628450106
以下のサイトを参考にさせていただきました
OpenCV-Python Tutorials 1 documentation >> 領域(輪郭)の特徴
Pynote >> 領域(輪郭)の特徴