128 lines
4.5 KiB
D
128 lines
4.5 KiB
D
import std.stdio, std.algorithm, std.string, std.functional,
|
|
std.typecons, std.typetuple, bitmap;
|
|
|
|
struct BlackWhite {
|
|
ubyte c;
|
|
alias c this;
|
|
static immutable black = typeof(this)(0),
|
|
white = typeof(this)(1);
|
|
}
|
|
|
|
alias Neighbours = BlackWhite[9];
|
|
alias Img = Image!BlackWhite;
|
|
|
|
/// Zhang-Suen thinning algorithm.
|
|
Img zhangSuen(Img image1, Img image2) pure nothrow @safe @nogc
|
|
in {
|
|
assert(image1.image.all!(x => x == Img.black || x == Img.white));
|
|
assert(image1.nx == image2.nx && image1.ny == image2.ny);
|
|
} out(result) {
|
|
assert(result.nx == image1.nx && result.ny == image1.ny);
|
|
assert(result.image.all!(x => x == Img.black || x == Img.white));
|
|
} body {
|
|
/// True if inf <= x <= sup.
|
|
static inInterval(T)(in T x, in T inf, in T sup) pure nothrow @safe @nogc {
|
|
return x >= inf && x <= sup;
|
|
}
|
|
|
|
/// Return 8-neighbours+1 of point (x,y) of given image, in order.
|
|
static void neighbours(in Img I, in size_t x, in size_t y,
|
|
out Neighbours n) pure nothrow @safe @nogc {
|
|
n = [I[x,y-1], I[x+1,y-1], I[x+1,y], I[x+1,y+1], // P2,P3,P4,P5
|
|
I[x,y+1], I[x-1,y+1], I[x-1,y], I[x-1,y-1], // P6,P7,P8,P9
|
|
I[x,y-1]];
|
|
}
|
|
|
|
if (image1.nx < 3 || image1.ny < 3) {
|
|
image2.image[] = image1.image[];
|
|
return image2;
|
|
}
|
|
|
|
immutable static zeroOne = [0, 1]; //**
|
|
Neighbours n;
|
|
bool hasChanged;
|
|
do {
|
|
hasChanged = false;
|
|
|
|
foreach (immutable ab; TypeTuple!(tuple(2, 4), tuple(0, 6))) {
|
|
foreach (immutable y; 1 .. image1.ny - 1) {
|
|
foreach (immutable x; 1 .. image1.nx - 1) {
|
|
neighbours(image1, x, y, n);
|
|
if (image1[x, y] && // Cond. 0
|
|
(!n[ab[0]] || !n[4] || !n[6]) && // Cond. 4
|
|
(!n[0] || !n[2] || !n[ab[1]]) && // Cond. 3
|
|
//n[].count([0, 1]) == 1 &&
|
|
n[].count(zeroOne) == 1 && // Cond. 2
|
|
// n[0 .. 8].sum in iota(2, 7)) {
|
|
inInterval(n[0 .. 8].sum, 2, 6)) { // Cond. 1
|
|
hasChanged = true;
|
|
image2[x, y] = Img.black;
|
|
} else
|
|
image2[x, y] = image1[x, y];
|
|
}
|
|
}
|
|
image1.swap(image2);
|
|
}
|
|
} while (hasChanged);
|
|
|
|
return image1;
|
|
}
|
|
|
|
void main() {
|
|
immutable before_txt = "
|
|
##..###
|
|
##..###
|
|
##..###
|
|
##..###
|
|
##..##.
|
|
##..##.
|
|
##..##.
|
|
##..##.
|
|
##..##.
|
|
##..##.
|
|
##..##.
|
|
##..##.
|
|
######.
|
|
.......";
|
|
|
|
immutable small_rc = "
|
|
................................
|
|
.#########.......########.......
|
|
.###...####.....####..####......
|
|
.###....###.....###....###......
|
|
.###...####.....###.............
|
|
.#########......###.............
|
|
.###.####.......###....###......
|
|
.###..####..###.####..####.###..
|
|
.###...####.###..########..###..
|
|
................................";
|
|
|
|
immutable rc = "
|
|
...........................................................
|
|
.#################...................#############.........
|
|
.##################...............################.........
|
|
.###################............##################.........
|
|
.########.....#######..........###################.........
|
|
...######.....#######.........#######.......######.........
|
|
...######.....#######........#######.......................
|
|
...#################.........#######.......................
|
|
...################..........#######.......................
|
|
...#################.........#######.......................
|
|
...######.....#######........#######.......................
|
|
...######.....#######........#######.......................
|
|
...######.....#######.........#######.......######.........
|
|
.########.....#######..........###################.........
|
|
.########.....#######.######....##################.######..
|
|
.########.....#######.######......################.######..
|
|
.########.....#######.######.........#############.######..
|
|
...........................................................";
|
|
|
|
foreach (immutable txt; [before_txt, small_rc, rc]) {
|
|
auto img = Img.fromText(txt);
|
|
"From:".writeln;
|
|
img.textualShow(/*bl=*/ '.', /*wh=*/ '#');
|
|
"\nTo thinned:".writeln;
|
|
img.zhangSuen(img.dup).textualShow(/*bl=*/ '.', /*wh=*/ '#');
|
|
writeln;
|
|
}
|
|
}
|