使用结构读取PGM文件

我正在做一个项目,其中涉及编写用于读取/写入和编码/解码PGM文件的功能。我使用的结构具有读取PGM文件的功能。我对结构及其语法是非常陌生的,所以我只是想知道这部分代码是否可以将扫描的数据正确读取到我的结构中。

这是我的代码(C):

#include <stdio.h>
#include "image.h"

int **allocatePGM(int numCols,int numRows){
        int ** = malloc(sizeof(int *) * numRows);
        for (int i=0; i<numRows; i++)
            pixels[i] = malloc(sizeof(int) * numCols);
        return pixels;

}

ImagePGM *readPGM(char *filename,ImagePGM *pImagePGM){
    FILE *inFile = NULL
    char PGMcheck[5];
    int max_value = 0;
    unsigned int width = 0,height = 0;
    unsigned int i = 0;
    int pixeldata = 0;




    inFile = fopen(filename,"r");
    if (inFile == NULL)
    printf("File could not be opened\n");
    exit(1);

fgets(PGMcheck,sizeof(PGMcheck),inFile);
if (strcmp(version,"P5")) {
    fprintf(stderr,"Wrong file type!\n");
    exit(1);
}
    printf("This file does not contain the PGM indicator \"P2\"");
    exit(1);
    }




    fscanf(inFile,"%d",&width);
    fscanf(inFile,&height);
    fscanf(inFile,max_value);

    struct ImagePGM.pImagePGM
    pImagePGM.magic = PGMcheck;
    pImagePGM.width = width;
    pImagePGM.height = height;
    pImagePGM.max_value = max_value;

    pImagePGM->pixels = allocatePGM(pImagePGM->width,pImagePGM->height);
    if (pImagePGM->max_value > 255) {
        for (i = 0; i < height; ++i) {
            for (j = 0; j < width; ++j) {
                pImagePGM->pixels[i][j];
            }
        }
    }
    return pImagePGM;

}

我的头文件包含以下结构...

typedef struct _imagePGM {
 char magic[3]; // magic identifier,"P2" for PGM
 int width; // number of columns
 int height; // number of rows
 int max_value; // maximum grayscale intensity
 int **pixels; // the actual grayscale pixel data,a 2D array
} ImagePGM;

你们觉得还好吗?

gao1046811914 回答:使用结构读取PGM文件

根据我之前的评论,您遇到一些与处理Plain PGM File Format有关的问题,这些问题将阻止您成功读取文件。

首先,不能保证fgets(PGMcheck,sizeof(PGMcheck),inFile);会正确读取PGMcheck。 后跟会是"(blanks,TABs,CRs,LFs)",因此fgets读取的内容不仅限于 magic-number ,除非后面跟着一个{ {1}}-格式不保证。尽管'\n'通常是进行行输入的正确方法,但不能保证PGM格式按行格式化,因此您可以使用 formatted-input 函数,或者逐个字符的方法。

(您可以使用fgets(),但这将需要解析结果缓冲区,并保存超出 magic-number 范围的缓冲区的任何部分,以将其作为下一个内容的开始部分阅读)

您已使用fgets()而非!=更正了字符串比较的尝试,但仍必须将 magic-number strcmp进行比较以进行读取一个Plain-PGM格式的文​​件(最初包含您的问题),继续阅读 magic-number 到一个字符串中,但是使用格式化输入功能("P2")仅读取直到遇到第一个空格,无论该空格是什么。

最后,没有必要将幻数存储为fscanf结构的一部分。这是您尝试填充结构之前的 validate 的内容。是plain_pgm还是不是-无需存储。

出于可移植性,在读取图像文件时,最好使用精确宽度类型进行存储。有很多好处,但是最主要的是,无论是在x86_64还是在TI-MSP432芯片上运行,程序都可以正确运行。确切的宽度类型在"P2"中定义,打印和读取确切宽度类型的宏在stdint.h中提供。代替inttypes.h的是char,代替int8_t的是unsigned char,依此类推,其中数字值指定类型的确切字节数。>

这样,您的pgm结构可能类似于:

uint8_t

您的分配在很大程度上是正确的,但是重新安排以返回 pointer-to-pointer-to typedef struct { /* struct for plain pgm image */ uint32_t w,h; /* use exact width types for portable code */ uint16_t max; /* 16-bit max */ uint16_t **pixels; /* pointer-to-pointer for pixel values */ } plain_pgm; (相当于uint16_t像素值),您可以执行以下操作:

maximum gray value

您的阅读功能需要大量帮助。首先,您通常要打开并确认文件已打开以在调用函数中读取,并将打开的uint16_t **alloc_pgm_pixels (uint32_t w,uint32_t h) { uint16_t **pixels = NULL; /* allocate/validate height number of pointers */ if (!(pixels = malloc (h * sizeof *pixels))) { perror ("malloc-pixels"); return NULL; } /* allocate/validate width number of values per-pointer */ for (uint32_t i = 0; i < h; i++) if (!(pixels[i] = malloc (w * sizeof *pixels[i]))) { perror ("malloc-pixels[i]"); return NULL; } return pixels; /* return allocated pointers & storage */ } 指针作为参数而不是文件名传递给读取函数。 (如果无法在调用程序中打开文件,则无需首先进行函数调用)。进行此更改并传递指向您的结构的指针后,您的read函数可能类似于:

