C Display PPM image as Text [duplicate] - c

This question already has answers here:
read PPM file and store it in an array; coded with C
(2 answers)
Closed 6 years ago.
I am new to programming C and i am experimenting with file manipulation, i am trying to output a PPM file with its comments and rgb data values like so:
P3
# The same image with width 3 and height 2,
# using 0 or 1 per color (red, green, blue)
3 2 1
1 0 0 0 1 0 0 0 1
1 1 0 1 1 1 0 0 0
In this program i have been able to check that it has the correct format and read in to a struct but where i am stuck is how to collect the rgb data and then printing it out. Here is what i have so far. The method to display this information is the showPPM struct which i have started but do not know how to read the image struct and collect its rgb values and display it, any help would be great.
#include<stdio.h>
#include<stdlib.h>
typedef struct {
unsigned char red,green,blue;
} PPMPixel;
typedef struct {
int x, y;
PPMPixel *data;
} PPMImage;
static PPMImage *readPPM(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 'P6')\n");
exit(1);
}
//alloc memory form image
img = (PPMImage *)malloc(sizeof(PPMImage));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
//check for comments
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);
}
while (fgetc(fp) != '\n') ;
//memory allocation for pixel data
img->data = (PPMPixel*)malloc(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, 3 * img->x, img->y, fp) != img->y) {
fprintf(stderr, "Error loading image '%s'\n", filename);
exit(1);
}
fclose(fp);
return img;
}
void showPPM(struct * image){
int rgb_array[600][400];
int i;
int j;
for(i = 0; i<600; i++)
{
for(j = 0; j<400; j++)
{
printf("%d", rgb_array[i][j]);
}
}
}
int main(){
PPMImage *image;
image = readPPM("aab.ppm");
showPPM(image);
}

Your code looks suspiciously like:
read PPM file and store it in an array; coded with C
There were some really good comments and help on that as well.
So, I leave the other stuff below as further help:
Starting off somewhat vague here, to let you have chance to work through it yourself...
First, you need to read in the entire ppm file into a buffer. fread() is probably the function of choice for this. You can use ftell() to get the size of the file. Next, you can typecast your PPMImage structure on top of the buffer used by fread(), and from there, you should be able to access the data via the data field, maybe even using array notation if you wished. (I think this is the step you are missing right now...)
Once you can access the data, you should be able to iterate over the data and printf() or whatever you need to the console based on the input data.

Related

Pset4 (cs50) recover does not work properly. It compiles, but does not recover more than 2 jpegs. Is something wrong with checking for JPEG signature?

