I've got a fresh install of OpenCV 3.2 with contribs, ffmpeg, and numpy. However, when I try to use the function selectROI I get an attribute error and I cannot figure out why!!!
I've tried to reinstall opencv and opencv-contrib however it doesn't seem to change anything.
import numpy as np
import ffmpy
import cv2
import os
def main():
...
r=0
cap = cv2.VideoCapture(filename)
...
while cap.grab():
...
if (frame_count>=next_valid):
# Initialisation of supporting variables
flag, frame = cap.retrieve()
if (go_around==0):
# Select ROI
r = cv2.selectROI(frame)
# Cropping and Brightening
imCrop = im[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
...
main()
I just wish I could make a selectable ROI and store the dimensions!
An adaptation of nathancy's response that worked better for me,
class SelectROI(object):
def __init__(self, name, im):
self.image = im
self.winname = name
cv2.namedWindow(name)
self.coords = []
self.dragging = False
self._update()
def _mouse_cb(self, event, x, y, flags, parameters):
# Record starting (x,y) coordinates on left mouse button click
if event == cv2.EVENT_LBUTTONDOWN:
self.coords[:] = [(x, y)]
self.dragging = True
elif event == 0 and self.dragging:
self.coords[1:] = [(x, y)]
# Record ending (x,y) coordintes on left mouse bottom release
elif event == cv2.EVENT_LBUTTONUP:
self.coords[1:] = [(x, y)]
self.dragging = False
xs, ys = list(zip(*self.coords))
self.coords = [(min(xs), min(ys)),
(max(xs), max(ys))]
print('roi:', self.coords)
# Clear drawing boxes on right mouse button click
elif event == cv2.EVENT_RBUTTONDOWN:
self.coords = []
self.dragging = False
self._update()
def _update(self):
im = self.image.copy()
if len(self.coords) == 2:
cv2.rectangle(im, self.coords[0], self.coords[1], (0, 255, 0), 2)
cv2.imshow(self.winname, im)
def __call__(self):
cv2.setMouseCallback(self.winname, self._mouse_cb)
cv2.waitKey()
cv2.destroyWindow(self.winname)
return self.coords if self.coords else None
def select_roi(name, im):
s=SelectROI(name, im)
return s()
You could always make your own custom ROI selector
import cv2
class ExtractImageWidget(object):
def __init__(self):
self.original_image = cv2.imread('1.jpg')
# Resize image, remove if you want raw image size
self.original_image = cv2.resize(self.original_image, (640, 556))
self.clone = self.original_image.copy()
cv2.namedWindow('image')
cv2.setMouseCallback('image', self.extract_coordinates)
# Bounding box reference points and boolean if we are extracting coordinates
self.image_coordinates = []
self.extract = False
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)]
self.extract = True
# Record ending (x,y) coordintes on left mouse bottom release
elif event == cv2.EVENT_LBUTTONUP:
self.image_coordinates.append((x,y))
self.extract = False
print('top left: {}, bottom right: {}'.format(self.image_coordinates[0], self.image_coordinates[1]))
# Draw rectangle around ROI
cv2.rectangle(self.clone, self.image_coordinates[0], self.image_coordinates[1], (0,255,0), 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__':
extract_image_widget = ExtractImageWidget()
while True:
cv2.imshow('image', extract_image_widget.show_image())
key = cv2.waitKey(1)
# Close program with keyboard 'q'
if key == ord('q'):
cv2.destroyAllWindows()
exit(1)
Related
I was trying to draw rectangles using mouse over an image, using openCV package in python. When ever I drew a rectangle, I got multiple rectangles overlapping one another, instead of a single rectangle. Like the below image
Here is my code. Please tell me where I went wrong and what needs to be corrected, so that I get only 1 rectangle.
import cv2
import numpy as np
drawing = False
ix,iy = -1, -1
img = cv2.imread('drawing_over_image/dog.jpg')
def draw(event, x, y, flags, params):
global ix, iy, drawing
if event == cv2.EVENT_LBUTTONDOWN:
ix,iy = x,y
drawing = True
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
if ix < x and iy < y:
cv2.rectangle(img=img, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
cv2.rectangle(img=img, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
if __name__ == "__main__":
while True:
cv2.imshow(winname='image', mat=img)
cv2.setMouseCallback('image', draw)
if cv2.waitKey(1) == 27:
cv2.destroyAllWindows()
break
Its drawing multiple rectangles because you are drawing rectangles on every mouse move after user is pressing on the button. Instead you should draw whenever the event is done namely when the user release the left button. I fixed your code and add a basic ref image for you to see your rectangle when you are drawing. Hope it helps!
import cv2
import numpy as np
drawing = False
ix,iy = -1, -1
img = cv2.imread('drawing_over_image/dog.jpg')
refimg = img.copy()
def draw(event, x, y, flags, params):
global ix, iy, drawing
if event == cv2.EVENT_LBUTTONDOWN:
ix,iy = x,y
drawing = True
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
if ix < x and iy < y:
cv2.rectangle(img=refimg, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
if ix < x and iy < y:
cv2.rectangle(img=img, pt1=(ix,iy), pt2=(x,y), color=[255,0,0], thickness=1)
if __name__ == "__main__":
while True:
cv2.imshow(winname='image', mat=refimg)
cv2.setMouseCallback('image', draw)
refimg = img.copy()
if cv2.waitKey(1) & 0xFF == 27:
cv2.destroyAllWindows()
break
Output:
You have two options:
use cv.selectROI()
do it all by hand
with selectROI()
rect = cv.selectROI("image", img, False)
cv.destroyWindow("image")
print(rect) # (x,y,w,h) or (0,0,0,0)
by hand
keep the original image
while dragging, from the event handler, draw on a copy, imshow() that
handle mousedown, mousemove, mouseup
call separate completion and cancelation functions to make it neat
sel1 = None # first point
sel2 = None # second point
def on_mouse(event, x, y, flags, params):
global sel1, sel2
if event == cv2.EVENT_LBUTTONDOWN:
sel1 = (x,y)
sel2 = None
elif event == cv2.EVENT_RBUTTONDOWN:
cancel_selection()
elif event == cv2.EVENT_MOUSEMOVE:
if sel1 is not None:
canvas = img.copy()
cv2.rectangle(canvas, sel1, (x,y), color=[255,0,0], thickness=2)
cv2.imshow("image", canvas)
elif event == cv2.EVENT_LBUTTONUP:
if sel1 is not None:
sel2 = (x,y)
selection_done(sel1, sel2)
sel1 = sel2 = None
def cancel_selection():
global sel1
sel1 = None
# restore display
cv2.imshow("image", img)
def selection_done(pt1, pt2):
# draw on the source image?
cv2.rectangle(img, pt1, pt2, color=[255,255,0], thickness=2)
cv2.imshow("image", img)
if __name__ == "__main__":
cv2.imshow('image', img)
cv2.setMouseCallback('image', on_mouse)
while True:
key = cv2.waitKey()
if key == 27: # ESC cancels ongoing selection, or exits the window
if sel1 is None:
break
else:
cancel_selection()
cv2.destroyAllWindows()
In my code after certain time (depend of the PC or OS) crashes. The error displayed is
libpng warning: Image width is zero in IHDR
libpng warning: Image height is zero in IHDR
libpng error: Invalid IHDR data
My code draw ROI on a video from webcam and take a snapshot. The ROI is deleted after 5-10 minutes (I'm not sure why, this is the error) and the picture saved not have dimensions then the error above is displayed but I'm not find the error. The instructions are, press "c" to stop the video and draw a ROI, press again "c" to take the picture, press "r" to resume the video, and repeat if you need another pictures.
I tested the code in Windows 10 and raspbian on a raspberry and the time is not the same but on both the program crashes. This is my code:
import cv2
# python PSI_Camera_test.py
class staticROI(object):
def __init__(self):
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
# Bounding box reference points and boolean if we are extracting coordinates
self.image_coordinates = []
self.extract = False
self.selected_ROI = False
self.img_counter = 0
self.update()
def update(self):
while True:
if self.capture.isOpened():
# Read frame
(self.status, self.frame) = self.capture.read()
cv2.imshow('image', self.frame)
key = cv2.waitKey(2)
# Crop image
if key == ord('c'):
self.clone = self.frame.copy()
cv2.namedWindow('image')
cv2.setMouseCallback('image', self.extract_coordinates)
while True:
key = cv2.waitKey(2)
cv2.imshow('image', self.clone)
# Crop and display cropped image
if key == ord('c'):
self.crop_ROI()
img_name = "Images\opencv_frame_{}.png".format(
self.img_counter)
cv2.imwrite(img_name, self.cropped_image)
print("{} written!".format(img_name))
self.img_counter += 1
# Resume video
if key == ord('r'):
break
# Close program with keyboard 'esp'
if key % 256 == 27:
cv2.destroyAllWindows()
exit(1)
else:
pass
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)]
self.extract = True
# Record ending (x,y) coordintes on left mouse bottom release
elif event == cv2.EVENT_LBUTTONUP:
self.image_coordinates.append((x, y))
self.extract = False
self.selected_ROI = True
# Draw rectangle around ROI
cv2.rectangle(
self.clone, self.image_coordinates[0], self.image_coordinates[1], (0, 255, 0), 2)
# Clear drawing boxes on right mouse button click
elif event == cv2.EVENT_RBUTTONDOWN:
self.clone = self.frame.copy()
self.selected_ROI = False
def crop_ROI(self):
if self.selected_ROI:
self.cropped_image = self.frame.copy()
x1 = self.image_coordinates[0][0]
y1 = self.image_coordinates[0][1]
x2 = self.image_coordinates[1][0]
y2 = self.image_coordinates[1][1]
self.cropped_image = self.cropped_image[y1:y2, x1:x2]
print('Cropped image: {} {}'.format(
self.image_coordinates[0], self.image_coordinates[1]))
else:
print('Select ROI to crop before cropping')
# python PSI_Camera_test.py
if __name__ == '__main__':
static_ROI = staticROI()
Thanks in advance!
So during my webcam/video I would like to when I click my mouse, a rectangle is drawn at the mouse position, and the rectangle size is fixed ex. 80 X 80. In my current code, the rectangle follows the mouse but the size is always different. I want a fixed size exactly at the mouse position when I Click on a frame in the video.
Here is my code.
import os
import numpy as np
import cv2
from PIL import Image
import re
print('kaishi')
flag=0
drawing = False
point1 = ()
point2 = ()
ref_point = []
xvalues=[];
yvalues=[];
ref_point = []
cx=0;
cy=0;
def mouse_drawing(event, x, y, flags, params):
global point1, point2,
drawing,ref_point2,flag,refPt,cropping,cx,cy
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
point1 = (x, y)
xvalues.append(x)
yvalues.append(y)
cx =x;
cy=y;
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point2 = (x, y)
elif event == cv2.EVENT_LBUTTONUP:
flag+=1;
print('finished square')
cap = cv2.VideoCapture(0)
cv2.namedWindow("Frame")
cv2.setMouseCallback("Frame", mouse_drawing)
while True:
_, frame = cap.read()
if point1 and point2 :
cv2.rectangle(frame,(200,cy),(cx,128),(0,0,255),0)
print(cx,cy)
flag=0;
cv2.imshow("Frame", frame)
key = cv2.waitKey(25)
if key== 13:
print('done')
elif key == 27:
break
cap.release()
cv2.destroyAllWindows()
The problem is that you fixed a point of the rectangle and the other point follows the mouse. In your code it would be here:
cv2.rectangle(frame,(200,cy),(cx,128),(0,0,255),0)
Now, it will depend how the rectangle will be, is the point where you click the top left point? if it is then it should be something like:
cv2.rectangle(frame,(cx,cy),(cx + 80, cy +80),(0,0,255),0)
This example will be for a 80 x 80 rectangle.... In your code this will happen when you click.
However, your code has a lot of unused code... I would do something like this:
import numpy as np
import cv2
drawing = False
point = (0,0)
def mouse_drawing(event, x, y, flags, params):
global point, drawing
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
point= (x, y)
cap = cv2.VideoCapture(0)
cv2.namedWindow("Frame")
cv2.setMouseCallback("Frame", mouse_drawing)
while True:
_, frame = cap.read()
if drawing :
cv2.rectangle(frame,point,(point[0]+80, point[1]+80),(0,0,255),0)
cv2.imshow("Frame", frame)
key = cv2.waitKey(25)
if key== 13:
print('done')
elif key == 27:
break
cap.release()
cv2.destroyAllWindows()
If you want the rectangle to follow your mouse after you clicked and to stop following after you release the button, change in the code I supplied before the mouse_drawing function like this:
def mouse_drawing(event, x, y, flags, params):
global point, drawing
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
point = (x, y)
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point = (x, y)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
point = (x, y)
I have been trying to make an OpenCV-Py program to draw rectangle, line, and circle on mouse click and drag. I could successfully do it for line and rectangle but the code for the circle is wrong and I need help with that.
import numpy as np
import cv2 as cv
import math
drawing = False # true if mouse is pressed
ix,iy = -1,-1
# mouse callback function
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
k = cv.waitKey(33)
if k == ord('r'):
cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
elif k==ord('c'):
cv.circle(img,int(((ix+x)/2,(iy+y)/2)),int(math.sqrt( ((ix-x)**2)+((iy-y)**2) )),(0,0,255),-1)
elif k== ord('l'):
cv.line(img,(ix,iy),(x,y),(255,0,0),5)
elif event == cv.EVENT_LBUTTONUP:
drawing = False
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
cv.imshow('image',img)
k = cv.waitKey(1) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
ERROR: Traceback (most recent call last):
File "mouse.py", line 19, in draw_circle
cv.circle(img,int(((ix+x)/2,(iy+y)/2)),int(math.sqrt( ((ix-x)**2)+((iy-y)**2) )),(0,0,255),-1)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'tuple'
To dynamically draw a circle with OpenCV,
import numpy as np
import cv2
import math
drawing = False # true if mouse is pressed
ix,iy = -1,-1
# Create a function based on a CV2 Event (Left button click)
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
# we take note of where that mouse was located
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
drawing == True
elif event == cv2.EVENT_LBUTTONUP:
radius = int(math.sqrt( ((ix-x)**2)+((iy-y)**2)))
cv2.circle(img,(ix,iy),radius,(0,0,255), thickness=1)
drawing = False
# Create a black image
img = np.zeros((512,512,3), np.uint8)
# This names the window so we can reference it
cv2.namedWindow('image')
# Connects the mouse button to our callback function
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
# EXPLANATION FOR THIS LINE OF CODE:
# https://stackoverflow.com/questions/35372700/whats-0xff-for-in-cv2-waitkey1/39201163
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
# Once script is done, its usually good practice to call this line
# It closes all windows (just in case you have multiple windows called)
cv2.destroyAllWindows()
This is how to draw the circle dynamically.
As you drag the mouse you can see the size of the circle dynamically change.
import math
import numpy as np
import cv2 as cv
cv.namedWindow('image', cv.WND_PROP_ASPECT_RATIO)
drawing = False # true if mouse is pressed
# Coordinate
x1, y1, x2, y2 = -1, -1, -1, -1
def run():
img = cv.imread(f'image/python.png')
cv.imshow('image', img)
# Create a layer to draw circle. The layer has the same dimension of image
layer = np.zeros((img.shape[0], img.shape[1], 3), dtype="uint8")
# mouse callback function
def draw_circle(event, x, y, flags, param):
global x1, y1, x2, y2, drawing
# Manage different button state
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
x1, y1 = x, y
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
# Fill all value to 0 to clean layer
layer.fill(0)
cv.circle(layer, (x1, y1),calc_radius(x1, y1, x, y), (255, 0, 0), 1)
# Create a mask of shape
img2gray = cv.cvtColor(layer, cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 0, 255, cv.THRESH_BINARY)
# Create a copy of original image
_img = img.copy()
# Set the value of mask to 0, to avoid color overlap problems
_img[np.where(mask)] = 0
cv.imshow('image', np.where(layer == 0, _img, layer))
elif event == cv.EVENT_LBUTTONUP:
drawing = False
layer.fill(0)
cv.circle(layer, (x1, y1), calc_radius(x1, y1, x, y), (255, 0, 0), 1)
# Create a mask of shape
img2gray = cv.cvtColor(layer, cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 0, 255, cv.THRESH_BINARY)
_img = img.copy()
# Set the value of mask to 0, to avoid color overlap problems
_img[np.where(mask)] = 0
# Merge two array using Numpy where function
cv.imshow('image', np.where(layer == 0, _img, layer))
# Assig callback
cv.setMouseCallback('image', draw_circle)
# Service function to calculate radius (Pythagorean theorem)
def calc_radius(x1, y1, x2, y2):
delta_x = abs(x2 - x1)
delta_y = abs(y2 - y1)
return int(math.sqrt((delta_x**2)+(delta_y**2)))
while True:
k = cv.waitKey(1)
if k == ord("c"): # c to terminate a program
cv.destroyAllWindows()
break
if __name__ == '__main__':
run()
I need to draw a rectangle of fixed size in image when left mouse button is clicked, and change its location in the image by moving the mouse.
I came up with this code, which basically draws rectangle in image, where mouse pointer is intersection point of diagonals of rectangle.
import cv2 as cv
import numpy as np
#flags
move_rectangle = False
#color for rectangle
BLUE = [255,0,0]
def mouse(event,x,y,flags,params):
global move_rectangle, BLUE
#draw rectangle where x,y is rectangle center
if event == cv.EVENT_LBUTTONDOWN:
move_rectangle = True
elif event == cv.EVENT_MOUSEMOVE:
if move_rectangle:
cv.rectangle(bg,(x-int(0.5*cols),y-int(0.5*rows)),
(x+int(0.5*cols),y+int(0.5*rows)),BLUE, -1)
elif event == cv.EVENT_LBUTTONUP:
move_rectangle = False
cv.rectangle(bg,(x-int(0.5*cols),y-int(0.5*rows)),
(x+int(0.5*cols),y+int(0.5*rows)),BLUE, -1)
if __name__ == '__main__':
#loading images
fg = cv.imread('sample.jpg')
bg = cv.imread('beach.jpg')
#grabing height and width of foreground image
rows, cols = fg.shape[:2]
cv.namedWindow('draw')
cv.setMouseCallback('draw', mouse)
while True:
cv.imshow('draw', bg)
k = cv.waitKey(1)
#waiting for esc to exit
if k == 27 & 0xFF:
break
cv.destroyAllWindows()
However, i can't figure out how to make it draw rectangle only in the position of mouse pointer. I need to get rid of that "shadow" behind front rectangle.
Shadow
How this can be done?
I believe what you need to do is simply reinitialize your image when you receive move event
import cv2 as cv
move_rectangle = False
BLUE = [255,0,0]
fg = cv.imread('sample.jpg')
bg = cv.imread('beach.jpg')
bgCopy = bg.copy()
def mouse(event,x,y,flags,params):
global move_rectangle, BLUE, fg, bg, bgCopy
#draw rectangle where x,y is rectangle center
if event == cv.EVENT_LBUTTONDOWN:
move_rectangle = True
elif event == cv.EVENT_MOUSEMOVE:
bg = bgCopy.copy() #!! your image is reinitialized with initial one
if move_rectangle:
cv.rectangle(bg,(x-int(0.5*cols),y-int(0.5*rows)),
(x+int(0.5*cols),y+int(0.5*rows)),BLUE, -1)
elif event == cv.EVENT_LBUTTONUP:
move_rectangle = False
cv.rectangle(bg,(x-int(0.5*cols),y-int(0.5*rows)),
(x+int(0.5*cols),y+int(0.5*rows)),BLUE, -1)
if __name__ == '__main__':
rows, cols = fg.shape[:2]
cv.namedWindow('draw')
cv.setMouseCallback('draw', mouse)
while True:
cv.imshow('draw', bg)
k = cv.waitKey(1)
#waiting for esc to exit
if k == 27 & 0xFF:
break
cv.destroyAllWindows()