FILE *

注意:,此读取功能不考虑注释行,实现忽略注释行留给您。您可以另外调用{{1 }}在读取幻数,宽度,高度和最大灰度值的每个部分之前和之间,使用类似于int read_pgm (FILE *fp,plain_pgm *pgm) { char buf[RDBUF]; /* buffer for magic number */ uint32_t h = 0,w = 0; /* height/width counters */ if (fscanf (fp,"%s",buf) != 1) { /* read magic number */ fputs ("error: invalid format - magic\n",stderr); return 0; } if (strcmp (buf,MAGIC_PLN) != 0) { /* validate magic number */ fprintf (stderr,"error: invalid magic number '%s'.\n",buf); return 0; } /* read pgm width,height,max gray value */ if (fscanf (fp,"%" SCNu32 " %" SCNu32 " %" SCNu16,&pgm->w,&pgm->h,&pgm->max) != 3) { fputs ("error: invalid format,h,w,max or included comments.\n",stderr); return 0; } /* validate allocation of pointers and storage for pixel values */ if (!(pgm->pixels = alloc_pgm_pixels (pgm->w,pgm->h))) return 0; for (;;) { /* loop continually until image read */ if (fscanf (fp,"%" SCNu16,&pgm->pixels[h][w]) != 1) { fputs ("error: stream error or short-read.\n",stderr); return 0; } if (++w == pgm->w) w = 0,h++; if (h == pgm->h) break; } return 1; } 的内容来跳过任何数量的空格,并读取并包括下一个fscanf字符到行尾,或者只是在循环中使用" # %[^\n']"来查找下一个非空白字符,并检查它是否为'#',如果不是,则使用fgetc ,清除到行尾。)

将其完全放在一个示例中,您可以这样做:

'#'

使用/输出示例

使用示例apollonian_gasket.ascii.pgm,a 600 wide by 600 high image of an Apollonian gasket文件作为测试文件,您将获得:

ungetc

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不尝试访问内存或不在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,#include <stdio.h> #include <stdlib.h> #include <string.h> #include <inttypes.h> #define RDBUF 32 /* if you need a constant,#define one (or more) */ #define MAGIC_PLN "P2" typedef struct { /* struct for plain pgm image */ uint32_t w,h; /* use exact width types for portable code */ uint16_t max; /* 16-bit max */ uint16_t **pixels; /* pointer-to-pointer for pixel values */ } plain_pgm; uint16_t **alloc_pgm_pixels (uint32_t w,uint32_t h) { uint16_t **pixels = NULL; /* allocate/validate height number of pointers */ if (!(pixels = malloc (h * sizeof *pixels))) { perror ("malloc-pixels"); return NULL; } /* allocate/validate width number of values per-pointer */ for (uint32_t i = 0; i < h; i++) if (!(pixels[i] = malloc (w * sizeof *pixels[i]))) { perror ("malloc-pixels[i]"); return NULL; } return pixels; /* return allocated pointers & storage */ } int read_pgm (FILE *fp,h++; if (h == pgm->h) break; } return 1; } int main (int argc,char **argv) { plain_pgm pgm = { .w = 0 }; /* plain_pgm struct instance */ /* use filename provided as 1st argument (stdin by default) */ FILE *fp = argc > 1 ? fopen (argv[1],"r") : stdin; if (!fp) { /* validate file open for reading */ perror ("file open failed"); return 1; } if (!read_pgm (fp,&pgm)) { /* validate/allocate/read pgm file */ fputs ("error: read_pgm failed.\n",stderr); return 1; } if (fp != stdin) /* close file if not stdin */ fclose (fp); /* output success */ printf ("successful read of '%s'\n%" PRIu32 "x%" PRIu32 " pixel values.\n",argc > 1 ? argv[1] : "stdin",pgm.w,pgm.h); for (uint32_t i = 0; i < pgm.h; i++) /* free pixel storage */ free (pgm.pixels[i]); free (pgm.pixels); /* free pointers */ } 是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

$ ./bin/read_pgm_plain dat/apollonian_gasket.ascii.pgm
successful read of 'dat/apollonian_gasket.ascii.pgm'
600x600 pixel values.

始终确认已释放已分配的所有内存,并且没有内存错误。

查看所做的更改,如果您不明白为什么未完成某项操作,请发表评论,我们很乐意为您提供进一步的帮助。

,

我不知道PGM规范,但是当您在不同于您的平台上编译时,您会遇到三个常见的错误,这些错误会使代码无法正常工作:

  1. Endianess。您必须为您的数据格式精确定义它。 在您的情况下,int可能是低位优先的,将代码移植到高位优先的平台时必须考虑到这一点。 另请参见https://en.wikipedia.org/wiki/Endianness

  2. 结构包装。根据平台的不同,编译器可以填充结构中的字段以加快访问速度。您可能希望对结构使用诸如pragma pack之类的结构,否则,您的代码可能还会与其他编译器发生问题(即使假设使用相同的平台)。 另请参见http://www.catb.org/esr/structure-packing/#_structure_alignment_and_padding

  3. 使用固定宽度类型。例如。使用int64_t代替long等。 另请参见https://en.wikipedia.org/wiki/C_data_types#Fixed-width_integer_types

本文链接:https://www.f2er.com/3148355.html

大家都在问