OpenCV video writer writes blank files due to memory leak? - python-2.7

I'm trying to save video files on a raspberry pi with python2.7 and opencv. The code shown below consistently saves several video files (size 16-18 Mb) to a usb but after the first few files the file sizes drop to 6 kb and appear to be empty since they won't open.
I opened task manager to monitor the memory usage by python during the saving and noticed that the RSS memory continually increases until roughly 200 MB which is when the video files start showing up blank.
Is that a sure indicator of a memory leak, or should I run other tests?
Is there something wrong in the below code that isn't releasing variables properly?
import cv2
import numpy as np
import datetime
dispWidth = 640
dispHeight = 480
FPS = 6
SetupNewVideoFile = True # state variable
VidCaptureDurationMinutes = 3
filepath = '/media/pi/9CEE-5383/Videos/'
i = 1 # counter for the video file names
fourcc = cv2.cv.CV_FOURCC('X','V','I','D')
while True:
# timer section that ends running file saves and triggers a new file save
Now = datetime.datetime.now() # refresh current time
delta = Now - VidCaptureStartTime
print('delta: ',delta.seconds,delta.days)
if ((delta.seconds/60) >= VidCaptureDurationMinutes) and delta.days >= 0:
print('delta: ',delta.seconds,delta.days)
SetupNewVideoFile = True
Vidoutput.release()
cap.release()
# setting up new file saves
if SetupNewVideoFile:
SetupNewVideoFile = False
title = "Video_"+str(i)+".avi"
i += 1
fullpath = filepath + title
print(fullpath)
Vidoutput = cv2.VideoWriter(fullpath, fourcc, FPS,(dispWidth,dispHeight))
VidCaptureStartTime = datetime.datetime.now() # updating video start time
cap = cv2.VideoCapture(-1) # start video capture
ret, frame = cap.read()
if ret: # display and save if a frame was successfully read
cv2.imshow('webcam',frame)
Vidoutput.write(frame) # save the frames
key = cv2.waitKey(1) & 0xFF
if key == ord('q'): # quits program
break
# clean up
cap.release()
Vidoutput.release()
cv2.destroyAllWindows()
cv2.waitKey(1) # these seem to be needed to flush the cv actions
cv2.waitKey(1)
cv2.waitKey(1)
cv2.waitKey(1)

