]> Creatis software - CreaPhase.git/blobdiff - octave_packages/image-1.0.15/readexif.m
Add a useful package (from Source forge) for octave
[CreaPhase.git] / octave_packages / image-1.0.15 / readexif.m
diff --git a/octave_packages/image-1.0.15/readexif.m b/octave_packages/image-1.0.15/readexif.m
new file mode 100644 (file)
index 0000000..c6eeeec
--- /dev/null
@@ -0,0 +1,529 @@
+## Copyright (C) 2009 Roderick Koehle    koehle(at)users.sourceforge.net
+##
+## 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 2
+## of the License, or (at your option) any later version.
+##
+## Octave 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 Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{exif} =} readexif(@var{filename}, @var{thumbnail})
+## Read EXIF information from JPEG image data.
+##
+## The exif tag information are returned in the @var{exif} data structure.
+## Integer ratios are expressed as column vector.
+## For example, a focal number of 2.8 is expressed
+## as FNumber=[28; 10]. Otherwise all data are returned by the type
+## as specified in the IFD structures.
+##
+## The filename for the thumbnail image is optional.
+## If given, the thumbnail jpeg image will be stored to
+## file @var{thumbnail}. 
+##
+## Reference:
+## JEITA CP-3451, Exchangeable image file format for digital still cameras:
+## Exif Version 2.2
+##
+## @seealso{imwrite, imfinfo}
+## @end deftypefn
+
+function exif = readexif(file, thumbnail)
+
+  % Enable the debug flag to see more of the JPG sections.
+
+  debug = false;
+
+  in = fopen(file);
+  if (in<0)
+    error('File "%s" not found !', file);
+  end
+
+  s = fread(in, 1, 'uint16', 'ieee-be');
+
+  JPEG.SOI = 0xffd8;
+  JPEG.APP0 = 0xffe0;
+  JPEG.APP1 = 0xffe1;
+  JPEG.APP2 = 0xffe2;
+  JPEG.DQT = 0xffdb;
+  JPEG.DHT = 0xffc4;
+  JPEG.DRI = 0xffdd;
+  JPEG.SOF = 0xffc0;
+  JPEG.SOS = 0xffda;
+  JPEG.EOI = 0xffd9;
+
+  % Stop if no Start of Image found
+
+  if s~=JPEG.SOI
+    error('JPEG Format error - missing start of image tag.');
+  end
+
+  exif = [];
+
+  while ~feof(in)
+    s = fread(in, 1, 'uint16', 'ieee-be');
+
+    switch s
+      case JPEG.SOI
+      case JPEG.APP0
+       l = fread(in, 1, 'uint16', 'ieee-be');
+        if debug, printf('APP0: %i\n', l); end
+        fseek(in, l-2, 'cof');
+      case JPEG.APP1
+       l = fread(in, 1, 'uint16', 'ieee-be');
+       if debug, printf('APP1: %i\n', l); end
+       app1 = fread(in, l-2, 'uchar');
+        if nargin==1
+          exif = parseexif(app1);
+        else
+          exif = parseexif(app1, thumbnail);
+        end
+        % stop reading further, remove following break
+        % if you want to extend this parser.
+       break; 
+      case JPEG.APP2
+        l = fread(in, 1, 'uint16', 'ieee-be');
+        if debug, printf('APP2: %i\n', l); end
+        fseek(in, l-2, 'cof');
+      case JPEG.DQT % define quantization table
+        l = fread(in, 1, 'uint16', 'ieee-be');
+        if debug, printf('DQT: %i\n', l); end;
+        fseek(in, l-2, 'cof');
+      case JPEG.DHT % define huffmann table
+        l = fread(in, 1, 'uint16', 'ieee-be');
+        fseek(in, l-2, 'cof');
+        if debug, printf('DHT: %i\n', l); end
+      case JPEG.DRI % define restart interoperability
+        l = fread(in, 1, 'uint16', 'ieee-be');
+        fseek(in, l-2, 'cof');
+        if debug, printf('DRI: %i\n', l); end
+      case JPEG.SOF % start of frame
+        l = fread(in, 1, 'uint16', 'ieee-be');
+        fseek(in, l-2, 'cof');
+        if debug, printf('SOF: %i\n', l); end
+      case JPEG.SOS % start of scan
+        l = fread(in, 1, 'uint16', 'ieee-be');
+        fseek(in, l-2, 'cof');
+        if debug, printf('SOS: %i\n', l); end
+
+        % JPEG compressed data comes here ...
+        break;
+
+      case JPEG.EOI % end of image
+        printf('EOI');
+      otherwise % Skip unknown tags
+       l = fread(in, 1, 'uint16', 'ieee-be');
+        if debug, printf('TAG %04X: %i\n', s, l); end
+        fseek(in, l-2, 'cof');
+    end
+  end
+
+  fclose(in);
+end
+
+%
+% Parse EXIF APP1 section
+%
+% This routine will parse the APP1 section of an jpeg image.
+% If a filename "thumb" is given, the tumbnail image data
+% will be exported to given file.
+%
+%   exif = parseexif(data, thumb)
+%
+function exif = parseexif(data, thumb)
+
+  id = char(data(1:6).');
+  if strncmp(id, ['Exif' 0 0], 6)
+
+    % TIFF header
+
+    byteorder = char(data(7:8).');
+    littleendian = strncmp(byteorder, 'II', 2);
+    bigendian    = strncmp(byteorder, 'MM', 2);
+
+    tag42     = intn(data(9:10), bigendian);
+    offset    = intn(data(11:14), bigendian);
+
+    if (~littleendian && ~bigendian) || tag42~=42
+      error('invalid TIFF header');
+    end
+
+    % IFD fields
+
+    exif = ifdparse(tifftags(), data, offset, bigendian);
+  else
+    exif = [];
+  end
+
+  % export thumbnail image
+
+  if nargin==2 && isfield(exif, 'JPEGInterchangeFormat')
+    i = exif.JPEGInterchangeFormat;
+    n = exif.JPEGInterchangeFormatLength;
+
+    jpg = data(7+i:7+i+n-1);
+    out = fopen(thumb, 'w');
+    if (out<0), 
+      error('Cannot open file "%s" for writing thumbnail image.', thumb);
+    end
+    fwrite(out, jpg, 'uint8');
+    fclose(out);
+  end
+end
+
+function ifd = ifdparse(dict, data, offset, endian)
+
+  debug = false;
+
+  ifd = [];
+
+  while offset
+    ifd_fields = intn(data(7+offset+(0:1)), endian);
+
+    if debug, printf('Tag  Type Count    Offset\n'); end
+    for i=1:ifd_fields
+      j = 9+offset+(i-1)*12;
+      ifd_tag    = intn(data(  j:j+1), endian);
+      ifd_type   = intn(data(j+2:j+3), endian);
+      ifd_count  = intn(data(j+4:j+7), endian);
+      ifd_offset = intn(data(j+8:j+11), endian);
+
+      name = ifdtagname(dict, ifd_tag);
+
+      if debug,
+        printf('%04x %04x %08x %08x %s : ', ifd_tag, ifd_type, ...
+               ifd_count, ifd_offset, name);
+      end
+      if ifd_type>0
+       n = ifdsize(ifd_type);
+
+       if n*ifd_count<=4
+         value = data(j+8:j+8+n*ifd_count-1);
+         value = reshape(value, n, ifd_count);
+       else
+         a = 7+ifd_offset;
+         b = 7+ifd_offset+n*ifd_count-1;
+          if (a>0 && b>0 && a<=length(data) && b<=length(data))
+           value = data(7+ifd_offset:7+ifd_offset+n*ifd_count-1);
+           value = reshape(value, n, ifd_count);
+         else
+           value = [];
+         end
+       end
+      end
+   
+      switch ifd_type
+       case 01 % unsigned char
+          ifd.(name) = uint8(value);
+         if debug,
+           printf('%02x ', uint8(value));
+           printf('\n');
+          end
+       case 02 % Ascii
+          ifd.(name) = char(value);
+          if debug, printf('%s\n', char(value)); end
+        case 03 % 16 bit unsigned int
+          ifd.(name) = uintn(value, endian);
+          if debug
+            printf('%i ', intn(value), endian);
+            printf('\n');
+          end
+        case 04 % 32 bit unsigned int
+          ifd.(name) = uintn(value, endian);
+          if debug, printf('%i\n', uintn(value, endian)); end
+        case 05 % 32 bit unsigned rational
+          ifd.(name) = [uintn(value(1:4,:), endian); uintn(value(5:8,:), endian)];
+         if debug, printf('%i/%i\n',uintn(value(1:4), endian),uintn(value(5:8)), endian); end
+        case 07 % unknown
+          ifd.(name) = uint8(value);
+          if debug
+           printf('%02x ', value);
+           printf('\n');
+          end
+        case 09 % 32 bit signed int
+          ifd.(name) = intn(value, endian);
+          if debug, printf('%i\n', intn(value, endian)); end
+        case 10 % 32 bit signed rational
+          ifd.(name) = [intn(value(1:4,:), endian); intn(value(5:8,:), endian)];
+         if debug, printf('%i/%i\n',intn(value(1:4), endian),intn(value(5:8)), endian); end
+        otherwise
+         printf('%02x ', value);
+         printf('\n');
+      end
+
+      switch ifd_tag
+       case 0x8769, % Exif Pointer
+         ifd.(name) = ifdparse(exiftags(), data, ifd_offset, endian);
+       case 0x8825, % GPS Pointer
+         ifd.(name) = ifdparse(gpstags(), data, ifd_offset, endian);
+        case 0xa005 % Interoperatibility Pointer
+         ifd.(name) = ifdparse(dict, data, ifd_offset, endian);
+%        case 0x927c % Makernotes
+%         ifd.(name) = ifdparse([], data, ifd_offset, endian);
+       otherwise
+      end
+    end
+    j = 9+offset+ifd_fields*12;
+    ifd_next = intn(data(j:j+3), endian);
+
+    offset = ifd_next;
+  end
+end
+
+%
+% Return bytelength for respective IFD type
+%
+function n = ifdsize(ifd_type)
+  switch ifd_type
+    case {1, 2, 7}, n = 1;
+    case 03       , n = 2;
+    case {4, 9}   , n = 4;
+    case {5, 10}  , n = 8;
+    otherwise     , n = 1;
+  end
+end
+
+
+%
+% Convert little endian character vector to integer
+%
+function y = intn(x, bigendian)
+  if bigendian
+    y = polycol(x, int32(256));
+  else
+    y = polycol(flipud(x), int32(256));
+  end
+end
+
+function y = uintn(x, bigendian)
+  if bigendian
+     y = polycol(x, uint32(256));
+  else
+     y = polycol(flipud(x), uint32(256));
+  end
+end
+
+%
+% Use own polyval that works with integers,
+% it evaluates the number polygon columnwise.
+%
+%   number = polycol(digits, base)
+%
+function y = polycol(c, x)
+  y = c(1,:);
+  for i=2:size(c, 1)
+    y = y.*x+c(i,:);
+  end
+end
+
+%
+% Query EXIF IFD tagname
+%
+% Unfortunately, neither MATLAB nor Octave provide a hash functionality,
+% so use structures as hash.
+%
+function name = ifdtagname(dict, key)
+  k = sprintf('K%04X', key);
+  if isfield(dict, k)
+    name = dict.(k);
+  else
+    name = sprintf('tag%04X', key);
+  end
+end
+
+%
+% Primary image IFD tags according to Exif 2.2
+%
+function dict = tifftags()
+  t = {
+
+    % TIFF Tags according to EXIF2.2, additional baseline TIFF tags are marked by a '%'
+
+    '0FE' 'NewSubfileType' %
+    '0FF' 'SubfileType' %
+    '100' 'ImageWidth'
+    '101' 'ImageLength'
+    '102' 'BitsPerSample'
+    '103' 'Compression'
+    '106' 'PhotometricInterpretation'
+    '108' 'CellWidth' %
+    '109' 'CellLength' %
+    '10A' 'FillOrder' %
+    '10E' 'ImageDescription'
+    '10F' 'Make'
+    '110' 'Model'
+    '111' 'StripOffsets'
+    '112' 'Orientation'
+    '115' 'SamplesPerPixel'
+    '116' 'RowsPerStrip'
+    '117' 'StripByteCounts'
+    '118' 'MinSampleValue' %
+    '119' 'MaxSampleValue' %
+    '11A' 'XResolution'
+    '11B' 'YResolution'
+    '11C' 'PlanarConfiguration'
+    '120' 'FreeOffsets' %
+    '121' 'FreeByteCounts' %
+    '122' 'GrayResponseUnit' %
+    '123' 'GrayResponseCurve' %
+    '128' 'ResolutionUnit'
+    '12D' 'TransferFunction'
+    '131' 'Software'
+    '132' 'DateTime'
+    '13B' 'Artist'
+    '13C' 'HostComputer' %
+    '13E' 'WhitePoint'
+    '13F' 'PrimaryChromaticities'
+    '140' 'ColorMap' %
+    '152' 'ExtraSamples' %
+    '201' 'JPEGInterchangeFormat'
+    '202' 'JPEGInterchangeFormatLength'
+    '211' 'YCbCrCoefficients'
+    '212' 'YCbCrSubSampling'
+    '213' 'YCbCrPositioning'
+    '214' 'ReferenceBlackWhite'
+    '8298' 'Copyright'
+    '8769' 'Exif IFD Pointer'
+    '8825' 'GPS Info IFD Pointer'
+  };
+
+  dict = [];
+  for i=1:size(t,1)
+    key   = sprintf('K%04X', hex2dec(t{i,1}));
+    value = t{i,2};
+    dict.(key) = strrep(value, ' ', '_');
+  end
+end
+
+%
+% EXIF private tags
+%
+function dict = exiftags()
+  t = {
+
+    % EXIF Tags
+
+    '829A' 'ExposureTime'
+    '829D' 'FNumber'
+    '8822' 'ExposureProgram'
+    '8824' 'SpectralSensitivity'
+    '8827' 'ISOSpeedRatings'
+    '8828' 'OECF'
+    '9000' 'ExifVersion'
+    '9003' 'DateTimeOriginal'
+    '9004' 'DateTimeDigitized'
+    '9101' 'ComponentsConfiguration'
+    '9102' 'CompressedBitsPerPixel'
+    '9201' 'ShutterSpeedValue'
+    '9202' 'ApertureValue'
+    '9203' 'BrightnessValue'
+    '9204' 'ExposureBiasValue'
+    '9205' 'MaxApertureValue'
+    '9206' 'SubjectDistance'
+    '9207' 'MeteringMode'
+    '9208' 'LightSource'
+    '9209' 'Flash'
+    '920A' 'FocalLength'
+    '9214' 'SubjectArea'
+    '927C' 'MakerNote'
+    '9286' 'UserComment'
+    '9290' 'SubsecTime'
+    '9291' 'SubsecTimeOriginal'
+    '9292' 'SubsecTimeDigitized'
+    'A000' 'FlashpixVersion'
+    'A001' 'ColorSpace'
+    'A002' 'PixelXDimension'
+    'A003' 'PixelYDimension'
+    'A004' 'RelatedSoundFile'
+    'A005' 'Interoperatibility IFD Pointer'
+    'A20B' 'FlashEnergy'
+    'A20C' 'SpatialFrequencyResponse'
+    'A20E' 'FocalPlaneXResolution'
+    'A20F' 'FocalPlaneYResolution'
+    'A210' 'FocalPlaneResolutionUnit'
+    'A214' 'SubjectLocation'
+    'A215' 'ExposureIndex'
+    'A217' 'SensingMethod'
+    'A300' 'FileSource'
+    'A301' 'SceneType'
+    'A302' 'CFAPattern'
+    'A401' 'CustomRendered'
+    'A402' 'ExposureMode'
+    'A403' 'WhiteBalance'
+    'A404' 'DigitalZoomRatio'
+    'A405' 'FocalLengthIn35mmFilm'
+    'A406' 'SceneCaptureType'
+    'A407' 'GainControl'
+    'A408' 'Contrast'
+    'A409' 'Saturation'
+    'A40A' 'Sharpness'
+    'A40B' 'DeviceSettingDescription'
+    'A40C' 'SubjectDistanceRange'
+    'A420' 'ImageUniqueID'
+
+    % Interoperatibility tags
+
+    '001' 'InteroperatibilityIndex'
+    '002' 'InteroperatibilityVersion'
+    '1000' 'RelatedImageFileFormat'
+    '1001' 'RelatedImageWidth'
+    '1002' 'RelatedImageLength'
+  };
+
+  dict = [];
+  for i=1:size(t,1)
+    key   = sprintf('K%04X', hex2dec(t{i,1}));
+    value = t{i,2};
+    dict.(key) = strrep(value, ' ', '_');
+  end
+end
+
+%
+% EXIF GPS tags
+%
+function dict = gpstags()
+  t = {
+    0 'GPSVersionID'
+    1 'GPSLatitudeRef'
+    2 'GPSLatitude'
+    3 'GPSLongitudeRef'
+    4 'GPSLongitude'
+    5 'GPSAltitudeRef'
+    6 'GPSAltitude'
+    7 'GPSTimeStamp'
+    8 'GPSSatellites'
+    9 'GPSStatus'
+    10 'GPSMeasureMode'
+    11 'GPSDOP'
+    12 'GPSSpeedRef'
+    13 'GPSSpeed'
+    14 'GPSTrackRef'
+    15 'GPSTrack'
+    16 'GPSImgDirectionRef'
+    17 'GPSImgDirection'
+    18 'GPSMapDatum'
+    19 'GPSDestLatitudeRef'
+    20 'GPSDestLatitude'
+    21 'GPSDestLongitudeRef'
+    22 'GPSDestLongitude'
+    23 'GPSDestBearingRef'
+    24 'GPSDestBearing'
+    25 'GPSDestDistanceRef'
+    26 'GPSDestDistance'
+  };
+
+  dict = [];
+  for i=1:size(t,1)
+    key   = sprintf('K%04X', t{i,1});
+    value = t{i,2};
+    dict.(key) = strrep(value, ' ', '_');
+  end
+end
+