def make_grayscale(img: ImageType) -> ImageType: """ Convert an Image to grayscale :param img: a PIL image in color (modes P, RGBA, RGB, CMYK, YCbCr, LAB or HSV) :return: a PIL image object in modes P, L or RGBA, if converted, or the original Image object in case no conversion is done. """ orig_mode = img.mode if orig_mode in ["RGB", "CMYK", "YCbCr", "LAB", "HSV"]: return img.convert("L") elif orig_mode == "RGBA": return img.convert("LA").convert("RGBA") elif orig_mode == "P": # Using ITU-R 601-2 luma transform: L = R * 299/1000 + G * 587/1000 + B * 114/1000 pal = img.getpalette() for i in range(len(pal) // 3): # Using ITU-R 601-2 luma transform g = (pal[3 * i] * 299 + pal[3 * i + 1] * 587 + pal[3 * i + 2] * 114) // 1000 pal[3 * i:3 * i + 3] = [g, g, g] img.putpalette(pal) return img else: return img
def do_reduce_colors(img: ImageType, max_colors: int) -> Tuple[ImageType, int, int]: """ Reduce the number of colors of an Image object It takes a PIL image object and tries to reduce the total number of colors, converting it to an indexed color (mode P) image. If the input image is in mode 1, it cannot be further reduced, so it's returned back with no changes. :param img: a PIL image in color (modes P, RGBA, RGB, CMYK, YCbCr, LAB or HSV) :param max_colors: an integer indicating the maximum number of colors allowed. :return: a PIL image in mode P (or mode 1, as stated above), an integer indicating the original number of colors (0 if source is not a mode P or mode 1 image) and an integer stating the resulting number of colors. """ orig_mode = img.mode if orig_mode == "1": return img, 2, 2 colors = img.getcolors() if colors: orig_colors = len(colors) else: orig_colors = 0 # Intermediate conversion steps when needed if orig_mode in ["CMYK", "YCbCr", "LAB", "HSV"]: img = img.convert("RGB") elif orig_mode == "LA": img = img.convert("RGBA") # Actual color reduction happening here if orig_mode in ["RGB", "L"]: palette = Image.ADAPTIVE elif orig_mode == "RGBA": palette = Image.ADAPTIVE transparent = Image.new("RGBA", img.size, (0, 0, 0, 0)) # blend with transparent image using own alpha img = Image.composite(img, transparent, img) elif orig_mode == "P": palette = img.getpalette() img = img.convert("RGBA") w, h = img.size alpha_layer = Image.new("L", img.size) for x in range(w): for y in range(h): r, g, b, a = img.getpixel((x, y)) alpha_layer.putpixel((x, y), a) img.putalpha(alpha_layer) else: return img, 0, 0 img = img.convert("P", palette=palette, colors=max_colors) return img, orig_colors, len(img.getcolors())