Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """
- \description TIFF file format dump
- \author paryan
- """
- from __future__ import print_function
- import argparse
- from struct import *
- import os
- import os.path as path
- #
- # field (data) type
- # code -> (byte size, python struct module unpack code)
- #
- vtypes = {
- 12:(8,'d'), # DOUBLE double (IEEE 8-byte)
- 11:(4,'f'), # FLOAT float (IEEE 4-byte)
- 10:(8,'ii'), # SRATIONAL (int32, int32)
- 9:(4,'i'), # SLONG int32
- 8:(2,'h'), # SSHORT int16
- 7:(1,'c'), # UNDEFINED
- 6:(1,'b'), # SBYTE int8
- 5:(8,'II'), # RATIONAL (uint32, uint32)
- 4:(4,'I'), # LONG uint32
- 3:(2,'H'), # SHORT uint16
- 2:(-1,'c'), # ASCII uint8 (7-bit)
- 1:(1,'c') # BYTE uint8
- }
- fieldtype_label = {
- 0: "UNDEFINED",
- 1: "BYTE",
- 2: "ASCII",
- 3: "SHORT",
- 4: "LONG",
- 5: "RATIONAL",
- 6: "SBYTE",
- 7: "UNDEFINED",
- 8: "SSHORT",
- 9: "SLONG",
- 10: "SRATIONAL",
- 11: "FLOAT",
- 12: "DOUBLE",
- }
- #
- # TIFF Tag dictionary
- # Ideally, int maps to tuple of name, and handler/reader or expected data type
- #
- tagdict = {
- 254: ("SubfileType"),# 0=Full, 1=Reduced resolution, 2=one page of many, 4=transparency mask
- 255: ("OldSubfileType"),# 1=full resolution image, 2=reduced image, 3=one page of many
- 256: ("ImageWidth"),
- 257: ("ImageLength"),
- 258: ("BitsPerSample"),
- 259: ("Compression"),
- # 1=None
- # 2=CCITT modified Huffman RLE
- # 3=CCITT Group 3 fax encoding (T.4)
- # 4=CCITT Group 4 fax encoding (T.6)
- # 5=LZW
- # 6=JPEG
- # 7=JPEG DCT
- # 9=TIFF/FX T.85 JBIG
- # 10=TIFF/FX T.43 Colour by layered JBIG
- # 32766=NeXT 2-bit RLE
- # 32771=#1 w/ word alignment (CCITRLEW)
- # 32773=Macintosh RLE (packbits)
- # 32809=Thunderscan RLE
- # 32946=Deflate
- # 8=Adobe Deflate
- # 34712=JPEG2000
- # 34925=LZMA2
- 262: ("PhotometricInterpretation"),
- # 0=min value is white
- # 1=min value is black
- # 2=RGB
- # 3=color map indexed (palette)
- # 4=mask
- # 5=color separation
- # 6=YCbCr (CCIR 601)
- # 8=CIE L*a*b*
- # 9=ICC L*a*b*
- 263: ("Thresholding"),
- # 1=bilevel
- # 2=halftone/dithered scan
- # 3=floyd-steinberg (error diffuse)
- 269: ("DocumentName"),
- 270: ("ImageDescription"),
- 271: ("Make"),
- 272: ("Model"),
- 273: ("StripOffsets"),
- 274: ("Orientation"),
- # 1=top left (normal)
- # 2=top right (mirror horizontal)
- # 3=bottom right (rotate 180)
- # 4=bottom left (mirror vertical)
- # 5=left top (row 0 lhs, col 0 top) mirror h rotate 270 CW
- # 6=right top (rotate90 cw)
- # 7=right bottom mirror h rotate 90 cw
- # 8=left bottom rotate 270 cw
- 277: ("SamplesPerPixel"),
- 278: ("RowsPerStrip"),
- 279: ("StripByteCounts"),
- 280: ("MinSampleValue"),
- 281: ("MaxSampleValue"),
- 282: ("XResolution"),
- 283: ("YResolution"),
- 284: ("PlanarConfiguration"), # 1=contiguous 2=separate planes
- 285: ("PageName"),
- 286: ("XPosition"),
- 287: ("YPosition"),
- 288: ("FreeOffsets"),
- 289: ("FreeByteCounts"),
- 290: ("GrayResponseUnit"),
- 291: ("GrayResponseCurve"),
- 296: ("ResolutionUnit"),
- # 1=None
- # 2=Inch
- # 3=cm (metric)
- 300: ("ColorResponseUnit"),
- 301: ("TransferFunction"),
- 305: ("Software"),
- 306: ("DateTime"),
- 315: ("Artist"),
- 316: ("HostComputer"),
- 317: ("Predictor"),# 1=None, 2=Horizontal, 3=Floating-point horizontal
- 318: ("WhitePoint"),
- 319: ("PrimaryChromaticities"),
- 320: ("ColorMap"),
- 322: ("TileWidth"), # tile width in pixels (cols)
- 323: ("TileLength"),# tile height in pixels (rows)
- 324: ("TileOffsets"),
- 325: ("TileByteCounts"),
- 330: ("SubIFD"),
- 332: ("InkSet"), # 1=CMYK 2=MultiInk (hi-fi)
- 333: ("InkNames"),
- 334: ("NumberOfInks"),
- 336: ("DotRange"),
- 337: ("TargetPrinter"),
- 338: ("ExtraSamples"), #0=Unspecified, 1=Associated alpha, 2=unassociated alpha
- 339: ("SampleFormat"),
- # 1=unsigned int
- # 2=signed int
- # 3=IEEE FP
- # 4=untyped (void)
- # 5=complex signed int
- # 6=complex IEEE FP
- 340: ("MinSampleValue"),
- 341: ("MaxSampleValue"),
- 346: ("Indexed"), # 0=not indexed, 1=indexed
- 351: ("OPIProxy"), # 0=no hi-res, 1=hi-res exists
- 404: ("VersionYear"), #
- 405: ("ModeNumber"), #
- 512: ("JPEGProcessingAlg"), # 1=Baseline, 14=Lossless
- 513: ("JPEGInterchangeFormat"),
- 514: ("JPEGInterchangeFormatLength"),
- 529: ("YCbCrCoefficients"),
- 530: ("YCbCrSubSampling"),
- 531: ("YCbCrPositioning"),
- 532: ("ReferenceBlackWhite"),
- 700: ("XMP"),
- 28672: ("SonyRawFileType"), #Sony ARW
- 28673: ("SonyUndocumented"), #Sony ARW
- 28688: ("SonyCurve"), #Sony ARW
- 32997: ("ImageDepth"),
- 32998: ("TileDepth"),
- 33421: ("CFARepeatPatternDim"),
- 33422: ("CFAPattern"),
- 33432: ("Copyright"),
- 33434: ("ExposureTime"),
- 33437: ("FNumber"),
- 33550: ("ModelPixelScaleTag"), #GeoTIFF
- 33723: ("IPTC"),
- 33920: ("IntegraphMatrixTag"), #IrasB
- 33922: ("ModelTiePointTag"), #GeoTIFF
- 34019: ("RasterPadding"), # 0=Byte,1=Word,2=Long,9=Sector,10=Long Sector
- 34264: ("ModelTransformationTag"), #GeoTIFF
- 34665: ("Exif"), # Exif IFD
- 34675: ("ICCProfile"),
- 34732: ("ImageLayer"),
- 34735: ("GeoKeyDirectoryTag"), #GeoTIFF
- 34736: ("GeoDoubleParamsTag"), #GeoTIFF
- 34737: ("GeoAsciiParamsTag"), #GeoTIFF
- 34850: ("ExposureProgram"),
- # 0=Undefined
- # 1=Manual
- # 2=Program AE
- # 3=Aperture-priority AE
- # 4=Shutter speed priority AE
- # 5=Creative(slow speed)
- # 6=Action(High speed)
- # 7=Portrait
- # 8=Landscape
- # 9=Bulb
- 34852: ("SpectralSensitivity"),
- 34853: ("GPSInfo"),# Sub IFD
- 34855: ("ISOSpeedRatings"),
- 34856: ("OptoElectricConversionFactor"),
- 34857: ("Interlace"),
- 34858: ("TimeZoneOffset"),
- 34859: ("SelfTimeMode"),
- 34864: ("SensitivityType"),
- 34865: ("StandardOutputSensitivity"),
- 34866: ("RecommendedExposureIndex"),
- 34867: ("ISOSpeed"),
- 34868: ("ISOSpeedLatitudeyyy"),
- 34869: ("ISOSpeedLatitudezzz"),
- 36864: ("ExifVersion"),
- 36867: ("DateTimeOriginal"),
- 36868: ("DateTimeDigitized"),
- 37121: ("ComponentsConfiguration"),
- 37122: ("CompressedBitsPerPixel"),
- 37377: ("ShutterSpeedValue"),
- 37378: ("ApertureValue"),
- 37379: ("BrightnessValue"),
- 37380: ("ExposureBiasValue"),
- 37381: ("MaxApertureValue"),
- 37382: ("SubjectDistance"),
- 37383: ("MeteringMode"),
- 37384: ("LightSource"),
- #
- 37385: ("Flash"),
- 37386: ("FocalLength"),
- 37396: ("SubjectArea"),
- 37398: ("TIFF/EPStandardID"),
- 37399: ("SensingMethod"),
- 37500: ("MakerNote"),
- 37510: ("UserComment"),
- 37520: ("SubsecTime"),
- 37521: ("SubsecTimeOriginal"),
- 37522: ("SubsecTimeDigitized"),
- 40960: ("FlashPixVersion"),
- 40961: ("ColorSpace"),
- 40962: ("PixelXDimension"),
- 40963: ("PixelYDimension"),
- 40964: ("RelatedSoundFile"),
- 40965: ("InteropOffset"),
- 41483: ("FlashEnergy"),
- 41484: ("SpatialFreqResponse"),
- 41486: ("FocalPlaneXResolution"),
- 41487: ("FocalPlaneYResolution"),
- 41488: ("FocalPlaneResolutionUnit"),
- 41492: ("SubjectLocation"),
- 41493: ("ExposureIndex"),
- 41495: ("SensingMethod"),
- 41728: ("FileSource"),
- 41729: ("SceneType"),
- 41730: ("CFAPattern"),
- 41985: ("CustomRendered"),
- 41986: ("ExposureMode"),
- 41987: ("WhiteBalance"),
- 41988: ("DigitalZoomRatio"),
- 41989: ("FocalLengthIn35mmFilm"),
- 41990: ("SceneCaptureType"),
- 41991: ("GainControl"),
- 41992: ("Contrast"),
- 41993: ("Saturation"),
- 41994: ("Sharpness"),
- 41995: ("DeviceSettingDescription"),
- 41996: ("SubjectDistanceRange"),
- 42016: ("ImageUniqueID"),
- 42034: ("LensSpecification"), # minfocal, maxfocal, minFnumMinFocal, minFnumMaxFocal
- 42035: ("LensMake"),
- 42036: ("LensModel"),
- 42037: ("LensSerialNumber"),
- 42112: ("GDAL_METADATA"),
- 42113: ("GDAL_NODATA"),
- 42240: ("Gamma"),
- 50706: ("DNGVersion"),
- 50708: ("UniqueCameraModel"),
- 50709: ("LocalizedCameraModel"),
- 50710: ("CFAPlaneColor"),
- 50711: ("CFALayout"),
- 50341: ("PrintImageMatching"),
- 50740: ("DNGPrivateData"),
- 50828: ("OriginalRawFileData"),
- 50829: ("ActiveArea"),
- 50936: ("ProfileName"),
- 50937: ("ProfileHueSatMapDims"),
- 50938: ("ProfileHueSatMapData1"),
- 50939: ("ProfileHueSatMapData2"),
- 50940: ("ProfileToneCurve"),
- 50941: ("ProfileEmbedPolicy"),
- 50942: ("ProfileCopyright"),
- 50964: ("ForwardMatrix1"),
- 50965: ("ForwardMatrix2"),
- 50966: ("PreviewApplicationName"),
- 50967: ("PreviewApplicationVersion"),
- 50968: ("PreviewSettingsName"),
- 50969: ("PreviewSettingsDigest"),
- 50970: ("PreviewColorSpace"),
- 50971: ("PreviewDateTime"),
- 50972: ("RawImageDigest"),
- }
- #
- # GPS specific tags on GPSInfo SubIFD
- #
- gps_tagdict = {
- 0: ("GPSVersionID"),
- 1: ("GPSLatitudeRef"),
- 2: ("GPSLatitude"),
- 3: ("GPSLongitudeRef"),
- 4: ("GPSLongitude"),
- 5: ("GPSAltitudeRef"),
- 6: ("GPSAltitude"),
- 7: ("GPSTimeStamp"),
- 8: ("GPSSatellites"),
- 9: ("GPSStatus"), # A=Active, V=Void
- 10: ("GPSMeasureMode"), # 2=2d, 3=3d
- 11: ("GPSDOP"),
- 12: ("GPSSpeedRef"), # K=kph, M=mph, N=knots
- 13: ("GPSSpeed"),
- 14: ("GPSTrackRef"), # M=Mag North, T=True North
- 15: ("GPSTrack"),
- 16: ("GPSImgDirectionRef"), # M=Mag North, T=True North
- 17: ("GPSImgDirection"),
- 18: ("GPSMapDatum"),
- 19: ("GPSDestLatitudeRef"), # N, S
- 20: ("GPSDestLatitude"),
- 21: ("GPSDestLongitudeRef"), # E,W
- 22: ("GPSDestLongitude"),
- 23: ("GPSDestBearingRef"), #M, T
- 24: ("GPSDestBearing"),
- 25: ("GPSDestDistanceRef"), # K,M, N
- 26: ("GPSDestDistance"),
- 27: ("GPSProcessingMethod"), # GPS, CELLID, WLAN, MANUAL
- 28: ("GPSAreaInformation"),
- 29: ("GPSDateStamp"), #YYYY:mm:dd
- 30: ("GPSDifferential"), # 1=Differential corrected
- 31: ("GPSHPositioningError"),
- }
- makernote_nikon_tagdict = {
- 1: ("MakerNoteVersion"),# 0210=D60
- 2: ("ISO"),
- 3: ("ColorMode"),
- 4: ("Quality"),
- 5: ("WhiteBalance"),
- 6: ("Sharpness"),
- 7: ("FocusMode"),
- 8: ("FlashSetting"),
- 9: ("FlashType"),
- 11: ("WhiteBalanceFineTune"),
- 12: ("WB_RBLevels"),
- 13: ("ProgramShift"),
- 14: ("ExposureDifference"),
- 15: ("ISOSelection"),
- 16: ("DataDump"),
- 17: ("PreviewIFD"),
- 18: ("FlashExposureComp"),
- 19: ("ISOSetting"),
- 20: ("ColorBalanceA"),
- 22: ("ImageBoundary"),
- 23: ("ExternalFlashExposureComp"),
- 24: ("FlashExposureBracketValue"),
- 25: ("ExposureBracketValue"),
- 26: ("ImageProcessing"),
- 27: ("CropHighSpeed"),
- 28: ("ExposureTiming"),
- 29: ("SerialNumber"),
- 30: ("ColorSpace"),
- 31: ("VRInfo"),
- 32: ("ImageAuthentication"),
- 33: ("FaceDetect"),
- 34: ("Active D-Lighting"),
- 35: ("PictureControlData"),
- 36: ("WorldTime"),
- 37: ("ISOInfo"),
- 43: ("DistortInfo"),
- 131: ("LensType"),
- 132: ("Lens"),
- 133: ("ManualFocusDistance"),
- 134: ("DigitalZoom"),
- 135: ("FlashMode"),
- 136: ("AFInfo"),
- 137: ("ShootingMode"), # bitpos
- # :0 = continuous
- # :1 = delay
- # :2 = PC Control
- # :3 = Self-timer
- # :4 = Exposure Bracketing
- # :5 = Auto ISO
- # :6 = WB Bracketing
- # :7 = IR Control
- # :8 = D-Lighting Bracketing
- 149: ("NoiseReduction"),
- 153: ("RawImageCenter"),
- 154: ("SensorPixelSize"),
- 156: ("SceneAssist"),
- 158: ("RetouchHistory"),
- 160: ("SerialNumber"),
- 162: ("ImageDataSize"),
- 165: ("ImageCount"),
- 166: ("DeletedImageCount"),
- 167: ("ShutterCount"),
- 185: ("AFTune"),
- 187: ("RetouchInfo"),
- }
- sony_tagdict = {
- 16: ("CameraInfo"),
- 32: ("FocusInfo"),
- 258: ("Quality"),
- 8206: ("PictureEffect"), #0 single
- 8207: ("SoftSkinEffect"), #0 single
- 8241: ("SerialNumber"), #0 single
- 36875: ("FaceDetectSetting"), #0 single
- 45056: ("FileFormat"), #0 single
- 45057: ("SonyModelID"), #0 single
- 45088: ("CreativeStyle"), #0 single
- 45089: ("ColorTemperature"), #0 single
- 45090: ("ColorCompensationFilter"), # neg green, pos magenta
- 45091: ("SceneMode"), #0 single
- 45092: ("ZoneMatching"), #0 single
- 45093: ("DynamicRangeOptimizer"), #0 single
- 45094: ("ImageStabilization"), #0 single
- 45095: ("LensType"), #0 single
- 45097: ("ColorMode"), #0 single
- 45098: ("LensSpec"), #0 single
- 45099: ("FullImageSize"), #0 single
- 45100: ("PreviewImageSize"), #0 single
- 45129: ("ReleaseMode"), #0 single
- 45130: ("SequenceNumber"), #0 single
- 45138: ("IntelligentAuto"),
- 45140: ("WhiteBalance"),
- }
- relative_offset = 0
- MAX_PRINT = 128
- MAX_EXCERPT = 16
- skipexif=False
- pause = False
- # indexed by field type
- type_handler = {}
- # indexed by tag
- tag_handler = {}
- # indexed by tag
- type_posthandler = {}
- def read_offset(f):
- return read_int(f)
- def subifd_reader(f, ifd):
- #raw_input('>')
- voffsets = default_reader(f, ifd)
- cpos = f.tell()
- for i,voffset in enumerate(voffsets):
- f.seek(relative_offset+voffset)
- print("---SubIFD {} of {}".format(i+1, ifd['count']))
- while read_IFD(f):
- pass
- print("---End SubIFD")
- f.seek(cpos)
- def nikon_lenstype_posthandler(content):
- ctype = ord(content[0])
- bincode = "{0:b}".format(ctype)[::-1]
- result = []
- if bincode[0] == '1': result.append('MF')
- if bincode[1] == '1': result.append('D')
- if bincode[2] == '1': result.append('G')
- if bincode[3] == '1': result.append('VR')
- print (" ".join(result))
- def makernote_reader(f, ifd):
- print (default_reader(f, ifd)[:MAX_PRINT])
- #sony_makernote_reader(f, ifd)
- def sony_makernote_reader(f, ifd):
- voffset = read_offset(f)
- current = f.tell()
- f.seek(relative_offset+voffset)
- #print(read_byte(f, 10))
- #print(read_short(f))
- read_IFD(f, read_IFD_entry, dict(td=sony_tagdict, tag={}, type=type_handler, post={}))
- f.seek(current)
- f.close()
- exit()
- def nikon_makernote_reader(f, ifd):
- global endian, relative_offset
- old_endian = endian
- #print (default_reader(f, ifd)[:MAX_PRINT])
- voffset = read_offset(f)
- current = f.tell()
- f.seek(relative_offset+voffset)
- print(read_byte(f, 10))
- relative_offset = f.tell()
- endian = read_byteorder(f)
- print(read_short(f))
- print(read_int(f))
- raw_input('press <return>')
- nikon_posthandler = {
- 131: nikon_lenstype_posthandler
- }
- read_IFD(f, read_IFD_entry, dict(td=makernote_nikon_tagdict, tag={}, type=type_handler, post=nikon_posthandler))
- f.seek(current)
- raw_input('press <return>')
- endian = old_endian
- relative_offset=0
- def exif_reader(f, ifd):
- print("EXIF")
- voffset = read_offset(f)
- cpos = f.tell()
- f.seek(voffset)
- if not skipexif:
- read_IFD(f)
- else:
- print(voffset)
- f.seek(cpos)
- def gpsinfo_reader(f, ifd):
- print("GPS")
- voffset = read_offset(f)
- cpos = f.tell()
- f.seek(voffset)
- if not skipexif:
- read_IFD(f, read_IFD_entry, dict(td=gps_tagdict, tag={}, type=type_handler, post={}))
- else:
- print(voffset)
- f.seek(cpos)
- def default_reader(f, ifd):
- #print(endian+ifd['pycode']*ifd['count'])
- is_offset = ifd['bytesize'] > 4 or ifd['bytesize'] <= 0
- is_padding = ifd['bytesize']<4
- if is_offset:
- offset = read_offset(f)
- current = f.tell()
- f.seek(relative_offset+offset)
- if not is_offset and is_padding:
- remaining = 4-ifd['bytesize']
- try:
- content = unpack(endian+ifd['pycode']*ifd['count']+'x'*remaining, f.read(4))
- except:
- print(remaining, endian+ifd['pycode']+'x'*remaining)
- exit(1)
- else:
- content = unpack(endian+ifd['pycode']*ifd['count'], f.read(ifd['bytesize']))
- if is_offset:
- f.seek(current)
- return content
- def read_byte(f, n=1):
- if n==1:
- return unpack(endian+'c', f.read(1))[0]
- else:
- return unpack(endian+'c'*n, f.read(n))
- def read_short(f, n=1):
- if n==1:
- return unpack(endian+'H', f.read(2))[0]
- else:
- return unpack(endian+'H'*n, f.read(2*n))
- def read_int(f, n=1):
- if n==1:
- return unpack(endian+'I', f.read(4))[0]
- else:
- return unpack(endian+'I'*n, f.read(4*n))
- def read_string(f, encoding="ascii"):
- tmp = []
- scode = 'c'
- while True:
- try:
- cc = unpack(scode, f.read(calcsize(scode)))[0]
- if(cc== b'\x00'):
- break
- except:
- break
- tmp.append(cc.decode(encoding))
- content = repr("".join(tmp))
- return content
- def string_reader(f, ifd):
- # 2
- offset = read_offset(f)
- current = f.tell()
- f.seek(relative_offset+offset)
- content = read_string(f)
- f.seek(current)
- return content
- def skip_posthandler(content):
- print("SKIPPED")
- pass
- def photointerp_posthandler(content):
- sftype = content[0]
- if sftype == 0: type_text = "White is zero"
- elif sftype == 1: type_text = "Black is zero"
- elif sftype == 2: type_text = "RGB"
- elif sftype == 3: type_text = "RGB Palette"
- elif sftype == 4: type_text = "Transparency mask"
- elif sftype == 5: type_text = "CMYK"
- elif sftype == 6: type_text = "YCbCr"
- elif sftype == 8: type_text = "CIE L*a*b*"
- elif sftype == 9: type_text = "ICC L*a*b*"
- elif sftype == 10: type_text = "ITU L*a*b*"
- elif sftype == 32803: type_text = "Color Filter Array"
- elif sftype == 34892: type_text = "Linear Raw"
- else: type_text = "unknown"
- print ("{}({})".format(sftype, type_text))
- def subfiletype_posthandler(content):
- sftype = content[0]
- if sftype == 0: type_text = "Full resolution image"
- elif sftype == 1: type_text = "Reduced resolution image"
- elif sftype == 2: type_text = "one page of multi-page"
- elif sftype == 3: type_text = "one page of multi-page reduced"
- elif sftype == 4: type_text = "transparency mask"
- elif sftype == 5: type_text = "transparency mask of reduced resolution image"
- elif sftype == 6: type_text = "transparency mask of multi-page image"
- elif sftype == 7: type_text = "transparency mask of multi-page reduced"
- else: type_text = "unknown"
- print ("{}({})".format(sftype, type_text))
- def samplefmt_posthandler(content):
- sftype = content[0]
- if sftype == 1: type_text = "Unsigned"
- elif sftype == 2: type_text = "Signed"
- elif sftype == 3: type_text = "Float"
- elif sftype == 4: type_text = "Undefined"
- elif sftype == 5: type_text = "Complex int"
- elif sftype == 6: type_text = "Complex float"
- else: type_text = "unknown"
- print ("{}({})".format(sftype, type_text))
- def compression_posthandler(content):
- ctype = content[0]
- label = "unknown compression type"
- # 1=None
- # 2=CCITT modified Huffman RLE
- # 3=CCITT Group 3 fax encoding (T.4)
- # 4=CCITT Group 4 fax encoding (T.6)
- # 5=LZW
- # 6=JPEG
- # 7=JPEG DCT
- # 9=TIFF/FX T.85 JBIG
- # 10=TIFF/FX T.43 Colour by layered JBIG
- # 32766=NeXT 2-bit RLE
- # 32767=Sony ARW
- # 32769=Packed RAW
- # 32771=#1 w/ word alignment (CCITRLEW)
- # 32773=Macintosh RLE (packbits)
- # 32809=Thunderscan RLE
- # 32946=Deflate
- # 8=Adobe Deflate
- # 34712=JPEG2000
- # 34925=LZMA2
- if ctype == 1: label = "Uncompressed"
- elif ctype == 5: label = "LZW"
- elif ctype in [6,7,99]: label = "JPEG"
- elif ctype == 32767: label = "Sony ARW"
- elif ctype == 32769: label = "Packed RAW"
- elif ctype == 32770: label = "Samsung SRW"
- elif ctype == 34713: label = "Nikon NEF"
- print("{}({})".format(ctype, label))
- def planarconfig_posthandler(content):
- ctype = content[0]
- if ctype == 0: label = "Contiguous"
- elif ctype == 1: label = "Planar"
- print("{}({})".format(ctype, label))
- def resunit_posthandler(content):
- ctype = content[0]
- label = "None"
- # 1=None
- if ctype == 2:
- label = "inch"
- elif ctype == 3:
- label = "centimeter"
- print("{}({})".format(ctype, label))
- def read_IFD_entry(f, handler):
- # Bytes 0-1 The Tag that identifies the field
- # Bytes 2-3 The field Type
- # field Tag, field Type
- tag, vtype = unpack(endian+'HH', f.read(4))
- if tag in handler['td']:
- #known Tag type
- print("{} ({})".format(tag, handler['td'][tag]))
- else:
- print("{} (Unknown Tag)".format(tag))
- # Bytes 4-7 The number of values, 'Count' of the indicated type
- vcount = unpack(endian+'i', f.read(4))[0]
- print("value count: {}\tType:{}({})".format(vcount, vtype, fieldtype_label[vtype]))
- if vtype==0:
- print("Unknown data type '{}', skipping".format(vtype))
- return
- # estimate data size, if datasize > 4-byte then the value contains offset instead of value
- tsize,tcode = vtypes[vtype]
- datasize = tsize*vcount
- ifd_entry_info = dict(tag=tag, type=vtype, count=vcount, pycode=tcode, elemsize=tsize, bytesize=datasize)
- # tag require special bytestream reader
- if tag in handler['tag']:
- return handler['tag'][tag](f, ifd_entry_info)
- if vtype in handler['type']:
- # bytestream extraction of type requires preprocessing or padding
- content = handler['type'][vtype](f, ifd_entry_info)
- else:
- content = default_reader(f, ifd_entry_info)
- if tag in handler['post']:
- handler['post'][tag](content)
- else:
- if(vcount)<MAX_PRINT:
- print("value: ", content)
- else:
- print("excerpt: ", content[:MAX_EXCERPT], '...')
- def read_IFD(f, entry_fn=read_IFD_entry, entry_handler=dict(td=tagdict, tag=tag_handler, type=type_handler, post=type_posthandler)):
- print("--------------------------BEGIN IFD")
- print(f.tell())
- # num IFD
- num_ifd = unpack(endian+'H', f.read(2))[0]
- if num_ifd==0: print("0 IFD entries")
- for i in range(num_ifd):
- #IFD entries
- print("\nIFD #{} of {}\toffset:{}".format(i+1, num_ifd, f.tell()))
- entry_fn(f, entry_handler)
- if pause:
- print()
- inp = raw_input("press <return> to continue >")
- if inp.lower() in ['q', 'quit', 'exit']:
- return False
- tmp = unpack(endian+'I', f.read(4))[0]
- print("--------------------------END IFD. Next : ", tmp)
- if tmp != 0:
- f.seek(relative_offset+tmp)
- return tmp!=0
- def read_byteorder(f):
- tmp = "".join([b.decode("utf-8") for b in unpack("cc", f.read(2))])
- if tmp=="II":
- _endian = "<"
- print("little-endian")
- else:
- _endian = ">"
- print("big-endian")
- return _endian
- def init_handlers():
- """ populate default handlers """
- #data type-specific reader
- type_handler[2] = string_reader
- #tag-specific
- tag_handler[330] = subifd_reader
- tag_handler[34665] = exif_reader
- tag_handler[34853] = gpsinfo_reader
- tag_handler[34893] = subifd_reader
- tag_handler[37500] = makernote_reader
- #existing data types, but requires post-processing
- type_posthandler[254] = subfiletype_posthandler
- type_posthandler[259] = compression_posthandler
- type_posthandler[262] = photointerp_posthandler
- type_posthandler[284] = planarconfig_posthandler
- type_posthandler[296] = resunit_posthandler
- type_posthandler[324] = skip_posthandler
- type_posthandler[325] = skip_posthandler
- type_posthandler[339] = samplefmt_posthandler
- #type_posthandler[34735] = geokey_posthandler
- if __name__=="__main__":
- parser = argparse.ArgumentParser()
- parser.add_argument('inputfile', nargs=1)
- parser.add_argument('-pause', help='pause on each display of IFD entry', action='store_true', default=False)
- parser.add_argument('-skipexif', help='do not read exif sub IFD', action='store_true', default=False)
- parser.add_argument('-maxlen', help='maximum length of array element to display', type=int, default=MAX_PRINT)
- parser.add_argument('-exlen', help='maximum length of displayed excerpt of array elements', type=int, default=MAX_EXCERPT)
- args = parser.parse_args()
- if not path.exists(args.inputfile[0]):
- exit("file not found")
- pause = args.pause
- skipexif = args.skipexif
- MAX_PRINT = args.maxlen
- MAX_EXCERPT = args.exlen
- filename = args.inputfile[0]
- base, ext = path.splitext(filename)
- init_handlers()
- with open(filename, "rb") as f:
- current = f.tell()
- f.seek(0, os.SEEK_END)
- fsize = f.tell()
- print('file size: ', fsize)
- f.seek(current)
- #
- # Byte 0-1 endianness
- #
- endian = read_byteorder(f)
- #
- # Byte 2-3 TIFF Identifier
- #
- tmp = unpack(endian+'H', f.read(2))[0]
- print("TIFF Identifier: 42 = ", tmp)
- assert(tmp==42)
- # offset of the first IFD, usually 0
- tmp = unpack(endian+'I', f.read(4))[0]
- print("Offset of 1st IFD : {} bytes\ncur. offset: {}".format(tmp, f.tell()))
- if(tmp != 0 and tmp != f.tell() and tmp<fsize):
- f.seek(tmp)
- while read_IFD(f):
- pass
Add Comment
Please, Sign In to add comment