Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion image_processing/median-filter/dub.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name "median-filter"
copyright "Copyright © 2015, Ilya Yaroshenko"
authors "Ilya Yaroshenko"
dependency "imageformats" version="~>5.2.0"
dependency "mir" version="~>0.9.0-alpha.0"
dependency "mir" version="~>0.10.0-beta"
buildType "release" {
buildOptions "releaseMode" "optimize" "inline" "noBoundsCheck"
}
36 changes: 17 additions & 19 deletions image_processing/median-filter/source/app.d
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import mir.ndslice;

import std.parallelism;
/++
A median filter is implemented as an example. The function
`movingWindowByChannel` can also be used with other filters that use a sliding
Expand Down Expand Up @@ -31,13 +32,9 @@ Returns:
where с is the number of channels in the image.
Dense data layout is guaranteed.
+/

Slice!(3, C*) movingWindowByChannel(alias filter, C)
(Slice!(3, C*) image, size_t nr, size_t nc)
{
import std.algorithm.iteration: map;
import std.array: array;

// 0. 3D
// The last dimension represents the color channel.
auto wnds = image
Expand All @@ -57,16 +54,16 @@ Slice!(3, C*) movingWindowByChannel(alias filter, C)
// Packs the last two dimensions.
.pack!2;

return wnds
// 6. Range composed of 2D
// Gathers all windows in the range.
.byElement
// 6. Range composed of 2D
// Gathers all windows in the range.
auto lazyRange = wnds.byElement;

return
// 7. Range composed of pixels
// 2D to pixel lazy conversion.
.map!filter
// 8. `C[]`
// The only memory allocation in this function.
.array
// The only memory allocation in this function with parallel initilization.
taskPool().amap!filter(lazyRange) // Uses default task pool
// 9. 3D
// Returns slice with corresponding shape.
.sliced(wnds.shape);
Expand All @@ -75,21 +72,24 @@ Slice!(3, C*) movingWindowByChannel(alias filter, C)
/++
Params:
r = input range
buf = buffer with length no less than the number of elements in `r`
pool = pool of tasks
Returns:
median value over the range `r`
+/
T median(Range, T)(Range r, T[] buf)
ubyte median(Window)(Window w)
{
import std.algorithm.sorting: sort;
size_t n;
foreach (e; r)
auto buf = buffers[taskPool().workerIndex];
foreach (e; w.byElement)
buf[n++] = e;
buf[0 .. n].sort();
immutable m = n >> 1;
return n & 1 ? buf[m] : cast(T)((buf[m - 1] + buf[m]) / 2);
return n & 1 ? buf[m] : cast(ubyte)((buf[m - 1] + buf[m]) / 2);
}

shared ubyte[][] buffers;

/++
This program works both with color and grayscale images.
+/
Expand All @@ -113,7 +113,7 @@ void main(string[] args)
if (!nr) nr = def;
if (!nc) nc = nr;

auto buf = new ubyte[nr * nc];
auto buffers = new shared ubyte[][](nr * nc, defaultPoolThreads() + 1);

foreach (name; args[1 .. $])
{
Expand All @@ -123,9 +123,7 @@ void main(string[] args)

auto ret = image.pixels
.sliced(cast(size_t)image.h, cast(size_t)image.w, cast(size_t)image.c)
.movingWindowByChannel
!(window => median(window.byElement, buf))
(nr, nc);
.movingWindowByChannel!median(nr, nc);

write_image(
name.stripExtension ~ "_filtered.png",
Expand Down