【问题标题】:C: Writing structs of RGB values to file to create ppm image - premature end of fileC:将 RGB 值的结构写入文件以创建 ppm 图像 - 文件过早结束
【发布时间】:2017-06-26 19:44:03
【问题描述】:

我正在创建一个隐写术程序,该程序通过将随机红色像素值更改为 ascii 字符来隐藏 .ppm 图像中的秘密消息。 该程序基于stackoverflow上用于读取和写入ppm图像的代码(read PPM file and store it in an array; coded with C),所有其他代码都是我自己的工作。我已经完成了执行此操作的所有必要功能,例如写入、读取、编码和解码文件,但我正在努力掌握 fwrite 功能。

目前,当程序对 .ppm 中的图像进行编码时,会将其转换为结构中的 rgb 值。然后它通过将红色值编辑为 ascii 字符来隐藏秘密消息。将图像“打印”到文件时会出现问题。程序完成后,生成的图像大约是应该打印的图像的 90%。示例如下所示: Example of the unfinished image

我已经通过打印所有 rgb 值来检查它是否存储了所有值,并且它是正确存储的。 (使用 showPPM 方法)。没有足够的内存来写图像吗?写入功能的图像是否太大?这些是我的猜测。

任何关于我应该如何更改 writePPM 函数以便我将 100% 的图像正确打印到文件的信息都会很棒。

下面是代码:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<time.h>

typedef struct {
    unsigned char red,green,blue;
} PPMPixel;

typedef struct {
int x, y;
PPMPixel *data;
} PPMImage;

void writePPM(PPMImage *img);

static PPMImage *getPPM(const char *filename)
{

    char buff[16];
     PPMImage *img;
     FILE *fp;
     int c, rgb_comp_color;
     //open PPM file for reading
     fp = fopen(filename, "rb");
     if (!fp) {
          fprintf(stderr, "Unable to open file '%s'\n", filename);
          exit(1);
     }

     //read image format
     if (!fgets(buff, sizeof(buff), fp)) {
          perror(filename);
          exit(1);
     }

//check the image format
if (buff[0] != 'P' || buff[1] != '3') {
     fprintf(stderr, "Invalid image format (must be 'P3')\n");
     exit(1);
}else{
    printf("P3\n");
}

//alloc memory form image
img = (PPMImage *)malloc(sizeof(PPMImage));
if (!img) {
     fprintf(stderr, "Unable to allocate memory\n");
     exit(1);
}


   c = getc(fp);
   while (c == '#') {
   while (getc(fp) != '\n') ;
     c = getc(fp);

}
ungetc(c, fp);
//read image size information
if (fscanf(fp, "%d %d", &img->x, &img->y) != 2) {
     fprintf(stderr, "Invalid image size (error loading '%s')\n", filename);
     exit(1);
}else{
    printf("Height: %d\n",img->x);
    printf("Width: %d\n",img->y);

}

//read rgb component
if (fscanf(fp, "%d", &rgb_comp_color) != 1) {
     fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
     exit(1);
}else{
    printf("%d\n",rgb_comp_color );
}

//check rgb component depth
if (rgb_comp_color!= 255) {
     fprintf(stderr, "'%s' does not have 8-bits components\n", filename);
     exit(1);
}

while (fgetc(fp) != '\n') ;
//memory allocation for pixel data
img->data = (PPMPixel*)malloc(24*img->x * img->y * sizeof(PPMPixel));

if (!img) {
     fprintf(stderr, "Unable to allocate memory\n");
     exit(1);
}

//read pixel data from file
if (fread(img->data, 10*img->x, img->y, fp) != img->y) {
     fprintf(stderr, "Error loading image '%s'\n", filename);
     exit(1);
}

fclose(fp);
return img;
}



struct PPMImage * encode(char * text, PPMImage * img)
{
    //convert secret message to ascii code

    int i,ascii,height,width;
    int total = 0;
    int rolling = 0;
    int original = 0;
    time_t t;
    srand((unsigned) time(&t));
    height=img->y;
    width=img->x;

    for(i = 0; text[i]; i++){

        ascii = text[i];

        //create random number between 0 and max the width
        total = total + rand() % width;
        original = total;
        //printf("Random Number: %d\n",total);

        if(total >= width){
            rolling = rolling + 1;
            total = total - width;
        }

        //printf("Before R: %d \n",img->data[0].red );
        img->x=rolling;
        img->y=total;

        printf("X: %d ",rolling );
        printf("Y: %d ",total );

        //set img position
        //at position random we set the red bit equal to ascii number 
        printf("Old R:  %d ",img->data[i].red );                    
        img->data[i].red=ascii; 
        printf("New R: %d\n ",img->data[i].red );   
    }

    //take img then print it out
    //setting the img values again for printing
    img->x=width;
    img->y=height;
    writePPM(img);

}

