OpenCV

OpenCV+AruCoでマーカーの姿勢推定

 前回やったマーカー検出を発展させて今回はマーカーの姿勢を求めます。

  1. import numpy as np
  2. import cv2
  3. from cv2 import aruco

  4. def main():
  5.     cap = cv2.VideoCapture(0)
  6.     marker_length = 0.060
  7.     dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)

  8.     camera_matrix = np.load("mtx.npy")
  9.     distortion_coeff = np.load("dist.npy")

  10.     while True:
  11.         ret, img = cap.read()
  12.         corners, ids, rejectedImgPoints = aruco.detectMarkers(img, dictionary)
  13.         aruco.drawDetectedMarkers(img, corners, ids, (0,255,255))

  14.         if len(corners) > 0:
  15.             for i, corner in enumerate(corners):
  16.                 rvec, tvec, _ = aruco.estimatePoseSingleMarkers(corner, marker_length, camera_matrix, distortion_coeff)
  17.                 
  18.                 tvec = np.squeeze(tvec)
  19.                 rvec = np.squeeze(rvec)
  20.                 rvec_matrix = cv2.Rodrigues(rvec)
  21.                 rvec_matrix = rvec_matrix[0]
  22.                 transpose_tvec = tvec[np.newaxis, :].T
  23.                 proj_matrix = np.hstack((rvec_matrix, transpose_tvec))
  24.                 euler_angle = cv2.decomposeProjectionMatrix(proj_matrix)[6]

  25.                 print("x : " + str(tvec[0]))
  26.                 print("y : " + str(tvec[1]))
  27.                 print("z : " + str(tvec[2]))
  28.                 print("roll : " + str(euler_angle[0]))
  29.                 print("pitch: " + str(euler_angle[1]))
  30.                 print("yaw  : " + str(euler_angle[2]))

  31.                 draw_pole_length = marker_length/2
  32.                 aruco.drawAxis(img, camera_matrix, distortion_coeff, rvec, tvec, draw_pole_length)

  33.         cv2.imshow('drawDetectedMarkers', img)
  34.         if cv2.waitKey(10) & 0xFF == ord('q'):
  35.             break

  36.     cv2.destroyAllWindows()

  37. if __name__ == '__main__':
  38.     main()

7行目でマーカーの一片の長さを[m]表記で指定します。
10,11行目でカメラのキャリブレーション結果をインポートします。
20行目でマーカーの情報を読み取っています。
22~28行目で読み取ったデータの調整をしています。

 詳しい動作についてはこのサイト(https://docs.opencv.org/master/d9/d6a/group__aruco.html):を見て解析して下さい。私も理解していない部分があります。

 実際に動作させると↓のようになります。

スクリーンショット 2020-10-19 212308

OpenCV+AruCoでマーカー検出

今回は、前回作ったマーカーをウェブカメラで検出します。ウェブカメラの映像を読み込む方法は前の記事(http://blog.livedoor.jp/chisato_tofu/archives/25032246.html)を参照してください。
  1. import cv2

  2. aruco = cv2.aruco
  3. dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)

  4. def arReader():
  5.     cap = cv2.VideoCapture(0)

  6.     while True:

  7.         ret, frame = cap.read()
  8.         
  9.         Height, Width = frame.shape[:2]

  10.         img = cv2.resize(frame,(int(Width),int(Height)))

  11.         corners, ids, rejectedImgPoints = aruco.detectMarkers(img, dictionary)

  12.         aruco.drawDetectedMarkers(img, corners, ids, (0,255,0))

  13.         cv2.imshow('drawDetectedMarkers', img)
  14.         
  15.         key = cv2.waitKey(1) & 0xFF
  16.         if key == ord('q'): break

  17.     cap.release()
  18.     cv2.destroyAllWindows()


  19. arReader()
4行目で使用する辞書情報を取得しているのでここを変更すると5X5やもっと多い数のマーカーなども使用できるはずです。
17行目でマーカーを検出し、19行目で描画しています。

 これを実行すると↓のように読み取ることができるはずです。
スクリーンショット 2020-10-18 143626

OpenCV+AruCoでマーカー作成

今回はArucoというライブラリでマーカーを作成する方法です。
  1. import cv2

  2. aruco = cv2.aruco

  3. dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)

  4. def main():
  5.     for i in range(50):

  6.         ar_image = aruco.drawMarker(dictionary, i, 150)   
  7.         fileName = "ar" + str(i).zfill(2) + ".png"
  8.         cv2.imwrite(fileName, ar_image)

  9. if __name__ == "__main__":
  10.     main()
