RosettaCodeData/Task/Image-convolution/D/image-convolution.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));
}