Reputation: 1643
I am trying to draw a line on an image using mouse operations. I am using OpenCV library. My code
import matplotlib.pyplot as plt
import cv2
src_window = 'CV2 Window'
SHOW_DEBUG_STEPS = True
drag = 0
select_flag = 0
x1 = 0
x2 = 0
y1 = 0
y2 = 0
point1 = [x1,y1]
point2 = [x2,y2]
SCALAR_YELLOW = (0.0,255.0,255.0)
cap = cv2.VideoCapture('example_01.mp4')
def closeAll():
cap.release()
cv2.destroyAllWindows()
def retn(ret):
if not ret:
print('Error reading the frame')
closeAll()
def frm(fFrame):
if fFrame is None:
print('Error reading the frame')
closeAll()
def drawMyLine(frame):
global point1
global point2
cv2.line(frame,(point1[0],point1[1]),(point2[0],point2[1]),SCALAR_YELLOW,2,8)
def myMouseHandler(event,x,y,flags,param):
global point1
global point2
global drag
global select_flag
global callback
if (event==cv2.EVENT_LBUTTONDOWN and not(drag) and not(select_flag)):
print('case 1')
point1=[x,y]
drag = 1
if (event == cv2.EVENT_MOUSEMOVE and drag and not(select_flag)):
print('case 2')
img1 = fFrame.copy()
point2 = [x,y]
drawMyLine(img1)
if (event == cv2.EVENT_LBUTTONUP and drag and not(select_flag)):
print('case 3')
img2 = fFrame.copy()
point2 = [x,y]
drag = 0
select_flag = 1
cv2.imshow(src_window,img2)
callback = 1
if not(cap.isOpened()):
print('Error reading the video')
ret,fFrame = cap.read()
retn(ret)
frm(fFrame)
fGray = cv2.cvtColor(fFrame,cv2.COLOR_BGR2GRAY)
cv2.imshow(src_window,fGray)
cv2.setMouseCallback(src_window,myMouseHandler)
cv2.waitKey(0)
When I run the code and try to draw a line by clicking the left mouse button, drag mouse to a second point and release the left mouse button, I see my print statements, case1, case2, case3 being printed in terminal. But the line is not coming up. I am not sure where am I going wrong.
Upvotes: 2
Views: 7635
Reputation: 1
import cv2
import numpy as np
def click_event(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),3,(255,0,0),-1)
points.append((x,y))
if len(points) >= 2:
cv2.line(img,points[-1],points[-2],(0,255,0),1)
cv2.imshow("image", img)
cv2.imshow("image",img)
points = []
cv2.setMouseCallback('image',click_event)
cv2.waitKey(0)
Upvotes: 0
Reputation: 46600
To draw lines on an image using mouse clicks, we must capture the event actions of a mouse click then record the starting and ending coordinates. OpenCV allows us to do this by processing mouse click events. Anytime a mouse click event is triggered, OpenCV will relay the information to our extract_coordinates
callback function by attaching it to the cv2.setMouseCallback
handler. In order to detect the event, OpenCV requires various arguments:
A pressed left click (cv2.EVENT_LBUTTONDOWN
) records the starting coordinates while a released left click (cv2.EVENT_LBUTTONUP
) records ending coordinates. We then draw a line with cv2.line
and print the coordinates to the console. A right click (cv2.EVENT_RBUTTONDOWN
) will reset the image. Here's a simple widget to draw lines on an image:
import cv2
class DrawLineWidget(object):
def __init__(self):
self.original_image = cv2.imread('1.jpg')
self.clone = self.original_image.copy()
cv2.namedWindow('image')
cv2.setMouseCallback('image', self.extract_coordinates)
# List to store start/end points
self.image_coordinates = []
def extract_coordinates(self, event, x, y, flags, parameters):
# Record starting (x,y) coordinates on left mouse button click
if event == cv2.EVENT_LBUTTONDOWN:
self.image_coordinates = [(x,y)]
# Record ending (x,y) coordintes on left mouse bottom release
elif event == cv2.EVENT_LBUTTONUP:
self.image_coordinates.append((x,y))
print('Starting: {}, Ending: {}'.format(self.image_coordinates[0], self.image_coordinates[1]))
# Draw line
cv2.line(self.clone, self.image_coordinates[0], self.image_coordinates[1], (36,255,12), 2)
cv2.imshow("image", self.clone)
# Clear drawing boxes on right mouse button click
elif event == cv2.EVENT_RBUTTONDOWN:
self.clone = self.original_image.copy()
def show_image(self):
return self.clone
if __name__ == '__main__':
draw_line_widget = DrawLineWidget()
while True:
cv2.imshow('image', draw_line_widget.show_image())
key = cv2.waitKey(1)
# Close program with keyboard 'q'
if key == ord('q'):
cv2.destroyAllWindows()
exit(1)
Upvotes: 5
Reputation: 2741
There are several problems with your code.
1) img1 = fFrame.copy()
instantiates img1
and then you draw on it, and as a local variable you never use it again, causing you to lose what you drew. Instead, draw on the actual frame as below.
if (event == cv2.EVENT_MOUSEMOVE and drag and not(select_flag)):
print('case 2')
point2 = [x,y]
drawMyLine(fFrame)
2) After drawing the current line, you should update the beginning of your next line (next point1
) to be the end of the current line (current point2
).
if (event == cv2.EVENT_MOUSEMOVE and drag and not(select_flag)):
print('case 2')
point2 = [x,y]
drawMyLine(fFrame)
point1 = [x,y] # <-- update for next draw
3) This is optional, you can directly show the current frame, instead of img2
.
if (event == cv2.EVENT_LBUTTONUP and drag and not(select_flag)):
print('case 3')
point2 = [x,y]
drag = 0
select_flag = 1
cv2.imshow(src_window,fFrame)
callback = 1
4) This is also optional, but in order to correctly draw the last line at the end of the click, you should call the draw function for the last time in that case.
if (event == cv2.EVENT_LBUTTONUP and drag and not(select_flag)):
print('case 3')
point2 = [x,y]
drawMyLine(fFrame) # <-- draw the last line
drag = 0
select_flag = 1
cv2.imshow(src_window,fFrame)
callback = 1
Overall, the rest of the code is the same, and the updated mouse handler is below.
def myMouseHandler(event,x,y,flags,param):
global drag
global select_flag
global callback
global point1
global point2
if (event==cv2.EVENT_LBUTTONDOWN and not(drag) and not(select_flag)):
print('case 1')
point1=[x,y]
drag = 1
if (event == cv2.EVENT_MOUSEMOVE and drag and not(select_flag)):
print('case 2')
point2 = [x,y]
drawMyLine(fFrame)
point1 = [x,y]
if (event == cv2.EVENT_LBUTTONUP and drag and not(select_flag)):
print('case 3')
point2 = [x,y]
drawMyLine(fFrame)
drag = 0
select_flag = 1
cv2.imshow(src_window,fFrame)
callback = 1
Upvotes: 0