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 OJPH_INFO(0x02000010, "Note: The .pfm implementation is "
273 "experimental. Here, we are assuming that the original data is "
274 "floating-point numbers.");
275
276 codestream.set_planar(false);
277 ojph::param_siz siz = codestream.access_siz();
278
279 ojph::ui32 num_comps = siz.get_num_components();
280 if (num_comps != 3 && num_comps != 1)
281 OJPH_ERROR(0x0200000C,
282 "The file has %d color components; this cannot be saved to"
283 " a .pfm file", num_comps);
284 bool all_same = true;
285 ojph::point p = siz.get_downsampling(0);
286 for (ojph::ui32 i = 1; i < siz.get_num_components(); ++i) {
287 ojph::point p1 = siz.get_downsampling(i);
288 all_same = all_same && (p1.x == p.x) && (p1.y == p.y);
289 }
290 if (!all_same)
291 OJPH_ERROR(0x0200000D,
292 "To save an image to ppm, all the components must have the "
293 "same downsampling ratio");
294 ojph::ui32 bit_depth[3];
295 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
296 bit_depth[c] = siz.get_bit_depth(c);
297 pfm.configure(siz.get_recon_width(0), siz.get_recon_height(0),
298 siz.get_num_components(), -1.0f, bit_depth);
299 pfm.open(output_filename);
300 base = &pfm;
301 }
302#ifdef OJPH_ENABLE_TIFF_SUPPORT
303 else if (is_matching(".tif", v) || is_matching(".tiff", v))
304 {
305 codestream.set_planar(false);
306 ojph::param_siz siz = codestream.access_siz();
307
308 bool all_same = true;
309 ojph::point p = siz.get_downsampling(0);
310 for (unsigned int i = 1; i < siz.get_num_components(); ++i)
311 {
312 ojph::point p1 = siz.get_downsampling(i);
313 all_same = all_same && (p1.x == p.x) && (p1.y == p.y);
314 }
315 if (!all_same)
316 OJPH_ERROR(0x02000005,
317 "To save an image to tif(f), all the components must have the "
318 "same downsampling ratio\n");
319 ojph::ui32 bit_depths[4] = { 0, 0, 0, 0 };
320 for (ojph::ui32 c = 0; c < siz.get_num_components(); c++)
321 {
322 bit_depths[c] = siz.get_bit_depth(c);
323 }
324 tif.configure(siz.get_recon_width(0), siz.get_recon_height(0),
325 siz.get_num_components(), bit_depths);
326 tif.open(output_filename);
327 base = &tif;
328 }
329#endif // !OJPH_ENABLE_TIFF_SUPPORT
330 else if (is_matching(".yuv", v))
331 {
332 codestream.set_planar(true);
333 ojph::param_siz siz = codestream.access_siz();
334
335 if (siz.get_num_components() != 3 && siz.get_num_components() != 1)
336 OJPH_ERROR(0x02000006,
337 "The file has %d color components; this cannot be saved to"
338 " .yuv file\n", siz.get_num_components());
339 ojph::param_cod cod = codestream.access_cod();
340 if (cod.is_using_color_transform())
341 OJPH_ERROR(0x02000007,
342 "The current implementation of yuv file object does not"
343 " support saving file when conversion from yuv to rgb is"
344 " needed; in any case, this is not the normal usage of yuv"
345 "file.");
346 ojph::ui32 comp_widths[3];
347 ojph::ui32 max_bit_depth = 0;
348 for (ojph::ui32 i = 0; i < siz.get_num_components(); ++i)
349 {
350 comp_widths[i] = siz.get_recon_width(i);
351 max_bit_depth = ojph_max(max_bit_depth, siz.get_bit_depth(i));
352 }
353 codestream.set_planar(true);
354 yuv.configure(max_bit_depth, siz.get_num_components(), comp_widths);
355 yuv.open(output_filename);
356 base = &yuv;
357 }
358 else if (is_matching(".raw", v))
359 {
360 ojph::param_siz siz = codestream.access_siz();
361
362 if (siz.get_num_components() != 1)
363 OJPH_ERROR(0x02000008,
364 "The file has %d color components; this cannot be saved to"
365 " .raw file (only one component is allowed).\n",
366 siz.get_num_components());
367 bool is_signed = siz.is_signed(0);
368 ojph::ui32 width = siz.get_recon_width(0);
369 ojph::ui32 bit_depth = siz.get_bit_depth(0);
370 raw.configure(is_signed, bit_depth, width);
371 raw.open(output_filename);
372 base = &raw;
373 }
374 else
375#ifdef OJPH_ENABLE_TIFF_SUPPORT
376 OJPH_ERROR(0x02000009,
377 "unknown output file extension; only pgm, ppm, tif(f) and raw(yuv))"
378 " are supported\n");
379#else
380 OJPH_ERROR(0x0200000A,
381 "unknown output file extension; only pgm, ppm, and raw(yuv) are"
382 " supported\n");
383#endif // !OJPH_ENABLE_TIFF_SUPPORT
384 }
385 else
386 OJPH_ERROR(0x0200000B,
387 "Please supply a proper output filename with a proper extension\n");
388
389 codestream.create();
390
391 if (codestream.is_planar())
392 {
393 ojph::param_siz siz = codestream.access_siz();
394 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
395 {
396 ojph::ui32 height = siz.get_recon_height(c);
397 for (ojph::ui32 i = height; i > 0; --i)
398 {
399 ojph::ui32 comp_num;
400 ojph::line_buf *line = codestream.pull(comp_num);
401 assert(comp_num == c);
402 base->write(line, comp_num);
403 }
404 }
405 }
406 else
407 {
408 ojph::param_siz siz = codestream.access_siz();
409 ojph::ui32 height = siz.get_recon_height(0);
410 for (ojph::ui32 i = 0; i < height; ++i)
411 {
412 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
413 {
414 ojph::ui32 comp_num;
415 ojph::line_buf *line = codestream.pull(comp_num);
416 assert(comp_num == c);
417 base->write(line, comp_num);
418 }
419 }
420 }
421
422 base->close();
423 codestream.close();
424 }
425 catch (const std::exception& e)
426 {
427 const char *p = e.what();
428 if (strncmp(p, "ojph error", 10) != 0)
429 printf("%s\n", p);
430 exit(-1);
431 }
432
433 clock_t end = clock();
434 double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
435 printf("Elapsed time = %f\n", elapsed_secs);
436
437 return 0;
438}
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_...
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
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
#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_INFO(t,...)
MACROs to insert file and line number for info, warning, and error.
#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