0

I am attempting to read a '.raw' file which stores the contents of an image that was taken on a camera using C. I would like to store these contents into a uint16_t *. In the following code I attempt to store this data into a pointer, using fread(), and then write this data into a test file, using fwrite(), to check if my data was correct. However, when I write the file back it is completely black when I check it.

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

#define MAX_ROW 2560
#define MAX_COL 2160

int main()
{
    char filename[32] = "image1.raw";
    FILE * image_raw = fopen(filename, "rb");
    fseek(image_raw, 0, 2);
    long filesize = ftell(image_raw);
    
    /*READ IMAGE DATA*/
    uint16_t * image_data_ptr;
    image_data_ptr = (uint16_t *)malloc(sizeof(uint16_t)*MAX_ROW*MAX_COL);
    fread(image_data_ptr, sizeof(uint16_t), filesize, image_raw);
    fclose(image_raw);
   
   
    /*TEST WRITING THE SAME DATA BACK INTO TEST RAW FILE*/
    FILE *fp;
    fp = fopen("TEST.raw", "w");
    fwrite(image_data_ptr, sizeof(uint16_t), filesize, fp);
    fclose(fp);
    
    return 0;
}
RMarms
  • 79
  • 1
  • 9
  • You probably wanted `filesize/2`. Also use binary mode (`"wb"`). Also you can check whether `fread` or `fwrite` returned any error. – user253751 Jul 20 '21 at 16:12
  • Thanks for your quick response. What is the purpose of using filesize/2? My file size is doubling everytime I write back so I think you're on to something. – RMarms Jul 20 '21 at 16:18
  • You seeked to the end of the file to see how big it was, then tried to read it without seeking again. How much do you think got read from the file? – Mark Ransom Jul 20 '21 at 16:18
  • why using `uint16_t`? – yano Jul 20 '21 at 16:18
  • 2
    After seeking the input file to the end to get its size, you are not seeking back to the beginning of the file before reading the bytes. Also, don't use [magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)) - `fseek(image_raw, 0, SEEK_END);` is easier to understand than `fseek(image_raw, 0, 2);` – Remy Lebeau Jul 20 '21 at 16:18
  • @RMarms You are telling `fwrite` to write `filesize` number of `uint16_t`s (2 bytes each) but `filesize` was measured in bytes – user253751 Jul 20 '21 at 16:19
  • Thank you all, not using fseek() was my issue. – RMarms Jul 20 '21 at 16:28
  • Some side notes: casting the return of `malloc` is [considered bad practice](https://stackoverflow.com/a/605858/2505965) in C, and `int main()` should really be `int main(void)`. – Oka Jul 20 '21 at 16:41

2 Answers2

1

There are multiple issues with your code:

  • lack of error handling.

  • not seeking the input file back to offset 0 after seeking it to get its size. Consider using stat() or equivalent to get the file size without having to seek the file at all.

  • not dividing filesize by sizeof(uint16_t) when reading from the input file, or writing to the output file. filesize is expressed in bytes, but fread/fwrite are expressed in number of items of a given size instead, and your items are not 1 byte in size.

  • not opening the output file in binary mode.

  • leaking the buffer you allocate.

With that said, try something more like this instead:

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

int main()
{
    char filename[32] = "image1.raw";

    FILE *image_raw = fopen(filename, "rb");
    if (!image_raw) {
        fprintf(stderr, "Can't open input file\n");
        return -1;
    }

    if (fseek(image_raw, 0, SEEK_END) != 0) {
        fprintf(stderr, "Can't seek input file\n");
        fclose(image_raw);
        return -1;
    }

    long filesize = ftell(image_raw);
    if (filesize == -1L) {
        fprintf(stderr, "Can't get input file size\n");
        fclose(image_raw);
        return -1;
    }

    rewind(image_raw);

    long numSamples = filesize / sizeof(uint16_t);

    /*READ IMAGE DATA*/
    uint16_t *image_data_ptr = (uint16_t*) malloc(filesize);
    if (!image_data_ptr) {
        fprintf(stderr, "Can't allocate memory\n");
        fclose(image_raw);
        return -1;
    }

    size_t numRead = fread(image_data_ptr, sizeof(uint16_t), numSamples, image_raw);
    if (numRead != numSamples) {
        fprintf(stderr, "Can't read samples from file\n");
        free(image_data_ptr);
        fclose(image_raw);
        return -1;
    }

    fclose(image_raw);
   

    /*TEST WRITING THE SAME DATA BACK INTO TEST RAW FILE*/
    FILE *fp = fopen("TEST.raw", "wb");
    if (!fp) {
        fprintf(stderr, "Can't open output file\n");
        free(image_data_ptr);
        return -1;
    }

    if (fwrite(image_data_ptr, sizeof(uint16_t), numSamples, fp) != numSamples) {
        fprintf(stderr, "Can't write to output file\n");
        fclose(fp);
        free(image_data_ptr);
        return -1;
    }

    fclose(fp);
    free(image_data_ptr);
    
    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Wow, this is such a helpful answer. Your code worked exactly for what I wanted to do. I really appreciate the time you took to write this out and I definitely got a better understanding of file manipulation. – RMarms Jul 20 '21 at 16:44
0

You have already a great answer and useful comments

anyway, consider that if you want to iterate over your file, loaded in memory as a whole, as an array of unsigned words:

  • if the file size could be odd what to do at the last byte/word

  • you may read the file as a whole in a single call, after having the file size determined

  • fstat() is the normal way to get the file size

  • get the file name from the command line as an argument is much more flexible than recompile the program or change the file name in order to use the program

The code below does just that:

  • uses image.raw as a default for the file name, but allowing you to enter the file name on the command line
  • uses fstat() to get the file size
  • uses a single fread() call to read the entire file as a single record

A test using the original program file as input:


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        20/07/2021     17:40           1067 main.c   
                                                                                                                                                   
PS > gcc -Wall -o tst main.c
PS > ./tst main.c
File is "main.c". Size is 1067 bytes
File "main.c" loaded in memory.
PS > ./tst xys
File is "xys". Could not open: No such file or directory

The C example

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

int main(int argc, char**argv)
{
    const char* default_file = "image.raw";
    char    f_name[256];
    if (argc < 2)
        strcpy(f_name, default_file);
    else
        strcpy(f_name, argv[1]);

    FILE* F = fopen(f_name, "rb");
    if (F == NULL)
    {
        printf("File is \"%s\". ", f_name);
        perror("Could not open");
        return -1;       
    }
    struct stat info;
    fstat(_fileno(F),&info);
    printf("File is \"%s\". Size is %lu bytes\n", f_name, info.st_size);

    uint16_t* image = malloc(info.st_size);
    if (image == NULL)
    {   perror("malloc() error");
        return -2;
    };

    if (fread(image, info.st_size, 1, F) != 1)
    {   perror("read error");
        free(image);
        return -3;
    };

    // use 'image'
    printf("File \"%s\" loaded in memory.\n", f_name);
    free(image);
    fclose(F);
    return 0;
}
arfneto
  • 1,227
  • 1
  • 6
  • 13