This program allows you to edit the photo itself by entering the
photo name, path, filter type as parameters.
Examples: changing the color of the photo, applying blur, and
mirror effects.
Language: C
I have posted the code below for testing.
Create the files filter.c, helpers.c, bmp.h, and helpers.h in
the same folder.
Parameters for filter effects:
-b (Blur)
-g (Grayscale)
-r (Reflection)
-s (Sepia)
-e (Edge)
Compile.
Run the program with the command which is in the "How to use"
section. The parameter is set to -g.
#include<getopt.h>#include<stdio.h>#include<stdlib.h>#include"helpers.h"intmain(int argc,char*argv[]){// Define allowable filterschar*filters ="bgrs";// Get filter flag and check validitychar filter =getopt(argc, argv, filters);if(filter =='?'){fprintf(stderr,"Invalid filter.\n");return1;}// Ensure only one filterif(getopt(argc, argv, filters)!=-1){fprintf(stderr,"Only one filter allowed.\n");return2;}// Ensure proper usageif(argc != optind +2){fprintf(stderr,"Usage: filter [flag] infile outfile\n");return3;}// Remember filenameschar*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);return4;}// Open output file
FILE *outptr =fopen(outfile,"w");if(outptr ==NULL){fclose(inptr);fprintf(stderr,"Could not create %s.\n", outfile);return5;}// 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.0if(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");return6;}int height =abs(bi.biHeight);int width = bi.biWidth;// Allocate memory for imageRGBTRIPLE(*image)[width]=calloc(height, width *sizeof(RGBTRIPLE));if(image ==NULL){fprintf(stderr,"Not enough memory to store image.\n");fclose(outptr);fclose(inptr);return7;}// Determine padding for scanlinesint padding =(4-(width *sizeof(RGBTRIPLE))%4)%4;// Iterate over infile's scanlinesfor(int i =0; i < height; i++){// Read row into pixel arrayfread(image[i],sizeof(RGBTRIPLE), width, inptr);// Skip over paddingfseek(inptr, padding,SEEK_CUR);}// Filter imageswitch(filter){// Blurcase'b':blur(height, width, image);break;// Grayscalecase'g':grayscale(height, width, image);break;// Reflectioncase'r':reflect(height, width, image);break;// Edgescase'e':edges(height, width, image);break;// Sepiacase's':sepia(height, width, image);break;}// Write outfile's BITMAPFILEHEADERfwrite(&bf,sizeof(BITMAPFILEHEADER),1, outptr);// Write outfile's BITMAPINFOHEADERfwrite(&bi,sizeof(BITMAPINFOHEADER),1, outptr);// Write new pixels to outfilefor(int i =0; i < height; i++){// Write row to outfilefwrite(image[i],sizeof(RGBTRIPLE), width, outptr);// Write padding at end of rowfor(int k =0; k < padding; k++){fputc(0x00, outptr);}}// Free memory for imagefree(image);// Close infilefclose(inptr);// Close outfilefclose(outptr);return0;}
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.
*/typedefuint8_t BYTE;typedefuint32_t DWORD;typedefint32_t LONG;typedefuint16_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.
*/typedefstruct{
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.
*/typedefstruct{
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.
*/typedefstruct{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;}__attribute__((__packed__))
RGBTRIPLE;
/*
description: Photo filters: Grayscale, blur, reflect and sepia
@file filter.c
@author Jeremy B.
@version 1.0 3 Nov 2020
*/#include"helpers.h"#include<math.h>// Convert image to grayscalevoidgrayscale(int height,int width, RGBTRIPLE image[height][width]){//2D arrays calculating color for each pixel and do an average of it to replace on the original//divide sum of colors value by 3.0 for average being rounded// rowfor(int i =0; i < height; i++){// columnfor(int j =0; j < width; j++){
RGBTRIPLE pixel = image[i][j];int avg =round((pixel.rgbtRed + pixel.rgbtGreen + pixel.rgbtBlue)/3.0);
image[i][j].rgbtRed = image[i][j].rgbtGreen = image[i][j].rgbtBlue = avg;}}return;}// Convert image to sepiavoidsepia(int height,int width, RGBTRIPLE image[height][width]){//2D arrays calculating color for each pixel using cs50 - pset4 given formula for sepia pixel//pixel value must not go over 255 and should be capped at 255 if over, value must be rounded//rowfor(int i =0; i < height; i++){//columnfor(int j =0; j < width; j++){
RGBTRIPLE pixel = image[i][j];int sepiaRed =round(.393* pixel.rgbtRed +.769* pixel.rgbtGreen +.189* pixel.rgbtBlue);int sepiaGreen =round(.349* pixel.rgbtRed +.686* pixel.rgbtGreen +.168* pixel.rgbtBlue);int sepiaBlue =round(.272* pixel.rgbtRed +.534* pixel.rgbtGreen +.131* pixel.rgbtBlue);//if value over 255, capped at 255 and original value replacementif(sepiaRed >255){
sepiaRed =255;
image[i][j].rgbtRed = sepiaRed;}else{
image[i][j].rgbtRed = sepiaRed;}if(sepiaGreen >255){
sepiaGreen =255;
image[i][j].rgbtGreen = sepiaGreen;}else{
image[i][j].rgbtGreen = sepiaGreen;}if(sepiaBlue >255){
sepiaBlue =255;
image[i][j].rgbtBlue = sepiaBlue;}else{
image[i][j].rgbtBlue = sepiaBlue;}}}return;}// Reflect image horizontallyvoidreflect(int height,int width, RGBTRIPLE image[height][width]){//setting swap
RGBTRIPLE swap[height][width];//2D array with an invert reading row and target pixel to swapfor(int i =0; i < height; i++){//target pixel to swap at first position in a rowint target =0;//reverse reading, target current pixelfor(int j = width -1; j >=0; j--, target++){
swap[i][target]= image[i][j];}}//swap loopfor(int i =0; i < height; i++){for(int j =0; j < width; j++){
image[i][j]= swap[i][j];}}return;}// Blur imagevoidblur(int height,int width, RGBTRIPLE image[height][width]){//set swap color
RGBTRIPLE swap[height][width];//image rowfor(int i =0; i < height; i++){//image columnfor(int j =0; j < width; j++){//set sum RGBfloat sum_red =0;float sum_blue =0;float sum_green =0;//set counterint counter =0;//set 2 area array, row and column for looking around current pixelint row_area[]={i -1, i, i +1};int col_area[]={j -1, j, j +1};//loop row area, up - current - down, around targeted pixelfor(int row =0; row <3; row++){//loop column area, left side - current - right side, around targeted pixelfor(int col =0; col <3; col++){//set currentint row_current = row_area[row];int col_current = col_area[col];if(row_current >=0&& row_current < height && col_current >=0&& col_current < width){
RGBTRIPLE pixel = image[row_current][col_current];//sum total of current pixel iterations (and around area) for each colors
sum_red += pixel.rgbtRed;
sum_green += pixel.rgbtGreen;
sum_blue += pixel.rgbtBlue;//counter++
counter++;}}}//determine value of total / count into swap
swap[i][j].rgbtRed =round(sum_red / counter);
swap[i][j].rgbtGreen =round(sum_green / counter);
swap[i][j].rgbtBlue =round(sum_blue / counter);}}//swap loopfor(int i =0; i < height; i++){for(int j =0; j < width; j++){
image[i][j]= swap[i][j];}}return;}// Detect edgesvoidedges(int height,int width, RGBTRIPLE image[height][width]){//set swap
RGBTRIPLE swap[height][width];//sobel operator algorithm//Gx kernel 3x3int Gx[3][3]={{-1,0,1},{-2,0,2},{-1,0,1}};//Gy Kernel 3x3int Gy[3][3]={{-1,-2,-1},{0,0,0},{1,2,1}};//image rowfor(int i =0; i < height; i++){//image columnfor(int j =0; j < width; j++){//set Gx RGBfloat Gx_red =0;float Gx_blue =0;float Gx_green =0;//set Gy RGBfloat Gy_red =0;float Gy_blue =0;float Gy_green =0;//set 2 area array, row and column for looking around current pixelint row_area[]={i -1, i, i +1};int col_area[]={j -1, j, j +1};//loop row area, up - current - down, around targeted pixelfor(int row =0; row <3; row++){//loop column area, left side - current - right side, around targeted pixelfor(int col =0; col <3; col++){//set currentint row_current = row_area[row];int col_current = col_area[col];if(row_current >=0&& row_current < height && col_current >=0&& col_current < width){
RGBTRIPLE pixel = image[row_current][col_current];//sum total of current pixel / position iterations (and around area) for each colors * kernels Gx and Gy positions
Gx_red += Gx[row][col]* pixel.rgbtRed;
Gx_green += Gx[row][col]* pixel.rgbtGreen;
Gx_blue += Gx[row][col]* pixel.rgbtBlue;
Gy_red += Gy[row][col]* pixel.rgbtRed;
Gy_green += Gy[row][col]* pixel.rgbtGreen;
Gy_blue += Gy[row][col]* pixel.rgbtBlue;}}}//Gy and Gx square root caluculationint GxGyRed =round(sqrt((Gx_red * Gx_red)+(Gy_red * Gy_red)));int GxGyGreen =round(sqrt((Gx_green * Gx_green)+(Gy_green * Gy_green)));int GxGyBlue =round(sqrt((Gx_blue * Gx_blue)+(Gy_blue * Gy_blue)));//determine value to swap, if value over 255, value is capped at 255if(GxGyRed >255){
GxGyRed =255;
swap[i][j].rgbtRed = GxGyRed;}else{
swap[i][j].rgbtRed = GxGyRed;}if(GxGyGreen >255){
GxGyGreen =255;
swap[i][j].rgbtGreen = GxGyGreen;}else{
swap[i][j].rgbtGreen = GxGyGreen;}if(GxGyBlue >255){
GxGyBlue =255;
swap[i][j].rgbtBlue = GxGyBlue;}else{
swap[i][j].rgbtBlue = GxGyBlue;}}}//swap loopfor(int i =0; i < height; i++){for(int j =0; j < width; j++){
image[i][j]= swap[i][j];}}return;}