After some more trouble shooting and help from the pympler module functions and tutorials (https://pythonhosted.org/Pympler/muppy.html) my problems still looked like a memory leak but I was unable to solve the specific error.
This other S.O. post (Releasing memory in Python) mentioned improvements in memory management made in version 3.3 of Python:
"In Python 3.3 the small object allocator was switched to using anonymous memory maps instead of the heap, so it should perform better at releasing memory."
So I switched over to Python 3.3 and now the code saves valid video files well past the error out time I saw previously.
This isn't an answer as to why the blank files were occurring, but at least it's a solution.

Related

Celery/redis tasks don't always complete - not sure why or how to fit it

I am running celery v 4.0.3/redis v 4.09 in a django v 3.0.1 application (Python v 3.6.9). I am also using face_recognition in a celery task find_faces to find faces in the images I have uploaded to the application, among other image processing celery tasks. There are no issues processing five or fewer image files in that all the image processing celery tasks complete successfully.
When I have the image process tasks (including find_faces) iterate over 100 images there are 10-30 images where the find_faces task does not complete. When I use flower v0.9.7 to take a look at the celery tasks, I see that the find_faces task status is "started" for those images that did not complete. All the other images have find_faces task status as "success". The status of these "started" tasks never changes, and there are no errors or exceptions reported. I can then run the image processing tasks, including the find_faces task, on each of these images individually, and the task status is "success". These results do not change if I run celery as a daemon or locally, or if I run the django app using wsgi and apache or runserver. Flower also reports that retries = 0 for all my tasks.
I have CELERYD_TASK_SOFT_TIME_LIMIT = 60 set globally in the django app, and max_retries=5 for the find_faces task.
#app.task(bind=True, max_retries=5)
def find_faces_task(self, document_id, use_cuda=settings.USE_CUDA):
logger.debug("find_faces_task START")
try:
temp_face = None
from memorabilia.models import TaskStatus, Document
args = "document_id=%s, use_cuda=%s" % (document_id, use_cuda)
ts = TaskStatus(document_id_id=document_id, task_id=self.request.id, task_name='find_faces_task', task_args=args, task_status=TaskStatus.PENDING)
ts.save()
import time
time_start = time.time()
# Check if we already have the faces for this document
from biometric_identification.models import Face
if len(Face.objects.filter(document_id=document_id)) != 0:
# This document has already been scanned, so need to remove it and rescan
# Have to manually delete each object per django docs to insure the
# model delete method is run to update the metadata.
logger.debug("Document %s has already been scanned" % document_id)
faces = Face.objects.filter(document_id=document_id)
for face in faces:
face.delete()
logger.debug("Deleted face=%s" % face.tag_value.value)
document = Document.objects.get(document_id=document_id)
image_file = document.get_default_image_file(settings.DEFAULT_DISPLAY_IMAGE)
image_path = image_file.path
time_start_looking = time.time()
temp_file = open(image_path, 'rb')
temp_image = Image.open(temp_file)
logger.debug("fred.mode=%s" % fred.mode)
width, height = temp_image.size
image = face_recognition.load_image_file(temp_file)
# Get the coordinates of each face
if use_cuda:
# With CUDA installed
logger.debug("Using CUDA for face recognition")
face_locations = face_recognition.face_locations(image, model="cnn", number_of_times_to_upsample=0)
else:
# without CUDA installed
logger.debug("NOT using CUDA for face recognition")
face_locations = face_recognition.face_locations(image, model="hog", number_of_times_to_upsample=2)
time_find_faces = time.time()
# Get the face encodings for each face in the picture
face_encodings = face_recognition.face_encodings(image, known_face_locations=face_locations)
logger.debug("Found %s face locations and %s encodings" % (len(face_locations), len(face_encodings)))
time_face_encodings = time.time()
# Save the faces found in the database
for location, encoding in zip(face_locations, face_encodings):
# Create the new Face object and load in the document, encoding, and location of a face found
# Locations seem to be of the form (y,x)
from memorabilia.models import MetaData, MetaDataValue
tag_type_people = MetaDataValue.objects.filter(metadata_id=MetaData.objects.filter(name='Tag_types')[0].metadata_id, value='People')[0]
tag_value_unknown = MetaDataValue.objects.filter(metadata_id=MetaData.objects.filter(name='Unknown')[0].metadata_id, value='Unknown')[0]
new_face = Face(document=document, face_encoding=numpy_to_json(encoding), face_location=location, image_size={'width': width, "height":height}, tag_type=tag_type_people, tag_value=tag_value_unknown)
# save the newly found Face object
new_face.save()
logger.debug("Saved new_face %s" % new_face.face_file)
time_end = time.time()
logger.debug("total time = {}".format(time_end - time_start))
logger.debug("time to find faces = {}".format(time_find_faces - time_start_looking))
logger.debug("time to find encodings = {}".format(time_face_encodings - time_find_faces))
ts.task_status = TaskStatus.SUCCESS
ts.comment = "Found %s faces" % len(face_encodings)
return document_id
except Exception as e:
logger.exception("Hit an exception in find_faces_task %s" % str(e))
ts.task_status = TaskStatus.ERROR
ts.comment = "An exception while finding faces: %s" % repr(e)
finally:
logger.debug("Finally clause in find-faces_task")
if temp_image:
temp_image.close()
if temp_file:
temp_file.close()
ts.save(update_fields=['task_status', 'comment'])
logger.debug("find_faces_task END")
The find_faces task is called as part of a larger chain of tasks that manipulate the images. Each image file goes through this chain, where step_1 and step_2 are chords for different image processing steps:
step_1 = chord( group( clean ), chordfinisher.si() ) # clean creates different image sizes
step_2 = chord( group( jobs ), chordfinisher.si() ) # jobs include find_faces
transaction.on_commit(lambda: chain(step_1, step_2, faces_2, ocr_job, change_state_task.si(document_id, 'ready')).delay())
#app.task
def chordfinisher( *args, **kwargs ):
return "OK"
The images are large, so it can take up to 30 seconds for the find_faces task to complete. I thought the CELERYD_TASK_SOFT_TIME_LIMIT = 60 would take care of this long processing time.
I am by no means a celery expert, so I assume there is a celery setting or option that I need to enable to make sure the find_faces task completes all the time. I just don't know what that would be.
After some more research, I can up with this suggestion from Lewis Carroll, in this post "Beware the oom-killer, my son! The jaws that bite, the claws that catch!", and this post Chaining Chords produces enormously big messages causing OOM on workers, and this post WorkerLostError: Worker exited prematurely: exitcode 155.
It seems my celery workers may have been running out of memory, as I did find traces of the dreaded oomkiller in my syslogs. I reconfigured my tasks to just be in a chain (removed all the groups and chords) so each task is run individually in sequence for each image, and the tasks all completed successfully, no matter how many images I processed.

openCV imwrite writing 0 kb image frames

I am running the below code to convert video into frames. Problem is it is creating Image files with 0 KB size and when I open it is not showing anything.. I don't understand what is creating the problem. Do I need to install any Image codecs?
'''
Using OpenCV takes a mp4 video and produces a number of images. I am using OpenCV 3.3.0 version and Python 2.7
Which will produce a folder called data with the images, There will be 2000+ images for example.mp4.
'''
import cv2
import numpy as np
import os
# Playing video from file:
try:
cap = cv2.VideoCapture('aa.mkv')
except:
print "Could not open video file"
raise
print cap.grab()
try:
if not os.path.exists('data'):
os.makedirs('data')
except OSError:
print ('Error: Creating directory of data')
currentFrame = 0
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
if not frame is None:
# Saves image of the current frame in jpg file
name = './data/frame' + str(currentFrame) + '.jpg'
print ('Creating...' + name)
cv2.imwrite(name, frame)
#cv2.imshow(name, frame)
else:
break
# To stop duplicate images
currentFrame += 1
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
You are not using imwrite function to write frames. Also your imshow function name is misspelled. I have made changes in your code. Try this:
import cv2
import numpy as np
import os
# Playing video from file:
cap = cv2.VideoCapture('aa.mkv')
try:
if not os.path.exists('data'):
os.makedirs('data')
except OSError:
print ('Error: Creating directory of data')
currentFrame = 0
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
if not frame is None:
# Saves image of the current frame in jpg file
name = './data/frame' + str(currentFrame) + '.jpg'
print ('Creating...' + name)
cv2.imwrite(name, frame)
cv2.imshow(name, frame)
else:
break
# To stop duplicate images
currentFrame += 1
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

Python - Facial Tracking H.264 video leads to extensive Lag and errors

Can someone help me with this one ? I have written this code in Python to perform facial tracking on a HD IP Camera feed using the H.264 format. The script works as it should except:
When I execute the program I get these error messages but then the video loads up.
[h264 # 000000000031af80] Missing reference picture, default is 0
[h264 # 000000000031af80] decode_slice_header error
[h264 # 00000000093768a0] Missing reference picture, default is 0
[h264 # 00000000093768a0] decode_slice_header error
The video is extremely slow with a 5-10 second lag per frame. However, If I remove the facial tracking segment the video is smooth as it should but then without facial tracking the entire purpose is defeated. I was also thinking of using multiprocessing so that I am able to speed this process up, but I want to be doubly sure that there is no other way before I head onto that route.
Please if someone could help me out.
=============================
import cv2
face_cascade = cv2.CascadeClassifier('cascade_face.xml')
# Function to Track Face
def facetrack(frame):
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.2, 5)
for (x,y,w,h) in faces:
cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
print ("X = %s and Y= %s" %(x,y))
# Capture video from an IP Camera - FOSCAM
video_capture = cv2.VideoCapture("rtsp://user:password#xx.xx.xx.xx:xx/videoMain")
# Extract Frame and perform Facial Tracking
while True:
ret, frame = video_capture.read()
facetrack(frame)
cv2.imshow('VIDEO',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
video_capture.release()
cv2.destroyAllWindows()

Multi Processing in python

I am trying to record Set top box signal using videorecording feature of opencv in python.
I am using capture device to capture the set top box signal whose FPS is 30.
Video Recording from External capture device is successful using opencv.
Here is the code for video recording
#VideoRecord.py
CaptureDeviceID = 1
cap = VideoCapture(CaptureDeviceID)
if cap.isOpened():
cap.set(CV_CAP_PROP_FRAME_WIDTH, 640)
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480)
else:
exit()
fourcc = cv2.cv.CV_FOURCC()
out = cv2.VideoWriter('output.avi',fourcc, 30.0, (640,480))
while(cap.isOpened()):
frameNo = 0
ret, frame = cap.read()
if ret==True:
frameNo++
# write the frame
out.write(frame)
if (frameNo > 150):
break
else:
break
# Release everything if job is finished
cap.release()
out.release()
cv2.destroyAllWindows()
Now through Script I am changing channel using IRBlaster command.
This channel change should come in recorded video.
code for sending channel+ command
# sendCommand.py
redrat_path = r'C:\Program Files\RedRat\RedRat Control\RR_CLI_V3.01\RR_CLI.exe
redrat_process=subprocess.Popen(\"%s %s\" % (redrat_path,\"RedRat-0 -output RedRatDB.xml -device STB1 -signal Channel+ "))
In words I want to record video and send command in parallel process.So that the created video file has this
channel change event recorded.
How should I achieve this effectively so that I will not miss any frame while I am sending channel change command?.
Do i have to use threading?
Please suggest some ideas.I want to use Python

Saving a stream while playing it using LibVLC

Using LibVLC, I'm trying to save a stream while playing it. This is the python code:
import os
import sys
import vlc
if __name__ == '__main__':
filepath = <either-some-url-or-local-path>
movie = os.path.expanduser(filepath)
if 'http://' not in filepath:
if not os.access(movie, os.R_OK):
print ( 'Error: %s file is not readable' % movie )
sys.exit(1)
instance = vlc.Instance("--sub-source marq --sout=file/ps:example.mpg")
try:
media = instance.media_new(movie)
except NameError:
print ('NameError: % (%s vs Libvlc %s)' % (sys.exc_info()[1],
vlc.__version__, vlc.libvlc_get_version()))
sys.exit(1)
player = instance.media_player_new()
player.set_media(media)
player.play()
#dont exit!
while(1):
continue
It saves the video stream to a file example.mpg. As per this doc, the command to save a stream is this :
--sout=file/ps:example.mpg
which I've using when creating an instance of vlc.Instance:
instance = vlc.Instance("--sub-source marq --sout=file/ps:example.mpg")
But the problem is that it only saves the stream, it doesn't play the stream simultaneously.
Is there any way (in LibVLC) I can save the stream (to a local file) while paying it?
Although, I'm looking for a solution in Python 3.3.1 but it is fine if there is any C or C++ solution.
I've created a similar, but not duplicate, topic yesterday.
Idea:
The basic idea is simple enough. You have to duplicate the output stream and redirect it to a file. This is done, as Maresh correctly pointed out, using the sout=#duplicate{...} directive.
Working Solution:
The following solution works on my machine ™. I've tested it on Ubuntu 12.10 with VLC v2.0.3 (TwoFlower) and Python 2.7.1. I think it should also work on Python 3 since most of the heavy lifting is done by libVlc anyway.
import os
import sys
import vlc
if __name__ == '__main__':
#filepath = <either-some-url-or-local-path>
movie = os.path.expanduser(filepath)
if 'http://' not in filepath:
if not os.access(movie, os.R_OK):
print ( 'Error: %s file is not readable' % movie )
sys.exit(1)
instance = vlc.Instance("--sout=#duplicate{dst=file{dst=example.mpg},dst=display}")
try:
media = instance.media_new(movie)
except NameError:
print ('NameError: % (%s vs Libvlc %s)' % (sys.exc_info()[1],
vlc.__version__, vlc.libvlc_get_version()))
sys.exit(1)
player = instance.media_player_new()
player.set_media(media)
player.play()
#dont exit!
while(1):
continue
Helpful Links
The Command-Line help was essential to decipher the plethora of VLCs
command line options.
Chapter 3 of VLC streaming HowTo. Explains the structure of the stream output, its directives and describes of the various available modules. Chapter 4 shows some examples.
LibVLC API documentation in case you want to change media option at
runtime
Update - Saving YouTube videos:
The above code doesn't play nice with YouTube. I searched around and discovered that an additional transcode directive can be used to convert YouTube's video stream to a regular video format. I used #transcode{vcodec=mp4v,acodec=mpga,vb=800,ab=128,deinterlace}
vcodec=mp4v is the video format you want to encode in (mp4v is MPEG-4, mpgv is MPEG-1, and there is also h263, DIV1, DIV2, DIV3, I420, I422, I444, RV24, YUY2).
acodec=mpga is the audio format you want to encode in (mpga is MPEG audio layer 2, a52 is A52 i.e. AC3 sound).
vb=800 is the video bitrate in Kbit/s.
ab=128 is the audio bitrate in Kbit/s.
deinterlace tells VLC to deinterlace the video on the fly.
The updated code looks like this:
import os
import sys
import vlc
if __name__ == '__main__':
#filepath = <either-some-url-or-local-path>
filepath = "http://r1---sn-nfpnnjvh-1gil.c.youtube.com/videoplayback?source=youtube&newshard=yes&fexp=936100%2C906397%2C928201%2C929117%2C929123%2C929121%2C929915%2C929906%2C929907%2C929125%2C929127%2C925714%2C929917%2C929919%2C912512%2C912515%2C912521%2C906838%2C904485%2C906840%2C931913%2C904830%2C919373%2C933701%2C904122%2C932216%2C936303%2C909421%2C912711%2C907228%2C935000&sver=3&expire=1373237257&mt=1373214031&mv=m&ratebypass=yes&id=1907b7271247a714&ms=au&ipbits=48&sparams=cp%2Cid%2Cip%2Cipbits%2Citag%2Cratebypass%2Csource%2Cupn%2Cexpire&itag=45&key=yt1&ip=2a02%3A120b%3Ac3c6%3A7190%3A6823%3Af2d%3A732c%3A3577&upn=z3zzcrvPC0U&cp=U0hWSFJOVV9KUUNONl9KSFlDOmt4Y3dEWFo3dDFu&signature=D6049FD7CD5FBD2CC6CD4D60411EE492AA0E9A77.5D0562CCF4E10A6CC53B62AAFFF6CB3BB0BA91C0"
movie = os.path.expanduser(filepath)
savedcopy = "yt-stream.mpg"
if 'http://' not in filepath:
if not os.access(movie, os.R_OK):
print ( 'Error: %s file is not readable' % movie )
sys.exit(1)
instance = vlc.Instance("--sout=#transcode{vcodec=mp4v,acodec=mpga,vb=800,ab=128,deinterlace}:duplicate{dst=file{dst=%s},dst=display}" % savedcopy)
try:
media = instance.media_new(movie)
except NameError:
print ('NameError: % (%s vs Libvlc %s)' % (sys.exc_info()[1],
vlc.__version__, vlc.libvlc_get_version()))
sys.exit(1)
player = instance.media_player_new()
player.set_media(media)
player.play()
#dont exit!
while(1):
continue
A couple of important points:
I've used MPEG audio and video codecs in the transcode directive. It seems to be important to use a matching extensions for the output file (mpg in this case). Otherwise VLC gets confused when opening the saved file for playback. Keep that in mind if you decide to switch to another video format.
You cannot add a regular YouTube URL as filepath. Instead you have to specify the location of the video itself. That's the reason why the filepath that I've used looks so cryptic. That filepath corresponds to video at http://www.youtube.com/watch?v=GQe3JxJHpxQ. VLC itself is able to extract the video location from a given YouTube URL, but libVLC doesn't do that out of the box. You'll have to write your own resolver to do that. See this related SO question. I followed this approach to manually resolve the video location for my tests.
I think you need to duplicate the output in order to play and record it at the same time:
vlc.Instance("--sub-source marq --sout=#stream_out_duplicate{dst=display,dst=std{access=file,mux=ts,dst=/path/file.mpg}}")
or
libvlc_media_add_option(media, ":sout=#stream_out_duplicate{dst=display,dst=std{access=file,mux=ts,dst=/path/file.mpg}}")
Did you try adding to the list of options the following option?
--sout-display
i.e.
instance = vlc.Instance("--sub-source marq --sout=file/ps:example.mpg --sout-display")
Some time ago in a sample code in the active state website i saw someone played and recorded a MP3 file using VLC using the vlc.py module. You can take a look at it's sample code to see how to duplicate a stream. I copied th code here for you (I copied it from http://code.activestate.com/recipes/577802-using-vlcpy-to-record-an-mp3-and-save-a-cue-file/):
import vlc
import time
import os
def new_filename(ext = '.mp3'):
"find a free filename in 00000000..99999999"
D = set(x[:8] for x in os.listdir('.')
if (x.endswith(ext) or x.endswith('.cue')) and len(x) == 12)
for i in xrange(10**8):
s = "%08i" %i
if s not in D:
return s
def initialize_cue_file(name,instream,audiofile):
"create a cue file and write some data, then return it"
cueout = '%s.cue' %name
outf = file(cueout,'w')
outf.write('PERFORMER "%s"\n' %instream)
outf.write('TITLE "%s"\n' %name)
outf.write('FILE "%s" WAVE\n' %audiofile)
outf.flush()
return outf
def initialize_player(instream, audiofile):
"initialize a vlc player which plays locally and saves to an mp3file"
inst = vlc.Instance()
p = inst.media_player_new()
cmd1 = "sout=#duplicate{dst=file{dst=%s},dst=display}" %audiofile
cmd2 ="no-sout-rtp-sap"
cmd3 = "no-sout-standard-sap"
cmd4 ="sout-keep"
med=inst.media_new(instream,cmd1,cmd2,cmd3,cmd4)
med.get_mrl()
p.set_media(med)
return p, med
def write_track_meta_to_cuefile(outf,instream,idx,meta,millisecs):
"write the next track info to the cue file"
outf.write(' TRACK %02i AUDIO\n' %idx)
outf.write(' TITLE "%s"\n' %meta)
outf.write(' PERFORMER "%s"\n' %instream)
m = millisecs // 60000
s = (millisecs - (m*60000)) // 1000
hs = (millisecs - (m*60000) - (s*1000)) //10
ts = '%02i:%02i:%02i' %(m,s,hs)
outf.write(' INDEX 01 %s\n' %ts)
outf.flush()
def test():
#some online audio stream for which this currently works ....
instream = 'http://streamer-mtc-aa05.somafm.com:80/stream/1018'
#if the output filename ends with mp3 vlc knows which mux to use
ext = '.mp3'
name = new_filename(ext)
audiofile = '%s%s' %(name,ext)
outf = initialize_cue_file(name,instream,audiofile)
p,med = initialize_player(instream, audiofile)
p.play()
np = None
i = 0
while 1:
time.sleep(.1)
new = med.get_meta(12)
if new != np:
i +=1
t = p.get_time()
print "millisecs: %i" %t
write_track_meta_to_cuefile(outf,instream,i,new,t)
np = new
print "now playing: %s" %np
if __name__=='__main__':
test()
Perhaps you need to clone your output, as suggested on the forum?