Skip to content

Instantly share code, notes, and snippets.

@geekskick
Last active June 25, 2016 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save geekskick/42ffe03b607ab6bb1dce9fcc1c5e7491 to your computer and use it in GitHub Desktop.
Save geekskick/42ffe03b607ab6bb1dce9fcc1c5e7491 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <math.h>
/// Uses luminosity to convert colour to greyscale
#define LUM_R 0.21F
#define LUM_G 0.72F
#define LUM_B 0.07F
/// Show message on terminal instead of in the stdout file
#define DEBUG_PRINT(msg) fprintf(stderr,"[DEBUG %d]: %s\n", __LINE__, msg)
struct pixel{
int red;
int green;
int blue;
};
void print_header(const unsigned int width, const unsigned int height, const unsigned int max_rgb, const char* magic_num, FILE *fd){
fprintf(fd, "%s\n", magic_num);
fprintf(fd, "%d %d\n", width, height);
if(max_rgb > 1){
fprintf(fd, "%d\n", max_rgb);
}
}
void print_image(float **image, const unsigned int width, const unsigned int height, FILE *fp){
for(int h = 0; h < height; h++){
for(int w = 0; w < width; w++){
fprintf(fp, "%d ", (int)image[h][w]);
}
fprintf(fp, "\n");
}
}
/// Colours range from 1 (black) to 0 (white) in a P1 format image
/// round the greyscale byte value to a 0 or 1 as required.
float find_closest_colour(const int pixel_value, const int rgb_max, const int max_val, const int min_val){
int mid_point = rgb_max / 2.0;
return pixel_value >= mid_point ? max_val: min_val;
}
void dither_image(float **image, const unsigned int width, const unsigned int height, const unsigned int rgb_max){
DEBUG_PRINT("Floyding");
unsigned int x, y; ///< For traversing through the image's rows and cols
float old_pixel, ///< The pixel removed from the image
new_pixel, ///< The manipulated pixel to add
q_err; ///< The quantisation error could be a -ve number so sign this
for(y = 0; y < height; y++){
for(x = 0; x < width; x++){
old_pixel = image[y][x];
/// Get new pixel value by thresholding 0 or whatever the max brightness was in the
/// original image
new_pixel = find_closest_colour(old_pixel, rgb_max, rgb_max, 0.0);
/// Calculate the difference between
q_err = old_pixel - new_pixel;
/*
char t[70];
sprintf(t, "q_err: (y: %d, x: %d) \t(o:%d n: %d)\t %d\n", y, x, old_pixel, new_pixel, q_err);
DEBUG_PRINT(t);
*/
/// change the Value into a range to either 0 or 1, then put it in the pixel
image[y][x] = find_closest_colour(new_pixel, rgb_max, 0.0, 1.0);
/// Check bounds of the memory to prevent segfault
if(x < (width - 1)){
image[y ][x + 1] += q_err * (7.0/16.0);
}
if(y < (height - 1)){
image[y + 1][x ] += q_err * (5.0/16.0);
if(x > 0){
image[y + 1][x - 1] += q_err * (3.0/16.0);
}
if(y < (height - 1)){
image[y + 1][x + 1] += q_err * (1.0/16.0);
}
}
}
}
}
float scale_number(const float number, const unsigned int max_rgb, const int max_scale){
float ratio = (float)max_scale/(float)max_rgb;
return ratio * number;
}
void bayer(float** image, const unsigned int width, const unsigned int height, const int max_rgb){
DEBUG_PRINT("Bayering");
static const int matrix_size = 4;
static const int b_matrix[matrix_size][matrix_size] = {
{ 1, 9, 3, 11 },
{ 13, 5, 15, 7 },
{ 4, 12, 2, 10 },
{ 16, 8, 14, 6 }
};
int matrix_scale = (int)pow((float)matrix_size, 2.0);
for(unsigned int h = 0; h < height; h++){
for(unsigned int w = 0; w < width; w++){
float scaled_pix = scale_number(image[h][w], max_rgb, matrix_scale);
int mat_number = b_matrix[h % matrix_size][w % matrix_size];
float new_pix = scaled_pix > mat_number? 0.0: 1.0;
image[h][w] = new_pix;
}
}
}
int main(int argc, const char **argv){
if(argc != 2){
printf("Usage:./ditherimage < <input_imagepath>.ppm > <output_imagepath>.pbm\n");
}
char b_m = argv[argc - 1][0];
unsigned int width, height, max_rgb;
char id[3], t[70];
struct pixel temp;
FILE *fp;
scanf("%s\n", id);
DEBUG_PRINT(id);
scanf("%d %d\n", &width, &height);
scanf("%d\n", &max_rgb);
sprintf(t, "(w: %d h: %d)", width, height);
DEBUG_PRINT(t);
/// Heap memory needed for the image
/// Get the rows
float **image;
image = calloc(height, sizeof(image));
if(!image){
DEBUG_PRINT("Error in calloc outer");
exit(1);
}
/// Get the columns
for(unsigned int h = 0; h < height; h++){
image[h] = calloc(width, sizeof(image[h]));
if(!image[h]) {
DEBUG_PRINT("Error in calloc inner");
exit(1);
}
}
DEBUG_PRINT("Reading image");
//go over the rows
for(unsigned int h = 0; h < height; h++){
for(unsigned int w = 0; w < width; w++){
scanf("%d %d %d\n", &temp.red, &temp.green, &temp.blue);
//greyscale it
image[h][w] = (LUM_R * temp.red) + (LUM_G * temp.green) + (LUM_B * temp.blue);
}
}
DEBUG_PRINT("Image read in and is now luminescent");
/// Save the luminsecent image - just to see what it looks like
fp = fopen("lum.pgm", "w");
if(!fp){
DEBUG_PRINT("Error opening file to write to");
perror("File IO");
}
else{
print_header(width, height, max_rgb, "P2", fp);
print_image(image, width, height, fp);
fclose(fp);
}
if(b_m == 'b'){
bayer(image, width, height, max_rgb);
}
else if(b_m == 'f'){
dither_image(image, width, height, max_rgb);
}
else{
DEBUG_PRINT("Error in args");
exit(1);
}
DEBUG_PRINT("Printing Image");
print_header(width, height, 0, "P1", stdout);
print_image(image, width, height, stdout);
DEBUG_PRINT("Image printed");
/// Finally free used Heap memory
for(int h = height; h < height; h++){
free(image[h]);
image[h] = NULL;
}
free(image);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment