1

I am trying to convert 24bpp BMP color image to 8bpp grayscale BMP image. I have done something but i am not getting 100% correct output.

I have converted 24bpp to 8bpp. but in result image colour table is also being considered as pixel data. I have tried setting offset byte in header but problem still persists.

#include <stdio.h>

int main()
{

    FILE* fIn = fopen("lena_colored_256.bmp","rb");         
    FILE* fOut = fopen("lena_gray.bmp", "w+");              
    int i, j, y;
    unsigned char byte[54];
    unsigned char colorTable[1024];
    if (fIn == NULL)// check if the input file has not been opened
    {
        printf("File does not exist.\n");
    }

    for (i = 0; i < 54; i++)//read the 54 byte header from fIn
    {
        byte[i] = getc(fIn);

    }
    byte[28] = (unsigned char)8; // write code to modify bitDepth to 8

    byte[11] = (unsigned char)04;
    //write code to add colorTable

    for (i = 0; i < 256; i++)
    {
            colorTable[i * 4 + 0] = i;
            colorTable[i * 4 + 1] = i;
            colorTable[i * 4 + 2] = i;
            colorTable[i * 4 + 3] = i;
    }


    for (i = 0; i < 54; i++)
    {
        putc(byte[i], fOut);
    }
    for (i = 0; i < 1024; i++)
    {
        putc(colorTable[i], fOut);

    }
        // extract image height, width and bitDepth from imageHeader 
    int *height = (int)& byte[18];
    int *width = (int)& byte[22];
    int *bitDepth = (int)& byte[28];

    printf("bitDepth : %d\n", *bitDepth);
    printf("width: %d\n", *width);
    printf("height: %d\n", *height);
    int size = (*height) * (*width)*3; //calculate image size

    unsigned char* buffer;
    buffer = (unsigned char*)malloc(sizeof(int) * size);

    for (i = 0; i < size; i=i+3)    //RGB to gray
    {

        buffer[i+2] = getc(fIn);//blue
        buffer[i+1] = getc(fIn);//green
        buffer[i+0] = getc(fIn);//red

        y = (buffer[i+0] * 0.33) + (buffer[i+1] * 0.33) + (buffer[i+2] * 0.33);

        putc(y, fOut);
    }


    fclose(fOut);
    fclose(fIn);

    return 0;
}

Colour table data is also being considered as pixel data by image. i have checked my colour table data entering into BMP file. I have printed out file pointer location, after entering at 94th byte it is increasing by 2 byte instead of 1 byte, this is happening total 4 times and other 1020 time file pointer is increasing by 1 byte. Any explanation regarding this?

