从文件中读取图案并在C语言中创建一个bmp图像

5

我想使用C语言读取文本文件。以下是文件内容:

enter image description here

你会注意到文件中的文本内容有一些模式。 0表示空,9表示黑色。因此,从0到9有一种着色方案。
我必须创建一个位图图像,并根据模式中的值调整颜色,使用0-256的颜色方案进行调整。 最终输出如下所示。

enter image description here

现在,需要注意的是文本文件中的图案与最终输出的位图文件相反(不一定)。位图图像中颜色的深浅根据文本内容模式中的值而定。

请问有人能告诉我如何在C语言中实现这一点。

我能够创建BMP文件,但无法按照文本文件中的图案进行创建。

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

int main()
{

char bitmap[1900];

    // -- FILE HEADER -- //

    // bitmap signature
    bitmap[0] = 0x42;
    bitmap[1] = 0x4d;

    // file size
    bitmap[2] = 58; // 40 + 14 + 12
    bitmap[3] = 0;
    bitmap[4] = 0;
    bitmap[5] = 0;
    int i=0;
    // reserved field (in hex. 00 00 00 00)
    for(i = 6; i < 10; i++) bitmap[i] = 0;

    // offset of pixel data inside the image
    for(i = 10; i < 14; i++) bitmap[i] = 0;

    // -- BITMAP HEADER -- //

    // header size
    bitmap[14] = 40;
    for(i = 15; i < 18; i++) bitmap[i] = 0;

    // width of the image
    bitmap[18] = 4;
    for(i = 19; i < 22; i++) bitmap[i] = 0;

    // height of the image
    bitmap[22] = 1;
    for(i = 23; i < 26; i++) bitmap[i] = 0;

    // reserved field
    bitmap[26] = 1;
    bitmap[27] = 0;

    // number of bits per pixel
    bitmap[28] = 24; // 3 byte
    bitmap[29] = 0;

    // compression method (no compression here)
    for(i = 30; i < 34; i++) bitmap[i] = 0;

    // size of pixel data
    bitmap[34] = 12; // 12 bits => 4 pixels
    bitmap[35] = 0;
    bitmap[36] = 0;
    bitmap[37] = 0;

    // horizontal resolution of the image - pixels per meter (2835)
    bitmap[38] = 0;
    bitmap[39] = 0;
    bitmap[40] = 0b00110000;
    bitmap[41] = 0b10110001;

    // vertical resolution of the image - pixels per meter (2835)
    bitmap[42] = 0;
    bitmap[43] = 0;
    bitmap[44] = 0b00110000;
    bitmap[45] = 0b10110001;

    // color pallette information
    for(i = 46; i < 50; i++) bitmap[i] = 0;

    // number of important colors
    for(i = 50; i < 54; i++) bitmap[i] = 0;

    // -- PIXEL DATA -- //
    for(i = 54; i < 66; i++) bitmap[i] = 255;


    FILE *file;
    file = fopen("bitmap.bmp", "w+");
    for(i = 0; i < 66; i++)
    {
        fputc(bitmap[i], file);
    }
    fclose(file);
    return 0;
}

6
太多的神奇数字了……为了你自己的心理健康,请使用命名常量! - Lundin
2
请使用 unsigned char bitmap[1900] - Weather Vane
2
为什么你创建了一个4x1的位图,而你需要的是24x15的位图?你需要在数组中的每个位置编写3个字节(每个字节都相同),以表示灰度像素数据,这个数组实际上是array[height][width][3] - Weather Vane
1
那是因为图像大小信息大于头文件中可以容纳的1个字节吗?该字段为uint32_t - Weather Vane
1
无符号整数 imagesize 等于高度乘以宽度乘以3;对于每个 i 在0到3之间,执行以下操作:bitmap[34+i]等于imagesize与255的按位与结果,然后将imagesize右移8位。需要注意的是,当图像宽度是4的倍数时(在这种情况下是这样),数组宽度才等于图像宽度,因为数组宽度(其“步幅”)必须始终是4的倍数。 - Weather Vane
显示剩余2条评论
2个回答