5行目でマーカーを作成する辞書を設定しています。(今回は4X4の50戸あるライブラリを使用しました。)
14,15行目はおまじないです。ないとなぜか動きません。

これを実行すると↓のようなマーカーがプログラムがあるフォルダに50個出力されます。

スクリーンショット 2020-10-18 124248

OpenCV+AruCoでカメラのキャリブレーション

今回はOpenCV+AruCoを利用する前準備として、使用するウェブカメラのキャリブレーションを行います。

  1. import cv2
  2. import matplotlib.pyplot as plt
  3. import numpy as np

  4. square_size = 2 #印刷した人ます分の長さ
  5. pattern_size = (7, 7)

  6. reference_img = 50

  7. pattern_points = np.zeros( (np.prod(pattern_size), 3), np.float32 )
  8. pattern_points[:,:2] = np.indices(pattern_size).T.reshape(-1, 2)
  9. pattern_points *= square_size
  10. objpoints = []
  11. imgpoints = []

  12. capture = cv2.VideoCapture(0)

  13. while len(objpoints) < reference_img:
  14.     
  15.     ret, img = capture.read()
  16.     height = img.shape[0]
  17.     width = img.shape[1]

  18.     gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

  19.     ret, corner = cv2.findChessboardCorners(gray, pattern_size)
  20.     
  21.     if ret == True:
  22.         display("detected coner!")
  23.         display(str(len(objpoints)+1) + "/" + str(reference_img))
  24.         term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1)
  25.         cv2.cornerSubPix(gray, corner, (5,5), (-1,-1), term)
  26.         imgpoints.append(corner.reshape(-1, 2))
  27.         objpoints.append(pattern_points)

  28.     cv2.imshow('image', img)
  29.     
  30.     if cv2.waitKey(100) & 0xFF == ord('q'):
  31.         break

  32. display("calculating camera parameter...")
  33. ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

  34. np.save("mtx", mtx)
  35. np.save("dist", dist.ravel())

  36. capture.release()
  37. cv2.destroyAllWindows()

 詳しいことは省きますが、このプログラムを実行すると
'detected coner!'
'1/50'
というようにチェス盤すべてが画面内に入っているときに画像の読み込みが始まっていき、
"mtx.npy"というカメラ行列の出力ファイルと"dist.npy"という歪みパラメータのファイルが出力されます。これは、次からマーカーの検出などをするときにカメラから出力される映像を補正するのに使います。

 ちなみに私は
