使用 C 复制从 tif 文件生成的 8 位灰度 bmp 文件

Copy an 8 bit greyscale bmp file generated from a tif file using C

以下代码是我试图从位图文件中提取图像特征的程序的一部分。我需要提取有关图像的信息(仅宽度和高度)并复制它。这些图像的分辨率为 2048X 2168,8 位灰度。

#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]='[=10=]';
    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);
}

不幸的是,我的原始图像文件是 tif 格式。所以当我转换它们时,我无法保留一些 header 文件信息。一个在线工具可以让我保留 bits-per-pixel(8 位),但随后我丢失了 biSizeImage(变为 0)。另一个转换工具正确地得到了我的大小,但图像比特率变为 24。(link1, link2)

我的原始 tif 图片示例 (temporary_link1) and the corresponding BMP image (temporary_link2)

当我 运行 以上时,我能够正确复制 header 信息但不能复制像素数据。如果可以使用其他方法复制(例如通过比较 EOF),这可能是一个不错的选择。我不确定计算填充和书写方向。

从将 tif 格式正确转换为我所需的 bmp 格式开始,我们将不胜感激。显然我是图像格式化和压缩的新手。

输出:

 width pixels=2048
 height pixels=2168
 bits per pixel=8
 image data size=0
 number colors=0

getImageInfo 不正确。整数值应该以 little-endian 格式保存。应该这样读:

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 不保证设置为正确的值。这应该大致等于 width * height * bit_pixel。使用这个公式。

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

此外,8 位图像包含大小为 1024 的颜色 table。此 table 紧接在 54 字节 header 之后和像素数据之前。阅读如下:

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;
}