首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不会读写bmp

不会读写bmp
EN

Stack Overflow用户
提问于 2022-10-12 19:32:10
回答 1查看 79关注 0票数 0

我想把bmp文件读入一个结构,然后写回去,但是图像总是黑色的,标题是可以的。但是像素写错了。我比较了十六进制值,它们是相同的,直到标题完成。剩下的就不一样了,而且更短了。

代码语言:javascript
复制
typedef uint16_t ImageType;

typedef struct
{
    uint32_t size;
    uint16_t additionalFeature;
    uint16_t copy;
    uint32_t offset;

} ImageHeader;

typedef struct
{
    uint32_t headerSize;
    int width;
    int height;
    uint16_t colorSpaces;
    uint16_t bitsPerPixel;
    uint32_t compression;
    uint32_t size;
    int verticalResolution;
    int horizontalResolution;
    uint32_t totalColors;
    uint32_t importantColors;
} ImageMetadata;

typedef struct {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
} ImageColors;

typedef struct {
    ImageType type;
    ImageHeader header;
    ImageMetadata metadata;
    ImageColors **pixels;
} Image;

这是我的形象结构。目前,它只是一个24 bpp图像以后,我想改变它。但我的问题是,在我写完图片后,它没有正确地显示出来。头数据正常,但图像不正确。

行是正确的,只有列是如何复制和压缩的。

我的包括:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdbool.h>

我就是这样读取像素的

代码语言:javascript
复制
void readImage(char *filename, Image *image)
{
    FILE *imageFile = fopen(filename, "rb");
    if (!imageFile)
    {
        perror("ReadImageFileException");
        fclose(imageFile);
        exit(EXIT_FAILURE);
    }

    fread(&(image->type), sizeof(ImageType), 1, imageFile);
    //validate for correct image type
    if (image->type != BMP_IMAGE_TYPE)
    {
        fprintf(stderr, "%hu", image->type);
        perror("ReadImageTypeException");
        fclose(imageFile);
        exit(EXIT_FAILURE);
    }

    fread(&(image->header), sizeof(ImageHeader), 1, imageFile);
    fread(&(image->metadata), sizeof(ImageMetadata), 1, imageFile);
    
    // Allocate space for the pixels.
    image->pixels = malloc( image->metadata.height * sizeof(ImageColors *) );
    for(int i = 0; i < image->metadata.height; i++){
        image->pixels[i] = malloc(image->metadata.width * sizeof(ImageColors));
    }
    
    // Read in each pixel
    for (int i = 0; i < image->metadata.height; i++){
        for(int j = 0; j < image->metadata.width; j++){
            ImageColors px;
            if( fread(&px, sizeof(ImageColors), 1, imageFile) < 1 ) {
                printf("Error while reading bmp pixel.\n");
                return;
            }

            image->pixels[i][j] = px;
        }
    }

    fclose(imageFile);
}

我就是这样写的。

代码语言:javascript
复制
void writeImage(char *filename, Image *image)
{
    FILE *imageFile = fopen(filename, "wb+");
    if (!imageFile)
    {
        perror("WriteImageFileException");
        fclose(imageFile);
        exit(EXIT_FAILURE);
    }

    size_t typeWritten = fwrite(&(image->type), sizeof(image->type), 1, imageFile);
    if (typeWritten == 0)
    {
        perror("WriteImageTypeException");
        fclose(imageFile);
        exit(EXIT_FAILURE);
    }

    size_t headerWritten = fwrite(&(image->header), sizeof(image->header), 1, imageFile);
    if (headerWritten == 0)
    {
        perror("WriteImageHeaderException");
        fclose(imageFile);
        exit(EXIT_FAILURE);
    }

    size_t metadataWritten = fwrite(&(image->metadata), sizeof(image->metadata), 1, imageFile);
    if (metadataWritten == 0)
    {
        perror("WriteImageMetadataException");
        fclose(imageFile);
        exit(EXIT_FAILURE);
    }

    fseek(imageFile, image->header.offset, SEEK_SET);
    // Read in each pixel
    for (int i = 0; i < image->metadata.height; i++){
        for(int j = 0; j < image->metadata.width; j++){
            if( fwrite(&image->pixels[i][j], sizeof(ImageColors), 1, imageFile) < 1 ) {
                printf("Error while wr bmp pixel.\n");
                fclose(imageFile);
                exit(EXIT_FAILURE);
            }

        }
    }

    fclose(imageFile);
}

