Mercurial > octave-libtiff
changeset 31153:c66d6c7f025e
Tiff: implemented write method for stripped images
* __tiff__.cc(F_tiff_write__): implemented internal function for processing write
method arguments.
* __tiff__.cc(write_stripped_image): implemented support for writing to a stripped
image for all cases exept logical images.
* Tiff.m: added write method to the Tiff class.
author | magedrifaat <magedrifaat@gmail.com> |
---|---|
date | Wed, 03 Aug 2022 22:06:43 +0200 |
parents | 2244617f4da5 |
children | 828b7cc9aa36 |
files | libinterp/dldfcn/__tiff__.cc scripts/io/Tiff.m |
diffstat | 2 files changed, 230 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/dldfcn/__tiff__.cc Wed Aug 03 03:39:30 2022 +0200 +++ b/libinterp/dldfcn/__tiff__.cc Wed Aug 03 22:06:43 2022 +0200 @@ -159,7 +159,6 @@ uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); img_fvec[pixel] = (img_u8[pixel / 8] >> bit_number) & 0x01; } - break; } else if (image_data->bits_per_sample == 4) { @@ -182,7 +181,6 @@ uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); img_fvec[pixel] = (img_u8[pixel / 2] >> shift) & 0x0F; } - break; } else if (image_data->bits_per_sample != 8 && image_data->bits_per_sample != 16 && @@ -886,6 +884,39 @@ if (! TIFFSetField(tif, tag_id, tag_data)) error ("Failed to set tag value"); } + + uint32_t get_rows_in_strip (uint32_t strip_no, uint32_t strip_count, + uint32_t rows_per_strip, + tiff_image_data *image_data) + { + // Calculate the expected number of elements in the strip data array + // All strips have equal number of rows except strips at the bottom + // of the image can have less number of rows + uint32_t rows_in_strip = rows_per_strip; + if (image_data->planar_configuration == PLANARCONFIG_CONTIG) + { + // All strips have equal number of rows excpet strips at the bottom + // of the image can have less number of rows + if (strip_no == strip_count - 1) + rows_in_strip = image_data->height - rows_in_strip * strip_no; + } + else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE) + { + // For separate planes, we should check the last strip of each channel + uint32_t strips_per_channel + = strip_count / image_data->samples_per_pixel; + for (uint32_t boundary_strip = strips_per_channel - 1; + boundary_strip <= strip_count; + boundary_strip += strips_per_channel) + if (strip_no == boundary_strip) + rows_in_strip = image_data->height + - rows_in_strip * (strip_no % strips_per_channel); + } + else + error ("Planar Configuration not supported"); + + return rows_in_strip; + } template <typename T> void @@ -901,35 +932,19 @@ if (rows_in_strip > image_data->height) rows_in_strip = image_data->height; - uint32_t strip_count = TIFFNumberOfStrips (tif); - dim_vector strip_dimensions; + // LibTIFF uses zero-based indexing as opposed to Octave's 1-based + strip_no--; - // Calculate the expected number of elements in the strip data array - // All strips have equal number of rows except strips at the bottom - // of the image can have less number of rows + uint32_t strip_count = TIFFNumberOfStrips (tif); + rows_in_strip = get_rows_in_strip (strip_no, strip_count, + rows_in_strip, image_data); + + dim_vector strip_dimensions; if (image_data->planar_configuration == PLANARCONFIG_CONTIG) - { - // All strips have equal number of rows excpet strips at the bottom - // of the image can have less number of rows - if (strip_no == strip_count) - rows_in_strip = image_data->height - rows_in_strip * (strip_no - 1); - strip_dimensions = dim_vector (rows_in_strip, image_data->width, - image_data->samples_per_pixel); - } + strip_dimensions = dim_vector (rows_in_strip, image_data->width, + image_data->samples_per_pixel); else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE) - { - // For separate planes, we should check the last strip of each channel - uint32_t strips_per_channel - = strip_count / image_data->samples_per_pixel; - for (uint32_t boundary_strip = strips_per_channel; - boundary_strip <= strip_count; - boundary_strip += strips_per_channel) - if (strip_no == boundary_strip) - rows_in_strip = image_data->height - rows_in_strip - * ((strip_no - 1) - % (strips_per_channel)); - strip_dimensions = dim_vector (rows_in_strip, image_data->width, 1); - } + strip_dimensions = dim_vector (rows_in_strip, image_data->width, 1); else error ("Planar configuration not supported"); @@ -966,8 +981,6 @@ perm(2) = 0; strip_data = strip_data.permute (perm); - // LibTIFF uses zero-based indexing as opposed to Octave's 1-based - strip_no--; void *data_vec = strip_data.fortran_vec (); if (image_data->bits_per_sample == 8 || image_data->bits_per_sample == 16 @@ -1213,6 +1226,86 @@ } } + template <typename T> + void + write_stripped_image (TIFF *tif, T pixel_data, tiff_image_data *image_data) + { + // TODO(maged): remove this? ASSUMES pixel data dimensions are already validated + + typedef typename T::element_type P; + + // Permute pixel data to be aligned in memory to the way LibTIFF + // expects the data to be (i.e. channel x width x height for chunky + // and width x height x channel for separate planes) + Array<octave_idx_type> perm (dim_vector (3, 1)); + if (image_data->planar_configuration == PLANARCONFIG_SEPARATE) + { + perm(0) = 1; + perm(1) = 0; + perm(2) = 2; + } + else + { + perm(0) = 2; + perm(1) = 1; + perm(2) = 0; + } + pixel_data = pixel_data.permute (perm); + + uint32_t row_per_strip; + if (! TIFFGetFieldDefaulted (tif, TIFFTAG_ROWSPERSTRIP, &row_per_strip)) + error ("Failed to obtain the RowPerStrip tag"); + + // The default value is INT_MAX so we need to cap it to the image height + if (row_per_strip > image_data->height) + row_per_strip = image_data->height; + + uint8_t *pixel_fvec = reinterpret_cast<uint8_t *> (pixel_data.fortran_vec ()); + uint32_t strip_count = TIFFNumberOfStrips (tif); + tsize_t strip_size; + uint32_t rows_in_strip; + for (uint32_t strip = 0; strip < strip_count; strip++) + { + rows_in_strip = get_rows_in_strip (strip, strip_count, + row_per_strip, image_data); + strip_size = rows_in_strip * image_data->width * sizeof (P); + if (image_data->planar_configuration == PLANARCONFIG_CONTIG) + strip_size *= image_data->samples_per_pixel; + if (! TIFFWriteEncodedStrip (tif, strip, pixel_fvec, strip_size)) + error ("Failed to rite strip data"); + pixel_fvec += strip_size; + } + } + + template <typename T> + void + write_tiled_image (TIFF *tif, T pixel_data, tiff_image_data *image_data) + { + // TODO(maged): remove this? ASSUMES pixel data dimensions are already validated + + } + + template <typename T> + void + write_image (TIFF *tif, T pixel_data, tiff_image_data *image_data) + { + // TODO(maged): check behavior in matlab + if (image_data->height != pixel_data.dim1 () + || image_data->width != pixel_data.dim2 () + || pixel_data.ndims () > 3 + || (image_data->samples_per_pixel > 1 && pixel_data.ndims () < 3) + || (pixel_data.ndims () > 2 + && image_data->samples_per_pixel != pixel_data.dim3 ())) + error ("Dimensions of the input don't match image dimenions"); + + if (image_data->is_tiled) + write_tiled_image<T> (tif, pixel_data, image_data); + else + write_stripped_image<T> (tif, pixel_data, image_data); + + } + + #endif DEFUN_DLD (__open_tiff__, args, , @@ -1447,6 +1540,99 @@ #endif } + DEFUN_DLD (__tiff_write__, args, , + "Write the image data to the current IFD") + { +#if defined (HAVE_TIFF) + int nargin = args.length (); + + if (nargin < 2) + error ("Wrong number of arguments\n"); + + TIFF *tif = (TIFF *)(args(0).uint64_value ()); + + // TODO(maged): check on windows + if (TIFFGetMode (tif) == O_RDONLY) + error ("Can't write data to a file opened in read-only mode"); + + // Obtain all necessary tags + tiff_image_data image_data (tif); + + uint16_t sample_format; + if (! TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format)) + error ("Failed to obtain a value for sample format"); + + if (sample_format == 3) + { + if (image_data.bits_per_sample != 32 + && image_data.bits_per_sample != 64) + error ("Floating point images are only supported for bit depths of 32 and 64"); + } + else if (sample_format != 1 && sample_format != 4) + error ("Unsupported sample format"); + + switch (image_data.bits_per_sample) + { + case 1: + // We need to check for both scalar and matrix types to handle single + // pixel image + if (args (1).is_bool_scalar () || args (1).is_bool_matrix ()) + write_image<boolNDArray> (tif, args (1).bool_array_value (), + &image_data); + else + error ("Expected logical matrix for BiLevel image"); + break; + case 8: + if (args (1).is_uint8_type ()) + write_image<uint8NDArray> (tif, args (1).uint8_array_value (), + &image_data); + else + error ("Only uint8 data is allowed for uint images with bit depth of 8"); + break; + case 16: + if (args (1).is_uint16_type ()) + write_image<uint16NDArray> (tif, args (1).uint16_array_value (), + &image_data); + else + error ("Only uint16 data is allowed for uint images with bit depth of 16"); + break; + case 32: + if (sample_format == 3) + if (args (1).is_single_type () || args (1).is_double_type ()) + write_image<FloatNDArray> (tif, args (1).float_array_value (), + &image_data); + else + error ("Only single and double data are allowed for floating-point images"); + else + if (args (1).is_uint32_type ()) + write_image<uint32NDArray> (tif, args (1).uint32_array_value (), + &image_data); + else + error ("Only uint32 data is allowed for uint images with bit depth of 32"); + break; + case 64: + if (sample_format == 3) + if (args (1).is_single_type () || args (1).is_double_type ()) + write_image<NDArray> (tif, args (1).array_value (), &image_data); + else + error ("Only single and double data are allowed for floating-point images"); + else + if (args (1).is_uint64_type ()) + write_image<uint64NDArray> (tif, args (1).uint64_array_value (), + &image_data); + else + error ("Only uint64 data is allowed for uint images with bit depth of 64"); + break; + default: + error ("Unsupported bit depth"); + } + + return octave_value_list (); +#else + err_disabled_feature ("write", "Tiff"); +#endif + } + DEFUN_DLD (__tiff_write_encoded_strip__, args, , "Write an encoded strip to the image") {
--- a/scripts/io/Tiff.m Wed Aug 03 03:39:30 2022 +0200 +++ b/scripts/io/Tiff.m Wed Aug 03 22:06:43 2022 +0200 @@ -116,6 +116,13 @@ argout = __tiff_read__ (t.tiff_handle); endfunction + function write (t, imageData) + if (t.closed) + error ("Image file was closed"); + endif + __tiff_write__ (t.tiff_handle, imageData); + endfunction + function writeEncodedStrip (t, stripNumber, imageData) if (t.closed) error ("Image file was closed"); @@ -188,7 +195,7 @@ %! img = Tiff (filename, "r"); %! data2 = img.read (); %! assert (size (data2), ex_size); -%! assert (data2, resize (data, size (data2)), -1e5); +%! assert (data2, resize (data, size (data2))); %! img.close (); %!endfunction @@ -481,10 +488,10 @@ %! function test_fn (filename) %! img = Tiff (filename, "w"); %! setTag (img, struct ("ImageLength", 20, "ImageWidth", 20, -%! "BitsPerSample", 16, "RowsPerStrip", 2)); +%! "BitsPerSample", 16, "RowsPerStrip", 3)); %! data = uint16 (reshape (1:400, [20, 20])); -%! for strip = 1:10 -%! writeEncodedStrip (img, strip, data(strip * 2 - 1: strip * 2, :)); +%! for strip = 1:7 +%! writeEncodedStrip (img, strip, data(strip * 3 - 2: min(strip * 3, 20), :)); %! endfor %! img.close (); %! verify_data (filename, data, [20, 20]); @@ -514,13 +521,13 @@ %! img = Tiff (filename, "w"); %! setTag (img, struct ("ImageLength", 20, "ImageWidth", 20, %! "BitsPerSample", 16, "SamplesPerPixel", 3, -%! "RowsPerStrip", 2, "PlanarConfiguration", 2, +%! "RowsPerStrip", 3, "PlanarConfiguration", 2, %! "PhotometricInterpretation", 2)); %! data = uint16 (reshape (1:1200, [20, 20, 3])); %! strip = 1; %! for sample = 1:3 -%! for row = 1:2:20 -%! writeEncodedStrip (img, strip, data(row: row + 1, :, sample)); +%! for row = 1:3:20 +%! writeEncodedStrip (img, strip, data(row: min(row + 2, 20), :, sample)); %! strip = strip + 1; %! endfor %! endfor