/usr/share/octave/packages/image-2.2.2/tiff_tag_read.m is in octave-image 2.2.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | ## Copyright (C) 2010-2012 Carnë Draug <carandraug+dev@gmail.com>
##
## This program is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free Software
## Foundation; either version 3 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, see <http://www.gnu.org/licenses/>.
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag})
## @deftypefnx {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag}, @var{ifd})
## @deftypefnx {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag}, "all")
## Read value of @var{tag}s from TIFF files.
##
## @var{file} must be a TIFF file and @var{tag} should be a tag ID. To check
## multiple tags, @var{tag} can be a vector. If @var{ifd} is supplied, only
## those IFDs (Image File Directory) will be read. As with @var{tag}, multiple
## IFDs can be checked by using a vector or with the string `all'. By default,
## only the first IFD is read.
##
## @var{value} and @var{offset} will be a matrix with a number of rows and
## columns equal to the number of @var{tag}s and @var{ifd}s requested. The index
## relate to the same order as the input. @var{offset} has the same structure as
## @var{value} and when equal to 1 its matching value on @var{value} will be an
## offset to a position in the file.
##
## @var{tag}s that can't be found will have a value of 0 and the corresponding
## @var{offset} will be 2.
##
## If an error occurs when reading @var{file} (such as lack of permissions of file
## is not a TIFF file), @var{offset} is set to -1 and @var{value} contains the
## error message.
##
## See the following examples:
## @example
## @group
## ## read value of tag 258 on IFD 1 (`off' will be 1 if `val' is an offset or 2 if not found)
## [val, off] = tiff_tag_read (filepath, 258);
## @end group
## @end example
##
## @example
## @group
## ## read value 258, 262, 254 o IFD 1 (`val' and `off' will be a 1x3 matrix)
## [val, off] = tiff_tag_read (filepath, [258 262 254]);
## if (off(1) == -1), error ("something happpened: %s", val); endif
## off(2,1) # will be 1 if val(2,1) is an offset to a file position or 2 if tag was not found
## val(2,1) # value of tag 262 on IFD 1
## @end group
## @end example
##
## @example
## @group
## ## read value 258, 262, 254 on the first 10 IFDs 1 (`val' and `off' will be a 1x10 matrix)
## [val, off] = tiff_tag_read (filepath, [258 262 254], 1:10);
## val(2,5) # value of tag 262 on IFD 5
## @end group
## @end example
##
## @example
## @group
## ## read value 258, 262, 254 o IFD 1 (`val' and `off' will be a 1x3 matrix)
## [val, off] = tiff_tag_read (filepath, [258 262 254], "all");
## val(2,end) # value of tag 262 on the last IFD
## @end group
## @end example
##
## @seealso{imread, imfinfo, readexif}
## @end deftypefn
## Based on the documentation at
## * http://en.wikipedia.org/wiki/Tagged_Image_File_Format
## * http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
## * http://ibb.gsf.de/homepage/karsten.rodenacker/IDL/Lsmfile.doc
## * http://www.awaresystems.be/imaging/tiff/faq.html
##
## and the function tiff_read by F. Nedelec, EMBL (www.cytosim.org)
## * http://www.cytosim.org/misc/index.html
##
## Explanation of the TIFF file structure:
##
## The idea of multi-page images meeds to be understood first. These allow one file
## to have multiple images. This may sound strange but consider situtations such as
## an MRI scan (one file can then contain one scan which is multiple images across
## one of the axis) or time-lapse experiment (where one file is not unlike a movie).
## TIFF files support this by being like a container of single images, called IFD
## (Image File Directory). For each page there will be a single IFD. One can see a
## TIFF as an archive file of multiple images files that many times have a single file.
##
## Each TIFF file starts with a small header that identifies the file as TIFF. The
## header ends with the position on the file for the first IFD. Each IFD has multiple
## entries that hold information about the image of that IFD including where on the
## file is the actual image. Each IFD entry is identified by a tag. Each tag has a
## unique meaning; for example, the IFD entry with tag 259 will say the compression
## type (if any), of the image in that IFD.
##
## A TIFF file will always have at least one IFD and each IFD will always have at
## least one IFD entry.
##
## * On the TIFF image file header:
## bytes 00-01 --> byte order used within the file: "II" for little endian
## and "MM" for big endian byte ordering.
## bytes 02-03 --> number 42 that identifies the file as TIFF
## bytes 04-07 --> file offset (in bytes) of the first IFD (Image File Directory)
##
## Note: offset is always from the start of the file ("bof" in fread) and first
## byte has an offset of zero.
##
## * On a TIFF's IFD structure:
## bytes 00-01 --> number of entries (or tags or fields or directories)
## bytes 02-13 --> the IFD entry #0
## bytes 14+=11 -> the IFD entry #N. Each will have exactly 12 bytes (the
## number of IFD entries was specified at the start of the IFD)
## bytes XX-XX --> file offset for next IFD (last 4 bytes of the IFD) or 0
## if it's the last IFD
##
## * On an IFD entry structure:
## bytes 00-01 --> tag that identifies the entry
## bytes 02-03 --> entry type
## 1 --> BYTE (uint8)
## 2 --> ASCII
## 3 --> SHORT (uint16)
## 4 --> LONG (uint32)
## 5 --> RATIONAL (two LONGS)
## 6 --> SBYTE (int8)
## 7 --> UNDEFINED (8 bit)
## 8 --> SSHORT (int16)
## 9 --> SLONG (int32)
## 10 --> FLOAT (single IEEE precision)
## 11 --> DOUBLE (double IEEE precision)
## bytes 04-07 --> number of values (count)
## bytes 08-11 --> file offset (from the beggining of file) or value (only if
## it fits in 4 bytes). It is possible that the offset is for
## a structure and not a value so we return the offset
##
## Note: file offset of the value may point anywhere in the file, even after the image.
##
## Tags numbered >= 32768 are private tags
## Tags numbered on the 65000--65535 range are reusable tags
function [val, off] = tiff_tag_read (file, tag, ifd = 1)
if (nargin < 2 || nargin > 3)
print_usage;
elseif (!isnumeric (tag) || !isvector (tag))
error ("`tag' must be either a numeric scalar or vector with tags -- identifying number of a field");
elseif (!(ischar (ifd) && strcmpi (ifd, "all")) && !(isnumeric (ifd) && isvector (ifd) && all (ifd == fix (ifd)) && all (ifd > 0)))
error ("`ifd' must be either the string `all' or numeric scalar or vector of positive integers with the IFD index");
endif
[FID, msg] = fopen (file, "r", "native");
if (FID == -1)
[val, off] = bad_exit (FID, sprintf ("Unable to fopen '%s': %s.", file, msg));
return
endif
## read byte order
byte_order = fread (FID, 2, "char=>char")'; # if we are retrieving a char, we need to transpose to get the string
if (strcmp (byte_order, "II"))
arch = "ieee-le"; # IEEE little endian format
elseif (strcmp (byte_order,"MM"))
arch = "ieee-be"; # IEEE big endian format
else
[val, off] = bad_exit (FID, sprintf ("First 2 bytes of '%s' returned '%s'. For TIFF should either be 'II' or 'MM'. Are you sure it's a TIFF.", file, byte_order));
return
endif
## read number 42
nTIFF = fread (FID, 1, "uint16", arch);
if (nTIFF != 42)
[val, off] = bad_exit (FID, sprintf ("'%s' is not a TIFF (missing value 42 on header at offset 2. Instead got '%g').", file, tiff_id));
return
endif
if (ischar (ifd) && strcmpi (ifd, "all"))
all_ifd = true;
else
all_ifd = false;
endif
## default values for val and off
def_val = 0;
def_off = 2;
## start output values with default values
if (ischar (ifd) && strcmpi (ifd, "all"))
val = def_val * ones (numel (tag), 1);
off = def_off * ones (numel (tag), 1);
else
val = def_val * ones (numel (tag), numel (ifd));
off = def_off * ones (numel (tag), numel (ifd));
endif
## read offset for the first IFD and move into it
offset_IFD = fread (FID, 1, "uint32", arch);
cIFD = 1; # current IFD
while (offset_IFD != 0 && (all_ifd || any (ifd >= cIFD)))
status = fseek (FID, offset_IFD, "bof");
if (status != 0)
[val, off] = bad_exit (FID, sprintf ("error on fseek when moving to IFD #%g", cIFD));
return
endif
## if checking on all IFD, add one column to the output
if (all_ifd)
val(:, end+1) = def_val;
off(:, end+1) = def_off;
endif
## read number of entries (nTag) and look for the desired tag ID
nTag = fread (FID, 1, "uint16", arch); # number of tags in the IFD
cTag = 1; # current tag
while (nTag >= cTag)
tagID = fread (FID, 1, "uint16", arch); # current tag ID
if (any(tagID == tag)) # found one
## column number of this IFD in the output matrix:
## we don't know at start the number of IFD so if all IFD have been requested
## we can't find them in `ifd', we need to set the index for output manually
if (all_ifd)
iCol = cIFD;
else
iCol = (ifd == cIFD);
endif
[val(tagID == tag, iCol), ...
off(tagID == tag, iCol) ] = read_value (FID, arch); # read tag value
elseif (all (tag < tagID))
## tags are in numeric order so if they wanted tags are all below current tag ID
## we can jump over to the next IFD
skip_bytes = 10 + (12 * (nTag - cTag));
status = fseek (FID, skip_bytes, "cof"); # Move to the next IFD
break
else
status = fseek (FID, 10, "cof"); # Move to the next tag
if (status != 0)
[val, off] = bad_exit (FID, sprintf ("error on fseek when moving out of tag #%g (tagID %g) on IFD %g.", cTag, tagID, cIFD));
return
endif
endif
cTag++;
endwhile
offset_IFD = fread (FID, 1, "uint32", arch);
cIFD++;
endwhile
fclose (FID);
endfunction
function [val, off] = read_value (FID, arch)
position = ftell (FID);
field_type = fread (FID, 1, "uint16", arch);
count = fread (FID, 1, "uint32", arch);
switch (field_type)
case 1, nBytes = 1; precision = "uint8"; # BYTE = 8-bit unsigned integer
case 2, nBytes = 1; precision = "uchar"; # ASCII = 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero)
case 3, nBytes = 2; precision = "uint16"; # SHORT = 16-bit (2-byte) unsigned integer
case 4, nBytes = 4; precision = "uint32"; # LONG = 32-bit (4-byte) unsigned integer
case 5, nBytes = 8; precision = "uint32"; # RATIONAL = Two LONGs: the first represents the numerator of a fraction; the second, the denominator
case 6, nBytes = 1; precision = "int8"; # SBYTE = An 8-bit signed (twos-complement) integer
case 7, nBytes = 1; precision = "uchar"; # UNDEFINED = An 8-bit byte that may contain anything, depending on the definition of the field
case 8, nBytes = 2; precision = "int16"; # SSHORT = A 16-bit (2-byte) signed (twos-complement) integer
case 9, nBytes = 4; precision = "int32"; # SLONG = A 32-bit (4-byte) signed (twos-complement) integer
case 10, nBytes = 8; precision = "int32"; # SRATIONAL = Two SLONG’s: the first represents the numerator of a fraction, the second the denominator
case 11, nBytes = 4; precision = "float32"; # FLOAT = Single precision (4-byte) IEEE format
case 12, nBytes = 8; precision = "float64"; # DOUBLE = Double precision (8-byte) IEEE format
otherwise
## From the TIFF file specification (page 16, section 2: TIFF structure):
## "Warning: It is possible that other TIFF field types will be added in the
## future. Readers should skip over fields containing an unexpected field type."
##
## However, we only get to this point of the code if we are in the tag requested
## by the use so it makes sense to error if we don't supported it yet.
error ("TIFF type %i not supported", field_type);
endswitch
if ((nBytes*count) > 4)
off = true;
val = fread (FID, 1, "uint32", arch);
if (rem (val, 2) != 0) # file offset must be an even number
warning ("Found an offset with an odd value %g (offsets should always be even numbers.", val);
endif
else
off = false;
switch precision
case {5, 10} val = fread (FID, 2*count, precision, arch); val = val(1)/val(2); # the first represents the numerator of a fraction; the second, the denominator
case {2} val = fread (FID, count, [precision "=>char"], arch)'; # if we are retrieving a char, we need to transpose to get the string
otherwise val = fread (FID, count, precision, arch);
endswitch
## adjust position to end of IFD entry (not all take up 4 Bytes)
fseek (FID, 4 - (nBytes*count), "cof");
endif
endfunction
function [val, off] = bad_exit (FID, msg)
off = -1;
val = sprintf (msg);
fclose (FID);
endfunction
|