-
-
Save pankoza2-pl/258ba202b577d957ddadaedd89c43124 to your computer and use it in GitHub Desktop.
Applying Filters by Manipulating Pixel Values!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This program enables you to apply some filters to your .bmp images | |
// greyscale, blur, and sepia filters work. | |
#include <getopt.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include "helpers.h" | |
int main(int argc, char *argv[]) | |
{ | |
// Define allowable filters | |
char *filters = "bgrs"; | |
// 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; | |
// Grayscale | |
case 'g': | |
grayscale(height, width, image); | |
break; | |
// Reflection | |
case 'r': | |
reflect(height, width, image); | |
break; | |
// Sepia | |
case 's': | |
sepia(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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "helpers.h" | |
#include <math.h> | |
#include <stdio.h> | |
#include <string.h> | |
// Convert image to grayscale | |
void grayscale(int height, int width, RGBTRIPLE image[height][width]) | |
{ | |
for (int i = 0; i < height; i++) | |
{ | |
for (int j = 0; j < width; j++) | |
{ | |
// Get average RGB values! | |
int average = round((float)(image[i][j].rgbtBlue + image[i][j].rgbtRed + image[i][j].rgbtGreen) / 3); | |
image[i][j].rgbtBlue = average; | |
image[i][j].rgbtRed = average; | |
image[i][j].rgbtGreen = average; | |
} | |
} | |
return; | |
} | |
// Convert image to sepia | |
void sepia(int height, int width, RGBTRIPLE image[height][width]) | |
{ | |
for (int i = 0; i < height; i++) | |
{ | |
for (int j = 0; j < width; j++) | |
{ | |
// use the Sepia conversion | |
int sepiaRed = round(.393 * image[i][j].rgbtRed + .769 * image[i][j].rgbtGreen + .189 * image[i][j].rgbtBlue); | |
int sepiaGreen = round(.349 * image[i][j].rgbtRed + .686 * image[i][j].rgbtGreen + .168 * image[i][j].rgbtBlue); | |
int sepiaBlue = round(.272 * image[i][j].rgbtRed + .534 * image[i][j].rgbtGreen + .131 * image[i][j].rgbtBlue); | |
if (sepiaBlue > 255) | |
{ | |
sepiaBlue = 255; | |
} | |
if (sepiaRed > 255) | |
{ | |
sepiaRed = 255; | |
} | |
if (sepiaGreen > 255) | |
{ | |
sepiaGreen = 255; | |
} | |
// store the result in the image pixel. | |
image[i][j].rgbtBlue = sepiaBlue; | |
image[i][j].rgbtRed = sepiaRed; | |
image[i][j].rgbtGreen = sepiaGreen; | |
} | |
} | |
return; | |
} | |
// Reflect image horizontally | |
void reflect(int height, int width, RGBTRIPLE image[height][width]) | |
{ | |
int half = round(width / 2); | |
for (int i = 0; i < height; i++) | |
{ | |
for (int j = 0; j < half; j++) | |
{ | |
// initialize a temp struct to hold an RGB value | |
RGBTRIPLE tempRGB; | |
// put the select pixel in temp slot | |
tempRGB.rgbtBlue = image[i][j].rgbtBlue; | |
tempRGB.rgbtRed = image[i][j].rgbtRed; | |
tempRGB.rgbtGreen = image[i][j].rgbtGreen; | |
// switch the selected pixel with its opposite. | |
image[i][j].rgbtBlue = image[i][width - 1 - j].rgbtBlue; | |
image[i][j].rgbtRed = image[i][width - 1 - j].rgbtRed; | |
image[i][j].rgbtGreen = image[i][width - 1 - j].rgbtGreen; | |
// assign the selected pixel to its opposite. | |
image[i][width - 1 - j].rgbtBlue = tempRGB.rgbtBlue; | |
image[i][width - 1 - j].rgbtRed = tempRGB.rgbtRed; | |
image[i][width - 1 - j].rgbtGreen = tempRGB.rgbtGreen; | |
} | |
} | |
return; | |
} | |
// Blur image | |
void blur(int height, int width, RGBTRIPLE image[height][width]) | |
{ | |
RGBTRIPLE tempIMG[height][width]; | |
for (int i = 0; i < height; i++) | |
{ | |
for (int j = 0; j < width; j++) | |
{ | |
int redAvg = 0; | |
int blueAvg = 0; | |
int greenAvg = 0; | |
int blueSum = image[i][j].rgbtBlue; | |
int redSum = image[i][j].rgbtRed; | |
int greenSum = image[i][j].rgbtGreen; | |
int count = 1; | |
// check top left | |
if ((i - 1) >= 0 && (j - 1) >= 0) | |
{ | |
blueSum += image[i - 1][j - 1].rgbtBlue; | |
redSum += image[i - 1][j - 1].rgbtRed; | |
greenSum += image[i - 1][j - 1].rgbtGreen; | |
count++; | |
} | |
// check top center | |
if ((i - 1) >= 0) | |
{ | |
blueSum += image[i - 1][j].rgbtBlue; | |
redSum += image[i - 1][j].rgbtRed; | |
greenSum += image[i - 1][j].rgbtGreen; | |
count++; | |
} | |
// check top right | |
if ((i - 1) >= 0 && (j + 1) < width) | |
{ | |
blueSum += image[i - 1][j + 1].rgbtBlue; | |
redSum += image[i - 1][j + 1].rgbtRed; | |
greenSum += image[i - 1][j + 1].rgbtGreen; | |
count++; | |
} | |
// check right | |
if ((j + 1) < width) | |
{ | |
blueSum += image[i][j + 1].rgbtBlue; | |
redSum += image[i][j + 1].rgbtRed; | |
greenSum += image[i][j + 1].rgbtGreen; | |
count++; | |
} | |
// check bottom right | |
if ((i + 1) < height && (j + 1) < width) | |
{ | |
blueSum += image[i + 1][j + 1].rgbtBlue; | |
redSum += image[i + 1][j + 1].rgbtRed; | |
greenSum += image[i + 1][j + 1].rgbtGreen; | |
count++; | |
} | |
// check bottom middle | |
if ((i + 1) < height) | |
{ | |
blueSum += image[i + 1][j].rgbtBlue; | |
redSum += image[i + 1][j].rgbtRed; | |
greenSum += image[i + 1][j].rgbtGreen; | |
count++; | |
} | |
// check bottom left | |
if ((i + 1) < height && (j - 1) >= 0) | |
{ | |
blueSum += image[i + 1][j - 1].rgbtBlue; | |
redSum += image[i + 1][j - 1].rgbtRed; | |
greenSum += image[i + 1][j - 1].rgbtGreen; | |
count++; | |
} | |
// check left | |
if ((j - 1) >= 0) | |
{ | |
blueSum += image[i][j - 1].rgbtBlue; | |
redSum += image[i][j - 1].rgbtRed; | |
greenSum += image[i][j - 1].rgbtGreen; | |
count++; | |
} | |
// get RGB average of all surrounding pixels | |
redAvg = round((float)redSum / count); | |
blueAvg = round((float)blueSum / count); | |
greenAvg = round((float)greenSum / count); | |
// assign those to a slot in a temp array that mirrors our image[] | |
tempIMG[i][j].rgbtBlue = blueAvg; | |
tempIMG[i][j].rgbtRed = redAvg; | |
tempIMG[i][j].rgbtGreen = greenAvg; | |
} | |
} | |
for (int i = 0; i < height; i++) | |
{ | |
for (int j = 0; j < width; j++) | |
{ | |
// copy the temp array items to the image and overwrite their original values. | |
image[i][j].rgbtBlue = tempIMG[i][j].rgbtBlue; | |
image[i][j].rgbtRed = tempIMG[i][j].rgbtRed; | |
image[i][j].rgbtGreen = tempIMG[i][j].rgbtGreen; | |
} | |
} | |
return; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "bmp.h" | |
// Convert image to grayscale | |
void grayscale(int height, int width, RGBTRIPLE image[height][width]); | |
// Convert image to sepia | |
void sepia(int height, int width, RGBTRIPLE image[height][width]); | |
// Reflect image horizontally | |
void reflect(int height, int width, RGBTRIPLE image[height][width]); | |
// Blur image | |
void blur(int height, int width, RGBTRIPLE image[height][width]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment