Mercurial > octave-libtiff
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 (); |