0

Some background: I'm reading frames from a video capture device (webcam) and writing it to a video file (out.avi) with OpenCV in python. The script I've written is meant to be called from a Node.js process so I can start recording video in response to whatever occurs in node.

I'm using Python 2.7, and OpenCV is installed on Ubuntu from the aptitude package repos so I'm not sure what version that is or if its important.

Here is the script I wrote:

#!/usr/bin/env python

# adapted from https://stackoverflow.com/questions/32943227/python-opencv-capture-images-from-webcam

from __future__ import print_function
from datetime import datetime
from cvDebug import CvDebug
import argparse
import json
import sys
import cv2

# TODO allow storing to a directory (prepended with date or not) --- TWW
parser = argparse.ArgumentParser(description='record and save a camera video')
parser.add_argument('-d', '--debug', action='store_true', help='turn on debugging')
parser.add_argument('-c', '--camera', type=int, default=0, help='camera number for recording the video')
parser.add_argument('-o', '--out', type=str, default='out.avi', help='name of the output-file')
parser.add_argument('-f', '--fps', type=int, default=10, help='frames per second for output video')
parser.add_argument('-l', '--length', type=int, default=1, help='length of time to record video in seconds')
parser.add_argument('-W', '--width', type=int, default=640, help='width of the image')
parser.add_argument('-H', '--height', type=int, default=480, help='height of the image')
parser.add_argument('-D', '--prepend-date', action='store_true')
parser.add_argument('-T', '--prepend-time', action='store_true')
parser.add_argument('--codec', type=str, default='XVID', help='codec to use when writing video')
# TODO argument to separate out image capture --- TWW
args = parser.parse_args(sys.argv[1:])

now = datetime.now()

if args.prepend_time is True:
    args.out = '{0}_{1}'.format(now.time().strftime('%H%M%S'), args.out)

if args.prepend_date is True:
    args.out = '{0}_{1}'.format(now.today().strftime('%Y%m%d'), args.out)

d = CvDebug(args.debug)


def main():
    # capture from camera at location 0
    d.time('opening camera', args.camera)
    cap = cv2.VideoCapture(args.camera)

    if not cap.isOpened():
        print('opening camera failed')
        cap.release()
        exit(1)

    d.time('setting width and height')
    cap.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, args.width)
    cap.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, args.height)

    # Change the camera setting using the set() function
    # cap.set(cv2.cv.CV_CAP_PROP_EXPOSURE, -6.0)
    # cap.set(cv2.cv.CV_CAP_PROP_GAIN, 4.0)
    # cap.set(cv2.cv.CV_CAP_PROP_BRIGHTNESS, 144.0)
    # cap.set(cv2.cv.CV_CAP_PROP_CONTRAST, 27.0)
    # cap.set(cv2.cv.CV_CAP_PROP_HUE, 13.0) # 13.0
    # cap.set(cv2.cv.CV_CAP_PROP_SATURATION, 28.0)

    # Read the current setting from the camera
    # test = cap.get(cv2.cv.CV_CAP_PROP_POS_MSEC)
    # d.log('Test:', test)
    # ratio = cap.get(cv2.cv.CV_CAP_PROP_POS_AVI_RATIO)
    # d.log('Ratio:', ratio)
    # frame_rate = cap.get(cv2.cv.CV_CAP_PROP_FPS)
    # d.log('Frame Rate:', frame_rate)
    height = cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)
    d.log('Height:', height)
    width = cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)
    d.log('Width:', width)
    brightness = cap.get(cv2.cv.CV_CAP_PROP_BRIGHTNESS)
    d.log('Brightness:', brightness)
    contrast = cap.get(cv2.cv.CV_CAP_PROP_CONTRAST)
    d.log('Contrast:', contrast)
    saturation = cap.get(cv2.cv.CV_CAP_PROP_SATURATION)
    d.log('Saturation:', saturation)
    # hue = cap.get(cv2.cv.CV_CAP_PROP_HUE)
    # d.log('Hue:', hue)
    # gain = cap.get(cv2.cv.CV_CAP_PROP_GAIN)
    # d.log('Gain:', gain)
    # exposure = cap.get(cv2.cv.CV_CAP_PROP_EXPOSURE)
    # d.log('Exposure:', exposure)

    d.time('opening video container', args.out)
    d.log('codec {0}, fps {1}, geo {2}x{3}'.format(args.codec, args.fps, width, height))
    vid = cv2.VideoWriter(args.out, cv2.cv.CV_FOURCC(*args.codec), args.fps, (int(width), int(height)), True)
    d.time('container opened')

    if not vid.isOpened():
        print('opening video container failed')
        cap.release()
        vid.release()
        d.destroy_image_windows()
        exit(1)

    exit_code = 0
    image_count = args.fps * args.length

    while image_count > 0:
        ret, img = cap.read()
        if ret:
            vid.write(img)
            d.time('frame', args.fps * args.length - image_count, 'written')
        else:
            exit_code = 1
            print('frame', args.fps * args.length - image_count, 'failed')
        image_count -= 1

    print(json.dumps({"file": args.out}))
    d.time('releasing capture & video container')
    cap.release()
    vid.release()
    d.time('released')
    d.destroy_image_windows()
    exit(exit_code)


