4

I have a bunch of images, to many to do by hand that are 16 color 8 bit PNG format that I need in 16 4 bit format, they all have the same palette.

I am scouring Google for the best library to use, but I am not finding much on this specific problem so I am coming here for hopefully some more targeted solutions.

I am trying to use PIL based on other answers I have found here, but not having any luck.

img = Image.open('DownArrow_focused.png')
img = img.point(lambda i: i * 16, "L")
img.save('DownArrow_focused.png', 'PNG')

but this gives me a grayscale image, not what I want.

PIL won't work, trying PyPNG. GIMP does this, but I have hundreds of these things I need to batch process them. And get batches of these to convert, so it isn't a one time thing.

A Java based solution would be acceptable as well, pretty much anything I can run from the command line on a Linux/OSX machine will be acceptable.

Community
  • 1
  • 1
  • As far as I can see PIL doesn't have a [4bit mode](http://www.pythonware.com/library/pil/handbook/concepts.htm) so you need another approach there. – Voo Sep 23 '11 at 23:50
  • I'm curious why you think you need this. It's already 16 colors, so after the mandatory PNG compression it won't be any different in size. – Mark Ransom Sep 24 '11 at 05:17
  • @Mark it is because my hardware only reads real 4 bit PNG thats why, and they do get marginally smaller, which is relevant to me in this situation, but that isn't the root of the reason I need to do this. –  Sep 24 '11 at 13:41
  • @trashgod I don't want nor need to dither the image, I need the actual file to be 4 bit I have already posterized it down to 16 colors and applied the palette. I just need to convert the 8 bit PNG a true 4 bit PNG. Dithering would be bad. –  Sep 24 '11 at 13:46
  • Your original image have a 8 bits palette of which only 16 colors are referenced in the image? Could you link or upload an example? – leonbloy Sep 26 '11 at 01:14

2 Answers2

1

In PNG the palette is always stored in RGB8 (3 bytes for each index=color), with an arbitrary (up to 256) number of entries. If you currently have a 8 bit image with a 16-colors palette (16 total entries), you dont need to alter the pallete, only to repack the pixel bytes (two indexes per byte). If so, I think you could do it with PNGJ with this code (untested):

public static void reencode(String orig, String dest) {
    PngReader png1 = FileHelper.createPngReader(new File(orig));
    ImageInfo pnginfo1 = png1.imgInfo;
    ImageInfo pnginfo2 = new ImageInfo(pnginfo1.cols, pnginfo1.rows, 4, false,false,true);  
    PngWriter png2 = FileHelper.createPngWriter(new File(dest), pnginfo2, false);
    png2.copyChunksFirst(png1, ChunksToWrite.COPY_ALL);
    ImageLine l2 = new ImageLine(pnginfo2);
    for (int row = 0; row < pnginfo1.rows; row++) {
        ImageLine l1 = png1.readRow(row);
        l2.tf_pack(l1.scanline, false);
        l2.setRown(row);
        png2.writeRow(l2);
    }
    png1.end();
    png2.copyChunksLast(png1, ChunksToWrite.COPY_ALL);
    png2.end();
    System.out.println("Done");
}

Elsewhere, if your current pallette has 16 "used" colors (but its length is greater because it includes unused colors), you need to do some work, modifying the palette chunk (but it also can be done).

leonbloy
  • 73,180
  • 20
  • 142
  • 190
  • the images are 16 indexed colors but 8 bits because that is all Photoshop can save as. You are correct all I need to do is cut down the pixel bytes. –  Sep 28 '11 at 12:20
0

Call Netpbm programs http://netpbm.sourceforge.net/ from a Python script using the following commands:

$ pngtopnm test.png | pnmquant 16 | pnmtopng > test16.png

$ file test16.png
test16.png: PNG image data, 700 x 303, 4-bit colormap, non-interlaced

And GIMP reports test16.png as having Color space: Indexed color (16 colors), which I guess is what you want.

This is not a pure Python solution but PIL is also not pure Python and has dependencies on shared libraries too. I think you cannot avoid a dependency on some external image software.

Simon C
  • 1,977
  • 11
  • 14