1 /* This file is part of the Pangolin Project.
2  * http://github.com/stevenlovegrove/Pangolin
3  *
4  * Copyright (c) 2013 Steven Lovegrove
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include <pangolin/factory/factory_registry.h>
29 #include <pangolin/utils/file_utils.h>
30 #include <pangolin/video/drivers/images.h>
31 #include <pangolin/video/iostream_operators.h>
32 
33 #include <cstring>
34 #include <fstream>
35 
36 namespace pangolin
37 {
38 
LoadFrame(size_t i)39 bool ImagesVideo::LoadFrame(size_t i)
40 {
41     if( i < num_files) {
42         Frame& frame = loaded[i];
43         for(size_t c=0; c< num_channels; ++c) {
44             const std::string& filename = Filename(i,c);
45             const ImageFileType file_type = FileType(filename);
46 
47             if(file_type == ImageFileTypeUnknown && unknowns_are_raw) {
48                 frame.push_back( LoadImage( filename, raw_fmt, raw_width, raw_height, raw_fmt.bpp * raw_width / 8) );
49             }else{
50                 frame.push_back( LoadImage( filename, file_type ) );
51             }
52         }
53         return true;
54     }
55     return false;
56 }
57 
PopulateFilenamesFromJson(const std::string & filename)58 void ImagesVideo::PopulateFilenamesFromJson(const std::string& filename)
59 {
60     std::ifstream ifs( PathExpand(filename));
61     picojson::value json;
62     const std::string err = picojson::parse(json, ifs);
63     if(err.empty()) {
64         const std::string folder = PathParent(filename) + "/";
65         device_properties = json["device_properties"];
66         json_frames = json["frames"];
67 
68         num_files = json_frames.size();
69         if(num_files == 0) {
70             throw VideoException("Empty Json Image archive.");
71         }
72 
73         num_channels = json_frames[0]["stream_files"].size();
74         if(num_channels == 0) {
75             throw VideoException("Empty Json Image archive.");
76         }
77 
78         filenames.resize(num_channels);
79         for(size_t c=0; c < num_channels; ++c) {
80             filenames[c].resize(num_files);
81             for(size_t i = 0; i < num_files; ++i) {
82                 const std::string path = json_frames[i]["stream_files"][c].get<std::string>();
83                 filenames[c][i] = (path.size() && path[0] == '/') ? path : (folder + path);
84             }
85         }
86         loaded.resize(num_files);
87     }else{
88         throw VideoException(err);
89     }
90 }
91 
PopulateFilenames(const std::string & wildcard_path)92 void ImagesVideo::PopulateFilenames(const std::string& wildcard_path)
93 {
94     const std::vector<std::string> wildcards = Expand(wildcard_path, '[', ']', ',');
95     num_channels = wildcards.size();
96 
97     if(wildcards.size() == 1 ) {
98         const std::string expanded_path = PathExpand(wildcards[0]);
99         const std::string possible_archive_path = expanded_path + "/archive.json";
100 
101         if (FileLowercaseExtention(expanded_path) == ".json" ) {
102             PopulateFilenamesFromJson(wildcards[0]);
103             return;
104         }else if(FileExists(possible_archive_path)){
105             PopulateFilenamesFromJson(possible_archive_path);
106             return;
107         }
108     }
109 
110     filenames.resize(num_channels);
111 
112     for(size_t i = 0; i < wildcards.size(); ++i) {
113         const std::string channel_wildcard = PathExpand(wildcards[i]);
114         FilesMatchingWildcard(channel_wildcard, filenames[i]);
115         if(num_files == size_t(-1)) {
116             num_files = filenames[i].size();
117         }else{
118             if( num_files != filenames[i].size() ) {
119                 std::cerr << "Warning: Video Channels have unequal number of files" << std::endl;
120             }
121             num_files = std::min(num_files, filenames[i].size());
122         }
123         if(num_files == 0) {
124             throw VideoException("No files found for wildcard '" + channel_wildcard + "'");
125         }
126     }
127 
128     // Resize empty frames vector to hold future images.
129     loaded.resize(num_files);
130 }
131 
ConfigureStreamSizes()132 void ImagesVideo::ConfigureStreamSizes()
133 {
134     size_bytes = 0;
135     for(size_t c=0; c < num_channels; ++c) {
136         const TypedImage& img = loaded[0][c];
137         const StreamInfo stream_info(img.fmt, img.w, img.h, img.pitch, (unsigned char*)(size_bytes));
138         streams.push_back(stream_info);
139         size_bytes += img.h*img.pitch;
140     }
141 }
142 
ImagesVideo(const std::string & wildcard_path)143 ImagesVideo::ImagesVideo(const std::string& wildcard_path)
144     : num_files(-1), num_channels(0), next_frame_id(0),
145       unknowns_are_raw(false)
146 {
147     // Work out which files to sequence
148     PopulateFilenames(wildcard_path);
149 
150     // Load first image in order to determine stream sizes etc
151     LoadFrame(next_frame_id);
152 
153     ConfigureStreamSizes();
154 
155     // TODO: Queue frames in another thread.
156 }
157 
ImagesVideo(const std::string & wildcard_path,const PixelFormat & raw_fmt,size_t raw_width,size_t raw_height)158 ImagesVideo::ImagesVideo(const std::string& wildcard_path,
159                          const PixelFormat& raw_fmt,
160                          size_t raw_width, size_t raw_height
161 )   : num_files(-1), num_channels(0), next_frame_id(0),
162       unknowns_are_raw(true), raw_fmt(raw_fmt),
163       raw_width(raw_width), raw_height(raw_height)
164 {
165     // Work out which files to sequence
166     PopulateFilenames(wildcard_path);
167 
168     // Load first image in order to determine stream sizes etc
169     LoadFrame(next_frame_id);
170 
171     ConfigureStreamSizes();
172 
173     // TODO: Queue frames in another thread.
174 }
175 
~ImagesVideo()176 ImagesVideo::~ImagesVideo()
177 {
178 }
179 
180 //! Implement VideoInput::Start()
Start()181 void ImagesVideo::Start()
182 {
183 
184 }
185 
186 //! Implement VideoInput::Stop()
Stop()187 void ImagesVideo::Stop()
188 {
189 
190 }
191 
192 //! Implement VideoInput::SizeBytes()
SizeBytes() const193 size_t ImagesVideo::SizeBytes() const
194 {
195     return size_bytes;
196 }
197 
198 //! Implement VideoInput::Streams()
Streams() const199 const std::vector<StreamInfo>& ImagesVideo::Streams() const
200 {
201     return streams;
202 }
203 
204 //! Implement VideoInput::GrabNext()
GrabNext(unsigned char * image,bool)205 bool ImagesVideo::GrabNext( unsigned char* image, bool /*wait*/ )
206 {
207     if(next_frame_id < loaded.size()) {
208         Frame& frame = loaded[next_frame_id];
209 
210         if(frame.size() != num_channels) {
211             LoadFrame(next_frame_id);
212         }
213 
214         for(size_t c=0; c < num_channels; ++c){
215             TypedImage& img = frame[c];
216             if(!img.ptr || img.w != streams[c].Width() || img.h != streams[c].Height() ) {
217                 return false;
218             }
219             const StreamInfo& si = streams[c];
220             std::memcpy(image + (size_t)si.Offset(), img.ptr, si.SizeBytes());
221             img.Deallocate();
222         }
223         frame.clear();
224 
225         next_frame_id++;
226         return true;
227     }
228 
229     return false;
230 }
231 
232 //! Implement VideoInput::GrabNewest()
GrabNewest(unsigned char * image,bool wait)233 bool ImagesVideo::GrabNewest( unsigned char* image, bool wait )
234 {
235     return GrabNext(image,wait);
236 }
237 
GetCurrentFrameId() const238 size_t ImagesVideo::GetCurrentFrameId() const
239 {
240     return (int)next_frame_id - 1;
241 }
242 
GetTotalFrames() const243 size_t ImagesVideo::GetTotalFrames() const
244 {
245     return num_files;
246 }
247 
Seek(size_t frameid)248 size_t ImagesVideo::Seek(size_t frameid)
249 {
250     next_frame_id = std::max(size_t(0), std::min(frameid, num_files));
251     return next_frame_id;
252 }
253 
DeviceProperties() const254 const picojson::value& ImagesVideo::DeviceProperties() const
255 {
256     return device_properties;
257 }
258 
FrameProperties() const259 const picojson::value& ImagesVideo::FrameProperties() const
260 {
261     const size_t frame = GetCurrentFrameId();
262 
263     if( json_frames.evaluate_as_boolean() && frame < json_frames.size()) {
264         const picojson::value& frame_props = json_frames[frame];
265         if(frame_props.contains("frame_properties")) {
266             return frame_props["frame_properties"];
267         }
268     }
269 
270     return null_props;
271 }
272 
PANGOLIN_REGISTER_FACTORY(ImagesVideo)273 PANGOLIN_REGISTER_FACTORY(ImagesVideo)
274 {
275     struct ImagesVideoVideoFactory final : public FactoryInterface<VideoInterface> {
276         std::unique_ptr<VideoInterface> Open(const Uri& uri) override {
277             const bool raw = uri.Contains("fmt");
278             const std::string path = PathExpand(uri.url);
279 
280             if(raw) {
281                 const std::string sfmt = uri.Get<std::string>("fmt", "GRAY8");
282                 const PixelFormat fmt = PixelFormatFromString(sfmt);
283                 const ImageDim dim = uri.Get<ImageDim>("size", ImageDim(640,480));
284                 return std::unique_ptr<VideoInterface>( new ImagesVideo(path, fmt, dim.x, dim.y) );
285             }else{
286                 return std::unique_ptr<VideoInterface>( new ImagesVideo(path) );
287             }
288         }
289     };
290 
291     auto factory = std::make_shared<ImagesVideoVideoFactory>();
292     FactoryRegistry<VideoInterface>::I().RegisterFactory(factory, 20, "file");
293     FactoryRegistry<VideoInterface>::I().RegisterFactory(factory, 20, "files");
294     FactoryRegistry<VideoInterface>::I().RegisterFactory(factory, 10, "image");
295     FactoryRegistry<VideoInterface>::I().RegisterFactory(factory, 10, "images");
296 }
297 
298 }
299