I am currently facing issues when trying to apply the run-length algorithm to a .bmp picture. When I am doing the encoding, all is working well, but at decoding there is a problem and I cannot figure it out, namely I cannot read the number of repetitive pixels and thus my output is a blank image. When I use the debugger, the number of repetitive pixels is not readed correctly from the binary file (the result of applying RLE on the bitmap file). Here is my code
#include<stdlib.h>
#include <stdint.h>
//structure defiens bitmap header
struct BITMAPFILEHEADER{
uint8_t type[2];//type of file (bit map)
uint32_t size;//size of file
uint16_t reserved1;//
uint16_t reserved2;//
uint32_t offsetbits;//off set bits
} __attribute__ ((packed));
struct BITMAPINFOHEADER{
uint32_t size;//bitmap size
// uint16_t w2;
uint32_t width;//width of bitmap
//uint16_t h2;
uint32_t height;//hight of bitmap
uint16_t planes;
uint16_t bitcount;
uint32_t compression;// compression ratio (zero for no compression)
uint32_t sizeimage;//size of image
long xpelspermeter;
long ypelspermeter;
uint32_t colorsused;
uint32_t colorsimportant;
} __attribute__ ((packed));
//const char* INPUT_FILE = "/home/bogdan/bee.bmp";
const char* INPUT_FILE = "/home/bogdan/Linux.bmp";
const char* ENCODED_FILE = "/home/bogdan/encoded.bin";
const char* DECODED_FILE = "/home/bogdan/decoded.bmp";
typedef struct SINGLE_PIXEL{
uint8_t green;//Green level 0-255
uint8_t red; //Red level 0-255
} PIXEL;
int comparePixels(PIXEL, PIXEL);
void encode();
void decode(char*);
int main()
{
encode();
decode(ENCODED_FILE);
return 0;
}
void encode() {
uint32_t i=0;//to count pixels read
uint32_t pixno=0;//number of pixels to read
struct BITMAPFILEHEADER source_head;//to store file header
struct BITMAPINFOHEADER source_info;//to store bitmap info header
PIXEL pixel;// the current pixel
FILE *in;// bitmap imput pointer file
FILE *out;//output file pointer
if(!(in=fopen(INPUT_FILE,"rb")))//open in binary read mode
{
printf("\ncan not open file");//error at opening file
exit(-1);
}
out=fopen(ENCODED_FILE,"wb");//opne in binary write mode
//read the headers to source file
fread(&source_head,sizeof(struct BITMAPFILEHEADER),1,in);
fread(&source_info,sizeof(struct BITMAPINFOHEADER),1,in);
//write the headers to the output file
fwrite(&source_head,sizeof(struct BITMAPFILEHEADER),1,out);
fwrite(&source_info,sizeof(struct BITMAPINFOHEADER),1,out);
//cumpute the number of pixels to read
pixno=source_info.width*source_info.height;
// init list of pixels
PIXEL pixArr[pixno];
printf("total pixels: %d", pixno);
//printf("w:%f h:%u pn:%lu", (source_head.size/1024.0/1024), source_info.height, pixno);
uint32_t sum = 0;
//read, modify and write pixels
for(i=0;i<pixno;++i)
{
//read pixel form source file
fread(&pixel,sizeof(PIXEL),1,in);
pixArr[i] = pixel;
}
for (i = 0; i < pixno; i++) {
// printf ("i = %d\tred = %d green = %d blue = %d\n",i, pixArr[i].red, pixArr[i].green, pixArr[i].blue);
int runlength = 1;
while ((i + 1) < pixno) {
if (comparePixels(pixArr[i], pixArr[i+1]) == 0){
// printf ("i = %d\t red = %d green = %d blue = %d\n",i, pixArr[i].red, pixArr[i].green, pixArr[i].blue);
runlength++;
i++;
} else {
break;
}
}
//fprintf(out, "%d", runlength);
fwrite(&runlength, sizeof(runlength), 1, out);
fwrite(&pixel,sizeof(PIXEL),1,out);
sum += runlength;
runlength = 0;
}
//write the modification to the output file
//close all fiels
fclose(in);
fclose(out);
printf("sum = %d",sum);
}
void decode(char * filePath) {
uint32_t i=0;//to count pixels read
uint32_t j=0;
uint32_t totalPixels=0;//number of pixels to read
uint32_t pixelRepetition = 1;
struct BITMAPFILEHEADER source_head;//to store file header
struct BITMAPINFOHEADER source_info;//to store bitmap info header
PIXEL pixel;// the current pixel
FILE *in;// bitmap encoded pointer file
FILE *out;//decoded bitmap file pointer
if (!(in = fopen(filePath, "rb"))) {
printf("\ncan not open file");
exit(-1);
}
out = fopen(DECODED_FILE, "wb");
//read the headers to source file
fread(&source_head,sizeof(struct BITMAPFILEHEADER),1,in);
fread(&source_info,sizeof(struct BITMAPINFOHEADER),1,in);
//write the headers to the output file
fwrite(&source_head,sizeof(struct BITMAPFILEHEADER),1,out);
fwrite(&source_info,sizeof(struct BITMAPINFOHEADER),1,out);
totalPixels=source_info.width*source_info.height;
while(i < totalPixels) {
fread(&pixelRepetition, sizeof(pixelRepetition), 1, out);
fread(&pixel,sizeof(PIXEL),1,in);
for (j = 0; j < pixelRepetition; j++) {
fwrite(&pixel,sizeof(PIXEL),1,out);
}
i += pixelRepetition;
}
fclose(in);
fclose(out);
}
int comparePixels(PIXEL px1, PIXEL px2) {
if (px1.red == px2.red && px1.green == px2.green && px1.blue == px2.blue) {
return 0;
} else {
return -1;
}
}
My implementation is working as follow: I read the header of a bmp file and put it directly to another file. After that I compare pixels to see if they have the same RGB values. If this is true (according to comparePixels function) I put the number of consecutive identical pixels and one pixel (the one which is repeated) into the file. This is the encode phase. At decoding, I am reading the header of the image and then I am trying to read the number of repetitions (1 default, means no repetitive pixels) and the pixel which is repeated. I will really appreciate any type of help. Thank you.
You are repeatedly writing out only the very last pixel you read in:
fwrite(&runlength, sizeof(runlength), 1, out);
fwrite(&pixel,sizeof(PIXEL),1,out);
You need to write out your current pixel instead:
fwrite(pixArr[i], sizeof(PIXEL),1,out);
Related
Hy I wanted to read a bmp file into a struct and write it then back but the image is alway black the header is ok. but the pixels get wrong written. I compared the hex values and they are the same until the header is finished. The rest is different and way shorter.
typedef uint16_t ImageType;
typedef struct
{
uint32_t size;
uint16_t additionalFeature;
uint16_t copy;
uint32_t offset;
} ImageHeader;
typedef struct
{
uint32_t headerSize;
int width;
int height;
uint16_t colorSpaces;
uint16_t bitsPerPixel;
uint32_t compression;
uint32_t size;
int verticalResolution;
int horizontalResolution;
uint32_t totalColors;
uint32_t importantColors;
} ImageMetadata;
typedef struct {
uint8_t blue;
uint8_t green;
uint8_t red;
} ImageColors;
typedef struct {
ImageType type;
ImageHeader header;
ImageMetadata metadata;
ImageColors **pixels;
} Image;
Here is my image structure. At the moment it is only for a 24 bpp Image later on I want to change it. But my problem is that after I wrote the image it is not correctly displayed. The header Data are ok but the image is not correct.
the rows are correct only the column is some how duplicated and compressed.
My includes are:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdbool.h>
This is how I read the pixels
void readImage(char *filename, Image *image)
{
FILE *imageFile = fopen(filename, "rb");
if (!imageFile)
{
perror("ReadImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->type), sizeof(ImageType), 1, imageFile);
//validate for correct image type
if (image->type != BMP_IMAGE_TYPE)
{
fprintf(stderr, "%hu", image->type);
perror("ReadImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->header), sizeof(ImageHeader), 1, imageFile);
fread(&(image->metadata), sizeof(ImageMetadata), 1, imageFile);
// Allocate space for the pixels.
image->pixels = malloc( image->metadata.height * sizeof(ImageColors *) );
for(int i = 0; i < image->metadata.height; i++){
image->pixels[i] = malloc(image->metadata.width * sizeof(ImageColors));
}
// Read in each pixel
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
ImageColors px;
if( fread(&px, sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while reading bmp pixel.\n");
return;
}
image->pixels[i][j] = px;
}
}
fclose(imageFile);
}
This is how I write them.
void writeImage(char *filename, Image *image)
{
FILE *imageFile = fopen(filename, "wb+");
if (!imageFile)
{
perror("WriteImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
size_t typeWritten = fwrite(&(image->type), sizeof(image->type), 1, imageFile);
if (typeWritten == 0)
{
perror("WriteImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
size_t headerWritten = fwrite(&(image->header), sizeof(image->header), 1, imageFile);
if (headerWritten == 0)
{
perror("WriteImageHeaderException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
size_t metadataWritten = fwrite(&(image->metadata), sizeof(image->metadata), 1, imageFile);
if (metadataWritten == 0)
{
perror("WriteImageMetadataException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fseek(imageFile, image->header.offset, SEEK_SET);
// Read in each pixel
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
if( fwrite(&image->pixels[i][j], sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while wr bmp pixel.\n");
fclose(imageFile);
exit(EXIT_FAILURE);
}
}
}
fclose(imageFile);
}
Thank you already in advanced. I only included the Methods that are responsible for reading and writing the bmp because in the future I want to add some other features.
#tshiono Thank you I added you lines into it but if I compare the hex files I can see that the image still has some differences:
The output image looks still the same.
My Main function looks like this:
int main(int argc, char **argv) {
int ch;
char *inputFile = "sample.bmp";
char *outputFile = "image-copy.bmp";
Image image;
// try to use port and password from parameter
while ( (ch = getopt_long_only(argc, argv, "", long_options, NULL)) != -1 ) {
switch (ch) {
case 'i':
inputFile = optarg;
break;
case 'o':
outputFile = optarg;
break;
default:
break;
}
}
printf("input File: %s\n", inputFile);
readImage(inputFile, &image);
writeImage(outputFile, &image);
return 0;
}
Thank you for the update.
IMHO the bmp file format is stupidly designed which includes some
pitfalls causing unexpected behaviors.
You may have found if we put the 2-byte MagicNumber "BM" in the header struct,
most compilers automatically put another 2 bytes for boundary alignment.
Another pitfall is we need to keep the byte count
of a row (line) to be the multiple of four for word alignment. Then the
starting addresses of every row is located at the word boundary, although
I'm not sure how much it makes the program run faster.
Anyway if the byte count of the row is NOT the multiple of four
(meaning the width is NOT the multiple of four), we need to put extra
dummy bytes for the adjustment at the end of every row. Then would you
please modify your reading/writing functions as:
readImage:
// Read in each pixel
int padding = image->metadata.width % 4; // add this line
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
ImageColors px;
if( fread(&px, sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while reading bmp pixel.\n");
return;
}
image->pixels[i][j] = px;
}
for (int j = 0; j < padding; j++) getc(imageFile); // add this line
}
writeImage:
// Read in each pixel
int padding = image->metadata.width % 4; // add this line
for (int i = 0; i < image->metadata.height; i++){
for(int j = 0; j < image->metadata.width; j++){
if( fwrite(&image->pixels[i][j], sizeof(ImageColors), 1, imageFile) < 1 ) {
printf("Error while wr bmp pixel.\n");
fclose(imageFile);
exit(EXIT_FAILURE);
}
}
for (int j = 0; j < padding; j++) putc(0, imageFile); // add this line
}
where the variable padding is the count of byte stuffing which can be
calculated based on the value of width.
[Update]
Try to add the line:
fseek(imageFile, image->header.offset, SEEK_SET);
just before the line:
// Read in each pixel
in the readImage() as that in writeImage().
[Explanation]
There are several versions in bmp file format and they have different
header size (which is yet another pitfall):
version header.offset (starting address of pixel data)
BMP v2 54
BMP v3 122
BMP v4 138
While your definitions of structs are based on BMP v2, the provided
bmp file is created in BMP v4. Then it causes the conflicts between
header data and the position of the pixel data. That is why your file
comparison shows the difference.
The viewability of the
collapsed image file will depend on the viewer software. In my environment
there is no problem to view the images.
Ideally we should prepare several types of header structs and use one of them
depending on the version. Practically the extended area contains
less significant data and we may skip them in most cases.
I am currently working on loading a bitmap file in C. I am kind of new to C, and I ran into a problem: I have a file pointer that reads unsigned chars to a struct of rgb pixels, (it's called rgb but it reads in the order of b,g,r and padding - this is the default of bitmap format file). My file is 12x12 pixels and when it reaches to row 9 it putting only the value '204' in each component, whike the image is white (i.e. all components = 255). All the components before this equal 255.
EDIT: I changed the enum to a three defined values returned by the image state (didn't load, blue, not blue).
EDIT2: I edited the code, but now im equals 0xffffffff and cannot be accessed to.
here's the code:
int CalaculateBlueness()
{
bih bih;
bfh bfh;
int counterblue = 0;
hsv hsv;
FILE *filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
//if the file doesn't exist in memory
if (filePtr == NULL)
{
return IMAGE_NOT_LOADED;
}
//read the bitmap file header
fread(&bfh, sizeof(bfh), 1, filePtr);
//verify that this is a bmp file by check bitmap id
if (bfh.bitmap_type[0] != 'B' || bfh.bitmap_type[1] != 'M')
{
fclose(filePtr);
return IMAGE_NOT_LOADED;
}
fclose(filePtr);
//ensure that the filePtr will point at the start of the image info header
filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
fseek(filePtr, 14, SEEK_CUR);
//read the bitmap info header
fread(&bih, sizeof(bih), 1, filePtr);
if (bih.bit_count!=24)
{
return ERROR_BPP;
}
//point the pointer file to the start of the raw data
//fseek(filePtr, bfh.file_size, SEEK_SET);
int size = bih.height * WIDTHBYTES(bih.width * 32);
unsigned char *im = calloc(1, size);
//put the raw bitmap pixel data in strcut rgb array
fread(&im, sizeof(size), 1, filePtr);
//convert each pixel to it's hue value and check if in the range of blue
for (size_t i = 0; i < bih.height; i++)
{
for (size_t j = 0; j < bih.width; j++)
{
hsv =rgbpixel_hue(im);
if (hsv.h>190 && hsv.h<250)
{
counterblue++;
}
fseek(im, 3, SEEK_CUR);
}
}
//check if more than 80% of the image is blue and return the defined state according to the result
if (counterblue > BLUE_THRESHOLD*(bih.height*bih.width))
{
return BLUE;
}
return NOT_BLUE;
}
Reading in bitmaps is always a difficult thing.
There are quite some points to consider.
fread(&im[i][j].res, sizeof(unsigned char), 1, filePtr);
With this line your read the reserved byte of an RGBQUAD from the file. However, this member is not in the file. The image data in the file contains scanlines. See below.
After reading the Bitmap File Header, you open the file again. However, you didn't close it. This might be succesful or it fails because the file was already open. But you don't chek the return value of fopen. Anyway, there is no need to do this because after having read the BFH the filepointer is positioned at the BITMAPINFOHEADER; you can just read it in. And you need to read it in, otherwise you won't know the dimensions of the bitmap.
From MSDN documentation:
The established bitmap file format consists of a BITMAPFILEHEADER structure followed by a BITMAPINFOHEADER [...] structure. An array of RGBQUAD structures (also called a color table) follows the bitmap information header structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).
For 24 bits-per-pixel bitmaps, there is no color table.
so the sequence is now:
//read the bitmap file header
fread(&bfh, sizeof(bfh), 1, filePtr);
//read the bitmap info header
fread(&bih, sizeof(bih), 1, filePtr);
int bpp= bih.biBitCount;
if (bpp != 24) return 0; // error: must be 24 bpp/ 3 bytes per pixel
Now we must calculate the amount of memory needed to store the image. An image consists of heighth rows of width pixels. The rows are called scanlines.
For some reason, a scanline is alligned on a 4 byte boundary. This means that the last bytes of a scanline may not be in use. So the amount of memory to read the bitmap data from the file is the heigth of the image, times the number of scanlines of the image:
// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
int size= bih.biHeight * WIDTHBYTES(bih.biWidth*32));
unsigned char *im= malloc(size);
Finally you can read the data. If there was a color table, you should have skipped that with a seek but since there is none, you can now read the data:
fread(im, size, 1, filePtr);
Now, to address the pixels you cannot use a simple two-dimensional notation governed by width and heigth of the image...due to the scanlines. You can use the following:
int scanlineSize= WIDTHBYTES(bih.biWidth*bih.biBitCount);
unsigned char *p, *scanline= im;
for (int i=0; i<bih.biHeight; i++)
{
p= scanline;
for (int j=0; j<bih.biWidth; j++)
{
g= *p++;
b= *p++;
r= *p++;
}
scanline += scanlineSize;
}
So to address a 3-byte pixel (x,y) use: im[y*scanlineSize + x*3]
.. except that I believe that scanlines are reversed, so the pixel would be at im[(bih.biHeight-y)*scanlinesize + x*3].
UPDATE: complete function
#include <winGDI.h>
// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
unsigned char *readBitmap(char *szFilename)
{
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
int i, j, size, scanlineSize;
unsigned char r, g, b, *p, *img, *scanline;
FILE *filePtr;
if ((filePtr=fopen(szFilename, "rb"))==0) return 0;
//read the bitmap file header
if (fread(&bfh, sizeof(bfh), 1, filePtr)!=1
|| bfh.bfType != 'MB') {fclose(filePtr); return 0;}
//read the bitmap info header
if (fread(&bih, sizeof(bih), 1, filePtr)!=1
|| bih.biSize!=sizeof(bih)) {fclose(filePtr); return 0;}
if (bih.biBitCount != 24) {fclose(filePtr); return 0;} // error: must be 24 bpp/ 3 bytes per pixel
// allocate memory and read the image
scanlineSize= WIDTHBYTES(bih.biWidth * bih.biBitCount);
size= bih.biHeight * scanlineSize;
if ((img= malloc(size))==0) {fclose(filePtr); return 0;}
if (fread(img, size, 1, filePtr)!=1) {free (img); fclose(filePtr); return 0;}
fclose(filePtr);
scanline= img;
for (i=0; i<bih.biHeight; i++)
{
p= scanline;
for (j=0; j<bih.biWidth; j++)
{
g= *p++;
b= *p++;
r= *p++;
}
scanline += scanlineSize;
}
return img;
}
close filePtr before opening file again
fclose(filePtr);
filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
and offset to raw data is bfh.offset
//point the pointer file to the start of the raw data
fseek(filePtr, bfh.offset, SEEK_SET);
I submitted this problem set, but I'm unable to get a full mark grade because the exit code "is expected to be 0 and not a 1". However, if you take a look at the code (the recover.c file), the exit code is 0. What is wrong? The program accomplishes everything it was made for, which is to read through bits in a corrupted file and find the bits that make up a JPG file and write them in a separate file. The only problem I'm having is this aforementioned exit code issue. Please help!
recover.c file
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include "bmp.h"
int main (void){
FILE* card_ptr = fopen("card.raw","r");
if (card_ptr == NULL){
fprintf(stderr,"File Not Found!");
return 1;
}
BYTE buffer[512];
bool found_jpg = false;
FILE* new_jpg_ptr;
int file_counter = 0;
while(fread(buffer,1,512,card_ptr)!=0x00){
if(buffer[0]== 0xff && buffer[1]== 0xd8 && buffer[2]==0xff && (buffer[3] & 0xf0)== 0xe0){
if(!found_jpg){
char filename[8];
sprintf(filename, "%03i.jpg", file_counter++);
found_jpg = true;
new_jpg_ptr = fopen(filename,"w");
if(new_jpg_ptr == NULL){
return 2;
}
fwrite(buffer,1,512,new_jpg_ptr);
}
else {
fclose(new_jpg_ptr);
char filename[8];
sprintf(filename, "%03i.jpg", file_counter++);
found_jpg = true;
new_jpg_ptr = fopen(filename,"w");
if(new_jpg_ptr == NULL){
return 3;
}
fwrite(buffer,1,512, new_jpg_ptr);
}
}
else {
if(found_jpg){
fwrite(buffer,1,512, new_jpg_ptr);
}
}
}
fclose(new_jpg_ptr);
fclose(card_ptr);
return 0;
}
bmp.h file
/**
* BMP-related data types based on Microsoft's own.
*/
#include <stdint.h>
/**
* Common Data Types
*
* The data types in this section are essentially aliases for C/C++
* primitive data types.
*
* Adapted from https://msdn.microsoft.com/en-us/library/cc230309.aspx.
* See http://en.wikipedia.org/wiki/Stdint.h for more on stdint.h.
*/
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
/**
* BITMAPFILEHEADER
*
* The BITMAPFILEHEADER structure contains information about the type, size,
* and layout of a file that contains a DIB [device-independent bitmap].
*
* Adapted from https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx.
*/
typedef struct
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;
/**
* BITMAPINFOHEADER
*
* The BITMAPINFOHEADER structure contains information about the
* dimensions and color format of a DIB [device-independent bitmap].
*
* Adapted from https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx.
*/
typedef struct
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;
/**
* RGBTRIPLE
*
* This structure describes a color consisting of relative intensities of
* red, green, and blue.
*
* Adapted from https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx.
*/
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
I experienced the same problem. My program worked as expected with no obvious reasons for a return code of 1. As you have, I had also included a header file with some definitions in it. Placing those definitions in the core file solved my problem. Perhaps if you move the needed definitions from your bmp.h file into recover.c, check50 will be able to compile.
My solution if you're interested.
// Recover any forgotten JPEGS from a given forensic image
# include <stdio.h>
# include <stdbool.h>
# include <stdint.h>
typedef uint8_t BYTE;
typedef struct
{
BYTE byte_1;
BYTE byte_2;
BYTE byte_3;
BYTE byte_4_min;
BYTE byte_4_max;
} __attribute__((__packed__))
SIG;
// declare helper function prototypes
int check_args(int argc, char *argv[]);
bool signature_is_present(BYTE potential_sig[4], SIG jpeg_sig);
SIG set_jpeg_signature(void);
// main program
int main (int argc, char *argv[])
{
// check input for validity
int check_args_value = check_args(argc, argv);
if (check_args_value != 0)
{
return check_args_value;
}
// call function to set default jpeg signature
SIG jpeg_sig = set_jpeg_signature();
// open buffers and declare other necessary variables for later use
BYTE FAT_block[512];
BYTE first_byte[1];
int signature_offset = -4;
int counter = 0;
// open forensic image
FILE * inptr = fopen(argv[1], "r");
FILE * outptr = NULL;
// loop through file searching for first '255'
while (fread(first_byte, sizeof(BYTE), 1, inptr) == 1)
{
// check if byte is 255, the first byte of a jpeg signature
if (*first_byte == jpeg_sig.byte_1)
{
// check for signature
BYTE possible_sig[4];
// back pointer up one byte to compensate for already discovered 255
fseek(inptr, -1, SEEK_CUR);
// read four bytes from file that could potentially be a signature.
fread(possible_sig, sizeof(possible_sig), 1, inptr);
if (signature_is_present(possible_sig, jpeg_sig))
{
// close previously open write file, if any.
if (outptr != NULL)
{
fclose(outptr);
//Increment counter for image signatures found.
counter++;
}
// setup name for image file
char file_name[8] = {};
snprintf(file_name, 8, "%.3i.jpg\n", counter);
// move pointer back 4 bytes after checking what they contain
fseek(inptr, signature_offset, SEEK_CUR);
//read FAT block from forensic image into buffer
fread(FAT_block, sizeof(FAT_block), 1, inptr);
// open new output file based on current signature
outptr = fopen(file_name, "w");
// write FAT block buffer to file
fwrite(FAT_block, sizeof(FAT_block), 1, outptr);
}
else // byte is 255 but not part of a signature
{
if (outptr != NULL)
{
// move pointer back 4 bytes after checking what they contain
fseek(inptr, signature_offset, SEEK_CUR);
//read FAT block from forensic image into buffer
fread(FAT_block, sizeof(FAT_block), 1, inptr);
// write FAT block buffer to file
fwrite(FAT_block, sizeof(FAT_block), 1, outptr);
}
}
}
else // if byte is not 255
{
if (outptr != NULL)
{
//back up one byte
fseek(inptr, -1, SEEK_CUR);
//read FAT block from forensic image into buffer
fread(FAT_block, sizeof(FAT_block), 1, inptr);
// write FAT block buffer to file
fwrite(FAT_block, sizeof(FAT_block), 1, outptr);
}
}
}
// close files
fclose(inptr);
if (outptr != NULL)
{
fclose(outptr);
}
return 0;
}
// helper functions
int check_args(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Invalid Input.\nForensic image file must be provided.\n");
return 1;
}
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL)
{
fprintf(stderr, "File does not exist.\n");
return 2;
}
fclose(inptr);
return 0;
}
bool signature_is_present(BYTE potential_sig[4], SIG jpeg_sig)
{
if( potential_sig[0] == jpeg_sig.byte_1 &&
potential_sig[1] == jpeg_sig.byte_2 &&
potential_sig[2] == jpeg_sig.byte_3 &&
potential_sig[3] >= jpeg_sig.byte_4_min &&
potential_sig[3] <= jpeg_sig.byte_4_max )
{
return true;
}
return false;
}
SIG set_jpeg_signature(void)
{
SIG jpeg_sig;
jpeg_sig.byte_1 = 255;
jpeg_sig.byte_2 = 216;
jpeg_sig.byte_3 = 255;
jpeg_sig.byte_4_min = 224;
jpeg_sig.byte_4_max = 239;
return jpeg_sig;
}
Well basically I wrote this program for my computer course (shoutout CS50) that recovers images from a .raw file. I have managed to have the program recover 48 of the 50 files in that file.
The issue im having right now with the program is that the program cannot recover both the first and the second file located on .raw. It either reads and writes the very first file (this girl in a snowy background) or the second file on the .raw (guy behind books).
For some reason if I change fopen from write to append I can switch between the photo of the girl and the guy, but I cant seem to be able to open both.
https://github.com/CoreData/cs50/blob/master/pset4/jpg/card.raw
This is the link to card.raw, unfortunately its not the same one that Im using but even using this one you get two different images for image1.jpg depending on whether you have fopen with an "a" or "w".
Any ideas???
if you guys want any additional info just let me know
#include <stdio.h>
#include <stdlib.h>
#include "bmp2.h"
int main(void)
{
/*OPEN CARD FILE*/
char* infile = "card.raw";;
FILE* card = fopen(infile, "r");
if (card == NULL)
{
printf("Could not open %s.\n", "card.raw");
return 2;
}
int f = 0, c = 0, l = 0, x = 128, imageno = 1;
// c signals that a jpg is being written
// l size control, 0 means 0 jpgs
FILE* images;
char* title = (char*)malloc(15);
/*repeat until end of card*/
do
{
//read one block into buffer
INTROJPG *buffer = (INTROJPG*)malloc(sizeof(INTROJPG)*x);
for (int i = 0; i < 128; i++)
{
fread(&buffer[i], sizeof(INTROJPG), 1, card);
}
if (buffer[0].first == 0xff && buffer[0].second == 0xd8 && buffer[0].third == 0xff)
{
sprintf(title, "image%d.jpg", imageno); //change jpg title
if (f == 1) //close previous jpg
{
fclose(images);
imageno++;
}
images = fopen(title, "w");
f = 1; //very first jpg has been opened
c = 1; //jpg open
l++; //jpg count + 1
}
//jpg already open?
if (c == 1)
{
for (int i = 0; i < 128; i++)
{
fwrite(&buffer[i], sizeof(INTROJPG), 1, images);
}
}
free(buffer);
}
while (l < 50);
free(title);
return 5;
//close any remaining files
}
and this is my bmp2.h file
#include <stdint.h>
/**
* Common Data Types
*
* The data types in this section are essentially aliases for C/C++
* primitive data types.
*
* Adapted from http://msdn.microsoft.com/en-us/library/cc230309.aspx.
* See http://en.wikipedia.org/wiki/Stdint.h for more on stdint.h.
*/
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
/**
* BITMAPFILEHEADER
*
* The BITMAPFILEHEADER structure contains information about the type, size,
* and layout of a file that contains a DIB [device-independent bitmap].
*
* Adapted from http://msdn.microsoft.com/en-us/library/dd183374(VS.85).aspx.
*/
typedef struct
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;
/**
* BITMAPINFOHEADER
*
* The BITMAPINFOHEADER structure contains information about the
* dimensions and color format of a DIB [device-independent bitmap].
*
* Adapted from http://msdn.microsoft.com/en-us/library/dd183376(VS.85).aspx.
*/
typedef struct
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;
/**
* RGBTRIPLE
*
* This structure describes a color consisting of relative intensities of
* red, green, and blue.
*
* Adapted from http://msdn.microsoft.com/en-us/library/aa922590.aspx.
*/
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
typedef struct
{
BYTE first;
BYTE second;
BYTE third;
BYTE fourth;
} __attribute__((__packed__))
INTROJPG;
typedef struct
{
BYTE image;
}
BYTEIMAGE;
First things first, I'll try to improve a few things in your code. I've also done this pset and it is nice to help others.
INTROJPG *buffer = (INTROJPG*)malloc(sizeof(INTROJPG)*x);
At this part, you know that the size of both INTROJPG and x are constant, so there is no need to constantly allocate and free memory at every iteration, that takes much more time than simply creating a normal array. Also, why is the buffer a pointer to INTROJPG? If it is only to test for a header at each iteration, I don't think it is worth it, you could simply access the first 4 bytes of a normal BYTE array.
I'd create a static array of 512 BYTEs (the struct on the library), because this is the size you are constantly allocating and freeing and also you are using BYTEs, not INTROJPGs.
Second, at this section and another similar one:
for (int i = 0; i < 128; i++)
{
fread(&buffer[i], sizeof(INTROJPG), 1, card);
}
There is absolutely no need for this loop or, again, even using INTROJPG. You are always reading and writing 512 bytes, you could use:
fread(buffer, 4, 128, card);
// or even better
fread(buffer, 512, 1, card);
Now about your problem, I've tested your code (without any modifications) multiple times and found nothing wrong with image1.jpg and image2.jpg. Yes, I changed "w" mode to "a" and vice-versa.
However, your code is faulty in regard to the last image, your last image is image49.jpg, when it should be image50.jpg, and your image49.jpg does not even open, and that's because the loop is finished before the rest of image49.jpg is stored, i.e., you are storing only the first 512 bytes of image49.jpg.
To fix that, I've changed the condition of the do-while loop to keep going until the end of the card file, IIRC the problem guarantees the last block being part of the last image or something like that, if not, it's up to you to fix this little problem!
#include <stdio.h>
#include <stdlib.h>
#include "bmp2.h"
int main(void)
{
/*OPEN CARD FILE*/
char* infile = "card.raw";;
FILE* card = fopen(infile, "r");
if (card == NULL)
{
printf("Could not open %s.\n", "card.raw");
return 2;
}
int f = 0, c = 0, imageno = 1;
// c signals that a jpg is being written
// l size control, 0 means 0 jpgs
FILE* images;
char title[25];
BYTE buffer[512];
/*repeat until end of card*/
do
{
fread(buffer, 512, 1, card);
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff)
{
sprintf(title, "image%d.jpg", imageno); //change jpg title
if (f == 1) //close previous jpg
{
fclose(images);
imageno++;
}
images = fopen(title, "w");
f = 1; //very first jpg has been opened
c = 1; //jpg open
}
//jpg already open?
if (c == 1) fwrite(buffer, 512, 1, images);
}
while (!feof(card));
return 5;
//close any remaining files
}
One last thing, why are you returning 5 at the end of the program? Just curious.
This is my function, I am using the headers BMP according to wikipedia BITMAPINFOHEADER. But, I am getting a file without any image...when putting padding, the process stops.
// Structures for header info
#pragma pack(push,1)
/* Windows 3.x bitmap file header */
typedef struct {
char filetype[2]; /* magic - always 'B' 'M' */
unsigned int filesize;
short reserved1;
short reserved2;
unsigned int dataoffset; /* offset in bytes to actual bitmap data */
} file_header;
/* Windows 3.x bitmap full header, including file header */
typedef struct {
file_header fileheader;
unsigned int headersize;
int width;
int height;
short planes;
short bitsperpixel; /* we only support the value 24 here */
unsigned int compression; /* we do not support compression */
unsigned int bitmapsize;
int horizontalres;
int verticalres;
unsigned int numcolors;
unsigned int importantcolors;
} bitmap_header;
#pragma pack(pop)
int RGB2GREY(char* input, char *greyImage) {
//variable declaration:
FILE *fp, *grey;
bitmap_header* hp;
int n;
char *data;
int oldBitsperpixel;
//Open input file:
fp = fopen(input, "rb");
if(fp==NULL){
//cleanup
}
//Read the input file headers:
hp=(bitmap_header*)malloc(sizeof(bitmap_header));
if(hp==NULL)
return 3;
n=fread(hp, sizeof(bitmap_header), 1, fp);
if(n<1){
//cleanup
}
//Read the data of the image:
data = (char*)malloc(sizeof(char)*hp->bitmapsize);
if(data==NULL){
//cleanup
}
//Put me in the position after header...
fseek(fp,sizeof(char)*hp->fileheader.dataoffset,SEEK_SET);
printf("Width %d and Height %d\n",hp->width,hp->height);
int i, j;
unsigned char BGR[3];
unsigned colorIntensity[3];
/*unsigned char bmppad[hp->width] = {0};*/
printf("New bitmapSize %d\n\n",hp->bitsperpixel);
//Open greayImage file:
grey = fopen(greyImage, "wb");
if(grey==NULL){
//cleanup
}
//Writes the header
n=fwrite(hp,sizeof(char),sizeof(bitmap_header),grey);
if(n<1){
//cleanup
}
//Again going to position after header
fseek(out,sizeof(char)*hp->fileheader.dataoffset,SEEK_SET);
for (i=0; i<hp->height; i++){
for (j=0; j<hp->width; j++){
//Reading pixel by pixel
fread(BGR, 3, 1, fp); //1 unsigned char of 3 positions
unsigned char colorGrey;
colorGrey = (unsigned char) 0.3*BGR[2] + 0.6*BGR[1] + 0.1*BGR[0];
colorIntensity[2] = colorGrey;
colorIntensity[1] = colorGrey;
colorIntensity[0] = colorGrey;
/*printf("B %d G %d R %d ",BGR[0],BGR[1],BGR[2]);
printf("Gray %d ",colorIntensity);*/
fwrite(colorIntensity, 3, 1, grey);
}
/*
// Adding pad option1
//fwrite(bmppad, sizeof(bmppad), 1, grey);
//Adding pad option2
for (j=0; j>hp->width; j++){
fwrite(0, 1, 1, grey);
}*/
}
fclose(fp);
fclose(grey);
free(hp);
free(data);
return 0;
}
In the grey output file, I get nothing...moreover, I wonder if there is a way to reduce from 24 to 8 bits.
ps. My code came from reading/writing bmp files in c
The formula came from Create greyscale BMP from RGB BMP
Thanks,
You are essentially turning a 32-bit color bitmap into a 32-bit grey bitmap by changing the color values in a way that they appear grey (you are not saving any space in this way; the bitmap stays as large as it was). Anayway, it explains why you do not need to adapt the bitmap header.
But when you read every three bytes and change every three bytes, you do not take scanlines into account.
An image consists of scanlines and a scanline consits of pixels. Scanlines are alligned on even word boundaries so the last few bytes of a scanline are unused (and the scanline is thus a bit longer than all the pixels on it).
To properly process the input and create the output, your loop must be:
(EDIT: updated to use 1 byte per pixel output):
#pragma pack(push,1)
typedef struct {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} pal_entry;
#pragma pack(pop)
int ToGreyScale(FILE *fp, FILE *grey, bitmap_header *hp)
{
int i, j;
int iScanlineSizeIn = ((hp->width * hp->bitsperpixel) + 31) / 32 * 4;
int iScanlineSizeOut= ((hp->width * 8 ) + 31) / 32 * 4;
unsigned char *scanlineIn = malloc(iScanlineSizeIn), *pIn;
unsigned char *scanlineOut= malloc(iScanlineSizeOut), *pOut;
pal_entry pal[256];
for (i=0; i<256; i++) // create a gray scale palette
{pal[i].rgbBlue= i; pal[i].rgbGreen= i; pal[i].rgbRed= i;}
hp->bitsperpixel= 8; // set output bits-per-pixel
hp->fileheader.filesize= sizeof(bitmap_header) + sizeof(pal) + hp->width*iScanlineSizeOut;
fwrite(hp, sizeof(bitmap_header), 1, grey); // write the header...
fwrite(pal, 256*sizeof(pal_entry), 1, grey); //..followed by palette
for (i=0; i<hp->height; i++)
{
if (fread(scanlineIn, iScanlineSizeIn, 1, fp) != 1) return(0);
pIn = scanlineIn;
pOut= scanlineOut;
for (j=0; j<hp->width; j++)
{
*pOut++ = (unsigned char) ((0.1 * *pIn++) + (0.6 * *pIn++) + (0.3 * *pIn++));
}
fwrite(scanlineOut, iScanlineSizeOut, 1, grey);
}
free(scanlineIn);
free(scanlineOut);
return(1);
}