@wancles3 (Ryo)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【OpenCV】 Webカメラの画面をマウスの右クリックで回転させたい

やりたいこと

Windows10、Python3.9にて
PCにwebカメラ(1~2台)をUSB接続して、他のソフトと組み合わせて車が走っているときのデータを取りたい。他のソフトとの兼ね合いで、画像はPCの特定の位置に画面を小さくして表示。webカメラの取り付け位置が、車のいろんなところに取り付けるので、上下反転したり、90度横向きの場合もあるので、カメラの表示は右クリックしたら回転するようにしたい。カメラも1台の場合もあるので、1台でもNGにならないようにしたい。

解決したいこと

できたこと
①カメラの画像2台表示
②カメラ1台でも2台でも NGでない。
できてない事
カメラの表示画面上で右クリックしても画像が回転しない。

該当するソースコード

Windows10、Python3.9

from datetime import datetime # 時刻関係のライブラリ
import cv2 # OpenCV のインポート
import tkinter as tk

countx = 0
county = 0
#-------------------------------------
# マウスイベント時に処理を行う
def mouse_event1(event, x, y, flags, param):
 global countx
 if event == cv2.EVENT_RBUTTONUP:
    countx = (countx + 1)% 4

#-------------------------------------
# マウスイベント時に処理を行う
def mouse_event2(event, x, y, flags, param):
 global county
 if event == cv2.EVENT_RBUTTONUP:
    county = (countx + 1)% 4

#-------------------------------------

i = 1
flag = True
captures = []

#カメラの台数確認
while( flag ):
    capture = cv2.VideoCapture(i)
    ret, frame = capture.read()
    flag = ret
    if flag:
       i += 1
       captures.append( capture )

while(True):
    for i, capture in enumerate( captures, start=1 ):
        ret, frame = capture.read()

        #画像をアスペクト比を変えずにリサイズする
        def scale_to_width (frame, width):
            h, w = frame.shape[:2]
            height = round(h * (width / w))
            dst = cv2.resize(frame, dsize=(width, height))
            #print(h,w,height,width,i)
            return dst
        dst = scale_to_width (frame, 450)
        cv2.imshow( 'frame' + str(i), dst )

        cv2.moveWindow('frame1', 1005,70) # カメラ1表示位置指定
        cv2.moveWindow('frame2', 1465,70) # カメラ2表示位置指定

         # 右クリックでFrame1回転
        if countx  == 1:
            frame1 = cv2.rotate(frame1,cv2.ROTATE_90_CLOCKWISE)
        elif countx  == 2:
            frame1 = cv2.rotate(frame1,cv2.ROTATE_180)
        elif countx  == 3:
            frame1 = cv2.rotate(frame1,cv2.ROTATE_90_COUNTERCLOCKWISE )

            cv2.setMouseCallback('frame1', mouse_event1)
            cv2.namedWindow('frame1', 16)

         # 右クリックでFrame2回転
        if county  == 1:
            frame2 = cv2.rotate(frame2,cv2.ROTATE_90_CLOCKWISE)
        elif county  == 2:
            frame2 = cv2.rotate(frame2,cv2.ROTATE_180)
        elif county  == 3:
            frame2 = cv2.rotate(frame2,cv2.ROTATE_90_COUNTERCLOCKWISE )

            cv2.setMouseCallback('frame2', mouse_event2)
            cv2.namedWindow('frame2', 16)

    key = cv2.waitKey(1)
    prop_val = cv2.getWindowProperty('frame1', cv2.WND_PROP_ASPECT_RATIO)
    if (key != -1) or (prop_val < 0):
        break



capture.release()
cv2.destroyAllWindows()

#ここまで


#------------------------------------
### 自分で試したこと
cap1、cap2とカメラ別に設定した場合、うまくいくようですが
カメラ1台のみの場合エラーで表示されません。

cap1 = cv2.VideoCapture(1)
cap2 = cv2.VideoCapture(2)

while True:

    # VideoCaptureから1フレーム読み込む
    ret, frame1 = cap1.read() # 戻り値のframeがimg
    ret, frame2 = cap2.read() # 戻り値のframeがimg





以上よろしくお願いします



0 likes

1Answer

動かしていないでソースだけみて気になった点ですが、cv2.setMouseCallback() を呼び出しているのが countx == 3 あるいは county == 3 の条件の中だけにしかない気がします。

mouse_event1 や mouse_event2 が呼び出されないと countxcounty がカウントアップされないので、cv2.setMouseCallback() が呼び出されることがないように思えます。

while(True): のループの前に cv2.setMouseCallback() を呼び出しておく必要があるのではないでしょうか?


追記:動かしていないけど、こんな感じでしょうか

from datetime import datetime # 時刻関係のライブラリ
import cv2 # OpenCV のインポート
import tkinter as tk

countx = 0
county = 0
#-------------------------------------
# マウスイベント時に処理を行う
def mouse_event1(event, x, y, flags, param):
 global countx
 if event == cv2.EVENT_RBUTTONUP:
    countx = (countx + 1)% 4

#-------------------------------------
# マウスイベント時に処理を行う
def mouse_event2(event, x, y, flags, param):
 global county
 if event == cv2.EVENT_RBUTTONUP:
    county = (county + 1)% 4

#-------------------------------------

i = 1
flag = True
captures = []

#カメラの台数確認
while( flag ):
    capture = cv2.VideoCapture(i)
    ret, frame = capture.read()
    flag = ret
    if flag:
       i += 1
       captures.append( capture )

