def _getexif(self): # Extract EXIF information. This method is highly experimental, # and is likely to be replaced with something better in a future # version. from PIL import TiffImagePlugin import io def fixup(value): if len(value) == 1: return value[0] return value # The EXIF record consists of a TIFF file embedded in a JPEG # application marker (!). try: data = self.info["exif"] except KeyError: return None file = io.BytesIO(data[6:]) head = file.read(8) exif = {} # process dictionary info = TiffImagePlugin.ImageFileDirectory(head) info.load(file) for key, value in info.items(): exif[key] = fixup(value) # get exif extension try: file.seek(exif[0x8769]) except KeyError: pass else: info = TiffImagePlugin.ImageFileDirectory(head) info.load(file) for key, value in info.items(): exif[key] = fixup(value) # get gpsinfo extension try: file.seek(exif[0x8825]) except KeyError: pass else: info = TiffImagePlugin.ImageFileDirectory(head) info.load(file) exif[0x8825] = gps = {} for key, value in info.items(): gps[key] = fixup(value) return exif
def logSpect2Tiff(outimg, fname, scaleinfo=None): """ Single channel spectrogram to tiff file normed to [0,1] """ info = TiffImagePlugin.ImageFileDirectory() scale = 80. shift = np.amax(outimg) if (scaleinfo == None): info[270] = str(scale) + ',' + str(shift) else: info[270] = scaleinfo #shift to put max at 0 shiftimg = outimg - shift #scale to map [-80, 0] to [-1,0] outimg = [x / scale for x in outimg] #shift to [0,1] outimg = [x + 1. for x in outimg] #clip anything below 0 (anything originally below -80dB) outimg = np.maximum(outimg, 0) # clip below 0 #print('logSpect2Tiff: writing image with min ' + str(np.amin(outimg)) + ', and max ' + str(np.amax(outimg))) savimg = Image.fromarray(np.flipud(outimg)) savimg.save(fname, tiffinfo=info) return info[270] # just in case you want it for some reason
def test_rt_metadata(self): """ Test writing arbitrary metadata into the tiff image directory Use case is ImageJ private tags, one numeric, one arbitrary data. https://github.com/python-pillow/Pillow/issues/291 """ img = hopper() textdata = "This is some arbitrary metadata for a text field" floatdata = 12.345 doubledata = 67.89 info = TiffImagePlugin.ImageFileDirectory() info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) info[tag_ids['ImageJMetaData']] = textdata info[tag_ids['RollAngle']] = floatdata info.tagtype[tag_ids['RollAngle']] = 11 info[tag_ids['YawAngle']] = doubledata info.tagtype[tag_ids['YawAngle']] = 12 f = self.tempfile("temp.tif") img.save(f, tiffinfo=info) loaded = Image.open(f) self.assertEqual(loaded.tag[50838], (len(textdata), )) self.assertEqual(loaded.tag[50839], textdata) self.assertAlmostEqual(loaded.tag[tag_ids['RollAngle']][0], floatdata, places=5) self.assertAlmostEqual(loaded.tag[tag_ids['YawAngle']][0], doubledata)
def logSpect2Tiff(outimg, fname, lwinfo=None): """ Single channel spectrogram to tiff file normed to [0,1] """ info = TiffImagePlugin.ImageFileDirectory() scale = 80. shift = float(np.amax(outimg)) lwinfo = lwinfo or {} lwinfo["scale"] = scale lwinfo["shift"] = shift #just chose a tag index that appears to be unused: https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml info[666] = json.dumps(lwinfo) #shift to put max at 0 shiftimg = outimg - shift #scale to map [-80, 0] to [-1,0] outimg = [x / scale for x in outimg] #shift to [0,1] outimg = [x + 1. for x in outimg] #clip anything below 0 (anything originally below -80dB) outimg = np.maximum(outimg, 0) # clip below 0 #print('logSpect2Tiff: writing image with min ' + str(np.amin(outimg)) + ', and max ' + str(np.amax(outimg))) savimg = Image.fromarray(np.flipud(outimg)) savimg.save(fname, tiffinfo=info) return info[666] # just in case you want it for some reason
def test_empty_metadata(self): f = io.BytesIO(b'II*\x00\x08\x00\x00\x00') head = f.read(8) info = TiffImagePlugin.ImageFileDirectory(head) try: self.assert_warning(UserWarning, lambda: info.load(f)) except struct.error: self.fail("Should not be struct errors there.")
def test_rt_metadata(self): """ Test writing arbitrary metadata into the tiff image directory Use case is ImageJ private tags, one numeric, one arbitrary data. https://github.com/python-pillow/Pillow/issues/291 """ img = hopper() # Behaviour change: re #1416 # Pre ifd rewrite, ImageJMetaData was being written as a string(2), # Post ifd rewrite, it's defined as arbitrary bytes(7). It should # roundtrip with the actual bytes, rather than stripped text # of the premerge tests. # # For text items, we still have to decode('ascii','replace') because # the tiff file format can't take 8 bit bytes in that field. basetextdata = "This is some arbitrary metadata for a text field" bindata = basetextdata.encode('ascii') + b" \xff" textdata = basetextdata + " " + chr(255) reloaded_textdata = basetextdata + " ?" floatdata = 12.345 doubledata = 67.89 info = TiffImagePlugin.ImageFileDirectory() ImageJMetaData = tag_ids['ImageJMetaData'] ImageJMetaDataByteCounts = tag_ids['ImageJMetaDataByteCounts'] ImageDescription = tag_ids['ImageDescription'] info[ImageJMetaDataByteCounts] = len(bindata) info[ImageJMetaData] = bindata info[tag_ids['RollAngle']] = floatdata info.tagtype[tag_ids['RollAngle']] = 11 info[tag_ids['YawAngle']] = doubledata info.tagtype[tag_ids['YawAngle']] = 12 info[ImageDescription] = textdata f = self.tempfile("temp.tif") img.save(f, tiffinfo=info) loaded = Image.open(f) self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata), )) self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], len(bindata)) self.assertEqual(loaded.tag[ImageJMetaData], bindata) self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata) self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata, )) self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata) loaded_float = loaded.tag[tag_ids['RollAngle']][0] self.assertAlmostEqual(loaded_float, floatdata, places=5) loaded_double = loaded.tag[tag_ids['YawAngle']][0] self.assertAlmostEqual(loaded_double, doubledata)
def add_comment(filename, comment): tags = TiffImagePlugin.ImageFileDirectory() tags[270] = comment tags.tagtype['Description'] = 2 Image.DEBUG = False TiffImagePlugin.WRITE_LIBTIFF = True return tags
def test_load_float(self): # Arrange ifd = TiffImagePlugin.ImageFileDirectory() data = b"abcdabcd" # Act ret = ifd.load_float(data) # Assert self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22))
def test_load_double(self): # Arrange ifd = TiffImagePlugin.ImageFileDirectory() data = b"abcdefghabcdefgh" # Act ret = ifd.load_double(data) # Assert self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194))
def test_load_string(self): # Arrange ifd = TiffImagePlugin.ImageFileDirectory() data = b"abc\0" # Act ret = ifd.load_string(data) # Assert self.assertEqual(ret, "abc")
def test_load_byte(self): # Arrange from PIL import TiffImagePlugin ifd = TiffImagePlugin.ImageFileDirectory() data = b"abc" # Act ret = ifd.load_byte(data) # Assert self.assertEqual(ret, b"abc")
def __generate_master__(self): """ create a master file from the original already in the package and set all metadata """ # open original # capture existing ICC profile (if there is one) # if no ICC profile, assign sRGB # if ICC profile and != sRGB, convert to sRGB # save as uncompressed TIFF logger = logging.getLogger(sys._getframe().f_code.co_name) profile_srgb2 = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'icc', 'sRGB_IEC61966-2-1_black_scaled.icc') profile_srgb4 = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'icc', 'sRGB_v4_ICC_preference.icc') original_path = os.path.join(self.path, self.original) original_image = Image.open(original_path) try: raw_profile = original_image.info['icc_profile'] except KeyError: raw_profile = getOpenProfile(profile_srgb2).tobytes() logger.warning( '{original} does not have an internal ICC color profile'. format(original=self.original)) else: logger.debug( 'detected internal ICC color profile in {original}'.format( original=self.original)) original_profile = getOpenProfile(BytesIO(raw_profile)) original_profile_name = getProfileName(original_profile).strip() target_profile = getOpenProfile(profile_srgb4) target_profile_name = getProfileName(target_profile).strip() logger.debug( 'attempting to convert from "{original}" to "{target}"'.format( original=original_profile_name, target=target_profile_name)) converted_image = profileToProfile(original_image, original_profile, target_profile) master_path = os.path.join(self.path, 'master.tif') tiffinfo = TiffImagePlugin.ImageFileDirectory() tiffinfo[TiffImagePlugin.ICCPROFILE] = target_profile.tobytes() tiffinfo.tagtype[ TiffImagePlugin.ICCPROFILE] = 1 # byte according to TiffTags.TYPES converted_image.DEBUG = True converted_image.save(master_path, tiffinfo=tiffinfo) hash_master = hash_of_file(master_path) logger.debug('saved converted master image to {master}'.format( master=master_path)) self.__append_event__( 'created master.tif file at {master}'.format(master=master_path)) self.manifest.set('master.tif', hash_master)
def test_undefined_zero(tmp_path): # Check that the tag has not been changed since this test was created tag = TiffTags.TAGS_V2[45059] assert tag.type == TiffTags.UNDEFINED assert tag.length == 0 info = TiffImagePlugin.ImageFileDirectory(b"II*\x00\x08\x00\x00\x00") info[45059] = b"test" # Assert that the tag value does not change by setting it to itself original = info[45059] info[45059] = info[45059] assert info[45059] == original
def scale_img(fname, basewidth, baseheight=None, info=None): """ rescale images according to specified basewidth in terms of pixels. Maintains aspect ratio. """ if (info == None): info = TiffImagePlugin.ImageFileDirectory() img = Image.open(fname) if (baseheight == None): wpercent = (basewidth / float(img.size[0])) hsize = int((float(img.size[1]) * float(wpercent))) img = img.resize((basewidth, hsize), Image.ANTIALIAS) else: img = img.resize((basewidth, baseheight), Image.ANTIALIAS) img.save(fname, tiffinfo=info) return img.size
def test_rt_metadata(self): """ Test writing arbitray metadata into the tiff image directory Use case is ImageJ private tags, one numeric, one arbitrary data. https://github.com/python-pillow/Pillow/issues/291 """ img = lena() textdata = "This is some arbitrary metadata for a text field" info = TiffImagePlugin.ImageFileDirectory() info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) info[tag_ids['ImageJMetaData']] = textdata f = self.tempfile("temp.tif") img.save(f, tiffinfo=info) loaded = Image.open(f) self.assertEqual(loaded.tag[50838], (len(textdata),)) self.assertEqual(loaded.tag[50839], textdata)
def save_image(image, fname, scaleinfo=None): print('save_image: shape is ' + str(image.shape)) if (height==1) : # orientation is freq bins in channels print('saving image in channel orientation') image = np.transpose(image, [2,1,0])[:,:,0] else : print('saving image in image orientation') image = image[:,:,0] print('AFTER reshaping, save_image: shape is ' + str(image.shape)) print('image max is ' + str(np.amax(image) )) print('image min is ' + str(np.amin(image) )) # Output should add back the mean pixels we subtracted at the beginning # [0,80db] -> [0, 255] # after style transfer, images range outside of [0,255]. # To preserve scale, and mask low values, we shift by (255-max), then clip at 0 and then have all bins in the top 80dB. image = np.clip(image-np.amax(image)+255, 0, 255).astype('uint8') info = TiffImagePlugin.ImageFileDirectory() if (scaleinfo == None) : info[270] = '80, 0' else : info[270] = scaleinfo #scipy.misc.imsave(path, image) bwarray=np.asarray(image)/255. savimg = Image.fromarray(np.float64(bwarray)) #============================== savimg.save(fname, tiffinfo=info) #print('RGB2TiffGray : tiffinfo is ' + str(info)) return info[270] # just in case you want it for some reason
def test_empty_metadata(self): f = io.BytesIO(b'II*\x00\x08\x00\x00\x00') head = f.read(8) info = TiffImagePlugin.ImageFileDirectory(head) # Should not raise struct.error. self.assert_warning(UserWarning, info.load, f)
def _getmp(self): # Extract MP information. This method was inspired by the "highly # experimental" _getexif version that's been in use for years now, # itself based on the ImageFileDirectory class in the TIFF plug-in. # The MP record essentially consists of a TIFF file embedded in a JPEG # application marker. try: data = self.info["mp"] except KeyError: return None file_contents = io.BytesIO(data) head = file_contents.read(8) endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<' mp = {} # process dictionary info = TiffImagePlugin.ImageFileDirectory(head) info.load(file_contents) for key, value in info.items(): mp[key] = _fixup(value) # it's an error not to have a number of images try: quant = mp[0xB001] except KeyError: raise SyntaxError("malformed MP Index (no number of images)") # get MP entries try: mpentries = [] for entrynum in range(0, quant): rawmpentry = mp[0xB002][entrynum * 16:(entrynum + 1) * 16] unpackedentry = unpack('{0}LLLHH'.format(endianness), rawmpentry) labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', 'EntryNo2') mpentry = dict(zip(labels, unpackedentry)) mpentryattr = { 'DependentParentImageFlag': bool(mpentry['Attribute'] & (1 << 31)), 'DependentChildImageFlag': bool(mpentry['Attribute'] & (1 << 30)), 'RepresentativeImageFlag': bool(mpentry['Attribute'] & (1 << 29)), 'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27, 'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24, 'MPType': mpentry['Attribute'] & 0x00FFFFFF } if mpentryattr['ImageDataFormat'] == 0: mpentryattr['ImageDataFormat'] = 'JPEG' else: raise SyntaxError("unsupported picture format in MPO") mptypemap = { 0x000000: 'Undefined', 0x010001: 'Large Thumbnail (VGA Equivalent)', 0x010002: 'Large Thumbnail (Full HD Equivalent)', 0x020001: 'Multi-Frame Image (Panorama)', 0x020002: 'Multi-Frame Image: (Disparity)', 0x020003: 'Multi-Frame Image: (Multi-Angle)', 0x030000: 'Baseline MP Primary Image' } mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'], 'Unknown') mpentry['Attribute'] = mpentryattr mpentries.append(mpentry) mp[0xB002] = mpentries except KeyError: raise SyntaxError("malformed MP Index (bad MP Entry)") # Next we should try and parse the individual image unique ID list; # we don't because I've never seen this actually used in a real MPO # file and so can't test it. return mp
def test_empty_metadata(): f = io.BytesIO(b"II*\x00\x08\x00\x00\x00") head = f.read(8) info = TiffImagePlugin.ImageFileDirectory(head) # Should not raise struct.error. pytest.warns(UserWarning, info.load, f)
def test_rt_metadata(tmp_path): """ Test writing arbitrary metadata into the tiff image directory Use case is ImageJ private tags, one numeric, one arbitrary data. https://github.com/python-pillow/Pillow/issues/291 """ img = hopper() # Behaviour change: re #1416 # Pre ifd rewrite, ImageJMetaData was being written as a string(2), # Post ifd rewrite, it's defined as arbitrary bytes(7). It should # roundtrip with the actual bytes, rather than stripped text # of the premerge tests. # # For text items, we still have to decode('ascii','replace') because # the tiff file format can't take 8 bit bytes in that field. basetextdata = "This is some arbitrary metadata for a text field" bindata = basetextdata.encode("ascii") + b" \xff" textdata = basetextdata + " " + chr(255) reloaded_textdata = basetextdata + " ?" floatdata = 12.345 doubledata = 67.89 info = TiffImagePlugin.ImageFileDirectory() ImageJMetaData = TAG_IDS["ImageJMetaData"] ImageJMetaDataByteCounts = TAG_IDS["ImageJMetaDataByteCounts"] ImageDescription = TAG_IDS["ImageDescription"] info[ImageJMetaDataByteCounts] = len(bindata) info[ImageJMetaData] = bindata info[TAG_IDS["RollAngle"]] = floatdata info.tagtype[TAG_IDS["RollAngle"]] = 11 info[TAG_IDS["YawAngle"]] = doubledata info.tagtype[TAG_IDS["YawAngle"]] = 12 info[ImageDescription] = textdata f = str(tmp_path / "temp.tif") img.save(f, tiffinfo=info) with Image.open(f) as loaded: assert loaded.tag[ImageJMetaDataByteCounts] == (len(bindata), ) assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bindata), ) assert loaded.tag[ImageJMetaData] == bindata assert loaded.tag_v2[ImageJMetaData] == bindata assert loaded.tag[ImageDescription] == (reloaded_textdata, ) assert loaded.tag_v2[ImageDescription] == reloaded_textdata loaded_float = loaded.tag[TAG_IDS["RollAngle"]][0] assert round(abs(loaded_float - floatdata), 5) == 0 loaded_double = loaded.tag[TAG_IDS["YawAngle"]][0] assert round(abs(loaded_double - doubledata), 7) == 0 # check with 2 element ImageJMetaDataByteCounts, issue #2006 info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8) img.save(f, tiffinfo=info) with Image.open(f) as loaded: assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bindata) - 8) assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bindata) - 8)