Browse Source

Initial commit

master
Golikov Ivan 6 years ago
commit
0521da63df
9 changed files with 510 additions and 0 deletions
  1. +2
    -0
      .gitignore
  2. +19
    -0
      CMakeLists.txt
  3. +22
    -0
      LICENSE
  4. +27
    -0
      Makefile
  5. +49
    -0
      README.md
  6. +8
    -0
      gaussian_blur/CMakeLists.txt
  7. +217
    -0
      gaussian_blur/gblur.c
  8. +22
    -0
      gaussian_blur/gblur.h
  9. +144
    -0
      main.c

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
build
.idea

+ 19
- 0
CMakeLists.txt View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 2.8)
project(gblur)

find_package(JPEG REQUIRED)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wpedantic -std=c11 -lm")

set(SOURCE_FILES
main.c)

include_directories(gaussian_blur)

add_executable(gblur ${SOURCE_FILES})

add_subdirectory(gaussian_blur)

set_property(TARGET gblur PROPERTY CXX_STANDARD 11)

target_link_libraries(gblur gaussian_blur)

+ 22
- 0
LICENSE View File

@@ -0,0 +1,22 @@
Copyright (c) <2016>, <Golikov Ivan>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 27
- 0
Makefile View File

@@ -0,0 +1,27 @@
SHELL := /bin/bash
RM := rm -rf
MKDIR := mkdir -p

all: ./build/Makefile
@ $(MAKE) -C build

./build/Makefile:
@ ($(MKDIR) build > /dev/null)
@ (cd build > /dev/null 2>&1 && cmake ..)

