cgv
Loading...
Searching...
No Matches
sliced_volume_io.cxx
1#if _MSC_VER > 1400
2#pragma warning(disable:4996)
3#endif
4
5#include "sliced_volume_io.h"
6#include <cgv/utils/scan.h>
7#include <cgv/utils/file.h>
9#include <cgv/utils/dir.h>
10#include <cgv/os/cmdline_tools.h>
11#include <cgv/media/video/video_reader.h>
12#include <cgv/media/image/image_reader.h>
13
14
15namespace cgv {
16 namespace media {
17 namespace volume {
18
19 enum SourceType {
20 ST_FILTER,
21 ST_INDEX,
22 ST_VIDEO
23 };
24
25 bool read_volume_from_video_with_ffmpeg(volume& V, const std::string& file_name,
26 volume::dimension_type dims, volume::extent_type extent, const cgv::data::component_format& cf,
27 size_t offset, FlipType flip_t, void (*on_progress_update)(int,void*), void* user_data, bool cycle_till_eof)
28 {
29 std::string fn_in_quotes = std::string("\"") + cgv::utils::file::platform_path(file_name) + "\"";
30 if (dims(0) == -1 || dims(1) == -1 || dims(2) < 0) {
31 int w, h, n = dims(2);
32 while (true) {
33 std::string cmd = "ffprobe -v error ";
34 if (dims(2) == -2)
35 cmd += "-count_frames ";
36 cmd += "-select_streams v:0 -show_entries stream=width,height";
37 if (dims(2) == -2)
38 cmd += ",nb_read_frames";
39 else
40 cmd += ",nb_frames";
41 cmd += " -show_entries side_data=rotation -of default=nokey=1:noprint_wrappers=1 ";
42 cmd += fn_in_quotes;
43 std::cout << "Analyze Video with " << cmd << std::endl;
44 std::string video_info = cgv::os::query_system_output(cmd, false);
45 std::vector<cgv::utils::line> lines;
46 cgv::utils::split_to_lines(video_info, lines, true);
47 bool do_swap_sides = false;
48 if (lines.size() >= 4) {
49 int rot;
50 if (cgv::utils::is_integer(lines[3].begin, lines[3].end, rot)) {
51 if (rot == 90 || rot == -90)
52 do_swap_sides = true;
53 }
54 }
55 std::stringstream ss(video_info);
56 ss >> w >> h;
57 if (do_swap_sides)
58 std::swap(w, h);
59 if (dims(2) < 0)
60 ss >> n;
61 // reattempt if nb_frames not supported by video format
62 if (ss.fail()) {
63 if (dims(2) == -1) {
64 dims(2) = -2;
65 std::cerr << "Video file <" << file_name << "> does not support nb_frames - need to could frames, please wait!" << std::endl;
66 continue;
67 }
68 std::cerr << "Error: could not analyze video file <" << file_name << "> with ffprobe" << std::endl;
69 return false;
70 }
71 else
72 break;
73 }
74 if (dims(2) < 0 && offset > n) {
75 std::cerr << "Error: frame offset " << offset << " larger than frame count of video file <" << file_name << "> with ffprobe" << std::endl;
76 return false;
77 }
78 if (dims(0) == -1) {
79 dims(0) = w;
80 extent(0) *= -dims(0);
81 }
82 else if (dims(0) != w) {
83 std::cerr << "Warning: mismatching video width - "
84 << dims(0) << " in svx file '" << file_name << "' vs "
85 << w << " in video file '" << file_name << "'" << std::endl;
86 }
87 if (dims(1) == -1) {
88 dims(1) = h;
89 extent(1) *= -dims(1);
90 }
91 else if (dims(1) != h) {
92 std::cerr << "Warning: mismatching video height - "
93 << dims(1) << " in svx file '" << file_name << "' vs "
94 << h << " in video file '" << file_name << "'" << std::endl;
95 }
96 if (dims(2) < 0) {
97 dims(2) = int(n - offset);
98 extent(2) *= -dims(2);
99 }
100 //else if (dims(2) + offset > n) {
101 // std::cerr << "Warning: corrected video frame count from svx (" << dims(2)
102 // << ") to smaller count found in video file (" << n - offset << ")" << std::endl;
103 // extent(2) *= float(n - offset) / dims(2);
104 // dims(2) = int(n - offset);
105 //}
106 }
107 V.get_format().set_component_format(cf);
108 V.get_format().set_width(dims(0));
109 V.get_format().set_height(dims(1));
110 V.get_format().set_depth(dims(2));
111 if (on_progress_update)
112 on_progress_update(-1, user_data);
113 dims(2) = int(V.get_format().get_depth());
114 V.resize(dims);
115 V.ref_extent() = extent;
116 std::string cmd = "ffmpeg -i ";
117 cmd += fn_in_quotes;
118 cmd += " -loglevel quiet";
119 if (!cycle_till_eof) {
120 cmd += " -frames:v ";
121 cmd += cgv::utils::to_string(dims(2));
122 }
123 if (offset > 0) {
124 cmd += " -vf \"select=gte(n\\,";
125 cmd += cgv::utils::to_string(offset);
126 cmd += ")\" ";
127 }
128 if(flip_t != FT_NO_FLIP)
129 {
130 if (offset == 0)
131 cmd += " -vf ";
132 if (flip_t == FT_HORIZONTAL)
133 cmd += "hflip ";
134 if (flip_t == FT_VERTICAL)
135 cmd += "vflip ";
136 if (flip_t == FT_VERTICAL_AND_HORIZONTAL)
137 cmd += "hflip vflip ";
138 }
139 cmd += " -f rawvideo -pix_fmt ";
140 switch (cf.get_standard_component_format()) {
141 case cgv::data::CF_L:
142 switch (cf.get_component_type()) {
143 case cgv::type::info::TI_UINT8: cmd += "gray"; break;
144 case cgv::type::info::TI_UINT16: cmd += "gray16be"; break;
145 default:
146 std::cerr << "unsupported luminance component type " << get_type_name(cf.get_component_type()) << std::endl;
147 return false;
148 }
149 break;
151 switch (cf.get_component_type()) {
152 case cgv::type::info::TI_UINT8: cmd += "rgb24"; break;
153 default:
154 std::cerr << "unsupported rgb component type " << get_type_name(cf.get_component_type()) << std::endl;
155 return false;
156 }
157 break;
158 default:
159 std::cerr << "unsupported component format " << (int)cf.get_standard_component_format() << std::endl;
160 return false;
161 }
162 cmd += " pipe:1";
163 std::cout << "COMMAND:\n" << cmd << "\n" << std::endl;
164 if (on_progress_update)
165 on_progress_update(0, user_data);
166
167 size_t bytes_read = cgv::os::read_system_output(cmd, V.get_data_ptr<uint8_t>(), V.get_size(),
168 "reading video", false, on_progress_update, user_data, V.get_slice_size(), cycle_till_eof);
169 if (bytes_read < V.get_size()) {
170 std::cerr << "Warning: could only read " << bytes_read << " of volume with size " << V.get_size() << std::endl;
171 }
172 if (on_progress_update)
173 on_progress_update(V.get_dimensions()(2) + 1, user_data);
174 return true;
175 }
176 bool read_from_sliced_volume(const std::string& file_name, volume& V)
177 {
178 ooc_sliced_volume svol;
179 if (!svol.open_read(file_name)) {
180 std::cerr << "could not open " << file_name << std::endl;
181 return false;
182 }
183 volume::dimension_type dims = svol.get_dimensions();
184
185 std::vector<std::string> slice_file_names;
186 std::string file_path;
187 SourceType st = ST_VIDEO;
188 if (svol.file_name_pattern.find_first_of('*') != std::string::npos)
189 st = ST_FILTER;
190 else if (svol.file_name_pattern.find_first_of('$') != std::string::npos)
191 st = ST_INDEX;
192 if (st == ST_FILTER) {
193 file_path = cgv::utils::file::get_path(svol.file_name_pattern);
194 if (!file_path.empty() && file_path.back() != '/')
195 file_path += '/';
196 void* handle = cgv::utils::file::find_first(svol.file_name_pattern);
197 while (handle != 0) {
198 if (!cgv::utils::file::find_directory(handle))
199 slice_file_names.push_back(cgv::utils::file::find_name(handle));
200 handle = cgv::utils::file::find_next(handle);
201 }
202 }
205 cgv::data::data_view* dv_ptr = 0;
206 if (st == ST_VIDEO) {
207 // first try to setup video reader
208 vr_ptr = new cgv::media::video::video_reader(df);
209 if (vr_ptr->open(svol.file_name_pattern)) {
210 svol.set_component_type(df.get_component_type());
211 svol.set_component_format(df.get_standard_component_format());
212 if (dims(2) == -1) {
213 dims(2) = vr_ptr->get<uint32_t>("nr_frames");
214 svol.ref_extent()(2) *= -dims(2);
215 }
216 dv_ptr = new cgv::data::data_view(&df);
217 }
218 // if this does not work use ffmpeg to read video file
219 else {
220 delete vr_ptr;
221 vr_ptr = 0;
222 bool result = read_volume_from_video_with_ffmpeg(V, svol.file_name_pattern, dims, svol.get_extent(), svol.get_format().get_component_format(), svol.offset);
223 svol.close();
224 return result;
225 }
226 }
227 else {
228 if (dims(2) == -1) {
229 if (st == ST_FILTER)
230 dims(2) = int(slice_file_names.size() - svol.offset);
231 else {
232 dims(2) = 0;
233 while (cgv::utils::file::exists(svol.get_slice_file_name(dims(2))))
234 ++dims(2);
235 }
236 svol.ref_extent()(2) *= -dims(2);
237 }
238 if ((dims(0) == -1 || dims(1) == -1)) {
239 std::string fst_file_name;
240 if (st == ST_FILTER) {
241 if (svol.offset >= slice_file_names.size()) {
242 std::cerr << "ERROR: offset larger than number of files" << std::endl;
243 return false;
244 }
245 fst_file_name = file_path + slice_file_names[svol.offset];
246 }
247 else {
248 fst_file_name = svol.get_slice_file_name(0);
249 }
250 if (!cgv::utils::file::exists(fst_file_name)) {
251 std::cerr << "ERROR could not find first slice <" << fst_file_name << ">" << std::endl;
252 return false;
253 }
255 if (!ir.open(fst_file_name)) {
256 std::cerr << "ERROR could not open first slice <" << fst_file_name << ">" << std::endl;
257 return false;
258 }
259 }
260 }
261 if (dims(0) == -1) {
262 dims(0) = int(df.get_width());
263 svol.ref_extent()(0) *= -dims(0);
264 }
265 if (dims(1) == -1) {
266 dims(1) = int(df.get_height());
267 svol.ref_extent()(1) *= -dims(1);
268 }
269 svol.resize(dims);
270
271 V.get_format().set_component_format(svol.get_format().get_component_format());
272 V.resize(dims);
273 V.ref_extent() = svol.get_extent();
274
275 std::size_t slize_size = V.get_voxel_size() * V.get_format().get_width() * V.get_format().get_height();
276 cgv::type::uint8_type* dst_ptr = V.get_data_ptr<cgv::type::uint8_type>();
277
278 for (int i = 0; i < (int)dims(2); ++i) {
279 switch (st) {
280 case ST_INDEX:
281 if (!svol.read_slice(i)) {
282 std::cerr << "could not read slice " << i << " with filename \"" << svol.get_slice_file_name(i) << "\"." << std::endl;
283 return false;
284 }
285 break;
286 case ST_FILTER: {
287 int j = i + svol.offset;
288 if ((unsigned)j > slice_file_names.size()) {
289 std::cerr << "could not read slice " << i << " from with filename with index " << j << " as only " << slice_file_names.size() << " match pattern." << std::endl;
290 return false;
291 }
292 if (!svol.read_slice(i, file_path + slice_file_names[j])) {
293 std::cerr << "could not read slice " << i << " from file \"" << slice_file_names[j] << "\"." << std::endl;
294 return false;
295 }
296 break;
297 }
298 case ST_VIDEO:
299 if (!vr_ptr->read_frame(*dv_ptr)) {
300 std::cerr << "could not frame " << i << " from avi file \"" << svol.file_name_pattern << "\"." << std::endl;
301 return false;
302 }
303 break;
304 }
305 const cgv::type::uint8_type* src_ptr =
306 st == ST_VIDEO ? dv_ptr->get_ptr<cgv::type::uint8_type>() :
307 svol.get_data_ptr<cgv::type::uint8_type>();
308 std::copy(src_ptr, src_ptr + slize_size, dst_ptr);
309 dst_ptr += slize_size;
310 }
311 svol.close();
312 return true;
313 }
314
315 bool write_as_sliced_volume(const std::string& file_name, const std::string& _file_name_pattern, const volume& V)
316 {
317 ooc_sliced_volume svol;
318 svol.get_format().set_component_format(V.get_format().get_component_format());
319 svol.resize(V.get_dimensions());
320 svol.ref_extent() = V.get_extent();
321 std::string file_name_pattern = _file_name_pattern;
322 cgv::utils::replace(file_name_pattern, "$h", cgv::utils::file::get_file_name(file_name));
323 std::string path = cgv::utils::file::get_path(file_name);
324 if (path.size() > 0)
325 file_name_pattern = path + "/" + file_name_pattern;
326 if (!svol.open_write(file_name, file_name_pattern, unsigned(V.get_format().get_depth()))) {
327 std::cerr << "could not open " << file_name << " for write." << std::endl;
328 return false;
329 }
330
331 std::size_t slize_size = V.get_voxel_size() * V.get_format().get_width() * V.get_format().get_height();
332 const cgv::type::uint8_type* src_ptr = V.get_data_ptr<cgv::type::uint8_type>();
333 cgv::type::uint8_type* dst_ptr = svol.get_data_ptr<cgv::type::uint8_type>();
334 for (int i = 0; i < (int)svol.get_nr_slices(); ++i) {
335 std::copy(src_ptr, src_ptr + slize_size, dst_ptr);
336 if (!svol.write_slice(i))
337 return false;
338 src_ptr += slize_size;
339 }
340 svol.close();
341 return true;
342 }
343
344 }
345 }
346}
More advanced text processing for splitting text into lines or tokens.
T get(const std::string &property)
query a property of the element and perform standard conversions if necessary.
Definition base.h:192
complete implementation of method actions that only call one method when entering a node
Definition action.h:113
the component format inherits the information of a packing_info and adds information on the component...
cgv::type::info::TypeId get_component_type() const
return the component type
ComponentFormat get_standard_component_format() const
return whether the component format is one of the standard formats
A data_format describes a multidimensional data block of data entries.
Definition data_format.h:17
size_t get_width() const
return the resolution in the first dimension, or 1 if not defined
size_t get_height() const
return the resolution in the second dimension, or 1 if not defined
cgv::type::func::transfer_const< P, S * >::type get_ptr() const
return a data pointer to type S
Definition data_view.h:61
the data view gives access to a data array of one, two, three or four dimensions.
Definition data_view.h:153
the image reader chooses a specific reader automatically based on the extension of the given file nam...
the video reader chooses a specific reader automatically based on the extension of the given file nam...
bool open(const std::string &file_name)
open the file and read the video header in order to determine the image format
bool read_frame(cgv::data::data_view &dv)
read the next frame to the given data view, if this is empty recreate it with a newly allocated data ...
@ CF_L
alpha format
@ CF_RGB
color format with two components R and G
const char * get_type_name(TypeId tid)
function that returns the name of a type specified through TypeId
Definition type_id.cxx:117
@ TI_UINT8
signed integer stored in 64 bits
Definition type_id.h:23
@ TI_UINT16
unsigned integer stored in 8 bits
Definition type_id.h:24
unsigned char uint8_type
this type provides an 8 bit unsigned integer type
unsigned int replace(std::string &s, char c1, char c2)
replace char c1 with c2 in the given string _s and return number of replacements
Definition scan.cxx:159
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
bool is_integer(const char *begin, const char *end, int &value)
check if the text range (begin,end( defines an integer value. If yes, store the value in the passed r...
Definition scan.cxx:367
void split_to_lines(const char *global_begin, const char *global_end, std::vector< line > &lines, bool truncate_trailing_spaces)
this function splits a text range at the newline characters into single lines.
the cgv namespace
Definition print.h:11
Helper functions to process strings.