4

这是一段可运行的代码。我没有检查一些错误情况(例如输入文件格式不良)。我添加了一些注释,尽管代码本身并不难理解。

#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <ctype.h>

#define BMP_ASSERT(condition, message) \
    do { \
        if (! (condition)) { \
            fprintf(stderr, __FILE__ ":%d: %s: Assertion `" #condition \
                "` failed.\n\t: %s\n", __LINE__, __func__, message); \
            exit(EXIT_FAILURE); \
        } \
    } while (false)

#define BMP_COLORMAP_SIZE   10

////////////////////////////////////////////////////////////////////////////////
// STRUCTS

typedef struct __attribute__((__packed__))
bmp_color 
{
    uint8_t             r,
                        g,
                        b;
    uint8_t             :8;
}                       bmp_color_t;

typedef struct __attribute__((__packed__))
bmp_header_infos
{
    uint32_t const      info_size;
    uint32_t            width;
    uint32_t            height;
    uint16_t const      planes;
    uint16_t const      bpp;
    uint32_t const      compression;
    uint32_t            img_size;
    uint32_t const      horz_resolution;
    uint32_t const      vert_resolution;
    uint32_t const      n_colors;
    uint32_t const      n_important_colors;
}                       bmp_header_infos_t;

typedef struct __attribute__((__packed__))
bmp_header
{
    /* Header */
    char const          signature[2];
    uint32_t            file_size;
    uint32_t const      :32; // reserved
    uint32_t const      img_offset;
    /* Infos */
    bmp_header_infos_t  infos;
    /* Color map */
    bmp_color_t         colormap[BMP_COLORMAP_SIZE];
}                       bmp_header_t;

typedef struct          bmp_file_datas
{
    size_t              width;
    size_t              height;
    /* Bit map */
    uint8_t             *bitmap;
}                       bmp_file_datas_t;


////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS

/* Give a header with the right magic values */
bmp_header_t *
bmp_get_header(void)
{
    static bmp_header_t _header = {
        {'B', 'M'},
        0u,
        sizeof(_header),
        {   // struct info
            sizeof(_header.infos),
            0u, 0u, // width, height
            1u,     // planes
            8u,     // bpp
            0u,     // no compression
            0u,     // img size
            0u, 0u, // resolution
            BMP_COLORMAP_SIZE,
            BMP_COLORMAP_SIZE,      // important colors
        },
        {{0u}}
    };
    bmp_header_t        *header = malloc(sizeof(_header));
    size_t              i,
                        color;

    assert(header != NULL);
    memcpy(header, &_header, sizeof(*header));
    // setting the scale of greys
    for (i = 0 ; i < BMP_COLORMAP_SIZE ; ++i)
    {
        color = (i * 255) / BMP_COLORMAP_SIZE;
        header->colormap[i] = (bmp_color_t){color, color, color};
    }
    return header;
}

/* Take all the file content and store it in a buffer */
char *
get_file_content(char const *filename)
{
    FILE        *file_handler = fopen(filename, "r");
    size_t      file_len;
    char        *buff;

    BMP_ASSERT(file_handler != NULL, strerror(errno));
    fseek(file_handler, 0, SEEK_END);
    file_len = ftell(file_handler);
    fseek(file_handler, 0, SEEK_SET);
    buff = malloc(file_len + 1);
    assert(buff != NULL);
    fread(buff, file_len, 1, file_handler);
    buff[file_len] = '\0';
    fclose(file_handler);
    return buff;
}

/* Get the greatest multiple of 4 that is >= size */
static inline size_t
multiple_of_four(size_t size)
{
    while (size % 4)
        ++size;
    return size;
}