void writePPM(PPMImage *img)
{
FILE *fp;
//open file to be written
fp = fopen("encoded.ppm", "wb");
if (!fp) {
     fprintf(stderr, "Unable to open file \n");
     exit(1);
}

//image format
fprintf(fp, "P3\n");

//comments
//need to store comments to be outputted
fprintf(fp, "# Created by Sean \n");

//image size
fprintf(fp,"%d %d\n",img->x,img->y);

// rgb component depth
fprintf(fp, "%d\n",255);

//write pixels currently not fully working
fwrite(img->data, sizeof(img->data), 3*img->y*img->x, fp);

//close file stream
fclose(fp);
}

void showPPM(PPMImage *img)
{
    int i;
    if(img){

    for(i=-1;i<img->x*img->y;i++){
        printf("Number: %d\n",i);
        printf("R: %d ",img->data[i].red );
        printf("G: %d ",img->data[i].green );
        printf("B: %d\n ",img->data[i].blue );

     }
}
}


char * decode(PPMImage * i1,PPMImage * i2){

//compare difference in number of bits in red pixels
//if there is a different then take the red pixel value from the encrypted image
//then translate it from ascii to chars then print.
printf("Decoding......\n");

int i;
     for(i=-1;i<i1->x*i1->y;i++){
            if(i1->data[i].red != i2->data[i].red){
                printf("%c",i1->data[i].red );
            }
     }

//to be able to test and finish this need to write code for encoding

}

int main(int argc, char *argv[]){

//input statements
if(argc == 3){
    PPMImage *image;
    image = getPPM(argv[2]);
    //uncomment the showPPM to display all rgb values in the encoded files
    //showPPM(image);
    if(argv[1] = "e"){
    printf("Please enter your secret message to be encoded estimated max characters: %d\n",image->y);   

        //need to add user input
    encode("test output!",image);
    }
}else if(argc == 4){
    PPMImage *i1;
    PPMImage *i2;
    i1 = getPPM(argv[2]);
    i2 = getPPM(argv[3]);

    if(argv[1] = "d"){
        decode(i1,i2);
    }
}else{
    printf("Wrong arguments");
}
}

【问题讨论】:

  • sizeof(img-&gt;data) 不是你想要的。这给了你 pointer 的大小而不是分配的内存的大小。您的代码中有神奇的数字,例如243,这使得各种尺寸所代表的含义不明显。所以我不确定你在fwrite 中到底需要什么尺寸。但绝对不是sizeof(img-&gt;data)
  • 你应该编辑这个以删除反诽谤水手的谈话,我们正试图在这里经营一个受人尊敬的联合。
  • 感谢您忘记那里的建议 :)

标签: c struct rgb ppm


【解决方案1】:

问题实际上出在用于读取 PPM 的代码中,您已经以一种看似可行的方式对其进行了修改,但实际上并没有,因为文件格式与您认为的不同。

您链接到的代码用于读取“原始”格式的 PPM 文件。这些文件以“P6”代码开头。在这些文件中,每个 RGB 值存储为 1 或 2 个字节(取决于 RGB 分量深度是否小于 256)。因此,如果最大值为 255,则每个值 1 个字节,因此文件大小为宽 * 高 * 3。

但是,您已修改代码以读取以“P3”代码开头的“普通”PPM 文件,方法是检查 P3 并读入更多数据。这些文件不将 RGB 值存储为原始二进制数据,而是作为 ASCII 文本以十进制格式指定值,并以空格分隔。因此,例如,如果您有原始格式的值 93,它只是 1 个字节,值为 93,但在“普通”格式中,它将是 3 个(或更多)字节:一个或多个带有 ASCII 值的字节对于空格(或制表符),然后是“9”的 ASCII 值(即 57),然后是“3”的 ASCII 值(即 51)。不可能根据宽度和高度计算文件的大小,因为空格是可变的,每个值可以表示在 1 到 3 位之间。

尽管您没有将数据解析为 ASCII 编码文本,但您的 PPM 读取代码 似乎 可以工作,因为您只是在读取一大块数据,(可选)修改几个随机字节,然后完全或几乎不变地再次写出。

因此,您可能的解决方案是:

  • 将 getPPM 代码改回原来的样子并使用实际的 P6 文件。
  • 编写一个 PPM 阅读器,将数据正确解析为包含空格分隔的十进制数字的 ASCII 文本(您可以写成 P3 或 P6)。

更多信息:PPM Format Specification

【讨论】:

  • 感谢您的帮助,我想更改程序以读取/写入 P3 图像,因此需要更改阅读器,我将如何将其更改为正确的要求。为每个 rgb 值添加空格?
  • 读入时,在循环中调用fscanf(fp, " %d %d %d ", &amp;red, &amp;green, &amp;blue);,直到读入宽度*高度值或到达文件末尾(使用feof(fp)测试)。读入整数,然后在结构中设置 8 位值。要写出来,你可以使用 fprintf。请注意,每行只能有 70 个值。首先仔细阅读格式规范。
  • 此代码处理读取 ppm 文件,但可能有点过于复杂:sourceforge.net/p/netpbm/code/HEAD/tree/stable/lib/libppm1.c
  • 感谢您的提示和帮助 :)
猜你喜欢
  • 1970-01-01
  • 2012-09-08
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多