Skip to content

pset 4

filter

Implement a program that applies filters to BMPs, per the below.

Terminal window
$ ./filter -r image.bmp reflected.bmp

Filtering options:

  • -g = grayscale
  • -r = reflect
  • -b = blur
  • -e = edges
  • Directoryimages
    • courtyard.bmp
    • stadium.bmp
    • tower.bmp
    • yard.bmp
  • bmp.h
  • filter
  • filter.c
  • helpers.c
  • helpers.h
  • Makefile
  • out.bmp
bmp.h
// 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 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;
filter.c
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include "helpers.h"
int main(int argc, char *argv[])
{
// Define allowable filters
char *filters = "begr";
// Get filter flag and check validity
char filter = getopt(argc, argv, filters);
if (filter == '?')
{
fprintf(stderr, "Invalid filter.\n");
return 1;
}
// Ensure only one filter
if (getopt(argc, argv, filters) != -1)
{
fprintf(stderr, "Only one filter allowed.\n");
return 2;
}
// Ensure proper usage
if (argc != optind + 2)
{
fprintf(stderr, "Usage: filter [flag] infile outfile\n");
return 3;
}
// Remember filenames
char *infile = argv[optind];
char *outfile = argv[optind + 1];
// Open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", infile);
return 4;
}
// Open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
fprintf(stderr, "Could not create %s.\n", outfile);
return 5;
}
// Read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
// Read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
// Ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
bi.biBitCount != 24 || bi.biCompression != 0)
{
fclose(outptr);
fclose(inptr);
fprintf(stderr, "Unsupported file format.\n");
return 6;
}
int height = abs(bi.biHeight);
int width = bi.biWidth;
// Allocate memory for image
RGBTRIPLE(*image)[width] = calloc(height, width * sizeof(RGBTRIPLE));
if (image == NULL)
{
fprintf(stderr, "Not enough memory to store image.\n");
fclose(outptr);
fclose(inptr);
return 7;
}
// Determine padding for scanlines
int padding = (4 - (width * sizeof(RGBTRIPLE)) % 4) % 4;
// Iterate over infile's scanlines
for (int i = 0; i < height; i++)
{
// Read row into pixel array
fread(image[i], sizeof(RGBTRIPLE), width, inptr);
// Skip over padding
fseek(inptr, padding, SEEK_CUR);
}
// Filter image
switch (filter)
{
// Blur
case 'b':
blur(height, width, image);
break;
// Edges
case 'e':
edges(height, width, image);
break;
// Grayscale
case 'g':
grayscale(height, width, image);
break;
// Reflect
case 'r':
reflect(height, width, image);
break;
}
// Write outfile's BITMAPFILEHEADER
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);
// Write outfile's BITMAPINFOHEADER
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);
// Write new pixels to outfile
for (int i = 0; i < height; i++)
{
// Write row to outfile
fwrite(image[i], sizeof(RGBTRIPLE), width, outptr);
// Write padding at end of row
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// Free memory for image
free(image);
// Close infile
fclose(inptr);
// Close outfile
fclose(outptr);
return 0;
}
helpers.c
#include "helpers.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
// Convert image to grayscale
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
float avg;
int val;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
avg = (float)(image[i][j].rgbtBlue + image[i][j].rgbtGreen + image[i][j].rgbtRed) / 3.0;
val = round(avg);
image[i][j].rgbtBlue = image[i][j].rgbtGreen = image[i][j].rgbtRed = val;
}
}
return;
}
// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE temp;
for (int i = 0; i < height; i++)
{
for (int j = 0, k = width - 1; j < width / 2; j++, k--)
{
temp = image[i][j];
image[i][j] = image[i][k];
image[i][k] = temp;
}
}
return;
}
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE blurred[height][width];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int cnt = 0;
int sumRed = 0;
int sumBlue = 0;
int sumGreen = 0;
for (int k = i - 1; k <= i + 1; k++)
{
for (int l = j - 1; l <= j + 1; l++)
{
if ((k >= 0) && (k < height) && (l >= 0) && (l < width))
{
sumBlue += image[k][l].rgbtBlue;
sumGreen += image[k][l].rgbtGreen;
sumRed += image[k][l].rgbtRed;
cnt++;
}
}
}
float avgBlue = sumBlue / (float) cnt;
float avgGreen = sumGreen / (float) cnt;
float avgRed = sumRed / (float) cnt;
blurred[i][j].rgbtBlue = round(avgBlue);
blurred[i][j].rgbtGreen = round(avgGreen);
blurred[i][j].rgbtRed = round(avgRed);
}
}
//copy blurred image to the original
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
image[i][j] = blurred[i][j];
}
}
return;
}
// Detect edges
void edges(int height, int width, RGBTRIPLE image[height][width])
{
int Gxk[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
int Gyk[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};
RGBTRIPLE(*edged)[width] = calloc(height, width * sizeof(RGBTRIPLE));
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int GxRed = 0, GyRed = 0, GxGreen = 0, GyGreen = 0, GxBlue = 0, GyBlue = 0;
for (int k = i - 1; k <= i + 1; k++)
{
for (int l = j - 1; l <= j + 1; l++)
{
if ((k >= 0) && (k < height) && (l >= 0) && (l < width))
{
GxRed += (image[k][l].rgbtRed * Gxk[k - i + 1][l - j + 1]);
GyRed += (image[k][l].rgbtRed * Gyk[k - i + 1][l - j + 1]);
GxGreen += (image[k][l].rgbtGreen * Gxk[k - i + 1][l - j + 1]);
GyGreen += (image[k][l].rgbtGreen * Gyk[k - i + 1][l - j + 1]);
GxBlue += (image[k][l].rgbtBlue * Gxk[k - i + 1][l - j + 1]);
GyBlue += (image[k][l].rgbtBlue * Gyk[k - i + 1][l - j + 1]);
}
}
}
// combine the directional values
double Red = sqrt((double)(GxRed * GxRed + GyRed * GyRed));
double Blue = sqrt((double)(GxBlue * GxBlue + GyBlue * GyBlue));
double Green = sqrt((double)(GxGreen * GxGreen + GyGreen * GyGreen));
// cap the values at 255
Red = (Red > 255) ? 255 : Red;
Green = (Green > 255) ? 255 : Green;
Blue = (Blue > 255) ? 255 : Blue;
// round the values and save it
edged[i][j].rgbtRed = round(Red);
edged[i][j].rgbtGreen = round(Green);
edged[i][j].rgbtBlue = round(Blue);
}
}
// copy edged image to the original
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
image[i][j] = edged[i][j];
}
}
free(edged);
return;
}
helpers.h
#include "bmp.h"
// Convert image to grayscale
void grayscale(int height, int width, RGBTRIPLE image[height][width]);
// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width]);
// Detect edges
void edges(int height, int width, RGBTRIPLE image[height][width]);
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width]);
// Makefile
filter:
    clang -fsanitize=signed-integer-overflow -fsanitize=undefined -ggdb3 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -o filter filter.c helpers.c

