使用C将ppm文件从P3转换为P6

使用C将ppm文件从P3转换为P6,c,pointers,segmentation-fault,ppm,C,Pointers,Segmentation Fault,Ppm,我试图编写一个程序,用C语言将p3 PPM文件转换为P6。但我遇到了两个问题。1,我的代码中有一个分段错误。第二,头文件未正确读入转换后的p6文件。这是我的头文件 #ifndef PPM_UTILS #define PPM_UTILS #include "stdio.h" #include "stdlib.h" #include "string.h" // First meaningful line of the PPM file typedef struct header { ch

我试图编写一个程序,用C语言将p3 PPM文件转换为P6。但我遇到了两个问题。1,我的代码中有一个分段错误。第二,头文件未正确读入转换后的p6文件。这是我的头文件

#ifndef PPM_UTILS
#define PPM_UTILS

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

// First meaningful line of the PPM file
typedef struct header {
    char MAGIC_NUMBER[3];
    unsigned int HEIGHT, WIDTH, MAX_COLOR;
} header_t;

// Represents an RGB pixel with integer values between 0-255
typedef struct pixel {
    unsigned int R, G, B;
} pixel_t;

// PPM Image representation
typedef struct image {
    header_t header;
    pixel_t* pixels;
} image_t;

header_t read_header(FILE* image_file);
image_t* read_ppm(FILE* image_file);
image_t* read_p6(FILE* image_file, header_t header);
image_t* read_p3(FILE* image_file, header_t header);

void write_header(FILE* out_file, header_t header);
void write_p6(FILE* out_file, image_t* image);
void write_p3(FILE* out_file, image_t* image);

#endif
我把代码的主要部分分为两个文件,在编译器中合并

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

header_t read_header(FILE* image_file)
{

    header_t header;

    fscanf(image_file, "%c %d %d %d",header.MAGIC_NUMBER, &header.WIDTH, &header.HEIGHT, &header.MAX_COLOR);
    return header;
}

void write_header(FILE* out_file, header_t header)
{

    fprintf(out_file, "%c %d %d %d", *header.MAGIC_NUMBER,header.HEIGHT,header.WIDTH,header.MAX_COLOR);
}

image_t* read_ppm(FILE* image_file)
{

    header_t header = read_header(image_file);

    image_t* image = NULL;
    if (strcmp("P3", header.MAGIC_NUMBER) == 0)
    {
        image = read_p3(image_file, header);
    }
    else if (strcmp("P6", header.MAGIC_NUMBER) == 0)
    {
        image = read_p6(image_file, header);
    }
    return image;
}

image_t* read_p6(FILE* image_file, header_t header)
{

    int size;
    size = header.HEIGHT * header.WIDTH;
    image_t* image = (image_t*) malloc (sizeof(image_t));
    image -> header = header;
    image -> pixels = (pixel_t*) malloc (sizeof(pixel_t)* size);
    char r,g,b;
    r = 0;
    g = 0;
    b = 0;
    int i;
    for (i=0;i<size;i++){
        fscanf(image_file, "%c%c%c", &r, &g, &b);
        image -> pixels -> R = (int) r;
        image -> pixels -> G = (int) g;
        image -> pixels -> B = (int) b;
    }
    return image;
}
image_t* read_p3(FILE* image_file, header_t header)
{
    int size;
    size = header.HEIGHT * header.WIDTH;
    image_t* image = (image_t*) malloc (sizeof(image_t));
    image -> header = header;
    image -> pixels = (pixel_t*) malloc (sizeof(pixel_t)* size);
    int r,g,b;
    r = 0;
    g = 0;
    b = 0;
    int i;
    for (i=0;i<size;i++){
        fscanf(image_file, "%d %d %d ", &r, &g, &b);
        image -> pixels -> R = (int) r;
        image -> pixels -> G = (int) g;
        image -> pixels -> B = (int) b;
    }
    return image;

}