if __name__ == '__main__':
    main()

And when I run it with python script.py -d using my Samsung R580 built-in webcam I get the following output:

[0.000 s][Δ 0.000 s] opening camera 0
[0.151 s][Δ 0.151 s] setting width and height
Height: 480.0
Width: 640.0
Brightness: 0.850000023842
Contrast: 0.649999976158
Saturation: 0.600000023842
[0.173 s][Δ 0.022 s] opening video container out.avi
codec XVID, fps 10, geo 640.0x480.0
[0.189 s][Δ 0.016 s] container opened
[2.353 s][Δ 2.163 s] frame 0 written
[2.437 s][Δ 0.085 s] frame 1 written
[2.528 s][Δ 0.091 s] frame 2 written
[2.618 s][Δ 0.090 s] frame 3 written
[2.713 s][Δ 0.095 s] frame 4 written
[2.804 s][Δ 0.091 s] frame 5 written
[2.892 s][Δ 0.088 s] frame 6 written
[2.985 s][Δ 0.094 s] frame 7 written
[3.076 s][Δ 0.091 s] frame 8 written
[3.168 s][Δ 0.092 s] frame 9 written
{"file": "out.avi"}
[3.169 s][Δ 0.000 s] releasing capture & video container
[3.196 s][Δ 0.028 s] released

Notice how the first frame takes over 2 seconds to retrieve! But then subsequent frames come after about every 0.091 seconds. I am not certain how to explain this behavior.

Also, I purchased a different webcam (Logitech C920 as recommended by Derek Molloy) and it certainly speeds up the capture of the first frame. On my laptop its acceptably fast, but on a beagleboard It still takes up to 0.7-1.0 seconds for that first frame (and it's at about 0.2 seconds for every consecutive frame). The frame-rate is acceptable, but I essentially want to minimize the amount of time it takes to start recording after the start of the script.

I tried to use the constant discussed in this stackoverflow (cv2.cv.CV_CAP_PROP_BUFFERSIZE) but the constant doesn't seem to exist in python-opencv that I installed.

Does anyone have experience with this that can lend some insight into what is going on? Does anyone have resources they can recommend reading up on?

Troy Weber
  • 897
  • 8
  • 8
  • The behaviour you are experiencing is normal. Initialisation and everything for camera takes time . So first frame takes some time – Arpit Solanki Jul 07 '17 at 19:29
  • Can you open the camera faster with raw `libav` or `v4l2` calls? – genpfault Jul 07 '17 at 19:43
  • It would probably make more sense to run this as a persistent process, and have the node.js code communicate with it using some sort of IPC. – Dan Mašek Jul 07 '17 at 19:45

0 Answers0