X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?p=CreaPhase.git;a=blobdiff_plain;f=octave_packages%2Fimage-1.0.15%2Freadexif.m;fp=octave_packages%2Fimage-1.0.15%2Freadexif.m;h=c6eeeec0acb571770dd20337d397ddd703515128;hp=0000000000000000000000000000000000000000;hb=c880e8788dfc484bf23ce13fa2787f2c6bca4863;hpb=1705066eceaaea976f010f669ce8e972f3734b05 diff --git a/octave_packages/image-1.0.15/readexif.m b/octave_packages/image-1.0.15/readexif.m new file mode 100644 index 0000000..c6eeeec --- /dev/null +++ b/octave_packages/image-1.0.15/readexif.m @@ -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 +## . + +## -*- 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 +