OpenJPH
Open-source implementation of JPEG2000 Part-15
Loading...
Searching...
No Matches
ojph_expand.cpp
Go to the documentation of this file.
1//***************************************************************************/
2// This software is released under the 2-Clause BSD license, included
3// below.
4//
5// Copyright (c) 2019, Aous Naman
6// Copyright (c) 2019, Kakadu Software Pty Ltd, Australia
7// Copyright (c) 2019, The University of New South Wales, Australia
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// 1. Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//
16// 2. Redistributions in binary form must reproduce the above copyright
17// notice, this list of conditions and the following disclaimer in the
18// documentation and/or other materials provided with the distribution.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//***************************************************************************/
32// This file is part of the OpenJPH software implementation.
33// File: ojph_expand.cpp
34// Author: Aous Naman
35// Date: 28 August 2019
36//***************************************************************************/
37
38#include <ctime>
39#include <iostream>
40#include <cstdlib>
41
42#include "ojph_arg.h"
43#include "ojph_mem.h"
44#include "ojph_img_io.h"
45#include "ojph_file.h"
46#include "ojph_codestream.h"
47#include "ojph_params.h"
48#include "ojph_message.h"
49
52{
53 ui32_list_interpreter(const int max_num_elements, int& num_elements,
54 ojph::ui32* list)
55 : max_num_eles(max_num_elements), si32list(list), num_eles(num_elements)
56 {}
57
58 virtual void operate(const char *str)
59 {
60 const char *next_char = str;
61 num_eles = 0;
62 do
63 {
64 if (num_eles)
65 {
66 if (*next_char != ',') //separate res by a comma
67 throw "resolutions in a list must be separated by a comma";
68 next_char++;
69 }
70 char *endptr;
71 si32list[num_eles] = (ojph::ui32)strtoul(next_char, &endptr, 10);
72 if (endptr == next_char)
73 throw "resolution number is improperly formatted";
74 next_char = endptr;
75 ++num_eles;
76 }
77 while (*next_char == ',' && num_eles < max_num_eles);
78 if (num_eles + 1 < max_num_eles)
79 {
80 if (*next_char)
81 throw "list elements must separated by a "",""";
82 }
83 else if (*next_char)
84 throw "there are too many elements in the resolution list";
85 }
86
87 const int max_num_eles;
90};
91
93static
94bool get_arguments(int argc, char *argv[],
95 char *&input_filename, char *&output_filename,
96 ojph::ui32& skipped_res_for_read,
97 ojph::ui32& skipped_res_for_recon,
98 bool& resilient)
99{
100 ojph::cli_interpreter interpreter;
101 interpreter.init(argc, argv);
102
103 ojph::ui32 skipped_res[2] = {0, 0};
104 int num_skipped_res = 0;
105 ui32_list_interpreter ilist(2, num_skipped_res, skipped_res);
106
107 interpreter.reinterpret("-i", input_filename);
108 interpreter.reinterpret("-o", output_filename);
109 interpreter.reinterpret("-skip_res", &ilist);
110 interpreter.reinterpret("-resilient", resilient);
111
112 //interpret skipped_string
113 if (num_skipped_res > 0)
114 {
115 skipped_res_for_read = skipped_res[0];
116 if (num_skipped_res > 1)
117 skipped_res_for_recon = skipped_res[1];
118 else
119 skipped_res_for_recon = skipped_res_for_read;
120 }
121
122 if (interpreter.is_exhausted() == false) {
123 printf("The following arguments were not interpreted:\n");
124 ojph::argument t = interpreter.get_argument_zero();
125 t = interpreter.get_next_avail_argument(t);
126 while (t.is_valid()) {
127 printf("%s\n", t.arg);
128 t = interpreter.get_next_avail_argument(t);
129 }
130 return false;
131 }
132 return true;
133}
134
136static
137const char* get_file_extension(const char* filename)
138{
139 size_t len = strlen(filename);
140 const char* p = strrchr(filename, '.');
141 if (p == NULL || p == filename + len - 1)
142 OJPH_ERROR(0x01000071,
143 "no file extension is found, or there are no characters "
144 "after the dot \'.\' for filename \"%s\" \n", filename);
145 return p;
146}
147
149static
150bool is_matching(const char *ref, const char *other)
151{
152 size_t num_ele = strlen(ref);
153
154 if (num_ele != strlen(other))
155 return false;
156
157 for (ojph::ui32 i = 0; i < num_ele; ++i)
158 if (ref[i] != other[i] && ref[i] != tolower(other[i]))
159 return false;
160
161 return true;
162}
163
165int main(int argc, char *argv[]) {
166
167 char *input_filename = NULL;
168 char *output_filename = NULL;
169 ojph::ui32 skipped_res_for_read = 0;
170 ojph::ui32 skipped_res_for_recon = 0;
171 bool resilient = false;
172
173 if (argc <= 1) {
174 std::cout <<
175 "\nThe following arguments are necessary:\n"
176 " -i <input file name>\n"
177#ifdef OJPH_ENABLE_TIFF_SUPPORT
178 " -o <output file name> (either pgm, ppm, tif(f), or raw(yuv))\n\n"
179#else
180 " -o <output file name> (either pgm, ppm, or raw(yuv))\n\n"
181#endif // !OJPH_ENABLE_TIFF_SUPPORT
182 "The following arguments are optional:\n"
183 " -skip_res x,y a comma-separated list of two elements containing the\n"
184 " number of resolutions to skip. You can specify 1 or 2\n"
185 " parameters; the first specifies the number of resolution\n"
186 " for which data reading is skipped. The second is the\n"
187 " number of skipped resolution for reconstruction, which is\n"
188 " either equal to the first or smaller. If the second is not\n"
189 " specified, it is made to equal to the first.\n"
190 " -resilient <true | false> if 'true', the decoder will not exit when\n"
191 " running into recoverable errors in the codestream.\n"
192 " Default: 'false'.\n"
193 "\n"
194 ;
195 return -1;
196 }
197 if (!get_arguments(argc, argv, input_filename, output_filename,
198 skipped_res_for_read, skipped_res_for_recon,
199 resilient))
200 {
201 return -1;
202 }
203
204 clock_t begin = clock();
205
206 try {
207 if (output_filename == NULL)
208 OJPH_ERROR(0x02000001,
209 "Please provide an output file using the -o option\n");
210
211 ojph::j2c_infile j2c_file;
212 j2c_file.open(input_filename);
213 ojph::codestream codestream;
214
215 ojph::ppm_out ppm;
216 ojph::pfm_out pfm;
217 #ifdef OJPH_ENABLE_TIFF_SUPPORT
218 ojph::tif_out tif;
219 #endif /* OJPH_ENABLE_TIFF_SUPPORT */
220 ojph::yuv_out yuv;
221 ojph::raw_out raw;
222 ojph::image_out_base *base = NULL;
223 const char *v = get_file_extension(output_filename);
224 if (v)
225 {
226 if (resilient)
227 codestream.enable_resilience();
228 codestream.read_headers(&j2c_file);
229 codestream.restrict_input_resolution(skipped_res_for_read,
230 skipped_res_for_recon);
231 ojph::param_siz siz = codestream.access_siz();
232
233 if (is_matching(".pgm", v))
234 {
235
236 if (siz.get_num_components() != 1)
237 OJPH_ERROR(0x02000002,
238 "The file has more than one color component, but .pgm can "
239 "contain only one color component\n");
240 ppm.configure(siz.get_recon_width(0), siz.get_recon_height(0),
241 siz.get_num_components(), siz.get_bit_depth(0));
242 ppm.open(output_filename);
243 base = &ppm;
244 }
245 else if (is_matching(".ppm", v))
246 {
247 codestream.set_planar(false);
248 ojph::param_siz siz = codestream.access_siz();
249
250 if (siz.get_num_components() != 3)
251 OJPH_ERROR(0x02000003,
252 "The file has %d color components; this cannot be saved to"
253 " a .ppm file\n", siz.get_num_components());
254 bool all_same = true;
255 ojph::point p = siz.get_downsampling(0);
256 for (ojph::ui32 i = 1; i < siz.get_num_components(); ++i)
257 {
258 ojph::point p1 = siz.get_downsampling(i);
259 all_same = all_same && (p1.x == p.x) && (p1.y == p.y);
260 }
261 if (!all_same)
262 OJPH_ERROR(0x02000004,
263 "To save an image to ppm, all the components must have the "
264 "same downsampling ratio\n");
265 ppm.configure(siz.get_recon_width(0), siz.get_recon_height(0),
266 siz.get_num_components(), siz.get_bit_depth(0));
267 ppm.open(output_filename);
268 base = &ppm;
269 }
270 else if (is_matching(".pfm", v))
271 {
272 codestream.set_planar(false);
273 ojph::param_siz siz = codestream.access_siz();
274 ojph::param_cod cod = codestream.access_cod();
275 ojph::param_nlt nlt = codestream.access_nlt();
276
277 ojph::ui32 num_comps = siz.get_num_components();
278 if (num_comps != 3 && num_comps != 1)
279 OJPH_ERROR(0x0200000C,
280 "The file has %d color components; this cannot be saved to"
281 " a .pfm file\n", num_comps);
282 bool all_same = true;
283 ojph::point p = siz.get_downsampling(0);
284 for (ojph::ui32 i = 1; i < siz.get_num_components(); ++i)
285 {
286 ojph::point p1 = siz.get_downsampling(i);
287 all_same = all_same && (p1.x == p.x) && (p1.y == p.y);
288 }
289 if (!all_same)
290 OJPH_ERROR(0x0200000D,
291 "To save an image to ppm, all the components must have the "
292 "same downsampling ratio\n");
293 ojph::ui32 bit_depth[3];
294 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c) {
295 ojph::ui8 bd = 0;
296 bool is = true;
297 bool result = nlt.get_type3_transformation(c, bd, is);
298 if (result == false)
299 OJPH_ERROR(0x0200000E,
300 "This codestream is not supported; it does not have an "
301 "NLT segment marker for this component (or no default NLT "
302 "settings) .\n");
303 if (bd != siz.get_bit_depth(c) || is != siz.is_signed(c))
304 OJPH_ERROR(0x0200000F,
305 "There is discrepancy in component %d configuration between "
306 "SIZ marker segment, which specifies bit_depth = %d and "
307 "signedness = %s, and NLT marker segment, which specifies "
308 "bit_depth = %d and signedness = %s.\n", c,
309 siz.get_bit_depth(c), is != siz.is_signed(c) ? "True" : "False",
310 bd, is ? "True" : "False");
311 bit_depth[c] = bd;
312 }
313 if (!cod.is_reversible())
314 OJPH_ERROR(0x02000010,
315 "This codestream is lossy (not reversible), and we currently "
316 "only support reversible codestreams for .pfm target files. "
317 "This is only temporary and will be changed at some point.\n");
318 pfm.configure(siz.get_recon_width(0), siz.get_recon_height(0),
319 siz.get_num_components(), -1.0f, bit_depth);
320 pfm.open(output_filename);
321 base = &pfm;
322 }
323#ifdef OJPH_ENABLE_TIFF_SUPPORT
324 else if (is_matching(".tif", v) || is_matching(".tiff", v))
325 {
326 codestream.set_planar(false);
327 ojph::param_siz siz = codestream.access_siz();
328
329 bool all_same = true;
330 ojph::point p = siz.get_downsampling(0);
331 for (unsigned int i = 1; i < siz.get_num_components(); ++i)
332 {
333 ojph::point p1 = siz.get_downsampling(i);
334 all_same = all_same && (p1.x == p.x) && (p1.y == p.y);
335 }
336 if (!all_same)
337 OJPH_ERROR(0x02000005,
338 "To save an image to tif(f), all the components must have the "
339 "same downsampling ratio\n");
340 ojph::ui32 bit_depths[4] = { 0, 0, 0, 0 };
341 for (ojph::ui32 c = 0; c < siz.get_num_components(); c++)
342 {
343 bit_depths[c] = siz.get_bit_depth(c);
344 }
345 tif.configure(siz.get_recon_width(0), siz.get_recon_height(0),
346 siz.get_num_components(), bit_depths);
347 tif.open(output_filename);
348 base = &tif;
349 }
350#endif // !OJPH_ENABLE_TIFF_SUPPORT
351 else if (is_matching(".yuv", v))
352 {
353 codestream.set_planar(true);
354 ojph::param_siz siz = codestream.access_siz();
355
356 if (siz.get_num_components() != 3 && siz.get_num_components() != 1)
357 OJPH_ERROR(0x02000006,
358 "The file has %d color components; this cannot be saved to"
359 " .yuv file\n", siz.get_num_components());
360 ojph::param_cod cod = codestream.access_cod();
361 if (cod.is_using_color_transform())
362 OJPH_ERROR(0x02000007,
363 "The current implementation of yuv file object does not"
364 " support saving file when conversion from yuv to rgb is"
365 " needed; in any case, this is not the normal usage of yuv"
366 "file.");
367 ojph::ui32 comp_widths[3];
368 ojph::ui32 max_bit_depth = 0;
369 for (ojph::ui32 i = 0; i < siz.get_num_components(); ++i)
370 {
371 comp_widths[i] = siz.get_recon_width(i);
372 max_bit_depth = ojph_max(max_bit_depth, siz.get_bit_depth(i));
373 }
374 codestream.set_planar(true);
375 yuv.configure(max_bit_depth, siz.get_num_components(), comp_widths);
376 yuv.open(output_filename);
377 base = &yuv;
378 }
379 else if (is_matching(".raw", v))
380 {
381 ojph::param_siz siz = codestream.access_siz();
382
383 if (siz.get_num_components() != 1)
384 OJPH_ERROR(0x02000008,
385 "The file has %d color components; this cannot be saved to"
386 " .raw file (only one component is allowed).\n",
387 siz.get_num_components());
388 bool is_signed = siz.is_signed(0);
389 ojph::ui32 width = siz.get_recon_width(0);
390 ojph::ui32 bit_depth = siz.get_bit_depth(0);
391 raw.configure(is_signed, bit_depth, width);
392 raw.open(output_filename);
393 base = &raw;
394 }
395 else
396#ifdef OJPH_ENABLE_TIFF_SUPPORT
397 OJPH_ERROR(0x02000009,
398 "unknown output file extension; only pgm, ppm, tif(f) and raw(yuv))"
399 " are supported\n");
400#else
401 OJPH_ERROR(0x0200000A,
402 "unknown output file extension; only pgm, ppm, and raw(yuv) are"
403 " supported\n");
404#endif // !OJPH_ENABLE_TIFF_SUPPORT
405 }
406 else
407 OJPH_ERROR(0x0200000B,
408 "Please supply a proper output filename with a proper extension\n");
409
410 codestream.create();
411
412 if (codestream.is_planar())
413 {
414 ojph::param_siz siz = codestream.access_siz();
415 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
416 {
417 ojph::ui32 height = siz.get_recon_height(c);
418 for (ojph::ui32 i = height; i > 0; --i)
419 {
420 ojph::ui32 comp_num;
421 ojph::line_buf *line = codestream.pull(comp_num);
422 assert(comp_num == c);
423 base->write(line, comp_num);
424 }
425 }
426 }
427 else
428 {
429 ojph::param_siz siz = codestream.access_siz();
430 ojph::ui32 height = siz.get_recon_height(0);
431 for (ojph::ui32 i = 0; i < height; ++i)
432 {
433 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
434 {
435 ojph::ui32 comp_num;
436 ojph::line_buf *line = codestream.pull(comp_num);
437 assert(comp_num == c);
438 base->write(line, comp_num);
439 }
440 }
441 }
442
443 base->close();
444 codestream.close();
445 }
446 catch (const std::exception& e)
447 {
448 const char *p = e.what();
449 if (strncmp(p, "ojph error", 10) != 0)
450 printf("%s\n", p);
451 exit(-1);
452 }
453
454 clock_t end = clock();
455 double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
456 printf("Elapsed time = %f\n", elapsed_secs);
457
458 return 0;
459}
bool is_valid()
Definition ojph_arg.h:58
void init(int argc, char *argv[])
Definition ojph_arg.h:73
void reinterpret(const char *str, int &val)
Definition ojph_arg.h:146
argument get_argument_zero()
Definition ojph_arg.h:126
argument get_next_avail_argument(const argument &arg)
Definition ojph_arg.h:133
The object represent a codestream.
param_siz access_siz()
Returns the underlying SIZ marker segment object.
param_cod access_cod()
Returns the underlying COD marker segment object.
void restrict_input_resolution(ui32 skipped_res_for_data, ui32 skipped_res_for_recon)
This function restricts resolution decoding for a codestream. It is for a reading (decoding) codestre...
void close()
Call this function to close the underlying file; works for both encoding and decoding codestreams.
void set_planar(bool planar)
Sets the sequence of pushing or pull rows from the machinery.
void enable_resilience()
This enables codestream resilience; that is, the library tries its best to decode the codestream,...
void read_headers(infile_base *file)
This call reads the headers of a codestream. It is for a reading (or decoding) codestream,...
void create()
This call is for a decoding (or reading) codestream. Call this function after calling restrict_input_...
param_nlt access_nlt()
Returns the underlying NLT marker segment object.
bool is_planar() const
Query if the codestream extraction is planar or not. See the documentation for ojph::codestream::set_...
line_buf * pull(ui32 &comp_num)
This call is to pull one row from the codestream, being decoded. The returned line_buf object holds o...
virtual ui32 write(const line_buf *line, ui32 comp_num)=0
virtual void close()
void open(const char *filename)
bool is_using_color_transform() const
bool is_reversible() const
non-linearity point transformation object (implements NLT marker segment)
bool get_type3_transformation(ui32 comp_num, ui8 &bit_depth, bool &is_signed)
get the state (enabled or disabled) of type 3 nonlinearity for a component or the default setting
ui32 get_bit_depth(ui32 comp_num) const
ui32 get_recon_height(ui32 comp_num) const
point get_downsampling(ui32 comp_num) const
ui32 get_recon_width(ui32 comp_num) const
bool is_signed(ui32 comp_num) const
ui32 get_num_components() const
void open(char *filename)
void configure(ui32 width, ui32 height, ui32 num_components, float scale, ui32 *bit_depth)
void open(char *filename)
void configure(ui32 width, ui32 height, ui32 num_components, ui32 bit_depth)
void open(char *filename)
void configure(bool is_signed, ui32 bit_depth, ui32 width)
void open(char *filename)
void configure(ui32 bit_depth, ui32 num_components, ui32 *comp_width)
uint32_t ui32
Definition ojph_defs.h:54
uint8_t ui8
Definition ojph_defs.h:50
#define ojph_max(a, b)
Definition ojph_defs.h:73
int main(int argc, char *argv[])
static const char * get_file_extension(const char *filename)
static bool get_arguments(int argc, char *argv[], char *&input_filename, char *&output_filename, ojph::ui32 &skipped_res_for_read, ojph::ui32 &skipped_res_for_recon, bool &resilient)
static bool is_matching(const char *ref, const char *other)
#define OJPH_ERROR(t,...)
virtual void operate(const char *str)
ui32_list_interpreter(const int max_num_elements, int &num_elements, ojph::ui32 *list)
const ojph::ui32 max_num_eles