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!
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()
Here, I have a code that takes a video as input and let the user draw a ROI. Later the cropped video will be displayed. This code was originally taken from I would like to define a Region of Interest in a video and only process that area.
I have provided the code below.
Question: I would like to save the cropped video in .mp4 format. The video output shows only a 1kb file. Can you go through the code and suggest a solution?
NB: I went through answer provided at OpenCV video not getting saved. Still I have failed to figure out the error.
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
#here, it is the copy of the first frame.
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))
print(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(r'E:\cardiovascular\brad1low.mp4')
#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]]
print(f'first frame roi is {first_frame_roi}')
#An empty image of full size just for visualization of difference
difference_image_canvas = np.zeros_like(first_frame)
out = cv2.VideoWriter(r'E:\cardiovascular\filename2.mp4', cv2.VideoWriter_fourcc(*'MP4V'), int(cap.get(cv2.CAP_PROP_FRAME_COUNT)), (int(first_frame_roi.shape[0]), (int(first_frame_roi.shape[1]))))
while cap.isOpened():
ret, frame = cap.read()
if ret:
#ROI of current frame
roi = frame[point1[1]:point2[1], point1[0]:point2[0]]
print(f'roi is {roi}')
cv2.imshow(DIFFERENCE_WINDOW_TITLE, roi)
out.write(roi)
key = cv2.waitKey(30) & 0xff
if key == 27:
break
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
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)
I am using the opencv-library in python. I have opened an existing video file and wrote a little script that allows me to draw a rectangle anywhere in the video. The problem is: I want to draw this rectangle on the first frame of the video and then leave it there to mark a Region of Interest for me.
I am using cv2.imshow(winname, frame) to show my video. Since this runs/shows the video with a very high framerate per second (and I don't want to change that, since my video is pretty long), when I start drawing the rectangle, many frames have already been shown.
Because I think this might be helpful, here is my code so far:
import cv2
#mouse callback function#
def draw_rectangle(event, x, y, flags, param):
global pt1, pt2, topLeft_clicked, bottomRight_clicked
#mouse click
if event == cv2.EVENT_LBUTTONDOWN:
#reset
if topLeft_clicked and bottomRight_clicked:
topLeft_clicked = False
bottomRight_clicked = False
pt1 = (0,0)
pt2 = (0,0)
#get coordinates of top left corner
if not topLeft_clicked:
pt1 = (x,y)
topLeft_clicked = True
#get coordinates of bottom right corner
elif not bottomRight_clicked:
pt2 = (x,y)
bottomRight_clicked = True
#start actual program
#initially we haven't drawn anything
pt1 = (0,0)
pt2 = (0,0)
topLeft_clicked = False
bottomRight_clicked = False
#capture video
cap = cv2.VideoCapture('Path to video')
cv2.namedWindow(winname='myName')
cv2.setMouseCallback('myName', draw_rectangle)
firstFrame = True
while True:
ret, frame = cap.read()
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
while ret and firstFrame:
cv2.imshow('myName', gray_frame)
if topLeft_clicked:
cv2.circle(gray_frame, center=pt1, radius=5, color=(255,0,0), thickness=-1)
if topLeft_clicked and bottomRight_clicked:
cv2.rectangle(gray_frame, pt1, pt2, (255,0,0), 2)
firstFrame = False
cv2.imshow('Estimate_Velocity', gray_frame)
if cv2.waitKey(1) &0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
I have experimented a little bit and found the following solution to my problem:
import cv2
#mouse callback function#
def draw_rectangle(event, x, y, flags, param):
global pt1, pt2, topLeft_clicked, bottomRight_clicked
#mouse click
if event == cv2.EVENT_LBUTTONDOWN:
#reset
if topLeft_clicked and bottomRight_clicked:
topLeft_clicked = False
bottomRight_clicked = False
pt1 = (0,0)
pt2 = (0,0)
#get coordinates of top left corner
if not topLeft_clicked:
pt1 = (x,y)
topLeft_clicked = True
#get coordinates of bottom right corner
elif not bottomRight_clicked:
pt2 = (x,y)
bottomRight_clicked = True
#start actual program
#initially we haven't drawn anything
pt1 = (0,0)
pt2 = (0,0)
topLeft_clicked = False
bottomRight_clicked = False
#capture video
cap = cv2.VideoCapture('Video path')
cv2.namedWindow(winname='myName')
cv2.setMouseCallback('myName', draw_rectangle)
firstFrame = True
while True:
ret, frame = cap.read()
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
while firstFrame:
cv2.imshow('myName', gray_frame)
if topLeft_clicked:
cv2.circle(gray_frame, center=pt1, radius=5, color=(255,0,0), thickness=-1)
if topLeft_clicked and bottomRight_clicked:
cv2.rectangle(gray_frame, pt1, pt2, (255,0,0), 2)
if cv2.waitKey(1) &0xFF == ord('c'):
firstFrame = False
break
if topLeft_clicked and bottomRight_clicked:
cv2.rectangle(gray_frame, pt1, pt2, (255,0,0), 2)
cv2.imshow('myName', gray_frame)
if cv2.waitKey(1) &0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
I haven’t tested this yet, but you can modify your while loop break condition:
if cv2.waitKey(1) &0xFF == ord('q'):
break
to
if (topLeft_clicked and bottomRight_clicked):
if cv2.waitKey(1) &0xFF == ord('q'):
break
else:
# change 1 to 0
cv2.waitKey(0)
You will need to press a key after you draw the box.
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)