s.gang
s.gang

Reputation: 131

24bpp to 8bpp conversion C with raw image data

I am currently trying to convert raw binary image data (512 x 512 24bpp) to a 512 x 512 8bpp image by using 3bits for the R channel, 3 for the G channel, and 2 for the B channel. However when using my code my picture comes out grey scale? Can anyone tell me what I'm doing wrong?

/*24 bit per pixel - 8 bit per pixel transformation*/

unsigned char buf[512][512][3];
unsigned char in[512][512][3];
unsigned char out[512][512][3];
unsigned char pix[512][512];

int main(){

    FILE *fp, *output;
    int i, j;

    /*open file*/
    if((fp = fopen("LennaRGB512.data", "rb")) == NULL){
        printf("error opening file\n");
    }

    /*read file into buffer*/
    for (i = 0; i < 512; i++) {
        for (j = 0; j < 512; j++) {
            buf[i][j][0] = fgetc(fp); /*r*/
            buf[i][j][1] = fgetc(fp); /*g*/
            buf[i][j][2] = fgetc(fp); /*b*/

            in[i][j][0] = buf[i][j][0];
            in[i][j][1] = buf[i][j][1];
            in[i][j][2] = buf[i][j][2];
        }
    }

    fclose(fp);

    output = fopen("lenna_8bpp.data", "wb");
    for(i = 0; i < 512; i++){
        char pix[512][512];
        for(j = 0; j < 512; j++){
            out[i][j][0] = (in[i][j][0] * 8) / 256;
            out[i][j][1] = (in[i][j][1] * 8) / 256;
            out[i][j][2] = (in[i][j][2] * 4) / 256;
            pix[i][j] = (out[i][j][0] << 5) | (out[i][j][1] << 2) | out[i][j][2];
            fputc(pix[i][j], output);
        }
    }



    fclose(output);
    return 0;
}

There are tons of questions on doing this with .bmp files and others but I can't find any help with manipulating the raw image data pixel by pixel.

Upvotes: 1

Views: 1256

Answers (1)

cxw
cxw

Reputation: 17051

I agree with the commenters. I think the grayscale is very likely an artifact of your viewer rather than your conversion. However, your conversion can also be improved. Try the following output loop:

unsigned char pix;   /* don't need 512*512 of them. */
unsigned char r, g, b;
for(row = 0; row < 512; row++){
    for(col = 0; col < 512; col++){
        r = in[row][col][0] >> 5;   /* keep 3 bits */
        g = in[row][col][1] >> 5;
        b = in[row][col][2] >> 6;   /* keep 2 bits */
        pix = (r << 5) | (g << 2) | b;
        fputc(pix, output);
    }
}

You are only processing one pixel at a time, so you only need one pix value.

For each of the r, g, and b, color components (remember to specify unsigned char throughout), use >> (right shift) to drop all the bits except the most significant. This is simpler and more clear than the *8/256 sequence. Also, I believe *8/256 only works because arithmetic is promoted to int — if it were done in chars, the *8 could cause overflow and lose data.

Edit The problem is indeed in the display. I have posted a palette and instructions on my blog since the full contents are too long for the space here. Yes, I know link-only answers are bad :( . I just saved it into the Archive in case of link rot.

You do need to open the image as Indexed, and then assign the colormap of the image.

Upvotes: 2

Related Questions