0
votes

The following code is part of my program that tries to extract image features from a bitmap file. I need to extract information (width and height only) about the image and also make copies of it. These images are of 2048X 2168 resolution, 8-bit greyscale.

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

#define NAMELENGTH 301

long getImageInfo(FILE* Finput, long offset, int nchars){
   unsigned char *ptrChar;
   unsigned char dummy;
   long value=0L;
   int i;

   dummy='0';
   ptrChar=&dummy;

   fseek(Finput,offset,SEEK_SET);
   for (i=1; i<=nchars; i++){
     fread(ptrChar,sizeof(char),1,Finput);
     value += ((long) ((*ptrChar)*pow(256,(i-1))));
    }
   return(value);
}

void copyImageInfo(FILE* Finput, FILE* Foutput){
    unsigned char *ptrChar;
    unsigned char dummy;
    long offset;
    int i;

    dummy='0';
    ptrChar=&dummy;
    int byte_size = ((int) getImageInfo(Finput,34,4));

    /* copying header: signature, image width & height, number bit/pixel, image size, number of colors */
    offset=0L;
    fseek(Finput,offset,SEEK_SET);
    fseek(Foutput,offset,SEEK_SET);
    for (i=0; i<=54; i++){
        fread(ptrChar,sizeof(char),1,Finput);
        fwrite(ptrChar,sizeof(char),1,Foutput);
    }

    /* copying pixel data */
    /* This part of the code may not be complete */
    offset=54L;
    fseek(Finput,offset,SEEK_SET);
    fseek(Foutput,offset,SEEK_SET);
    for (i=0; i<=byte_size; i++){
        fread(ptrChar,sizeof(char),1,Finput);
        fwrite(ptrChar,sizeof(char),1,Foutput);
    }
}

int main(int argc, char *argv[]){
    FILE* Finput;
    FILE* Foutput;

    char input_name[NAMELENGTH];
    char output_name[NAMELENGTH];
    char job_name[NAMELENGTH];
    char history_name[NAMELENGTH];

    unsigned char *ptrChar;
    int pix_width,pix_height;
    int bit_pixel,byte_size,ncolors;

    strcpy(input_name,argv[1]); /* first argument contains path to the image file */
    strcpy(job_name, argv[1]);
    job_name[strlen(job_name)-4]='\0';
    sprintf(history_name,"%s%s",job_name,"_hist.txt"); /* history file stores image header information */

    if( (Finput=fopen(input_name,"r"))==NULL ){
        fprintf(stderr,"\n ERROR: file %s could not be opened for reading\n",input_name);
        exit(-1);
    }
    else{
        fseek(Finput,0L,SEEK_END);
        if (getImageInfo(Finput,0,2)!=19778) fprintf(stdout,"\n WARNING: wrong BMP signature!\n");
        pix_width    = ((int) getImageInfo(Finput,18,4));
        pix_height   = ((int) getImageInfo(Finput,22,4));
        bit_pixel    = ((int) getImageInfo(Finput,28,2));
        byte_size    = ((int) getImageInfo(Finput,34,4));
        ncolors      = ((int) getImageInfo(Finput,46,4));

        fprintf(stdout,"\n width pixels=%d",pix_width);
        fprintf(stdout,"\n height pixels=%d",pix_height);
        fprintf(stdout,"\n bits per pixel=%d",bit_pixel);
        fprintf(stdout,"\n image data size=%d",byte_size);
        fprintf(stdout,"\n number colors=%d\n",ncolors);

        /* history file */
        if ( (Foutput=fopen(history_name,"a"))==NULL ){
            fprintf(stderr,"\n ERROR: file %s could not be opened for appending\n",history_name);
            exit(-1);
        }
        else{
            fprintf(Foutput,"Path to Image: %s ",input_name);
            fprintf(Foutput,"\n\t 18 - biWidth - width pixels=%d",pix_width);
            fprintf(Foutput,"\n\t 22 - biHeight - height pixels=%d",pix_height);
            fprintf(Foutput,"\n\t 28 - biBitCount - bits per pixel=%d",bit_pixel);
            fprintf(Foutput,"\n\t 34 - biSizeImage - image data size=%d",byte_size);
            fprintf(Foutput,"\n\t 46 - biClrUsed - number colors=%d\n",ncolors);
            fclose(Foutput);

        }

        sprintf(output_name,"%s%s",job_name,"_copy.bmp");
        if ( (Foutput=fopen(output_name,"wb"))==NULL ){
            fprintf(stderr,"\n ERROR: file %s could not be opened for writing\n",output_name);
            exit(-1);
        }
        else{
            copyImageInfo(Finput,Foutput);
            fclose(Foutput);
        }
    }
    fclose(Finput);
}