recover

Implement a program that recovers JPEGs from a forensic image, per the below.

Terminal window
$ ./recover card.raw
  • card.raw
  • recovered-images
  • recover.c
recover.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
typedef uint8_t BYTE;
int main(int argc, char *argv[])
{
// Ensure proper use
if (argc != 2)
{
printf("Usage: ./recover image\n");
return 1;
}
// Save the filename
char *infile = argv[1];
// Open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
fclose(inptr);
fprintf(stderr, "Could not open %s\n", infile);
return 1;
}
// Recovery
BYTE *buffer = malloc(512 * sizeof(BYTE));
char *imgname = malloc(10 * sizeof(char));
bool firstFound = false, running = false;
int imgnameCnt = 0;
sprintf(imgname, "%03i.jpg", imgnameCnt);
FILE *img = fopen(imgname, "w");
while (fread(buffer, 512 * sizeof(BYTE), 1, inptr) == 1)
{
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
if (firstFound == false)
{
firstFound = true;
fwrite(buffer, 512 * sizeof(BYTE), 1, img);
running = true;
}
else
{
fclose(img);
running = false;
sprintf(imgname, "%03i.jpg", ++imgnameCnt);
img = fopen(imgname, "w");
fwrite(buffer, 512 * sizeof(BYTE), 1, img);
running = true;
}
}
else
{
if (running == true)
{
fwrite(buffer, 512 * sizeof(BYTE), 1, img);
}
}
}
fclose(img);
fclose(inptr);
free(buffer);
free(imgname);
}


© 2020-2025 Ucchas Muhury