prateek k
  • 137
  • 2
  • 15
  • Possible duplicate [Converting color bmp to grayscale bmp?](https://stackoverflow.com/questions/4147639/converting-color-bmp-to-grayscale-bmp) -- though not exact. The use of `unsigned char` here is correct. – David C. Rankin Jul 25 '19 at 16:25
  • Without colour table, image is not forming ie some error in coming while opening of output image – prateek k Jul 25 '19 at 16:32
  • 24bpp BMP image don't need colour table but 8bpp BMP image need colour table. https://abhijitnathwani.github.io/blog/2017/12/19/Introduction-to-Image-Processing-using-C – prateek k Jul 25 '19 at 16:34
  • You only need a colour table with an 8bpp image if its **not** greyscale. Then you need a palette. – Weather Vane Jul 25 '19 at 17:12
  • @WeatherVane I was wrong, [the palette is mandatory](https://en.wikipedia.org/wiki/BMP_file_format#File_structure) for images where bpp is <= 8. So there are two choices for a gray scale image: 8bpp with a gray palette, or more than 8bpp (e.g. 24bpp) where every pixel is gray. – user3386109 Jul 25 '19 at 19:43
  • 1
    @prateekk There are four fields in the bitmap headers that need to change: `bfSize`, `bfOffset `, `biSizeImage`, and `biBitCount`. In addition, the padding bytes need to be handled correctly (each line of the image data must contain a multiple of four bytes). Note that the padding bytes are included in `bfSize` and `biSizeImage`. And the palette size is included in `bfSize` and `bfOffset`. – user3386109 Jul 25 '19 at 19:51
  • @user3386109 hmm I feel sure I have used it with direct mapping. After all, there are 256 gray shades so there is no reason to map it, except perhaps for gamma correction. Perhaps image viewers are tolerant. – Weather Vane Jul 25 '19 at 20:06
  • @WeatherVane Yup, I seem to recall doing this at some point in the past. Perhaps I'm misremembering, and it was a different file format. In any case, the viewers that I tried today won't take an 8bpp image unless it has a palette. – user3386109 Jul 25 '19 at 20:59
  • @user3386109 i have checked my colour table data entering into BMP file. I have printed out file pointer location, after entering at 94th byte it is increasing by 2 byte instead of 1 byte, this is happening total 4 times and other 1020 time file pointer is increasing by 1 byte. Any explanation regarding this? – prateek k Jul 26 '19 at 20:41
  • The title says input is 24-bit bitmap, but the code says `"lena_colored_256.bmp"` which suggests the input is 8-bit. Show what's the printout for `printf("bitDepth : %d\n", *bitDepth);` – Barmak Shemirani Jul 27 '19 at 14:43
  • @BarmakShemirani, its a 256x256 image. bitDepth is initially 24, i am changing it to 8, name of image shouldn't matter regarding my problem. – prateek k Jul 27 '19 at 19:48
  • The casting of pointers to int is very wrong – Antti Haapala -- Слава Україні Aug 04 '19 at 22:16

2 Answers2

2

Changing 24-bit to 8-bit bitmap is not as simple as changing the bitcount in header file. 24-bit bitmap doesn't have a color table. You have to build a color table for the 8-bit. Fortunately this is relatively easy for gray scale images.

Several values in bitmap header file need to be modified.

Then change the 24-bit to gray scale, and change to 8-bit bitmap. See bitmap file format for additional information. Also read about "padding" where the width of bitmap in bytes should always be a multiple of 4.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

#pragma pack(push, 2)
typedef struct {
    int16_t bfType;
    int32_t bfSize;
    int16_t bfReserved1;
    int16_t bfReserved2;
    int32_t bfOffBits;
} BITMAPFILEHEADER;

typedef struct {
    int32_t biSize;
    int32_t biWidth;
    int32_t biHeight;
    int16_t biPlanes;
    int16_t biBitCount;
    int32_t biCompression;
    int32_t biSizeImage;
    int32_t biXPelsPerMeter;
    int32_t biYPelsPerMeter;
    int32_t biClrUsed;
    int32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)

int main()
{
    FILE* fin = fopen("fin.bmp", "rb");
    FILE* fout = fopen("fout.bmp", "wb");
    if(!fin) { printf("fin error\n"); goto error; }
    if(!fout) { printf("fout error\n"); goto error; }

    BITMAPFILEHEADER bf;
    BITMAPINFOHEADER bi;
    fread(&bf, sizeof bf, 1, fin);
    fread(&bi, sizeof bi, 1, fin);

    if(sizeof bf != 14) { printf("Wrong pragma pack\n"); goto error; }
    if(sizeof bi != 40) { printf("Wrong pragma pack\n"); goto error; }
    if(bf.bfType != 0x4D42) { printf("not bmp, or not LE system\n"); goto error; }
    if(bi.biSize != 40) { printf("Can't handle this bitmap format\n"); goto error; }
    if(bi.biBitCount != 24) { printf("not 24-bit\n"); goto error; }

    int height = bi.biHeight;
    if(height < 0) height = -height; 

    //width in bytes:
    int src_wb = ((bi.biWidth * 24 + 31) / 32) * 4;
    int dst_wb = ((bi.biWidth * 8 + 31) / 32) * 4;
    int src_size = src_wb * height;
    int dst_size = dst_wb * height;

    //allocate for source and destination
    uint8_t *src = malloc(src_size);
    uint8_t *dst = malloc(dst_size);

    //read pixels
    fread(src, 1, src_size, fin);

    //make gray scale color-table
    uint8_t clr[1024] = { 0 };
    for(int i = 0; i < 256; i++)
        clr[i * 4 + 0] = clr[i * 4 + 1] = clr[i * 4 + 2] = (uint8_t)i;

    for(int y = height - 1; y >= 0; y--)
    {
        for(int x = 0; x < bi.biWidth; x++)
        {
            uint8_t blu = src[y * src_wb + x * 3 + 0];
            uint8_t grn = src[y * src_wb + x * 3 + 1];
            uint8_t red = src[y * src_wb + x * 3 + 2];
            uint8_t gry = (uint8_t)(.33 * red + .34 * grn + .33 * blu);
            dst[y * dst_wb + x] = gry; //this will match the index in color-table
        }
    }

    //modify bitmap headers
    bf.bfSize = 54 + 1024 + dst_size;
    bi.biBitCount = 8;
    bi.biSizeImage = dst_size;

    fwrite(&bf, sizeof bf, 1, fout);
    fwrite(&bi, sizeof bi, 1, fout);
    fwrite(clr, 1, 1024, fout);
    fwrite(dst, 1, dst_size, fout);
    free(src);
    free(dst);
error:
    fclose(fout);
    fclose(fin);
    return 0;
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
1
FILE* fOut = fopen("lena_gray.bmp", "w+");//ERROR
FILE* fOut = fopen("lena_gray.bmp", "wb");//TRUE