Megidd
Megidd

Reputation: 7938

Visualize an image pixel buffer with a bit depth of 3 i.e. each pixel having 3 bits of data

Question

What are the tools/standards/file formats by which I can visualize my image pixel buffer on which each pixel has 3 bits?

My Language is Go.

PNG header

Bit depth: 3 vs 4

An image pixel buffer is created on which each pixel has only 3 bits of data. Having saved it as a PNG file with header info of bit depth set to 3, I cannot visualize it:

Screenshot: 3-bit header bit depth of PNG

But when the PNG file header info for bit depth is set to 4, I can be previewed, although I'm not sure if the preview is actually correct or wrong:

Screenshot: 4-bit header bit depth of PNG File

The change that I'm making is just the header info for bit depth, that's all. My code is in Golang:

    // Set bit depth and color type.

    e.tmp[8] = 3 // Preview is not possible.
    e.tmp[8] = 4 // Preview is possible.

    e.tmp[9] = ctGrayscale

PNG standard

According to Wikipedia we have:

The standard allows indexed color PNGs to have 1, 2, 4 or 8 bits per pixel; grayscale images with no alpha channel may have 1, 2, 4, 8 or 16 bits per pixel. Everything else uses a bit depth per channel of either 8 or 16.

Upvotes: 0

Views: 125

Answers (2)

Megidd
Megidd

Reputation: 7938

Have you tried to pad the 3 bits into a minimum of 8 bits?

Eventually, to test and visualize the correctness of the 3-bit pixel buffer, I followed the @ih8ie8 comment above.


// To visualize 3 bit depth, we pad the 3 bits into 8 bits.
// Then save as 8-bit gray PNG file.
func Pad3bitTo8bit(pix3bit []uint8) []uint8 {
    pix8bit := make([]uint8, 0, (len(pix3bit)<<3)/3)
    var color uint8
    var kolor uint8
    for i := 0; i < len(pix3bit); i++ {
        switch i % 3 {
        case 0:
            color = pix3bit[i] & 0b11100000
            color <<= 0
            pix8bit = append(pix8bit, color)
            color = pix3bit[i] & 0b00011100
            color <<= 3
            pix8bit = append(pix8bit, color)
            color = pix3bit[i] & 0b00000011
            kolor = pix3bit[i+1] & 0b10000000
            color = (color << 6) | (kolor >> 2)
            pix8bit = append(pix8bit, color)
        case 1:
            color = pix3bit[i] & 0b01110000
            color <<= 1
            pix8bit = append(pix8bit, color)
            color = pix3bit[i] & 0b00001110
            color <<= 4
            pix8bit = append(pix8bit, color)
            color = pix3bit[i] & 0b00000001
            kolor = pix3bit[i+1] & 0b11000000
            color = (color << 7) | (kolor >> 1)
            pix8bit = append(pix8bit, color)
        case 2:
            color = pix3bit[i] & 0b00111000
            color <<= 2
            pix8bit = append(pix8bit, color)
            color = pix3bit[i] & 0b00000111
            color <<= 5
            pix8bit = append(pix8bit, color)
        default:
            log.Fatalln("Code shouldn't reach here.")
        }
    }
    return pix8bit
}

This image might clarify the above procedure:

Screenshot

Then save as a gray 8-bit PNG file:

                // PNG specification doesn't recognize 3 bit depth at all.
                // To visualize 3 bit depth, we pad the 3 bits into 8 bits.
                pix8bit := Pad3bitTo8bit(*pix)

                // Save as 8-bit gray PNG file.
                img.SavePNG(tt.name+"-gray-3bit--padded-to-8bit.png", &image.Gray{
                    Pix:    pix8bit,
                    Rect:   rect,
                    Stride: 1 * rect.Dx(), // For 8-bit pixels.
                })

Upvotes: -1

Mark Adler
Mark Adler

Reputation: 112169

Having saved it as a PNG file with header info of bit depth set to 3

No, you did not. As you note yourself at the end of your question, there is no such thing. PNG supports 1, 2, 4, 8, and 16 bits per pixel. Whatever you saved is not a valid PNG file, and whatever you were using to view it rightfully rejected it.

You will need to use at least four bits per pixel with PNG. I am not aware of an image format that represents the uncompressed data as three bits per pixel. The uncompressed representation should not matter, as it is transient. Either you store it as compressed, where PNG compression will use approximately three bits per pixel in the compressed data anyway, if only eight values out of the 16 are used. Or you store it in memory as one byte, eight bits, per pixel, in order to deliver it to whatever will display it on the screen.

I just tried PNG compression with random 3-bit values in 4-bit pixels. The compressed data portion is 3.03 bits per pixel.

Upvotes: 3

Related Questions