前提・実現したいこと
マウスで画像の座標を取得し、自動追跡するシステムを作っています。
追跡することはできたのですが、マウスで一回クリックして追跡する点を選択すると、なぜか一点ではなく複数点選択されているようで、クリックが10回未満で追跡を開始してしまいます。
きちんと一点ずつ選択できるようにしたいです。
該当のソースコード
python
1import cv2 2import numpy as np 3 4#フレーム数 5FRAME = 108 6frame = 2 7#インターバル 8INTERVAL = 30 9# 反復アルゴリズムの終了条件 10CRITERIA = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03) 11 12#マウスを用いた座標取得 13class mouseParam: 14 def __init__(self, input_img_name): 15 #マウス入力用のパラメータ 16 self.mouseEvent = {"x":None, "y":None, "event":None, "flags":None} 17 #マウス入力の設定 18 cv2.setMouseCallback(input_img_name, self.__CallBackFunc, None) 19 #コールバック関数 20 def __CallBackFunc(self, eventType, x, y, flags, userdata): 21 self.mouseEvent["x"] = x 22 self.mouseEvent["y"] = y 23 self.mouseEvent["event"] = eventType 24 self.mouseEvent["flags"] = flags 25 #マウス入力用のパラメータを返すための関数 26 def getData(self): 27 return self.mouseEvent 28 #マウスイベントを返す関数 29 def getEvent(self): 30 return self.mouseEvent["event"] 31 #マウスフラグを返す関数 32 def getFlags(self): 33 return self.mouseEvent["flags"] 34 #xの座標を返す関数 35 def getX(self): 36 return self.mouseEvent["x"] 37 #yの座標を返す関数 38 def getY(self): 39 return self.mouseEvent["y"] 40 #xとyの座標を返す関数 41 def getPos(self): 42 return (self.mouseEvent["x"], self.mouseEvent["y"]) 43 44#取得した座標から特徴点を取得する 45class Motion: 46 #コンストラクタ 47 def __init__(self): 48 #表示ウィンドウ(特徴点追跡用) 49 cv2.namedWindow("motion") 50 #表示ウィンドウ(特徴点指定用) 51 cv2.namedWindow("input window") 52 #インターバル 53 self.interval = INTERVAL 54 #現在のフレーム(カラー) 55 self.frame = None 56 #現在のフレーム(グレー) 57 self.gray_next = None 58 #前回のフレーム(グレー) 59 self.gray_prev = None 60 #特徴点 61 self.features = None 62 #特徴点のステータス 63 self.status = None 64 #特徴点のステータス(一番最初の画像で取得した特徴点) 65 self.status_src = None 66 #特徴点(一番最初の画像で取得した特徴点) 67 self.features_src = None 68 #メインループ 69 def run(self): 70 #最初のフレームの処理 71 self.frame = cv2.imread("IMG_1.tiff") 72 self.gray_prev = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY) 73 self.gray_next = cv2.cvtColor(self.frame,cv2.COLOR_BGR2GRAY) 74 #最初の特徴点10点を指定 75 cv2.imshow("input window",self.frame) 76 #コールバックの設定 77 mouseData = mouseParam("input window") 78 while 1: 79 cv2.waitKey(10) 80 #左クリックがあったら表示 81 if mouseData.getEvent() == cv2.EVENT_LBUTTONDOWN: 82 mouseData.getX() 83 mouseData.getY() 84 mouseData.getPos() 85 print(mouseData.getX()) 86 print(mouseData.getY()) 87 self.onMouse(mouseData.getX(),mouseData.getY(),mouseData.getPos()) 88 #右クリックがあったら終了 89 elif mouseData.getEvent() == cv2.EVENT_RBUTTONDOWN: 90 break; 91 #特徴点描画と10点の特徴点を取得 92 if self.features is not None: 93 if len(self.features) >= 10: 94 #オプティカルフローの計算 95 features_prev = self.features 96 #グレースケールに変換 97 self.gray_next = cv2.cvtColor(self.frame,cv2.COLOR_BGR2GRAY) 98 self.features, self.status, err = cv2.calcOpticalFlowPyrLK( \ 99 self.gray_prev, \ 100 self.gray_next, \ 101 features_prev, \ 102 None, \ 103 winSize = (10, 10), \ 104 maxLevel = 3, \ 105 criteria = CRITERIA, \ 106 flags = 0) 107 #有効な特徴点のみ残す 108 self.refreshFeatures() 109 if len(self.features) >= 10: 110 #最初の特徴点を格納しておく 111 self.status_src = self.status 112 self.features_src = self.features 113 print("特徴点10個取得出来ました") 114 break; 115 #画像毎の特徴点を抽出し、追跡する 116 for frame in range(2,FRAME): 117 #グレースケールに変換 118 self.gray_next = cv2.cvtColor(self.frame,cv2.COLOR_BGR2GRAY) 119 #self.gray_next = self.frame 120 #特徴点が登録されている場合にOpticalFlowを計算 121 if self.features is not None: 122 #オプティカルフローの計算 123 features_prev = self.features 124 self.features, self.status, err = cv2.calcOpticalFlowPyrLK( \ 125 self.gray_prev, \ 126 self.gray_next, \ 127 features_prev, \ 128 None, \ 129 winSize = (10, 10), \ 130 maxLevel = 3, \ 131 criteria = CRITERIA, \ 132 flags = 0) 133 #有効な特徴点のみ残す 134 self.refreshFeatures() 135 #フレームに有効な特徴点を描画 136 if self.features is not None: 137 for features in self.features: 138 cv2.circle(self.frame,(features[0][0],features[0][1]),2,(15,241,255),-1,8,0) 139 #表示 140 cv2.imshow("motion",self.frame) 141 #次のループ処理の準備 142 self.gray_prev = self.gray_next 143 #画像を読み込む 144 self.frame = cv2.imread("IMG_"+str(frame)+".tiff") 145 #インターバル 146 key = cv2.waitKey(self.interval) 147 #終了処理 148 cv2.destroyAllWindows() 149 #マウスクリックで特徴点を指定する 150 #クリックされた近傍に既存の特徴点がある場合は既存の特徴点を削除する 151 #クリックされた近傍に既存の特徴点がない場合は新規特徴点を追加する 152 def onMouse(self,x,y,XY): 153 #最初の特徴点追加 154 if self.features is None: 155 self.addFeature(x,y) 156 return 157 #クリック点がすでに取得済みの座標点であれば追加しない 158 for click in self.features: 159 #print(click) 160 if np.all(click == XY): 161 return 162 #探索半径(pixel) 163 radius = 5 164 #既存の特徴点が近傍にあるか探索 165 index = self.getFeatureIndex(x,y,radius) 166 #クリックされた近傍に既存の特徴点があるので既存の特徴点を削除する 167 if index >= 0: 168 self.features = np.delete(self.features, index,0) 169 self.status = np.delete(self.status,index,0) 170 #クリックされた近傍に既存の特徴点がないので新規に特徴点を追加する 171 else: 172 self.addFeature(x,y) 173 return 174 #指定した半径内にある既存の特徴点のインデックスを1つ取得する 175 #指定した半径内に特徴点がない場合 index = -1 を応答 176 def getFeatureIndex(self,x,y,radius): 177 index = -1 178 #特徴点が一つも登録されていない 179 if self.features is None: 180 print("features is None") 181 return index 182 max_r2 = radius ** 2 183 index = 0 184 for point in self.features: 185 dx = x - point[0][0] 186 dy = y - point[0][1] 187 r2 = dx ** 2 + dy ** 2 188 if r2 <= max_r2: 189 #この特徴点は指定された半径内 190 return index 191 else: 192 #この特徴点は指定された半径外 193 index += 1 194 #全ての特徴点が指定された半径の外側にある 195 print("feature is out of radius") 196 return -1 197 # 特徴点を新規に追加する 198 def addFeature(self, x, y): 199 # 特徴点が未登録 200 if self.features is None: 201 # ndarrayの作成し特徴点の座標を登録 202 self.features = np.array([[[x, y]]],np.float32) 203 self.status = np.array([1]) 204 # 特徴点を高精度化 205 cv2.cornerSubPix(self.gray_next, self.features, (10, 10), (-1, -1), CRITERIA) 206 # 特徴点を追加登録 207 else: 208 # 既存のndarrayの最後に特徴点の座標を追加 209 self.features = np.append(self.features, [[[x, y]]], axis = 0).astype(np.float32) 210 self.status = np.append(self.status, 1) 211 # 特徴点を高精度化 212 cv2.cornerSubPix(self.gray_next, self.features, (10, 10), (-1, -1), CRITERIA) 213 # 有効な特徴点のみ残す 214 def refreshFeatures(self): 215 # 特徴点が未登録 216 if self.features is None: 217 return 218 # 全statusをチェックする 219 i = 0 220 while i < len(self.features): 221 # 特徴点として認識できず 222 if self.status[i] == 0: 223 # 既存のndarrayから削除 224 self.features = np.delete(self.features, i, 0) 225 self.status = np.delete(self.status, i, 0) 226 i -= 1 227 i += 1 228 229if __name__ == '__main__': 230 Motion().run()
実行結果例
521
165
521
165
feature is out of radius
521
165
feature is out of radius
521
165
feature is out of radius
521
165
feature is out of radius
521
165
feature is out of radius
521
165
feature is out of radius
458
183
feature is out of radius
458
183
feature is out of radius
458
183
feature is out of radius
特徴点10個取得出来ました
補足情報(FW/ツールのバージョンなど)
Windows7 32bit
Python ver.3.7.1
OpenCV ver.3.4.3
回答1件
あなたの回答
tips
プレビュー