void write_p6(FILE* out_file, image_t* image)
{
    header_t header = image -> header;
    header.MAGIC_NUMBER[1]=6;
    write_header(out_file, header);
    int size = header.HEIGHT * header.WIDTH;
    int i;
    for (i=0;i<size;i++){
        fprintf(out_file,"%c%c%c", (char) image->pixels->R, (char) image->pixels->G, (char) image->pixels->B);
    }

}
void write_p3(FILE* out_file, image_t* image)
{
    header_t header = image -> header;
    header.MAGIC_NUMBER[1]=3;
    write_header(out_file, header);
    int size = header.HEIGHT * header.WIDTH;
    int i;
    for (i=0;i<size;i++){
        fprintf(out_file,"%d %d %d ",image->pixels->R,image->pixels->G,image->pixels->B);

    }

}
#包括
#包括“ppm_utils.h”
头\u t读取头(文件*图像\u文件)
{
标题(t)标题;;
fscanf(图像文件,“%c%d%d%d”、header.MAGIC\u编号、header.WIDTH、header.HEIGHT和header.MAX\u颜色);
返回头;
}
无效写入头(文件*输出头文件,头文件)
{
fprintf(输出文件,“%c%d%d%d”,*页眉.幻数,页眉.高度,页眉.宽度,页眉.最大颜色);
}
图像*读取ppm(文件*图像文件)
{
header\u t header=读取头(图像文件);
image_t*image=NULL;
if(strcmp(“P3”,标题幻数)==0)
{
图像=读取p3(图像文件,标题);
}
else if(strcmp(“P6”,标题幻数)==0)
{
image=read_p6(图像文件,标题);
}
返回图像;
}
图像\u t*读取\u p6(文件*图像\u文件,头\u t头)
{
整数大小;
尺寸=收割台高度*收割台宽度;
image_t*image=(image_t*)malloc(sizeof(image_t));
图像->标题=标题;
图像->像素=(像素*t*)malloc(大小f(像素*t)*大小);
字符r,g,b;
r=0;
g=0;
b=0;
int i;
对于(i=0;i像素->R=(int)R;
图像->像素->G=(int)G;
图像->像素->B=(int)B;
}
返回图像;
}
图像\u t*读取\u p3(文件*图像\u文件,头\u t头)
{
整数大小;
尺寸=收割台高度*收割台宽度;
image_t*image=(image_t*)malloc(sizeof(image_t));
图像->标题=标题;
图像->像素=(像素*t*)malloc(大小f(像素*t)*大小);
int r,g,b;
r=0;
g=0;
b=0;
int i;
对于(i=0;i像素->R=(int)R;
图像->像素->G=(int)G;
图像->像素->B=(int)B;
}
返回图像;
}
无效写入p6(文件*输出文件,图像*图像)
{
页眉\u t页眉=图像->页眉;
header.MAGIC_编号[1]=6;
写入_头(out_文件,头);
int size=header.HEIGHT*header.WIDTH;
int i;
对于(i=0;i像素->R,(字符)图像->像素->G,(字符)图像->像素->B);
}
}
无效写入p3(文件*输出文件,图像*图像)
{
页眉\u t页眉=图像->页眉;
header.MAGIC_编号[1]=3;
写入_头(out_文件,头);
int size=header.HEIGHT*header.WIDTH;
int i;
对于(i=0;i像素->R,图像->像素->G,图像->像素->B);
}
}

#包括
#包括“ppm_utils.h”
int main(int argc,char*argv[])
{
如果(argc!=3)
{
printf(“程序需要两个参数”);
返回1;
}
文件*fr;
fr=fopen(argv[1],“r”);
如果(fr==NULL)
{
printf(“开户失败”);
}
文件*fw;
fw=fopen(argv[2],“w”);
如果(fw==NULL)
{
printf(“开户失败”);
}
图像=读取ppm(fr);
写入p6(fw,图像);
免费(图像);
自由(图像->像素);
fclose(fr);
fclose(fw);
返回0;
}

你们有什么解决办法吗?

在读取标题时,需要指定要读取的幻数的字符数:

fscanf(image_file, "%2c %d %d %d",header.MAGIC_NUMBER, &header.WIDTH, &header.HEIGHT, &header.MAX_COLOR);
要与strcmp()一起使用,数组中的最后一个字节必须设置为0:

header.MAGIC_NUMBER[2] = 0;
写入标题时,需要将其作为字符串写入:

fprintf(out_file, "%s %d %d %d", header.MAGIC_NUMBER,header.HEIGHT,header.WIDTH,header.MAX_COLOR);
for(i=0;i像素->R=(int)R;
图像->像素->G=(int)G;
图像->像素->B=(int)B;
}

使用
fscanf()
,以解析PPM格式头的方式重复分配相同的内存,这是行不通的

正如我在本文第一点中所解释的,PPM头可能包含注释

请注意,此答案同样适用于所有PNM格式,P1至P6(分别为PBM、PGM、PPM、PBM、PGM、PPM和PAM)。它们的标题格式相同,但标题中列出的非负整数值的数量除外(PBM为2,PGM和PPM为3,PAM为4)

(P7,Portable AnyMap或PAM,有一个额外的参数tupletype,它是一个大写字符串。虽然它是一种非常好的通用图像格式,但它的实现频率低于其他参数。如果您自己为tupletype添加解析,下面的方法也适用于它。)

似乎许多人在构造“读取一个PNM格式头值”时遇到了困难我见过太多的例子,这些例子适用于一些但不是所有的PNM格式文件,损害了不可操作性,给开发人员和用户带来了不必要的麻烦,所以我认为有必要用一个例子来说明如何正确地处理这些问题

