117 lines
3.8 KiB
D
117 lines
3.8 KiB
D
import std.string, std.math, std.algorithm, grayscale_image;
|
|
|
|
struct ConvolutionFilter {
|
|
double[][] kernel;
|
|
double divisor, offset_;
|
|
string name;
|
|
}
|
|
|
|
|
|
Image!Color convolve(Color)(in Image!Color im,
|
|
in ConvolutionFilter filter)
|
|
pure nothrow in {
|
|
assert(im !is null);
|
|
assert(!filter.divisor.isNaN && !filter.offset_.isNaN);
|
|
assert(filter.divisor != 0);
|
|
assert(filter.kernel.length > 0 && filter.kernel[0].length > 0);
|
|
foreach (const row; filter.kernel) // Is rectangular.
|
|
assert(row.length == filter.kernel[0].length);
|
|
assert(filter.kernel.length % 2 == 1); // Odd sized kernel.
|
|
assert(filter.kernel[0].length % 2 == 1);
|
|
assert(im.ny >= filter.kernel.length);
|
|
assert(im.nx >= filter.kernel[0].length);
|
|
} out(result) {
|
|
assert(result !is null);
|
|
assert(result.nx == im.nx && result.ny == im.ny);
|
|
} body {
|
|
immutable knx2 = filter.kernel[0].length / 2;
|
|
immutable kny2 = filter.kernel.length / 2;
|
|
auto io = new Image!Color(im.nx, im.ny);
|
|
|
|
static if (is(Color == RGB))
|
|
alias CT = typeof(Color.r); // Component type.
|
|
else static if (is(typeof(Color.c)))
|
|
alias CT = typeof(Color.c);
|
|
else
|
|
alias CT = Color;
|
|
|
|
foreach (immutable y; kny2 .. im.ny - kny2) {
|
|
foreach (immutable x; knx2 .. im.nx - knx2) {
|
|
static if (is(Color == RGB))
|
|
double[3] total = 0.0;
|
|
else
|
|
double total = 0.0;
|
|
|
|
foreach (immutable sy, const kRow; filter.kernel) {
|
|
foreach (immutable sx, immutable k; kRow) {
|
|
immutable p = im[x + sx - knx2, y + sy - kny2];
|
|
static if (is(Color == RGB)) {
|
|
total[0] += p.r * k;
|
|
total[1] += p.g * k;
|
|
total[2] += p.b * k;
|
|
} else {
|
|
total += p * k;
|
|
}
|
|
}
|
|
}
|
|
|
|
immutable D = filter.divisor;
|
|
immutable O = filter.offset_ * CT.max;
|
|
static if (is(Color == RGB)) {
|
|
io[x, y] = Color(
|
|
cast(CT)min(max(total[0]/ D + O, CT.min), CT.max),
|
|
cast(CT)min(max(total[1]/ D + O, CT.min), CT.max),
|
|
cast(CT)min(max(total[2]/ D + O, CT.min), CT.max));
|
|
} else static if (is(typeof(Color.c))) {
|
|
io[x, y] = Color(
|
|
cast(CT)min(max(total / D + O, CT.min), CT.max));
|
|
} else {
|
|
// If Color doesn't have a 'c' field, then Color is
|
|
// assumed to be a built-in type.
|
|
io[x, y] =
|
|
cast(CT)min(max(total / D + O, CT.min), CT.max);
|
|
}
|
|
}
|
|
}
|
|
|
|
return io;
|
|
}
|
|
|
|
|
|
void main() {
|
|
immutable ConvolutionFilter[] filters = [
|
|
{[[-2.0, -1.0, 0.0],
|
|
[-1.0, 1.0, 1.0],
|
|
[ 0.0, 1.0, 2.0]], divisor:1.0, offset_:0.0, name:"Emboss"},
|
|
|
|
{[[-1.0, -1.0, -1.0],
|
|
[-1.0, 9.0, -1.0],
|
|
[-1.0, -1.0, -1.0]], divisor:1.0, 0.0, "Sharpen"},
|
|
|
|
{[[-1.0, -2.0, -1.0],
|
|
[ 0.0, 0.0, 0.0],
|
|
[ 1.0, 2.0, 1.0]], divisor:1.0, 0.5, "Sobel_emboss"},
|
|
|
|
{[[1.0, 1.0, 1.0],
|
|
[1.0, 1.0, 1.0],
|
|
[1.0, 1.0, 1.0]], divisor:9.0, 0.0, "Box_blur"},
|
|
|
|
{[[1, 4, 7, 4, 1],
|
|
[4, 16, 26, 16, 4],
|
|
[7, 26, 41, 26, 7],
|
|
[4, 16, 26, 16, 4],
|
|
[1, 4, 7, 4, 1]], divisor:273, 0.0, "Gaussian_blur"}];
|
|
|
|
Image!RGB im;
|
|
im.loadPPM6("Lenna100.ppm");
|
|
|
|
foreach (immutable filter; filters)
|
|
im.convolve(filter)
|
|
.savePPM6(format("lenna_%s.ppm", filter.name));
|
|
|
|
const img = im.rgb2grayImage();
|
|
foreach (immutable filter; filters)
|
|
img.convolve(filter)
|
|
.savePGM(format("lenna_gray_%s.ppm", filter.name));
|
|
}
|