How to save video Python 3.5 - python-3.x

I have a video 256x300 uint8 (gray). I am tracking an object and displaying a rectangle in the video. I'd like to save that video (with the rectangle displayed). I tried some examples but they are all for RGB video.
import cv2
import sys
import subprocess as sp
if __name__ == '__main__' :
# Set up tracker.
# Instead of MIL, you can also use
# BOOSTING, KCF, TLD, MEDIANFLOW or GOTURN
tracker = cv2.Tracker_create("MIL")
# Read video
video = cv2.VideoCapture("videotest.avi")
# Exit if video not opened.
if not video.isOpened():
print("Could not open video")
sys.exit()
# Read first frame.
ok, frame = video.read()
if not ok:
print("Cannot read video file")
sys.exit()
# Define an initial bounding box
bbox = (90, 10, 30, 30)
# Uncomment the line below to select a different bounding box
# bbox = cv2.selectROI(frame, False)
# Initialize tracker with first frame and bounding box
ok = tracker.init(frame, bbox)
while True:
# Read a new frame
ok, frame = video.read()
if not ok:
break
# Update tracker
ok, bbox = tracker.update(frame)
# Draw bounding box
if ok:
p1 = (int(bbox[0]), int(bbox[1]))
p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
cv2.rectangle(frame, p1, p2, (0,0,255))
# Display result
cv2.imshow("Tracking", frame)
# Exit if ESC pressed
k = cv2.waitKey(1) & 0xff
if k == 27 : break
video.release()
cv2.destroyAllWindows()

Related

How to update FPS dynamically on window title of cv2 in Python

