I have to open/read a file which is an ascii art file (image)
and requires me to return the width and height of the image to the main routine and then requires me to pass the picture data back via a pointer. Here is the function prototype that I MUST use:
unsigned char *foo(char *filename, int *width, int *height)
Inside of foo, I must use a dynamic array of chars
to store tha image data. I need to use fread() to read
that data. I must also account for the carriage return at the end of each line.
Once I have opened and read the data, pass it back to the main routine. The main routine must then create a dynamic 2D array to store the image, copy the 1D array
into the 2D array, and display the image on the screen.
Image File Name: data.txt
My Code:
#include <stdio.h>
#include <stdlib.h>
void readDimension(FILE *inFile, int *width, int *height)
{
int i;
for (i = 0; i < 2; i++)
{
if (i == 0)
{
fscanf(inFile, "%d", width);
}
if (i == 1)
{
fscanf(inFile, "%d", height);
}
}
}
unsigned char *foo(char *filename, int *width, int *height)
{
FILE *inFile = fopen(filename, "rb");
readDimension(inFile, width, height);
unsigned char *ret = malloc(*width * *height);
fread(ret, 1, *width * *height, inFile);
fclose(inFile);
return ret;
}
int main(int argc, char** argv)
{
FILE *inFile;
int width, height;
unsigned char art;
if (argc == 1)
{
printf("Please specify a file name.\n");
}
else if (argc == 2)
{
inFile = fopen(argv[1], "rb");
if (inFile != NULL)
{
fclose(inFile);
art = foo(argv[1], &width, &height);
int n = sizeof(art);
printf("Data in Array: \\%c \n", art);
printf("Size of Array: %d \n", n);
}
else
{
printf("Error: File Not Found %s", argv[1]);
}
}
printf("Width: %d\n", width); // Testing
printf("Height: %d\n", height); // Testing
}
The problem is that you are trying to get the size of art, which is a pointer. It has fixed size. Size should be computed as width*height:
printf("Size of Array: %d x %d (%d)\n", width, height, width*height);
You declared art as an unsigned char, but you are assigning it a pointer to unsigned char. This is incorrect: art should be declared with an asterisk.
You are also printing a single character %c while passing an array art. This is not going to print anything of interest. If you wish to print the first character, print art[0]. If you wish to print the entire picture, make a pair of nested loops that iterate over the width and height, print each character, and print '\n' after each line.
Here are a few additional notes:
You should not read the entire file in one go with fread, because it would introduce \ns into the data. Instead, you should read the file line by line.
You can read width and height in the same function that reads the data
You do not need a loop to read two integers. You can do it in a single line, like this: fscanf(inFile, "%d %d", width, height);
Related
P6
650 652
255
P6 indicates that it is a PPM image. The next two fields are the width and height of the image. The last field gives the maximum pixel value. At the end of the header is a \n and then the binary pixel data. The image is in color so there are three bytes (red, green, blue). The goal of my readPPM function is to return the pixel data in a one-dimensional array of unsigned chars, plus the width and height of the image. The goal of my writePPM function (I haven't done anything for that function yet) is to write the PPM format image to an empty file from the given information returned from my readPPM function. I'm still not sure how to make this program work so I don't store the 650 in width and 652 in height. I'll worry about that once I can actually read and write the text files.
Update: I've successfully stored the header (P6, 650, 652, and 255)) in their right variables (I know because I tried printing their values and they came out right). My output is shown below. I was wondering what those nonsense characters below the maximum were. Are those the binary pixel data? If so, then I just need to write my writePPM function.
Output:
PSix: P6
Width: 650
Height: 652
maximum: 255
?ɡ?Ƞ?ɢ?ˤ?ɢ?Ş?ĝ?Ǡ?ʤ?ɣ?ɡ?ɡ?ǟ?Ĝ?ŝ?Ƞ?ȡ?ʣ?ʣ?ȡ?Ŝ?Ŝ?Ȟ?ʠ?̤?ƞ?Ơ?ʤ?ʦ?ʦ?ɧ?Ƥ?ǥ?Ȧ?ɩ?ʪ?ʫ?ʫ?ʪ?ʨ?ɤ?Ǡ?ʢ?̤?ȡ?ȡ?ʥ?ɥ?ʧ?Ģ?ǥ?Ƥ?Ģ? ????Ƥ????ǥ?ʨ?ɧ?ʨ?ɧ?ɧ?ͨ?ϧ?ʟ?ˠ?Х?Φ?ͥ?ˣ?ǟ??Ơ?ʦ?ɥ?Ġ????????????? ?????ħ?Ũ???????©?Ǯ?????????????Ǩ?˭?ū?????????????????????????鴢紣㴢ⳡ?ܴᷟ?⺡㻢Ḥ?ݳ?۱?ܲ?۱?خ??ٯ?֮?ѫ?Ъ?̨?մ?յ?Ǫ?̲?ٿ?һ????ư?ɳ?ɳ?ɱ?ɱ?˰?ˮ?ɪ?Ȧ?ͫ?ֵ?ұ?Ť?¡?ʩ?Ӳ?Ϯ?ͫ?ϭ?ѯ?ѯ?Ϭ?̭?˰?Ͷ?͵?˳?ʱ?Ǯ?ȯ?ε?ӷ?ϳ?̯?ɬ?̫?ͬ?ͭ?˪?ΰ?ͮ?ί?Ӵ?Ӷ?ϲ?ˮ?ɬ?ʭ?Ѵ?ҵ?г?ϲ?ˮ?ȫ?ɬ?ǩ?ϱ?в?˭?ʬ?˭?̭?ί?ͮ?ͮ?ͮ?ί?ˬ?ȩ?ʫ?ͮ?ղ?ү?Ӳ?ֵ?Ҵ?ѳ?ϲ?ˮ?ȭ?˰?ˮ?ˮ?ͯ?̮?̬?ͯ?ˮ?̱?˲?ȯ?ƭ?Ư?Ů?®?¯?????????????????????????????????????¨?é?é?ū?¨???????????????????????????????ī?ī?ī?Ŭ?Ŭ?ī?ū?ū?é???????ĭ?Ů?®???????????©??????????ȫ?ȫ?Ȫ?ɫ?̭?̭?˭?ʬ?ɫ?ʬ?ʮ?˯?˯?˯?˯?Ȭ?Ȫ?ɫ?̮?ͯ?ϱ?ѳ?ӵ?Ѵ?б?ϰ?ϭ?ά?Ѯ?Ա?Ӱ?Э?Ϭ?ү?ү?Э?ά?ѯ?ױ?ӭ?ϩ?ͧ?ϧ?ѩ?Ԭ?ذ?խ?ѩ?Ϧ?Ϧ?Ч?ҩ?ҩ?ҩ?ת?ث??خ?ׯ?֮?կ?կ?Ԭ?խ?ج?ج?ܬ?߰?㳩䴨峨ݫ?٧?ެ?䲧峨䲧䲧Ⱗ䲩붮?ﺲ국굯???????걦뱥ﶥ???????????????????????????????????????±????ð?????????????????????????????????????????????????æ?ũ?ç?é?Ī?ë???????£?Ʀ?Ʀ?ƥ?Ť?Ť?ƥ?ɧ?ʨ?ͫ?ά?ʩ?Ť?â?ţ?Ǩ?ť?Ƨ?ˬ?ɭ?¦???????????????????????????????ç???????????????????????????????????????????????????????????????????????????????????????????????????????????????????ñ?°?ñ??????????î????????????????????????鲯豮汭尬䯫汭곮췱洫ᱧద嵫緭䴪᯦᯦䲩洫鴬鴬鴬국국贩춬??????뵩괪???????????겱⪭????겳???뵫贩贩趫긭긭긭跩綨깫컭궩糦뵩뷩켨컪鸧綨浧絬鷮鷰峬洫???긭跩긭캱캳깲綯㳩ᱧ㳧嵩㵨᳦᳤ⴥ乩缬轭轭缬ߴ?۰?ߴ?幬
main.cc
int main() {
char fileName[50] = "binary_pixels.txt";
int width = 0; // width of the image
int height = 0; // heigt of the image
int size = 128; // size of the array
// read the PPM file and store its contents inside an array and return
// the pointer to that array to pixelArray
unsigned char* pixelArray[size] = readPPM(fileName, &width, &height);
// print the contents of the array
for (int i = 0; i < 128; i++) {
printf("%s\n", pixelArray[i]);
} // end of for loop
} // end of main
readPPM.cc
unsigned char readPPM(const char* fileName, char* pSix, int* width,
int* height, int* maximum) {
// open the file to read just the header reading
FILE* fr = fopen(fileName, "r");
// formatted read of header
fscanf(fr, "%s", pSix);
// check to see if it's a PPM image file
if (strncmp(pSix, "p6" , 10) != 0) {
printf("They are not the same\n");
} else {
printf("They are the same\n");
}
// read the rest of header
fscanf(fr, "%d\n %d\n", width, height);
fscanf(fr, "%d\n", maximum);
// check to see if they were stored properly
printf("PSix: %s\n", pSix);
printf("Width: %d\n", *width);
printf("Height: %d\n", *height);
printf("maximum: %d\n", *maximum);
// allocate array for pixels
unsigned char* pixels = new unsigned char[width * height];
// unformatted read of binary pixel data
while (fread(pixels, sizeof(pixel), 128, fr)) {
printf("%s", pixels);
} // end of for loop
// close file
fclose(fr);
// return the array
return pixels;
} // end of readPPM
//struct to hold a pixel.
struct rgb
{
char r;
char g;
char b;
};
//struct to hold the image and its info.
struct image
{
char p;
int format;
int width;
int height;
int intensity;
//struct rgb **rgb;
unsigned char *pixels;
};
int main(void)
{
struct image m;
FILE *fp = //fopen(...);
fscanf(fp, "%c%d\n", &m.p, &m.format);
fscanf(fp, "%d %d\n", &m.width, &m.height);
fscanf(fp, "%d\n", &m.intensity);
printf("%c%d\n", m.p, m.format);
printf("%d %d\n", m.width, m.height);
printf("%d\n", m.intensity);
//allocate array to hold the pixels
m.pixel = (unsigned char*)malloc(m.width*m.heigth*3*sizeof(unsigned char));
//read pixels into m.pixel
return 0;
}
You are not reading the char into the correct location. pixels is already a pointer to chars so you should pass it as is to fscanf:
fscanf(fr, "%c", pixels)
Of course there are other issues but I this should let you pass this road block.
Okay, this is not working!
What is wrong with it.
Can somebody point it out to me. I've stared at it for hours.
I have seeked help earlier and he come up with struct but im not really getting it and it doesnt work so.
It's not getting the right calculation. I'm thinking that there might be something wrong with reading the files.
/*
read binary file and calculate parameter of polygon
*/
typedef int16_t points[2];
int main(int argc, char* argv[]) {
FILE* fp = fopen(argv[1], "rb");
// int points[1000];
//long numbytesread=0;
int16_t num_of_coors=0;
//open the files
if( fp == NULL ) {
printf("Could not open.\n");
return -1; // -1 indicates error
}
//read file
fread(&num_of_coors, sizeof(int16_t), 2, fp);
points*points=malloc(sizeof(points)*num_of_coors);
fread(points, sizeof(points), num_of_coors, fp);
//read the array and seperate x coordinates and y coordinates
//calculate using formula (x1-x2)^2+(y1-y2)^2
//need 2 points, 4 coordinates at any single time. read a pair at a time
double sum=0;
int i=0;
//int coors=points[0]*2+1 ;
for(i=1;i<=num_of_coors;i++){
sum+=sqrt(pow((points[i]-points[i+2]),2) + pow((points[i+1]-points[i+3]),2));
}
sum+=sqrt(pow((points[1]-points[num_of_coors-2]),2) + pow((points[2]-points[num_of_coors-1]),2));
printf("The perimeter is %.2lf\n", sum);
fclose(fp);
free(points);
}
I guess the binary format of your file is:
int16_t <num of points>
int16_t <Xcoord> int16_t <Ycoord>
....
First, it is bad way to use same name for type name and for variable name.
Second, you work with 'points' type is incorrect.
The correct code should looks like:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
read binary file and calculate parameter of polygon
*/
typedef int16_t points[2];
int main(int argc, char* argv[]) {
FILE* fp = fopen(argv[1], "rb");
// int points[1000];
//long numbytesread=0;
int16_t num_of_coors=0;
//open the files
if( fp == NULL ) {
printf("Could not open.\n");
return -1; // -1 indicates error
}
//read file
fread(&num_of_coors, sizeof(int16_t), 1, fp);
points* p=malloc(sizeof(points)*num_of_coors);
fread(p, sizeof(points), num_of_coors, fp);
//read the array and seperate x coordinates and y coordinates
//calculate using formula (x1-x2)^2+(y1-y2)^2
//need 2 points, 4 coordinates at any single time. read a pair at a time
double sum=0;
int i=0;
//int coors=points[0]*2+1 ;
for(i=1;i<num_of_coors;i++) {
sum+=sqrt(pow(p[i-1][0]-p[i][0],2)+pow(p[i-1][1]-p[i][1],2));
}
sum+=sqrt(pow(p[0][0]-p[num_of_coors-1][0],2)+pow(p[0][1]-p[num_of_coors-1][1],2));
printf("The perimeter is %.2lf\n", sum);
fclose(fp);
free(p);
return 0;
}
I'm writing a program that takes in two duplicate PNM P6 files, puts the memory of the first file into a buffer, creates a yellow diagonal line over that, and writes the result to the second file. When I run it, the output file is corrupted and can't be displayed. I noticed when looking at the output that it's missing the three lines that should be at the top:
P6
1786 1344
255
I don't know how to programmatically ensure that those lines stay in the code -- I can't figure out why they're even being overwritten in the first place.
When I manually add those lines to the output, the file is no longer corrupted. However, no yellow diagonal line appears. Is this part of the same issue, or is there something else I should look into fixing?
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
unsigned char red, green, blue;
} iPixel;
typedef struct
{
int x, y;
iPixel *data;
} Image;
Image *
ReadImage(char *filename)
{
FILE *f = fopen(filename, "rb");
Image *img = NULL;
char magicNum[128];
int width, height, maxval;
if (f == NULL)
{
fprintf(stderr, "Unable to open file %s\n", filename);
return NULL;
}
fscanf(f, "%s\n%d %d\n%d\n", magicNum, &width, &height, &maxval);
/*
printf("Magic num = %s width = %d, height = %d, maxval = %d\n",
magicNum, width, height, maxval)
;
*/
if (strcmp(magicNum, "P6") != 0)
{
fprintf(stderr, "Unable to read from file %s, because it is not a PNM file of type P6\n", filename);
return NULL;
}
img = (Image *) malloc(sizeof(Image));
img -> x = width;
img -> y = height;
img -> data = (iPixel*) malloc(img -> x * img -> y * sizeof(iPixel));
fread(img -> data, sizeof(iPixel), width*height, f);
fclose(f);
return img;
}
void WriteImage(Image *img, char *filename)
{
FILE *f = fopen(filename, "wb");
fwrite(img->data, sizeof(iPixel), img-> x * img-> y, f);
fclose(f);
}
Image *
YellowDiagonal(Image *input)
{
int i, j;
for (i = 0; i < input->x; i++)
{
for (j=0; j < input->y; j++)
{
if (i==j)
{
input->data[i].red=255;
input->data[i].green=255;
input->data[i].blue=0;
}
}
}
return input;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
fprintf(stderr, "Usage: ./3A_c.c <input image file> <output image file>\n");
exit(EXIT_FAILURE);
}
Image *img;
img = ReadImage(argv[1]);
fprintf(stderr, "Read.\n");
YellowDiagonal(img);
fprintf(stderr, "Diagonal line.\n");
WriteImage(img, argv[2]);
fprintf(stderr, "Write.\n");
}
Be careful to write exactly the same format you are reading. The PNM format is well defined, and you are reading it correctly. However, in the writing routine there were a couple of mistakes:
opening a file with "w" or "wb" truncates it to 0 bytes;
best practice is always to check if fopen succeeds;
reading actual ASCII data can be done with fscanf, binary data with fread. Similarly, writing ASCII should be done with fprintf and only binary data again with fwrite.
If you want to make sure you write the same data as you read in before, you need to save it somewhere. The maxval variable is read, but not saved, and so I cannot write it back. However, it is not a huge problem because the rest of your code assumes the image is R8G8B8 anyway, and so maxval should always be 255.
Here is an adjusted WriteImage that works.
void WriteImage(Image *img, char *filename)
{
FILE *f = fopen(filename, "wb");
if (f == NULL)
{
printf ("Unable to open '%s' for writing!\n", filename);
/* better would be: "return -1" to indicate an error, 0 otherwise */
return;
}
fprintf (f, "P6\n");
fprintf (f, "%d %d\n", img->x, img->y);
/* actually you need to write 'maxval' here */
fprintf (f, "%d\n", 255);
fwrite(img->data, sizeof(iPixel), img->x * img->y, f);
fclose(f);
}
With the above out of the way, you can now see your 'diagonal line' is not correct! I'm not going to fix that (I suppose not being able to see what happened stopped you in your tracks), but here are a few pointers just to guide you:
no need to return an Image * if you are changing data in situ anyway
no need to check every single pixel
check the coordinates of what pixels are changed ...
My program decodes an image that is covered by random pixels, to decode the image, I have to multiply each pixel's red color component by 10. The green and blue color components are the same values as the new red component. I've created multiple helper functions, to make the code easier to read in main, but when I try to run my a.out, I keep getting "Segmentation Fault". I can't seem to find my mistakes! Help is appreciated.
void check_argument(int arg_list)
{
if (arg_list < 2)
{
perror("usage: a.out <input file>\n");
}
}
void print_pixel(int a, FILE *out)
{
int r, g, b;
r = a * 10;
if (r > 255)
{
r = 255;
}
g = r;
b = r;
fprintf(out, "%d\n", r);
fprintf(out, "%d\n", g);
fprintf(out, "%d\n", b);
}
void read_header(FILE *in)
{
char str[20];
for (int i = 0; i < 3; i++)
{
fgets(str, 20, in);
}
}
FILE* open_files(FILE *infile, char *input[])
{
infile = fopen(input[1], "r");
if (infile == NULL)
{
perror("Error: Cannot read file.\n");
}
return infile;
}
void decode(int arg_list, char *in[])
{
FILE *input, *output;
int check, red, green, blue;
open_files(input, in);
output = fopen("hidden.ppm", "w");
fprintf(output, "P3\n");
fprintf(output, "%d %d\n", 500, 375);
fprintf(output, "255\n");
read_header(input);
check = fscanf(input, "%d %d %d", &red, &green, &blue);
while (check != EOF)
{
print_pixel(red, output);
check = fscanf(input, "%d %d %d", &red, &green, &blue);
}
fclose(input);
fclose(output);
}
int main(int argc, char *argv[])
{
check_argument(argc);
decode(argc, argv);
}
After calling open_files(input, in); you will not have the file handle in input.
As this is supposed to be homework, I'll try to show you the some common source of bugs and how to find them.
Variables which are used must (should) be assigned to before that. This counts especially for pointers, e. g. FILE *.
If a function (e. g. fopen()) fails, it normally indicates this by returning a special value which must be checked before continuing.
To check which value a variable has, you can use printf() to show it.
This is for finding principal errors such as segfaults.
But logical errors are hard to find as well: if you read 3 values and store them into variables, it might be more useful to use them all instead of only one of them. (But maybe this one is not yet the goal of this exercise.)
I wrote the lines before this before I learned that it is not the task to search for bugs in a given program, but to write a program by yourself, so I'll get a little more concrete by now.
A FILE * is something returned by fopen(). You can return it or you can write it to a variable or another memory location indirectly pointed to by a pointer "one level deeper".
So you should rewrite your open_files() (BTW: why file*s*? It's currently only one...):
either for returning the value (preferrable):
FILE* open_files(char *input[])
{
FILE *infile = fopen(input[1], "r");
if (infile == NULL)
{
perror("Error: Cannot read file.\n");
}
return infile;
}
and call it with
input = open_files(input);
or with "pass by reference":
void open_files(FILE **infile, char *input[])
{
*infile = fopen(input[1], "r");
if (*infile == NULL)
{
perror("Error: Cannot read file.\n");
}
return *infile;
}
and call it with
open_files(&input, in);
Only doing that you'll have your variable input at the caller's site really written to.
I just want to output 3 integers from the file. Why this doesn't work? I get -1079184140 and similar.
int main(int argc, char *argv[])
{
FILE* stream = fopen(argv[2], "r");
char line[80];
for (int i = 0; i < 3; i++)
{
fgets(line, 80, stream);
printf("%d \n", line);
}
fclose(streamForInput);
}
I would use sscanf.
int number;
sscanf (line, "%d", &number);
printf ("%d \n", number);
That will pull the first integer on a line. This is not the most secure or robust way, but that is out of scope.
PS:
fclose(streamForInput);
Should be:
fclose(stream);
Hmm. The first problem is:
printf("%d \n", line);
because line is a char[]. But you use a %d to output it, so you output line, which is an address. So printf prints the address of line... instead you coud use printf ("%d", atoi(line));
To print a string, which line is, use %s:
printf("%s \n", line);
Now, if it really were an integer, you could use %d:
int num = atoi(line);
printf("%d \n", num );
What you're seeing is the result of treating a pointer type (which is what a string in C basically is) as an integer type. Since pointers hold memory addresses, that -1079184140 is the actual address the pointer holds, represented as a 32 bit signed integer.
If you know exactly the content of the file (three numbers separated by white space), why not directly read it?
if (fscanf(stream, "%d%d%d", &foo, &bar, &baz) < 3)
// handle error
printf("%d\n%d\n%d\n", foo, bar, baz);
But if you want to read lines, there are already other good answers.
To read a file line by line for integers
void read_file(char *filename, int *readbuff, int size)
{
FILE *fp = fopen(filename,"r");
if(fp == NULL){
printf("Failed to open file %s \n", filename);
return;
}
/*the condition in for loop checks if the integer was read into
readbuff[i] and the readbuff is not overflown*/
for(int i = 0 ; fscanf(fp,"%d\n",&readbuff[i]) == 1 && i < size; ++i);
fclose(fp);
return;
}