(* Version 1.7 2007/11/19 JM Alliot Le téléchargement et l'utilisation de ce programme est autorisé à condition que son origine soit explicitement mentionnée et que son utilisation se limite à des fins non commerciales, notamment de recherche, d'éducation et d'enseignement. Tous droits réservés. The download and use of this program is allowed only if its provenance is explicitly stated , and if it is used only for non profit, educational or research activities. All rights reserved. *) type endianness = Motorola | Intel;; let jtags = [ (0xc0,"SOF0 - DCT traditionnel"); (0xc1,"DCT séquentiel étendu - SOF1"); (0xc2,"DCT progressif - SOF2"); (0xc3,"Codage sans perte de donnée (lossless) - SOF3"); (0xc4,"Définition de table de Huffman - DHT"); (0xc5,"DCT séquentiel différentiel - SOF5"); (0xc6,"DCT progressif différentiel - SOF6"); (0xc7,"Sans perte de données (lossless), différentiel - SOF7"); (0xc8,"Réservé pour extensions JPEG - JPG"); (0xc9,"DCT séquentiel étendu - SOF9"); (0xca,"DCT progressif - SOF10"); (0xcb,"Codage sans perte de donnée (lossless) - SOF11"); (0xcc,"Définit le conditionnement de codage arithmétique - DAC"); (0xcd,"DCT séquentiel différentiel - SOF13"); (0xce,"DCT progressif différentiel - SOF14"); (0xcf,"Sans perte de données (lossless), différentiel - SOF15"); (0xd0,"Redémarrage - RST0"); (0xd1,"Redémarrage - RST1"); (0xd2,"Redémarrage - RST2"); (0xd3,"Redémarrage - RST3"); (0xd4,"Redémarrage - RST4"); (0xd5,"Redémarrage - RST5"); (0xd6,"Redémarrage - RST6"); (0xd7,"Redémarrage - RST7"); (0xd8,"Début d'image - SOI"); (0xd9,"Fin d'image - EOI"); (0xda,"Début de scan - SOS"); (0xdb,"Définition de table de quantification - DQT"); (0xdc,"Définition du nombre de lignes - DNL"); (0xdd,"Définition de l'intervalle de redémarrage - DRI"); (0xde,"Définition progression hiérarchique - DHP"); (0xdf,"Extension - EXP"); (0xe0,"Réservé pour les applications externes - APP0"); (0xe1,"Réservé pour les applications externes - APP1"); (0xe2,"Réservé pour les applications externes - APP2"); (0xe3,"Réservé pour les applications externes - APP3"); (0xe4,"Réservé pour les applications externes - APP4"); (0xe5,"Réservé pour les applications externes - APP5"); (0xe6,"Réservé pour les applications externes - APP6"); (0xe7,"Réservé pour les applications externes - APP7"); (0xe8,"Réservé pour les applications externes - APP8"); (0xe9,"Réservé pour les applications externes - APP9"); (0xea,"Réservé pour les applications externes - APP10"); (0xeb,"Réservé pour les applications externes - APP11"); (0xec,"Réservé pour les applications externes - APP12"); (0xed,"Réservé pour les applications externes - APP13"); (0xee,"Réservé pour les applications externes - APP14"); (0xef,"Réservé pour les applications externes - APP15"); (0xf0,"Réservé pour les extensions JPEG - JPG0"); (0xf1,"Réservé pour les extensions JPEG - JPG1"); (0xf2,"Réservé pour les extensions JPEG - JPG2"); (0xf3,"Réservé pour les extensions JPEG - JPG3"); (0xf4,"Réservé pour les extensions JPEG - JPG4"); (0xf5,"Réservé pour les extensions JPEG - JPG5"); (0xf6,"Réservé pour les extensions JPEG - JPG6"); (0xf7,"Réservé pour les extensions JPEG - JPG7"); (0xf8,"Réservé pour les extensions JPEG - JPG8"); (0xf9,"Réservé pour les extensions JPEG - JPG9"); (0xfa,"Réservé pour les extensions JPEG - JPG10"); (0xfb,"Réservé pour les extensions JPEG - JPG11"); (0xfc,"Réservé pour les extensions JPEG - JPG12"); (0xfd,"Réservé pour les extensions JPEG - JPG13"); (0xfe,"Commentaire - COM"); (0x01,"Pour usage temporaire lors du codage arithmétique - TEM") ];; (* Autres: "Réservé - RES")*) let tags = [ (0xfe,"NewSubfileType"); (0xff,"SubfileType"); (0x100,"ImageWidth"); (0x101,"ImageLength"); (0x102,"BitsPerSample"); (0x103,"Compression"); (0x106,"PhotometricInterpretation"); (0x107,"Thresholding"); (0x108,"CellWidth"); (0x109,"CellLength"); (0x10a,"FillOrder"); (0x10d,"DocumentName"); (0x10e,"ImageDescription"); (0x10f,"Make"); (0x110,"Model"); (0x111,"StripOffsets"); (0x112,"Orientation"); (0x115,"SamplesPerPixel"); (0x116,"RowsPerStrip"); (0x117,"StripByteCounts"); (0x118,"MinSampleValue"); (0x119,"MaxSampleValue"); (0x11a,"XResolution"); (0x11b,"YResolution"); (0x11c,"PlanarConfiguration"); (0x11d,"PageName"); (0x11e,"XPosition"); (0x11f,"YPosition"); (0x120,"FreeOffsets"); (0x121,"FreeByteCounts"); (0x122,"GrayResponseUnit"); (0x123,"GrayResponseCurve"); (0x124,"T4Options"); (0x125,"T6Options"); (0x128,"ResolutionUnit"); (0x129,"PageNumber"); (0x12d,"TransferFunction"); (0x131,"Software"); (0x132,"DateTime"); (0x13b,"Artist"); (0x13c,"HostComputer"); (0x13d,"Predictor"); (0x13e,"WhitePoint"); (0x13f,"PrimaryChromaticity"); (0x140,"ColorMap"); (0x141,"HalfToneHints"); (0x142,"TileWidth"); (0x143,"TileLength"); (0x144,"TileOffset"); (0x145,"TileByteCounts"); (0x14a,"SubIFDs"); (0x14c,"InkSet"); (0x14d,"InkNames"); (0x14e,"NumberOfInks"); (0x150,"DotRange"); (0x151,"TargetPrinter"); (0x152,"ExtraSamples"); (0x153,"SampleFormat"); (0x154,"SMinSampleValue"); (0x155,"SMaxSampleValue"); (0x156,"TransferRange"); (* JPEG tags *) (0x200,"JPEGProc"); (0x201,"JPEGInterchangeFormat"); (0x202,"JPEGInterchangeFormatLngth"); (0x203,"JPEGRestartInterval"); (0x205,"JPEGLosslessPredictors"); (0x206,"JPEGPointTransforms"); (0x207,"JPEGQTables"); (0x208,"JPEGDCTables"); (0x209,"JPEGACTables"); (0x211,"YCbCrCoefficients"); (0x212,"YCbCrSubSampling"); (0x213,"YCbCrPositionning"); (0x214,"ReferenceBlackWhite"); (* Adobe XMP extension *) (0x2bc,"AdobeXMP"); (0x8298,"Copyrigth"); (* IPTC info *) (0x83bb,"IPTC"); (* Adobe extension *) (0x8649,"PhotoshopImageResourceBlocks"); (* ExifIFD pointer *) (0x8769,"ExifIFD"); (* ICC profile *) (0x8773,"ICCProfile"); (* GPS information *) (0x8825,"GPSIFD"); (* Adobe DNG extensions *) (0xc612,"DNGVersion"); (0xc613,"DNGBackWardVersion"); (0xc614,"DNGUniqueCameraModel"); (0xc615,"DNGLocalizedCameraModel"); (0xc616,"DNGCFAPlaneColor"); (0xc617,"DNGCFALayout"); (0xc618,"DNGLinearizationTable"); (0xc619,"DNGBlackLevelRepeatDim"); (0xc61a,"DNGBlackLevel"); (0xc61b,"DNGBlackLevelDeltaH"); (0xc61c,"DNGBlackLevelDeltaV"); (0xc61d,"DNGWhiteLevel"); (0xc61e,"DNGDefaultScale"); (0xc61f,"DNGDefaultCropOrigin"); (0xc620,"DNGDefaultCropSize"); (0xc621,"DNGColorMatrix1"); (0xc622,"DNGColorMatrix2"); (0xc623,"DNGCameraCalibration1"); (0xc624,"DNGCameraCalibration2"); (0xc625,"DNGReductionMatrix1"); (0xc626,"DNGReductionMatrix2"); (0xc627,"DNGAnalogBalance"); (0xc628,"DNGAsShotNeutral"); (0xc629,"DNGAsShotWhiteXY"); (0xc62a,"DNGBaselineExposure"); (0xc62b,"DNGBaselineNoise"); (0xc62c,"DNGBaselineSharpness"); (0xc62d,"DNGBayerGreenSplit"); (0xc62e,"DNGBayerLinearResponseLimit"); (0xc62f,"DNGCameraSerialNumber"); (0xc630,"DNGLensInfo"); (0xc631,"DNGChromaBlurRadius"); (0xc632,"DNGAntiAliasStrength"); (0xc634,"DNGPrivateDate"); (0xc635,"DNGMakerNoteSafety"); (0xc65a,"DNGCalibrationIlluminant1"); (0xc65b,"DNGCalibrationIlluminant2"); (0xc65c,"DNGBestQualityScale"); (50739,"DNGShadowScale"); (50781,"DNGRawDataUnique"); (50827,"DNGOriginalRawFileName"); (50828,"DNGOriginalRawFileData"); (50829,"DNGActiveArea"); (50830,"DNGMaskedAreas"); (50831,"DNGAsShotICCProfile"); (50832,"DNGAsShotPreProfileMatrix"); (50833,"DNGCurrentICCProfile"); (50834,"DNGCurrentPreProfileMatrix"); (* Exif tags *) (0xa005,"ExifInteroperabilityIFD"); (0x9000,"ExifVersion"); (0xa000,"FlashPixVersion"); (0xa001,"ColorSpace"); (0x9101,"ComponentsConfiguration"); (0x9102,"CompressedBitsPerPixel"); (0xa002,"PixelXDimension"); (0xa003,"PixelYDimension"); (0x927c,"MakerNote"); (0x9286,"UserComment"); (0xa004,"RelatedSoundFile"); (0x9003,"DateTimeOriginal"); (0x9004,"DateTimeDigitized"); (0x9290,"SubSecTime"); (0x9291,"SubSecTimeOriginal"); (0x9292,"SubSecTimeDigitized"); (0xa420,"UniqueImageId"); (0x829a,"ExposureTime"); (0x829d,"FNumber"); (0x8822,"ExposureProgram"); (0x8824,"SpectralSensitivity"); (0x8827,"ISOSpeedRating"); (0x8828,"OECF"); (0x9201,"ShutterSpeedValue"); (0x9202,"ApertureValue"); (0x9203,"BrightnessValue"); (0x9204,"ExposureBias"); (0x9205,"MaxApertureValue"); (0x9206,"SubjectDistance"); (0x9207,"MeteringMode"); (0x9208,"LightSource"); (0x9209,"Flash"); (0x920a,"FocalLength"); (0x9214,"SubjectArea"); (0xa20b,"FlashEnergy"); (0xa20c,"SpatialFrequencyResponse"); (0xa20e,"FocalPlaneXResolution"); (0xa20f,"FocalPlaneYResolution"); (0xa210,"FocalPlaneResolutionUnit"); (0xa214,"SubjectLocation"); (0xa215,"ExposureIndex"); (0xa217,"SensingMethod"); (0xa300,"FileSource"); (0xa301,"SceneType"); (0xa302,"CFAPattern"); (0xa401,"CustomRendered"); (0xa402,"ExposureMode"); (0xa403,"WhiteBalance"); (0xa404,"DigitalZoomRatio"); (0xa405,"FocalLengthIn35mmFilm"); (0xa406,"SceneCaptureType"); (0xa407,"GainControl"); (0xa408,"Contrast"); (0xa409,"Saturation"); (0xa40a,"Sharpness"); (0xa40b,"DeviceSettingDescription"); (0xa40c,"SubjectDistanceRange"); (* TIFF-EP *) (0x828d,"CFARepeatPatternDim"); (0x828e,"CFAPattern"); (0x828f,"BatteryLevel"); (0x8829,"Interlace"); (0x882a,"TimeZoneOffset"); (0x882b,"SelfTimerMode"); (0x920b,"FlashEnergy"); (0x920c,"SpatialFrequencyResponse"); (0x920d,"Noise"); (0x920e,"FocalPlaneXResolution"); (0x920f,"FocalPlaneYResolution"); (0x9210,"FocalPlaneResolutionUnit"); (0x9211,"ImageNumber"); (0x9212,"SecurityClassification"); (0x9213,"ImageHistory"); (0x9214,"SubjectLocation"); (0x9215,"ExposureIndex"); (0x9216,"TIFF/EPStandardID"); (0x9217,"SensingMethod"); (* Canon extension *) (0xc640,"RawStripping [n vertical rows of width x+last row width y]") ];; let make = ref "";; let nib_h t = (t land 0xf0) lsr 4;; let nib_l t = (t land 0x0f);; let name_tag tag = try List.assoc tag tags with Not_found -> "UnknownTag";; let types = [| ("",0); (* 0 is not a valid type *) ("BYTE",1); ("ASCII",1); ("SHORT",2); ("LONG",4); ("RATIONAL",8); ("SBYTE",1); ("UNDEFINED",1); ("SSHORT",2); ("SLONG",4); ("SRATIONAL",8); ("FLOAT",4); ("DOUBLE",8)|];; let input_word fp endian = let v = Array.init 2 (fun _ -> input_byte fp) in match endian with Intel -> v.(0)+256*v.(1) | Motorola -> v.(1)+256*v.(0);; let input_dword fp endian = let v = Array.init 4 (fun _ -> input_byte fp) in match endian with Intel -> v.(0)+256*(v.(1)+256*(v.(2)+256*v.(3))) | Motorola -> v.(3)+256*(v.(2)+256*(v.(1)+256*v.(0))) let rec read_chunk offset fp endian tag typ count goffset= let pos = pos_in fp in seek_in fp (goffset+offset); (* Remove terminating char (0) in ASCII string *) if typ=2 then begin let s = String.create (count-1) in for i = 0 to count-2 do s.[i]<-input_char fp done; Printf.printf "%s" s; if tag=0x10f then (* Make Tag *) make := s; end else if count <10000 then for i = 1 to count do if (tag=0x14a) then begin let v = input_dword fp endian in Printf.printf "Start SubIFD%d\n" i; let pos = pos_in fp in read_full_dir v fp endian goffset; seek_in fp pos; Printf.printf "End SubIFD%d\n" i; flush stdout (* TIFF-EP SubIFD subdir *) end else match typ with 1 | 6 | 7-> Printf.printf "%d " (input_byte fp) | 3 | 8 -> Printf.printf "%d " (input_word fp endian) | 4 | 9 -> Printf.printf "%d " (input_dword fp endian) | 5 | 10 -> Printf.printf "(%d/" (input_dword fp endian); Printf.printf "%d) " (input_dword fp endian); | _ -> (); done else Printf.printf "count=%d. Too long to print" count; print_newline(); seek_in fp pos and read_dir offset fp endian goffset = Printf.printf "Start IFD\n"; seek_in fp (goffset+offset); let number=input_word fp endian in Printf.printf "number of entries=%d\n" number; for i = 0 to number-1 do let tag = input_word fp endian and typ = input_word fp endian and count = input_dword fp endian in let len = snd types.(typ) in let total = count * len in if total<=4 then begin let pos = pos_in fp in Printf.printf "pos:%d tag:0x%x %s type:%s count:%d val:" pos tag (name_tag tag) (fst types.(typ)) count; for i = 1 to count do match len with 1 -> Printf.printf "%d " (input_byte fp) | 2 -> Printf.printf "%d " (input_word fp endian) | 4 -> Printf.printf "%d " (input_dword fp endian ) | x -> Printf.printf "tag=%d type=%d count=%d total=%d pos=%d\n" tag typ count total pos; failwith "Zorgulub"; done; print_newline (); seek_in fp pos; let v = if len=4 then input_dword fp endian else 0 in seek_in fp (pos+4); if tag=0x8769 then begin Printf.printf "Start SubExifIFD\n"; read_full_dir v fp endian goffset; Printf.printf "End SubExifIFD\n" (* ExifIFD subdir *) end; if tag=0x14a then begin Printf.printf "Start SubIFD\n"; read_full_dir v fp endian goffset; Printf.printf "End SubIFD\n" (* TIFF-EP SubIFD subdir *) end; if tag=0x201 then begin Printf.printf "Start SubJPEG\n"; let pos = pos_in fp in decode_jpeg fp ; seek_in fp pos; Printf.printf "End SubJPEG\n" (* JPEG subimage *) end; end else begin let pos = pos_in fp in let offset = input_dword fp endian in Printf.printf "pos:%d tag:0x%x %s type:%s count:%d offset:%d val: " pos tag (name_tag tag) (fst types.(typ)) count offset; if (tag=0x927c) && (!make="Canon") then (* Canon MakerNote is the only one we try to decode a little*) begin Printf.printf "Start MakerNote\n"; read_full_dir offset fp endian goffset; Printf.printf "End MakerNote\n" end else read_chunk offset fp endian tag typ count goffset; end; done; let offset = input_dword fp endian in Printf.printf "EndIFD\n"; offset and read_full_dir offset fp endian goffset = let pos = pos_in fp in let offset = ref offset in while (!offset <>0) do Printf.printf "offset=%d\n" !offset; offset := read_dir !offset fp endian goffset; done; seek_in fp pos and decode_tiff fp goffset = seek_in fp goffset; let c1 = input_char fp and c2 = input_char fp in let endian = if (c1='I') && (c2='I') then Intel else if (c1='M') && (c2='M') then Motorola else failwith "No Endianness" in let magic = input_word fp endian in if magic <>42 then failwith "Not TIFF"; let offset = (input_dword fp endian) in if (offset=16) && (input_char fp = 'C') && (input_char fp = 'R') then begin let s1 = input_byte fp and s2 = input_byte fp in Printf.printf "Canon CR2 version %d.%d\n" s1 s2; let cr_offset=(input_dword fp endian) in Printf.printf "Raw offset=%d\n" cr_offset; end; read_full_dir offset fp endian goffset and decode_jpeg fp = let name_jtag jtag = try List.assoc jtag jtags with Not_found -> "Reserved" and get_joffset fp = let v1 = input_byte fp and v2 = input_byte fp in let length = 256*v1+v2 in let pos = pos_in fp in (pos+length-2,length) in try ( while true do let v = input_byte fp in if v = 0xff then let v = input_byte fp in if (v<>0x0) && (v<>0xff) then ( let name = name_jtag v in Printf.printf "%d %s " (pos_in fp) name; (match v with 0xc0|0xc1|0xc2|0xc3|0xc5|0xc6|0xc7|0xc9|0xca|0xcb|0xcd|0xce|0xcf | 0xde -> (* SOF DHP *) let (offset,length) = get_joffset fp in let p = input_byte fp and y = input_word fp Motorola and x = input_word fp Motorola and nf = input_byte fp in Printf.printf "length=%d p=%d y=%d x=%d nf=%d " length p x y nf; for i = 0 to nf-1 do let c = input_byte fp and hv = input_byte fp and tq = input_byte fp in Printf.printf "c=%d h=%d v=%d tq=%d " c (nib_h hv) (nib_l hv) tq done; seek_in fp offset | 0xc4 -> (* DHT *) let (offset,length) = get_joffset fp in let t = input_byte fp in Printf.printf "length=%d th=%d tl=%d " length (nib_h t) (nib_l t); let l = Array.create 16 0 in for i = 0 to 15 do l.(i) <- input_byte fp; Printf.printf "l(%d)=%d " i l.(i) done; for i = 0 to 15 do for j=0 to l.(i)-1 do let b = input_byte fp in Printf.printf "v(%d,%d)=%d " i j b done; done; seek_in fp offset; | 0xdb -> (* DQT *) let (offset,length) = get_joffset fp in let pq = input_byte fp and tq=input_byte fp in Printf.printf "length=%d Pq=%d Tq=%d " length pq tq; for i = 0 to 63 do let q = input_byte fp in Printf.printf "Q(%d)=%d " i q done; seek_in fp offset; | 0xe0|0xe1|0xe2|0xe3|0xe4|0xe5|0xe6|0xe7|0xe9|0xea|0xeb|0xed|0xee|0xef -> (* APP *) let (offset,length) = get_joffset fp in Printf.printf "length=%d " length; let c = ref 0 in while (c:=input_byte fp; !c<>0) do Printf.printf "%c" (char_of_int !c); done; if v = 0xe1 then ( decode_tiff fp (1+pos_in fp); ); seek_in fp offset; | 0xf0|0xf1|0xf2|0xf3|0xf4|0xf5|0xf6|0xf7|0xf8|0xf9|0xfa|0xfb|0xfd -> (* JPG *) let (offset,length) = get_joffset fp in Printf.printf "length=%d " length; seek_in fp offset; | 0xda -> (* SOS *) let (offset,length) = get_joffset fp in let ns= input_byte fp in Printf.printf "length=%d Ns=%d " length ns; for i = 0 to ns-1 do let cs = input_byte fp and t = input_byte fp in Printf.printf "(%d: Cs=%d Td=%d Ta=%d) " i cs (nib_h t) (nib_l t); done; let ss=input_byte fp and se=input_byte fp and a =input_byte fp in Printf.printf "Ss=%d Se=%d Ah=%d Al=%d " ss se (nib_h a) (nib_l a); seek_in fp offset; | 0xd9 -> (* EOI *) raise Exit; | 0x01 | 0xd8-> (* TEM SOI *) (); | 0xcc -> (* DAC *) let (offset,length) = get_joffset fp in let n = (length-2)/2 in Printf.printf "length=%d n=%d " length n; for i = 0 to n-1 do let t = input_byte fp and cs = input_byte fp in Printf.printf "(%d: Tc=%d Tb=%d Cs=%d) " i (nib_h t) (nib_l t) cs; done; seek_in fp offset; | 0xdd -> (* DRI *) let (offset,length) = get_joffset fp in let ri = input_byte fp in Printf.printf "length=%d Ri=%d " length ri; seek_in fp offset; | 0xfe -> (* COM *) let (offset,length) = get_joffset fp in Printf.printf "length=%d " length ; for i = 0 to length-3 do let c = input_char fp in Printf.printf "%c" c done; seek_in fp offset; | 0xdc -> (* DNL *) let (offset,length) = get_joffset fp in let nl = input_byte fp in Printf.printf "length=%d NL=%d " length nl; seek_in fp offset; | 0xdf -> (* EXP *) let (offset,length) = get_joffset fp in let e = input_byte fp in Printf.printf "length=%d Eh=%d Ev=%d " length (nib_h e) (nib_l e); seek_in fp offset; | x -> Printf.printf "Marker=%x\n" x;flush stdout;failwith "Marker not parsed" ); print_newline (); ); done) with End_of_file | Exit -> ();; let _ = let fp = open_in_bin Sys.argv.(1) in let v1 = input_byte fp in let v2 = input_byte fp in seek_in fp 0; if (v1 = 0xff) && (v2 = 0xd8) then decode_jpeg fp else decode_tiff fp 0; close_in fp; exit 0;;