1. meznat

    Posted

    meznat
Changes in title
+Pythonでウィンドウのリアルタイム描画と特徴検出
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,165 @@
+# はじめに
+今回はこういうものを作ります。
+
+<iframe width="500" height="400" src="https://www.youtube.com/embed/WfCbJppJh8I" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+
+
+YouTubeをブラウザで再生し,その画面に検知処理を行っています。
+ウィンドウを動かしたりリサイズしたりしても検知画面が追従します。
+こういったデスクトップのリアルタイム画像処理は情報がなかったので今回Pythonで実装してみました。
+
+# アルゴリズム
+以下の流れで実装しています。
+
+```
+While True:
+1.画面領域の認識
+2.スクリーンショットを撮影
+3.画像検知処理
+4.検知画像の表示
+```
+
+#
+
+# 1.画面領域の認識
+検知したい画面のウィンドウを指定します。これには以下の処理が含まれます。
+
+```
+・ウィンドウタイトルの取得
+・winAPIでウィンドウ情報の取得
+```
+どちらもWinAPIを叩かないといけないですが,学習コストの割にうまみが少ないので手ごろなライブラリで処理したいです。
+一つ目は[ahk](https://pypi.org/project/ahk/)というAutoHotKeyのラッパーで簡単に処理できます。
+
+```GetTitle.py
+#pip install ahk
+from ahk import AHK
+
+window_title = "YouTube"
+
+def GetTitle(window_title):
+ ahk = AHK()
+ wins = list(ahk.windows())
+ titles = [win.title for win in wins]
+ for t in titles:
+ text = t.decode("shift-jis", errors="ignore")
+ if window_title in text:
+ return text
+
+```
+キーワード入力に対し部分一致でタイトルを取得できます。
+ahkは`ahk.windows()`でid一覧を取得でき,ウィンドウタイトルの取得も`.title`で済むのでかなり簡単にウィンドウタイトルを取得できます。取得したテキストはバイナリ列ですのでデコードして使用します。ただし処理速度は遅めなので今回はループ外で一度実行するだけにします。
+ウィンドウタイトルを取得したらWinAPIを叩いてウィンドウの位置とサイズを取得します。こちらの処理もahkで取得できますが速さを求めるために今回は使用しません。
+
+```GetWindowRectFromName.py
+import ctypes
+from ctypes.wintypes import HWND, DWORD, RECT
+
+TargetWindowTitle = GetTitle(window_title)
+
+def GetWindowRectFromName(TargetWindowTitle):
+ TargetWindowHandle = ctypes.windll.user32.FindWindowW(0, TargetWindowTitle)
+ Rectangle = ctypes.wintypes.RECT()
+ ctypes.windll.user32.GetWindowRect(
+ TargetWindowHandle, ctypes.pointer(Rectangle))
+ return (Rectangle.left, Rectangle.top, Rectangle.right, Rectangle.bottom)
+
+```
+
+# 2.スクリーンショットの撮影
+スクリーンショットの撮影方法はいくつかありますが今回は[mss](https://pypi.org/project/mss/)というライブラリを使用します。公式で超早いと謳っているだけにPillowでスクリーンショットを撮影するよりも早いと実感できるくらいには早い。
+
+```SCT.py
+#pip install mss
+import mss
+
+bbox = GetWindowRectFromName(TargetWindowTitle)
+
+def SCT(bbox):
+ with mss.mss() as sct:
+ img = sct.grab(bbox)
+ return img
+```
+
+# 3.画像検知処理
+ここは相当の自由度がありますので古典的な物体検出から深層学習までお好きな処理を実装するのが良いです。今回はなんでもよかったですが[OpenCVのCanShift?](https://code-graffiti.com/opencv-meanshift-camshift-tracking-in-python/)を実行してみました。精度は...冒頭を見てみてください。
+
+```face_detection.py
+#pip install opencv-python
+#curl https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml > haarcascade_frontalface_default.xml
+import cv2
+
+img = SCT(bbox)
+
+def FaceDetection(img):
+ face_cascade = cv2.CascadeClassifier(
+ 'haarcascade_frontalface_default.xml')
+ face_rects = face_cascade.detectMultiScale(img)
+ (face_x, face_y, w, h) = tuple(face_rects[0])
+ track_window = (face_x, face_y, w, h)
+
+ roi = img[face_y:face_y+h, face_x:face_x+w]
+ hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
+ roi_hist = cv2.calcHist([hsv_roi], [0], None, [180], [0, 180])
+ cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
+ term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
+ hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
+ dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
+ rect, track_window = cv2.meanShift(dst, track_window, term_crit)
+
+ return face_rects, track_window
+```
+
+# 4.検知画像の表示
+OpenCVで矩形画像を作成しています。パラメーターはなんとなくで調整してください。
+
+```DrawRect.py
+face_rects, track_window = FaceDetection(img)
+x, y, w, h = track_window
+img2 = cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 0, 255), thickness=5)
+img_show("window", img2, position=(
+ bbox[0]*2-bbox[2], bbox[1]-30), size=(bbox[2]-bbox[0], bbox[3]-bbox[1]))
+```
+
+# まとめ
+以下のコードをYouTubeを開いた状態で実行してみるといい感じの画面であればうまくいきます。いい感じかどうかはタイトルの文字コードに依存しますので変なタイトルのyoutubeだと全く動きません。英語が望ましいです。
+
+```main.py
+def main(window_title="YouTube"):
+
+ TargetWindowTitle = GetTitle(window_title)
+
+ While True:
+ try:
+ bbox = GetWindowRectFromName(TargetWindowTitle)
+
+ img = SCT(bbox)
+
+ face_rects, track_window = FaceDetection(img)
+
+ x, y, w, h = track_window
+ img2 = cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 0, 255), thickness=5)
+ img_show("window", img2, position=(
+ bbox[0]*2-bbox[2], bbox[1]-30), size=(bbox[2]-bbox[0], bbox[3]-bbox[1]))
+
+ # escape sequence
+ # ESC to escape
+ k = cv2.waitKey(1) & 0xFF
+ if k == 27: # wait for ESC key to exit
+ cv2.destroyAllWindows()
+ return
+ # or topleft mouse to escape
+ if AHK().mouse_position == (0, 0):
+ cv2.destroyAllWindows()
+ return
+ except:
+ continue
+
+```
+
+OpenCVはすぐにエラー落ちするので雑にtry処理で括っています。また画像を閉じようとするとプロセスがすぐ死にますのでESCキーまたはマウスを左上にもっていくことで終了処理としています。
+
+# おわりに
+YouTubeを垂れ流して機械学習とかできたら最高ですよね。でも画像処理といえばGPUは欠かせないのでその辺を解決しないといけません。そのうち挑戦してみたいと思います。
+
+