WIP blue_noise impl

This commit is contained in:
Stephen Seo 2021-01-16 18:54:56 +09:00
parent b455a469fe
commit 3643dbea1a
3 changed files with 95 additions and 2 deletions

View file

@ -1,12 +1,14 @@
COMMON_FLAGS=-Wall -Wextra -Wpedantic COMMON_FLAGS=-Wall -Wextra -Wpedantic -std=c++17
ifdef DEBUG ifdef DEBUG
CPPFLAGS=${COMMON_FLAGS} -g -O0 CPPFLAGS=${COMMON_FLAGS} -g -O0
else else
CPPFLAGS=${COMMON_FLAGS} -O3 -DNDEBUG CPPFLAGS=${COMMON_FLAGS} -O3 -DNDEBUG
endif endif
SOURCES=src/main.cpp SOURCES= \
src/main.cpp \
src/blue_noise.cpp
OBJECTS=${subst .cpp,.o,${SOURCES}} OBJECTS=${subst .cpp,.o,${SOURCES}}
all: Dithering all: Dithering

37
src/blue_noise.cpp Normal file
View file

@ -0,0 +1,37 @@
#include "blue_noise.hpp"
#include <random>
std::vector<bool> dither::blue_noise(std::size_t width, std::size_t height) {
std::size_t count = width * height;
std::vector<std::size_t> dither_array;
dither_array.resize(count);
std::vector<bool> pbp; // Prototype Binary Pattern
pbp.resize(count);
std::default_random_engine re(std::random_device{}());
std::uniform_int_distribution<std::size_t> 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>
bool temp = pbp[i];
pbp[i] = pbp[ridx];
pbp[ridx] = temp;
}
return {};
}

54
src/blue_noise.hpp Normal file
View file

@ -0,0 +1,54 @@
#ifndef BLUE_NOISE_HPP
#define BLUE_NOISE_HPP
#include <vector>
#include <tuple>
#include <cmath>
namespace dither {
std::vector<bool> 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<std::size_t, std::size_t> 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<bool>& 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