简体   繁体   中英

Drawing a line on an image using mouse clicks with Python OpenCV library

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.

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:

  • event : Event that took place (left/right pressed or released mouse click)
  • x : The x-coordinate of event
  • y : The y-coordinate of event
  • flags : Relevant flags passed by OpenCV
  • Parameters : Extra parameters passed by OpenCV

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)

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
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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM