# HG changeset patch # User magedrifaat # Date 1661533423 -7200 # Node ID a91f2f79e58ccc798c67460c566c052f42291a21 # Parent 6a9d985e7474853845a15aef50b68024d6a08d59 Tiff: added internal handler for imread using the Tiff interface * __tiff__.cc (F__tiff_imread__): implemented internal function to implement imread functionality using the Tiff interface, except for the PixelRegion option. diff -r 6a9d985e7474 -r a91f2f79e58c libinterp/corefcn/__tiff__.cc --- a/libinterp/corefcn/__tiff__.cc Thu Aug 25 20:17:59 2022 +0200 +++ b/libinterp/corefcn/__tiff__.cc Fri Aug 26 19:03:43 2022 +0200 @@ -120,6 +120,12 @@ } } + bool + is_numeric_scalar (octave_value ov) + { + return ov.isnumeric () && ov.isreal () && ov.is_scalar_type (); + } + // A map of tag names supported by matlab, there are some differences // than LibTIFF's names (e.g. Photometric vs PhotometricInerpretation) static const std::map tag_name_map = { @@ -1597,6 +1603,8 @@ error ("Failed to obtain the number of samples per pixel"); uint16NDArray data_array = tag_ov.uint16_array_value (); + // FIXME: this only works for RGB images. Need to handle grayscale, + // palette, cmyk and ycbcr if (data_array.numel () > samples_per_pixel - 3) error ("Failed to set field, too many values"); @@ -3459,7 +3467,7 @@ check_closed (tiff_handle); set_internal_handlers (); - + TIFF *tif = tiff_handle->get_file (); if (! args(1).is_double_type () && ! args(1).is_uint32_type () @@ -3536,4 +3544,175 @@ err_disabled_feature ("F__tiff_make_tagid__", "Tiff"); #endif } + + DEFUN (__tiff_imread__, args, nargout, + "Handler for imread that uses Tiff interface") + { +#if defined (HAVE_TIFF) + int nargin = args.length (); + + if (nargin == 0 || ! args(0).is_string ()) + error ("No filename provided\n"); + + uint16_t offset = 1; + + TIFF *tif = TIFFOpen (args(0).string_value ().c_str (), "r"); + uint16_t dir_count = TIFFNumberOfDirectories (tif); + uint16_t page = 1; + + // Handle unpaired index parameter + if (nargin > 1 && ! args(1).is_string ()) + { + if (is_numeric_scalar (args(1))) + page = args(1).uint16_scalar_value (); + else + error ("imread: index must be a numeric scalar"); + offset++; + } + + if ((nargin - offset) % 2 != 0) + error ("imread: PARAM/VALUE arguments must occur in pairs"); + + // Handle all index/frames params + bool found_index = false; + for (uint16_t arg_idx = offset; arg_idx < nargin; arg_idx+=2) + { + if (! args(arg_idx).is_string ()) + error ("imread: PARAM in PARAM/VALUE pair must be string"); + + const char *param_cstr = args(arg_idx).string_value ().c_str (); + if (strcasecmp (param_cstr, "index") == 0 + || strcasecmp (param_cstr, "frames") == 0) + { + if (found_index) + error ("imread: Index or Frames may only be specified once"); + + found_index = true; + octave_value val = args(arg_idx + 1); + if (is_numeric_scalar (val)) + page = val.uint16_scalar_value (); + else + error ("imread: %s must be a scalar", param_cstr); + } + } + + // validate frame numbers + if (page < 1 || page > dir_count) + error ("imread: index/frames specified are outside the number of images"); + + // Convert to zero-based indexing + page = page - 1; + + // Go to the first page + if (! TIFFSetDirectory (tif, page)) + error ("imread: failed to read page %d", page); + + // Obtain image info + tiff_image_data image_data (tif); + + // Set the default region + uint32NDArray row_region (dim_vector (1, 3)); + row_region(0) = 0; + row_region(1) = 1; + row_region(2) = image_data.height; + uint32NDArray col_region (dim_vector (1, 3)); + col_region(0) = 0; + col_region(1) = 1; + col_region(2) = image_data.width; + + // Obtain and validate other params (pixelregion, info) + for (uint16_t arg_idx = offset; arg_idx < nargin; arg_idx+=2) + { + if (! args(arg_idx).is_string ()) + error ("imread: PARAM in PARAM/VALUE pair must be string"); + + const char *param_cstr = args(arg_idx).string_value ().c_str (); + if (strcasecmp (param_cstr, "index") == 0 + || strcasecmp (param_cstr, "frames") == 0) + { + // Already handled + } + else if (strcasecmp (param_cstr, "pixelregion") == 0) + { + octave_value region_ov = args(arg_idx + 1); + + if (! region_ov.iscell () || region_ov.numel () != 2) + error ("imread: %s must be a 2-element cell array", param_cstr); + + Cell region_cell = region_ov.cell_value (); + row_region = region_cell(0).floor ().uint32_array_value (); + col_region = region_cell(1).floor ().uint32_array_value (); + + if (row_region.numel () < 2 || row_region.numel () > 3 + || col_region.numel () < 2 || col_region.numel () > 3) + error ("imread: range for %s must be a 2 or 3 element vector", + param_cstr); + + if (row_region.numel () == 2) + { + row_region(2) = row_region(1); + row_region(1) = 1; + } + if (col_region.numel () == 2) + { + col_region(2) = col_region(1); + col_region(1) = 1; + } + + if (static_cast (row_region(2)) > image_data.height) + error ("imread: end ROWS for PixelRegions option is larger than image height"); + if (static_cast (col_region(2)) > image_data.width) + error ("imread: end COLS for PixelRegions option is larger than image width"); + } + else if (strcasecmp (param_cstr, "info") == 0) + { + // FIXME: is this useful for our use case? + } + else + error ("imread: invalid PARAMETER '%s'", param_cstr); + } + + // Read image according to params + // FIXME: this should convert YCbCr images to RGB + uint16_t sample_format; + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format); + + octave_value_list retval (3, Matrix ()); + switch (sample_format) + { + case 1: + case 4: + retval (0) = read_unsigned_image (tif, &image_data); + break; + case 2: + retval (0) = read_signed_image (tif, &image_data); + break; + case 3: + retval (0) = read_float_image (tif, &image_data); + break; + default: + // FIXME: should this fallback to magick instead? + error ("Unsupported sample format"); + } + + if (nargout > 1) + { + // Also return the color map if available + uint16_t photometric; + if (TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photometric) + && photometric == PHOTOMETRIC_PALETTE) + { + const TIFFField *fip + = TIFFFieldWithTag (tif, TIFFTAG_COLORMAP); + if (fip) + retval(1) = get_field_data (tif, fip); + } + } + // FIXME: matlab returns all channels in the first argout + // and doesnt separate the alpha + return retval; +#else + err_disabled_feature ("imread", "Tiff"); +#endif + } }