オブジェクトの傾きからパラメーターをとる必要がでてきました。
できればnoOpenCVチャレンジを継続したかったのですが、時間的にそうも言っていられず……
久々にOpenCVを弄る事に。
実際のところ
考え方
- 画像を二値化する
- オブジェクトの領域を区切る
- オブジェクト毎に主成分分析(PCA:principal component analysis)にて方向を判定
スクリプト
import cv2 import numpy as np from math import atan2, cos, sin, sqrt, pi ## 角度を設定するメソッド def getOrientation(pts, img): sz = len(pts) data_pts = np.empty((sz, 2), dtype=np.float64) for i in range(data_pts.shape[0]): data_pts[i,0] = pts[i,0,0] data_pts[i,1] = pts[i,0,1] # 主成分分析(PCA)にて方向情報を絞る mean = np.empty((0)) mean, eigenvectors, eigenvalues = cv2.PCACompute2(data_pts, mean) # 先ほどのベクトル情報をもとに角度を計算 angle = atan2(eigenvectors[0,1], eigenvectors[0,0]) print(" Rotation Angle: " + str(int(np.rad2deg(angle))) + " degrees") return angle if __name__ == '__main__': # 画像の読み込み。第二引数"1"はグレースケール img_src = cv2.imread("testImage.png", 1) # グレースケールに変換 img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY) # 二値変換 thresh = 100 max_pixel = 255 ret, img_dst = cv2.threshold(img_gray, thresh, max_pixel, cv2.THRESH_BINARY) # 輪郭を決定 contours, _ = cv2.findContours(img_dst, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) for i, c in enumerate(contours): # つかう領域を設定 area = cv2.contourArea(c) # 大きすぎるのと小さすぎるのは無視 if area < 1e2 or 1e5 < area: continue # 角度検知と出力 getOrientation(c, img_src) # 表示 cv2.imshow("Show BINARIZATION Image", img_dst) cv2.waitKey() cv2.destroyAllWindows()
使った画像
ペイント3Dで直線を3本引いてみました。
Shift押しながらなので45度刻み……のはず。
実行
実行すると、角度が出た後に画面が表示されます。
$ DISPLAY=:0.0 python3 graypython.py Rotation Angle: 134 degrees Rotation Angle: 0 degrees Rotation Angle: 45 degrees
ペイント3Dで引いたのでかなり正確かと思ってましたが、1度ほどのズレはどうしても出るみたいですね。