Skip to content

Commit

Permalink
Merge pull request #154 from aous72/Adding_NLT
Browse files Browse the repository at this point in the history
Adding support for NLT marker segment of type 3.

The code is not very complete, but it is in a useful state for publishing.
The code also fixes an important bug in the block decoder in commit 9f8011c

This PR adds partial support for .pfm files, as explained next.
Lossy compression is not supported -- it is possible to add support at some future point.
Reversible coding of .pfm files is supported, where the NLT marker is automatically inserted.
However, the current implementation supports only 28-bit for encoding 27-bit for decoding.
Therefore floating point values stored in a .pfm file, which are 32-bit, need to be truncated.
Using the '-bit_depth' option, ojph_compress can perform this truncation.
'ojph_compress' should work correctly with codestreams generated with ojph_compress, converting truncated values back to normal floating point values.
  • Loading branch information
aous72 authored Sep 22, 2024
2 parents 8159ca1 + 796fb6b commit f9fdf80
Show file tree
Hide file tree
Showing 25 changed files with 1,225 additions and 165 deletions.
115 changes: 113 additions & 2 deletions src/apps/common/ojph_img_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ namespace ojph {

ui32 cur_line;
si64 start_of_data;
int planar;
bool planar;
ui32 bit_depth[3];
bool is_signed[3];
point subsampling[3];
Expand Down Expand Up @@ -446,6 +446,68 @@ namespace ojph {
size_t buffer_size;
};

////////////////////////////////////////////////////////////////////////////
//
//
//
//
//
////////////////////////////////////////////////////////////////////////////
class pfm_in : public image_in_base
{
public:
pfm_in(mem_fixed_allocator *p = NULL)
{
fh = 0;
fname = NULL;
alloc_p = p;
temp_buf = NULL;
temp_buf_byte_size = 0;
bit_depth[0] = bit_depth[1] = bit_depth[2] = 32;
scale = 0.0f;
little_endian = true;
width = height = num_comps = 0;

cur_line = 0;
start_of_data = 0;
}
virtual ~pfm_in()
{
close();
if (alloc_p == NULL && temp_buf)
free(temp_buf);
}

void open(const char* filename);
void finalize_alloc();
void configure(ui32* bit_depth) {
assert(num_comps != 0);
for (ui32 c = 0; c < num_comps; ++c)
this->bit_depth[c] = bit_depth[c];
}
virtual ui32 read(const line_buf* line, ui32 comp_num);
void close() { if(fh) { fclose(fh); fh = NULL; } fname = NULL; }

size get_size() { assert(fh); return size(width, height); }
ui32 get_width() { assert(fh); return width; }
ui32 get_height() { assert(fh); return height; }
ui32 get_num_components() { assert(fh); return num_comps; }

private:
FILE *fh;
const char *fname;
mem_fixed_allocator *alloc_p;
float *temp_buf;
size_t temp_buf_byte_size;
ui32 bit_depth[3]; // this truncates data to bit_depth in the LSB
float scale;
bool little_endian;
ui32 width, height, num_comps;
ui32 cur_line;
si64 start_of_data;
};


////////////////////////////////////////////////////////////////////////////
// Accelerators (defined in ojph_img_io_*)
typedef void (*conversion_fun)(const line_buf *ln0, const line_buf *ln1,
Expand Down Expand Up @@ -559,7 +621,7 @@ namespace ojph {
ui32 width, height, num_components;
ui32 bit_depth, bytes_per_sample;
ui8* buffer;
ui32 buffer_size;
size_t buffer_size;
ui32 cur_line, samples_per_line, bytes_per_line;
conversion_fun converter;
const line_buf *lptr[3];
Expand Down Expand Up @@ -703,6 +765,55 @@ namespace ojph {
ui8* buffer;
ui32 buffer_size;
};

////////////////////////////////////////////////////////////////////////////
//
//
//
//
//
////////////////////////////////////////////////////////////////////////////
class pfm_out : public image_out_base
{
public:
pfm_out()
{
fh = NULL;
fname = NULL;
buffer = NULL;
buffer_size = 0;
width = height = num_components = 0;
scale = -1.0f;
bit_depth[0] = bit_depth[1] = bit_depth[2] = 32;
cur_line = 0;
start_of_data = 0;
}
virtual ~pfm_out()
{
close();
if (buffer)
free(buffer);
}

void open(char* filename);
void configure(ui32 width, ui32 height, ui32 num_components,
float scale, ui32* bit_depth);
virtual ui32 write(const line_buf* line, ui32 comp_num);
virtual void close() { if(fh) { fclose(fh); fh = NULL; } fname = NULL; }

private:
FILE *fh;
const char *fname;
float* buffer;
size_t buffer_size;
ui32 width, height, num_components;
float scale;
ui32 bit_depth[3];
ui32 cur_line;
si64 start_of_data;
};


}

#endif // !OJPH_IMG_IO_H
135 changes: 132 additions & 3 deletions src/apps/ojph_compress/ojph_compress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,9 +526,9 @@ int main(int argc, char * argv[]) {
std::cout <<
"\nThe following arguments are necessary:\n"
#ifdef OJPH_ENABLE_TIFF_SUPPORT
" -i input file name (either pgm, ppm, tif(f), or raw(yuv))\n"
" -i input file name (either pgm, ppm, pfm, tif(f), or raw(yuv))\n"
#else
" -i input file name (either pgm, ppm, or raw(yuv))\n"
" -i input file name (either pgm, ppm, pfm, or raw(yuv))\n"
#endif // !OJPH_ENABLE_TIFF_SUPPORT
" -o output file name\n\n"

Expand Down Expand Up @@ -587,7 +587,28 @@ int main(int argc, char * argv[]) {
" component; for example: 12,10,10\n"
" -downsamp {x,y},{x,y},...,{x,y} a list of x,y points, one for each\n"
" component; for example {1,1},{2,2},{2,2}\n\n"
;
"\n"

".pfm files receive special treatment. Currently, lossy compression\n"
"with these files is not supported, only lossless. When these files are\n"
"used, the NLT segment marker is automatically inserted into the\n"
"codestream. For these files the following arguments can be useful\n"
" -signed a comma - separated list of true or false parameters, one\n"
" for each component; for example: true,false,false.\n"
" The sign only affects how values are treated; for negative\n"
" values the standard requires a special non-linear\n"
" transformation. When signed is false, no transformation\n"
" is employed, as we assume all values are 0 or positive.\n"
" When signed is true, the aforementioned transformation is\n"
" employed on negative values only.\n"
" -bit_depth a comma-separated list of bit depth values, one per \n"
" component; for example: 12,10,10.\n"
" Floating value numbers are treated as integers, and they\n"
" are shifted to the right, keeping only the specified\n"
" number of bits. Note that a bit depth of 28 upwards is not\n"
" supported.\n"

"\n";
return -1;
}
if (!get_arguments(argc, argv, input_filename, output_filename,
Expand All @@ -611,6 +632,7 @@ int main(int argc, char * argv[]) {
ojph::codestream codestream;

ojph::ppm_in ppm;
ojph::pfm_in pfm;
ojph::yuv_in yuv;
ojph::raw_in raw;
ojph::dpx_in dpx;
Expand Down Expand Up @@ -736,6 +758,113 @@ int main(int argc, char * argv[]) {

base = &ppm;
}
else if (is_matching(".pfm", v))
{
pfm.open(input_filename);
ojph::param_siz siz = codestream.access_siz();
siz.set_image_extent(ojph::point(image_offset.x + pfm.get_width(),
image_offset.y + pfm.get_height()));
ojph::ui32 num_comps = pfm.get_num_components();
assert(num_comps == 1 || num_comps == 3);
siz.set_num_components(num_comps);

if (bit_depth[0] == 0)
OJPH_ERROR(0x01000091,
"-bit_depth must be specified (this is temporary only).\n");

if (bit_depth[0] != 0) // one was set
if (num_bit_depths < num_comps) // but if not enough, repeat
for (ojph::ui32 c = num_bit_depths; c < num_comps; ++c)
bit_depth[c] = bit_depth[num_bit_depths - 1];
if (is_signed[0] != -1) // one was set
if (num_is_signed < num_comps) // but if not enough, repeat
for (ojph::ui32 c = num_is_signed; c < num_comps; ++c)
is_signed[c] = is_signed[num_is_signed - 1];

bool all_the_same = true;
if (num_comps == 3)
{
all_the_same = all_the_same
&& bit_depth[0] == bit_depth[1]
&& bit_depth[1] == bit_depth[2];
all_the_same = all_the_same
&& is_signed[0] == is_signed[1]
&& is_signed[1] == is_signed[2];
}

pfm.configure(bit_depth);
ojph::point ds(1, 1);
for (ojph::ui32 c = 0; c < num_comps; ++c) {
ojph::ui32 bd = 32;
if (bit_depth[c] != 0)
bd = bit_depth[c];
bool is = true;
if (is_signed[c] != -1)
is = is_signed[c] != 0;
siz.set_component(c, ds, bd, is);
}
siz.set_image_offset(image_offset);
siz.set_tile_size(tile_size);
siz.set_tile_offset(tile_offset);

ojph::param_cod cod = codestream.access_cod();
cod.set_num_decomposition(num_decompositions);
cod.set_block_dims(block_size.w, block_size.h);
if (num_precincts != -1)
cod.set_precinct_size(num_precincts, precinct_size);
cod.set_progression_order(prog_order);
if (num_comps == 1)
{
if (employ_color_transform != -1)
OJPH_WARN(0x01000092,
"-colour_trans option is not needed and was not used; "
"this is because the image has one component only\n");
}
else
{
if (employ_color_transform == -1)
cod.set_color_transform(true);
else
cod.set_color_transform(employ_color_transform == 1);
}
cod.set_reversible(reversible);
if (!reversible && quantization_step != -1.0f)
codestream.access_qcd().set_irrev_quant(quantization_step);

ojph::param_nlt nlt = codestream.access_nlt();
if (reversible) {
if (all_the_same)
nlt.set_type3_transformation(ojph::param_nlt::ALL_COMPS, true);
else
for (ojph::ui32 c = 0; c < num_comps; ++c)
nlt.set_type3_transformation(c, true);
}
else
OJPH_ERROR(0x01000093, "The support for pfm image is not "
"complete; I need to figure how to modify the interface "
"to better support the exchange of floating point data. "
"Feeding float point data is not supported yet, unless it "
"is for lossless compression.");

codestream.set_planar(false);
if (profile_string[0] != '\0')
codestream.set_profile(profile_string);
codestream.set_tilepart_divisions(tileparts_at_resolutions,
tileparts_at_components);
codestream.request_tlm_marker(tlm_marker);

if (dims.w != 0 || dims.h != 0)
OJPH_WARN(0x01000094,
"-dims option is not needed and was not used\n");
if (num_components != 0)
OJPH_WARN(0x01000095,
"-num_comps is not needed and was not used\n");
if (comp_downsampling[0].x != 0 || comp_downsampling[0].y != 0)
OJPH_WARN(0x01000096,
"-downsamp is not needed and was not used\n");

base = &pfm;
}
#ifdef OJPH_ENABLE_TIFF_SUPPORT
else if (is_matching(".tif", v) || is_matching(".tiff", v))
{
Expand Down
54 changes: 54 additions & 0 deletions src/apps/ojph_expand/ojph_expand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ int main(int argc, char *argv[]) {
ojph::codestream codestream;

ojph::ppm_out ppm;
ojph::pfm_out pfm;
#ifdef OJPH_ENABLE_TIFF_SUPPORT
ojph::tif_out tif;
#endif /* OJPH_ENABLE_TIFF_SUPPORT */
Expand Down Expand Up @@ -266,6 +267,59 @@ int main(int argc, char *argv[]) {
ppm.open(output_filename);
base = &ppm;
}
else if (is_matching(".pfm", v))
{
codestream.set_planar(false);
ojph::param_siz siz = codestream.access_siz();
ojph::param_cod cod = codestream.access_cod();
ojph::param_nlt nlt = codestream.access_nlt();

ojph::ui32 num_comps = siz.get_num_components();
if (num_comps != 3 && num_comps != 1)
OJPH_ERROR(0x0200000C,
"The file has %d color components; this cannot be saved to"
" a .pfm file\n", num_comps);
bool all_same = true;
ojph::point p = siz.get_downsampling(0);
for (ojph::ui32 i = 1; i < siz.get_num_components(); ++i)
{
ojph::point p1 = siz.get_downsampling(i);
all_same = all_same && (p1.x == p.x) && (p1.y == p.y);
}
if (!all_same)
OJPH_ERROR(0x0200000D,
"To save an image to ppm, all the components must have the "
"same downsampling ratio\n");
ojph::ui32 bit_depth[3];
for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c) {
ojph::ui8 bd = 0;
bool is = true;
bool result = nlt.get_type3_transformation(c, bd, is);
if (result == false)
OJPH_ERROR(0x0200000E,
"This codestream is not supported; it does not have an "
"NLT segment marker for this component (or no default NLT "
"settings) .\n");
if (bd != siz.get_bit_depth(c) || is != siz.is_signed(c))
OJPH_ERROR(0x0200000F,
"There is discrepancy in component %d configuration between "
"SIZ marker segment, which specifies bit_depth = %d and "
"signedness = %s, and NLT marker segment, which specifies "
"bit_depth = %d and signedness = %s.\n", c,
siz.get_bit_depth(c), is != siz.is_signed(c) ? "True" : "False",
bd, is ? "True" : "False");
bit_depth[c] = bd;
}
if (!cod.is_reversible())
OJPH_ERROR(0x02000010,
"This codestream is lossy (not reversible), and we currently "
"only support reversible codestreams for .pfm target files. "
"This is only temporary and will be changed at some point.\n");
pfm.configure(siz.get_recon_width(0), siz.get_recon_height(0),
siz.get_num_components(), -1.0f, bit_depth);
pfm.open(output_filename);
base = &pfm;
}
#ifdef OJPH_ENABLE_TIFF_SUPPORT
else if (is_matching(".tif", v) || is_matching(".tiff", v))
{
Expand Down
Loading

0 comments on commit f9fdf80

Please sign in to comment.