I am using python opencv to do some video related work. I am also calculating the FPS and showing it on top left corner of the cv2 window. Now instead of showing it in top left corner, I want to show it on window title. Below is the code:
import cv2
import datetime
import imutils
def GetCoord(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
print("X: {} | Y: {}".format(x, y))
winName = "My Project"
cv2.namedWindow(winName)
cv2.setMouseCallback(winName, GetCoord)
cap = cv2.VideoCapture(0)
fps_start_time = datetime.datetime.now()
fps = 0
total_frames = 0
while True:
ret, frame = cap.read()
frame = imutils.resize(frame, width=800)
total_frames = total_frames + 1
fps_end_time = datetime.datetime.now()
time_diff = fps_end_time - fps_start_time
if time_diff.seconds == 0:
fps = 0.0
else:
fps = (total_frames / time_diff.seconds)
fps_text = "FPS: {:.2f}".format(fps)
cv2.putText(frame, fps_text, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
cv2.imshow(winName, frame)
key = cv2.waitKey(1)
if key == ord('q'):
break
cv2.destroyAllWindows()
Instead of showing it on top left corner, I want to do something like the below:
cv2.imshow(winName + " FPS: {}".format(fps_text), frame)
But doing so, the application performs very strangely and keeps opening a new windows. Is there a way to achieve this?
The Cause of the Problem:
Each window in opencv has a unique name. The window is created with this name either when you call cv2.namedWindow, or implicitly when you call cv2.imshow with a new name.
By using:
cv2.imshow(winName + " FPS: {}".format(fps_text), frame)
You actually create new windows each time the fps_text is different than the ones before.
The Solution:
Each opencv window also has a title property. By default the window title is the window unique name, but these are 2 different properties.
You can use cv2.setWindowTitle to modify the title:
fps_text = "{:.2f}".format(fps)
winTitle = winName + " FPS: {}".format(fps_text)
cv2.setWindowTitle(winName, winTitle)
Note that the window unique name does not change.

How to do OpenCV Laser shot detection with Python on light backgrounds?

I'm trying to build a laser shot detection game in Python3 using OpenCV. I have a proof of concept working which will highlight detected momentary "shots" of the laser on the background.
The problem I'm having is that the laser signature doesn't get detected on lighter backgrounds, or if there is a very white / bright color item near the shot. I'm sure this is because the way I'm using binary thresholds to detect the laser as the brightest thing in the frame, and with light elements the laser gets washed out.
My question is how can I alter my approach to handle this situation, or perhaps "calibrate" the background / other items so that the laser can be detected? Ultimately I'm trying to detect laser shots on a computer screen, where the background where shots are landing is a video with its own high lights and the screen puts out its own light.
Any guidance is appreciated.
main.py
import cv2
from camera import VideoCamera
from detection import LaserDetector
debug=False
radius_min = float(1)
radius_max = float(10)
shot_size = 5
color_blue = (255, 0, 0)
cam = VideoCamera(640, 480)
shots = []
try:
while(True):
frame = cam.get_frame()
if frame is not False:
shot = LaserDetector(frame, debug).detect()
if shot:
x, y, radius = shot
if radius >= radius_min and radius <= radius_max:
shots.append(shot)
if debug:
print(f"(x:{int(x)}, y:{int(y)}, r:{int(radius)})")
for s in shots:
cv2.circle(frame, (int(s[0]), int(s[1])), shot_size, color_blue, 1)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except KeyboardInterrupt:
pass
finally:
print("\nClosing video capture...")
cam.release()
cv2.destroyAllWindows()
camera.py
import cv2
class VideoCamera(object):
def __init__(self, cam_width, cam_height):
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, cam_width)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, cam_height)
def release(self):
self.capture.release()
def __del__(self):
self.capture.release()
def get_frame(self):
success, frame = self.capture.read()
if success:
return frame
else:
return False
def frame_to_jpeg(self, frame):
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
detection.py
import cv2
import numpy as np
import decimal
class LaserDetector(object):
def __init__(self, frame, debug=False):
self.debug = debug
self.frame = frame
def get_contour_points(self, mask):
countours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
if len(countours) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
c = max(countours, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
moments = cv2.moments(c)
if moments["m00"] > 0:
# set the center
(x,y) = int(moments["m10"] / moments["m00"]), \
int(moments["m01"] / moments["m00"])
radius = round(decimal.Decimal(radius), 3)
return (int(x), int(y), radius)
return False
def get_hsv_threshold_mask(self, frame):
hsv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
ret, h_frame = cv2.threshold(h, 125, 160, cv2.THRESH_BINARY)
ret, v_frame = cv2.threshold(v, 250, 256, cv2.THRESH_BINARY)
output = cv2.bitwise_and(h_frame, v_frame, frame)
if self.debug:
indiv_output = np.concatenate((h_frame, v_frame, output), axis=1)
cv2.imshow("threshold", indiv_output)
return output
def detect(self):
mask = self.get_hsv_threshold_mask(self.frame)
return self.get_contour_points(mask)

Is there a way to tell if pyautogui is detecting the color/clicking?

import cv2
import numpy as np
import pyautogui
SCREEN_SIZE = (1920, 1080)
#define the codec
fourcc = cv2.VideoWriter_fourcc(*"XVID")
#create the video write object
out = cv2.VideoWriter("output.avi", fourcc, 20.0, (SCREEN_SIZE))
while True:
#make a screenshot
img = pyautogui.screenshot(region=(680, 785, 560, 20))
#convert these pixels to a proper numpy array to work with OpenCV
frame = np.array(img)
#convert colors from BGR to RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
black = [0,0,0]
for x in range(img.width):
for y in range(img.height):
if img.getpixel((x, y)) == black:
print(x, y)
pyautogui.click(x, y)
#write the frame
out.write(frame)
#show the frame
cv2.imshow("screenshot", frame)
# if the user clicks q, it exits
if cv2.waitKey(1) == ord("q"):
break
# make sure everything is closed when exited
cv2.destroyAllWindows()
out.release()
I am creating a script to detect black squares, and click them. For some reason when using this, there is no error, but it is not clicking. Is there a way to tell whether it is detecting the color/clicking?
Edit: It does not output the coordinates, and when changing it to print "black" once finding the color, there is still no output.

Create Iterable mouse click event in Python?

I am trying to show image to user and asking to click on 4 points for a rectangle from user and once user is done he will press 'c' and exit and my script should return the touch points by user.
For this approach I have written below script using OpenCV,, but not sure how to make class for this purpose iterable, might be I am wrong with some OO technical part but requirement and logic is correct because when not created inside class both functions works fine.
Code:
class mouse_select_points:
''' Select 4 user points from user and crop to straighten'''
__depends__ = ['img_path']
__provides__ = ['ratio','image_resized','click_points']
def __init__(self, thickness=2,shape=(400,600),click_count=0,click_points=[],img_path=""):
self.thickness = thickness
self.shape = shape
self.lent = click_count
self.refPt = click_points
self.img = img_path
self.font = cv2.FONT_HERSHEY_SIMPLEX
def __call__(self):
image = cv2.imread(self.img)
print("first loaded image size:",image.shape)
orig_resized = image.copy() #create a copy of resized image
ratio = image.shape[1] / 600.0
self.shape = image.shape
cv2.namedWindow("image")
cv2.setMouseCallback("image", self._click_and_crop, param = [image] ) #setting param as image to be sent to mouse click function callback
# keep looping until the 'c' key is pressed
while True:
# display the image and wait for a keypress
cv2.imshow("image", image)
cv2.putText(image,"press 'c' to crop or 'r' to reset",(10,15), self.font, .5,(255,255,255),1,cv2.LINE_AA)
key = cv2.waitKey(1) & 0xFF
# if the 'c' key is pressed, break from the loop
elif key == ord("c") and self.lent == 4:
break
return ratio,orig_resized,self.refPt
def _click_and_crop(self,event, x, y, flags, param):
image = param[0]
# if the left mouse button was clicked, record the starting
# (x, y) coordinates and indicate that cropping is being performed
if event == cv2.EVENT_LBUTTONDOWN:
self.refPt.append([x, y])
cv2.circle(image,(int(x),int(y)),self.thickness,(255,1,255),-1)
self.lent += 1
print("inside if")
cv2.imshow("image", image)
##testing
ratio,orig_image = mouse_select_points(img_path=r"Image1.JPG")
I think you want to pick the points repeatly. Here is it.
#!/usr/bin/python3
# 2018.06.13 13:40:12 CST
# 2018.06.13 14:17:49 CST
import cv2
class PickPoints:
''' Select 4 user points from user and crop to straighten'''
__depends__ = ['img_path']
__provides__ = ['ratio','image_resized','click_points']
def __init__(self, thickness=2,shape=(400,600),click_count=0,click_points=[],img_path=""):
self.thickness = thickness
self.shape = shape
self.lent = click_count
self.refPt = click_points
self.img = img_path
self.font = cv2.FONT_HERSHEY_SIMPLEX
def __call__(self):
image = cv2.imread(self.img)
#print("first loaded image size:",image.shape)
print("="*60)
orig_resized = image.copy() #create a copy of resized image
self.shape = image.shape
self.lent = 0
self.refPt = []
self.to_exit = False
cv2.namedWindow("image")
cv2.setMouseCallback("image", self._click_and_crop, param = [image] ) #setting param as image to be sent to mouse click function callback
# keep looping until the 'c' key is pressed
while True:
# display the image and wait for a keypress
cv2.imshow("image", image)
cv2.putText(image,"press 'c' to crop or 'r' to reset",(10,15), self.font, .5,(255,255,255),1,cv2.LINE_AA)
key = cv2.waitKey(30) & 0xFF
# if the 'c' key is pressed, break from the loop
if key == ord("r"):
print("Reset")
self.lent = 0
self.refPt = []
image[:] = orig_resized
if key == ord("c"):
pass
if self.to_exit or key in (27, ord('q'), ord('Q')):
print("Exist")
cv2.destroyAllWindows()
self.to_exit = False
break
return self.refPt
def _click_and_crop(self,event, x, y, flags, param):
image = param[0]
# if the left mouse button was clicked, record the starting
# (x, y) coordinates and indicate that cropping is being performed
if event == cv2.EVENT_LBUTTONDOWN:
if len(self.refPt) <4:
self.refPt.append([x, y])
cv2.circle(image,(int(x),int(y)),self.thickness,(0,255,0),-1)
self.lent += 1
print("#{} {}".format(self.lent, self.refPt[-1]))
cv2.imshow("image", image)
if event == cv2.EVENT_RBUTTONDOWN:
self.to_exit = True
##testing
fname = "/home/auss/Pictures/test.png"
pk = PickPoints(img_path=fname)
print(pk())
print(pk())
print(pk())

Opencv draw a rectangle in a picture were never shown

Hey everybody i have some trouble using opencv 3.x and python 3.x.
What i want to do is to draw a basic rectangle in a picture but the rectangle will never be drawn.
I read this similar thread but it doesn't helped me with my fault.
Python OpenCV: mouse callback for drawing rectangle
It would be nice if someone could give me a hint.
#!/usr/bin/env python3
import cv2
import numpy as np
Path = 'picture.jpg'
image_float_size = 400.0
image_int_size = int(image_float_size)
color = [0,255,0]
rectangle = False
def on_event(event,x,y,flags,param):
global startpointx,startpointy,rectangle
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
startpointx = x
startpointy = y
print('Down',x,y) #debugging
cv2.rectangle(resized,(x,y),(x,y),(0,255,0),-1)
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
print('Up',x,y)
cv2.rectangle(resized,(startpointx,startpointy),(x,y),(0,255,0),-1)
elif event == cv2.EVENT_MOUSEMOVE:
if rectangle:
print('Move',startpointx,startpointy,x,y)#debugging
cv2.rectangle(resized,(startpointx,startpointy),(x,y),(0,255,0),-1)
# Read the image and convert it into gray
image = cv2.imread(Path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# resize the image
ration = image_float_size / gray_image.shape[1]
dim = (image_int_size,int(gray_image.shape[0]*ration))
resized = cv2.resize(gray_image, dim, interpolation = cv2.INTER_AREA)
# set window for the image
cv2.namedWindow('window')
# mouse callback
cv2.setMouseCallback('window',on_event)
# wait forever for user to press any key, after key pressed close all windows
while True:
cv2.imshow('window',resized)
if cv2.waitKey(0):
break
cv2.destroyAllWindows()
You perform drawing (displaying of an image by using cv2.imshow) only once because cv2.waitKey(0) waits indefinitely. If you use some non-zero argument it will wait for that number of milliseconds. But notice that you're constantly rewriting/modifying an image. This is probably not what you want. I think you need to create a temporary (drawing) copy of an image first and restore it each time from original one before new drawing (rectangle).
#!/usr/bin/env python3
import cv2
import numpy as np
Path = 'data/lena.jpg'
image_float_size = 400.0
image_int_size = int(image_float_size)
color = [0,255,0]
rectangle = False
def on_event(event,x,y,flags,param):
global draw_image
global startpointx,startpointy,rectangle
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
startpointx = x
startpointy = y
print('Down',x,y) #debugging
draw_image = resized.copy()
cv2.rectangle(draw_image,(x,y),(x,y),(0,255,0))
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
print('Up',x,y)
draw_image = resized.copy()
cv2.rectangle(draw_image,(startpointx,startpointy),(x,y),(0,255,0))
elif event == cv2.EVENT_MOUSEMOVE:
if rectangle:
print('Move',startpointx,startpointy,x,y)#debugging
draw_image = resized.copy()
cv2.rectangle(draw_image,(startpointx,startpointy),(x,y),(0,255,0))
# Read the image and convert it into gray
image = cv2.imread(Path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# resize the image
ration = image_float_size / gray_image.shape[1]
dim = (image_int_size,int(gray_image.shape[0]*ration))
resized = cv2.resize(gray_image, dim, interpolation = cv2.INTER_AREA)
draw_image = resized.copy()
# set window for the image
cv2.namedWindow('window')
# mouse callback
cv2.setMouseCallback('window',on_event)
while True:
cv2.imshow('window', draw_image)
ch = 0xFF & cv2.waitKey(1)
if ch == 27:
break
cv2.destroyAllWindows()

Resources