From 3643dbea1a81364df28d4103f93c40754dbb2a4a Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Sat, 16 Jan 2021 18:54:56 +0900 Subject: [PATCH] WIP blue_noise impl --- Makefile | 6 ++++-- src/blue_noise.cpp | 37 +++++++++++++++++++++++++++++++ src/blue_noise.hpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/blue_noise.cpp create mode 100644 src/blue_noise.hpp diff --git a/Makefile b/Makefile index c02c895..9e06ccc 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,14 @@ -COMMON_FLAGS=-Wall -Wextra -Wpedantic +COMMON_FLAGS=-Wall -Wextra -Wpedantic -std=c++17 ifdef DEBUG CPPFLAGS=${COMMON_FLAGS} -g -O0 else CPPFLAGS=${COMMON_FLAGS} -O3 -DNDEBUG endif -SOURCES=src/main.cpp +SOURCES= \ + src/main.cpp \ + src/blue_noise.cpp OBJECTS=${subst .cpp,.o,${SOURCES}} all: Dithering diff --git a/src/blue_noise.cpp b/src/blue_noise.cpp new file mode 100644 index 0000000..63a2488 --- /dev/null +++ b/src/blue_noise.cpp @@ -0,0 +1,37 @@ +#include "blue_noise.hpp" + +#include + +std::vector dither::blue_noise(std::size_t width, std::size_t height) { + std::size_t count = width * height; + std::vector dither_array; + dither_array.resize(count); + + std::vector pbp; // Prototype Binary Pattern + pbp.resize(count); + + std::default_random_engine re(std::random_device{}()); + std::uniform_int_distribution dist(0, count - 1); + + // initialize pbp + for(std::size_t i = 0; i < count; ++i) { + if(i < count / 2) { + pbp[i] = true; + } else { + pbp[i] = false; + } + } + // randomize pbp + for(std::size_t i = 0; i < count-1; ++i) { + decltype(dist)::param_type range{i+1, count-1}; + std::size_t ridx = dist(re, range); + // probably can't use std::swap since using std::vector + bool temp = pbp[i]; + pbp[i] = pbp[ridx]; + pbp[ridx] = temp; + } + + + + return {}; +} diff --git a/src/blue_noise.hpp b/src/blue_noise.hpp new file mode 100644 index 0000000..7f587d7 --- /dev/null +++ b/src/blue_noise.hpp @@ -0,0 +1,54 @@ +#ifndef BLUE_NOISE_HPP +#define BLUE_NOISE_HPP + +#include +#include +#include + +namespace dither { + +std::vector blue_noise(std::size_t width, std::size_t height); + +namespace internal { + inline std::size_t twoToOne(std::size_t x, std::size_t y, std::size_t width) { + return x + y * width; + } + + inline std::tuple oneToTwo(std::size_t i, std::size_t width) { + return {i % width, i / width}; + } + + constexpr double mu_squared = 1.5 * 1.5; + + inline double gaussian(double x, double y) { + return std::exp(-std::sqrt(x*x + y*y)/(2*mu_squared)); + } + + inline double filter( + const std::vector& pbp, + std::size_t x, std::size_t y, + std::size_t width) { + double sum = 0.0; + + // Should be range -M/2 to M/2, but size_t cannot be negative, so range + // is 0 to M. + // p' = (M + x - (p - M/2)) % M = (3M/2 + x - p) % M + // q' = (M + y - (q - M/2)) % M = (3M/2 + y - q) % M + for(std::size_t q = 0; q <= width; ++q) { + std::size_t q_prime = (3 * width / 2 + y - q) % width; + for(std::size_t p = 0; p <= width; ++p) { + std::size_t p_prime = (3 * width / 2 + x - p) % width; + bool pbp_value = pbp[twoToOne(p_prime, q_prime, width)]; + if(pbp_value) { + sum += gaussian((double)p - width/2.0, (double)q - width/2.0); + } + } + } + + return sum; + } +} // namespace dither::internal + +} // namespace dither + +#endif