2018-12-16 05:11:41 +08:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include "kernel.h"
|
2018-12-20 04:50:02 +08:00
|
|
|
#include "utils.h"
|
2018-12-16 05:11:41 +08:00
|
|
|
|
2019-01-01 08:15:51 +08:00
|
|
|
/// Sum a region convolution kernel. Region is defined by a width x height rectangle whose
|
|
|
|
/// top left corner is at (x, y)
|
|
|
|
double sum_kernel(const conv *map, int x, int y, int width, int height) {
|
|
|
|
double ret = 0;
|
2018-12-16 05:11:41 +08:00
|
|
|
/*
|
2019-01-01 08:15:51 +08:00
|
|
|
* Compute set of filter values which are "in range"
|
2018-12-16 05:11:41 +08:00
|
|
|
*/
|
|
|
|
|
2019-01-01 08:15:51 +08:00
|
|
|
int xstart = x;
|
|
|
|
if (xstart < 0)
|
|
|
|
xstart = 0;
|
|
|
|
int xend = width + x;
|
|
|
|
if (xend > map->size)
|
|
|
|
xend = map->size;
|
|
|
|
|
|
|
|
int ystart = y;
|
|
|
|
if (ystart < 0)
|
|
|
|
ystart = 0;
|
|
|
|
int yend = height + y;
|
|
|
|
if (yend > map->size)
|
|
|
|
yend = map->size;
|
|
|
|
|
|
|
|
for (int yi = ystart; yi < yend; yi++) {
|
|
|
|
for (int xi = xstart; xi < xend; xi++) {
|
|
|
|
ret += map->data[yi * map->size + xi];
|
2018-12-16 05:11:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-01 08:15:51 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2018-12-16 05:11:41 +08:00
|
|
|
|
2019-01-01 08:15:51 +08:00
|
|
|
double sum_kernel_normalized(const conv *map, int x, int y, int width, int height) {
|
|
|
|
double ret = sum_kernel(map, x, y, width, height);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
if (ret > 1) {
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
return ret;
|
2018-12-16 05:11:41 +08:00
|
|
|
}
|
|
|
|
|
2018-12-20 07:41:58 +08:00
|
|
|
static double attr_const gaussian(double r, double x, double y) {
|
2018-12-16 05:11:41 +08:00
|
|
|
// Formula can be found here:
|
|
|
|
// https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
|
|
|
|
// Except a special case for r == 0 to produce sharp shadows
|
|
|
|
if (r == 0)
|
|
|
|
return 1;
|
|
|
|
return exp(-0.5 * (x * x + y * y) / (r * r)) / (2 * M_PI * r * r);
|
|
|
|
}
|
|
|
|
|
|
|
|
conv *gaussian_kernel(double r) {
|
|
|
|
conv *c;
|
|
|
|
int size = r * 2 + 1;
|
|
|
|
int center = size / 2;
|
|
|
|
double t;
|
|
|
|
|
|
|
|
c = cvalloc(sizeof(conv) + size * size * sizeof(double));
|
|
|
|
c->size = size;
|
|
|
|
t = 0.0;
|
|
|
|
|
|
|
|
for (int y = 0; y < size; y++) {
|
|
|
|
for (int x = 0; x < size; x++) {
|
|
|
|
double g = gaussian(r, x - center, y - center);
|
|
|
|
t += g;
|
|
|
|
c->data[y * size + x] = g;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = 0; y < size; y++) {
|
|
|
|
for (int x = 0; x < size; x++) {
|
|
|
|
c->data[y * size + x] /= t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2019-01-01 08:15:51 +08:00
|
|
|
/// preprocess kernels to make shadow generation faster
|
|
|
|
/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
|
|
|
|
void shadow_preprocess(conv *map, double **shadow_sum) {
|
|
|
|
const int d = map->size;
|
|
|
|
|
|
|
|
if (*shadow_sum)
|
|
|
|
free(*shadow_sum);
|
|
|
|
|
|
|
|
auto sum = *shadow_sum = ccalloc(d * d, double);
|
|
|
|
sum[0] = map->data[0];
|
|
|
|
|
|
|
|
for (int x = 1; x < d; x++) {
|
|
|
|
sum[x] = sum[x - 1] + map->data[x];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = 1; y < d; y++) {
|
|
|
|
sum[y * d] = sum[(y - 1) * d] + map->data[y * d];
|
|
|
|
for (int x = 1; x < d; x++) {
|
|
|
|
double tmp = sum[(y - 1) * d + x] + sum[y * d + x - 1] -
|
|
|
|
sum[(y - 1) * d + x - 1];
|
|
|
|
sum[y * d + x] = tmp + map->data[y * d + x];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 05:11:41 +08:00
|
|
|
// vim: set noet sw=8 ts=8 :
|