distclean:
@ ($(MKDIR) build > /dev/null)
@ (cd build > /dev/null 2>&1 && cmake .. > /dev/null 2>&1)
@- $(MAKE) --silent -C build clean || true
@- $(RM) ./build/Makefile
@- $(RM) ./build/src
@- $(RM) ./build/test
@- $(RM) ./build/CMake*
@- $(RM) ./build/cmake.*
@- $(RM) ./build/*.cmake
@- $(RM) ./build/*.txt

ifeq ($(findstring distclean,$(MAKECMDGOALS)),)
$(MAKECMDGOALS): ./build/Makefile
@ $(MAKE) -C build $(MAKECMDGOALS)
endif

+ 49
- 0
README.md View File

@@ -0,0 +1,49 @@
Gaussian blur
=============

This simple program performs a gaussian blur on given image, using «sliding window» technique, which allows to
compute resulting image with O(n × M × N ) + O(m × M × N ) complexity, where M × N stands for input image dimensions and
m, n stands for 6 * sigma (sigma is passed as a parameter to program).

Downloading
-----------

You can clone this repo with git:

`git clone https://bitbucket.org/ivnglkv/gblur`

## Dependencies

This program uses [libjpeg](http://ijg.org/) library.

## Compiling

In order to build this program you should have `cmake` and `make` installed. Also install development headers for libjpeg
library.

After that, build it with

`make`

Output binary will be placed into `build` directory.

## Usage

`gblur -i input_file [-o output_file] [-s radius] [-h]`

Options:

t-i: path to file with input RGB JPEG image.
-o: path to file where blurred RGB JPEG image will be recorded. If no path will be specified,
input file will be overwritten!
-s: radius of blur. Default is 5.
-h: show this help.

Example of usage:
`gblur -i /home/user/Images/img.jpg -o /home/user/Images/blurred_img.jpg -s 5`

## License

Copyright (c) 2016, Golikov Ivan

Licensed under [The BSD-3 Clause License]()

+ 8
- 0
gaussian_blur/CMakeLists.txt View File

@@ -0,0 +1,8 @@
project(gblur)

set(SOURCES "gblur.c")
set(HEADERS "gblur.h")
add_library(gaussian_blur ${SOURCES} ${HEADERS})

target_link_libraries(gaussian_blur ${JPEG_LIBRARY})
target_link_libraries(gaussian_blur m)

+ 217
- 0
gaussian_blur/gblur.c View File

@@ -0,0 +1,217 @@
#include <stdio.h>
#include <math.h>

#include "gblur.h"

// This function reads image pixels and some metadata from given file into struct image_data
// and returns a pointer to created struct.
// Don't forget to call destroy_image_data() after using this function!!!
struct image_data * decompress_image(FILE * file) {
struct image_data * result;

result = malloc(sizeof(struct image_data));

if (result == NULL)
return NULL;

struct jpeg_decompress_struct image_info;
struct jpeg_error_mgr jpeg_error;

jpeg_create_decompress(&image_info);
image_info.err = jpeg_std_error(&jpeg_error);

jpeg_stdio_src(&image_info, file);

jpeg_read_header(&image_info, TRUE);

jpeg_start_decompress(&image_info);

result->x = image_info.output_width;
result->y = image_info.output_height;
result->num_channels = image_info.num_components;


if (result->x > MAX_IMAGE_SIDE_SIZE
|| result->y > MAX_IMAGE_SIDE_SIZE
|| result->num_channels != 3) {
return NULL;
}

size_t data_size = result->x
* result->y
* result->num_channels;

JSAMPROW buffer[1];

result->pixels = (JSAMPROW)malloc(data_size);

if (result->pixels == NULL)
return NULL;

while (image_info.output_scanline < result->y) {
// dirty pointer math to find place where to write pixels from next line in 1D-array
buffer[0] = result->pixels
+ result->num_channels
* result->x
* image_info.output_scanline;

jpeg_read_scanlines(&image_info, buffer, 1);
}

jpeg_finish_decompress(&image_info);
jpeg_destroy_decompress(&image_info);

return result;
}

int8_t compress_image(struct image_data * image, FILE * file, uint8_t quality) {
if (image == NULL || image->pixels == NULL)
return 1;

struct jpeg_compress_struct image_info;
struct jpeg_error_mgr jpeg_error;

image_info.err = jpeg_std_error(& jpeg_error);
jpeg_create_compress(& image_info);

jpeg_stdio_dest(& image_info, file);

image_info.image_width = image->x;
image_info.image_height = image->y;
image_info.input_components = image->num_channels;
if (image->num_channels == 3) {
image_info.in_color_space = JCS_RGB;
}

jpeg_set_defaults(& image_info);
jpeg_set_quality(& image_info, quality, TRUE);
jpeg_start_compress(& image_info, TRUE);

JSAMPROW row_pointer[1];
int row_stride;
row_stride = image_info.image_width * image->num_channels;

while (image_info.next_scanline < image_info.image_height) {
row_pointer[0] = image->pixels + image_info.next_scanline * row_stride;
jpeg_write_scanlines(& image_info, row_pointer, 1);
}

jpeg_finish_compress(& image_info);
jpeg_destroy_compress(& image_info);

return 0;
}

void destroy_image_data(struct image_data * image) {
if (image != NULL) {
free(image->pixels);
free(image);
}
}

int8_t gaussian_blur(struct image_data * original_image, struct image_data * blurred_image, double sigma) {
if (original_image->x != blurred_image->x
|| original_image->y != blurred_image->y
|| original_image->num_channels != blurred_image->num_channels) {
return 1;
}

size_t n = (size_t)ceil(3 * sigma);
size_t window_size = n * 2 + 1;
double window[window_size];

window[n] = 1;
for (size_t i = n + 1; i < window_size; ++i) {
double foo = -pow(i - n, 2);
double s2 = 2 * sigma * sigma;
window[i] = exp(foo / s2);
window[window_size - i - 1] = window[i];
}

// Horizontal box blur
JSAMPROW tmp;

JDIMENSION x = original_image->x;
JDIMENSION y = original_image->y;
size_t num_channels = original_image->num_channels;

tmp = (JSAMPROW)malloc(x * num_channels);
for (int j = 0; j < y; ++j) {
for (int i = 0; i < x; ++i) {
double sum = 0;

double blurred_red = 0;
double blurred_green = 0;
double blurred_blue = 0;
for (int k = 0; k < window_size; ++k) {
uint64_t l = i + (k - n);
// start index of pixel, making contribution to blurring current pixel
// image->pixels[pixel_index], image->pixels[pixel + 1], image->pixels[pixel + 2] are holding
// values of red, green and blue colors of pixel respectively
uint64_t pixel_index = (num_channels * x) * j + num_channels * l;

if (l >= 0 && l < x) {
blurred_red += original_image->pixels[pixel_index] * window[k];
blurred_green += original_image->pixels[pixel_index + 1] * window[k];
blurred_blue += original_image->pixels[pixel_index + 2] * window[k];

sum += window[k];
}
}

blurred_red = floor(blurred_red / sum);
blurred_green = floor(blurred_green / sum);
blurred_blue = floor(blurred_blue / sum);

tmp[num_channels * i] = (unsigned char)blurred_red;
tmp[num_channels * i + 1] = (unsigned char)blurred_green;
tmp[num_channels * i + 2] = (unsigned char)blurred_blue;
}

for (int m = 0; m < num_channels * x; ++m) {
blurred_image->pixels[m+ j * x * num_channels] = tmp[m];
}
}

// Vertical box blur
for (int i = 0; i < x; ++i) {
for (int j = 0; j < y; ++j) {
double sum = 0;

double blurred_red = 0;
double blurred_green = 0;
double blurred_blue = 0;
for (int k = 0; k < window_size; ++k) {
uint64_t l = j + (k - n);
uint64_t pixel = num_channels * x * l + num_channels * i;

if (l >= 0 && l < y) {

blurred_red += blurred_image->pixels[pixel] * window[k];
blurred_green += blurred_image->pixels[pixel + 1] * window[k];
blurred_blue += blurred_image->pixels[pixel + 2] * window[k];

sum += window[k];
}
}

blurred_red = floor(blurred_red / sum);
blurred_green = floor(blurred_green / sum);
blurred_blue = floor(blurred_blue / sum);

tmp[num_channels * j] = (JSAMPLE)blurred_red;
tmp[num_channels * j + 1] = (JSAMPLE)blurred_green;
tmp[num_channels * j + 2] = (JSAMPLE)blurred_blue;
}

for (int m = 0; m < num_channels * y; m+=3) {
int column = m / 3;
blurred_image->pixels[num_channels * x * column + num_channels * i] = tmp[m];
blurred_image->pixels[num_channels * x * column + num_channels * i + 1] = tmp[m + 1];
blurred_image->pixels[num_channels * x * column + num_channels * i + 2] = tmp[m + 2];
}
}
free(tmp);

return 0;
}

+ 22
- 0
gaussian_blur/gblur.h View File

@@ -0,0 +1,22 @@
#pragma once

#include <stdint.h>
#include <stdlib.h>
#include <jpeglib.h>

#define MAX_IMAGE_SIDE_SIZE 64000

struct image_data {
JDIMENSION x;
JDIMENSION y;
uint8_t num_channels;
unsigned char * pixels;
};

struct image_data * decompress_image(FILE *);

int8_t compress_image(struct image_data *, FILE *, uint8_t);

void destroy_image_data(struct image_data *);

int8_t gaussian_blur(struct image_data *, struct image_data *, double);

+ 144
- 0
main.c View File

@@ -0,0 +1,144 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <unistd.h>
#include "gblur.h"

void print_help() {
printf("This program performs gaussian blur on given image using sigma parameter.\n"
"\n"
"Usage:\n"
"\tgblur -i input_file [-o output_file] [-s radius] [-h]\n"
"Options:\n"
"\t-i: path to file with input RGB JPEG image.\n"
"\t-o: path to file where blurred RGB JPEG image will be recorded. If no path will be specified, "
"input file will be overwritten!\n"
"\t-s: radius of blur. Default is 5.\n"
"\t-h: show this help.\n"
"\n"
"Example of usage:"
"\tgblur -i /home/user/Images/img.jpg -o /home/user/Images/blurred_img.jpg -s 5\n");
}

int main(int argc, char *argv[]) {
int opt;
double sigma = 0.0;
char *input_filename = NULL;
char *out_filename = NULL;

// Parsing passed arguments
while ((opt = getopt(argc, argv, "i:o:s:h")) != -1) {
switch(opt) {
case 'i':
input_filename = optarg;
break;
case 'o':
out_filename = optarg;
break;
case 's':
sigma = atof(optarg);
break;
case 'h':
print_help();
exit(0);
break;
case ':':
printf("Option needs a value\n");
break;
case '?':
printf("Unknown option\n");
break;
default:
break;
}
}

if (input_filename) {
if (access(input_filename, R_OK) == -1) {
fprintf(stderr, "Can not read file %s\n", input_filename);
exit(1);
}
} else {
fprintf(stderr, "Please specify input filename!\n");
print_help();
exit(1);
}

if (sigma == 0.0) {
printf("Value of sigma is 0, so using default value: 5.0\n");
sigma = 5.0;
}

FILE *infile;

if ((infile = fopen(input_filename, "rb")) == NULL) {
fprintf(stderr, "Can not open %s\n", input_filename);
exit(1);
}

struct image_data * original_image = decompress_image(infile);

if (original_image == NULL) {
fprintf(stderr, "Failed to decompress image %s\n", input_filename);
exit(1);
}

FILE *outfile;

if ((outfile = fopen(out_filename, "wb")) == NULL) {
fprintf(stderr, "Can not open %s\n", out_filename);
exit(1);
}

struct image_data * blurred_image;
blurred_image = malloc(sizeof(struct image_data));

if (blurred_image == NULL) {
fprintf(stderr, "Failed to allocate memory!\n");

destroy_image_data(original_image);

exit(1);
}

blurred_image->x = original_image->x;
blurred_image->y = original_image->y;
blurred_image->num_channels = original_image->num_channels;

size_t data_size = blurred_image->x
* blurred_image->y
* blurred_image->num_channels;

blurred_image->pixels = (unsigned char *)malloc(data_size);

int8_t blur_result;

blur_result = gaussian_blur(original_image, blurred_image, sigma);

if (blur_result != 0) {
fprintf(stderr, "Failed to blur image!\n");

destroy_image_data(original_image);
destroy_image_data(blurred_image);

exit(1);
}

int8_t compress_result;
compress_result = compress_image(blurred_image, outfile, 90);

if (compress_result != 0) {
fprintf(stderr, "Failed to write blurred image to file!\n");

destroy_image_data(original_image);
destroy_image_data(blurred_image);

exit(1);
}

destroy_image_data(original_image);
destroy_image_data(blurred_image);

return 0;
}

Loading…
Cancel
Save