谢谢你已经进阶了。我只包括负责读和写bmp的方法,因为将来我想添加一些其他特性。

@tshiono谢谢,我在其中添加了行,但是如果我比较了十六进制文件,我可以看到图像仍然有一些不同:

输出图像看起来还是一样的。

我的主要功能如下:

代码语言:javascript
复制
int main(int argc, char **argv) {
    int ch;
    char *inputFile = "sample.bmp";
    char *outputFile = "image-copy.bmp";
    Image image;
    
    // try to use port and password from parameter
    while ( (ch = getopt_long_only(argc, argv, "", long_options, NULL)) != -1 ) {
        switch (ch) {
            case 'i':
                inputFile = optarg;
                break;
            case 'o':
                outputFile = optarg;
                break;
            default:
                break;
        }
    }
    
    printf("input File: %s\n", inputFile);
    
    readImage(inputFile, &image);
    writeImage(outputFile, &image);
    return 0;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-17 13:13:02

感谢您的更新。

IMHO,bmp文件格式是愚蠢设计的,其中包括一些导致意外行为的陷阱。您可能已经发现,如果我们将2字节的MagicNumber "BM“放在标头结构中,大多数编译器会自动地为边界对齐再放2字节。

另一个缺陷是,我们需要将一行(行)的字节计数保持为字对齐的4的倍数。然后,每一行的起始地址都位于单词边界,尽管我不确定它能使程序运行得更快。无论如何,如果行的字节数不是4的倍数(即宽度不是4的倍数),我们需要在每行的末尾放置额外的虚拟字节来进行调整。那么,请将您的读写功能修改为:

代码语言:javascript
复制
readImage:

    // Read in each pixel
    int padding = image->metadata.width % 4;                  // add this line
    for (int i = 0; i < image->metadata.height; i++){
        for(int j = 0; j < image->metadata.width; j++){
            ImageColors px;
            if( fread(&px, sizeof(ImageColors), 1, imageFile) < 1 ) {
                printf("Error while reading bmp pixel.\n");
                return;
            }
            image->pixels[i][j] = px;
        }
        for (int j = 0; j < padding; j++) getc(imageFile);    // add this line
    }

writeImage:

    // Read in each pixel
    int padding = image->metadata.width % 4;                  // add this line
    for (int i = 0; i < image->metadata.height; i++){
        for(int j = 0; j < image->metadata.width; j++){
            if( fwrite(&image->pixels[i][j], sizeof(ImageColors), 1, imageFile) < 1 ) {
                printf("Error while wr bmp pixel.\n");
                fclose(imageFile);
                exit(EXIT_FAILURE);
            }
        }
        for (int j = 0; j < padding; j++) putc(0, imageFile); // add this line
    }

其中变量padding是字节填充的计数,可以根据width的值进行计算。

更新

尝试添加这一行:

代码语言:javascript
复制
    fseek(imageFile, image->header.offset, SEEK_SET);

就在台词前:

代码语言:javascript
复制
    // Read in each pixel

readImage()中和在writeImage()中一样。

解释

bmp文件格式有几个版本,它们的头大小不同(这是另一个缺陷):

代码语言:javascript
复制
version     header.offset (starting address of pixel data)
BMP v2       54
BMP v3      122
BMP v4      138

虽然结构的定义是基于BMP v2的,但提供的bmp文件是在BMP v4中创建的。这样会导致头部数据与像素数据位置之间的冲突。这就是为什么您的文件比较显示了差异。

折叠图像文件的可查看性将取决于查看器软件。在我的环境中,查看图像是没有问题的。

理想情况下,我们应该准备几种类型的头结构,并根据版本使用其中的一种。实际上,扩展区域包含不太重要的数据,在大多数情况下我们可以跳过它们。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74047148

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档