I am currently trying to implement a function that will allow me to use the cursor to "grab" a video and move it around. I want to be able to only move the video when the cursor is held down and moved. I have defined the mouse events, captured the coordinates of my cursor but I am not sure how to write the function to crop the video.
x1, x2, y1, y2 = 0, 0, 0, 0
mouse_down = False
def mouse_event_callback(event, x, y, flags, param):
global x1, x2, y1, y2, mouse_down
if event == cv2.EVENT_LBUTTONDOWN:
x1, y1 = x, y
mouse_down = True
elif event == cv2.EVENT_MOUSEMOVE:
if mouse_down:
x2, y2 = x, y
mouse_grab_video(x1, x2, y1, y2)
elif event == cv2.EVENT_LBUTTONUP:
mouse_down = False
def mouse_grab_video(x1, x2, y1, y2):
...
Any help would be much appreciated!
You can crop the image directly based on the coordinate you get from your mouse event.
#!/usr/bin/env python3
import argparse
import cv2
import numpy as np
from PIL import Image
drawing = False # true if mouse is pressed
ix,iy = -1,-1
refPt = []
img = ""
clone = ""
ROIRegion = []
# mouse callback function
def draw_rectangle(event,x,y,flags,param):
global ix,iy,drawing,img,clone,refPt, ROIRegion
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
refPt = [(x, y)]
ROIRegion.append(refPt)
#clone = img.copy()
elif event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
img = clone.copy()
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),3)
a=x
b=y
if a != x | b != y:
cv2.rectangle(img,(ix,iy),(x,y),(0,0,0),-1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
refPt.append((x,y))
img = clone.copy()
cv2.rectangle(img, (ix,iy),(x,y), (0, 255, 0), 2)
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image")
args = vars(ap.parse_args())
img = cv2.imread(args["image"])
img = np.array(img)
clone = img.copy()
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_rectangle)
while(1):
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == ord("r"):
del ROIRegion[-1]
del refPt[-1]
img = clone.copy()
elif k == 27:
break
#Crop image to ROI
for region in range(len(ROIRegion)):
cv2.rectangle(img, ROIRegion[region][0],ROIRegion[region][1], (0, 255, 0), 2)
roi = clone[ROIRegion[region][0][1]:ROIRegion[region][1][1], ROIRegion[region][0][0]:ROIRegion[region][1][0]]
For zoom you may use EVENT_MOUSEWHEEL to increment scale and resize the image based on the scale. update imshow for latest image you scale. you can refer to this link
Related
so i'm using opencv and i want to make a sort of selection tool but the problem is can't make the rectangle transparent. here's the code:
import numpy as np
import cv2 as cv
drawing = False
def draw_rec(event,x,y,flags,param):
global ix,iy,drawing
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv.EVENT_LBUTTONUP:
drawing = False
cv.rectangle(img,(ix,iy),(x,y),(0,0,0),-1)
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 5)
img = cv.imread('baboon.jpg', -1)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_rec)
while(1):
cv.imshow('image',img)
k = cv.waitKey(1) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
The first mistake in the code is:
elif event == cv.EVENT_LBUTTONUP:
drawing = False
cv.rectangle(img,(ix,iy),(x,y),(0,0,0),-1)
The -1 parameter means to fill the rectangle. source If we change -1 to 1:
From my point of view, the result is not satisfactory. The multiple rectangle display is caused by the mouse_movement.
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 5)
Each time the mouse moves, the rectangle will be drawn. I think it is better if we draw when the mouse movement finishes:
Code:
import numpy as np
import cv2 as cv
drawing = False
def draw_rec(event,x,y,flags,param):
global ix,iy,drawing
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv.EVENT_LBUTTONUP:
drawing = False
cv.rectangle(img,(ix,iy),(x,y),(0,255,0),5)
img = cv.imread('27BR1.jpg', -1)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_rec)
while(1):
cv.imshow('image',img)
k = cv.waitKey(1) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
I am trying to draw a circle a present coordinate when the mouse moves from one place to another in OpenCV screen
Right now I am able to see all the circle where the mouse has been moved from one place to another as mentioned below in the snap
But I need to display only one circle(present coordinate) while using EVENT_MOUSEMOVE in mouse events as mentioned in snapshot
mentioned below is my code
import cv2
import numpy as np
import math
drawing = False
def draw_circle(event, x, y, flags, param):
global x1, y1, drawing, radius, num, img, img2
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
num += 1
x1, y1 = x, y
radius = int(math.sqrt((x - x1)**2 + (y - y1)**2))
print(radius)
cv2.circle(img, (x1,y1), radius, (255, 0, 0), 1)
if event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
radius = 5
cv2.circle(img, (x, y), radius, (0, 0, 255), -1)
if event == cv2.EVENT_LBUTTONUP:
#drawing = False
num += 1
radius = int(math.sqrt((x - x1)**2 + (y - y1)**2))
print(radius)
cv2.circle(img, (x1,y1), radius, (255, 0, 255), 1)
if __name__ == "__main__":
num = 0
windowName = 'Drawing'
img = np.zeros((500, 500, 3), np.uint8)
cv2.namedWindow(windowName)
cv2.setMouseCallback(windowName, draw_circle)
while (True):
cv2.imshow(windowName, img)
if cv2.waitKey(20) == 27:
break
cv2.destroyAllWindows()
Suggestions will be very helpful
I am having problems understanding regions of interest in opencv. I have some code that does a simple background subtraction from my first_frame. I am also able to draw a rectangle with my mouse_draw_rect function.
Now, I would like the background subtraction to only happen inside the rectangle that I draw because I would like to speed up the algorithm processing. I know I have to set some sort of Region of Interest but I tried it all yesterday and today and nothing that I have tried from tutorials have worked. Can someone help guide me through this process please?
EDIT: attempted to fix code
import numpy as np
import cv2
import matplotlib.pyplot as plt
cap = cv2.VideoCapture(0)
_, first_frame = cap.read()
def mouse_draw_rect(event, x, y, flags, params):
global point1, point2, drawing, first_frame, x1, x2, y1, y2
if event == cv2.EVENT_LBUTTONDOWN:
if drawing is False:
drawing = True
point1 = (x, y)
x1 = (x)
y1 = (y)
else:
drawing = False
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point2 = (x, y)
x2 = (x)
y2 = (y)
elif event == cv2.EVENT_MBUTTONDOWN:
first_frame = frame
drawing = False
point1 = ()
point2 = ()
x1 = ()
x2 = ()
y1 = ()
y2 = ()
cv2.namedWindow('Original')
cv2.setMouseCallback("Original", mouse_draw_rect)
while True:
ret, frame = cap.read( )
if point1 and point2:
cv2.rectangle(frame, point1, point2, (0, 0, 0),5)
difference = cv2.absdiff(first_frame[y1:y2, x1:x2], frame[y1:y2, x1:x2])
difference = cv2.GaussianBlur(difference, (3, 3), 0)
_, difference = cv2.threshold(difference, 18, 255, cv2.THRESH_BINARY)
cv2.imshow('first frame (1)', first_frame)
cv2.imshow('Original', frame)
cv2.imshow('difference', difference)
key = cv2.waitKey(30) & 0xff
if key == 27:
break
cap.release()
cv2.destroyAllWindows()
The main problem is with the ROI selection event and how it is being called currently. The current implementation is not dynamic which means we are unable to visualize what ROI we are trying to select. Also, we have started processing before even selecting the ROI.
The proper way to select the ROI is that once we have captured the first frame, register the mouse click event and visualize the frame indefinitely with imshow and waitKey(n) until some specific key is pressed. Alternatively, we may be able to achieve the same effect without the infinite loop by using waitKey(0) (Not tested).
At this stage, we should be able to draw the desired ROI rectangle. The key factor here is the execution must be halted either by using an infinite loop or waitKey(0). Just registering the event is not enough. After ROI selection is done, then proceed with the rest of the code.
Some recommendations are as follows:
Avoid the usage of global variables where possible
Create separate window for ROI selection and discard it afterwards
Create separate functions for each individual task
Following is the complete code demonstrating correct usage of mouse click event to select the ROI for video processing:
import numpy as np
import cv2
import matplotlib.pyplot as plt
ORIGINAL_WINDOW_TITLE = 'Original'
FIRST_FRAME_WINDOW_TITLE = 'First Frame'
DIFFERENCE_WINDOW_TITLE = 'Difference'
canvas = None
drawing = False # true if mouse is pressed
#Retrieve first frame
def initialize_camera(cap):
_, frame = cap.read()
return frame
# mouse callback function
def mouse_draw_rect(event,x,y,flags, params):
global drawing, canvas
if drawing:
canvas = params[0].copy()
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
params.append((x,y)) #Save first point
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
cv2.rectangle(canvas, params[1],(x,y),(0,255,0),2)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
params.append((x,y)) #Save second point
cv2.rectangle(canvas,params[1],params[2],(0,255,0),2)
def select_roi(frame):
global canvas
canvas = frame.copy()
params = [frame]
ROI_SELECTION_WINDOW = 'Select ROI'
cv2.namedWindow(ROI_SELECTION_WINDOW)
cv2.setMouseCallback(ROI_SELECTION_WINDOW, mouse_draw_rect, params)
roi_selected = False
while True:
cv2.imshow(ROI_SELECTION_WINDOW, canvas)
key = cv2.waitKey(10)
#Press Enter to break the loop
if key == 13:
break;
cv2.destroyWindow(ROI_SELECTION_WINDOW)
roi_selected = (3 == len(params))
if roi_selected:
p1 = params[1]
p2 = params[2]
if (p1[0] == p2[0]) and (p1[1] == p2[1]):
roi_selected = False
#Use whole frame if ROI has not been selected
if not roi_selected:
print('ROI Not Selected. Using Full Frame')
p1 = (0,0)
p2 = (frame.shape[1] - 1, frame.shape[0] -1)
return roi_selected, p1, p2
if __name__ == '__main__':
cap = cv2.VideoCapture(0)
#Grab first frame
first_frame = initialize_camera(cap)
#Select ROI for processing. Hit Enter after drawing the rectangle to finalize selection
roi_selected, point1, point2 = select_roi(first_frame)
#Grab ROI of first frame
first_frame_roi = first_frame[point1[1]:point2[1], point1[0]:point2[0], :]
#An empty image of full size just for visualization of difference
difference_image_canvas = np.zeros_like(first_frame)
while cap.isOpened():
ret, frame = cap.read()
if ret:
#ROI of current frame
roi = frame[point1[1]:point2[1], point1[0]:point2[0], :]
difference = cv2.absdiff(first_frame_roi, roi)
difference = cv2.GaussianBlur(difference, (3, 3), 0)
_, difference = cv2.threshold(difference, 18, 255, cv2.THRESH_BINARY)
#Overlay computed difference image onto the whole image for visualization
difference_image_canvas[point1[1]:point2[1], point1[0]:point2[0], :] = difference.copy()
cv2.imshow(FIRST_FRAME_WINDOW_TITLE, first_frame)
cv2.imshow(ORIGINAL_WINDOW_TITLE, frame)
cv2.imshow(DIFFERENCE_WINDOW_TITLE, difference_image_canvas)
key = cv2.waitKey(30) & 0xff
if key == 27:
break
else:
break
cap.release()
cv2.destroyAllWindows()
Pro Tip:
Sometimes, when a camera is initialized, it takes some time to warm-up depending upon the ambient light present in the room. You may consider skipping a few initial frames to let the camera settle from the initialization phase. It can be done by defining the initialize_camera function in the above code as follows:
def initialize_camera(cap):
for i in range(0,60): #Skip first 60 frames
_, frame = cap.read()
return frame
Simply crop by the area in the rectangle you have drawn.
Instead of
difference = cv2.absdiff(first_frame, frame)
use
difference = cv2.absdiff(first_frame[y1:y2, x1:x2], frame[y1:y2, x1:x2])
On each frame you can create a subimage using subimage = image[y1:y2,x1:x2] And then use the subimage for processing.
A quick and dirty implementation in you code:
Replace
elif event == cv2.EVENT_MOUSEMOVE:
with
elif event == cv2.EVENT_LBUTTONUP:
And add the subimage:
if point1 and point2:
cv2.rectangle(frame, point1, point2, (0, 0, 0),5)
subimg = frame[point1[1]:point2[1],point1[0]:point2[0]]
cv2.imshow("Subimage",subimg)
I am trying to draw two zones by clicking by mouse events and i thought about using two threads and a lock and here is my code :
import numpy as np
import cv2
from threading import Thread, RLock
CANVAS_SIZE = (600,800)
FINAL_LINE_COLOR = (255, 255, 255)
WORKING_LINE_COLOR = (127, 127, 127)
verrou = RLock()
class ZoneDrawer(Thread):
def __init__(self, window_name):
Thread.__init__(self)
self.window_name = window_name
self.done = False
self.current = (0, 0)
self.points = []
def on_mouse(self, event, x, y, buttons, user_param):
if event == cv2.EVENT_MOUSEMOVE:
self.current = (x, y)
elif event == cv2.EVENT_LBUTTONDOWN:
print("Adding point #%d with position(%d,%d)" % (len(self.points), x, y))
self.points.append((x, y))
elif event == cv2.EVENT_RBUTTONDOWN:
self.done = True
def run(self):
cv2.namedWindow(self.window_name)
cv2.imshow(self.window_name, np.zeros(CANVAS_SIZE, np.uint8))
cv2.waitKey(1)
cv2.setMouseCallback(self.window_name, self.on_mouse)
while(not self.done):
canvas = np.zeros(CANVAS_SIZE, np.uint8)
with verrou:
if (len(self.points) > 0):
cv2.polylines(canvas, np.array([self.points]), True, FINAL_LINE_COLOR, 1)
cv2.line(canvas, self.points[-1], self.current, WORKING_LINE_COLOR)
cv2.imshow(self.window_name, canvas)
if cv2.waitKey(50) == 27:
self.done = True
cv2.waitKey()
cv2.destroyWindow(self.window_name)
thread_1 = ZoneDrawer("zone1")
thread_2 = ZoneDrawer("zone2")
thread_1.start()
thread_2.start()
thread_1.join()
thread_2.join()
But this code is still not working. Any help or suggestion ?
The following scrip can be used to select regions of an image using the mouse (as seen in the .gif).
import cv2, numpy as np
# Mouse callback function
global click_list
positions, click_list, shapes = [(0,0)], [], []
def callback(event, x, y, flags, param):
positions[-1] = (x,y)
if event == 1: click_list.append((x,y))
cv2.namedWindow('img')
cv2.setMouseCallback('img', callback)
# Mainloop - show the image and collect the data
while True:
# Create a blank image
img = np.zeros((600,600,3), np.uint8)
# Try to draw the shape being collected
for idx in range(len(click_list)-1):
cv2.line(img, click_list[idx], click_list[idx+1], (0,255,0), 5)
# Draw the stored shapes
for shape in shapes:
for idx in range(len(shape)):
cv2.line(img, shape[idx], shape[idx-1], 255, 5)
# Show the image
cv2.imshow('img', img)
# Wait, and allow the user to quit with the 'esc' key
k = cv2.waitKey(1)
# If user presses 's', go on to the next shape
if k == 115:
shapes.append(click_list)
click_list = []
# If user presses 'esc' break
if k == 27: break
# Clean up
cv2.destroyAllWindows()
I'm trying to run the opencv/samples/python2 provided code for Camshift using threads.I've created two objects for App() class which call the run method.
##
from threading import Thread
from multiprocessing import Process
import numpy as np
import cv2
import video
class App(object):
def __init__(self, video_src):
self.cam = video.create_capture(video_src)
ret, self.frame = self.cam.read()
cv2.namedWindow('camshift')
cv2.setMouseCallback('camshift', self.onmouse)
self.selection = None
self.drag_start = None
self.tracking_state = 0
self.show_backproj = False
def onmouse(self, event, x, y, flags, param):
x, y = np.int16([x, y]) # BUG
if event == cv2.EVENT_LBUTTONDOWN:
self.drag_start = (x, y)
self.tracking_state = 0
if self.drag_start:
if flags & cv2.EVENT_FLAG_LBUTTON:
h, w = self.frame.shape[:2]
xo, yo = self.drag_start
x0, y0 = np.maximum(0, np.minimum([xo, yo], [x, y]))
x1, y1 = np.minimum([w, h], np.maximum([xo, yo], [x, y]))
self.selection = None
if x1-x0 > 0 and y1-y0 > 0:
self.selection = (x0, y0, x1, y1)
else:
self.drag_start = None
if self.selection is not None:
self.tracking_state = 1
def show_hist(self):
bin_count = self.hist.shape[0]
bin_w = 24
img = np.zeros((256, bin_count*bin_w, 3), np.uint8)
for i in xrange(bin_count):
h = int(self.hist[i])
cv2.rectangle(img, (i*bin_w+2, 255), ((i+1)*bin_w-2, 255-h), (int(180.0*i/bin_count), 255, 255), -1)
img = cv2.cvtColor(img, cv2.COLOR_HSV2BGR)
cv2.imshow('hist', img)
def run(self):
while True:
ret, self.frame = self.cam.read()
vis = self.frame.copy()
hsv = cv2.cvtColor(self.frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, np.array((0., 60., 32.)), np.array((180., 255., 255.)))
if self.selection:
x0, y0, x1, y1 = self.selection
self.track_window = (x0, y0, x1-x0, y1-y0)
hsv_roi = hsv[y0:y1, x0:x1]
mask_roi = mask[y0:y1, x0:x1]
hist = cv2.calcHist( [hsv_roi], [0], mask_roi, [16], [0, 180] )
cv2.normalize(hist, hist, 0, 255, cv2.NORM_MINMAX);
self.hist = hist.reshape(-1)
#self.show_hist()
vis_roi = vis[y0:y1, x0:x1]
cv2.bitwise_not(vis_roi, vis_roi)
vis[mask == 0] = 0
if self.tracking_state == 1:
self.selection = None
prob = cv2.calcBackProject([hsv], [0], self.hist, [0, 180], 1)
prob &= mask
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
track_box, self.track_window = cv2.CamShift(prob, self.track_window, term_crit)
print track_box[0]
if self.show_backproj:
vis[:] = prob[...,np.newaxis]
try: cv2.ellipse(vis, track_box, (0, 0, 255), 2)
except: print track_box
cv2.imshow('camshift', vis)
ch = 0xFF & cv2.waitKey(5)
if ch == 27:
break
if ch == ord('b'):
self.show_backproj = not self.show_backproj
cv2.destroyAllWindows()
if __name__ == '__main__':
import sys
try: video_src = sys.argv[1]
except: video_src = 0
print __doc__
left=App(1)# 1=device id for left camera
right=App(2)#2=device id for right camera
threadLeft=Process(target=left.run())
threadRight=Process(target=right.run())
threadRight.start()
threadLeft.start()
On execution , two windows appear one for right and other for left.However the onmouse() is not being called when i'm dragging the mouse on either of the windows.Seems that the windows freeze.Following which, on closing either one of them , the pattern which i made previously on the window to track any object , gets used in the second unclosed window automatically.
So is there any other method with which i can call this code for my two camera's .Any help is appreciated.Thanks
To create a Process, give a function to target. In the above code, left.run() is executed, not passed to Process.
Change the code to this:
threadLeft = Process(target=left.run)
threadRight = Process(target=right.run)