def _open(self): # header s = self.fp.read(128) if not _accept(s): raise SyntaxError("not a PCX file") # image bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1 if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: raise SyntaxError("bad PCX image size") logger.debug("BBox: %s %s %s %s", *bbox) # format version = i8(s[1]) bits = i8(s[3]) planes = i8(s[65]) stride = i16(s, 66) logger.debug("PCX version %s, bits %s, planes %s, stride %s", version, bits, planes, stride) self.info["dpi"] = i16(s, 12), i16(s, 14) if bits == 1 and planes == 1: mode = rawmode = "1" elif bits == 1 and planes in (2, 4): mode = "P" rawmode = "P;%dL" % planes self.palette = ImagePalette.raw("RGB", s[16:64]) elif version == 5 and bits == 8 and planes == 1: mode = rawmode = "L" # FIXME: hey, this doesn't work with the incremental loader !!! self.fp.seek(-769, 2) s = self.fp.read(769) if len(s) == 769 and i8(s[0]) == 12: # check if the palette is linear greyscale for i in range(256): if s[i*3+1:i*3+4] != o8(i)*3: mode = rawmode = "P" break if mode == "P": self.palette = ImagePalette.raw("RGB", s[1:]) self.fp.seek(128) elif version == 5 and bits == 8 and planes == 3: mode = "RGB" rawmode = "RGB;L" else: raise IOError("unknown PCX mode") self.mode = mode self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] bbox = (0, 0) + self.size logger.debug("size: %sx%s", *self.size) self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
def test_getcolor(): palette = ImagePalette() map = {} for i in range(256): map[palette.getcolor((i, i, i))] = i assert_equal(len(map), 256) assert_exception(ValueError, lambda: palette.getcolor((1, 2, 3)))
def test_getdata(self): # Arrange data_in = list(range(256))*3 palette = ImagePalette("RGB", data_in) # Act mode, data_out = palette.getdata() # Assert self.assertEqual(mode, "RGB;L")
def test_getcolor(self): palette = ImagePalette() test_map = {} for i in range(256): test_map[palette.getcolor((i, i, i))] = i self.assertEqual(len(test_map), 256) self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3)))
def _open(self): # Screen s = self.fp.read(13) if s[:6] not in [b"GIF87a", b"GIF89a"]: raise SyntaxError("not a GIF file") self.info["version"] = s[:6] self.size = i16(s[6:]), i16(s[8:]) self.tile = [] flags = i8(s[10]) bits = (flags & 7) + 1 if flags & 128: # get global palette self.info["background"] = i8(s[11]) # check if palette contains colour indices p = self.fp.read(3 << bits) for i in range(0, len(p), 3): if not (i // 3 == i8(p[i]) == i8(p[i + 1]) == i8(p[i + 2])): p = ImagePalette.raw("RGB", p) self.global_palette = self.palette = p break self.__fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() self.seek(0) # get ready to read first frame
def chunk_PLTE(self, offset, bytes): "PLTE -- palette data" s = self.fp.read(bytes) if self.mode == "P": self.palette = ImagePalette.raw("RGB", s) return s
def _open(self): # check magic s = self.fp.read(6) if s != "P7 332": raise SyntaxError, "not an XV thumbnail file" # Skip to beginning of next line self.fp.readline() # skip info comments while 1: s = self.fp.readline() if not s: raise SyntaxError, "Unexpected EOF reading XV thumbnail file" if s[0] != '#': break # parse header line (already read) s = string.split(s.strip()) self.mode = "P" self.size = int(s[0]), int(s[1]) self.palette = ImagePalette.raw("RGB", PALETTE) self.tile = [ ("raw", (0, 0)+self.size, self.fp.tell(), (self.mode, 0, 1) )]
def _open(self): # check magic if self.fp.read(6) != _MAGIC: raise SyntaxError("not an XV thumbnail file") # Skip to beginning of next line self.fp.readline() # skip info comments while True: s = self.fp.readline() if not s: raise SyntaxError("Unexpected EOF reading XV thumbnail file") if s[0] != b'#': break # parse header line (already read) s = s.strip().split() self.mode = "P" self.size = int(s[0:1]), int(s[1:2]) self.palette = ImagePalette.raw("RGB", PALETTE) self.tile = [ ("raw", (0, 0)+self.size, self.fp.tell(), (self.mode, 0, 1) )]
def test_file(self): palette = ImagePalette.ImagePalette("RGB", list(range(256))*3) f = self.tempfile("temp.lut") palette.save(f) p = ImagePalette.load(f) # load returns raw palette information self.assertEqual(len(p[0]), 768) self.assertEqual(p[1], "RGB") p = ImagePalette.raw(p[1], p[0]) self.assertIsInstance(p, ImagePalette.ImagePalette) self.assertEqual(p.palette, palette.tobytes())
def test_file(self): palette = ImagePalette() file = self.tempfile("temp.lut") palette.save(file) from PIL.ImagePalette import load, raw p = load(file) # load returns raw palette information self.assertEqual(len(p[0]), 768) self.assertEqual(p[1], "RGB") p = raw(p[1], p[0]) self.assertIsInstance(p, ImagePalette)
def test_file(): palette = ImagePalette() file = tempfile("temp.lut") palette.save(file) from PIL.ImagePalette import load, raw p = load(file) # load returns raw palette information assert_equal(len(p[0]), 768) assert_equal(p[1], "RGB") p = raw(p[1], p[0]) assert_true(isinstance(p, ImagePalette))
def test_rawmode_valueerrors(self): # Arrange palette = ImagePalette.raw("RGB", list(range(256))*3) # Act / Assert self.assertRaises(ValueError, palette.tobytes) self.assertRaises(ValueError, palette.getcolor, (1, 2, 3)) f = self.tempfile("temp.lut") self.assertRaises(ValueError, palette.save, f)
def test_rawmode_getdata(self): # Arrange data_in = list(range(256))*3 palette = ImagePalette.raw("RGB", data_in) # Act rawmode, data_out = palette.getdata() # Assert self.assertEqual(rawmode, "RGB") self.assertEqual(data_in, data_out)
def test_load_via_imagepalette(self): # Arrange from PIL import ImagePalette test_file = "Tests/images/gimp_gradient.ggr" # Act palette = ImagePalette.load(test_file) # Assert # load returns raw palette information self.assertEqual(len(palette[0]), 1024) self.assertEqual(palette[1], "RGBA")
def test_load_1_3_via_imagepalette(self): # Arrange from PIL import ImagePalette # GIMP 1.3 gradient files contain a name field test_file = "Tests/images/gimp_gradient_with_name.ggr" # Act palette = ImagePalette.load(test_file) # Assert # load returns raw palette information self.assertEqual(len(palette[0]), 1024) self.assertEqual(palette[1], "RGBA")
def test_sanity(self): im = Image.open(TEST_FILE) im.load() self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "MIC") # Adjust for the gamma of 2.2 encoded into the file lut = ImagePalette.make_gamma_lut(1/2.2) im = Image.merge('RGBA', [chan.point(lut) for chan in im.split()]) im2 = hopper("RGBA") self.assert_image_similar(im, im2, 10)
def test_make_linear_lut(): # Arrange black = 0 white = 255 # Act lut = ImagePalette.make_linear_lut(black, white) # Assert assert isinstance(lut, list) assert len(lut) == 256 # Check values for i in range(0, len(lut)): assert lut[i] == i
def test__new(self): im = hopper("RGB") im_p = hopper("P") blank_p = Image.new("P", (10, 10)) blank_pa = Image.new("PA", (10, 10)) blank_p.palette = None blank_pa.palette = None def _make_new(base_image, im, palette_result=None): new_im = base_image._new(im) assert new_im.mode == im.mode assert new_im.size == im.size assert new_im.info == base_image.info if palette_result is not None: assert new_im.palette.tobytes() == palette_result.tobytes() else: assert new_im.palette is None _make_new(im, im_p, im_p.palette) _make_new(im_p, im, None) _make_new(im, blank_p, ImagePalette.ImagePalette()) _make_new(im, blank_pa, ImagePalette.ImagePalette())
def _open(self): # HEAD s = self.fp.read(128) magic = i16(s[4:6]) if not (magic in [0xAF11, 0xAF12] and i16(s[14:16]) in [0, 3] and # flags s[20:22] == b"\x00\x00"): # reserved raise SyntaxError("not an FLI/FLC file") # image characteristics self.mode = "P" self.size = i16(s[8:10]), i16(s[10:12]) # animation speed duration = i32(s[16:20]) if magic == 0xAF11: duration = (duration * 1000) / 70 self.info["duration"] = duration # look for palette palette = [(a, a, a) for a in range(256)] s = self.fp.read(16) self.__offset = 128 if i16(s[4:6]) == 0xF100: # prefix chunk; ignore it self.__offset = self.__offset + i32(s) s = self.fp.read(16) if i16(s[4:6]) == 0xF1FA: # look for palette chunk s = self.fp.read(6) if i16(s[4:6]) == 11: self._palette(palette, 2) elif i16(s[4:6]) == 4: self._palette(palette, 0) palette = [o8(r)+o8(g)+o8(b) for (r, g, b) in palette] self.palette = ImagePalette.raw("RGB", b"".join(palette)) # set things up to decode first frame self.__frame = -1 self.__fp = self.fp self.__rewind = self.fp.tell() self._n_frames = None self._is_animated = None self.seek(0)
def test_make_linear_lut(self): # Arrange black = 0 white = 255 # Act lut = ImagePalette.make_linear_lut(black, white) # Assert self.assertIsInstance(lut, list) self.assertEqual(len(lut), 256) # Check values for i in range(0, len(lut)): self.assertEqual(lut[i], i)
def _open(self): # HEAD s = self.fp.read(128) magic = i16(s[4:6]) if not (magic in [0xAF11, 0xAF12] and i16(s[14:16]) in [0, 3] and # flags s[20:22] == b"\x00\x00"): # reserved raise SyntaxError("not an FLI/FLC file") # image characteristics self.mode = "P" self.size = i16(s[8:10]), i16(s[10:12]) # animation speed duration = i32(s[16:20]) if magic == 0xAF11: duration = (duration * 1000) / 70 self.info["duration"] = duration # look for palette palette = [(a, a, a) for a in range(256)] s = self.fp.read(16) self.__offset = 128 if i16(s[4:6]) == 0xF100: # prefix chunk; ignore it self.__offset = self.__offset + i32(s) s = self.fp.read(16) if i16(s[4:6]) == 0xF1FA: # look for palette chunk s = self.fp.read(6) if i16(s[4:6]) == 11: self._palette(palette, 2) elif i16(s[4:6]) == 4: self._palette(palette, 0) palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette] self.palette = ImagePalette.raw("RGB", b"".join(palette)) # set things up to decode first frame self.__frame = -1 self.__fp = self.fp self.__rewind = self.fp.tell() self._n_frames = None self._is_animated = None self.seek(0)
def test_getcolor(): palette = ImagePalette.ImagePalette() test_map = {} for i in range(256): test_map[palette.getcolor((i, i, i))] = i assert len(test_map) == 256 with pytest.raises(ValueError): palette.getcolor((1, 2, 3)) # Test unknown color specifier with pytest.raises(ValueError): palette.getcolor("unknown")
def _open(self): # Header s = self.fp.read(775) self.mode = "L" # FIXME: "P" self.size = i16(s[0:2]), i16(s[2:4]) # transparency index tindex = i16(s[5:7]) if tindex < 256: self.info["transparent"] = tindex self.palette = ImagePalette.raw("RGB", s[7:]) self.tile = [("raw", (0, 0) + self.size, 775, ("L", 0, -1))]
def show(scan, place=None): rrrgggbbb = [40, 160, 50, 50, 20, 160, 150, 100, 0, 160, 250, 150] pal = ImagePalette.ImagePalette("RGB", rrrgggbbb, 12) if place is None: img = Image.fromarray(scan, mode="P") else: img = Image.fromarray( scan[max(0, place[0] - place[2]):min(scan.shape[0] - 1, place[0] + place[2]), max(0, place[1] - place[3]):min(scan.shape[1] - 1, place[1] + place[3])], mode="P") img = img.resize((img.size[0] * 10, img.size[1] * 10)) img.putpalette(pal) img.show()
def test_make_gamma_lut(self): # Arrange exp = 5 # Act lut = ImagePalette.make_gamma_lut(exp) # Assert self.assertIsInstance(lut, list) self.assertEqual(len(lut), 256) # Check a few values self.assertEqual(lut[0], 0) self.assertEqual(lut[63], 0) self.assertEqual(lut[127], 8) self.assertEqual(lut[191], 60) self.assertEqual(lut[255], 255)
def getPaletteOfImg(imgPath, contrast_val=1.0, color_val=1.0): img = Image.open(imgPath) pal = ImagePalette.ImagePalette() print(pal) contrast = contrast_img(img, contrast_val, color_val) print('contrast: {}'.format(contrast)) pal = img.getpalette() print(pal) newPalette = [] palette = Haishoku.getPalette(imgPath) for i in range(0, len(palette)): for j in palette[i][1]: newPalette.append(j) print(' Palette: {}'.format(newPalette)) return newPalette
def test_make_gamma_lut(): # Arrange exp = 5 # Act lut = ImagePalette.make_gamma_lut(exp) # Assert assert isinstance(lut, list) assert len(lut) == 256 # Check a few values assert lut[0] == 0 assert lut[63] == 0 assert lut[127] == 8 assert lut[191] == 60 assert lut[255] == 255
def _open(self): # HEAD s = self.fp.read(128) magic = i16(s[4:6]) if magic not in [0xAF11, 0xAF12]: raise SyntaxError, "not an FLI/FLC file" # image characteristics self.mode = "P" self.size = i16(s[8:10]), i16(s[10:12]) # animation speed duration = i32(s[16:20]) if magic == 0xAF11: duration = (duration * 1000) / 70 self.info["duration"] = duration # look for palette palette = map(lambda a: (a,a,a), range(256)) s = self.fp.read(16) self.__offset = 128 if i16(s[4:6]) == 0xF100: # prefix chunk; ignore it self.__offset = self.__offset + i32(s) s = self.fp.read(16) if i16(s[4:6]) == 0xF1FA: # look for palette chunk s = self.fp.read(6) if i16(s[4:6]) == 11: self._palette(palette, 2) elif i16(s[4:6]) == 4: self._palette(palette, 0) palette = map(lambda (r,g,b): chr(r)+chr(g)+chr(b), palette) self.palette = ImagePalette.raw("RGB", string.join(palette, "")) # set things up to decode first frame self.frame = -1 self.__fp = self.fp self.seek(0)
def _open(self): if self.fp.read(8) != _MAGIC: raise SyntaxError("not a PNG file") # # Parse headers up to the first IDAT chunk self.png = PngStream(self.fp) while True: # # get next chunk cid, pos, len = self.png.read() try: s = self.png.call(cid, pos, len) except EOFError: break except AttributeError: if Image.DEBUG: print(cid, pos, len, "(unknown)") s = ImageFile._safe_read(self.fp, len) self.png.crc(cid, s) # # Copy relevant attributes from the PngStream. An alternative # would be to let the PngStream class modify these attributes # directly, but that introduces circular references which are # difficult to break if things go wrong in the decoder... # (believe me, I've tried ;-) self.mode = self.png.im_mode self.size = self.png.im_size self.info = self.png.im_info self.text = self.png.im_text # experimental self.tile = self.png.im_tile if self.png.im_palette: rawmode, data = self.png.im_palette self.palette = ImagePalette.raw(rawmode, data) self.__idat = len # used by load_read()
def _new(self, im): new = Image() new.im = im new.mode = im.mode new.size = im.size if self.palette: new.palette = self.palette.copy() if im.mode == "P" and not new.palette: new.palette = ImagePalette.ImagePalette() try: new.info = self.info.copy() except AttributeError: # fallback (pre-1.5.2) new.info = {} for k, v in self.info: new.info[k] = v return new
def _open(self): if self.fp.read(8) != _MAGIC: raise SyntaxError("not a PNG file") # # Parse headers up to the first IDAT chunk self.png = PngStream(self.fp) while True: # # get next chunk cid, pos, length = self.png.read() try: s = self.png.call(cid, pos, length) except EOFError: break except AttributeError: if Image.DEBUG: print(cid, pos, length, "(unknown)") s = ImageFile._safe_read(self.fp, length) self.png.crc(cid, s) # # Copy relevant attributes from the PngStream. An alternative # would be to let the PngStream class modify these attributes # directly, but that introduces circular references which are # difficult to break if things go wrong in the decoder... # (believe me, I've tried ;-) self.mode = self.png.im_mode self.size = self.png.im_size self.info = self.png.im_info self.text = self.png.im_text # experimental self.tile = self.png.im_tile if self.png.im_palette: rawmode, data = self.png.im_palette self.palette = ImagePalette.raw(rawmode, data) self.__idat = length # used by load_read()
def _get_palette_bytes(im, palette, info): if im.mode == "P": if palette and isinstance(palette, bytes): source_palette = palette[:768] else: source_palette = im.im.getpalette("RGB")[:768] else: # L-mode if palette and isinstance(palette, bytes): source_palette = palette[:768] else: source_palette = bytearray([i // 3 for i in range(768)]) used_palette_colors = palette_bytes = None if _get_optimize(im, info): used_palette_colors = _get_used_palette_colors(im) # create the new palette if not every color is used if len(used_palette_colors) < 256: palette_bytes = b"" new_positions = {} i = 0 # pick only the used colors from the palette for oldPosition in used_palette_colors: palette_bytes += source_palette[oldPosition * 3:oldPosition * 3 + 3] new_positions[oldPosition] = i i += 1 # replace the palette color id of all pixel with the new id image_bytes = bytearray(im.tobytes()) for i in range(len(image_bytes)): image_bytes[i] = new_positions[image_bytes[i]] im.frombytes(bytes(image_bytes)) new_palette_bytes = (palette_bytes + (768 - len(palette_bytes)) * b'\x00') im.putpalette(new_palette_bytes) im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes, size=len(palette_bytes)) if not palette_bytes: palette_bytes = source_palette return palette_bytes, used_palette_colors
def Colormap2Palette(colormap=colormapLinrad): r=[] g=[] b=[] for i in range(256): j=31*i +1 t=colormap[j:j+10] rr=int(255.0*float(t)) t=colormap[j+10:j+20] gg=int(255.0*float(t)) t=colormap[j+20:j+30] bb=int(255.0*float(t)) r.append(rr) g.append(gg) b.append(bb) palette=ImagePalette.ImagePalette("RGB",r+g+b) WsjtMod.g.palette=palette return palette
def test_transparent_optimize(tmp_path): # From issue #2195, if the transparent color is incorrectly optimized out, GIF loses # transparency. # Need a palette that isn't using the 0 color, and one that's > 128 items where the # transparent color is actually the top palette entry to trigger the bug. data = bytes(range(1, 254)) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) im = Image.new("L", (253, 1)) im.frombytes(data) im.putpalette(palette) out = str(tmp_path / "temp.gif") im.save(out, transparency=253) with Image.open(out) as reloaded: assert reloaded.info["transparency"] == 253
def process_image(src_ps3_path: str, src_ryukishi_path: str, dst_path: str, SMALL_IMAGE_MODE: bool, FOUR_THREE_ASPECT: bool): containing_path = str(Path(src_ps3_path).parent) containing_path_lower = containing_path.lower() ps3_image = Image.open(src_ps3_path) ryukishi_image = Image.open(src_ryukishi_path) output_image_mode = 'RGB' if has_transparency(ryukishi_image): output_image_mode = 'RGBA' # Convert paletted images to RGB first if ryukishi_image.mode in ['P', '1']: print(f"WARNING: converting paletted or binary image [{src_ryukishi_path}] to RGB/RGBA") ryukishi_image = ryukishi_image.convert(output_image_mode) # Check for strange image modes if ryukishi_image.mode not in ['RGB', 'RGBA', 'L']: raise Exception(f"Unhandled image mode: [{ryukishi_image.mode}]") out_noeffect = resize_image(ps3_image, ryukishi_image, SMALL_IMAGE_MODE, FOUR_THREE_ASPECT) if 'flashback' in containing_path_lower: # TODO: handle transparency when applying flashback effect! Currently converting to greyscale ('L') loses alpha out = out_noeffect.convert('L') out.putpalette(ImagePalette.sepia()) out = out.convert(output_image_mode) # Adjust the strength of the sepia effect by blending with the original image # 0 = no sepia, 1 = full sepia out = Image.blend(out_noeffect, out, .7) elif 'greyscale' in containing_path_lower: # TODO: handle transparency when applying greyscale effect! Currently converting to greyscale ('L') loses alpha out = out_noeffect.convert('L') elif 'negative' in containing_path_lower: out = ImageOps.invert(out_noeffect) elif 'blur' in containing_path_lower: # This folder uses a zoom motion blur effect which I'm not sure how to implement uisng PIL # This folder will have to be done manually print(f"WARNING: file {dst_path} should have zoom motion blur applied manually") out = out_noeffect else: out = out_noeffect out.save(dst_path)
def test_transparent_optimize(self): # from issue #2195, if the transparent color is incorrectly # optimized out, gif loses transparency # Need a palette that isn't using the 0 color, and one # that's > 128 items where the transparent color is actually # the top palette entry to trigger the bug. data = bytes(bytearray(range(1, 254))) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) im = Image.new('L', (253, 1)) im.frombytes(data) im.putpalette(palette) out = self.tempfile('temp.gif') im.save(out, transparency=253) reloaded = Image.open(out) self.assertEqual(reloaded.info['transparency'], 253)
def test_write_with_raw_palette(self): palette = ImagePalette.raw('RGB', b'\x01\x02\x03\x04\x05\x06') img = SubImage8Bit(Image.new('P', (2, 2), color=1)) img.image.putpalette(palette) imgs = Images8Bit([img], palette=palette, width=9, height=8) buffer = BytesIO() save_8bit_sti(imgs, buffer) self.assertEqual(buffer.getvalue(), # Header b'STCI\x48\x00\x00\x00\x08\x00\x00\x00' + b'\x00\x00\x00\x00' + b'\x28\x00\x00\x00' + b'\x08\x00\x09\x00' + b'\x00\x01\x00\x00\x01\x00\x08\x08\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x08' + (3 * b'\x00') + b'\x00\x00\x00\x00' + (12 * b'\x00') + # Palette b'\x01\x02\x03' + b'\x04\x05\x06' + (254 * b'\x00\x00\x00') + # Sub Image Header b'\x00\x00\x00\x00' + b'\x08\x00\x00\x00' + b'\x00\x00' + b'\x00\x00' + b'\x02\x00' + b'\x02\x00' + # Data b'\x02\x01\x01\x00\x02\x01\x01\x00')
def _open(self): # HEAD s = self.fp.read(32) if i32(s) != 0x59a66a95: raise SyntaxError("not an SUN raster file") offset = 32 self.size = i32(s[4:8]), i32(s[8:12]) depth = i32(s[12:16]) if depth == 1: self.mode, rawmode = "1", "1;I" elif depth == 8: self.mode = rawmode = "L" elif depth == 24: self.mode, rawmode = "RGB", "BGR" else: raise SyntaxError("unsupported mode") compression = i32(s[20:24]) if i32(s[24:28]) != 0: length = i32(s[28:32]) offset = offset + length self.palette = ImagePalette.raw("RGB;L", self.fp.read(length)) if self.mode == "L": self.mode = rawmode = "P" stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) if compression == 1: self.tile = [ ("raw", (0, 0) + self.size, offset, (rawmode, stride))] elif compression == 2: self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)]
def _open(self): # HEAD s = self.fp.read(32) if i32(s) != 0x59a66a95: raise SyntaxError("not an SUN raster file") offset = 32 self.size = i32(s[4:8]), i32(s[8:12]) depth = i32(s[12:16]) if depth == 1: self.mode, rawmode = "1", "1;I" elif depth == 8: self.mode = rawmode = "L" elif depth == 24: self.mode, rawmode = "RGB", "BGR" else: raise SyntaxError("unsupported mode") compression = i32(s[20:24]) if i32(s[24:28]) != 0: length = i32(s[28:32]) offset = offset + length self.palette = ImagePalette.raw("RGB;L", self.fp.read(length)) if self.mode == "L": self.mode = rawmode = "P" stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) if compression == 1: self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride)) ] elif compression == 2: self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)]
def encode(coding: str, image, quality: int, speed: int, supports_transparency: bool, grayscale: bool = False, resize=None): log("pillow.encode%s", (coding, image, quality, speed, supports_transparency, grayscale, resize)) assert coding in ("jpeg", "webp", "png", "png/P", "png/L"), "unsupported encoding: %s" % coding assert image, "no image to encode" pixel_format = bytestostr(image.get_pixel_format()) palette = None w = image.get_width() h = image.get_height() rgb = { "RLE8": "P", "XRGB": "RGB", "BGRX": "RGB", "RGBX": "RGB", "RGBA": "RGBA", "BGRA": "RGBA", "BGR": "RGB", }.get(pixel_format, pixel_format) bpp = 32 pixels = image.get_pixels() assert pixels, "failed to get pixels from %s" % image #remove transparency if it cannot be handled, #and deal with non 24-bit formats: if pixel_format == "r210": stride = image.get_rowstride() from xpra.codecs.argb.argb import r210_to_rgba, r210_to_rgb #@UnresolvedImport if supports_transparency: pixels = r210_to_rgba(pixels, w, h, stride, w * 4) pixel_format = "RGBA" rgb = "RGBA" else: image.set_rowstride(image.get_rowstride() * 3 // 4) pixels = r210_to_rgb(pixels, w, h, stride, w * 3) pixel_format = "RGB" rgb = "RGB" bpp = 24 elif pixel_format == "BGR565": from xpra.codecs.argb.argb import bgr565_to_rgbx, bgr565_to_rgb #@UnresolvedImport if supports_transparency: image.set_rowstride(image.get_rowstride() * 2) pixels = bgr565_to_rgbx(pixels) pixel_format = "RGBA" rgb = "RGBA" else: image.set_rowstride(image.get_rowstride() * 3 // 2) pixels = bgr565_to_rgb(pixels) pixel_format = "RGB" rgb = "RGB" bpp = 24 elif pixel_format == "RLE8": pixel_format = "P" palette = [] #pillow requires 8 bit palette values, #but we get 16-bit values from the image wrapper (X11 palettes are 16-bit): for r, g, b in image.get_palette(): palette.append((r >> 8) & 0xFF) palette.append((g >> 8) & 0xFF) palette.append((b >> 8) & 0xFF) bpp = 8 else: assert pixel_format in ("RGBA", "RGBX", "BGRA", "BGRX", "BGR", "RGB"), \ "invalid pixel format '%s'" % pixel_format try: #PIL cannot use the memoryview directly: if isinstance(pixels, memoryview): pixels = pixels.tobytes() #it is safe to use frombuffer() here since the convert() #calls below will not convert and modify the data in place #and we save the compressed data then discard the image im = Image.frombuffer(rgb, (w, h), pixels, "raw", pixel_format, image.get_rowstride(), 1) if palette: im.putpalette(palette) im.palette = ImagePalette.ImagePalette("RGB", palette=palette, size=len(palette)) if coding != "png/L" and grayscale: if rgb.find( "A") >= 0 and supports_transparency and coding != "jpeg": im = im.convert("LA") else: im = im.convert("L") rgb = "L" bpp = 8 elif coding.startswith( "png") and not supports_transparency and rgb == "RGBA": im = im.convert("RGB") rgb = "RGB" bpp = 24 except Exception: log.error( "pillow.encode%s converting %s pixels from %s to %s failed", (coding, image, speed, supports_transparency, grayscale, resize), type(pixels), pixel_format, rgb, exc_info=True) raise client_options = {} if resize: if speed >= 95: resample = "NEAREST" elif speed > 80: resample = "BILINEAR" elif speed >= 30: resample = "BICUBIC" else: resample = "LANCZOS" resample_value = getattr(Image, resample, 0) im = im.resize(resize, resample=resample_value) client_options["resample"] = resample if coding in ("jpeg", "webp"): #newer versions of pillow require explicit conversion to non-alpha: if pixel_format.find("A") >= 0: im = im.convert("RGB") q = int(min(100, max(1, quality))) kwargs = im.info kwargs["quality"] = q client_options["quality"] = q if coding == "jpeg" and speed < 50: #(optimizing jpeg is pretty cheap and worth doing) kwargs["optimize"] = True client_options["optimize"] = True elif coding == "webp" and q >= 100: kwargs["lossless"] = 1 pil_fmt = coding.upper() else: assert coding in ("png", "png/P", "png/L"), "unsupported encoding: %s" % coding if coding in ("png/L", "png/P") and supports_transparency and rgb == "RGBA": #grab alpha channel (the last one): #we use the last channel because we know it is RGBA, #otherwise we should do: alpha_index= image.getbands().index('A') alpha = im.split()[-1] #convert to simple on or off mask: #set all pixel values below 128 to 255, and the rest to 0 def mask_value(a): if a <= 128: return 255 return 0 mask = Image.eval(alpha, mask_value) else: #no transparency mask = None if coding == "png/L": im = im.convert("L", palette=Image.ADAPTIVE, colors=255) bpp = 8 elif coding == "png/P": #convert to 255 indexed colour if: # * we're not in palette mode yet (source is >8bpp) # * we need space for the mask (256 -> 255) if palette is None or mask: #I wanted to use the "better" adaptive method, #but this does NOT work (produces a black image instead): #im.convert("P", palette=Image.ADAPTIVE) im = im.convert("P", palette=Image.WEB, colors=255) bpp = 8 kwargs = im.info if mask: # paste the alpha mask to the color of index 255 im.paste(255, mask) client_options["transparency"] = 255 kwargs["transparency"] = 255 if speed == 0: #optimizing png is very rarely worth doing kwargs["optimize"] = True client_options["optimize"] = True #level can range from 0 to 9, but anything above 5 is way too slow for small gains: #76-100 -> 1 #51-76 -> 2 #etc level = max(1, min(5, (125 - speed) // 25)) kwargs["compress_level"] = level #no need to expose to the client: #client_options["compress_level"] = level #default is good enough, no need to override, other options: #DEFAULT_STRATEGY, FILTERED, HUFFMAN_ONLY, RLE, FIXED #kwargs["compress_type"] = Image.DEFAULT_STRATEGY pil_fmt = "PNG" buf = BytesIO() im.save(buf, pil_fmt, **kwargs) if SAVE_TO_FILE: # pragma: no cover filename = "./%s.%s" % (time.time(), pil_fmt) im.save(filename, pil_fmt) log.info("saved %s to %s", coding, filename) log("sending %sx%s %s as %s, mode=%s, options=%s", w, h, pixel_format, coding, im.mode, kwargs) data = buf.getvalue() buf.close() return coding, Compressed( coding, data), client_options, image.get_width(), image.get_height(), 0, bpp
def test_imagepalette(self): im = hopper("P") im.putpalette(ImagePalette.negative()) im.putpalette(ImagePalette.random()) im.putpalette(ImagePalette.sepia()) im.putpalette(ImagePalette.wedge())
def _open(self): # process header s = self.fp.read(18) idlen = i8(s[0]) colormaptype = i8(s[1]) imagetype = i8(s[2]) depth = i8(s[16]) flags = i8(s[17]) self.size = i16(s[12:]), i16(s[14:]) # validate header fields if colormaptype not in (0, 1) or\ self.size[0] <= 0 or self.size[1] <= 0 or\ depth not in (1, 8, 16, 24, 32): raise SyntaxError("not a TGA file") # image mode if imagetype in (3, 11): self.mode = "L" if depth == 1: self.mode = "1" # ??? elif imagetype in (1, 9): self.mode = "P" elif imagetype in (2, 10): self.mode = "RGB" if depth == 32: self.mode = "RGBA" else: raise SyntaxError("unknown TGA mode") # orientation orientation = flags & 0x30 if orientation == 0x20: orientation = 1 elif not orientation: orientation = -1 else: raise SyntaxError("unknown TGA orientation") self.info["orientation"] = orientation if imagetype & 8: self.info["compression"] = "tga_rle" if idlen: self.info["id_section"] = self.fp.read(idlen) if colormaptype: # read palette start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:]) if mapdepth == 16: self.palette = ImagePalette.raw( "BGR;16", b"\0"*2*start + self.fp.read(2*size)) elif mapdepth == 24: self.palette = ImagePalette.raw( "BGR", b"\0"*3*start + self.fp.read(3*size)) elif mapdepth == 32: self.palette = ImagePalette.raw( "BGRA", b"\0"*4*start + self.fp.read(4*size)) # setup tile descriptor try: rawmode = MODES[(imagetype & 7, depth)] if imagetype & 8: # compressed self.tile = [("tga_rle", (0, 0)+self.size, self.fp.tell(), (rawmode, orientation, depth))] else: self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), (rawmode, 0, orientation))] except KeyError: pass # cannot decode
import os #os.environ['CUDA_HOME'] = '/opt/anaconda3/lib/python3.6/site-packages/torch/cuda' from torch.nn import functional as F from torchvision.transforms import ToTensor, Normalize, Compose import torch print(torch.cuda.is_available()) import cv2 import random from pathlib import Path # In[3]: random.seed(42) NUCLEI_PALETTE = ImagePalette.random() random.seed() # In[4]: rcParams['figure.figsize'] = 15, 15 # In[5]: from models.ternausnet2 import TernausNetV2 # print('...') # In[6]: def get_model(model_path):
def test_sanity(self): ImagePalette("RGB", list(range(256)) * 3) self.assertRaises(ValueError, lambda: ImagePalette("RGB", list(range(256)) * 2))
def _setup(self): "Setup this image object based on current tags" if self.tag.has_key(0xBC01): raise IOError, "Windows Media Photo files not yet supported" getscalar = self.tag.getscalar # extract relevant tags self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)] self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1) # photometric is a required tag, but not everyone is reading # the specification photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0) fillorder = getscalar(FILLORDER, 1) if Image.DEBUG: print "*** Summary ***" print "- compression:", self._compression print "- photometric_interpretation:", photo print "- planar_configuration:", self._planar_configuration print "- fill_order:", fillorder # size xsize = getscalar(IMAGEWIDTH) ysize = getscalar(IMAGELENGTH) self.size = xsize, ysize if Image.DEBUG: print "- size:", self.size format = getscalar(SAMPLEFORMAT, 1) # mode: check photometric interpretation and bits per pixel key = (photo, format, fillorder, self.tag.get(BITSPERSAMPLE, (1, )), self.tag.get(EXTRASAMPLES, ())) if Image.DEBUG: print "format key:", key try: self.mode, rawmode = OPEN_INFO[key] except KeyError: if Image.DEBUG: print "- unsupported format" raise SyntaxError, "unknown pixel mode" if Image.DEBUG: print "- raw mode:", rawmode print "- pil mode:", self.mode self.info["compression"] = self._compression # BEGIN PATCH xres = getscalar(X_RESOLUTION, (1, 1)) yres = getscalar(Y_RESOLUTION, (1, 1)) if xres and yres: xres = xres[0] / (xres[1] or 1) yres = yres[0] / (yres[1] or 1) resunit = getscalar(RESOLUTION_UNIT, 1) if resunit == 2: # Inches self.info["dpi"] = xres, yres elif resunit == 3: # Centimeters self.info["dpi"] = xres * 2.54, yres * 2.54 else: # No absolute unit of measurement. self.info["resolution"] = xres, yres # END PATCH # build tile descriptors x = y = l = 0 self.tile = [] if self.tag.has_key(STRIPOFFSETS): # striped image h = getscalar(ROWSPERSTRIP, ysize) w = self.size[0] a = None for o in self.tag[STRIPOFFSETS]: if not a: a = self._decoder(rawmode, l) self.tile.append( (self._compression, (0, min(y, ysize), w, min(y + h, ysize)), o, a)) y = y + h if y >= self.size[1]: x = y = 0 l = l + 1 a = None elif self.tag.has_key(324): # tiled image w = getscalar(322) h = getscalar(323) a = None for o in self.tag[324]: if not a: a = self._decoder(rawmode, l) # FIXME: this doesn't work if the image size # is not a multiple of the tile size... self.tile.append( (self._compression, (x, y, x + w, y + h), o, a)) x = x + w if x >= self.size[0]: x, y = 0, y + h if y >= self.size[1]: x = y = 0 l = l + 1 a = None else: if Image.DEBUG: print "- unsupported data organization" raise SyntaxError("unknown data organization") # fixup palette descriptor if self.mode == "P": palette = map(lambda a: chr(a / 256), self.tag[COLORMAP]) self.palette = ImagePalette.raw("RGB;L", string.join(palette, ""))
def _open(self): if not _accept(self.fp.read(9)): raise SyntaxError("not an XPM file") # skip forward to next string while True: s = self.fp.readline() if not s: raise SyntaxError("broken XPM file") m = xpm_head.match(s) if m: break self.size = int(m.group(1)), int(m.group(2)) pal = int(m.group(3)) bpp = int(m.group(4)) if pal > 256 or bpp != 1: raise ValueError("cannot read this XPM file") # # load palette description palette = [b"\0\0\0"] * 256 for i in range(pal): s = self.fp.readline() if s[-2:] == b'\r\n': s = s[:-2] elif s[-1:] in b'\r\n': s = s[:-1] c = i8(s[1]) s = s[2:-2].split() for i in range(0, len(s), 2): if s[i] == b"c": # process colour key rgb = s[i+1] if rgb == b"None": self.info["transparency"] = c elif rgb[0:1] == b"#": # FIXME: handle colour names (see ImagePalette.py) rgb = int(rgb[1:], 16) palette[c] = o8((rgb >> 16) & 255) +\ o8((rgb >> 8) & 255) +\ o8(rgb & 255) else: # unknown colour raise ValueError("cannot read this XPM file") break else: # missing colour key raise ValueError("cannot read this XPM file") self.mode = "P" self.palette = ImagePalette.raw("RGB", b"".join(palette)) self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))]
def getheader(im, palette=None, info=None): """Return a list of strings representing a GIF header""" # Header Block # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp version = b"87a" for extensionKey in ["transparency", "duration", "loop", "comment"]: if info and extensionKey in info: if ((extensionKey == "duration" and info[extensionKey] == 0) or (extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255))): continue version = b"89a" break else: if im.info.get("version") == "89a": version = b"89a" header = [ b"GIF"+version + # signature + version o16(im.size[0]) + # canvas width o16(im.size[1]) # canvas height ] if im.mode == "P": if palette and isinstance(palette, bytes): source_palette = palette[:768] else: source_palette = im.im.getpalette("RGB")[:768] else: # L-mode if palette and isinstance(palette, bytes): source_palette = palette[:768] else: source_palette = bytearray([i//3 for i in range(768)]) used_palette_colors = palette_bytes = None if _get_optimize(im, info): used_palette_colors = _get_used_palette_colors(im) # create the new palette if not every color is used if len(used_palette_colors) < 256: palette_bytes = b"" new_positions = {} i = 0 # pick only the used colors from the palette for oldPosition in used_palette_colors: palette_bytes += source_palette[oldPosition*3:oldPosition*3+3] new_positions[oldPosition] = i i += 1 # replace the palette color id of all pixel with the new id image_bytes = bytearray(im.tobytes()) for i in range(len(image_bytes)): image_bytes[i] = new_positions[image_bytes[i]] im.frombytes(bytes(image_bytes)) new_palette_bytes = (palette_bytes + (768 - len(palette_bytes)) * b'\x00') im.putpalette(new_palette_bytes) im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes, size=len(palette_bytes)) if not palette_bytes: palette_bytes = source_palette # Logical Screen Descriptor # calculate the palette size for the header import math color_table_size = int(math.ceil(math.log(len(palette_bytes)//3, 2)))-1 if color_table_size < 0: color_table_size = 0 # size of global color table + global color table flag header.append(o8(color_table_size + 128)) # background + reserved/aspect if info and "background" in info: background = info["background"] elif "background" in im.info: # This elif is redundant within GifImagePlugin # since im.info parameters are bundled into the info dictionary # However, external scripts may call getheader directly # So this maintains earlier behaviour background = im.info["background"] else: background = 0 header.append(o8(background) + o8(0)) # end of Logical Screen Descriptor # add the missing amount of bytes # the palette has to be 2<<n in size actual_target_size_diff = (2 << color_table_size) - len(palette_bytes)//3 if actual_target_size_diff > 0: palette_bytes += o8(0) * 3 * actual_target_size_diff # Header + Logical Screen Descriptor + Global Color Table header.append(palette_bytes) return header, used_palette_colors
def _seek(self, frame): if frame == 0: # rewind self.__offset = 0 self.dispose = None self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1 self.__frame = -1 self.__fp.seek(self.__rewind) self._prev_im = None self.disposal_method = 0 else: # ensure that the previous frame was loaded if not self.im: self.load() if frame != self.__frame + 1: raise ValueError("cannot seek to frame %d" % frame) self.__frame = frame self.tile = [] self.fp = self.__fp if self.__offset: # backup to last frame self.fp.seek(self.__offset) while self.data(): pass self.__offset = 0 if self.dispose: self.im.paste(self.dispose, self.dispose_extent) from copy import copy self.palette = copy(self.global_palette) while True: s = self.fp.read(1) if not s or s == b";": break elif s == b"!": # # extensions # s = self.fp.read(1) block = self.data() if i8(s) == 249: # # graphic control extension # flags = i8(block[0]) if flags & 1: self.info["transparency"] = i8(block[3]) self.info["duration"] = i16(block[1:3]) * 10 # disposal method - find the value of bits 4 - 6 dispose_bits = 0b00011100 & flags dispose_bits = dispose_bits >> 2 if dispose_bits: # only set the dispose if it is not # unspecified. I'm not sure if this is # correct, but it seems to prevent the last # frame from looking odd for some animations self.disposal_method = dispose_bits elif i8(s) == 254: # # comment extension # self.info["comment"] = block elif i8(s) == 255: # # application extension # self.info["extension"] = block, self.fp.tell() if block[:11] == b"NETSCAPE2.0": block = self.data() if len(block) >= 3 and i8(block[0]) == 1: self.info["loop"] = i16(block[1:3]) while self.data(): pass elif s == b",": # # local image # s = self.fp.read(9) # extent x0, y0 = i16(s[0:]), i16(s[2:]) x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) self.dispose_extent = x0, y0, x1, y1 flags = i8(s[8]) interlace = (flags & 64) != 0 if flags & 128: bits = (flags & 7) + 1 self.palette =\ ImagePalette.raw("RGB", self.fp.read(3 << bits)) # image data bits = i8(self.fp.read(1)) self.__offset = self.fp.tell() self.tile = [("gif", (x0, y0, x1, y1), self.__offset, (bits, interlace))] break else: pass # raise IOError, "illegal GIF tag `%x`" % i8(s) try: if self.disposal_method < 2: # do not dispose or none specified self.dispose = None elif self.disposal_method == 2: # replace with background colour self.dispose = Image.core.fill("P", self.size, self.info["background"]) else: # replace with previous contents if self.im: self.dispose = self.im.copy() # only dispose the extent in this frame if self.dispose: self.dispose = self.dispose.crop(self.dispose_extent) except (AttributeError, KeyError): pass if not self.tile: # self.__fp = None raise EOFError self.mode = "L" if self.palette: self.mode = "P"
def _open(self): read = self.fp.read # # header s = read(26) if s[:4] != b"8BPS" or i16(s[4:]) != 1: raise SyntaxError("not a PSD file") psd_bits = i16(s[22:]) psd_channels = i16(s[12:]) psd_mode = i16(s[24:]) mode, channels = MODES[(psd_mode, psd_bits)] if channels > psd_channels: raise IOError("not enough channels") self.mode = mode self.size = i32(s[18:]), i32(s[14:]) # # color mode data size = i32(read(4)) if size: data = read(size) if mode == "P" and size == 768: self.palette = ImagePalette.raw("RGB;L", data) # # image resources self.resources = [] size = i32(read(4)) if size: # load resources end = self.fp.tell() + size while self.fp.tell() < end: signature = read(4) id = i16(read(2)) name = read(i8(read(1))) if not (len(name) & 1): read(1) # padding data = read(i32(read(4))) if (len(data) & 1): read(1) # padding self.resources.append((id, name, data)) if id == 1039: # ICC profile self.info["icc_profile"] = data # # layer and mask information self.layers = [] size = i32(read(4)) if size: end = self.fp.tell() + size size = i32(read(4)) if size: self.layers = _layerinfo(self.fp) self.fp.seek(end) # # image descriptor self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) # keep the file open self._fp = self.fp self.frame = 0
def seek(self, frame): if frame == 0: # rewind self.__offset = 0 self.dispose = None self.__frame = -1 self.__fp.seek(self.__rewind) if frame != self.__frame + 1: raise ValueError("cannot seek to frame %d" % frame) self.__frame = frame self.tile = [] self.fp = self.__fp if self.__offset: # backup to last frame self.fp.seek(self.__offset) while self.data(): pass self.__offset = 0 if self.dispose: self.im = self.dispose self.dispose = None from copy import copy self.palette = copy(self.global_palette) while True: s = self.fp.read(1) if not s or s == b";": break elif s == b"!": # # extensions # s = self.fp.read(1) block = self.data() if i8(s) == 249: # # graphic control extension # flags = i8(block[0]) if flags & 1: self.info["transparency"] = i8(block[3]) self.info["duration"] = i16(block[1:3]) * 10 try: # disposal methods if flags & 8: # replace with background colour self.dispose = Image.core.fill("P", self.size, self.info["background"]) elif flags & 16: # replace with previous contents self.dispose = self.im.copy() except (AttributeError, KeyError): pass elif i8(s) == 255: # # application extension # self.info["extension"] = block, self.fp.tell() if block[:11] == b"NETSCAPE2.0": block = self.data() if len(block) >= 3 and i8(block[0]) == 1: self.info["loop"] = i16(block[1:3]) while self.data(): pass elif s == b",": # # local image # s = self.fp.read(9) # extent x0, y0 = i16(s[0:]), i16(s[2:]) x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) flags = i8(s[8]) interlace = (flags & 64) != 0 if flags & 128: bits = (flags & 7) + 1 self.palette = ImagePalette.raw("RGB", self.fp.read(3 << bits)) # image data bits = i8(self.fp.read(1)) self.__offset = self.fp.tell() self.tile = [("gif", (x0, y0, x1, y1), self.__offset, (bits, interlace))] break else: pass # raise IOError, "illegal GIF tag `%x`" % i8(s) if not self.tile: # self.__fp = None raise EOFError("no more images in GIF file") self.mode = "L" if self.palette: self.mode = "P"
def _bitmap(self, header=0, offset=0): """ Read relevant info about the BMP """ read, seek = self.fp.read, self.fp.seek if header: seek(header) file_info = dict() file_info["header_size"] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info["direction"] = -1 # --------------------- If requested, read header at a specific position header_data = ImageFile._safe_read( self.fp, file_info["header_size"] - 4 ) # read the rest of the bmp header, without its size # --------------------------------------------------- IBM OS/2 Bitmap v1 # ------ This format has different offsets because of width/height types if file_info["header_size"] == 12: file_info["width"] = i16(header_data[0:2]) file_info["height"] = i16(header_data[2:4]) file_info["planes"] = i16(header_data[4:6]) file_info["bits"] = i16(header_data[6:8]) file_info["compression"] = self.RAW file_info["palette_padding"] = 3 # ---------------------------------------------- Windows Bitmap v2 to v5 elif file_info["header_size"] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 if file_info["header_size"] >= 40: # v3 and OS/2 file_info["y_flip"] = i8(header_data[7]) == 0xFF file_info["direction"] = 1 if file_info["y_flip"] else -1 file_info["width"] = i32(header_data[0:4]) file_info["height"] = ( i32(header_data[4:8]) if not file_info["y_flip"] else 2 ** 32 - i32(header_data[4:8]) ) file_info["planes"] = i16(header_data[8:10]) file_info["bits"] = i16(header_data[10:12]) file_info["compression"] = i32(header_data[12:16]) file_info["data_size"] = i32(header_data[16:20]) # byte size of pixel data file_info["pixels_per_meter"] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info["colors"] = i32(header_data[28:32]) file_info["palette_padding"] = 4 self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info["pixels_per_meter"])) if file_info["compression"] == self.BITFIELDS: if len(header_data) >= 52: for idx, mask in enumerate(["r_mask", "g_mask", "b_mask", "a_mask"]): file_info[mask] = i32(header_data[36 + idx * 4 : 40 + idx * 4]) else: for mask in ["r_mask", "g_mask", "b_mask", "a_mask"]: file_info[mask] = i32(read(4)) file_info["rgb_mask"] = (file_info["r_mask"], file_info["g_mask"], file_info["b_mask"]) file_info["rgba_mask"] = ( file_info["r_mask"], file_info["g_mask"], file_info["b_mask"], file_info["a_mask"], ) else: raise IOError("Unsupported BMP header type (%d)" % file_info["header_size"]) # ------------------ Special case : header is reported 40, which # ---------------------- is shorter than real size for bpp >= 16 self.size = file_info["width"], file_info["height"] # -------- If color count was not found in the header, compute from bits file_info["colors"] = file_info["colors"] if file_info.get("colors", 0) else (1 << file_info["bits"]) # -------------------------------- Check abnormal values for DOS attacks if file_info["width"] * file_info["height"] > 2 ** 31: raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) # ----------------------- Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None)) if self.mode is None: raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"]) # ----------------- Process BMP with Bitfields compression (not palette) if file_info["compression"] == self.BITFIELDS: SUPPORTED = { 32: [(0xFF0000, 0xFF00, 0xFF, 0x0), (0xFF0000, 0xFF00, 0xFF, 0xFF000000), (0x0, 0x0, 0x0, 0x0)], 24: [(0xFF0000, 0xFF00, 0xFF)], 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)], } MASK_MODES = { (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (24, (0xFF0000, 0xFF00, 0xFF)): "BGR", (16, (0xF800, 0x7E0, 0x1F)): "BGR;16", (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15", } if file_info["bits"] in SUPPORTED: if file_info["bits"] == 32 and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]: raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])] self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode elif file_info["bits"] in (24, 16) and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]: raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] else: raise IOError("Unsupported BMP bitfields layout") else: raise IOError("Unsupported BMP bitfields layout") elif file_info["compression"] == self.RAW: if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" else: raise IOError("Unsupported BMP compression (%d)" % file_info["compression"]) # ---------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images # ----------------------------------------------------- 1-bit images if not (0 < file_info["colors"] <= 65536): raise IOError("Unsupported BMP Palette size (%d)" % file_info["colors"]) else: padding = file_info["palette_padding"] palette = read(padding * file_info["colors"]) greyscale = True indices = (0, 255) if file_info["colors"] == 2 else list(range(file_info["colors"])) # ------------------ Check if greyscale and ignore palette if so for ind, val in enumerate(indices): rgb = palette[ind * padding : ind * padding + 3] if rgb != o8(val) * 3: greyscale = False # -------- If all colors are grey, white or black, ditch palette if greyscale: self.mode = "1" if file_info["colors"] == 2 else "L" raw_mode = self.mode else: self.mode = "P" self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette) # ----------------------------- Finally set the tile data for the plugin self.info["compression"] = file_info["compression"] self.tile = [ ( "raw", (0, 0, file_info["width"], file_info["height"]), offset or self.fp.tell(), (raw_mode, ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3), file_info["direction"]), ) ]
def _setup(self): "Setup this image object based on current tags" if 0xBC01 in self.tag_v2: raise IOError("Windows Media Photo files not yet supported") # extract relevant tags self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1) # photometric is a required tag, but not everyone is reading # the specification photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) fillorder = self.tag_v2.get(FILLORDER, 1) if DEBUG: print("*** Summary ***") print("- compression:", self._compression) print("- photometric_interpretation:", photo) print("- planar_configuration:", self._planar_configuration) print("- fill_order:", fillorder) # size xsize = self.tag_v2.get(IMAGEWIDTH) ysize = self.tag_v2.get(IMAGELENGTH) self.size = xsize, ysize if DEBUG: print("- size:", self.size) format = self.tag_v2.get(SAMPLEFORMAT, (1,)) if len(format) > 1 and max(format) == min(format) == 1: # SAMPLEFORMAT is properly per band, so an RGB image will # be (1,1,1). But, we don't support per band pixel types, # and anything more than one band is a uint8. So, just # take the first element. Revisit this if adding support # for more exotic images. format = (1,) # mode: check photometric interpretation and bits per pixel key = ( self.tag_v2.prefix, photo, format, fillorder, self.tag_v2.get(BITSPERSAMPLE, (1,)), self.tag_v2.get(EXTRASAMPLES, ()) ) if DEBUG: print("format key:", key) try: self.mode, rawmode = OPEN_INFO[key] except KeyError: if DEBUG: print("- unsupported format") raise SyntaxError("unknown pixel mode") if DEBUG: print("- raw mode:", rawmode) print("- pil mode:", self.mode) self.info["compression"] = self._compression xres = self.tag_v2.get(X_RESOLUTION, (1, 1)) yres = self.tag_v2.get(Y_RESOLUTION, (1, 1)) if xres and not isinstance(xres, tuple): xres = (xres, 1.) if yres and not isinstance(yres, tuple): yres = (yres, 1.) if xres and yres: xres = xres[0] / (xres[1] or 1) yres = yres[0] / (yres[1] or 1) resunit = self.tag_v2.get(RESOLUTION_UNIT, 1) if resunit == 2: # dots per inch self.info["dpi"] = xres, yres elif resunit == 3: # dots per centimeter. convert to dpi self.info["dpi"] = xres * 2.54, yres * 2.54 else: # No absolute unit of measurement self.info["resolution"] = xres, yres # build tile descriptors x = y = l = 0 self.tile = [] if STRIPOFFSETS in self.tag_v2: # striped image offsets = self.tag_v2[STRIPOFFSETS] h = self.tag_v2.get(ROWSPERSTRIP, ysize) w = self.size[0] if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", "group4", "tiff_jpeg", "tiff_adobe_deflate", "tiff_thunderscan", "tiff_deflate", "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"]: # if DEBUG: # print "Activating g4 compression for whole file" # Decoder expects entire file as one tile. # There's a buffer size limit in load (64k) # so large g4 images will fail if we use that # function. # # Setup the one tile for the whole image, then # replace the existing load function with our # _load_libtiff function. self.load = self._load_libtiff # To be nice on memory footprint, if there's a # file descriptor, use that instead of reading # into a string in python. # libtiff closes the file descriptor, so pass in a dup. try: fp = hasattr(self.fp, "fileno") and \ os.dup(self.fp.fileno()) # flush the file descriptor, prevents error on pypy 2.4+ # should also eliminate the need for fp.tell for py3 # in _seek if hasattr(self.fp, "flush"): self.fp.flush() except IOError: # io.BytesIO have a fileno, but returns an IOError if # it doesn't use a file descriptor. fp = False # libtiff handles the fillmode for us, so 1;IR should # actually be 1;I. Including the R double reverses the # bits, so stripes of the image are reversed. See # https://github.com/python-pillow/Pillow/issues/279 if fillorder == 2: key = ( self.tag_v2.prefix, photo, format, 1, self.tag_v2.get(BITSPERSAMPLE, (1,)), self.tag_v2.get(EXTRASAMPLES, ()) ) if DEBUG: print("format key:", key) # this should always work, since all the # fillorder==2 modes have a corresponding # fillorder=1 mode self.mode, rawmode = OPEN_INFO[key] # 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 self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode: rawmode = 'I;16N' # Offset in the tile tuple is 0, we go from 0,0 to # w,h, and we only do this once -- eds a = (rawmode, self._compression, fp) self.tile.append( (self._compression, (0, 0, w, ysize), 0, a)) a = None else: for i in range(len(offsets)): a = self._decoder(rawmode, l, i) self.tile.append( (self._compression, (0, min(y, ysize), w, min(y+h, ysize)), offsets[i], a)) if DEBUG: print("tiles: ", self.tile) y = y + h if y >= self.size[1]: x = y = 0 l += 1 a = None elif TILEOFFSETS in self.tag_v2: # tiled image w = self.tag_v2.get(322) h = self.tag_v2.get(323) a = None for o in self.tag_v2[TILEOFFSETS]: if not a: a = self._decoder(rawmode, l) # FIXME: this doesn't work if the image size # is not a multiple of the tile size... self.tile.append( (self._compression, (x, y, x+w, y+h), o, a)) x = x + w if x >= self.size[0]: x, y = 0, y + h if y >= self.size[1]: x = y = 0 l += 1 a = None else: if DEBUG: print("- unsupported data organization") raise SyntaxError("unknown data organization") # fixup palette descriptor if self.mode == "P": palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
def CSPalette(): palette = ImagePalette.ImagePalette() for label in labels: palette.getcolor(label.color) return palette
def _open(self): # The Sun Raster file header is 32 bytes in length and has the following format: # typedef struct _SunRaster # { # DWORD MagicNumber; /* Magic (identification) number */ # DWORD Width; /* Width of image in pixels */ # DWORD Height; /* Height of image in pixels */ # DWORD Depth; /* Number of bits per pixel */ # DWORD Length; /* Size of image data in bytes */ # DWORD Type; /* Type of raster file */ # DWORD ColorMapType; /* Type of color map */ # DWORD ColorMapLength; /* Size of the color map in bytes */ # } SUNRASTER; # HEAD s = self.fp.read(32) if i32(s) != 0x59a66a95: raise InvalidFileType("not a SUN raster file") offset = 32 self.size = i32(s[4:8]), i32(s[8:12]) depth = i32(s[12:16]) data_length = i32(s[16:20]) # unreliable, ignore. file_type = i32(s[20:24]) palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary palette_length = i32(s[28:32]) if depth == 1: self.mode, rawmode = "1", "1;I" elif depth == 4: self.mode, rawmode = "L", "L;4" elif depth == 8: self.mode = rawmode = "L" elif depth == 24: if file_type == 3: self.mode, rawmode = "RGB", "RGB" else: self.mode, rawmode = "RGB", "BGR" elif depth == 32: if file_type == 3: self.mode, rawmode = 'RGB', 'RGBX' else: self.mode, rawmode = 'RGB', 'BGRX' else: raise NotImplementedError("Unsupported Mode/Bit Depth") if palette_length: if palette_length > 1024: raise NotImplementedError("Unsupported Color Palette Length") if palette_type != 1: raise NotImplementedError("Unsupported Palette Type") offset = offset + palette_length self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) if self.mode == "L": self.mode = "P" rawmode = rawmode.replace('L', 'P') # 16 bit boundaries on stride stride = ((self.size[0] * depth + 15) // 16) * 2 # file type: Type is the version (or flavor) of the bitmap # file. The following values are typically found in the Type # field: # 0000h Old # 0001h Standard # 0002h Byte-encoded # 0003h RGB format # 0004h TIFF format # 0005h IFF format # FFFFh Experimental # Old and standard are the same, except for the length tag. # byte-encoded is run-length-encoded # RGB looks similar to standard, but RGB byte order # TIFF and IFF mean that they were converted from T/IFF # Experimental means that it's something else. # (http://www.fileformat.info/format/sunraster/egff.htm) if file_type in (0, 1, 3, 4, 5): self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride)) ] elif file_type == 2: self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)] else: raise NotImplementedError('Unsupported Sun Raster file type')
def _open(self): if not _accept(self.fp.read(9)): raise SyntaxError("not an XPM file") # skip forward to next string while True: s = self.fp.readline() if not s: raise SyntaxError("broken XPM file") m = xpm_head.match(s) if m: break self.size = int(m.group(1)), int(m.group(2)) pal = int(m.group(3)) bpp = int(m.group(4)) if pal > 256 or bpp != 1: raise ValueError("cannot read this XPM file") # # load palette description palette = [b"\0\0\0"] * 256 for i in range(pal): s = self.fp.readline() if s[-2:] == b'\r\n': s = s[:-2] elif s[-1:] in b'\r\n': s = s[:-1] c = i8(s[1]) s = s[2:-2].split() for i in range(0, len(s), 2): if s[i] == b"c": # process colour key rgb = s[i + 1] if rgb == b"None": self.info["transparency"] = c elif rgb[0:1] == b"#": # FIXME: handle colour names (see ImagePalette.py) rgb = int(rgb[1:], 16) palette[c] = (o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 255)) else: # unknown colour raise ValueError("cannot read this XPM file") break else: # missing colour key raise ValueError("cannot read this XPM file") self.mode = "P" self.palette = ImagePalette.raw("RGB", b"".join(palette)) self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))]
def _bitmap(self, header=0, offset=0): """ Read relevant info about the BMP """ read, seek = self.fp.read, self.fp.seek if header: seek(header) file_info = dict() file_info['header_size'] = i32( read(4) ) # read bmp header size @offset 14 (this is part of the header size) file_info['direction'] = -1 # --------------------- If requested, read header at a specific position header_data = ImageFile._safe_read( self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size # --------------------------------------------------- IBM OS/2 Bitmap v1 # ------ This format has different offsets because of width/height types if file_info['header_size'] == 12: file_info['width'] = i16(header_data[0:2]) file_info['height'] = i16(header_data[2:4]) file_info['planes'] = i16(header_data[4:6]) file_info['bits'] = i16(header_data[6:8]) file_info['compression'] = self.RAW file_info['palette_padding'] = 3 # ---------------------------------------------- Windows Bitmap v2 to v5 elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5 if file_info['header_size'] >= 40: # v3 and OS/2 file_info['y_flip'] = i8(header_data[7]) == 0xff file_info['direction'] = 1 if file_info['y_flip'] else -1 file_info['width'] = i32(header_data[0:4]) file_info['height'] = i32( header_data[4:8] ) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) file_info['planes'] = i16(header_data[8:10]) file_info['bits'] = i16(header_data[10:12]) file_info['compression'] = i32(header_data[12:16]) file_info['data_size'] = i32( header_data[16:20]) # byte size of pixel data file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 self.info["dpi"] = tuple( map(lambda x: int(math.ceil(x / 39.3701)), file_info['pixels_per_meter'])) if file_info['compression'] == self.BITFIELDS: if len(header_data) >= 52: for idx, mask in enumerate( ['r_mask', 'g_mask', 'b_mask', 'a_mask']): file_info[mask] = i32(header_data[36 + idx * 4:40 + idx * 4]) else: for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']: file_info[mask] = i32(read(4)) file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) else: raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) # ------------------ Special case : header is reported 40, which # ---------------------- is shorter than real size for bpp >= 16 self.size = file_info['width'], file_info['height'] # -------- If color count was not found in the header, compute from bits file_info['colors'] = file_info['colors'] if file_info.get( 'colors', 0) else (1 << file_info['bits']) # -------------------------------- Check abnormal values for DOS attacks if file_info['width'] * file_info['height'] > 2**31: raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) # ----------------------- Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) if self.mode is None: raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) # ----------------- Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: SUPPORTED = { 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)] } MASK_MODES = { (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX", (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (24, (0xff0000, 0xff00, 0xff)): "BGR", (16, (0xf800, 0x7e0, 0x1f)): "BGR;16", (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15" } if file_info['bits'] in SUPPORTED: if file_info['bits'] == 32 and file_info[ 'rgba_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] self.mode = "RGBA" if raw_mode in ("BGRA", ) else self.mode elif file_info['bits'] in ( 24, 16 ) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] else: raise IOError("Unsupported BMP bitfields layout") else: raise IOError("Unsupported BMP bitfields layout") elif file_info['compression'] == self.RAW: if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" else: raise IOError("Unsupported BMP compression (%d)" % file_info['compression']) # ---------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images # ----------------------------------------------------- 1-bit images if not (0 < file_info['colors'] <= 65536): raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors']) else: padding = file_info['palette_padding'] palette = read(padding * file_info['colors']) greyscale = True indices = (0, 255) if file_info['colors'] == 2 else list( range(file_info['colors'])) # ------------------ Check if greyscale and ignore palette if so for ind, val in enumerate(indices): rgb = palette[ind * padding:ind * padding + 3] if rgb != o8(val) * 3: greyscale = False # -------- If all colors are grey, white or black, ditch palette if greyscale: self.mode = "1" if file_info['colors'] == 2 else "L" raw_mode = self.mode else: self.mode = "P" self.palette = ImagePalette.raw( "BGRX" if padding == 4 else "BGR", palette) # ----------------------------- Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] self.tile = [ ('raw', (0, 0, file_info['width'], file_info['height']), offset or self.fp.tell(), (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction'])) ]