comparison libinterp/dldfcn/__tiff__.cc @ 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
comparison
equal deleted inserted replaced
31152:2244617f4da5 31153:c66d6c7f025e
157 // TODO(maged): is it necessary to check FillOrder? 157 // TODO(maged): is it necessary to check FillOrder?
158 uint8_t bit_number = 7 - pixel % 8; 158 uint8_t bit_number = 7 - pixel % 8;
159 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); 159 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec);
160 img_fvec[pixel] = (img_u8[pixel / 8] >> bit_number) & 0x01; 160 img_fvec[pixel] = (img_u8[pixel / 8] >> bit_number) & 0x01;
161 } 161 }
162 break;
163 } 162 }
164 else if (image_data->bits_per_sample == 4) 163 else if (image_data->bits_per_sample == 4)
165 { 164 {
166 if (image_data->samples_per_pixel != 1) 165 if (image_data->samples_per_pixel != 1)
167 error ("4-bit images are only supported for grayscale"); 166 error ("4-bit images are only supported for grayscale");
180 { 179 {
181 uint8_t shift = pixel % 2 == 0? 4: 0; 180 uint8_t shift = pixel % 2 == 0? 4: 0;
182 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); 181 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec);
183 img_fvec[pixel] = (img_u8[pixel / 2] >> shift) & 0x0F; 182 img_fvec[pixel] = (img_u8[pixel / 2] >> shift) & 0x0F;
184 } 183 }
185 break;
186 } 184 }
187 else if (image_data->bits_per_sample != 8 && 185 else if (image_data->bits_per_sample != 8 &&
188 image_data->bits_per_sample != 16 && 186 image_data->bits_per_sample != 16 &&
189 image_data->bits_per_sample != 32 && 187 image_data->bits_per_sample != 32 &&
190 image_data->bits_per_sample != 64) 188 image_data->bits_per_sample != 64)
884 uint32_t tag_data = tag_ov.double_value (); 882 uint32_t tag_data = tag_ov.double_value ();
885 883
886 if (! TIFFSetField(tif, tag_id, tag_data)) 884 if (! TIFFSetField(tif, tag_id, tag_data))
887 error ("Failed to set tag value"); 885 error ("Failed to set tag value");
888 } 886 }
887
888 uint32_t get_rows_in_strip (uint32_t strip_no, uint32_t strip_count,
889 uint32_t rows_per_strip,
890 tiff_image_data *image_data)
891 {
892 // Calculate the expected number of elements in the strip data array
893 // All strips have equal number of rows except strips at the bottom
894 // of the image can have less number of rows
895 uint32_t rows_in_strip = rows_per_strip;
896 if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
897 {
898 // All strips have equal number of rows excpet strips at the bottom
899 // of the image can have less number of rows
900 if (strip_no == strip_count - 1)
901 rows_in_strip = image_data->height - rows_in_strip * strip_no;
902 }
903 else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE)
904 {
905 // For separate planes, we should check the last strip of each channel
906 uint32_t strips_per_channel
907 = strip_count / image_data->samples_per_pixel;
908 for (uint32_t boundary_strip = strips_per_channel - 1;
909 boundary_strip <= strip_count;
910 boundary_strip += strips_per_channel)
911 if (strip_no == boundary_strip)
912 rows_in_strip = image_data->height
913 - rows_in_strip * (strip_no % strips_per_channel);
914 }
915 else
916 error ("Planar Configuration not supported");
917
918 return rows_in_strip;
919 }
889 920
890 template <typename T> 921 template <typename T>
891 void 922 void
892 write_strip (TIFF *tif, uint32_t strip_no, T strip_data, 923 write_strip (TIFF *tif, uint32_t strip_no, T strip_data,
893 tiff_image_data *image_data) 924 tiff_image_data *image_data)
899 // The tag has a default value of UINT32_MAX which means the entire 930 // The tag has a default value of UINT32_MAX which means the entire
900 // image, but we need to cap it to the height for later calculations 931 // image, but we need to cap it to the height for later calculations
901 if (rows_in_strip > image_data->height) 932 if (rows_in_strip > image_data->height)
902 rows_in_strip = image_data->height; 933 rows_in_strip = image_data->height;
903 934
935 // LibTIFF uses zero-based indexing as opposed to Octave's 1-based
936 strip_no--;
937
904 uint32_t strip_count = TIFFNumberOfStrips (tif); 938 uint32_t strip_count = TIFFNumberOfStrips (tif);
939 rows_in_strip = get_rows_in_strip (strip_no, strip_count,
940 rows_in_strip, image_data);
941
905 dim_vector strip_dimensions; 942 dim_vector strip_dimensions;
906
907 // Calculate the expected number of elements in the strip data array
908 // All strips have equal number of rows except strips at the bottom
909 // of the image can have less number of rows
910 if (image_data->planar_configuration == PLANARCONFIG_CONTIG) 943 if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
911 { 944 strip_dimensions = dim_vector (rows_in_strip, image_data->width,
912 // All strips have equal number of rows excpet strips at the bottom 945 image_data->samples_per_pixel);
913 // of the image can have less number of rows
914 if (strip_no == strip_count)
915 rows_in_strip = image_data->height - rows_in_strip * (strip_no - 1);
916 strip_dimensions = dim_vector (rows_in_strip, image_data->width,
917 image_data->samples_per_pixel);
918 }
919 else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE) 946 else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE)
920 { 947 strip_dimensions = dim_vector (rows_in_strip, image_data->width, 1);
921 // For separate planes, we should check the last strip of each channel
922 uint32_t strips_per_channel
923 = strip_count / image_data->samples_per_pixel;
924 for (uint32_t boundary_strip = strips_per_channel;
925 boundary_strip <= strip_count;
926 boundary_strip += strips_per_channel)
927 if (strip_no == boundary_strip)
928 rows_in_strip = image_data->height - rows_in_strip
929 * ((strip_no - 1)
930 % (strips_per_channel));
931 strip_dimensions = dim_vector (rows_in_strip, image_data->width, 1);
932 }
933 else 948 else
934 error ("Planar configuration not supported"); 949 error ("Planar configuration not supported");
935 950
936 if (strip_data.dim1 () > rows_in_strip) 951 if (strip_data.dim1 () > rows_in_strip)
937 warning ("The strip is composed of %u rows but the input has %ld rows.", 952 warning ("The strip is composed of %u rows but the input has %ld rows.",
964 perm(0) = 2; 979 perm(0) = 2;
965 perm(1) = 1; 980 perm(1) = 1;
966 perm(2) = 0; 981 perm(2) = 0;
967 strip_data = strip_data.permute (perm); 982 strip_data = strip_data.permute (perm);
968 983
969 // LibTIFF uses zero-based indexing as opposed to Octave's 1-based
970 strip_no--;
971 void *data_vec = strip_data.fortran_vec (); 984 void *data_vec = strip_data.fortran_vec ();
972 if (image_data->bits_per_sample == 8 985 if (image_data->bits_per_sample == 8
973 || image_data->bits_per_sample == 16 986 || image_data->bits_per_sample == 16
974 || image_data->bits_per_sample == 32 987 || image_data->bits_per_sample == 32
975 || image_data->bits_per_sample == 64) 988 || image_data->bits_per_sample == 64)
1211 default: 1224 default:
1212 error ("Unsupported bit depth"); 1225 error ("Unsupported bit depth");
1213 } 1226 }
1214 } 1227 }
1215 1228
1229 template <typename T>
1230 void
1231 write_stripped_image (TIFF *tif, T pixel_data, tiff_image_data *image_data)
1232 {
1233 // TODO(maged): remove this? ASSUMES pixel data dimensions are already validated
1234
1235 typedef typename T::element_type P;
1236
1237 // Permute pixel data to be aligned in memory to the way LibTIFF
1238 // expects the data to be (i.e. channel x width x height for chunky
1239 // and width x height x channel for separate planes)
1240 Array<octave_idx_type> perm (dim_vector (3, 1));
1241 if (image_data->planar_configuration == PLANARCONFIG_SEPARATE)
1242 {
1243 perm(0) = 1;
1244 perm(1) = 0;
1245 perm(2) = 2;
1246 }
1247 else
1248 {
1249 perm(0) = 2;
1250 perm(1) = 1;
1251 perm(2) = 0;
1252 }
1253 pixel_data = pixel_data.permute (perm);
1254
1255 uint32_t row_per_strip;
1256 if (! TIFFGetFieldDefaulted (tif, TIFFTAG_ROWSPERSTRIP, &row_per_strip))
1257 error ("Failed to obtain the RowPerStrip tag");
1258
1259 // The default value is INT_MAX so we need to cap it to the image height
1260 if (row_per_strip > image_data->height)
1261 row_per_strip = image_data->height;
1262
1263 uint8_t *pixel_fvec = reinterpret_cast<uint8_t *> (pixel_data.fortran_vec ());
1264 uint32_t strip_count = TIFFNumberOfStrips (tif);
1265 tsize_t strip_size;
1266 uint32_t rows_in_strip;
1267 for (uint32_t strip = 0; strip < strip_count; strip++)
1268 {
1269 rows_in_strip = get_rows_in_strip (strip, strip_count,
1270 row_per_strip, image_data);
1271 strip_size = rows_in_strip * image_data->width * sizeof (P);
1272 if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
1273 strip_size *= image_data->samples_per_pixel;
1274 if (! TIFFWriteEncodedStrip (tif, strip, pixel_fvec, strip_size))
1275 error ("Failed to rite strip data");
1276 pixel_fvec += strip_size;
1277 }
1278 }
1279
1280 template <typename T>
1281 void
1282 write_tiled_image (TIFF *tif, T pixel_data, tiff_image_data *image_data)
1283 {
1284 // TODO(maged): remove this? ASSUMES pixel data dimensions are already validated
1285
1286 }
1287
1288 template <typename T>
1289 void
1290 write_image (TIFF *tif, T pixel_data, tiff_image_data *image_data)
1291 {
1292 // TODO(maged): check behavior in matlab
1293 if (image_data->height != pixel_data.dim1 ()
1294 || image_data->width != pixel_data.dim2 ()
1295 || pixel_data.ndims () > 3
1296 || (image_data->samples_per_pixel > 1 && pixel_data.ndims () < 3)
1297 || (pixel_data.ndims () > 2
1298 && image_data->samples_per_pixel != pixel_data.dim3 ()))
1299 error ("Dimensions of the input don't match image dimenions");
1300
1301 if (image_data->is_tiled)
1302 write_tiled_image<T> (tif, pixel_data, image_data);
1303 else
1304 write_stripped_image<T> (tif, pixel_data, image_data);
1305
1306 }
1307
1308
1216 #endif 1309 #endif
1217 1310
1218 DEFUN_DLD (__open_tiff__, args, , 1311 DEFUN_DLD (__open_tiff__, args, ,
1219 "Open a Tiff file and return its handle") 1312 "Open a Tiff file and return its handle")
1220 { 1313 {
1445 #else 1538 #else
1446 err_disabled_feature ("read", "Tiff"); 1539 err_disabled_feature ("read", "Tiff");
1447 #endif 1540 #endif
1448 } 1541 }
1449 1542
1543 DEFUN_DLD (__tiff_write__, args, ,
1544 "Write the image data to the current IFD")
1545 {
1546 #if defined (HAVE_TIFF)
1547 int nargin = args.length ();
1548
1549 if (nargin < 2)
1550 error ("Wrong number of arguments\n");
1551
1552 TIFF *tif = (TIFF *)(args(0).uint64_value ());
1553
1554 // TODO(maged): check on windows
1555 if (TIFFGetMode (tif) == O_RDONLY)
1556 error ("Can't write data to a file opened in read-only mode");
1557
1558 // Obtain all necessary tags
1559 tiff_image_data image_data (tif);
1560
1561 uint16_t sample_format;
1562 if (! TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format))
1563 error ("Failed to obtain a value for sample format");
1564
1565 if (sample_format == 3)
1566 {
1567 if (image_data.bits_per_sample != 32
1568 && image_data.bits_per_sample != 64)
1569 error ("Floating point images are only supported for bit depths of 32 and 64");
1570 }
1571 else if (sample_format != 1 && sample_format != 4)
1572 error ("Unsupported sample format");
1573
1574 switch (image_data.bits_per_sample)
1575 {
1576 case 1:
1577 // We need to check for both scalar and matrix types to handle single
1578 // pixel image
1579 if (args (1).is_bool_scalar () || args (1).is_bool_matrix ())
1580 write_image<boolNDArray> (tif, args (1).bool_array_value (),
1581 &image_data);
1582 else
1583 error ("Expected logical matrix for BiLevel image");
1584 break;
1585 case 8:
1586 if (args (1).is_uint8_type ())
1587 write_image<uint8NDArray> (tif, args (1).uint8_array_value (),
1588 &image_data);
1589 else
1590 error ("Only uint8 data is allowed for uint images with bit depth of 8");
1591 break;
1592 case 16:
1593 if (args (1).is_uint16_type ())
1594 write_image<uint16NDArray> (tif, args (1).uint16_array_value (),
1595 &image_data);
1596 else
1597 error ("Only uint16 data is allowed for uint images with bit depth of 16");
1598 break;
1599 case 32:
1600 if (sample_format == 3)
1601 if (args (1).is_single_type () || args (1).is_double_type ())
1602 write_image<FloatNDArray> (tif, args (1).float_array_value (),
1603 &image_data);
1604 else
1605 error ("Only single and double data are allowed for floating-point images");
1606 else
1607 if (args (1).is_uint32_type ())
1608 write_image<uint32NDArray> (tif, args (1).uint32_array_value (),
1609 &image_data);
1610 else
1611 error ("Only uint32 data is allowed for uint images with bit depth of 32");
1612 break;
1613 case 64:
1614 if (sample_format == 3)
1615 if (args (1).is_single_type () || args (1).is_double_type ())
1616 write_image<NDArray> (tif, args (1).array_value (), &image_data);
1617 else
1618 error ("Only single and double data are allowed for floating-point images");
1619 else
1620 if (args (1).is_uint64_type ())
1621 write_image<uint64NDArray> (tif, args (1).uint64_array_value (),
1622 &image_data);
1623 else
1624 error ("Only uint64 data is allowed for uint images with bit depth of 64");
1625 break;
1626 default:
1627 error ("Unsupported bit depth");
1628 }
1629
1630 return octave_value_list ();
1631 #else
1632 err_disabled_feature ("write", "Tiff");
1633 #endif
1634 }
1635
1450 DEFUN_DLD (__tiff_write_encoded_strip__, args, , 1636 DEFUN_DLD (__tiff_write_encoded_strip__, args, ,
1451 "Write an encoded strip to the image") 1637 "Write an encoded strip to the image")
1452 { 1638 {
1453 #if defined (HAVE_TIFF) 1639 #if defined (HAVE_TIFF)
1454 int nargin = args.length (); 1640 int nargin = args.length ();