-
Notifications
You must be signed in to change notification settings - Fork 9
/
imstream.m
179 lines (176 loc) · 6.78 KB
/
imstream.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
%IMSTREAM Open an image or video stream for reading a frame at a time
%
% h = imstream(filename, [cache_len])
%
% This class creates a single interface to both streams from both videos
% and sequences of images. The interface is similar to that of MATLAB's
% VideoReader class. However, the read method can only read an image at a
% time, and access to frames is also achieved by subscripted reference,
% i.e. h(3) returns the third frame.
%
% The class also implements a frame cache, which can improve efficiency
% when frames get read several times.
%
% IN:
% filename - string containing the full or partial path to a video file
% or first frame in an image sequence.
% cache_len - scalar indicating how many frames can be stored in the
% frame cache. Default: 1 (cache only the current frame).
%
% OUT:
% h - handle to the stream.
%
%Example:
% % Process all frame triplets
% ims = imstream('input.000.png', 3); % Cache last 3 frames used
% n = ims.num_frames; % Get the number of frames
% for a = 2:n-1
% % Create the next triplet of frames
% A = cat(4, ims(a-1), ims(a), ims(a+1));
% % Process the frame triplet
% end
%
% See also VIDEOREADER, IMSEQ.
% Copyright (C) Oliver Woodford 2011
classdef imstream < handle
properties (Hidden = true, SetAccess = private)
sh; % Stream handle
number_of_frames;
curr_frame;
last_frame_lowlevel;
buffer; % Image cache stuff
end
methods
% Constructor
function this = imstream(fname, varargin)
if iscell(fname)
% Assume this is a list of images
this.sh = imseq(fname);
this.number_of_frames = this.sh.NumberOfFrames;
else
[~, ~, fext] = fileparts(fname);
switch lower(fext(2:end))
case {'bmp', 'tif', 'tiff', 'jpeg', 'jpg', 'png', 'ppm', 'pgm', 'pbm', 'gif'}
% Image sequence
this.sh = imseq(fname);
this.number_of_frames = this.sh.NumberOfFrames;
case {'mpg', 'avi', 'mp4', 'm4v', 'mpeg', 'mxf', 'mj2', 'wmv', 'asf', 'asx', 'mov', 'ogg'}
% Video file
this.sh = VideoReader(fname);
this.number_of_frames = round(this.sh.Duration * this.sh.FrameRate);
otherwise
error('File extension %s not recognised.', fext);
end
end
% Create the buffered stream
this.buffer = cache(@(n) read_lowlevel(this, n), varargin{:});
% Current frame for VideoIO compatibility
this.curr_frame = 0;
this.last_frame_lowlevel = 0;
end
% Destructor
function delete(this)
delete(this.sh);
end
% Disp - pass on to the underlying stream
function disp(this, varargin)
disp(this.sh, varargin{:});
end
% Pass on set and get requests to the underlying stream
function varargout = get(this, varargin)
[varargout{1:nargout}] = get(this.sh, varargin{:});
end
function set(this, varargin)
set(this.sh, varargin{:});
end
% The main function - read!
function A = read(this, frame)
A = get(this.buffer, frame); % Read from the buffered stream
this.curr_frame = frame;
end
% Forward calls like imstream(a) to read
function varargout = subsref(this, frame)
switch frame(1).type
case {'()', '{}'}
if numel(frame(1).subs) ~= 1
error('Only one dimensional indexing supported');
end
[varargout{1:nargout}] = read(this, frame(1).subs{1});
case '.'
if any(strcmp(frame(1).subs, methods(this)))
% Forward these references to the relevant method
[varargout{1:nargout}] = builtin('subsref', this, frame);
elseif any(strcmp(frame(1).subs, methods(this.sh))) || any(strcmp(frame(1).subs, properties(this.sh)))
% Forward these references to the video/image
% sequence class
[varargout{1:nargout}] = builtin('subsref', this.sh, frame);
else
error('%s is not a public property or method of the imstream or %s classes.', frame(1).subs, class(this.sh));
end
end
end
% Get the number of frames
function n = num_frames(this)
n = this.number_of_frames;
end
function n = numel(this, varargin)
if nargin > 1
n = prod(cellfun(@numel, varargin));
else
n = num_frames(this);
end
end
function sz = size(this, dim)
sz = [num_frames(this) 1];
if nargin < 2
return;
end
sz = sz(min(dim, end));
end
% Support the videoReader (from VideoIO toolbox) interface for backwards compatibility
function b = next(this)
b = step(this, 1);
end
function A = getframe(this)
A = read(this, this.curr_frame);
end
function A = getnext(this)
next(this);
A = getframe(this);
end
function b = step(this, delta)
b = seek(this, this.curr_frame + delta);
end
function b = seek(this, fnum)
b = isempty(read(this, fnum));
end
function this = close(this)
delete(this);
end
% Support for new videoReader methods
function b = hasFrame(this)
b = this.curr_frame < num_frames(this);
end
function A = readFrame(this)
A = read(this, this.curr_frame + 1);
end
% The low-level read, which uses time-based reads
function A = read_lowlevel(this, fnum)
fnum = double(fnum);
if fnum ~= this.last_frame_lowlevel + 1
this.sh.CurrentTime = (fnum - 1) / this.sh.FrameRate;
end
A = readFrame(this.sh);
this.last_frame_lowlevel = fnum;
end
end
% Other functions
methods(Static)
function b = isPlatformSupported()
b = true; % Always supported
end
function formats = getFileFormats()
formats = cat(2, VideoReader.getFileFormats(), imseq.getFileFormats());
end
end
end