mtx = 
array([[1.09857661e+04, 0.00000000e+00, 3.85026317e+02],
       [0.00000000e+00, 3.88955066e+03, 1.67061166e+02],
       [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
dist = 
array([ 5.78695277e+01, -5.66734140e+03,  1.91833601e+00,  3.71670683e-01,
       -2.21831989e+02])
という値になりました。


ches

OpenCVでウェブカメラから顔検出

前回のプログラムをいじってウェブカメラの映像からリアルタイムで顔検出をできるようにしました。

  1. import cv2

  2. video = cv2.VideoCapture(0)
  3.  
  4. while video.isOpened():
  5.     ret, frame = video.read()
  6.     if not ret: break

  7.     facerect = cascade.detectMultiScale(frame, scaleFactor=1.1, minNeighbors=6, minSize=(30, 30))
  8.      
  9.     rectangle_color = (0, 255, 0)
  10.  
  11.     if len(facerect) > 0:
  12.         for rect in facerect:
  13.             cv2.rectangle(frame, tuple(rect[0:2]),tuple(rect[0:2] + rect[2:4]), rectangle_color, thickness=2)
  14.             cut_frame = frame[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]]
  15.             cut_frame = cv2.resize(cut_frame,(rect[2]//20, rect[3]//20))
  16.             cut_frame = cv2.resize(cut_frame,(rect[2], rect[3]),cv2.INTER_NEAREST)
  17.             frame[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]]=cut_frame
  18.  
  19.     cv2.imshow('frame', frame)
  20.  
  21.     key = cv2.waitKey(1) & 0xFF
  22.     if key == ord('q'): break
  23.  
  24. video.release()
  25. cv2.destroyAllWindows()
3行目でウェブカメラの映像を読み込んでいます。パソコンにカメラが一つだけついている場合は0番で問題ありませんが、複数ついている場合は番号を変えて目的のカメラに合わせてください。
6,7行目でフレームの読み込みと読み込めなかった場合の終了処理をしています。
9行目で顔の検出をしています。
13行目とそのあとのfor文で顔を認識したときのモザイク処理をしています。
21行目でフレームの描写をしています。
23,24行目でポップアップの終了処理をしています。
26,27行目でプログラムの終了処理をしています。


 また、今回も前回と同じフリー素材(https://www.pakutaso.com/)を使わせていただきました。

OpenCVで正面の顔検出

今回はOpenCVで顔を検出する方法です。今回はカスケード型検出器である"haarcascade_frontalface_default.xml"というものを用いました。

プログラムは以下の通りです。プログラムの後に解説を付けておきます。
  1. %matplotlib inline
  2. import cv2
  3. import matplotlib.pyplot as plt
  4.  
  5. img = cv2.imread('input2.jpg')
  6.  
  7. gry_img = cv2.imread('input2.jpg', 0)
  8.  
  9. cascade = cv2.CascadeClassifier(cascade_path)
  10.  
  11. facerect = cascade.detectMultiScale(gry_img, scaleFactor=1.1, minNeighbors=5, minSize=(100, 100))
  12.  
  13. rectangle_color = (0, 255, 0)
  14.  
  15. if len(facerect) > 0:
  16.     for rect in facerect:
  17.         cv2.rectangle(img, tuple(rect[0:2]),tuple(rect[0:2] + rect[2:4]), rectangle_color, thickness=2)
  18.  
  19. cv2.imwrite('output.jpg',threshold_img)
5行目で結果表示用の画像を読み込みます。
7行目で処理用の白黒画像を読み込みます。
9行目でカスケード検出器の特徴量を取得します。
11行目で顔検出をします。

初めはプログラム内で検出器のパスを指定していたのですが、うまくいかずエラーを吐いてしまったので"haarcascade_frontalface_default.xml"をOpenCVのファイル内から探してきて直接フォルダ内に入れることで解決しています。


 試しにフリー素材(https://www.pakutaso.com/)から顔を検出した結果、いい感じに検出できました。

output1
output2
output3
output4

OpenCVの基本的な画像処理

今回はOpenCVを使う上でよく使うと思われるいくつかの基本的な画像処理を紹介します。
 元画像として、適してるのかはわかりませんが私のアイコンを利用します。
test
↑これ(test.jpg)です。

①グレースケール
 画像処理といったらこれといった感じです。明暗のみの情報になるので処理するのが簡単になります。
  1. %matplotlib inline
  2. import cv2
  3. import matplotlib.pyplot as plt
  4.  
  5. gry_img = cv2.imread('test.jpg', 0)

  6. cv2.imwrite('output.jpg',gry_img)

 第1引数"test"を第2引数"0"(グレースケール)で読み込みます。
 実行結果↓
output


②二値化
 これもよく画像処理で使われます。白と黒の二色だけで構成される画像にします。
  1. %matplotlib inline
  2. import cv2
  3. import matplotlib.pyplot as plt
  4.  
  5. gry_img = cv2.imread('test.jpg', 0)
  6. ret, threshold_img = cv2.threshold(gry_img, 127, 255, cv2.THRESH_OTSU) 
  7.  
  8. cv2.imwrite('output.jpg',threshold_img)

 第2引数で閾値を決め、第3引数で白い部分の値、第4引数で二値化を実行しています。
 実行結果↓
output


③エッジ検出
 画像の中のものの輪郭を検出します。
  1. %matplotlib inline
  2. import cv2
  3. import matplotlib.pyplot as plt
  4.  
  5. img = cv2.imread('test.jpg', 0)
  6. canny_img = cv2.Canny(img, 50, 110)
  7.  
  8. cv2.imwrite('output.jpg',canny_img)

 第2,3引数が閾値になります。
 実行結果↓
output


④ぼかし
  1. %matplotlib inline
  2. import cv2
  3. import matplotlib.pyplot as plt
  4.  
  5. img = cv2.imread('test.jpg')
  6. gauss_img = cv2.GaussianBlur(img, (15,15), 0)
  7.  
  8. cv2.imwrite('output.jpg',gauss_img)

 第2引数でカーネルを、第3引数で標準偏差を設定します。
 実行結果↓

output


 これらを利用したりしなかったりしてもっと複雑な処理をしていきたいと思います。

OpenCVでの画像出力

 OpenCVで画像出力をする方法はいくつかありますが、今回は3つ紹介します。

①画像ファイルとして保存する
  1. import cv2
  2. img = cv2.imread('test.jpg')
  3. cv2.imwrite('output.jpg',img)
 ファイル内の"test"というファイルを読み込み、"output"というファイルを出力してくれます。


②別のウィンドウに表示する
  1. import cv2
  2. img = cv2.imread('test.jpg')
  3. cv2.imshow('output',img)
  4. cv2.waitKey(0) #表示時間の無期限化
 ファイル内の"test"を読み込み、"output"というウィンドウを開きそこに表示します。


 今回使っているJupyterNotebook上で表示する方法には下の③,④があります。

③matplotlib
  1. %matplotlib inline
  2. import cv2
  3. import matplotlib.pyplot as plt
  4. img = cv2.imread('test.jpg')
  5. plt.imshow(img)

 ファイル内の"test"を読み込み、このコードの下に表示します。
 これは本来グラフを扱うためのライブラリです。

④IPython.display
  1. import cv2
  2. from IPython.display import Image
  3. Image('test.jpg')

 表示のされ方は③と同じです。

 これからはこれらを主に利用していこうと思います。

WindowsでPython+OpenCVの環境構築

 Windows環境でPythonとOpenCVを用いて画像処理をする環境構築+αの話です。

 今回はAnacondaというプラットフォームで使うことにします。

 まず、(https://www.anaconda.com/products/individual)このサイトに行き、Downloadを押します。Windowsの64-Bit Graphical installer をクリックしダウンロードします。

 ダウンロード出来たらインストーラーを管理者として実行します。インストーラーはNEXTとかagreeを押していけばオッケーです。

 次にOpenCVのインストールをします。先ほどインストールしたソフトの中のAnaconda Powershel Pronptを起動します。起動したら、"pip install -upgrade pip"を実行しpipのアップグレートをします。そのあと、"conda update -all"を実行し、一応すべてのライブラリを更新します。

 ここまで順調にいけばAnaconda上でOpenCVを使えるようになっています。

 余談ですがAnacondaの実行方法はJupyter Notebook (Anaconda)を実行し、出てきた3つのURLのどれかをGoogle Choromeで開くとこのように実行できます。スクリーンショット 2020-10-12 130354
 ここで右上のNewのところからフォルダーやプログラムを作ることができます。
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

読者登録
LINE読者登録QRコード