/* Get the informations from buffer: size of line, number of lines */
void
get_file_infos(char *buff, bmp_header_t *header, bmp_file_datas_t *datas)
{
    /* width & height */
    header->infos.width =  strchr(buff, '\n') - buff;
    header->infos.height = strlen(buff) / (header->infos.width + 1);
                                            // + 1 for the terminating '\n'
    datas->width = multiple_of_four(header->infos.width);
    datas->height = header->infos.height;
printf("File size: %u, %u\n", header->infos.width, header->infos.height);
    /* image size & bitmap allocation */
    header->infos.img_size = datas->width * datas->height;
    datas->bitmap = malloc(header->infos.img_size * sizeof(*datas->bitmap));
    assert(datas->bitmap != NULL);
    header->file_size = header->img_offset + header->infos.img_size;
}

/* Take the informations from the buffer and store them in the bitmap */
void
write_bitmap(char *buff, bmp_file_datas_t *datas)
{
    size_t          ibuff,
                    iline = 0,
                    ibitmap = 0;

    for (ibuff = 0 ; buff[ibuff] ; ++ibuff)
    {
        if (isdigit(buff[ibuff]))
        {
            datas->bitmap[ibitmap] = BMP_COLORMAP_SIZE - 1 - (buff[ibuff] - '0');
            ++ibitmap;
        }
        else if (buff[ibuff] == '\n')
        {
            ++iline;
            ibitmap = iline * datas->width;
        }
    }
}

/* Write the datas in the file: the header and the bitmap */
void
write_in_file(bmp_header_t *header,
              bmp_file_datas_t *datas,
              char const *filename)
{
    FILE        *file_handler = fopen(filename, "w");

    BMP_ASSERT(file_handler != NULL, strerror(errno));
    fwrite(header, sizeof(*header), 1, file_handler);
    fwrite(datas->bitmap, header->infos.img_size, 1, file_handler);
    fclose(file_handler);
}


int main(int argc, char **argv)
{
    char                *buff;
    bmp_header_t        *header;
    bmp_file_datas_t    datas;

    if (argc != 3)
    {
        fprintf(stderr, "Usage: %s <input filename> <output filename>\n",
                                argv[0]);
        return EXIT_FAILURE;
    }
    buff = get_file_content(argv[1]);
    header = bmp_get_header();
    get_file_infos(buff, header, &datas);
    write_bitmap(buff, &datas);
    write_in_file(header, &datas, argv[2]);
    free(buff), free(header), free(datas.bitmap);
    return EXIT_SUCCESS;
}

1
在我的Debian上,使用gcc -std=c11 -Wall -Wextra -pedantic可以正常工作(包括gcc-5gcc-6)。所以它应该能够适用于其他标准编译器。请更具体地描述错误信息。 - Boiethios
1
你还感兴趣吗?你没有回答我的问题:你遇到了什么样的错误?没有详细信息我无法提供帮助。 - Boiethios
先生 @Boiethios,我已经修复了错误。我提供了输入文件,即包含模式的文本文件和输出文件,即bmp文件。但它只是执行并关闭。什么也没有发生。 - Muhammad Hashim Shafiq
1
@MuhammadHashimShafiq 我猜您是用 C++ 编译器来编译的,因为这是一个 C++ 错误。C 可以从 void * 隐式转换到每个指针类型。请用 C 编译器编译 C 代码,效果会更好。 - Boiethios
1
@MuhammadHashimShafiq 如果您写C++代码,请用C ++标签替换C标签。 - Boiethios
显示剩余5条评论

2
实现所需功能的一个简单方法如下:(这并不是真正的C语言,但毕竟这是你的作业而不是我的)
WIDTH = 0, HEIGHT = 0
if(LINE=READ_LINE(fin))
  WIDTH = strlen(LINE)
  ++HEIGHT
else
  ERROR!!!
PUSH(LINE)
while(LINE=READ_LINE(fin))
  if(WIDTH != strlen(LINE))
    ERROR!!!
  else
    ++HEIGHT
    PUSH(LINE)
WRITE_BMP_HEADER(WIDTH, HEIGHT)
for(h = 0; h < HEIGHT; ++h)
  LINE = POP()
  for(w = 0; w < WIDTH; ++w)
    COLOR = (LINE[w] - '0') * 255 / 9;
    WRITE_BMP_COLOR(COLOR)
  WRITE_BMP_PADDING(W)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接