Unfortunately, my original image files are in tif format. So when I convert them, I am unable to preserve some of the header file information. One online tool lets me preserve the bits-per-pixel (8 bit) but then I lose the biSizeImage (which turns to 0). Another conversion tool gets me the size correctly but image bitrate changes to 24. (link1, link2)

An example of my original tif image (temporary_link1) and the corresponding BMP image (temporary_link2)

When I run the above, I am able to copy the header information correctly but not the pixel data. If this can be copied using some other method (like for example by comparing the EOF), it may be a good alternative. I am unsure about calculating the padding and also the direction of writing.

Any guidance would be appreciated starting with the correct conversion of tif format to my required bmp format. Evidently I am new to image formatting and compression.

Output:

 width pixels=2048
 height pixels=2168
 bits per pixel=8
 image data size=0
 number colors=0
1
It doesn't help a bit that you don't reveal the tool or that there is no sample file ..Antti Haapala
ImageMagick probably has what you are looking for?Neil
Show your program's output.Barmak Shemirani
I did not know an easy way to host my input tif image without compression. I will update the names of my online tool that I used for conversion and also the output.abk

1 Answers

1
votes

getImageInfo is incorrect. The integer values are supposed to be saved in little-endian format. It should be read as follows:

unsigned int getImageInfo(FILE *fin, long offset, int nchars)
{
    fseek(fin, offset, SEEK_SET);
    unsigned int value = 0;
    for(int i = 0; i < nchars; i++)
    {
        unsigned char dummy = '0';
        fread((char*)&dummy, sizeof(char), 1, fin);
        value += dummy << (8 * i);
    }
    return value;
}

byte_size is not guaranteed to be set to the correct value. This should be roughly equal to width * height * bit_pixel. Use this formula.

byte_size = ((pix_width * bit_pixel + 31) / 32) * 4 * pix_height;

Moreover, 8-bit image includes color table of size 1024. This table is immediately after 54 byte header, and before the pixel data. Read it as follows:

unsigned int getImageInfo(FILE *fin, long offset, int nchars)
{
    fseek(fin, offset, SEEK_SET);
    unsigned int value = 0;
    for(int i = 0; i < nchars; i++)
    {
        unsigned char dummy = '0';
        fread((char*)&dummy, sizeof(char), 1, fin);
        value += dummy << (8 * i);
    }
    return value;
}

void copyImageInfo(FILE* Finput, FILE* Foutput) 
{
    int w = getImageInfo(Finput, 18, 4);
    int h = getImageInfo(Finput, 22, 4);
    int bit_pixel = getImageInfo(Finput, 28, 2);
    int byte_size = ((w * bit_pixel + 31) / 32) * 4 * h;
    int ncolors = getImageInfo(Finput, 46, 4);

    fprintf(stdout, "\n width pixels=%d", w);
    fprintf(stdout, "\n height pixels=%d", h);
    fprintf(stdout, "\n bits per pixel=%d", bit_pixel);
    fprintf(stdout, "\n image data size=%d", byte_size);
    fprintf(stdout, "\n number colors=%d\n", ncolors);

    char header[54]; //bitmap header
    char *pixels = malloc(byte_size);  //pixel data

    fseek(Finput, 0, SEEK_SET);
    fseek(Foutput, 0, SEEK_SET);

    fread(header, sizeof(header), 1, Finput);
    fwrite(header, sizeof(header), 1, Foutput);

    if(bit_pixel <= 8)
    {
        //color table
        int colors_size = 4 * (1 << bit_pixel);
        char *colors = malloc(colors_size);
        fread(colors, 1, colors_size, Finput);
        fwrite(colors, 1, colors_size, Foutput);
        free(colors);
    }

    fread(pixels, 1, byte_size, Finput);
    fwrite(pixels, 1, byte_size, Foutput);

    free(pixels);
}

int main(void) 
{
    char input_name[] = "input.bmp";
    char output_name[] = "output.bmp";

    FILE *Finput = fopen(input_name, "rb");
    if(!Finput)
    {
        fprintf(stderr, "\n ERROR: file %s\n", input_name);
        exit(-1);
    }

    FILE *Foutput = fopen(output_name, "wb");
    if(!Foutput)
    {
        fprintf(stderr, "\n ERROR: file %s\n", input_name);
        fclose(Finput);
        exit(-1);
    }

    if(getImageInfo(Finput, 0, 2) != 19778) 
        fprintf(stdout, "\n WARNING: wrong BMP signature!\n");
    else
        copyImageInfo(Finput, Foutput);
    fclose(Foutput);
    fclose(Finput);
    return 0;
}