def _save(im, fp, tile, bufsize=0): """Helper to save image based on tile list :param im: Image object. :param fp: File object. :param tile: Tile list. :param bufsize: Optional buffer size """ im.load() if not hasattr(im, "encoderconfig"): im.encoderconfig = () tile.sort(key=_tilesort) # FIXME: make MAXBLOCK a configuration parameter # It would be great if we could have the encoder specify what it needs # But, it would need at least the image size in most cases. RawEncode is # a tricky case. bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c if fp == sys.stdout: fp.flush() return try: fh = fp.fileno() fp.flush() except (AttributeError, io.UnsupportedOperation): # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: fp.seek(o, 0) e.setimage(im.im, b) if e.pushes_fd: e.setfd(fp) l, s = e.encode_to_pyfd() else: while True: l, s, d = e.encode(bufsize) fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) e.cleanup() else: # slight speedup: compress to real file object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: fp.seek(o, 0) e.setimage(im.im, b) if e.pushes_fd: e.setfd(fp) l, s = e.encode_to_pyfd() else: s = e.encode_to_file(fh, bufsize) if s < 0: raise IOError("encoder error %d when writing image file" % s) e.cleanup() if hasattr(fp, "flush"): fp.flush()
def _to_bytes_generator(pil_image, min_buffer_size=65536): """ An iterator version of the :func:`PIL.Image.tobytes` method .. note:: The PIL implementation stores the data in a separate array, doubling the memory usage. This is implemented as a generator to be a more efficient way of processing the data. :param pil_image: :class:`PIL.Image` instance :param min_buffer_size: """ pil_image.load() e = Image._getencoder(pil_image.mode, 'raw', pil_image.mode) e.setimage(pil_image.im) # This encoder fails if the buffer is not large enough to hold one full line of data - see RawEncode.c buffer_size = max(min_buffer_size, pil_image.size[0] * 4) # signal is negative for errors, 1 for finished, and 0 otherwise length, signal, data = e.encode(buffer_size) while signal == 0: yield data length, signal, data = e.encode(buffer_size) if signal > 0: yield data else: raise RuntimeError("encoder error {0} in tobytes when reading image pixel data".format(signal))
def test_encode_registry(self): Image.register_encoder("MOCK", mock_encode) self.assertIn("MOCK", Image.ENCODERS) enc = Image._getencoder("RGB", "MOCK", ("args",), extra=("extra",)) self.assertIsInstance(enc, MockEncoder) self.assertEqual(enc.args, ("RGB", "args", "extra"))
def test_encode_registry(self): Image.register_encoder('MOCK', mock_encode) self.assertIn('MOCK', Image.ENCODERS) enc = Image._getencoder('RGB', 'MOCK', ('args', ), extra=('extra', )) self.assertIsInstance(enc, MockEncoder) self.assertEqual(enc.args, ('RGB', 'args', 'extra'))
def test_encode_registry(self): Image.register_encoder("MOCK", mock_encode) assert "MOCK" in Image.ENCODERS enc = Image._getencoder("RGB", "MOCK", ("args", ), extra=("extra", )) assert isinstance(enc, MockEncoder) assert enc.args == ("RGB", "args", "extra")
def test_encode_registry(self): Image.register_encoder('MOCK', mock_encode) self.assertIn('MOCK', Image.ENCODERS) enc = Image._getencoder('RGB', 'MOCK', ('args',), extra=('extra',)) self.assertIsInstance(enc, MockEncoder) self.assertEqual(enc.args, ('RGB', 'args', 'extra'))
def _save(im, fp, tile): "Helper to save image based on tile list" im.load() if not hasattr(im, "encoderconfig"): im.encoderconfig = () tile.sort(key=_tilesort) # FIXME: make MAXBLOCK a configuration parameter bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c try: fh = fp.fileno() fp.flush() except (AttributeError, io.UnsupportedOperation): # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: fp.seek(o, 0) e.setimage(im.im, b) while True: l, s, d = e.encode(bufsize) fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: # slight speedup: compress to real file object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: fp.seek(o, 0) e.setimage(im.im, b) s = e.encode_to_file(fh, bufsize) if s < 0: raise IOError("encoder error %d when writing image file" % s) try: fp.flush() except: pass
def PilImageToNp(im: Image.Image): im.load() encoder = Image._getencoder(im.mode, 'raw', im.mode) encoder.setimage(im.im) shape, typestr = Image._conv_type_shape(im) data = np.empty(shape, dtype=np.dtype(typestr)) mem = data.data.cast('B', (data.data.nbytes,)) bufsize, s, offset = 65536, 0, 0 while not s: _, s, d = encoder.encode(bufsize) mem[offset:offset + len(d)] = d offset += len(d) if s < 0: raise GraphicsException(f"Encoder error: {s}") return data
def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) compression = im.info.get('compression','raw') libtiff = compression in ["tiff_ccitt", "group3", "group4", "tiff_raw_16"] # -- multi-page -- skip TIFF header on subsequent pages if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) # PIL always starts the first IFD at offset 8 fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] # additions written by Greg Couch, [email protected] # inspired by image-sig posting from Kevin Cazabon, [email protected] if hasattr(im, 'tag'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION): if key in im.tag.tagdata: ifd[key] = im.tag.tagdata.get(key) # preserve some more tags from original TIFF image file # -- 2008-06-06 Florian Hoech ifd.tagtype = im.tag.tagtype for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag: ifd[key] = im.tag[key] # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] if "resolution" in im.encoderinfo: ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ = _cvt_res(im.encoderinfo["resolution"]) if "x resolution" in im.encoderinfo: ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) if "y resolution" in im.encoderinfo: ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) if "resolution unit" in im.encoderinfo: unit = im.encoderinfo["resolution unit"] if unit == "inch": ifd[RESOLUTION_UNIT] = 2 elif unit == "cm" or unit == "centimeter": ifd[RESOLUTION_UNIT] = 3 else: ifd[RESOLUTION_UNIT] = 1 if "software" in im.encoderinfo: ifd[SOFTWARE] = im.encoderinfo["software"] if "date time" in im.encoderinfo: ifd[DATE_TIME] = im.encoderinfo["date time"] if "artist" in im.encoderinfo: ifd[ARTIST] = im.encoderinfo["artist"] if "copyright" in im.encoderinfo: ifd[COPYRIGHT] = im.encoderinfo["copyright"] dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 ifd[X_RESOLUTION] = _cvt_res(dpi[0]) ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) if bits != (1,): ifd[BITSPERSAMPLE] = bits if len(bits) != 1: ifd[SAMPLESPERPIXEL] = len(bits) if extra is not None: ifd[EXTRASAMPLES] = extra if format != 1: ifd[SAMPLEFORMAT] = format ifd[PHOTOMETRIC_INTERPRETATION] = photo if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0]*bits[0]+7)//8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression,1) # no compression by default if libtiff: if Image.DEBUG: print ("Saving using libtiff encoder") print (ifd.items()) _fp = 0 if hasattr(fp, "fileno"): fp.seek(0) _fp = os.dup(fp.fileno()) blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes. atts = dict([(k,v) for (k,(v,)) in ifd.items() if k not in blocklist]) try: # pull in more bits from the original file, e.g x,y resolution # so that we can save(load('')) == original file. for k,v in im.ifd.items(): if k not in atts and k not in blocklist: if type(v[0]) == tuple and len(v) > 1: # A tuple of more than one rational tuples # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = [float(elt[0])/float(elt[1]) for elt in v] continue if type(v[0]) == tuple and len(v) == 1: # A tuple of one rational tuples # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0][0])/float(v[0][1]) continue if type(v) == tuple and len(v) == 1: # int or similar atts[k] = v[0] continue if type(v) == str: atts[k] = v continue except: # if we don't have an ifd here, just punt. pass if Image.DEBUG: print (atts) a = (rawmode, compression, _fp, filename, atts) e = Image._getencoder(im.mode, compression, a, im.encoderconfig) e.setimage(im.im, (0,0)+im.size) while 1: l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock if not _fp: fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) ImageFile._save(im, fp, [ ("raw", (0,0)+im.size, offset, (rawmode, stride, 1)) ]) # -- helper for multi-page save -- if "_debug_multipage" in im.encoderinfo: #just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd
def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) compression = im.encoderinfo.get('compression', im.info.get('compression', 'raw')) libtiff = WRITE_LIBTIFF or compression != 'raw' # required for color libtiff images ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) # -- multi-page -- skip TIFF header on subsequent pages if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) # PIL always starts the first IFD at offset 8 fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo", {}) if Image.DEBUG: print("Tiffinfo Keys: %s" % info.keys) keys = list(info.keys()) for key in keys: ifd[key] = info.get(key) try: ifd.tagtype[key] = info.tagtype[key] except: pass # might not be an IFD, Might not have populated type # additions written by Greg Couch, [email protected] # inspired by image-sig posting from Kevin Cazabon, [email protected] if hasattr(im, 'tag'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag: ifd[key] = im.tag[key] ifd.tagtype[key] = im.tag.tagtype.get(key, None) # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] for key, name, cvt in [ (IMAGEDESCRIPTION, "description", lambda x: x), (X_RESOLUTION, "resolution", _cvt_res), (Y_RESOLUTION, "resolution", _cvt_res), (X_RESOLUTION, "x_resolution", _cvt_res), (Y_RESOLUTION, "y_resolution", _cvt_res), (RESOLUTION_UNIT, "resolution_unit", lambda x: {"inch": 2, "cm": 3, "centimeter": 3}.get(x, 1)), (SOFTWARE, "software", lambda x: x), (DATE_TIME, "date_time", lambda x: x), (ARTIST, "artist", lambda x: x), (COPYRIGHT, "copyright", lambda x: x)]: name_with_spaces = name.replace("_", " ") if "_" in name and name_with_spaces in im.encoderinfo: warnings.warn("%r is deprecated; use %r instead" % (name_with_spaces, name), DeprecationWarning) ifd[key] = cvt(im.encoderinfo[name.replace("_", " ")]) if name in im.encoderinfo: ifd[key] = cvt(im.encoderinfo[name]) dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 ifd[X_RESOLUTION] = _cvt_res(dpi[0]) ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) if bits != (1,): ifd[BITSPERSAMPLE] = bits if len(bits) != 1: ifd[SAMPLESPERPIXEL] = len(bits) if extra is not None: ifd[EXTRASAMPLES] = extra if format != 1: ifd[SAMPLEFORMAT] = format ifd[PHOTOMETRIC_INTERPRETATION] = photo if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0]*bits[0]+7)//8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer # no compression by default: ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) if libtiff: if Image.DEBUG: print("Saving using libtiff encoder") print(ifd.items()) _fp = 0 if hasattr(fp, "fileno"): try: fp.seek(0) _fp = os.dup(fp.fileno()) except io.UnsupportedOperation: pass # ICC Profile crashes. blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. for k, v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()): if k not in atts and k not in blocklist: if type(v[0]) == tuple and len(v) > 1: # A tuple of more than one rational tuples # flatten to floats, # following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = [float(elt[0])/float(elt[1]) for elt in v] continue if type(v[0]) == tuple and len(v) == 1: # A tuple of one rational tuples # flatten to floats, # following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0][0])/float(v[0][1]) continue if (type(v) == tuple and (len(v) > 2 or (len(v) == 2 and v[1] == 0))): # List of ints? # Avoid divide by zero in next if-clause if type(v[0]) in (int, float): atts[k] = list(v) continue if type(v) == tuple and len(v) == 2: # one rational tuple # flatten to float, # following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0])/float(v[1]) continue if type(v) == tuple and len(v) == 1: v = v[0] # drop through if isStringType(v): atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" continue else: # int or similar atts[k] = v if Image.DEBUG: print(atts) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode # contains I;16, we need to convert from native to image # byte order. if im.mode in ('I;16B', 'I;16'): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) # print (im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) e.setimage(im.im, (0, 0)+im.size) while True: # undone, change to self.decodermaxblock: l, s, d = e.encode(16*1024) if not _fp: fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) ImageFile._save(im, fp, [ ("raw", (0, 0)+im.size, offset, (rawmode, stride, 1)) ]) # -- helper for multi-page save -- if "_debug_multipage" in im.encoderinfo: # just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd
def test_encode_registry_fail(self): with pytest.raises(OSError): Image._getencoder("RGB", "DoesNotExist", ("args", ), extra=("extra", ))
def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory_v2(prefix=prefix) compression = im.encoderinfo.get('compression', im.info.get('compression', 'raw')) libtiff = WRITE_LIBTIFF or compression != 'raw' # required for color libtiff images ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo", {}) if DEBUG: print("Tiffinfo Keys: %s" % list(info)) if isinstance(info, ImageFileDirectory_v1): info = info.to_v2() for key in info: ifd[key] = info.get(key) try: ifd.tagtype[key] = info.tagtype[key] except: pass # might not be an IFD, Might not have populated type # additions written by Greg Couch, [email protected] # inspired by image-sig posting from Kevin Cazabon, [email protected] if hasattr(im, 'tag_v2'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag_v2: ifd[key] = im.tag_v2[key] ifd.tagtype[key] = im.tag_v2.tagtype.get(key, None) # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] for key, name in [(IMAGEDESCRIPTION, "description"), (X_RESOLUTION, "resolution"), (Y_RESOLUTION, "resolution"), (X_RESOLUTION, "x_resolution"), (Y_RESOLUTION, "y_resolution"), (RESOLUTION_UNIT, "resolution_unit"), (SOFTWARE, "software"), (DATE_TIME, "date_time"), (ARTIST, "artist"), (COPYRIGHT, "copyright")]: name_with_spaces = name.replace("_", " ") if "_" in name and name_with_spaces in im.encoderinfo: warnings.warn("%r is deprecated; use %r instead" % (name_with_spaces, name), DeprecationWarning) ifd[key] = im.encoderinfo[name.replace("_", " ")] if name in im.encoderinfo: ifd[key] = im.encoderinfo[name] dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 ifd[X_RESOLUTION] = dpi[0] ifd[Y_RESOLUTION] = dpi[1] if bits != (1,): ifd[BITSPERSAMPLE] = bits if len(bits) != 1: ifd[SAMPLESPERPIXEL] = len(bits) if extra is not None: ifd[EXTRASAMPLES] = extra if format != 1: ifd[SAMPLEFORMAT] = format ifd[PHOTOMETRIC_INTERPRETATION] = photo if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0]*bits[0]+7)//8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer # no compression by default: ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) if libtiff: if DEBUG: print("Saving using libtiff encoder") print("Items: %s" % sorted(ifd.items())) _fp = 0 if hasattr(fp, "fileno"): try: fp.seek(0) _fp = os.dup(fp.fileno()) except io.UnsupportedOperation: pass # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library # based on the data in the strip. # ICCPROFILE crashes. blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ICCPROFILE] atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. legacy_ifd = {} if hasattr(im, 'tag'): legacy_ifd = im.tag.to_v2() for k, v in itertools.chain(ifd.items(), getattr(im, 'tag_v2', {}).items(), legacy_ifd.items()): if k not in atts and k not in blocklist: if isinstance(v, unicode if bytes is str else str): atts[k] = v.encode('ascii', 'replace') + b"\0" else: atts[k] = v if DEBUG: print("Converted items: %s" % sorted(atts.items())) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode # contains I;16, we need to convert from native to image # byte order. if im.mode in ('I;16B', 'I;16'): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) # print(im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) e.setimage(im.im, (0, 0)+im.size) while True: # undone, change to self.decodermaxblock: l, s, d = e.encode(16*1024) if not _fp: fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) ImageFile._save(im, fp, [ ("raw", (0, 0)+im.size, offset, (rawmode, stride, 1)) ]) # -- helper for multi-page save -- if "_debug_multipage" in im.encoderinfo: # just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd
if hasattr(img, '_getexif'): exifinfo = img._getexif() if None != exifinfo: for tag, value in exifinfo.items(): decoded = TAGS.get(tag, tag) print decoded filein = open("org.jpg","rb") da = exifread.process_file(filein, debug=True) f2 = open("org2.jpg","rb") da2 = exifread.process_file(f2) filein.close() f2.close() exif_dict2 = piexif.load("org.jpg") im = Image.open("org.jpg") exif_dict = piexif.load(im.info["exif"]) w,h = im.size print exif_dict["0th"] exif_dict["0th"][piexif.ImageIFD.XResolution] = (w,1) exif_dict["0th"][piexif.ImageIFD.YResolution] = (h,1) exif_dict["Exif"][piexif.ExifIFD.UserComment] = "wyscsddgdfg" #exif_dict.update({"Image":{305:"wyscsddgdfg"}}) exif_dict["0th"][piexif.ImageIFD.Software] = "1212122wyscsddgdfg" exif_bytes = piexif.dump(exif_dict) im.app.update({"COM":"ABCDEFEG"}) im.applist.append(('COM','ADBCDEFG')) encd = Image._getencoder() im.save("oorg.jpg","jpeg",exif=exif_bytes,app=im.app, applist=im.applist,encoderinfo=im.encoderinfo) im2 = Image.open("oorg.jpg") exif_dict21 = piexif.load("oorg.jpg") print da
def test_encode_registry_fail(self): self.assertRaises( IOError, lambda: Image._getencoder( 'RGB', 'DoesNotExist', ('args', ), extra=('extra', )))
def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) compression = im.info.get('compression', 'raw') libtiff = compression in [ "tiff_ccitt", "group3", "group4", "tiff_jpeg", "tiff_adobe_deflate", "tiff_thunderscan", "tiff_deflate", "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16" ] # -- multi-page -- skip TIFF header on subsequent pages if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) # PIL always starts the first IFD at offset 8 fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] # additions written by Greg Couch, [email protected] # inspired by image-sig posting from Kevin Cazabon, [email protected] if hasattr(im, 'tag'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION): if key in im.tag.tagdata: ifd[key] = im.tag.tagdata.get(key) # preserve some more tags from original TIFF image file # -- 2008-06-06 Florian Hoech ifd.tagtype = im.tag.tagtype for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag: ifd[key] = im.tag[key] # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] if "resolution" in im.encoderinfo: ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ = _cvt_res(im.encoderinfo["resolution"]) if "x resolution" in im.encoderinfo: ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) if "y resolution" in im.encoderinfo: ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) if "resolution unit" in im.encoderinfo: unit = im.encoderinfo["resolution unit"] if unit == "inch": ifd[RESOLUTION_UNIT] = 2 elif unit == "cm" or unit == "centimeter": ifd[RESOLUTION_UNIT] = 3 else: ifd[RESOLUTION_UNIT] = 1 if "software" in im.encoderinfo: ifd[SOFTWARE] = im.encoderinfo["software"] if "date time" in im.encoderinfo: ifd[DATE_TIME] = im.encoderinfo["date time"] if "artist" in im.encoderinfo: ifd[ARTIST] = im.encoderinfo["artist"] if "copyright" in im.encoderinfo: ifd[COPYRIGHT] = im.encoderinfo["copyright"] dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 ifd[X_RESOLUTION] = _cvt_res(dpi[0]) ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) if bits != (1, ): ifd[BITSPERSAMPLE] = bits if len(bits) != 1: ifd[SAMPLESPERPIXEL] = len(bits) if extra is not None: ifd[EXTRASAMPLES] = extra if format != 1: ifd[SAMPLEFORMAT] = format ifd[PHOTOMETRIC_INTERPRETATION] = photo if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) # no compression by default if libtiff: if Image.DEBUG: print("Saving using libtiff encoder") print(ifd.items()) _fp = 0 if hasattr(fp, "fileno"): fp.seek(0) _fp = os.dup(fp.fileno()) blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes. atts = dict([(k, v) for (k, (v, )) in ifd.items() if k not in blocklist]) try: # pull in more bits from the original file, e.g x,y resolution # so that we can save(load('')) == original file. for k, v in im.ifd.items(): if k not in atts and k not in blocklist: if type(v[0]) == tuple and len(v) > 1: # A tuple of more than one rational tuples # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = [float(elt[0]) / float(elt[1]) for elt in v] continue if type(v[0]) == tuple and len(v) == 1: # A tuple of one rational tuples # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0][0]) / float(v[0][1]) continue if type(v) == tuple and len(v) == 1: # int or similar atts[k] = v[0] continue if type(v) == str: atts[k] = v continue except: # if we don't have an ifd here, just punt. pass if Image.DEBUG: print(atts) a = (rawmode, compression, _fp, filename, atts) e = Image._getencoder(im.mode, compression, a, im.encoderconfig) e.setimage(im.im, (0, 0) + im.size) while 1: l, s, d = e.encode(16 * 1024) # undone, change to self.decodermaxblock if not _fp: fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) ImageFile._save(im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))]) # -- helper for multi-page save -- if "_debug_multipage" in im.encoderinfo: #just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd
def merge_gif(fp, images, durations, loops=0): _im = images[0] _durations = [durations for im in images] if isinstance(durations, int) else durations _bits = 8 _size = _im.size try: assert len(_durations) == len(images) for im in images: im.load() assert _size == im.size except AssertionError: raise # Header _h = [ 'GIF89a', o16(_size[0]), # width o16(_size[1]), # height chr(128 + (_bits - 1)), # flags: palette + bits chr(0), # background chr(0), # reserved/aspect ] fp.write(''.join(_h)) # Palette _mode = 'P' # palette/grayscale (P/L) _colors = 256 _palette = _im.im.getpalette('RGB')[:_colors * 3] fp.write(_palette) # Ext: application extension _loop = loops _ext = [ '\x21\xFF\x0B', 'NETSCAPE2.0', '\x03\x01', o16(_loop), '\x00', ] fp.write(''.join(_ext)) # Images for idx, im in enumerate(images): # Ext: graphics control extension _cext = [ '\x21\xF9\x04', '\x08', # flag: transparency: \x08 | \x09 o16(_durations[idx]), # druation '\x00', # transparency '\x00', ] fp.write(''.join(_cext)) # Image _h = [ '\x2C', # ',' sperator o16(0), o16(0), # offset o16(im.size[0]), o16(im.size[1]), # size chr(0), # flag: no local palette chr(_bits), # color bits ] fp.write(''.join(_h)) _encoder = Image._getencoder(im.mode, "gif", im.mode) _encoder.setimage(im.im, (0, 0) + im.size) _bufsize = 4096 while True: l, s, d = _encoder.encode(_bufsize) fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) fp.write('\x00') # End fp.write('\x3b') # ';' trailer fp.flush()
def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory_v2(prefix=prefix) compression = im.encoderinfo.get('compression', im.info.get('compression', 'raw')) libtiff = WRITE_LIBTIFF or compression != 'raw' # required for color libtiff images ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo", {}) if DEBUG: print("Tiffinfo Keys: %s" % list(info)) if isinstance(info, ImageFileDirectory_v1): info = info.to_v2() for key in info: ifd[key] = info.get(key) try: ifd.tagtype[key] = info.tagtype[key] except: pass # might not be an IFD, Might not have populated type # additions written by Greg Couch, [email protected] # inspired by image-sig posting from Kevin Cazabon, [email protected] if hasattr(im, 'tag_v2'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag_v2: ifd[key] = im.tag_v2[key] ifd.tagtype[key] = im.tag_v2.tagtype.get(key, None) # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] for key, name in [(IMAGEDESCRIPTION, "description"), (X_RESOLUTION, "resolution"), (Y_RESOLUTION, "resolution"), (X_RESOLUTION, "x_resolution"), (Y_RESOLUTION, "y_resolution"), (RESOLUTION_UNIT, "resolution_unit"), (SOFTWARE, "software"), (DATE_TIME, "date_time"), (ARTIST, "artist"), (COPYRIGHT, "copyright")]: name_with_spaces = name.replace("_", " ") if "_" in name and name_with_spaces in im.encoderinfo: warnings.warn( "%r is deprecated; use %r instead" % (name_with_spaces, name), DeprecationWarning) ifd[key] = im.encoderinfo[name.replace("_", " ")] if name in im.encoderinfo: ifd[key] = im.encoderinfo[name] dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 ifd[X_RESOLUTION] = dpi[0] ifd[Y_RESOLUTION] = dpi[1] if bits != (1, ): ifd[BITSPERSAMPLE] = bits if len(bits) != 1: ifd[SAMPLESPERPIXEL] = len(bits) if extra is not None: ifd[EXTRASAMPLES] = extra if format != 1: ifd[SAMPLEFORMAT] = format ifd[PHOTOMETRIC_INTERPRETATION] = photo if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer # no compression by default: ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) if libtiff: if DEBUG: print("Saving using libtiff encoder") print("Items: %s" % sorted(ifd.items())) _fp = 0 if hasattr(fp, "fileno"): try: fp.seek(0) _fp = os.dup(fp.fileno()) except io.UnsupportedOperation: pass # ICC Profile crashes. blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. legacy_ifd = {} if hasattr(im, 'tag'): legacy_ifd = im.tag.to_v2() for k, v in itertools.chain(ifd.items(), getattr(im, 'tag_v2', {}).items(), legacy_ifd.items()): if k not in atts and k not in blocklist: if isinstance(v, unicode if bytes is str else str): atts[k] = v.encode('ascii', 'replace') + b"\0" else: atts[k] = v if DEBUG: print("Converted items: %s" % sorted(atts.items())) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode # contains I;16, we need to convert from native to image # byte order. if im.mode in ('I;16B', 'I;16'): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) # print(im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) e.setimage(im.im, (0, 0) + im.size) while True: # undone, change to self.decodermaxblock: l, s, d = e.encode(16 * 1024) if not _fp: fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) ImageFile._save(im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))]) # -- helper for multi-page save -- if "_debug_multipage" in im.encoderinfo: # just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd
def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) compression = im.encoderinfo.get('compression', im.info.get('compression', 'raw')) libtiff = WRITE_LIBTIFF or compression != 'raw' # required for color libtiff images ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) # -- multi-page -- skip TIFF header on subsequent pages if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) # PIL always starts the first IFD at offset 8 fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo", {}) if Image.DEBUG: print("Tiffinfo Keys: %s" % info.keys) keys = list(info.keys()) for key in keys: ifd[key] = info.get(key) try: ifd.tagtype[key] = info.tagtype[key] except: pass # might not be an IFD, Might not have populated type # additions written by Greg Couch, [email protected] # inspired by image-sig posting from Kevin Cazabon, [email protected] if hasattr(im, 'tag'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag: ifd[key] = im.tag[key] ifd.tagtype[key] = im.tag.tagtype.get(key, None) # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] if "resolution" in im.encoderinfo: ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ = _cvt_res(im.encoderinfo["resolution"]) if "x resolution" in im.encoderinfo: ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) if "y resolution" in im.encoderinfo: ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) if "resolution unit" in im.encoderinfo: unit = im.encoderinfo["resolution unit"] if unit == "inch": ifd[RESOLUTION_UNIT] = 2 elif unit == "cm" or unit == "centimeter": ifd[RESOLUTION_UNIT] = 3 else: ifd[RESOLUTION_UNIT] = 1 if "software" in im.encoderinfo: ifd[SOFTWARE] = im.encoderinfo["software"] if "date time" in im.encoderinfo: ifd[DATE_TIME] = im.encoderinfo["date time"] if "artist" in im.encoderinfo: ifd[ARTIST] = im.encoderinfo["artist"] if "copyright" in im.encoderinfo: ifd[COPYRIGHT] = im.encoderinfo["copyright"] dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 ifd[X_RESOLUTION] = _cvt_res(dpi[0]) ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) if bits != (1, ): ifd[BITSPERSAMPLE] = bits if len(bits) != 1: ifd[SAMPLESPERPIXEL] = len(bits) if extra is not None: ifd[EXTRASAMPLES] = extra if format != 1: ifd[SAMPLEFORMAT] = format ifd[PHOTOMETRIC_INTERPRETATION] = photo if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer # no compression by default: ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) if libtiff: if Image.DEBUG: print("Saving using libtiff encoder") print(ifd.items()) _fp = 0 if hasattr(fp, "fileno"): fp.seek(0) _fp = os.dup(fp.fileno()) # ICC Profile crashes. blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] atts = {} # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. for k, v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()): if k not in atts and k not in blocklist: if type(v[0]) == tuple and len(v) > 1: # A tuple of more than one rational tuples # flatten to floats, # following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = [float(elt[0]) / float(elt[1]) for elt in v] continue if type(v[0]) == tuple and len(v) == 1: # A tuple of one rational tuples # flatten to floats, # following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0][0]) / float(v[0][1]) continue if type(v) == tuple and len(v) > 2: # List of ints? if type(v[0]) in (int, float): atts[k] = list(v) continue if type(v) == tuple and len(v) == 2: # one rational tuple # flatten to float, # following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0]) / float(v[1]) continue if type(v) == tuple and len(v) == 1: v = v[0] # drop through if isStringType(v): atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" continue else: # int or similar atts[k] = v if Image.DEBUG: print(atts) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode # contains I;16, we need to convert from native to image # byte order. if im.mode in ('I;16B', 'I;16'): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) # print (im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) e.setimage(im.im, (0, 0) + im.size) while True: # undone, change to self.decodermaxblock: l, s, d = e.encode(16 * 1024) if not _fp: fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) ImageFile._save(im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))]) # -- helper for multi-page save -- if "_debug_multipage" in im.encoderinfo: # just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd
def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) compression = im.encoderinfo.get('compression',im.info.get('compression','raw')) libtiff = compression in ["tiff_ccitt", "group3", "group4", "tiff_jpeg", "tiff_adobe_deflate", "tiff_thunderscan", "tiff_deflate", "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"] # -- multi-page -- skip TIFF header on subsequent pages if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) # PIL always starts the first IFD at offset 8 fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo",{}) if Image.DEBUG: print ("Tiffinfo Keys: %s"% info.keys) keys = list(info.keys()) for key in keys: ifd[key] = info.get(key) try: ifd.tagtype[key] = info.tagtype[key] except: pass # might not be an IFD, Might not have populated type # additions written by Greg Couch, [email protected] # inspired by image-sig posting from Kevin Cazabon, [email protected] if hasattr(im, 'tag'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): if key in im.tag: ifd[key] = im.tag[key] ifd.tagtype[key] = im.tag.tagtype.get(key, None) # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] if "resolution" in im.encoderinfo: ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ = _cvt_res(im.encoderinfo["resolution"]) if "x resolution" in im.encoderinfo: ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) if "y resolution" in im.encoderinfo: ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) if "resolution unit" in im.encoderinfo: unit = im.encoderinfo["resolution unit"] if unit == "inch": ifd[RESOLUTION_UNIT] = 2 elif unit == "cm" or unit == "centimeter": ifd[RESOLUTION_UNIT] = 3 else: ifd[RESOLUTION_UNIT] = 1 if "software" in im.encoderinfo: ifd[SOFTWARE] = im.encoderinfo["software"] if "date time" in im.encoderinfo: ifd[DATE_TIME] = im.encoderinfo["date time"] if "artist" in im.encoderinfo: ifd[ARTIST] = im.encoderinfo["artist"] if "copyright" in im.encoderinfo: ifd[COPYRIGHT] = im.encoderinfo["copyright"] dpi = im.encoderinfo.get("dpi") if dpi: ifd[RESOLUTION_UNIT] = 2 ifd[X_RESOLUTION] = _cvt_res(dpi[0]) ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) if bits != (1,): ifd[BITSPERSAMPLE] = bits if len(bits) != 1: ifd[SAMPLESPERPIXEL] = len(bits) if extra is not None: ifd[EXTRASAMPLES] = extra if format != 1: ifd[SAMPLEFORMAT] = format ifd[PHOTOMETRIC_INTERPRETATION] = photo if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation stride = len(bits) * ((im.size[0]*bits[0]+7)//8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression,1) # no compression by default if libtiff: if Image.DEBUG: print ("Saving using libtiff encoder") print (ifd.items()) _fp = 0 if hasattr(fp, "fileno"): fp.seek(0) _fp = os.dup(fp.fileno()) blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes. atts={} # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. for k,v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()): if k not in atts and k not in blocklist: if type(v[0]) == tuple and len(v) > 1: # A tuple of more than one rational tuples # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = [float(elt[0])/float(elt[1]) for elt in v] continue if type(v[0]) == tuple and len(v) == 1: # A tuple of one rational tuples # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0][0])/float(v[0][1]) continue if type(v) == tuple and len(v) > 2: # List of ints? # BitsPerSample is one example, I get (8,8,8) # UNDONE continue if type(v) == tuple and len(v) == 2: # one rational tuple # flatten to float, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0])/float(v[1]) continue if type(v) == tuple and len(v) == 1: v = v[0] # drop through if isStringType(v): atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" continue else: # int or similar atts[k] = v if Image.DEBUG: print (atts) # libtiff always returns the bytes in native order. # we're expecting image byte order. So, if the rawmode # contains I;16, we need to convert from native to image # byte order. if im.mode in ('I;16B', 'I;16'): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) # print (im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, compression, a, im.encoderconfig) e.setimage(im.im, (0,0)+im.size) while 1: l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock if not _fp: fp.write(d) if s: break if s < 0: raise IOError("encoder error %d when writing image file" % s) else: offset = ifd.save(fp) ImageFile._save(im, fp, [ ("raw", (0,0)+im.size, offset, (rawmode, stride, 1)) ]) # -- helper for multi-page save -- if "_debug_multipage" in im.encoderinfo: #just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd