[英]Python OpenCV: mouse callback for drawing rectangle
I want to save an image from the video stream and then draw a rectangle onto the shown image to produce a region of interest.我想保存视频 stream 中的图像,然后在显示的图像上绘制一个矩形以生成感兴趣区域。 Later, save that ROI in a file.
稍后,将该 ROI 保存在一个文件中。 I used opencv python grabcut example to use the setMouseCallback function. But I don't know what I'm doing incorrect as it is not giving the result I expect.
我使用 opencv python grabcut 示例来使用 setMouseCallback function。但我不知道我做错了什么,因为它没有给出我期望的结果。 I would like to see the green rectangle drawn on the static image shown in
mouse input
window and the roi being saved to file.我想看到在
mouse input
window 中显示的 static 图像上绘制的绿色矩形,并将 roi 保存到文件中。 Please help debug this code or show a better approach:请帮助调试此代码或展示更好的方法:
import cv2
rect = (0,0,1,1)
rectangle = False
rect_over = False
def onmouse(event,x,y,flags,params):
global sceneImg,rectangle,rect,ix,iy,rect_over
# Draw Rectangle
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
if rectangle == True:
cv2.rectangle(sceneImg,(ix,iy),(x,y),(0,255,0),2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
rect_over = True
cv2.rectangle(sceneImg,(ix,iy),(x,y),(0,255,0),2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
x1,y1,w,h = rect
roi = sceneImg[y1:y1+h, x1:x1+w]
cv2.imwrite('roi.jpg', roi)
# Named window and mouse callback
cv2.namedWindow('video')
cv2.namedWindow('mouse input')
cv2.setMouseCallback('mouse input',onmouse)
camObj = cv2.VideoCapture(-1)
keyPressed = None
running = True
scene = False
# Start video stream
while running:
readOK, frame = camObj.read()
keyPressed = cv2.waitKey(5)
if keyPressed == ord('s'):
scene = True
cv2.imwrite('sceneImg.jpg',frame)
sceneImg = cv2.imread('sceneImg.jpg')
cv2.destroyWindow('video')
cv2.imshow('mouse input', sceneImg)
elif keyPressed == ord('r'):
scene = False
cv2.destroyWindow('mouse input')
elif keyPressed == ord('q'):
running = False
if not scene:
cv2.imshow('video', frame)
cv2.destroyAllWindows()
camObj.release()
You need to reset the image everytime when the {event == cv2.EVENT_MOUSEMOVE:} called.每次调用 {event == cv2.EVENT_MOUSEMOVE:} 时都需要重置图像。
Your code should look something like this:您的代码应如下所示:
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
if rectangle == True:
sceneImg = sceneImg2.copy()
cv2.rectangle(sceneImg,(ix,iy),(x,y),(0,255,0),2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
rect_over = True
cv2.rectangle(sceneImg,(ix,iy),(x,y),(0,255,0),2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
This is my current work around where I again render the mouse input
window upon EVENT_LBUTTONUP
.这是我当前的工作,我再次在
EVENT_LBUTTONUP
渲染mouse input
窗口。 To avoid the bounding box showing up in the ROI saved to file I use a copy of the inputed scene:为了避免在保存到文件的 ROI 中出现边界框,我使用了输入场景的副本:
import cv2
rect = (0,0,1,1)
rectangle = False
rect_over = False
def onmouse(event,x,y,flags,params):
global sceneImg,rectangle,rect,ix,iy,rect_over, roi
# Draw Rectangle
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
if rectangle == True:
# cv2.rectangle(sceneCopy,(ix,iy),(x,y),(0,255,0),1)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
rect_over = True
sceneCopy = sceneImg.copy()
cv2.rectangle(sceneCopy,(ix,iy),(x,y),(0,255,0),1)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
roi = sceneImg[rect[1]:rect[1]+rect[3], rect[0]:rect[0]+rect[2]]
cv2.imshow('mouse input', sceneCopy)
cv2.imwrite('roi.jpg', roi)
# Named window and mouse callback
cv2.namedWindow('mouse input')
cv2.setMouseCallback('mouse input',onmouse)
cv2.namedWindow('video')
camObj = cv2.VideoCapture(-1)
keyPressed = None
running = True
scene = False
# Start video stream
while running:
readOK, frame = camObj.read()
keyPressed = cv2.waitKey(5)
if keyPressed == ord('s'):
scene = True
cv2.destroyWindow('video')
cv2.imwrite('sceneImg.jpg',frame)
sceneImg = cv2.imread('sceneImg.jpg')
cv2.imshow('mouse input', sceneImg)
elif keyPressed == ord('r'):
scene = False
cv2.destroyWindow('mouse input')
elif keyPressed == ord('q'):
running = False
if not scene:
cv2.imshow('video', frame)
cv2.destroyAllWindows()
camObj.release()
Thus, I can visualize the rectangle which is supposed to bound the ROI but I still don't know how to visualize the bounding box while the mouse left button is down and the mouse cursor is moving.因此,我可以可视化应该绑定 ROI 的矩形,但我仍然不知道如何在鼠标左键按下且鼠标光标移动时可视化边界框。 That visualization works in the grabcut example but I couldn't figure it out in my case.
该可视化在抓取示例中有效,但在我的案例中我无法弄清楚。 Upon uncommenting the line for drawing rectangle during
EVENT_MOUSEMOVE
I get multiple rectangles drawn onto the image.在
EVENT_MOUSEMOVE
期间取消注释绘制矩形的线后,我会在图像上绘制多个矩形。 If someone answers with a way to visualize a single rectangle as it is being created I can accept it.如果有人回答了一种在创建时可视化单个矩形的方法,我可以接受。
You can use built in function provided by opencv: 您可以使用opencv提供的内置功能:
roi = cv2.selectROI(image)
It creates/displays "ROI selection" window, shows frame. 它创建/显示“ ROI选择”窗口,显示框架。 Use mouse to select bounding box dragging mouse pointer from top-left to bottom-right corner.
使用鼠标选择边界框,将鼠标指针从左上角拖动到右下角。
The function has a couple of not required parameters: 该函数有几个不需要的参数:
selectROI(windowName, img[, showCrosshair[, fromCenter]]) -> retval
windowName
name of the window where selection process will be shown. windowName
将显示选择过程的窗口的名称。 img
image to select a ROI. img
图像以选择ROI。 showCrosshair
if true crosshair of selection rectangle will be shown. showCrosshair
。 fromCenter
if true center of selection will match initial mouse position. fromCenter
参数,如果选择的真实中心将与鼠标的初始位置匹配。 In opposite case a corner of selection rectangle will correspond to the initial mouse position. returns selected ROI or empty rect if selection canceled. 返回选定的ROI或空白矩形(如果取消选择)。
Controls: use space
or enter
to finish selection, use key c
to cancel selection. 控制:使用
space
或enter
键完成选择,使用键c
取消选择。
Overloaded function doesn't require windowName
. 重载的函数不需要
windowName
。
selectROI(img[, showCrosshair[, fromCenter]]) -> retval
Building on top of answer provided by @Marco167, I will just change one line as otherwise there's object reference problem.基于@Marco167 提供的答案,我将只更改一行,否则会出现 object 参考问题。
So, instead of sceneImg = sceneImg2.copy()
I'd suggest sceneImg[:] = sceneImg2[:]
, where sceneImg2
should be same, separately loaded image, like:因此,我建议
sceneImg[:] = sceneImg2[:]
而不是sceneImg = sceneImg2.copy()
,其中sceneImg2
应该是相同的,单独加载的图像,例如:
sceneImg = cv2.imread('sceneImg.jpg')
sceneImg2 = cv2.imread('sceneImg.jpg')
Also, I've moved rectangle
check to the condition.此外,我已将
rectangle
检查移至该条件。
This way on mouse move, you first redraw the original image (thus removing the existing rectangle) and draw rectangle.这种方式在鼠标移动时,首先重绘原始图像(从而删除现有矩形)并绘制矩形。 Moving the mouse by even a pixel you again redraw the original picture removing any rectangle and draw again one rectangle.
将鼠标移动一个像素,您将再次重绘原始图片,删除任何矩形并再次绘制一个矩形。 At any given point there will be just one rectangle.
在任何给定点,都只有一个矩形。
Yup, replying after over 7 years, just in case anyone would ever find it useful:)是的,7 年多后回复,以防万一有人发现它有用:)
Putting it all together:把它们放在一起:
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE and rectangle :
sceneImg[:] = sceneImg2[:]
cv2.rectangle(sceneImg,(ix,iy),(x,y),(0,255,0),2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
rect_over = True
cv2.rectangle(sceneImg,(ix,iy),(x,y),(0,255,0),2)
rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.