clang-format --style=google
This commit is contained in:
parent
62d15771ad
commit
ecd65fc42b
9 changed files with 1446 additions and 1480 deletions
|
@ -12,17 +12,21 @@ Args::Args()
|
|||
output_filename_("output.png") {}
|
||||
|
||||
void Args::DisplayHelp() {
|
||||
std::cout
|
||||
<< "[-h | --help] [-b <size> | --blue-noise <size>] [--usecl | "
|
||||
"--nousecl]\n"
|
||||
" -h | --help\t\t\t\tDisplay this help text\n"
|
||||
" -b <size> | --blue-noise <size>\tGenerate blue noise square with "
|
||||
"size\n"
|
||||
" --usecl | --nousecl\t\t\tUse/Disable OpenCL (enabled by default)\n"
|
||||
" -t <int> | --threads <int>\t\tUse CPU thread count when not using "
|
||||
"OpenCL\n"
|
||||
" -o <filelname> | --output <filename>\tOutput filename to use\n"
|
||||
" --overwrite\t\t\t\tEnable overwriting of file (default disabled)\n";
|
||||
std::cout << "[-h | --help] [-b <size> | --blue-noise <size>] [--usecl | "
|
||||
"--nousecl]\n"
|
||||
" -h | --help\t\t\t\tDisplay this help text\n"
|
||||
" -b <size> | --blue-noise <size>\tGenerate blue noise "
|
||||
"square with "
|
||||
"size\n"
|
||||
" --usecl | --nousecl\t\t\tUse/Disable OpenCL (enabled by "
|
||||
"default)\n"
|
||||
" -t <int> | --threads <int>\t\tUse CPU thread count when "
|
||||
"not using "
|
||||
"OpenCL\n"
|
||||
" -o <filelname> | --output <filename>\tOutput filename to "
|
||||
"use\n"
|
||||
" --overwrite\t\t\t\tEnable overwriting of file (default "
|
||||
"disabled)\n";
|
||||
}
|
||||
|
||||
bool Args::ParseArgs(int argc, char **argv) {
|
||||
|
@ -53,7 +57,8 @@ bool Args::ParseArgs(int argc, char **argv) {
|
|||
std::strcmp(argv[0], "--threads") == 0)) {
|
||||
threads_ = std::strtoul(argv[1], nullptr, 10);
|
||||
if (threads_ == 0) {
|
||||
std::cout << "ERROR: Failed to parse thread count, using 4 by default"
|
||||
std::cout << "ERROR: Failed to parse thread count, using 4 by "
|
||||
"default"
|
||||
<< std::endl;
|
||||
threads_ = 4;
|
||||
}
|
||||
|
|
|
@ -4,19 +4,19 @@
|
|||
#include <string>
|
||||
|
||||
struct Args {
|
||||
Args();
|
||||
Args();
|
||||
|
||||
static void DisplayHelp();
|
||||
static void DisplayHelp();
|
||||
|
||||
/// Returns true if help was printed
|
||||
bool ParseArgs(int argc, char **argv);
|
||||
/// Returns true if help was printed
|
||||
bool ParseArgs(int argc, char **argv);
|
||||
|
||||
bool generate_blue_noise_;
|
||||
bool use_opencl_;
|
||||
bool overwrite_file_;
|
||||
unsigned int blue_noise_size_;
|
||||
unsigned int threads_;
|
||||
std::string output_filename_;
|
||||
bool generate_blue_noise_;
|
||||
bool use_opencl_;
|
||||
bool overwrite_file_;
|
||||
unsigned int blue_noise_size_;
|
||||
unsigned int threads_;
|
||||
std::string output_filename_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,44 +1,45 @@
|
|||
int twoToOne(int x, int y, int width, int height) {
|
||||
while(x < 0) {
|
||||
x += width;
|
||||
}
|
||||
while(y < 0) {
|
||||
y += height;
|
||||
}
|
||||
x = x % width;
|
||||
y = y % height;
|
||||
return x + y * width;
|
||||
while (x < 0) {
|
||||
x += width;
|
||||
}
|
||||
while (y < 0) {
|
||||
y += height;
|
||||
}
|
||||
x = x % width;
|
||||
y = y % height;
|
||||
return x + y * width;
|
||||
}
|
||||
|
||||
//float gaussian(float x, float y) {
|
||||
// return exp(-(x*x + y*y) / (1.5F * 1.5F * 2.0F));
|
||||
//}
|
||||
// float gaussian(float x, float y) {
|
||||
// return exp(-(x*x + y*y) / (1.5F * 1.5F * 2.0F));
|
||||
// }
|
||||
|
||||
__kernel void do_filter(
|
||||
__global float *filter_out, __global const float *precomputed,
|
||||
__global const int *pbp, const int width, const int height,
|
||||
const int filter_size) {
|
||||
int i = get_global_id(0);
|
||||
if(i < 0 || i >= width * height) {
|
||||
return;
|
||||
__kernel void do_filter(__global float *filter_out,
|
||||
__global const float *precomputed,
|
||||
__global const int *pbp, const int width,
|
||||
const int height, const int filter_size) {
|
||||
int i = get_global_id(0);
|
||||
if (i < 0 || i >= width * height) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = i % width;
|
||||
int y = i / width;
|
||||
|
||||
float sum = 0.0F;
|
||||
for (int q = 0; q < filter_size; ++q) {
|
||||
int q_prime = height - filter_size / 2 + y + q;
|
||||
for (int p = 0; p < filter_size; ++p) {
|
||||
int p_prime = width - filter_size / 2 + x + p;
|
||||
if (pbp[twoToOne(p_prime, q_prime, width, height)] != 0) {
|
||||
sum += precomputed[twoToOne(p, q, filter_size, filter_size)];
|
||||
// sum += gaussian(p - filter_size / 2.0F + 0.5F, q -
|
||||
// filter_size / 2.0F + 0.5F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int x = i % width;
|
||||
int y = i / width;
|
||||
|
||||
float sum = 0.0F;
|
||||
for(int q = 0; q < filter_size; ++q) {
|
||||
int q_prime = height - filter_size / 2 + y + q;
|
||||
for(int p = 0; p < filter_size; ++p) {
|
||||
int p_prime = width - filter_size / 2 + x + p;
|
||||
if(pbp[twoToOne(p_prime, q_prime, width, height)] != 0) {
|
||||
sum += precomputed[twoToOne(p, q, filter_size, filter_size)];
|
||||
//sum += gaussian(p - filter_size / 2.0F + 0.5F, q - filter_size / 2.0F + 0.5F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filter_out[i] = sum;
|
||||
filter_out[i] = sum;
|
||||
}
|
||||
|
||||
// vim: syntax=c
|
||||
|
|
1352
src/blue_noise.cpp
1352
src/blue_noise.cpp
File diff suppressed because it is too large
Load diff
|
@ -1,470 +1,469 @@
|
|||
#ifndef BLUE_NOISE_HPP
|
||||
#define BLUE_NOISE_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
#include <CL/opencl.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include <CL/opencl.h>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <condition_variable>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "utility.hpp"
|
||||
#include "image.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace dither {
|
||||
|
||||
image::Bl blue_noise(int width, int height, int threads = 1, bool use_opencl = true);
|
||||
image::Bl blue_noise(int width, int height, int threads = 1,
|
||||
bool use_opencl = true);
|
||||
|
||||
namespace internal {
|
||||
std::vector<unsigned int> blue_noise_impl(int width, int height, int threads = 1);
|
||||
std::vector<unsigned int> blue_noise_cl_impl(
|
||||
const int width, const int height, const int filter_size,
|
||||
cl_context context, cl_device_id device, cl_program program);
|
||||
std::vector<unsigned int> blue_noise_impl(int width, int height,
|
||||
int threads = 1);
|
||||
std::vector<unsigned int> blue_noise_cl_impl(const int width, const int height,
|
||||
const int filter_size,
|
||||
cl_context context,
|
||||
cl_device_id device,
|
||||
cl_program program);
|
||||
|
||||
inline std::vector<bool> random_noise(int size, int subsize) {
|
||||
std::vector<bool> pbp(size);
|
||||
std::default_random_engine re(std::random_device{}());
|
||||
std::uniform_int_distribution<int> dist(0, size - 1);
|
||||
inline std::vector<bool> random_noise(int size, int subsize) {
|
||||
std::vector<bool> pbp(size);
|
||||
std::default_random_engine re(std::random_device{}());
|
||||
std::uniform_int_distribution<int> dist(0, size - 1);
|
||||
|
||||
// initialize pbp
|
||||
for(int i = 0; i < size; ++i) {
|
||||
if(i < subsize) {
|
||||
pbp[i] = true;
|
||||
} else {
|
||||
pbp[i] = false;
|
||||
}
|
||||
}
|
||||
// randomize pbp
|
||||
for(int i = 0; i < size-1; ++i) {
|
||||
decltype(dist)::param_type range{i+1, size-1};
|
||||
int 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 pbp;
|
||||
// initialize pbp
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (i < subsize) {
|
||||
pbp[i] = true;
|
||||
} else {
|
||||
pbp[i] = false;
|
||||
}
|
||||
}
|
||||
// randomize pbp
|
||||
for (int i = 0; i < size - 1; ++i) {
|
||||
decltype(dist)::param_type range{i + 1, size - 1};
|
||||
int 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;
|
||||
}
|
||||
|
||||
constexpr float mu = 1.5F;
|
||||
constexpr float mu_squared = mu * mu;
|
||||
constexpr float double_mu_squared = 2.0F * mu * mu;
|
||||
return pbp;
|
||||
}
|
||||
|
||||
inline float gaussian(float x, float y) {
|
||||
return std::exp(-(x*x + y*y)/(double_mu_squared));
|
||||
constexpr float mu = 1.5F;
|
||||
constexpr float mu_squared = mu * mu;
|
||||
constexpr float double_mu_squared = 2.0F * mu * mu;
|
||||
|
||||
inline float gaussian(float x, float y) {
|
||||
return std::exp(-(x * x + y * y) / (double_mu_squared));
|
||||
}
|
||||
|
||||
inline std::vector<float> precompute_gaussian(int size) {
|
||||
std::vector<float> precomputed;
|
||||
if (size % 2 == 0) {
|
||||
++size;
|
||||
}
|
||||
precomputed.reserve(size * size);
|
||||
|
||||
for (int i = 0; i < size * size; ++i) {
|
||||
auto xy = utility::oneToTwo(i, size);
|
||||
precomputed.push_back(
|
||||
gaussian(xy.first - (size / 2), xy.second - (size / 2)));
|
||||
}
|
||||
|
||||
return precomputed;
|
||||
}
|
||||
|
||||
inline float filter(const std::vector<bool> &pbp, int x, int y, int width,
|
||||
int height, int filter_size) {
|
||||
float sum = 0.0f;
|
||||
|
||||
if (filter_size % 2 == 0) {
|
||||
++filter_size;
|
||||
}
|
||||
|
||||
// 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' = (N + y - (q - M/2)) % N = (N + M/2 + y - q) % N
|
||||
for (int q = 0; q < filter_size; ++q) {
|
||||
int q_prime = (height - filter_size / 2 + y + q) % height;
|
||||
for (int p = 0; p < filter_size; ++p) {
|
||||
int p_prime = (width - filter_size / 2 + x + p) % width;
|
||||
if (pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
|
||||
sum += gaussian(p - filter_size / 2, q - filter_size / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::vector<float> precompute_gaussian(int size) {
|
||||
std::vector<float> precomputed;
|
||||
if (size % 2 == 0) {
|
||||
++size;
|
||||
}
|
||||
precomputed.reserve(size * size);
|
||||
return sum;
|
||||
}
|
||||
|
||||
for(int i = 0; i < size * size; ++i) {
|
||||
auto xy = utility::oneToTwo(i, size);
|
||||
precomputed.push_back(gaussian(
|
||||
xy.first - (size / 2),
|
||||
xy.second - (size / 2)));
|
||||
}
|
||||
inline float filter_with_precomputed(const std::vector<bool> &pbp, int x, int y,
|
||||
int width, int height, int filter_size,
|
||||
const std::vector<float> &precomputed) {
|
||||
float sum = 0.0f;
|
||||
|
||||
return precomputed;
|
||||
if (filter_size % 2 == 0) {
|
||||
++filter_size;
|
||||
}
|
||||
|
||||
for (int q = 0; q < filter_size; ++q) {
|
||||
int q_prime = (height - filter_size / 2 + y + q) % height;
|
||||
for (int p = 0; p < filter_size; ++p) {
|
||||
int p_prime = (width - filter_size / 2 + x + p) % width;
|
||||
if (pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
|
||||
sum += precomputed[utility::twoToOne(p, q, filter_size, filter_size)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline float filter(
|
||||
const std::vector<bool>& pbp,
|
||||
int x, int y,
|
||||
int width, int height, int filter_size) {
|
||||
float sum = 0.0f;
|
||||
return sum;
|
||||
}
|
||||
|
||||
if (filter_size % 2 == 0) {
|
||||
++filter_size;
|
||||
inline void compute_filter(const std::vector<bool> &pbp, int width, int height,
|
||||
int count, int filter_size,
|
||||
std::vector<float> &filter_out,
|
||||
const std::vector<float> *precomputed = nullptr,
|
||||
int threads = 1) {
|
||||
if (threads == 1) {
|
||||
if (precomputed) {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
filter_out[utility::twoToOne(x, y, width, height)] =
|
||||
internal::filter_with_precomputed(pbp, x, y, width, height,
|
||||
filter_size, *precomputed);
|
||||
}
|
||||
|
||||
// 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' = (N + y - (q - M/2)) % N = (N + M/2 + y - q) % N
|
||||
for(int q = 0; q < filter_size; ++q) {
|
||||
int q_prime = (height - filter_size / 2 + y + q) % height;
|
||||
for(int p = 0; p < filter_size; ++p) {
|
||||
int p_prime = (width - filter_size / 2 + x + p) % width;
|
||||
if(pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
|
||||
sum += gaussian(p - filter_size/2,
|
||||
q - filter_size/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
filter_out[utility::twoToOne(x, y, width, height)] =
|
||||
internal::filter(pbp, x, y, width, height, filter_size);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
||||
inline float filter_with_precomputed(
|
||||
const std::vector<bool>& pbp,
|
||||
int x, int y,
|
||||
int width, int height, int filter_size,
|
||||
const std::vector<float> &precomputed) {
|
||||
float sum = 0.0f;
|
||||
|
||||
if (filter_size % 2 == 0) {
|
||||
++filter_size;
|
||||
}
|
||||
|
||||
for(int q = 0; q < filter_size; ++q) {
|
||||
int q_prime = (height - filter_size / 2 + y + q) % height;
|
||||
for(int p = 0; p < filter_size; ++p) {
|
||||
int p_prime = (width - filter_size / 2 + x + p) % width;
|
||||
if(pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
|
||||
sum += precomputed[utility::twoToOne(p, q, filter_size, filter_size)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
} else {
|
||||
if (threads == 0) {
|
||||
threads = 10;
|
||||
}
|
||||
|
||||
inline void compute_filter(
|
||||
const std::vector<bool> &pbp, int width, int height,
|
||||
int count, int filter_size, std::vector<float> &filter_out,
|
||||
const std::vector<float> *precomputed = nullptr,
|
||||
int threads = 1) {
|
||||
if(threads == 1) {
|
||||
if(precomputed) {
|
||||
for(int y = 0; y < height; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
filter_out[utility::twoToOne(x, y, width, height)] =
|
||||
internal::filter_with_precomputed(
|
||||
pbp, x, y, width, height, filter_size, *precomputed);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int y = 0; y < height; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
filter_out[utility::twoToOne(x, y, width, height)] =
|
||||
internal::filter(pbp, x, y, width, height, filter_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(threads == 0) {
|
||||
threads = 10;
|
||||
}
|
||||
int active_count = 0;
|
||||
std::mutex cv_mutex;
|
||||
std::condition_variable cv;
|
||||
if(precomputed) {
|
||||
for(int i = 0; i < count; ++i) {
|
||||
{
|
||||
std::unique_lock lock(cv_mutex);
|
||||
active_count += 1;
|
||||
}
|
||||
std::thread t([] (int *ac, std::mutex *cvm,
|
||||
std::condition_variable *cv, int i,
|
||||
const std::vector<bool> *pbp, int width,
|
||||
int height, int filter_size,
|
||||
std::vector<float> *fout,
|
||||
const std::vector<float> *precomputed) {
|
||||
int x, y;
|
||||
std::tie(x, y) = utility::oneToTwo(i, width);
|
||||
(*fout)[i] = internal::filter_with_precomputed(
|
||||
*pbp, x, y, width, height, filter_size, *precomputed);
|
||||
std::unique_lock lock(*cvm);
|
||||
*ac -= 1;
|
||||
cv->notify_all();
|
||||
},
|
||||
&active_count, &cv_mutex, &cv, i, &pbp, width, height,
|
||||
filter_size, &filter_out, precomputed);
|
||||
t.detach();
|
||||
|
||||
std::unique_lock lock(cv_mutex);
|
||||
while(active_count >= threads) {
|
||||
cv.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < count; ++i) {
|
||||
{
|
||||
std::unique_lock lock(cv_mutex);
|
||||
active_count += 1;
|
||||
}
|
||||
std::thread t([] (int *ac, std::mutex *cvm,
|
||||
std::condition_variable *cv, int i,
|
||||
const std::vector<bool> *pbp, int width,
|
||||
int height, int filter_size,
|
||||
std::vector<float> *fout) {
|
||||
int x, y;
|
||||
std::tie(x, y) = utility::oneToTwo(i, width);
|
||||
(*fout)[i] = internal::filter(
|
||||
*pbp, x, y, width, height, filter_size);
|
||||
std::unique_lock lock(*cvm);
|
||||
*ac -= 1;
|
||||
cv->notify_all();
|
||||
},
|
||||
&active_count, &cv_mutex, &cv, i, &pbp, width, height,
|
||||
filter_size, &filter_out);
|
||||
t.detach();
|
||||
|
||||
std::unique_lock lock(cv_mutex);
|
||||
while(active_count >= threads) {
|
||||
cv.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::unique_lock lock(cv_mutex);
|
||||
while(active_count > 0) {
|
||||
cv.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
int active_count = 0;
|
||||
std::mutex cv_mutex;
|
||||
std::condition_variable cv;
|
||||
if (precomputed) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
{
|
||||
std::unique_lock lock(cv_mutex);
|
||||
active_count += 1;
|
||||
}
|
||||
std::thread t(
|
||||
[](int *ac, std::mutex *cvm, std::condition_variable *cv, int i,
|
||||
const std::vector<bool> *pbp, int width, int height,
|
||||
int filter_size, std::vector<float> *fout,
|
||||
const std::vector<float> *precomputed) {
|
||||
int x, y;
|
||||
std::tie(x, y) = utility::oneToTwo(i, width);
|
||||
(*fout)[i] = internal::filter_with_precomputed(
|
||||
*pbp, x, y, width, height, filter_size, *precomputed);
|
||||
std::unique_lock lock(*cvm);
|
||||
*ac -= 1;
|
||||
cv->notify_all();
|
||||
},
|
||||
&active_count, &cv_mutex, &cv, i, &pbp, width, height, filter_size,
|
||||
&filter_out, precomputed);
|
||||
t.detach();
|
||||
|
||||
std::unique_lock lock(cv_mutex);
|
||||
while (active_count >= threads) {
|
||||
cv.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
{
|
||||
std::unique_lock lock(cv_mutex);
|
||||
active_count += 1;
|
||||
}
|
||||
std::thread t(
|
||||
[](int *ac, std::mutex *cvm, std::condition_variable *cv, int i,
|
||||
const std::vector<bool> *pbp, int width, int height,
|
||||
int filter_size, std::vector<float> *fout) {
|
||||
int x, y;
|
||||
std::tie(x, y) = utility::oneToTwo(i, width);
|
||||
(*fout)[i] =
|
||||
internal::filter(*pbp, x, y, width, height, filter_size);
|
||||
std::unique_lock lock(*cvm);
|
||||
*ac -= 1;
|
||||
cv->notify_all();
|
||||
},
|
||||
&active_count, &cv_mutex, &cv, i, &pbp, width, height, filter_size,
|
||||
&filter_out);
|
||||
t.detach();
|
||||
|
||||
std::unique_lock lock(cv_mutex);
|
||||
while (active_count >= threads) {
|
||||
cv.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::pair<int, int> filter_minmax(const std::vector<float> &filter,
|
||||
std::vector<bool> pbp) {
|
||||
// ensure minority pixel is "true"
|
||||
unsigned int count = 0;
|
||||
for (bool value : pbp) {
|
||||
if(value) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (count * 2 >= pbp.size()) {
|
||||
//std::cout << "MINMAX flip\n"; // DEBUG
|
||||
for (unsigned int i = 0; i < pbp.size(); ++i) {
|
||||
pbp[i] = !pbp[i];
|
||||
}
|
||||
}
|
||||
|
||||
float min = std::numeric_limits<float>::infinity();
|
||||
float max = -std::numeric_limits<float>::infinity();
|
||||
int min_index = -1;
|
||||
int max_index = -1;
|
||||
|
||||
for(std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
|
||||
if(!pbp[i] && filter[i] < min) {
|
||||
min_index = i;
|
||||
min = filter[i];
|
||||
}
|
||||
if(pbp[i] && filter[i] > max) {
|
||||
max_index = i;
|
||||
max = filter[i];
|
||||
}
|
||||
}
|
||||
|
||||
return {min_index, max_index};
|
||||
std::unique_lock lock(cv_mutex);
|
||||
while (active_count > 0) {
|
||||
cv.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::pair<int, int> filter_abs_minmax(
|
||||
const std::vector<float> &filter) {
|
||||
float min = std::numeric_limits<float>::infinity();
|
||||
float max = -std::numeric_limits<float>::infinity();
|
||||
int min_index = -1;
|
||||
int max_index = -1;
|
||||
|
||||
std::default_random_engine re(std::random_device{}());
|
||||
std::size_t startIdx = std::uniform_int_distribution<std::size_t>(0, filter.size() - 1)(re);
|
||||
|
||||
for(std::vector<float>::size_type i = startIdx; i < filter.size(); ++i) {
|
||||
if(filter[i] < min) {
|
||||
min_index = i;
|
||||
min = filter[i];
|
||||
}
|
||||
if(filter[i] > max) {
|
||||
max_index = i;
|
||||
max = filter[i];
|
||||
}
|
||||
}
|
||||
for(std::vector<float>::size_type i = 0; i < startIdx; ++i) {
|
||||
if(filter[i] < min) {
|
||||
min_index = i;
|
||||
min = filter[i];
|
||||
}
|
||||
if(filter[i] > max) {
|
||||
max_index = i;
|
||||
max = filter[i];
|
||||
}
|
||||
}
|
||||
|
||||
return {min_index, max_index};
|
||||
inline std::pair<int, int> filter_minmax(const std::vector<float> &filter,
|
||||
std::vector<bool> pbp) {
|
||||
// ensure minority pixel is "true"
|
||||
unsigned int count = 0;
|
||||
for (bool value : pbp) {
|
||||
if (value) {
|
||||
++count;
|
||||
}
|
||||
|
||||
inline int get_one_or_zero(
|
||||
const std::vector<bool>& pbp, bool get_one,
|
||||
int idx, int width, int height) {
|
||||
std::queue<int> checking_indices;
|
||||
|
||||
auto xy = utility::oneToTwo(idx, width);
|
||||
int count = 0;
|
||||
int loops = 0;
|
||||
enum { D_DOWN = 0, D_LEFT = 1, D_UP = 2, D_RIGHT = 3 } dir = D_RIGHT;
|
||||
int next;
|
||||
|
||||
while(true) {
|
||||
if(count == 0) {
|
||||
switch(dir) {
|
||||
case D_RIGHT:
|
||||
xy.first = (xy.first + 1) % width;
|
||||
++loops;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_DOWN;
|
||||
break;
|
||||
case D_DOWN:
|
||||
xy.first = (xy.first + width - 1) % width;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_LEFT;
|
||||
break;
|
||||
case D_LEFT:
|
||||
xy.second = (xy.second + height - 1) % height;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_UP;
|
||||
break;
|
||||
case D_UP:
|
||||
xy.first = (xy.first + 1) % width;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_RIGHT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(dir) {
|
||||
case D_DOWN:
|
||||
xy.second = (xy.second + 1) % height;
|
||||
--count;
|
||||
break;
|
||||
case D_LEFT:
|
||||
xy.first = (xy.first + width - 1) % width;
|
||||
--count;
|
||||
break;
|
||||
case D_UP:
|
||||
xy.second = (xy.second + height - 1) % height;
|
||||
--count;
|
||||
break;
|
||||
case D_RIGHT:
|
||||
xy.first = (xy.first + 1) % width;
|
||||
--count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
next = utility::twoToOne(xy.first, xy.second, width, height);
|
||||
if((get_one && pbp[next]) || (!get_one && !pbp[next])) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
if (count * 2 >= pbp.size()) {
|
||||
// std::cout << "MINMAX flip\n"; // DEBUG
|
||||
for (unsigned int i = 0; i < pbp.size(); ++i) {
|
||||
pbp[i] = !pbp[i];
|
||||
}
|
||||
}
|
||||
|
||||
inline void write_filter(const std::vector<float> &filter, int width, const char *filename) {
|
||||
int min, max;
|
||||
std::tie(min, max) = filter_abs_minmax(filter);
|
||||
float min = std::numeric_limits<float>::infinity();
|
||||
float max = -std::numeric_limits<float>::infinity();
|
||||
int min_index = -1;
|
||||
int max_index = -1;
|
||||
|
||||
printf("Writing to %s, min is %.3f, max is %.3f\n", filename, filter[min], filter[max]);
|
||||
FILE *filter_image = fopen(filename, "w");
|
||||
fprintf(filter_image, "P2\n%d %d\n255\n", width, (int)filter.size() / width);
|
||||
for(std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
|
||||
fprintf(filter_image, "%d ",
|
||||
(int)(((filter[i] - filter[min])
|
||||
/ (filter[max] - filter[min]))
|
||||
* 255.0f));
|
||||
if((i + 1) % width == 0) {
|
||||
fputc('\n', filter_image);
|
||||
}
|
||||
}
|
||||
fclose(filter_image);
|
||||
for (std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
|
||||
if (!pbp[i] && filter[i] < min) {
|
||||
min_index = i;
|
||||
min = filter[i];
|
||||
}
|
||||
|
||||
inline image::Bl toBl(const std::vector<bool>& pbp, int width) {
|
||||
image::Bl bwImage(width, pbp.size() / width);
|
||||
assert((unsigned long)bwImage.getSize() >= pbp.size()
|
||||
&& "New image::Bl size too small (pbp's size is not a multiple of width)");
|
||||
|
||||
for(unsigned int i = 0; i < pbp.size(); ++i) {
|
||||
bwImage.getData()[i] = pbp[i] ? 255 : 0;
|
||||
}
|
||||
|
||||
return bwImage;
|
||||
if (pbp[i] && filter[i] > max) {
|
||||
max_index = i;
|
||||
max = filter[i];
|
||||
}
|
||||
}
|
||||
|
||||
inline image::Bl rangeToBl(const std::vector<unsigned int> &values, int width) {
|
||||
int min = std::numeric_limits<int>::max();
|
||||
int max = std::numeric_limits<int>::min();
|
||||
return {min_index, max_index};
|
||||
}
|
||||
|
||||
for (int value : values) {
|
||||
if (value < min) {
|
||||
min = value;
|
||||
}
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
inline std::pair<int, int> filter_abs_minmax(const std::vector<float> &filter) {
|
||||
float min = std::numeric_limits<float>::infinity();
|
||||
float max = -std::numeric_limits<float>::infinity();
|
||||
int min_index = -1;
|
||||
int max_index = -1;
|
||||
|
||||
std::default_random_engine re(std::random_device{}());
|
||||
std::size_t startIdx =
|
||||
std::uniform_int_distribution<std::size_t>(0, filter.size() - 1)(re);
|
||||
|
||||
for (std::vector<float>::size_type i = startIdx; i < filter.size(); ++i) {
|
||||
if (filter[i] < min) {
|
||||
min_index = i;
|
||||
min = filter[i];
|
||||
}
|
||||
if (filter[i] > max) {
|
||||
max_index = i;
|
||||
max = filter[i];
|
||||
}
|
||||
}
|
||||
for (std::vector<float>::size_type i = 0; i < startIdx; ++i) {
|
||||
if (filter[i] < min) {
|
||||
min_index = i;
|
||||
min = filter[i];
|
||||
}
|
||||
if (filter[i] > max) {
|
||||
max_index = i;
|
||||
max = filter[i];
|
||||
}
|
||||
}
|
||||
|
||||
return {min_index, max_index};
|
||||
}
|
||||
|
||||
inline int get_one_or_zero(const std::vector<bool> &pbp, bool get_one, int idx,
|
||||
int width, int height) {
|
||||
std::queue<int> checking_indices;
|
||||
|
||||
auto xy = utility::oneToTwo(idx, width);
|
||||
int count = 0;
|
||||
int loops = 0;
|
||||
enum { D_DOWN = 0, D_LEFT = 1, D_UP = 2, D_RIGHT = 3 } dir = D_RIGHT;
|
||||
int next;
|
||||
|
||||
while (true) {
|
||||
if (count == 0) {
|
||||
switch (dir) {
|
||||
case D_RIGHT:
|
||||
xy.first = (xy.first + 1) % width;
|
||||
++loops;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_DOWN;
|
||||
break;
|
||||
case D_DOWN:
|
||||
xy.first = (xy.first + width - 1) % width;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_LEFT;
|
||||
break;
|
||||
case D_LEFT:
|
||||
xy.second = (xy.second + height - 1) % height;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_UP;
|
||||
break;
|
||||
case D_UP:
|
||||
xy.first = (xy.first + 1) % width;
|
||||
count = loops * 2 - 1;
|
||||
dir = D_RIGHT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case D_DOWN:
|
||||
xy.second = (xy.second + 1) % height;
|
||||
--count;
|
||||
break;
|
||||
case D_LEFT:
|
||||
xy.first = (xy.first + width - 1) % width;
|
||||
--count;
|
||||
break;
|
||||
case D_UP:
|
||||
xy.second = (xy.second + height - 1) % height;
|
||||
--count;
|
||||
break;
|
||||
case D_RIGHT:
|
||||
xy.first = (xy.first + 1) % width;
|
||||
--count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
next = utility::twoToOne(xy.first, xy.second, width, height);
|
||||
if ((get_one && pbp[next]) || (!get_one && !pbp[next])) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
inline void write_filter(const std::vector<float> &filter, int width,
|
||||
const char *filename) {
|
||||
int min, max;
|
||||
std::tie(min, max) = filter_abs_minmax(filter);
|
||||
|
||||
printf("Writing to %s, min is %.3f, max is %.3f\n", filename, filter[min],
|
||||
filter[max]);
|
||||
FILE *filter_image = fopen(filename, "w");
|
||||
fprintf(filter_image, "P2\n%d %d\n255\n", width, (int)filter.size() / width);
|
||||
for (std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
|
||||
fprintf(filter_image, "%d ",
|
||||
(int)(((filter[i] - filter[min]) / (filter[max] - filter[min])) *
|
||||
255.0f));
|
||||
if ((i + 1) % width == 0) {
|
||||
fputc('\n', filter_image);
|
||||
}
|
||||
}
|
||||
fclose(filter_image);
|
||||
}
|
||||
|
||||
inline image::Bl toBl(const std::vector<bool> &pbp, int width) {
|
||||
image::Bl bwImage(width, pbp.size() / width);
|
||||
assert((unsigned long)bwImage.getSize() >= pbp.size() &&
|
||||
"New image::Bl size too small (pbp's size is not a multiple of "
|
||||
"width)");
|
||||
|
||||
for (unsigned int i = 0; i < pbp.size(); ++i) {
|
||||
bwImage.getData()[i] = pbp[i] ? 255 : 0;
|
||||
}
|
||||
|
||||
return bwImage;
|
||||
}
|
||||
|
||||
inline image::Bl rangeToBl(const std::vector<unsigned int> &values, int width) {
|
||||
int min = std::numeric_limits<int>::max();
|
||||
int max = std::numeric_limits<int>::min();
|
||||
|
||||
for (int value : values) {
|
||||
if (value < min) {
|
||||
min = value;
|
||||
}
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::cout << "rangeToBl: Got min == " << min << " and max == " << max << std::endl;
|
||||
std::cout << "rangeToBl: Got min == " << min << " and max == " << max
|
||||
<< std::endl;
|
||||
#endif
|
||||
|
||||
max -= min;
|
||||
max -= min;
|
||||
|
||||
image::Bl grImage(width, values.size() / width);
|
||||
assert((unsigned long)grImage.getSize() >= values.size()
|
||||
&& "New image::Bl size too small (values' size is not a multiple of width)");
|
||||
image::Bl grImage(width, values.size() / width);
|
||||
assert((unsigned long)grImage.getSize() >= values.size() &&
|
||||
"New image::Bl size too small (values' size is not a multiple of "
|
||||
"width)");
|
||||
|
||||
for(unsigned int i = 0; i < values.size(); ++i) {
|
||||
grImage.getData()[i] = std::round(((float)((int)(values[i]) - min) / (float)max) * 255.0F);
|
||||
}
|
||||
for (unsigned int i = 0; i < values.size(); ++i) {
|
||||
grImage.getData()[i] =
|
||||
std::round(((float)((int)(values[i]) - min) / (float)max) * 255.0F);
|
||||
}
|
||||
|
||||
return grImage;
|
||||
return grImage;
|
||||
}
|
||||
|
||||
inline std::pair<int, int> filter_minmax_in_range(
|
||||
int start, int width, int height, int range,
|
||||
const std::vector<float> &vec) {
|
||||
float max = -std::numeric_limits<float>::infinity();
|
||||
float min = std::numeric_limits<float>::infinity();
|
||||
|
||||
int maxIdx = -1;
|
||||
int minIdx = -1;
|
||||
|
||||
auto startXY = utility::oneToTwo(start, width);
|
||||
for (int y = startXY.second - range / 2; y <= startXY.second + range / 2;
|
||||
++y) {
|
||||
for (int x = startXY.first - range / 2; x <= startXY.first + range / 2;
|
||||
++x) {
|
||||
int idx = utility::twoToOne(x, y, width, height);
|
||||
if (idx == start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vec[idx] < min) {
|
||||
min = vec[idx];
|
||||
minIdx = idx;
|
||||
}
|
||||
|
||||
if (vec[idx] > max) {
|
||||
max = vec[idx];
|
||||
maxIdx = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::pair<int, int> filter_minmax_in_range(int start, int width,
|
||||
int height,
|
||||
int range,
|
||||
const std::vector<float> &vec) {
|
||||
float max = -std::numeric_limits<float>::infinity();
|
||||
float min = std::numeric_limits<float>::infinity();
|
||||
if (minIdx < 0) {
|
||||
throw std::runtime_error("Invalid minIdx value");
|
||||
} else if (maxIdx < 0) {
|
||||
throw std::runtime_error("Invalid maxIdx value");
|
||||
}
|
||||
return {minIdx, maxIdx};
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
int maxIdx = -1;
|
||||
int minIdx = -1;
|
||||
|
||||
auto startXY = utility::oneToTwo(start, width);
|
||||
for(int y = startXY.second - range / 2; y <= startXY.second + range / 2; ++y) {
|
||||
for(int x = startXY.first - range / 2; x <= startXY.first + range / 2; ++x) {
|
||||
int idx = utility::twoToOne(x, y, width, height);
|
||||
if(idx == start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(vec[idx] < min) {
|
||||
min = vec[idx];
|
||||
minIdx = idx;
|
||||
}
|
||||
|
||||
if(vec[idx] > max) {
|
||||
max = vec[idx];
|
||||
maxIdx = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(minIdx < 0) {
|
||||
throw std::runtime_error("Invalid minIdx value");
|
||||
} else if(maxIdx < 0) {
|
||||
throw std::runtime_error("Invalid maxIdx value");
|
||||
}
|
||||
return {minIdx, maxIdx};
|
||||
}
|
||||
} // namespace dither::internal
|
||||
|
||||
} // namespace dither
|
||||
} // namespace dither
|
||||
|
||||
#endif
|
||||
|
|
348
src/image.cpp
348
src/image.cpp
|
@ -1,241 +1,221 @@
|
|||
#include "image.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
bool image::Base::isValid() const {
|
||||
return getWidth() > 0 && getHeight() > 0 && getSize() > 0;
|
||||
return getWidth() > 0 && getHeight() > 0 && getSize() > 0;
|
||||
}
|
||||
|
||||
image::Bl::Bl() :
|
||||
data(),
|
||||
width(0),
|
||||
height(0)
|
||||
{}
|
||||
image::Bl::Bl() : data(), width(0), height(0) {}
|
||||
|
||||
image::Bl::Bl(int width, int height) :
|
||||
data(width * height),
|
||||
width(width),
|
||||
height(height)
|
||||
{}
|
||||
image::Bl::Bl(int width, int height)
|
||||
: data(width * height), width(width), height(height) {}
|
||||
|
||||
image::Bl::Bl(const std::vector<uint8_t> &data, int width) :
|
||||
data(data),
|
||||
width(width),
|
||||
height(data.size() / width)
|
||||
{}
|
||||
image::Bl::Bl(const std::vector<uint8_t> &data, int width)
|
||||
: data(data), width(width), height(data.size() / width) {}
|
||||
|
||||
image::Bl::Bl(std::vector<uint8_t> &&data, int width) :
|
||||
data(std::move(data)),
|
||||
width(width),
|
||||
height(data.size() / width)
|
||||
{}
|
||||
image::Bl::Bl(std::vector<uint8_t> &&data, int width)
|
||||
: data(std::move(data)), width(width), height(data.size() / width) {}
|
||||
|
||||
image::Bl::Bl(const std::vector<float> &data, int width) :
|
||||
data{},
|
||||
width(width),
|
||||
height(data.size() / width)
|
||||
{
|
||||
for(float gspixel : data) {
|
||||
this->data.push_back(static_cast<uint8_t>(255.0F * gspixel));
|
||||
}
|
||||
image::Bl::Bl(const std::vector<float> &data, int width)
|
||||
: data{}, width(width), height(data.size() / width) {
|
||||
for (float gspixel : data) {
|
||||
this->data.push_back(static_cast<uint8_t>(255.0F * gspixel));
|
||||
}
|
||||
}
|
||||
|
||||
void image::Bl::randomize() {
|
||||
if(!isValid()) {
|
||||
return;
|
||||
}
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::default_random_engine re(std::random_device{}());
|
||||
std::uniform_int_distribution<unsigned int> dist;
|
||||
std::default_random_engine re(std::random_device{}());
|
||||
std::uniform_int_distribution<unsigned int> dist;
|
||||
|
||||
for(unsigned int i = 0; i < data.size(); ++i) {
|
||||
data[i] = i < data.size() / 2 ? 255 : 0;
|
||||
}
|
||||
for (unsigned int i = 0; i < data.size(); ++i) {
|
||||
data[i] = i < data.size() / 2 ? 255 : 0;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < data.size() - 1; ++i) {
|
||||
int ridx = dist(re, decltype(dist)::param_type{i+1, (unsigned int)data.size()-1});
|
||||
uint8_t temp = data[i];
|
||||
data[i] = data[ridx];
|
||||
data[ridx] = temp;
|
||||
}
|
||||
for (unsigned int i = 0; i < data.size() - 1; ++i) {
|
||||
int ridx = dist(
|
||||
re, decltype(dist)::param_type{i + 1, (unsigned int)data.size() - 1});
|
||||
uint8_t temp = data[i];
|
||||
data[i] = data[ridx];
|
||||
data[ridx] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int image::Bl::getSize() const {
|
||||
return data.size();
|
||||
unsigned int image::Bl::getSize() const { return data.size(); }
|
||||
|
||||
uint8_t *image::Bl::getData() {
|
||||
if (!isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &data[0];
|
||||
}
|
||||
|
||||
uint8_t* image::Bl::getData() {
|
||||
if(!isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &data[0];
|
||||
const uint8_t *image::Bl::getDataC() const {
|
||||
if (!isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &data[0];
|
||||
}
|
||||
|
||||
const uint8_t* image::Bl::getDataC() const {
|
||||
if(!isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &data[0];
|
||||
}
|
||||
unsigned int image::Bl::getWidth() const { return width; }
|
||||
|
||||
unsigned int image::Bl::getWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
||||
unsigned int image::Bl::getHeight() const {
|
||||
return height;
|
||||
}
|
||||
unsigned int image::Bl::getHeight() const { return height; }
|
||||
|
||||
bool image::Bl::canWriteFile(file_type type) {
|
||||
if(!isValid()) {
|
||||
std::cout << "Cannot write image because isValid() is false\n";
|
||||
return false;
|
||||
}
|
||||
switch(type) {
|
||||
if (!isValid()) {
|
||||
std::cout << "Cannot write image because isValid() is false\n";
|
||||
return false;
|
||||
}
|
||||
switch (type) {
|
||||
case file_type::PBM:
|
||||
case file_type::PGM:
|
||||
case file_type::PPM:
|
||||
case file_type::PNG:
|
||||
return true;
|
||||
return true;
|
||||
default:
|
||||
std::cout << "Cannot write image because received invalid file_type\n";
|
||||
return false;
|
||||
}
|
||||
std::cout << "Cannot write image because received invalid "
|
||||
"file_type\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool image::Bl::writeToFile(file_type type, bool canOverwrite, const char *filename) {
|
||||
if(!isValid() || !canWriteFile(type)) {
|
||||
std::cout << "ERROR: Image is not valid or cannot write file type\n";
|
||||
return false;
|
||||
bool image::Bl::writeToFile(file_type type, bool canOverwrite,
|
||||
const char *filename) {
|
||||
if (!isValid() || !canWriteFile(type)) {
|
||||
std::cout << "ERROR: Image is not valid or cannot write file type\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *file = fopen(filename, "r");
|
||||
if (file && !canOverwrite) {
|
||||
fclose(file);
|
||||
std::cout << "ERROR: Will not overwite existing file \"" << filename << "\""
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
if (type == file_type::PNG) {
|
||||
FILE *outfile = fopen(filename, "wb");
|
||||
if (outfile == nullptr) {
|
||||
std::cout << "ERROR: Failed to open file for writing (png)\n";
|
||||
return false;
|
||||
}
|
||||
const static auto pngErrorLFn = [](png_structp /* unused */,
|
||||
png_const_charp message) {
|
||||
std::cerr << "WARNING [libpng]: " << message << std::endl;
|
||||
};
|
||||
const static auto pngWarnLFn = [](png_structp /* unused */,
|
||||
png_const_charp message) {
|
||||
std::cerr << "ERROR [libpng]: " << message << std::endl;
|
||||
};
|
||||
|
||||
png_structp png_ptr = png_create_write_struct(
|
||||
PNG_LIBPNG_VER_STRING, nullptr, pngErrorLFn, pngWarnLFn);
|
||||
|
||||
if (png_ptr == nullptr) {
|
||||
fclose(outfile);
|
||||
std::cout << "ERROR: Failed to set up writing png file (png_ptr)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *file = fopen(filename, "r");
|
||||
if(file && !canOverwrite) {
|
||||
fclose(file);
|
||||
std::cout << "ERROR: Will not overwite existing file \"" << filename
|
||||
<< "\"" << std::endl;
|
||||
return false;
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == nullptr) {
|
||||
png_destroy_write_struct(&png_ptr, nullptr);
|
||||
fclose(outfile);
|
||||
std::cout << "ERROR: Failed to set up writing png file (png_infop)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(file) {
|
||||
fclose(file);
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
fclose(outfile);
|
||||
std::cout << "ERROR: Failed to write image file (png error)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(type == file_type::PNG) {
|
||||
FILE *outfile = fopen(filename, "wb");
|
||||
if (outfile == nullptr) {
|
||||
std::cout << "ERROR: Failed to open file for writing (png)\n";
|
||||
return false;
|
||||
}
|
||||
const static auto pngErrorLFn = [] (png_structp /* unused */,
|
||||
png_const_charp message) {
|
||||
std::cerr << "WARNING [libpng]: " << message << std::endl;
|
||||
};
|
||||
const static auto pngWarnLFn = [] (png_structp /* unused */,
|
||||
png_const_charp message) {
|
||||
std::cerr << "ERROR [libpng]: " << message << std::endl;
|
||||
};
|
||||
png_init_io(png_ptr, outfile);
|
||||
|
||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
nullptr,
|
||||
pngErrorLFn,
|
||||
pngWarnLFn);
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
if (png_ptr == nullptr) {
|
||||
fclose(outfile);
|
||||
std::cout << "ERROR: Failed to set up writing png file (png_ptr)\n";
|
||||
return false;
|
||||
}
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == nullptr) {
|
||||
png_destroy_write_struct(&png_ptr, nullptr);
|
||||
fclose(outfile);
|
||||
std::cout << "ERROR: Failed to set up writing png file (png_infop)\n";
|
||||
return false;
|
||||
}
|
||||
// png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
fclose(outfile);
|
||||
std::cout << "ERROR: Failed to write image file (png error)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, outfile);
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
//png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
|
||||
|
||||
for (unsigned int j = 0; j < this->data.size() / this->width; ++j) {
|
||||
unsigned char *dataPtr = &this->data.at(j * this->width);
|
||||
png_write_rows(png_ptr, &dataPtr, 1);
|
||||
}
|
||||
|
||||
png_write_end(png_ptr, nullptr);
|
||||
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
fclose(outfile);
|
||||
return true;
|
||||
for (unsigned int j = 0; j < this->data.size() / this->width; ++j) {
|
||||
unsigned char *dataPtr = &this->data.at(j * this->width);
|
||||
png_write_rows(png_ptr, &dataPtr, 1);
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
png_write_end(png_ptr, nullptr);
|
||||
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
fclose(outfile);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case file_type::PBM:
|
||||
file = fopen(filename, "w");
|
||||
fprintf(file, "P1\n%d %d", width, height);
|
||||
break;
|
||||
file = fopen(filename, "w");
|
||||
fprintf(file, "P1\n%d %d", width, height);
|
||||
break;
|
||||
case file_type::PGM:
|
||||
file = fopen(filename, "wb");
|
||||
fprintf(file, "P5\n%d %d\n255\n", width, height);
|
||||
break;
|
||||
file = fopen(filename, "wb");
|
||||
fprintf(file, "P5\n%d %d\n255\n", width, height);
|
||||
break;
|
||||
case file_type::PPM:
|
||||
file = fopen(filename, "wb");
|
||||
fprintf(file, "P6\n%d %d\n255\n", width, height);
|
||||
break;
|
||||
file = fopen(filename, "wb");
|
||||
fprintf(file, "P6\n%d %d\n255\n", width, height);
|
||||
break;
|
||||
default:
|
||||
fclose(file);
|
||||
std::cout << "ERROR: Cannot write image file, invalid type\n";
|
||||
return false;
|
||||
}
|
||||
for (unsigned int i = 0; i < data.size(); ++i) {
|
||||
if (type == file_type::PBM && i % width == 0) {
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
switch (type) {
|
||||
case file_type::PBM:
|
||||
fprintf(file, "%d ", data[i] == 0 ? 0 : 1);
|
||||
break;
|
||||
case file_type::PGM:
|
||||
// fprintf(file, "%c ", data[i]);
|
||||
fputc(data[i], file);
|
||||
break;
|
||||
case file_type::PPM:
|
||||
// fprintf(file, "%c %c %c ", data[i], data[i], data[i]);
|
||||
fputc(data[i], file);
|
||||
fputc(data[i], file);
|
||||
fputc(data[i], file);
|
||||
break;
|
||||
default:
|
||||
fclose(file);
|
||||
std::cout << "ERROR: Cannot write image file, invalid type\n";
|
||||
return false;
|
||||
}
|
||||
for(unsigned int i = 0; i < data.size(); ++i) {
|
||||
if(type == file_type::PBM && i % width == 0) {
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
switch(type) {
|
||||
case file_type::PBM:
|
||||
fprintf(file, "%d ", data[i] == 0 ? 0 : 1);
|
||||
break;
|
||||
case file_type::PGM:
|
||||
//fprintf(file, "%c ", data[i]);
|
||||
fputc(data[i], file);
|
||||
break;
|
||||
case file_type::PPM:
|
||||
//fprintf(file, "%c %c %c ", data[i], data[i], data[i]);
|
||||
fputc(data[i], file);
|
||||
fputc(data[i], file);
|
||||
fputc(data[i], file);
|
||||
break;
|
||||
default:
|
||||
fclose(file);
|
||||
std::cout << "ERROR: Cannot write image file, invalid type\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return true;
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image::Bl::writeToFile(file_type type, bool canOverwrite, const std::string &filename) {
|
||||
return writeToFile(type, canOverwrite, filename.c_str());
|
||||
bool image::Bl::writeToFile(file_type type, bool canOverwrite,
|
||||
const std::string &filename) {
|
||||
return writeToFile(type, canOverwrite, filename.c_str());
|
||||
}
|
||||
|
|
137
src/image.hpp
137
src/image.hpp
|
@ -2,91 +2,96 @@
|
|||
#define DITHERING_IMAGE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace image {
|
||||
enum class color_type {
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
Alpha,
|
||||
};
|
||||
enum class color_type {
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
Alpha,
|
||||
};
|
||||
|
||||
enum class file_type {
|
||||
PBM,
|
||||
PGM,
|
||||
PPM,
|
||||
PNG,
|
||||
};
|
||||
enum class file_type {
|
||||
PBM,
|
||||
PGM,
|
||||
PPM,
|
||||
PNG,
|
||||
};
|
||||
|
||||
class Base {
|
||||
public:
|
||||
Base() = default;
|
||||
virtual ~Base() {}
|
||||
class Base {
|
||||
public:
|
||||
Base() = default;
|
||||
virtual ~Base() {}
|
||||
|
||||
Base(const Base &other) = default;
|
||||
Base(Base &&other) = default;
|
||||
Base(const Base &other) = default;
|
||||
Base(Base &&other) = default;
|
||||
|
||||
Base& operator=(const Base &other) = default;
|
||||
Base& operator=(Base &&other) = default;
|
||||
Base &operator=(const Base &other) = default;
|
||||
Base &operator=(Base &&other) = default;
|
||||
|
||||
virtual void randomize() = 0;
|
||||
virtual void randomize() = 0;
|
||||
|
||||
virtual unsigned int getSize() const = 0;
|
||||
virtual uint8_t* getData() = 0;
|
||||
virtual const uint8_t* getDataC() const = 0;
|
||||
virtual unsigned int getSize() const = 0;
|
||||
virtual uint8_t *getData() = 0;
|
||||
virtual const uint8_t *getDataC() const = 0;
|
||||
|
||||
virtual unsigned int getWidth() const = 0;
|
||||
virtual unsigned int getHeight() const = 0;
|
||||
virtual unsigned int getWidth() const = 0;
|
||||
virtual unsigned int getHeight() const = 0;
|
||||
|
||||
virtual int getTypesCount() = 0;
|
||||
virtual std::vector<color_type> getTypes() = 0;
|
||||
virtual int getTypeStride(color_type type) = 0;
|
||||
virtual int getTypesCount() = 0;
|
||||
virtual std::vector<color_type> getTypes() = 0;
|
||||
virtual int getTypeStride(color_type type) = 0;
|
||||
|
||||
virtual bool canWriteFile(file_type type) = 0;
|
||||
virtual bool writeToFile(file_type type, bool canOverwrite, const char *filename) = 0;
|
||||
virtual bool writeToFile(file_type type, bool canOverwrite, const std::string &filename) = 0;
|
||||
bool isValid() const;
|
||||
};
|
||||
virtual bool canWriteFile(file_type type) = 0;
|
||||
virtual bool writeToFile(file_type type, bool canOverwrite,
|
||||
const char *filename) = 0;
|
||||
virtual bool writeToFile(file_type type, bool canOverwrite,
|
||||
const std::string &filename) = 0;
|
||||
bool isValid() const;
|
||||
};
|
||||
|
||||
class Bl : public Base {
|
||||
public:
|
||||
Bl();
|
||||
Bl(int width, int height);
|
||||
Bl(const std::vector<uint8_t> &data, int width);
|
||||
Bl(std::vector<uint8_t> &&data, int width);
|
||||
Bl(const std::vector<float> &data, int width);
|
||||
virtual ~Bl() {}
|
||||
class Bl : public Base {
|
||||
public:
|
||||
Bl();
|
||||
Bl(int width, int height);
|
||||
Bl(const std::vector<uint8_t> &data, int width);
|
||||
Bl(std::vector<uint8_t> &&data, int width);
|
||||
Bl(const std::vector<float> &data, int width);
|
||||
virtual ~Bl() {}
|
||||
|
||||
Bl(const Bl &other) = default;
|
||||
Bl(Bl &&other) = default;
|
||||
Bl(const Bl &other) = default;
|
||||
Bl(Bl &&other) = default;
|
||||
|
||||
Bl& operator=(const Bl &other) = default;
|
||||
Bl& operator=(Bl &&other) = default;
|
||||
Bl &operator=(const Bl &other) = default;
|
||||
Bl &operator=(Bl &&other) = default;
|
||||
|
||||
void randomize() override;
|
||||
void randomize() override;
|
||||
|
||||
unsigned int getSize() const override;
|
||||
uint8_t* getData() override;
|
||||
const uint8_t* getDataC() const override;
|
||||
unsigned int getSize() const override;
|
||||
uint8_t *getData() override;
|
||||
const uint8_t *getDataC() const override;
|
||||
|
||||
unsigned int getWidth() const override;
|
||||
unsigned int getHeight() const override;
|
||||
unsigned int getWidth() const override;
|
||||
unsigned int getHeight() const override;
|
||||
|
||||
int getTypesCount() override { return 1; }
|
||||
std::vector<color_type> getTypes() override { return { color_type::Black }; }
|
||||
int getTypeStride(color_type) override { return 0; }
|
||||
int getTypesCount() override { return 1; }
|
||||
std::vector<color_type> getTypes() override { return {color_type::Black}; }
|
||||
int getTypeStride(color_type) override { return 0; }
|
||||
|
||||
bool canWriteFile(file_type type) override;
|
||||
bool writeToFile(file_type type, bool canOverwrite, const char *filename) override;
|
||||
bool writeToFile(file_type type, bool canOverwrite, const std::string &filename) override;
|
||||
private:
|
||||
std::vector<uint8_t> data;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
}
|
||||
bool canWriteFile(file_type type) override;
|
||||
bool writeToFile(file_type type, bool canOverwrite,
|
||||
const char *filename) override;
|
||||
bool writeToFile(file_type type, bool canOverwrite,
|
||||
const std::string &filename) override;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> data;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
} // namespace image
|
||||
|
||||
#endif
|
||||
|
|
88
src/main.cpp
88
src/main.cpp
|
@ -1,52 +1,52 @@
|
|||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include "arg_parse.hpp"
|
||||
#include "blue_noise.hpp"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Args args;
|
||||
if(args.ParseArgs(argc, argv)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// validation
|
||||
if (args.generate_blue_noise_) {
|
||||
if (args.output_filename_.empty()) {
|
||||
std::cout << "ERROR: Cannot generate blue-noise, output filename is not specified"
|
||||
<< std::endl;
|
||||
Args::DisplayHelp();
|
||||
return 1;
|
||||
} else if (args.blue_noise_size_ < 16) {
|
||||
std::cout << "ERROR: blue-noise size is too small"
|
||||
<< std::endl;
|
||||
Args::DisplayHelp();
|
||||
return 1;
|
||||
} else if (!args.overwrite_file_) {
|
||||
FILE *file = std::fopen(args.output_filename_.c_str(), "r");
|
||||
if (file) {
|
||||
std::fclose(file);
|
||||
std::cout << "ERROR: overwrite not specified, but filename exists"
|
||||
<< std::endl;
|
||||
Args::DisplayHelp();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cout << "ERROR: No operation specified\n";
|
||||
Args::DisplayHelp();
|
||||
}
|
||||
|
||||
if (args.generate_blue_noise_) {
|
||||
std::cout << "Generating blue_noise..." << std::endl;
|
||||
image::Bl bl = dither::blue_noise(args.blue_noise_size_,
|
||||
args.blue_noise_size_,
|
||||
args.threads_,
|
||||
args.use_opencl_);
|
||||
if(!bl.writeToFile(image::file_type::PNG, args.overwrite_file_, args.output_filename_)) {
|
||||
std::cout << "ERROR: Failed to write blue-noise to file\n";
|
||||
}
|
||||
}
|
||||
|
||||
Args args;
|
||||
if (args.ParseArgs(argc, argv)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// validation
|
||||
if (args.generate_blue_noise_) {
|
||||
if (args.output_filename_.empty()) {
|
||||
std::cout << "ERROR: Cannot generate blue-noise, output filename "
|
||||
"is not specified"
|
||||
<< std::endl;
|
||||
Args::DisplayHelp();
|
||||
return 1;
|
||||
} else if (args.blue_noise_size_ < 16) {
|
||||
std::cout << "ERROR: blue-noise size is too small" << std::endl;
|
||||
Args::DisplayHelp();
|
||||
return 1;
|
||||
} else if (!args.overwrite_file_) {
|
||||
FILE *file = std::fopen(args.output_filename_.c_str(), "r");
|
||||
if (file) {
|
||||
std::fclose(file);
|
||||
std::cout << "ERROR: overwrite not specified, but filename exists"
|
||||
<< std::endl;
|
||||
Args::DisplayHelp();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cout << "ERROR: No operation specified\n";
|
||||
Args::DisplayHelp();
|
||||
}
|
||||
|
||||
if (args.generate_blue_noise_) {
|
||||
std::cout << "Generating blue_noise..." << std::endl;
|
||||
image::Bl bl =
|
||||
dither::blue_noise(args.blue_noise_size_, args.blue_noise_size_,
|
||||
args.threads_, args.use_opencl_);
|
||||
if (!bl.writeToFile(image::file_type::PNG, args.overwrite_file_,
|
||||
args.output_filename_)) {
|
||||
std::cout << "ERROR: Failed to write blue-noise to file\n";
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
#ifndef DITHERING_UTILITY_HPP
|
||||
#define DITHERING_UTILITY_HPP
|
||||
|
||||
#include <utility>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
namespace utility {
|
||||
inline int twoToOne(int x, int y, int width, int height) {
|
||||
while(x < 0) {
|
||||
x += width;
|
||||
}
|
||||
while(y < 0) {
|
||||
y += height;
|
||||
}
|
||||
x = x % width;
|
||||
y = y % height;
|
||||
return x + y * width;
|
||||
}
|
||||
|
||||
inline std::pair<int, int> oneToTwo(int i, int width) {
|
||||
return {i % width, i / width};
|
||||
}
|
||||
|
||||
inline float dist(int a, int b, int width) {
|
||||
auto axy = utility::oneToTwo(a, width);
|
||||
auto bxy = utility::oneToTwo(b, width);
|
||||
float dx = axy.first - bxy.first;
|
||||
float dy = axy.second - bxy.second;
|
||||
return std::sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
inline int twoToOne(int x, int y, int width, int height) {
|
||||
while (x < 0) {
|
||||
x += width;
|
||||
}
|
||||
while (y < 0) {
|
||||
y += height;
|
||||
}
|
||||
x = x % width;
|
||||
y = y % height;
|
||||
return x + y * width;
|
||||
}
|
||||
|
||||
inline std::pair<int, int> oneToTwo(int i, int width) {
|
||||
return {i % width, i / width};
|
||||
}
|
||||
|
||||
inline float dist(int a, int b, int width) {
|
||||
auto axy = utility::oneToTwo(a, width);
|
||||
auto bxy = utility::oneToTwo(b, width);
|
||||
float dx = axy.first - bxy.first;
|
||||
float dy = axy.second - bxy.second;
|
||||
return std::sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
} // namespace utility
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue