]> Creatis software - CreaPhase.git/blob - 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
1 ## Copyright (C) 2009 Roderick Koehle    koehle(at)users.sourceforge.net
2 ##
3 ## This program is free software; you can redistribute it and/or
4 ## modify it under the terms of the GNU General Public License
5 ## as published by the Free Software Foundation; either version 2
6 ## of the License, or (at your option) any later version.
7 ##
8 ## Octave is distributed in the hope that it will be useful, but
9 ## WITHOUT ANY WARRANTY; without even the implied warranty of
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 ## General Public License for more details.
12 ##
13 ## You should have received a copy of the GNU General Public License
14 ## along with Octave; see the file COPYING.  If not, see
15 ## <http://www.gnu.org/licenses/>.
16
17 ## -*- texinfo -*-
18 ## @deftypefn {Function File} {@var{exif} =} readexif(@var{filename}, @var{thumbnail})
19 ## Read EXIF information from JPEG image data.
20 ##
21 ## The exif tag information are returned in the @var{exif} data structure.
22 ## Integer ratios are expressed as column vector.
23 ## For example, a focal number of 2.8 is expressed
24 ## as FNumber=[28; 10]. Otherwise all data are returned by the type
25 ## as specified in the IFD structures.
26 ##
27 ## The filename for the thumbnail image is optional.
28 ## If given, the thumbnail jpeg image will be stored to
29 ## file @var{thumbnail}. 
30 ##
31 ## Reference:
32 ## JEITA CP-3451, Exchangeable image file format for digital still cameras:
33 ## Exif Version 2.2
34 ##
35 ## @seealso{imwrite, imfinfo}
36 ## @end deftypefn
37
38 function exif = readexif(file, thumbnail)
39
40   % Enable the debug flag to see more of the JPG sections.
41
42   debug = false;
43
44   in = fopen(file);
45   if (in<0)
46     error('File "%s" not found !', file);
47   end
48
49   s = fread(in, 1, 'uint16', 'ieee-be');
50
51   JPEG.SOI = 0xffd8;
52   JPEG.APP0 = 0xffe0;
53   JPEG.APP1 = 0xffe1;
54   JPEG.APP2 = 0xffe2;
55   JPEG.DQT = 0xffdb;
56   JPEG.DHT = 0xffc4;
57   JPEG.DRI = 0xffdd;
58   JPEG.SOF = 0xffc0;
59   JPEG.SOS = 0xffda;
60   JPEG.EOI = 0xffd9;
61
62   % Stop if no Start of Image found
63
64   if s~=JPEG.SOI
65     error('JPEG Format error - missing start of image tag.');
66   end
67
68   exif = [];
69
70   while ~feof(in)
71     s = fread(in, 1, 'uint16', 'ieee-be');
72
73     switch s
74       case JPEG.SOI
75       case JPEG.APP0
76         l = fread(in, 1, 'uint16', 'ieee-be');
77         if debug, printf('APP0: %i\n', l); end
78         fseek(in, l-2, 'cof');
79       case JPEG.APP1
80         l = fread(in, 1, 'uint16', 'ieee-be');
81         if debug, printf('APP1: %i\n', l); end
82         app1 = fread(in, l-2, 'uchar');
83         if nargin==1
84           exif = parseexif(app1);
85         else
86           exif = parseexif(app1, thumbnail);
87         end
88         % stop reading further, remove following break
89         % if you want to extend this parser.
90         break; 
91       case JPEG.APP2
92         l = fread(in, 1, 'uint16', 'ieee-be');
93         if debug, printf('APP2: %i\n', l); end
94         fseek(in, l-2, 'cof');
95       case JPEG.DQT % define quantization table
96         l = fread(in, 1, 'uint16', 'ieee-be');
97         if debug, printf('DQT: %i\n', l); end;
98         fseek(in, l-2, 'cof');
99       case JPEG.DHT % define huffmann table
100         l = fread(in, 1, 'uint16', 'ieee-be');
101         fseek(in, l-2, 'cof');
102         if debug, printf('DHT: %i\n', l); end
103       case JPEG.DRI % define restart interoperability
104         l = fread(in, 1, 'uint16', 'ieee-be');
105         fseek(in, l-2, 'cof');
106         if debug, printf('DRI: %i\n', l); end
107       case JPEG.SOF % start of frame
108         l = fread(in, 1, 'uint16', 'ieee-be');
109         fseek(in, l-2, 'cof');
110         if debug, printf('SOF: %i\n', l); end
111       case JPEG.SOS % start of scan
112         l = fread(in, 1, 'uint16', 'ieee-be');
113         fseek(in, l-2, 'cof');
114         if debug, printf('SOS: %i\n', l); end
115
116         % JPEG compressed data comes here ...
117         break;
118
119       case JPEG.EOI % end of image
120         printf('EOI');
121       otherwise % Skip unknown tags
122         l = fread(in, 1, 'uint16', 'ieee-be');
123         if debug, printf('TAG %04X: %i\n', s, l); end
124         fseek(in, l-2, 'cof');
125     end
126   end
127
128   fclose(in);
129 end
130
131 %
132 % Parse EXIF APP1 section
133 %
134 % This routine will parse the APP1 section of an jpeg image.
135 % If a filename "thumb" is given, the tumbnail image data
136 % will be exported to given file.
137 %
138 %   exif = parseexif(data, thumb)
139 %
140 function exif = parseexif(data, thumb)
141
142   id = char(data(1:6).');
143   if strncmp(id, ['Exif' 0 0], 6)
144
145     % TIFF header
146
147     byteorder = char(data(7:8).');
148     littleendian = strncmp(byteorder, 'II', 2);
149     bigendian    = strncmp(byteorder, 'MM', 2);
150
151     tag42     = intn(data(9:10), bigendian);
152     offset    = intn(data(11:14), bigendian);
153
154     if (~littleendian && ~bigendian) || tag42~=42
155       error('invalid TIFF header');
156     end
157
158     % IFD fields
159
160     exif = ifdparse(tifftags(), data, offset, bigendian);
161   else
162     exif = [];
163   end
164
165   % export thumbnail image
166
167   if nargin==2 && isfield(exif, 'JPEGInterchangeFormat')
168     i = exif.JPEGInterchangeFormat;
169     n = exif.JPEGInterchangeFormatLength;
170
171     jpg = data(7+i:7+i+n-1);
172     out = fopen(thumb, 'w');
173     if (out<0), 
174       error('Cannot open file "%s" for writing thumbnail image.', thumb);
175     end
176     fwrite(out, jpg, 'uint8');
177     fclose(out);
178   end
179 end
180
181 function ifd = ifdparse(dict, data, offset, endian)
182
183   debug = false;
184
185   ifd = [];
186
187   while offset
188     ifd_fields = intn(data(7+offset+(0:1)), endian);
189
190     if debug, printf('Tag  Type Count    Offset\n'); end
191     for i=1:ifd_fields
192       j = 9+offset+(i-1)*12;
193       ifd_tag    = intn(data(  j:j+1), endian);
194       ifd_type   = intn(data(j+2:j+3), endian);
195       ifd_count  = intn(data(j+4:j+7), endian);
196       ifd_offset = intn(data(j+8:j+11), endian);
197
198       name = ifdtagname(dict, ifd_tag);
199
200       if debug,
201         printf('%04x %04x %08x %08x %s : ', ifd_tag, ifd_type, ...
202                ifd_count, ifd_offset, name);
203       end
204       if ifd_type>0
205         n = ifdsize(ifd_type);
206
207         if n*ifd_count<=4
208           value = data(j+8:j+8+n*ifd_count-1);
209           value = reshape(value, n, ifd_count);
210         else
211           a = 7+ifd_offset;
212           b = 7+ifd_offset+n*ifd_count-1;
213           if (a>0 && b>0 && a<=length(data) && b<=length(data))
214             value = data(7+ifd_offset:7+ifd_offset+n*ifd_count-1);
215             value = reshape(value, n, ifd_count);
216           else
217             value = [];
218           end
219         end
220       end
221    
222       switch ifd_type
223         case 01 % unsigned char
224           ifd.(name) = uint8(value);
225           if debug,
226             printf('%02x ', uint8(value));
227             printf('\n');
228           end
229         case 02 % Ascii
230           ifd.(name) = char(value);
231           if debug, printf('%s\n', char(value)); end
232         case 03 % 16 bit unsigned int
233           ifd.(name) = uintn(value, endian);
234           if debug
235             printf('%i ', intn(value), endian);
236             printf('\n');
237           end
238         case 04 % 32 bit unsigned int
239           ifd.(name) = uintn(value, endian);
240           if debug, printf('%i\n', uintn(value, endian)); end
241         case 05 % 32 bit unsigned rational
242           ifd.(name) = [uintn(value(1:4,:), endian); uintn(value(5:8,:), endian)];
243           if debug, printf('%i/%i\n',uintn(value(1:4), endian),uintn(value(5:8)), endian); end
244         case 07 % unknown
245           ifd.(name) = uint8(value);
246           if debug
247             printf('%02x ', value);
248             printf('\n');
249           end
250         case 09 % 32 bit signed int
251           ifd.(name) = intn(value, endian);
252           if debug, printf('%i\n', intn(value, endian)); end
253         case 10 % 32 bit signed rational
254           ifd.(name) = [intn(value(1:4,:), endian); intn(value(5:8,:), endian)];
255           if debug, printf('%i/%i\n',intn(value(1:4), endian),intn(value(5:8)), endian); end
256         otherwise
257           printf('%02x ', value);
258           printf('\n');
259       end
260
261       switch ifd_tag
262         case 0x8769, % Exif Pointer
263           ifd.(name) = ifdparse(exiftags(), data, ifd_offset, endian);
264         case 0x8825, % GPS Pointer
265           ifd.(name) = ifdparse(gpstags(), data, ifd_offset, endian);
266         case 0xa005 % Interoperatibility Pointer
267           ifd.(name) = ifdparse(dict, data, ifd_offset, endian);
268 %        case 0x927c % Makernotes
269 %          ifd.(name) = ifdparse([], data, ifd_offset, endian);
270         otherwise
271       end
272     end
273     j = 9+offset+ifd_fields*12;
274     ifd_next = intn(data(j:j+3), endian);
275
276     offset = ifd_next;
277   end
278 end
279
280 %
281 % Return bytelength for respective IFD type
282 %
283 function n = ifdsize(ifd_type)
284   switch ifd_type
285     case {1, 2, 7}, n = 1;
286     case 03       , n = 2;
287     case {4, 9}   , n = 4;
288     case {5, 10}  , n = 8;
289     otherwise     , n = 1;
290   end
291 end
292
293
294 %
295 % Convert little endian character vector to integer
296 %
297 function y = intn(x, bigendian)
298   if bigendian
299     y = polycol(x, int32(256));
300   else
301     y = polycol(flipud(x), int32(256));
302   end
303 end
304
305 function y = uintn(x, bigendian)
306   if bigendian
307      y = polycol(x, uint32(256));
308   else
309      y = polycol(flipud(x), uint32(256));
310   end
311 end
312
313 %
314 % Use own polyval that works with integers,
315 % it evaluates the number polygon columnwise.
316 %
317 %   number = polycol(digits, base)
318 %
319 function y = polycol(c, x)
320   y = c(1,:);
321   for i=2:size(c, 1)
322     y = y.*x+c(i,:);
323   end
324 end
325
326 %
327 % Query EXIF IFD tagname
328 %
329 % Unfortunately, neither MATLAB nor Octave provide a hash functionality,
330 % so use structures as hash.
331 %
332 function name = ifdtagname(dict, key)
333   k = sprintf('K%04X', key);
334   if isfield(dict, k)
335     name = dict.(k);
336   else
337     name = sprintf('tag%04X', key);
338   end
339 end
340
341 %
342 % Primary image IFD tags according to Exif 2.2
343 %
344 function dict = tifftags()
345   t = {
346
347     % TIFF Tags according to EXIF2.2, additional baseline TIFF tags are marked by a '%'
348
349     '0FE' 'NewSubfileType' %
350     '0FF' 'SubfileType' %
351     '100' 'ImageWidth'
352     '101' 'ImageLength'
353     '102' 'BitsPerSample'
354     '103' 'Compression'
355     '106' 'PhotometricInterpretation'
356     '108' 'CellWidth' %
357     '109' 'CellLength' %
358     '10A' 'FillOrder' %
359     '10E' 'ImageDescription'
360     '10F' 'Make'
361     '110' 'Model'
362     '111' 'StripOffsets'
363     '112' 'Orientation'
364     '115' 'SamplesPerPixel'
365     '116' 'RowsPerStrip'
366     '117' 'StripByteCounts'
367     '118' 'MinSampleValue' %
368     '119' 'MaxSampleValue' %
369     '11A' 'XResolution'
370     '11B' 'YResolution'
371     '11C' 'PlanarConfiguration'
372     '120' 'FreeOffsets' %
373     '121' 'FreeByteCounts' %
374     '122' 'GrayResponseUnit' %
375     '123' 'GrayResponseCurve' %
376     '128' 'ResolutionUnit'
377     '12D' 'TransferFunction'
378     '131' 'Software'
379     '132' 'DateTime'
380     '13B' 'Artist'
381     '13C' 'HostComputer' %
382     '13E' 'WhitePoint'
383     '13F' 'PrimaryChromaticities'
384     '140' 'ColorMap' %
385     '152' 'ExtraSamples' %
386     '201' 'JPEGInterchangeFormat'
387     '202' 'JPEGInterchangeFormatLength'
388     '211' 'YCbCrCoefficients'
389     '212' 'YCbCrSubSampling'
390     '213' 'YCbCrPositioning'
391     '214' 'ReferenceBlackWhite'
392     '8298' 'Copyright'
393     '8769' 'Exif IFD Pointer'
394     '8825' 'GPS Info IFD Pointer'
395   };
396
397   dict = [];
398   for i=1:size(t,1)
399     key   = sprintf('K%04X', hex2dec(t{i,1}));
400     value = t{i,2};
401     dict.(key) = strrep(value, ' ', '_');
402   end
403 end
404
405 %
406 % EXIF private tags
407 %
408 function dict = exiftags()
409   t = {
410
411     % EXIF Tags
412
413     '829A' 'ExposureTime'
414     '829D' 'FNumber'
415     '8822' 'ExposureProgram'
416     '8824' 'SpectralSensitivity'
417     '8827' 'ISOSpeedRatings'
418     '8828' 'OECF'
419     '9000' 'ExifVersion'
420     '9003' 'DateTimeOriginal'
421     '9004' 'DateTimeDigitized'
422     '9101' 'ComponentsConfiguration'
423     '9102' 'CompressedBitsPerPixel'
424     '9201' 'ShutterSpeedValue'
425     '9202' 'ApertureValue'
426     '9203' 'BrightnessValue'
427     '9204' 'ExposureBiasValue'
428     '9205' 'MaxApertureValue'
429     '9206' 'SubjectDistance'
430     '9207' 'MeteringMode'
431     '9208' 'LightSource'
432     '9209' 'Flash'
433     '920A' 'FocalLength'
434     '9214' 'SubjectArea'
435     '927C' 'MakerNote'
436     '9286' 'UserComment'
437     '9290' 'SubsecTime'
438     '9291' 'SubsecTimeOriginal'
439     '9292' 'SubsecTimeDigitized'
440     'A000' 'FlashpixVersion'
441     'A001' 'ColorSpace'
442     'A002' 'PixelXDimension'
443     'A003' 'PixelYDimension'
444     'A004' 'RelatedSoundFile'
445     'A005' 'Interoperatibility IFD Pointer'
446     'A20B' 'FlashEnergy'
447     'A20C' 'SpatialFrequencyResponse'
448     'A20E' 'FocalPlaneXResolution'
449     'A20F' 'FocalPlaneYResolution'
450     'A210' 'FocalPlaneResolutionUnit'
451     'A214' 'SubjectLocation'
452     'A215' 'ExposureIndex'
453     'A217' 'SensingMethod'
454     'A300' 'FileSource'
455     'A301' 'SceneType'
456     'A302' 'CFAPattern'
457     'A401' 'CustomRendered'
458     'A402' 'ExposureMode'
459     'A403' 'WhiteBalance'
460     'A404' 'DigitalZoomRatio'
461     'A405' 'FocalLengthIn35mmFilm'
462     'A406' 'SceneCaptureType'
463     'A407' 'GainControl'
464     'A408' 'Contrast'
465     'A409' 'Saturation'
466     'A40A' 'Sharpness'
467     'A40B' 'DeviceSettingDescription'
468     'A40C' 'SubjectDistanceRange'
469     'A420' 'ImageUniqueID'
470
471     % Interoperatibility tags
472
473     '001' 'InteroperatibilityIndex'
474     '002' 'InteroperatibilityVersion'
475     '1000' 'RelatedImageFileFormat'
476     '1001' 'RelatedImageWidth'
477     '1002' 'RelatedImageLength'
478   };
479
480   dict = [];
481   for i=1:size(t,1)
482     key   = sprintf('K%04X', hex2dec(t{i,1}));
483     value = t{i,2};
484     dict.(key) = strrep(value, ' ', '_');
485   end
486 end
487
488 %
489 % EXIF GPS tags
490 %
491 function dict = gpstags()
492   t = {
493     0 'GPSVersionID'
494     1 'GPSLatitudeRef'
495     2 'GPSLatitude'
496     3 'GPSLongitudeRef'
497     4 'GPSLongitude'
498     5 'GPSAltitudeRef'
499     6 'GPSAltitude'
500     7 'GPSTimeStamp'
501     8 'GPSSatellites'
502     9 'GPSStatus'
503     10 'GPSMeasureMode'
504     11 'GPSDOP'
505     12 'GPSSpeedRef'
506     13 'GPSSpeed'
507     14 'GPSTrackRef'
508     15 'GPSTrack'
509     16 'GPSImgDirectionRef'
510     17 'GPSImgDirection'
511     18 'GPSMapDatum'
512     19 'GPSDestLatitudeRef'
513     20 'GPSDestLatitude'
514     21 'GPSDestLongitudeRef'
515     22 'GPSDestLongitude'
516     23 'GPSDestBearingRef'
517     24 'GPSDestBearing'
518     25 'GPSDestDistanceRef'
519     26 'GPSDestDistance'
520   };
521
522   dict = [];
523   for i=1:size(t,1)
524     key   = sprintf('K%04X', t{i,1});
525     value = t{i,2};
526     dict.(key) = strrep(value, ' ', '_');
527   end
528 end
529