mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-03 20:22:48 +08:00
Merge video I/O.
This commit is contained in:
@@ -20,6 +20,7 @@ from sift import *
|
||||
from collection import *
|
||||
|
||||
from io import *
|
||||
from video import *
|
||||
|
||||
|
||||
def _update_doc(doc):
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
import numpy as np
|
||||
import os, time
|
||||
from scikits.image.io import ImageCollection
|
||||
|
||||
try:
|
||||
import pygst
|
||||
pygst.require("0.10")
|
||||
import gst, 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, img_mat)
|
||||
else:
|
||||
cv.Resize(img, img_mat)
|
||||
# opencv stores images in BGR format
|
||||
cv.CvtColor(img_mat, 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)
|
||||
buff = 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='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(self.frame_count())
|
||||
return ImageCollection(time_range, load_func=self.get_index_frame)
|
||||
|
||||
|
||||
__all__ = ["Video"]
|
||||
|
||||
Reference in New Issue
Block a user