I am learning how to code and I have no experience with that at all. I've successful got to PSET4 and stuck on recover. I've read everything online about this problem and i found out that many people have similar code as I do and it works. Does not work for me whatsoever. Please have a look and give me a hint what did I do wrong and how to correct it.
Here is everything about the pset4 recover i downloaded their card.raw from here card.raw
/** recovering JPEG files from a memory card
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char* argv[])
{
// ensure proper usage
if (argc != 2)
{
fprintf(stderr,
"Usage: ./recover infile (the name of a forensic image from which to recover JPEGs)\n");
return 1;
}
// open input file (forensic image)
FILE* inptr = fopen(argv[1], "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE* outptr = NULL;
// create a pointer array of 512 elements to store 512 bytes from the memory card
BYTE* buffer = malloc(sizeof(BYTE) * 512);
if (buffer == NULL)
{
return 3;
}
// count amount of jpeg files found
int jpeg = 0;
// string for a file name using sprintf
char filename[8] = { 0 };
// read memory card untill the end of file
while (fread(buffer, sizeof(BYTE) * 512, 1, inptr) != 0)
{
// check if jpeg is found
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff
&& (buffer[3] >= 0xe0 || buffer[3] <= 0xef))
{
if (jpeg > 0)
{
fclose(outptr);
}
sprintf(filename, "%03d.JPEG", jpeg);
outptr = fopen(filename, "w");
jpeg++;
}
if (jpeg > 0)
{
fwrite(buffer, sizeof(BYTE) * 512, 1, outptr);
}
}
// free memory
free(buffer);
// close filename
fclose(outptr);
// close input file (forensic image)
fclose(inptr);
return 0;
}
The main problem is that you invoke undefined behavior because filename is not enough big. sprintf() need be 9 and 17 bytes with your code but you only has 8. So you have a buffer overflow.
Just change:
char filename[8] = { 0 };
to
char filename[17] = { 0 };
Because, you use an int, this value is implemented defined but in many system has an int with 32 bits. So the value possible are between -2^31 and 2^31 - 1 that make a maximum of 11 chars (-2147483648). We add the number of chars in ".JPEG", 5. We have 16 but you forget the null terminate byte of a c-string. So we are 17 maximum.
Modern compiler warning you: gcc version 7.1.1 20170516 (GCC):
In function ‘main’:
warning: ‘sprintf’ writing a terminating nul past the end of the destination [-Wformat-overflow ]
sprintf(filename, "%03d.JPEG", jpeg++);
^
note: ‘sprintf’ output between 9 and 17 bytes into a destination of size 8
sprintf(filename, "%03d.JPEG", jpeg++);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Plus, your typedef is useless because a char world be always a byte in C. More than that you don't need a byte but an octet so like char, uint8_t would be always an octet in C. So you don't need typedef.
Again one thing, you allocate your buffer but it's useless because your buffer has a constant size. So just create an array is more simple.
#include <stdint.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: ./recover infile (the name of a forensic image "
"from which to recover JPEGs)\n");
return 1;
}
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL) {
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE *outptr = NULL;
uint8_t buffer[512];
size_t const buffer_size = sizeof buffer / sizeof *buffer;
size_t jpeg = 0;
while (fread(buffer, sizeof *buffer, buffer_size, inptr) == buffer_size) {
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff &&
buffer[3] == 0xe0) {
if (outptr != NULL) {
fclose(outptr);
}
char filename[26];
sprintf(filename, "%03zu.JPEG", jpeg++);
outptr = fopen(filename, "w");
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
if (outptr != NULL) {
fclose(outptr);
}
fclose(inptr);
}
Note: This example is clearly not perfect, this will be better to make a true parser for jpeg file to have a better control flow. Here we suppose that all gonna be right.
how do you know that an instance of a JPEG image will always end with '\n'? Or better, how do you know that a JPEG image will be an exact multiple of 512?
You dont know.
So the posted code needs to calculate the actual value OR use some method to have the last call to fread() for any specific JPEG instance, to stop reading at the end of that image,
Then the check for the ID bytes of the next JPEG image will find the next image.
Otherwise, the start of the next image is already written to the prior output file and the check for a new image will fail.
In general this will result in the last created file containing more than one image.
This link: 'https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format' is a web page that describes the format of a JPEG file.
On every digital camera that I have used, the SD card has a directory of all the files.
Suggest using that directory and the info in the linked web page to find each JPEG image and to determine when the end of that image has been encountered. (I.E. the 0xFF 0xD9)

C: Writing structs of RGB values to file to create ppm image - premature end of file

The program i am creating a steganography program that hides a secret message inside a .ppm image by changing random red pixel values to ascii characters.
The program is based on code that is on stackoverflow for reading and writing ppm images (read PPM file and store it in an array; coded with C), all other code is my own work. I have completed all the necessary functions to do this such as writing,reading,encoding and decoding the files but i am struggling to grasp the fwrite function.
Currently when the program encodes an image it takes in the .ppm converts it to its rgb values in a struct. Then it hides the secret message by editing the red values to ascii characters. The issue arises when it comes to "printing" the image to a file. When the program has completed the image produced is around 90% of what it should be printing. Example show below:
Example of the unfinished image
I have checked it is storing all the values are being stored correctly by printing all the rgb values and it is. (used the showPPM method). Is there not enough memory to write the image? is the image to large for the write function? these are my guesses.
Any information on how i should go about changing the writePPM function so that i correctly print 100% of the image to the file would be great.
Here is the code below:
#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");
}
}
The problem is actually in the code for reading in the PPM, which you have modified in a way that appears to work, but actually doesn't because the file format is different to what you think it is.
The code that you linked to is for reading PPM files in the "raw" format. These files start with the "P6" code. In these files, each RGB value is stored as either 1 or 2 bytes (depending on whether the RGB component depth is less than 256). So, if the max value is 255, it's 1 bytes per value, so the file size is width * height * 3.
However, you have modified the code to read "plain" PPM files, which start with the "P3" code, by checking for P3 and reading in more data. These files don't store the RGB values as raw binary data, but as ASCII text specifying the value in decimal format, separated by whitespace. So for example if you had the value 93 in raw format, it would just be 1 byte with a value of 93, but in the "plain" format it would be 3 (or more) bytes: one or more bytes with the ASCII value for a space (or tab), then the ASCII value for "9" (which is 57) then the ASCII value for "3" (which is 51). It's impossible to calculate the size of the file based on the width and height because the whitespace can be variable, and each value could be represented between 1 and 3 digits.
Despite the fact that you are not parsing the data as ASCII-encoded text, your PPM-reading code seems to work because you are just reading in a chunk of data, (optionally) modifying a few random bytes and then writing it out again either totally or mostly unchanged.
So, your possible solutions are:
Change the getPPM code back to what it was and use an actual P6 file.
Write a PPM reader that parses the data properly as ASCII text containing whitespace-separated decimal numbers (you can write out as either P3 or P6).
More info: PPM Format Specification

Display rgb values of ppm image and conflict type error in C

I'm fairly new to C and have been tasked with creating a program that reads an image and displays that image. I've created (what I believe to be) a function to read in the files format/comments etc, and have attempted to create a function to display each pixels rgb values.
I cannot test it because I have an error in my program - 'conflicting types for getPPM'. Could anyone explain why I'm getting this error? and any possible fix to this would be great.
If anyone could suggest a way to be able to store the comments (maybe in array?) and print them later on that would also be really helpful! Thanks.
Also feel free to scrutinize the program as a whole and give me feedback, I'm not entirely sure if it will actually do anything once the above error is fixed.
Thanks very much!
#include <stdio.h>
#include <stdlib.h>
#define MAX_HEIGHT 600
#define MAX_WIDTH 400
int main(int argc, char ** argv){
// get image file
FILE *file;
file = fopen("aab(1).ppm", "r");
//if there is no file then return an error
if(file == NULL){
fprintf(stderr, "File is not valid");
return 0;
}
else {
struct PPM *newPPM = getPPM(file);
return 0;
}
}
struct PPM {
char format[2]; //PPM format code
int height, width; //image pixel height and width
int max; //max rgb colour value
} PPM;
struct PPM_Pixel {
//Create variables to hold the rgb pixel values
int red, green, blue;
} PPM_Pixel;
struct PPM *getPPM(FILE * fd){
struct PPM *image;
char buffer[16];
char c;
char commentArray[256];
fd = fopen(fd, "r");
struct PPM *newPPM = malloc(sizeof(PPM));
//checks if the file being read is valid/the correct file
if(fd == NULL){
exit(1);
}
//read the image of the format
if(!fgets(buffer, sizeOf(buffer), fd)){
exit(1);
}
//checks the format of the ppm file is correct
if(buffer[0] != 'P' || buffer[1] != '3'){
fprintf(stderr, "Invalid image format! \n");
exit(1);
}else {
printf("%s", newPPM->format);
}
//allocate memory for the image
image = malloc(sizeof(PPM));
if(!image){
fprintf(stderr, "Cannot allocate memory");
exit(1);
}
//checks whether the next character is a comment and store it in a linked list
c = getc(fd);
while(c == '#'){
while(getc(fd) != '\n'){
c = getc(fd);
}
}
if(fscaf(fd, "%d %d", &image->height, &image->width)!= 2){
fprintf(stderr, "Size of image is invalid");
exit(1);
}
fclose(fd);
return newPPM;
};
showPPM(struct PPM * image){
//two-dimensional array to show the rows and columns of pixels.
int rgb_array[MAX_HEIGHT][MAX_WIDTH];
int i;
int j;
for(i = 0; i<MAX_HEIGHT; i++){
for(j = 0; j<MAX_WIDTH; j++){
printf("%d", rgb_array[i][j]);
}
}
};
the following code
contains lots of comments about what is wrong and/or how to correct
performs appropriate error checking
corrects some function signatures
properly declares a prototype for each function
corrects some of the 'keypunch' errors
corrects the 'syntax' errors
moved the struct definitions to before those definitions are first referenced
Caveat: this code does not correct the problems with accessing the PPM image file.
Caveat: this code still does not pass the malloc'd memory areas to free(), so those statements still need to be added.
#include <stdio.h>
#include <stdlib.h>
#define MAX_HEIGHT 600
#define MAX_WIDTH 400
// 1) giving the struct tag name the same as an instance name is a bad idea
// 2) good programming practice is to define a struct separately
// from the instance of that struct
struct PPM
{
char format[2]; //PPM format code
int height, width; //image pixel height and width
int max; //max rgb colour value
};
struct PPM myPPM;
struct PPM_Pixel
{
//Create variables to hold the rgb pixel values
//int red, green, blue;
// follow the axiom:
// only one statement per line and (at most) one variable declaration per statement
int red;
int green;
int blue;
};
struct PPM_Pixel myPPM_Pixel;
// prototypes
struct PPM *getPPM(FILE * fp);
void showPPM(struct PPM * image);
// this line will cause the compiler to raise two warnings
// about unused variable 'argc' and 'argv'
//int main(int argc, char ** argv)
int main( void )
{
// get image file
//FILE *file;
//file = fopen("aab(1).ppm", "r");
//if there is no file then return an error
//if(file == NULL)
FILE *fp == NULL;
if( NUL == (fp = fopen("aab(1).ppm", "r") ) )
{
//fprintf(stderr, "File is not valid");
// should include system error message
perror( "fopen for read of aab(1).ppm failed" );
//return 0;
// need to indicate an error occurred
exit( EXIT_FAILURE );
}
// implied else, fopen was successful
// following statement will raise a compiler warning
// about: 'set but not used variable'
struct PPM *newPPM = getPPM(fp);
fclose( fp ); // moved to here from 'getPPM()'
} // end function: main
struct PPM *getPPM(FILE * fp)
{
struct PPM *image;
char buffer[16];
//char c;
// the returned value from 'getc()' is an integer, not a character
int c;
char commentArray[256];
// following line is nonsence and the passed in parameter is already open
//fd = fopen(fd, "r");
// when allocating memory, always check the returned value
// to assure the operation was successful
struct PPM *newPPM = NULL;
if( NULL == (newPPM = malloc( sizeof struct PPM ) ) )
{
perror( "malloc for struct PPM failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
// already done in main()
//checks if the file being read is valid/the correct file
//if(fd == NULL){
// exit(1);
//}
//read the image of the format
if(!fgets(buffer, sizeOf(buffer), fd)){
exit(1);
}
//checks the format of the ppm file is correct
if(buffer[0] != 'P' || buffer[1] != '3'){
fprintf(stderr, "Invalid image format! \n");
exit( EXIT_FAILURE );
}
// implied else, file format correct
printf("%s", newPPM->format);
//allocate memory for the image
// note: there is no item named 'PPM'
// image = malloc(sizeof(PPM));
//if(!image){
// best to keep things together
if( NULL == (image = malloc( sizeof struct PPM ) ) )
{
//fprintf(stderr, "Cannot allocate memory");
// should include the OS message for the failure
perror( "malloc for struct PPM failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
// the following code does not store a comment in a linked list
// and
// skips over 1/2 the characters in the line due to the double call
// to 'getc()' in the inner while loop
//checks whether the next character is a comment and store it in a linked list
//c = getc(fd);
//while(c == '#'){
// while(getc(fd) != '\n'){
// c = getc(fd);
// }
//}
// this line does not compile! did you actually mean to call: 'fscanf()'?
//if(fscaf(fd, "%d %d", &image->height, &image->width)!= 2)
if( 2 == fscanf( fp, "%d %d", &image->height, &image->width) )
{
// next line NOT true, what actually happened is the call to 'fscanf()' failed
//fprintf(stderr, "Size of image is invalid");
perror( "fscanf for image height and width failed" );
exit( EXIT_FAILURE );
}
// implied else, fscanf successful
// best practice to close a file, where it was opened.
//fclose(fd);
return newPPM;
//}; do not follow a function definition with a semicolon
}
// Note: the following function is never called
// showPPM(struct PPM * image) each function signature needs a return type, even if 'void'
void showPPM(struct PPM * image)
{
// Note: PPM images have the number of pixels in each row rounded up to a multiple of 4
// but this posted code fails to handle that
//two-dimensional array to show the rows and columns of pixels.
// bad idea, suggest passing the (already input) width and height
// and use those passed in values to define the array sizes
int rgb_array[MAX_HEIGHT][MAX_WIDTH];
int i;
int j;
for(i = 0; i<MAX_HEIGHT; i++)
{
for(j = 0; j<MAX_WIDTH; j++)
{
printf("%d", rgb_array[i][j]);
}
}
// }; do not follow a function definition with a semicolon
}

Issues writing PNM P6

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 ...

C - Need help swapping red and green components [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
This code (which I edited from code I found on a previous Stack Overflow question) is able to take the input of a ppm image and output the same image, but with all of the colors complementary. In my assignment, I am asked to do this, but then to swap the red and green components (basically make img->data[i].red into img->data[i].green and vice versa). However, I am unsure of how to go about doing this. I can't just make two equal statements, as the first would essentially nullify the second. The only thing that I can think of is to create a temporary array of the PPMImage type (a struct created earlier). However, I am a bit unsure of how the code and logic for this would work.
Here is the code. The 'for' loop in the changecolorPPM function is where the colors are complemented, and where I am pretty sure the swapping needs to occur.
#include<stdio.h>
#include<stdlib.h>
typedef struct {
unsigned char red,green,blue;
} pixel_t;
typedef struct {
int x, y;
pixel_t *data;
} PPMImage;
#define CREATOR "RPFELGUEIRAS"
#define RGB_COMPONENT_COLOR 255
static PPMImage *readPPM(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] != '6') {
fprintf(stderr, "Invalid image format (must be 'P6')\n");
exit(1);
}
//alloc memory form image
img = (PPMImage *)malloc(sizeof(PPMImage));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
//check for comments
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);
}
//read rgb component
if (fscanf(fp, "%d", &rgb_comp_color) != 1) {
fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
exit(1);
}
//check rgb component depth
if (rgb_comp_color!= RGB_COMPONENT_COLOR) {
fprintf(stderr, "'%s' does not have 8-bits components\n", filename);
exit(1);
}
while (fgetc(fp) != '\n') ;
//memory allocation for pixel data
img->data = (pixel_t*)malloc(img->x * img->y * sizeof(pixel_t));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
//read pixel data from file
if (fread(img->data, 3 * img->x, img->y, fp) != img->y) {
fprintf(stderr, "Error loading image '%s'\n", filename);
exit(1);
}
fclose(fp);
return img;
}
void writePPM(const char *filename, PPMImage *img)
{
FILE *fp;
//open file for output
fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
exit(1);
}
//write the header file
//image format
fprintf(fp, "P6\n");
//comments
fprintf(fp, "# Created by %s\n",CREATOR);
//image size
fprintf(fp, "%d %d\n",img->x,img->y);
// rgb component depth
fprintf(fp, "%d\n",RGB_COMPONENT_COLOR);
// pixel data
fwrite(img->data, 3 * img->x, img->y, fp);
fclose(fp);
}
void changeColorPPM(PPMImage *img)
{
int i;
if(img){
for(i=0;i<img->x*img->y;i++){
img->data[i].red=RGB_COMPONENT_COLOR-img->data[i].red;
img->data[i].green=RGB_COMPONENT_COLOR-img->data[i].green;
img->data[i].blue=RGB_COMPONENT_COLOR-img->data[i].blue;
}
}
}
int main(int argc, char* argv[]){
PPMImage *image;
char* filename = argv[1];
image = readPPM(filename);
changeColorPPM(image);
writePPM("OutputFile.ppm",image);
printf("Press Enter");
getchar();
}
From your question, I believe you are looking for a swap function which could easily be implemented as
img->data[i].red = img->data[i].red + img->data[i].green;
img->data[i].green = img->data[i].red - img->data[i].green;
img->data[i].red = img->data[i].red - img->data[i].green;
Update:
This section is fine if there is no overflow from the addition operation. However, if the precision of the destination is not sufficient enough to hold the bits, then there could be potential loss of data. Hence, in these cases, as Mats and other experts suggested, the traditional temporary variable method would be the best way forward
temp = img->data[i].red;
img->data[i].red = img->data[i].green;
img->data[i].green = temp;
Note:
If you are into image processing, then this swap may not be pleasing from a visual perspective. Usually, Green contains the maximum intensity i.e. amount of brightness information as compared to Red and Blue and human eye can catch the variations in intensity very easily. It will interesting to check, how the final picture looks like after the swap.

Resources