opencv で マーカー付き用紙の向きを直してみる
マーカーの検出を利用して、向きを直してみます。
カメラの歪み補正等(キャリブレーション)には踏み込みません。
四隅に黒い円のマーカーを付けた用紙です。
この傾きを補正しようと思います。
流れは
マーカー検出
座標の並び替え
射影変換
#!/usr/bin/env python # -*- coding: utf-8 -* import cv2 import numpy as np """ 円検出と、射影変換 """ def main(): image = cv2.imread('DSC_3009.jpg') #cv2.imshow('original', image) height, width ,depth = image.shape # リサイズ image = cv2.resize(image, (width/4, height/4)) imageOrg = image.copy() height, width ,depth = image.shape # グレースケールに変換 image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # ガウシアンフィルタ image = cv2.GaussianBlur(image, (5, 5), 0) # 二値変換 ret, image = cv2.threshold(image,127,255,cv2.THRESH_BINARY) # Python: cv2.findCirclesGridDefault(image, patternSize[, centers[, flags]]) → retval, centers # CALIB_CB_SYMMETRIC_GRID uses symmetric pattern of circles. # CALIB_CB_CLUSTERING uses a special algorithm for grid detection. It is more robust to perspective distortions but much more sensitive to background clutter. retval, centers = cv2.findCirclesGrid(image, (2,2), flags=cv2.CALIB_CB_SYMMETRIC_GRID + cv2.CALIB_CB_CLUSTERING) if retval: print centers image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) image = cv2.drawChessboardCorners(image, (2,2), centers, retval) # 射影変換のために、並び替えた配列を用意する points = np.array([ centers[0], centers[2], centers[3], centers[1] ]) print points # 外接矩形 bRect = cv2.boundingRect(points) print bRect cv2.rectangle(image, (bRect[0], bRect[1]), (bRect[0]+bRect[2], bRect[1]+bRect[3]), (255,0,0), 2) cv2.imshow('drawChessboardCorners', image) cv2.imwrite('90.drawChessboardCorners.png', image) # 射影変換 # 右上、左上、左下、右下 pts1 = np.float32(points) pts2 = np.float32([ [bRect[0]+bRect[2],bRect[1]], [bRect[0],bRect[1]], [bRect[0],bRect[1]+bRect[3]], [bRect[0]+bRect[2],bRect[1]+bRect[3]], ]) print(pts1) print(pts2) M = cv2.getPerspectiveTransform(pts1,pts2) imageOrg = cv2.warpPerspective(imageOrg,M,(width,height)) cv2.imshow('getPerspectiveTransform', imageOrg) cv2.imwrite('90.getPerspectiveTransform.png', imageOrg) cv2.waitKey(0) cv2.destroyAllWindows() return 0 if __name__ == '__main__': try: main() except KeyboardInterrupt: pass
検出された円と、外接矩形
完了
opencv で カードの向きを直してみる
チュートリアルを通じて学んだことを活かして、机の上においたカードを普通に(真上ではない角度から)撮影した画像を、まっすぐに修正してみる。
なお、カードが真っ白だったので、向きが分かるように、適当に文字を上に乗せました。(カードにペンでかけばよかったかな・・・)
試行錯誤の結果ですが、処理の流れは、、、
白を検出(HSVに変換します)
二値化
境界を検出する
境界を近傍する
4つ角の座標を得る
射影変換する
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np from pprint import pprint def main(): image = cv2.imread('DSC_2848.2.jpg') #cv2.imshow('original', image) # リサイズ height, width ,depth = image.shape image = cv2.resize(image, (width/4, height/4)) imageOrg = image cv2.imshow('resize', image) cv2.imwrite('50.resize.png', image) # HSVへ変換 image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 白抽出:凄く明るい場所 threashhold_min = np.array([0,0,180], np.uint8) threashhold_max = np.array([255,255,255], np.uint8) image = cv2.inRange(image, threashhold_min, threashhold_max) # BGRへ変換 # inRange で グレースケールされている image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) cv2.imshow('inRange', image) cv2.imwrite('50.inRange.png', image) # ノイズ除去 kernel = np.ones((9,9), np.uint8) image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel) image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel) cv2.imshow('removeNoise', image) cv2.imwrite('50.removeNoise.png', image) # 反転処理 image = 255 - image # 境界抽出 gray_min = np.array([0], np.uint8) gray_max = np.array([128], np.uint8) threshold_gray = cv2.inRange(image, gray_min, gray_max) image, contours, hierarchy = cv2.findContours(threshold_gray,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # 最大面積を探す max_area_contour=-1 max_area = 0 for contour in contours: area=cv2.contourArea(contour) if(max_area<area): max_area=area max_area_contour = contour #print(max_area_contour) # カラー化 #image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGBA) contours = [max_area_contour] cv2.drawContours(imageOrg, max_area_contour, -1, (0, 255, 0), 5) # 輪郭の近似 epsilon = 0.01 * cv2.arcLength(max_area_contour,True) approx = cv2.approxPolyDP(max_area_contour,epsilon,True) #print(approx) if len(approx) == 4: cv2.drawContours(imageOrg, [approx], -1, (255, 0, 0), 3) cv2.imshow('findContours', imageOrg) cv2.imwrite('50.findContours.png', imageOrg) # ソートが必要かな? #pprint(approx) #approx= np.sort(approx,axis=1) #approx= np.sort(approx,axis=0) #pprint(approx) height, width ,depth = imageOrg.shape # 射影変換 pts1 = np.float32(approx) pts2 = np.float32([[600,200],[300,200],[300,350],[600,350]]) pprint(pts1) pprint(pts2) M = cv2.getPerspectiveTransform(pts1,pts2) imageOrg = cv2.warpPerspective(imageOrg,M,(width,height)) cv2.imshow('getPerspectiveTransform', imageOrg) cv2.imwrite('50.getPerspectiveTransform.png', imageOrg) cv2.waitKey(0) cv2.destroyAllWindows() return 0 if __name__ == '__main__': try: main() except KeyboardInterrupt: pass
リサイズ
白を検出して二値化
ノイズ除去
境界検出
射影変換
cv2.approxPolyDP
で帰ってくる配列ですが、3番目の引数に True を指定したので、閉じたポリゴンを返してくれるはず。
closed – これが真の場合,近似された曲線は閉じたものになり(つまり,最初と最後の頂点が接続されます),そうでない場合は,開いた曲線になります.
ですが、並び順が固定なのか、変動するのか不明。
このプログラムでは、右上 > 左上 > 左下 > 右下 の順(右上スタートの反時計回り)でしたが、毎回そうなのか、どうなのか・・・。
opencv チュートリアルチャンレンジ 82 画像のInpainting
画像のInpainting — OpenCV-Python Tutorials 1 documentation
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np img = cv2.imread('170519-144402.cut.jpg') mask = cv2.imread('170519-144402.mask.jpg',0) # INPAINT_TELEA dst = cv2.inpaint(img, mask, 3, cv2.INPAINT_TELEA) cv2.imshow('inpaint.INPAINT_TELEA',dst) cv2.imwrite('inpaint.INPAINT_TELEA.png',dst) # INPAINT_NS dst = cv2.inpaint(img, mask, 3, cv2.INPAINT_NS) cv2.imshow('inpaint.INPAINT_NS',dst) cv2.imwrite('inpaint.INPAINT_NS.png',dst) cv2.waitKey(0) cv2.destroyAllWindows()
やってみる
マスク
処理結果 INPAINT_TELEA
処理結果 INPAINT_NS
流石に大きすぎたかな?
opencv チュートリアルチャンレンジ 81 画像のノイズ除去
画像のノイズ除去 — OpenCV-Python Tutorials 1 documentation
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np img = cv2.imread('170523-170301.jpg') #img = cv2.imread('170519-174830.jpg') #img = cv2.imread('170519-144402.jpg') #img = cv2.imread('chessboard.jpg') cv2.imshow('goodFeaturesToTrack',img) dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21) cv2.imshow('fastNlMeansDenoisingColored',dst) cv2.imwrite('fastNlMeansDenoisingColored.png',dst) cv2.waitKey(0) cv2.destroyAllWindows()
やってみる
opencv チュートリアルチャンレンジ 43 Shi-Tomasiのコーナー検出とGood Features to Track(追跡に向いた特徴)
Shi-Tomasiのコーナー検出とGood Features to Track(追跡に向いた特徴) — OpenCV-Python Tutorials 1 documentation
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np img = cv2.imread('170523-170301.jpg') #img = cv2.imread('170519-174830.jpg') #img = cv2.imread('170519-144402.jpg') #img = cv2.imread('chessboard.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) corners = cv2.goodFeaturesToTrack(gray, 25, 0.01, 10) corners = np.int0(corners) for i in corners: x,y = i.ravel() cv2.circle(img, (x,y), 3, [0,0,255], -1) cv2.imshow('goodFeaturesToTrack',img) cv2.imwrite('goodFeaturesToTrack.png',img) cv2.waitKey(0) cv2.destroyAllWindows()
opencv チュートリアルチャンレンジ 42 Harrisコーナー検出
Harrisコーナー検出 — OpenCV-Python Tutorials 1 documentation
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np img = cv2.imread('170523-170301.jpg') #img = cv2.imread('170519-174830.jpg') #img = cv2.imread('170519-144402.jpg') #img = cv2.imread('chessboard.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) gray = np.float32(gray) #dst = cv2.cornerHarris(gray, 2, 3, 0.04) dst = cv2.cornerHarris(gray, 4, 5, 0.04) #result is dilated for marking the corners, not important dst = cv2.dilate(dst, None) tmp = np.zeros(dst.shape[:2], np.uint8) tmp[ dst > 0.01*dst.max() ] = [255] tmp[ dst <= 0.01*dst.max() ] = [0] cv2.imshow('cornerHarrisMask',tmp) cv2.imwrite('cornerHarrisMask.png',tmp) # Threshold for an optimal value, it may vary depending on the image. img[ dst > 0.01*dst.max() ] = [0,0,255] cv2.imshow('cornerHarris',img) cv2.imshow('cornerHarris.png',img) cv2.waitKey(0) cv2.destroyAllWindows()
Harrisのコーナー検出を適用した結果は各画素が上記のスコア( R )を表すグレースケール画像になります.適切な閾値処理を施すと,画像中のコーナーを検出できます.
やってみる
cv2.cornerHarris で出てきた結果をゴニョゴニョ(適切な閾値処理)した結果を、コーナーとみなすらしい。
元画像
cv2.cornerHarris の結果
cv2.cornerHarris の閾値処理後
適用結果
opencv チュートリアルチャレンジ14 ハフ変換による円検出
ハフ変換による円検出 — OpenCV-Python Tutorials 1 documentation
void HoughCircles(Mat& image, vector<Vec3f>& circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0) ハフ変換を用いて,グレースケール画像から円を検出します. パラメタ: image – 8ビット,シングルチャンネル,グレースケールの入力画像. circles – 検出された円を出力するベクトル.各ベクトルは,3要素の浮動小数点型ベクトル (x, y, radius) としてエンコードされます. method – 現在のところ, CV_HOUGH_GRADIENT メソッドのみが実装されています.基本的には 2段階ハフ変換 で,これについては Yuen90 で述べられています. dp – 画像分解能に対する投票分解能の比率の逆数.例えば, dp=1 の場合は,投票空間は入力画像と同じ分解能をもちます.また dp=2 の場合は,投票空間の幅と高さは半分になります. minDist – 検出される円の中心同士の最小距離.このパラメータが小さすぎると,正しい円の周辺に別の円が複数誤って検出されることになります.逆に大きすぎると,検出できない円がでてくる可能性があります. param1 – 手法依存の 1 番目のパラメータ. CV_HOUGH_GRADIENT の場合は, Canny() エッジ検出器に渡される2つの閾値の内,大きい方の閾値を表します(小さい閾値は,この値の半分になります). param2 – 手法依存の 2 番目のパラメータ. CV_HOUGH_GRADIENT の場合は,円の中心を検出する際の投票数の閾値を表します.これが小さくなるほど,より多くの誤検出が起こる可能性があります.より多くの投票を獲得した円が,最初に出力されます. minRadius – 円の半径の最小値. maxRadius – 円の半径の最大値.
やってみよう
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np from matplotlib import pyplot as plt cimg = cv2.imread('170523-170301.jpg') img = cv2.cvtColor(cimg,cv2.COLOR_BGR2GRAY) img = cv2.medianBlur(img,15) circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20, param1=50,param2=30,minRadius=0,maxRadius=0) circles = np.uint16(np.around(circles)) for i in circles[0,:]: # draw the outer circle cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2) # draw the center of the circle cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3) cv2.imshow('HoughCircles',cimg) cv2.imwrite('HoughCircles.png',cimg) cv2.waitKey(0) cv2.destroyAllWindows()
これが
こう
なんだろうか、この検出結果は。
よくみると、複数の光源によって、ゴルフボールに複数の影ができていて、その影を検出しているようだ。
また、テカリ?も検出しているようにみえる。