Files
scikit-image/skimage/io/video.py
T
2012-06-29 11:27:23 +02:00

367 lines
10 KiB
Python

import numpy as np
import os
from skimage.io import ImageCollection
try:
import pygst
pygst.require("0.10")
import gst
import gobject
gobject.threads_init()
from gst.extend.discoverer import Discoverer
gstreamer_available = True
except ImportError:
gstreamer_available = False
try:
import cv
opencv_available = True
except ImportError:
opencv_available = False
class CvVideo(object):
"""
Opencv-based video loader.
Parameters
----------
source : str
Media location URI. Video file path or http address of IP camera.
size: tuple, optional
Size of returned array.
"""
def __init__(self, source=None, size=None, backend=None):
if not opencv_available:
raise ImportError("Opencv 2.0+ required")
self.source = source
self.capture = cv.CreateFileCapture(self.source)
self.size = size
def get(self):
"""
Retrieve a video frame as a numpy array.
Returns
-------
output : array (image)
Retrieved image.
"""
img = cv.QueryFrame(self.capture)
if not self.size:
self.size = cv.GetSize(img)
img_mat = np.empty((self.size[1], self.size[0], 3), dtype=np.uint8)
if cv.GetSize(img) == self.size:
cv.Copy(img, cv.fromarray(img_mat))
else:
cv.Resize(img, cv.fromarray(img_mat))
# opencv stores images in BGR format
cv.CvtColor(cv.fromarray(img_mat), cv.fromarray(img_mat),
cv.CV_BGR2RGB)
return img_mat
def seek_frame(self, frame_number):
"""
Seek to specified frame in video.
Parameters
----------
frame_number : int
Frame position
"""
cv.SetCaptureProperty(self.capture, cv.CV_CAP_PROP_POS_FRAMES,
frame_number)
def seek_time(self, milliseconds):
"""
Seek to specified time in video.
Parameters
----------
milliseconds : int
Time position
"""
cv.SetCaptureProperty(self.capture, cv.CV_CAP_PROP_POS_MSEC,
milliseconds)
def frame_count(self):
"""
Returns frame count of video.
Returns
-------
output : int
Frame count.
"""
return cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FRAME_COUNT)
def duration(self):
"""
Returns time length of video in milliseconds.
Returns
-------
output : int
Time length [ms].
"""
return cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FPS) * \
cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FRAME_COUNT)
class GstVideo(object):
"""
GStreamer-based video loader.
Parameters
----------
source : str
Media location URI. Video file path or http address of IP camera.
size: tuple, optional
Size of returned array.
sync: bool, optional (default False)
Frames are extracted per frame or per time basis. If enabled the video
time step continues onward according to the play rate. Useful for ip
cameras and other real time video feeds.
"""
def __init__(self, source=None, size=None, sync=False):
if not gstreamer_available:
raise ImportError("GStreamer Python bindings 0.10+ required")
self.source = source
self.size = size
self.video_length = 0
self.video_rate = 0
# extract video size
if not size:
gobject.idle_add(self._discover_one)
self.mainloop = gobject.MainLoop()
self.mainloop.run()
if not self.size:
self.size = (640, 480)
if os.path.exists(self.source):
self.source = "file://" + self.source
self._create_main_pipeline(self.source, self.size, sync)
def _discover_one(self):
"""
Callback to start media discovery process, used to retrieve video parameters.
"""
discoverer = Discoverer(self.source)
discoverer.connect('discovered', self._discovered)
discoverer.discover()
return False
def _discovered(self, d, is_media):
"""
Callback to on media discovery result.
"""
if is_media:
self.size = (d.videowidth, d.videoheight)
self.video_length = d.videolength / gst.MSECOND
self.video_rate = d.videorate.num
self.mainloop.quit()
return False
def _create_main_pipeline(self, source, size, sync):
"""
Create the frame extraction pipeline.
"""
pipeline_string = "uridecodebin name=decoder uri=%s ! ffmpegcolorspace ! videoscale ! appsink name=play_sink" % self.source
self.pipeline = gst.parse_launch(pipeline_string)
caps = "video/x-raw-rgb, width=%d, height=%d, depth=24, bpp=24" % size
self.decoder = self.pipeline.get_by_name("decoder")
self.appsink = self.pipeline.get_by_name('play_sink')
self.appsink.set_property('emit-signals', True)
self.appsink.set_property('sync', sync)
self.appsink.set_property('drop', True)
self.appsink.set_property('max-buffers', 1)
self.appsink.set_property('caps', gst.caps_from_string(caps))
if self.pipeline.set_state(gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE:
raise NameError("Failed to load video source %s" % self.source)
self.appsink.emit('pull-preroll')
def get(self):
"""
Retrieve a video frame as a numpy array.
Returns
-------
output : array (image)
Retrieved image.
"""
buff = self.appsink.emit('pull-buffer')
img_mat = np.ndarray(shape=(self.size[1], self.size[0], 3),
dtype=np.uint8, buffer=buff.data)
return img_mat
def seek_frame(self, frame_number):
"""
Seek to specified frame in video.
Parameters
----------
frame_number : int
Frame position
"""
self.pipeline.seek_simple(gst.FORMAT_DEFAULT, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT, frame_number)
def seek_time(self, milliseconds):
"""
Seek to specified time in video.
Parameters
----------
milliseconds : int
Time position
"""
self.pipeline.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT, milliseconds / 1000.0 * gst.SECOND)
def frame_count(self):
"""
Returns frame count of video.
Returns
-------
output : int
Frame count.
"""
return self.video_length / 1000 * self.video_rate
def duration(self):
"""
Returns time length of video in milliseconds.
Returns
-------
output : int
Time length [ms].
"""
return self.video_length
class Video(object):
"""
Video loader. Supports Opencv and Gstreamer backends.
Parameters
----------
source : str
Media location URI. Video file path or http address of IP camera.
size: tuple, optional
Size of returned array.
sync: bool, optional (default False)
Frames are extracted per frame or per time basis. Gstreamer only.
If enabled the video time step continues onward according to the play rate.
Useful for IP cameras and other real time video feeds.
backend: str, 'gstreamer' or 'opencv'
Backend to use.
"""
def __init__(self, source=None, size=None, sync=False, backend=None):
if backend == None:
# select backend that is available
if gstreamer_available:
self.video = GstVideo(source, size, sync)
elif opencv_available:
self.video = CvVideo(source, size)
else:
# if no backend available, raise exception
self.video = GstVideo(source, size, sync)
elif backend == "gstreamer":
self.video = GstVideo(source, size, sync)
elif backend == "opencv":
self.video = CvVideo(source, size)
else:
raise ValueError("Unknown backend: %s", backend)
def get(self):
"""
Retrieve the next video frame as a numpy array.
Returns
-------
output : array (image)
Retrieved image.
"""
return self.video.get()
def seek_frame(self, frame_number):
"""
Seek to specified frame in video.
Parameters
----------
frame_number : int
Frame position
"""
self.video.seek_frame(frame_number)
def seek_time(self, milliseconds):
"""
Seek to specified time in video.
Parameters
----------
milliseconds : int
Time position
"""
self.video.seek_time(milliseconds)
def frame_count(self):
"""
Returns frame count of video.
Returns
-------
output : int
Frame count.
"""
return self.video.frame_count()
def duration(self):
"""
Returns time length of video in milliseconds.
Returns
-------
output : int
Time length [ms].
"""
return self.video.duration()
def get_index_frame(self, frame_number):
"""
Retrieve a specified video frame as a numpy array.
Parameters
----------
frame_number : int
Frame position
Returns
-------
output : array (image)
Retrieved image.
"""
self.video.seek_frame(frame_number)
return self.video.get()
def get_collection(self, time_range=None):
"""
Returns an ImageCollection object.
Parameters
----------
time_range: range (int), optional
Time steps to extract, defaults to the entire length of video.
Returns
-------
output: ImageCollection
Collection of images iterator.
"""
if not time_range:
time_range = range(int(self.frame_count()))
return ImageCollection(time_range, load_func=self.get_index_frame)
__all__ = ["Video"]