cv2.namedWindow('frame1', 16)
cv2.setMouseCallback('frame1', mouse_event1)
cv2.moveWindow('frame1', 1005,70) # カメラ1表示位置指定

cv2.namedWindow('frame2', 16)
cv2.setMouseCallback('frame2', mouse_event2)
cv2.moveWindow('frame2', 1465,70) # カメラ2表示位置指定

while(True):
    for i, capture in enumerate( captures, start=1 ):
        ret, frame = capture.read()

        #画像をアスペクト比を変えずにリサイズする
        def scale_to_width (frame, width):
            h, w = frame.shape[:2]
            height = round(h * (width / w))
            dst = cv2.resize(frame, dsize=(width, height))
            #print(h,w,height,width,i)
            return dst
        dst = scale_to_width (frame, 450)
        count = countx if i == 1 else county
        if count  == 1:
            dst = cv2.rotate(dst,cv2.ROTATE_90_CLOCKWISE)
        elif count  == 2:
            dst = cv2.rotate(dst,cv2.ROTATE_180)
        elif count  == 3:
            dst = cv2.rotate(dst,cv2.ROTATE_90_COUNTERCLOCKWISE )

        cv2.imshow( 'frame' + str(i), dst )

    key = cv2.waitKey(1)
    prop_val = cv2.getWindowProperty('frame1', cv2.WND_PROP_ASPECT_RATIO)
    if (key != -1) or (prop_val < 0):
        break

capture.release()
cv2.destroyAllWindows()
0Like

Comments

  1. @wancles3

    Questioner

    コメントいただきありがとうございました。
    早速while(True): のループの前に cv2.setMouseCallback() を入れてみました。
     TypeError: function missing required argument 'window_name' (pos 1)
    エラーが出たので、cv2.setMouseCallback('frame1')としてみました。
     TypeError: function missing required argument 'on_mouse' (pos 2)
    のエラーが出ます。

    元のプログラムにprint(countx,county)を追加して、countが変わるか確認しました。
    結果は右クリックしても countx,county=0,0 のままでした。
    countが0のままなのでmouse_eventを呼びに行っていないのは間違いないようです。

    が、どうやったら呼びに行くのか、まだ不明です。


  2. 先にウインドウ名を登録しないといけないのではないでしょうか?

    cv2.namedWindow('frame1', 16)
    cv2.setMouseCallback('frame1', mouse_event1)
    cv2.namedWindow('frame2', 16)
    cv2.setMouseCallback('frame2', mouse_event2)
  3. @wancles3

    Questioner

    ありがとうございます。
    確かに名前をつけて呼び出さないといけないなということで
    入れ替えてみましたが変化なしでした。

    前回ご指摘いただきました、callbackがcountx == 3にしかかかってないのでは?
    に対して、インデントを下記のようにIfの頭にずらしてみました。


    # 右クリックでFrame1回転
    if countx == 1:
      frame1 = cv2.rotate(frame1,cv2.ROTATE_90_CLOCKWISE)
    elif countx == 2:
      frame1 = cv2.rotate(frame1,cv2.ROTATE_180)
    elif countx == 3:
      frame1 = cv2.rotate(frame1,cv2.ROTATE_90_COUNTERCLOCKWISE )

    cv2.namedWindow('frame1', 16)
    cv2.setMouseCallback('frame1', mouse_event1)

    そして画面を右クリックするとエラーが出ました
     エラー:NameError: name 'frame1' is not defined
    ということは、元々frame1、frame2を使っているのに、namedWindowで同じframe1、frame2を使ったから? と思い 
    cv2.namedWindow('frame1', 16) → cv2.namedWindow('camera1', 16)
    cv2.setMouseCallback('frame1', mouse_event1) → cv2.setMouseCallback('camera1', mouse_event1)
    と変更したところ、frame1、frame2のキャプチャ画面と、何も映っていないcamera1、camera2 の4画面が出ました。
    ここでクリックすると、やはり同じエラー
     エラー:NameError: name 'frame1' is not defined
    が出ます。

    定義をどうしたらいいでしょうか?
  4. たぶん、エラーが起きているのは cv2.imshow() とか cv2.moveWindow() ですよね?

    ループ( `while(True):` )の前に window 名を登録しないといけないのだと思います。

    おおもとに回答に追記しました。
  5. @wancles3

    Questioner

    前に移して、起動してみました。
    通常通り起動しましたので、右クリックすると下記エラーが出ました。
    ---------------------------
    frame1 = cv2.rotate(frame1,cv2.ROTATE_90_CLOCKWISE)
    NameError: name 'frame1' is not defined
    ---------------------------

    どうやらframe1は下記を指しているようです
    frame1 = cv2.rotate(frame1,cv2.ROTATE_90_CLOCKWISE)
    NameError: name 'frame1' is not defined

  6. ウィンドウ名じゃなくて、変数としての frame1 が存在しないということですね。
    その時点で、画像データは dst な気がします。

    回転する処理を cv2.imshow の前に移動

    i の数字によって countx と county のどちらを見るか決定

    ratate() では frame1, frame2 ではなく dst を使う

    という感じでしょうか。
  7. @wancles3

    Questioner

    ありがとうございました。ようやく解決しました!
    感謝いたします!!

    ちなみに1個新たな問題が出まして、カメラ1個の場合
    何も表示されてない Frame2 が出てきます。
    これは表示しないことはできますか? 

Your answer might help someone💌