读取任何PNM格式的头的正确操作很简单:使用
fgetc()
读取头的固定部分(
p
、格式数字和以下空白字符,
'\t'
'\n'
'\v'
'\f'
'\r'
、或
'/code>之一),以及一个帮助函数来解析非负整数值。(对于P1和P4格式,只有两个值,以像素为单位的宽度和高度,按顺序排列;对于P2、P3、P5和P6格式,有三个值:宽度、高度和maxval,其中maxval是文件中使用的最大组件值。)

如果你真的关心学习,我建议你去阅读上面链接到的I中的伪代码,并尝试首先实现自己的伪代码。如果你陷入困境,你可以与下面已知的工作版本进行比较


为简单起见,让我们使用一个辅助函数,
pnm\u is\u whitespace(character)
,如果
character
是以下空白字符之一,则该函数返回true(非零):

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

static inline int pnm_is_whitespace(const int c)
{
    return (c == '\t' || c == '\n' || c == '\v' ||
            c == '\f' || c == '\r' || c == ' ');
}
上述函数解析输入的方式与
%d
scanf()
函数系列e中的解析方式非常相似
for (i=0;i<size;i++){
    fscanf(image_file, "%c%c%c", &r, &g, &b);
    image -> pixels -> R = (int) r;
    image -> pixels -> G = (int) g;
    image -> pixels -> B = (int) b;
}
#include <stdlib.h>
#include <stdio.h>

static inline int pnm_is_whitespace(const int c)
{
    return (c == '\t' || c == '\n' || c == '\v' ||
            c == '\f' || c == '\r' || c == ' ');
}
static inline int pnm_header_value(FILE *src)
{
    int  c;

    /* Skip leading whitespace and comments. */
    c = fgetc(src);
    while (1) {
        if (c == EOF) {
            /* File/stream ends before the value. */
            return -1;
        } else
        if (c == '#') {
            /* Comment. Skip the rest of the line. */
            do {
                c = fgetc(src);
            } while (c != EOF && c != '\r' && c != '\n');
        } else
        if (pnm_is_whitespace(c)) {
            /* Skip whitespace. */
            c = fgetc(src);
        } else
            break;
    }

    /* Parse the nonnegative integer decimal number. */
    if (c >= '0' && c <= '9') {
        int  result = (c - '0');

        c = fgetc(src);
        while (c >= '0' && c <= '9') {
            const int  old = result;

            /* Add digit to number. */
            result = 10*result + (c - '0');

            /* Overflow? */
            if (result < old)
                return -1;

            /* Next digit. */
            c = fgetc(src);
        }

        /* Do not consume the separator. */
        if (c != EOF)
            ungetc(c, src);

        /* Success. */
        return result;
    }

    /* Invalid character. */
    return -1;
}
enum {
    PNM_P1      = 1, /* ASCII PBM */
    PNM_P2      = 2, /* ASCII PGM */
    PNM_P3      = 3, /* ASCII PPM */
    PNM_P4      = 4, /* BINARY PBM */
    PNM_P5      = 5, /* BINARY PGM */
    PNM_P6      = 6, /* BINARY PPM */
    PNM_UNKNOWN = 0
};

static inline int pnm_header(FILE *src,
                             int  *width,
                             int  *height,
                             int  *maxdepth)
{
    int  format = PNM_UNKNOWN;
    int  value;

    if (!src)
        return PNM_UNKNOWN;

    /* The image always begins with a 'P'. */
    if (fgetc(src) != 'P')
        return PNM_UNKNOWN;

    /* The next character determines the format. */
    switch (fgetc(src)) {
    case '1': format = PNM_P1; break;
    case '2': format = PNM_P2; break;
    case '3': format = PNM_P3; break;
    case '4': format = PNM_P4; break;
    case '5': format = PNM_P5; break;
    case '6': format = PNM_P6; break;
    default:
        errno = 0;
        return PNM_UNKNOWN;
    }

    /* The next character must be a whitespace. */
    if (!pnm_is_whitespace(fgetc(src)))
        return PNM_UNKNOWN;

    /* Next item is the width as a decimal string. */
    value = pnm_header_value(src);
    if (value < 1)
        return PNM_UNKNOWN;
    if (width)
        *width = value;

    /* Next item is the height as a decimal string. */
    value = pnm_header_value(src);
    if (value < 1)
        return PNM_UNKNOWN;
    if (height)
        *height = value;

    /* Maxdepth, for all but P1 and P4 formats. */
    if (format == PNM_P1 || format == PNM_P4) {
        if (maxdepth)
            *maxdepth = 1;
    } else {
        value = pnm_header_value(src);
        if (value < 1)
            return PNM_UNKNOWN;
        if (maxdepth)
            *maxdepth = value;
    }

    /* The final character in the header must be a whitespace. */
    if (!pnm_is_whitespace(fgetc(src)))
        return PNM_UNKNOWN;

    /* Success. */
    return format;
}