Summary
Macで指定したデバイスから画面キャプチャするのはできるようになったので、さらに、キャプチャ画面上でマウスを使って矩形領域を指定して、その領域だけをキャプチャするようにしたい。
前回と同様にデバイスが複数ある場合には最初に選択する。
call back関数を定義し、その関数でevent, x, y, flags, param
を引数として受け取るようにする。
cv2.namedWindow('Type [q] to quit capturing')
cv2.setMouseCallback('Type [q] to quit capturing', draw_circle)
cv2.namedWindow()で設定した名前の文字列と同一文字列で対象のwindowを特定できるようにしてcall back関数を指定する。
この対応が誤っていると、
(-27:Null pointer) NULL window handler in function 'cvSetMouseCallback'.
などとおこられます。
whileループ中では毎回新しいフレーム画像が取得されるので、必要な図形をマイフレーム再描画する必要がある。
draw_circleの中では、クリックイベントが発生するごとにポジションをpt0に保存する。
一度マウスがクリックされたなら、画面内のどこか(最新のpt0の場所)に赤丸を書き続ける。
def draw_circle(event, x, y, flags, param):
global circle_clicked, pt0
print(x, y, event, flags, param, cv2.EVENT_LBUTTONDOWN, circle_clicked)
if event == cv2.EVENT_LBUTTONDOWN:
print("Draw!!! (%d, %d), %s" % (x, y, circle_clicked))
# pt0 shall be updeted for every Left click
pt0 = (x, y)
if circle_clicked == False:
circle_clicked = True
while True:
# Capture frame-by-frame
ret, frame = cap.read()
if circle_clicked == True:
cv2.circle(frame, center=pt0, radius=5, color=(0,0,255), thickness=-1)
マウスでクリックしたところに赤い丸を表示するプログラムはこんな感じ。
プログラムサンプルimport cv2
def draw_circle(event, x, y, flags, param):
global circle_clicked, pt0
print(x, y, event, flags, param, cv2.EVENT_LBUTTONDOWN, circle_clicked)
if event == cv2.EVENT_LBUTTONDOWN:
print("Draw!!! (%d, %d), %s" % (x, y, circle_clicked))
# pt0 shall be updeted for every Left click
pt0 = (x, y)
if circle_clicked == False:
circle_clicked = True
# init position values
pt0 = (0, 0)
circle_clicked = False
cap = cv2.VideoCapture(0)
cv2.namedWindow('Type [q] to quit capturing')
cv2.setMouseCallback('Type [q] to quit capturing', draw_circle)
while True:
# Capture frame-by-frame
ret, frame = cap.read()
if circle_clicked == True:
cv2.circle(frame, center=pt0, radius=5, color=(0,0,255), thickness=-1)
# show the frame
cv2.imshow('Type [q] to quit capturing', frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
# terminate resources
cap.release()
cv2.destroyAllWindows()
カメラ画像の取得したい矩形領域の左上と右下の2点でマウス左クリックすると、領域を選択できる。その状態でs
キーを押すと領域を切り出して画像として保存する。
選択された領域と切り出された画像が一致していないのは、キーを押す操作をしているあいだに手持ちの被写体がずれているからです。
1回目のクリックが完了すると、現在選択されている区画を緑線で表示する。
2度目にクリックすると赤枠になり選択領域が確定する。(最初の画像)
完成したスクリプトは次のようになります。 起動すると、ビデオデバイスを調査し2つ以上のデバイスが見つかったら、ユーザにデバイス番号を入力させます。デバイスが1つしかなければ自動で選定し、1つも見つからなければエラー終了します。
領域の選択が完了した状態でs
キーを押すと、赤枠で囲まれた領域を切り出して./photo_crop.jpg
に保存し、赤枠で領域を確定していない城代でs
キーが押された場合は、全体をキャプチャーして./photo.jpg
に保存します。q
で終了。
ソースコードはこちら(video_impose_test.py)
(avfcam_list.swift)をスクリプトと同じ場所に置いてください。)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =======================================================
# opencvを使ってカメラから画像をキャプチャするスクリプト
# マウスで矩形領域を指定してその範囲をキャプチャする
#
# video_impose_test.py
# coded by Noboru Harada (noboru@ieee.org)
#
# Changes:
# 2021/07/07: First version
# =======================================================
import sys
import os
import subprocess
import json
import OrderedDict
from collections import numpy as np
import cv2
def draw_circle(event, x, y, flags, param):
global circle_clicked, pt0
print(x, y, event, flags, param, cv2.EVENT_LBUTTONDOWN, circle_clicked)if event == cv2.EVENT_LBUTTONDOWN:
print("Draw!!! (%d, %d), %s" % (x, y, circle_clicked))# pt0 shall be updeted for every Left click
pt0 = (x, y)if circle_clicked == False:
True
circle_clicked =
def draw_rectangle(event, x, y, flags, param):
global pt1, pt2, topLeft_clicked, botRight_clicked
print(x, y, event, flags, param, cv2.EVENT_LBUTTONDOWN, topLeft_clicked, botRight_clicked)if event == cv2.EVENT_LBUTTONDOWN:
if topLeft_clicked == True and botRight_clicked == True:
# release botRight_clicked
False
botRight_clicked = # renew topLeft_clicked
True
topLeft_clicked =
pt1 = (x, y)
pt2 = (x, y)False and botRight_clicked == False:
elif topLeft_clicked ==
pt1 = (x, y)True
topLeft_clicked = True and botRight_clicked == False:
elif topLeft_clicked ==
pt2 = (x, y)True
botRight_clicked =
print("Draw!!! (%d, %d), %s, %s" % (x, y, topLeft_clicked, botRight_clicked))False:
elif botRight_clicked ==
pt2 = (x, y)
# init position values
0, 0)
pt0 = (0, 0)
pt1 = (0, 0)
pt2 = (False
circle_clicked = False
topLeft_clicked = False
botRight_clicked =
# Get path for the swift script (supporse to be in the same location with this python script)
script_path = os.path.dirname(os.path.abspath(__file__))
script_path = "swift " + script_path + "/avfcam_list.swift"
print(script_path)
# Check camera devices with a swift script
True)
camera_devices = subprocess.check_output(script_path, shell=
json_dict = json.loads(camera_devices, object_pairs_hook=OrderedDict)
-8")
camera_devices = camera_devices.decode("utf
print(camera_devices)
if json_dict.get('SPCameraDataType') == None:
print("No camera device found")exit()
sys.
'SPCameraDataType'])
num_devices = len(json_dict[if num_devices == 0:
print("No camera device found")exit()
sys.
0
val = if num_devices > 1:
print("Select a camera devie:")for s in range(num_devices):
'SPCameraDataType'][s].get("_name")
name = json_dict[
print(" %d: %s" % (s, name))try:
val = int(input())except ValueError:
print("Wrong device id")0
val =
if val > num_devices-1 or val < 0:
print("Wrong device id")0
val =
device_id = val'SPCameraDataType'][device_id].get("_name")
device_name = json_dict[
print(" Use Camera device %d: %s" % (val, device_name))'q' to quit capturing")
print("Type
cap = cv2.VideoCapture(device_id)'Type [q] to quit capturing')
cv2.namedWindow(
#cv2.setMouseCallback('Type [q] to quit capturing', draw_circle)
'Type [q] to quit capturing', draw_rectangle)
cv2.setMouseCallback(
# get window size
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
while(True):
# Capture frame-by-frame
read()
ret, frame = cap.
# if circle_clicked == True:
# cv2.circle(frame, center=pt0, radius=5, color=(0,0,255), thickness=-1)
if topLeft_clicked == True and botRight_clicked == False:
0, 255, 0), 1)
cv2.rectangle(frame, pt1, pt2, (5, color=(0,0,255), thickness=-1)
cv2.circle(frame, center=pt1, radius=True and botRight_clicked == True:
elif topLeft_clicked == 0, 0, 255), 2)
cv2.rectangle(frame, pt1, pt2, (
# show the frame
'Type [q] to quit capturing', frame)
cv2.imshow(
1) & 0xFF
key = cv2.waitKey(
if key == ord('q'):
break
if key == ord('s'):
read()
ret, frame = cap.if topLeft_clicked == True and botRight_clicked == True:
x1, y1 = pt1
x2, y2 = pt2if x1 > x2 or y1 > y2:
x1, y1 = pt2
x2, y2 = pt1
crop_frame = frame[y1 : y2, x1 : x2]
filename = "./photo_crop.jpg"
cv2.imwrite(filename,crop_frame)else:
filename = "./photo.jpg"
cv2.imwrite(filename,frame)
# terminate resources
cap.release() cv2.destroyAllWindows()