mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-29 00:23:43 +08:00
367 lines
10 KiB
Python
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"]
|