D Programming Language
Report a bug If you spot a problem with this page, click here to create a Bugzilla issue. Improve this page Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using local clone.

# std.experimental.ndslice

# Multidimensional Random Access Ranges

The package provides a multidimensional array implementation. It would be well suited to creating machine learning and image processing algorithms, but should also be general enough for use anywhere with homogeneously-typed multidimensional data. In addition, it includes various functions for iteration, accessing, and manipulation.

Quick Start: sliced is a function designed to create a multidimensional view over a range. Multidimensional view is presented by Slice type.

    auto matrix = new double[12].sliced(3, 4);
    matrix[] = 0;

Note: In many examples std.range.iota is used instead of a regular array, which makes it possible to carry out tests without memory allocation.

Category | Submodule | Declarations
--- | --- | ---
Basic Level Slice , its properties, operator overloading | std.experimental.ndslice.slice | sliced Slice assumeSameStructure ReplaceArrayWithPointer DeepElementType
Middle Level Various iteration operators | std.experimental.ndslice.iteration | transposed strided reversed rotated everted swapped allReversed dropToHypercube and other drop primitives
Advanced Level Abstract operators for loop free programming Take movingWindowByChannel as an example | std.experimental.ndslice.selection | blocks windows diagonal reshape byElement byElementInStandardSimplex indexSlice pack evertPack unpack

## Example: Image Processing

A median filter is implemented as an example. The function movingWindowByChannel can also be used with other filters that use a sliding window as the argument, in particular with convolution matrices such as the Sobel operator.

movingWindowByChannel iterates over an image in sliding window mode. Each window is transferred to a filter, which calculates the value of the pixel that corresponds to the given window.

This function does not calculate border cases in which a window overlaps the image partially. However, the function can still be used to carry out such calculations. That can be done by creating an amplified image, with the edges reflected from the original image, and then applying the given function to the new file.

Note: You can find the example at GitHub.

    /++
    Params:
        filter = unary function. Dimension window 2D is the argument.
        image = image dimensions `(h, w, c)`,
            where с is the number of channels in the image
        nr = number of rows in the window
        nс = number of columns in the window

    Returns:
        image dimensions `(h - nr + 1, w - nc + 1, c)`,
            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
            // 1. 2D composed of 1D
            // Packs the last dimension.
            .pack!1
            // 2. 2D composed of 2D composed of 1D
            // Splits image into overlapping windows.
            .windows(nr, nc)
            // 3. 5D
            // Unpacks the windows.
            .unpack
            // 4. 5D
            // Brings the color channel dimension to the third position.
            .transposed!(0, 1, 4)
            // 5. 3D Composed of 2D
            // Packs the last two dimensions.
            .pack!2;

        return wnds
            // 6. Range composed of 2D
            // Gathers all windows in the range.
            .byElement
            // 7. Range composed of pixels
            // 2D to pixel lazy conversion.
            .map!filter
            // 8. `C[]`
            // The only memory allocation in this function.
            .array
            // 9. 3D
            // Returns slice with corresponding shape.
            .sliced(wnds.shape);
    }

A function that calculates the value of iterator median is also necessary.

    /++

    Params:
        r = input range
        buf = buffer with length no less than the number of elements in `r`
    Returns:
        median value over the range `r`
    +/
    T median(Range, T)(Range r, T[] buf)
    {
        import std.algorithm.sorting: sort;
        size_t n;
        foreach (e; r)
            buf[n++] = e;
        buf[0 .. n].sort();
        immutable m = n >> 1;
        return n & 1 ? buf[m] : cast(T)((buf[m - 1] + buf[m]) / 2);
    }

The main function:

    void main(string[] args)
    {
        import std.conv: to;
        import std.getopt: getopt, defaultGetoptPrinter;
        import std.path: stripExtension;

        uint nr, nc, def = 3;
        auto helpInformation = args.getopt(
            "nr", "number of rows in window, default value is " ~ def.to!string, &nr,
            "nc", "number of columns in window, default value is equal to nr", &nc);
        if (helpInformation.helpWanted)
        {
            defaultGetoptPrinter(
                "Usage: median-filter [<options...>] [<file_names...>]\noptions:",
                helpInformation.options);
            return;
        }
        if (!nr) nr = def;
        if (!nc) nc = nr;

        auto buf = new ubyte[nr * nc];

        foreach (name; args[1 .. $])
        {
            import imageformats; // can be found at code.dlang.org

            IFImage image = read_image(name);

            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);

            write_image(
                name.stripExtension ~ "_filtered.png",
                ret.length!1,
                ret.length!0,
                (&ret[0, 0, 0])[0 .. ret.elementsCount]);
        }
    }

This program works both with color and grayscale images.

    $ median-filter --help
    Usage: median-filter [<options...>] [<file_names...>]
    options:
         --nr number of rows in window, default value is 3
         --nc number of columns in window default value equals to nr
    -h --help This help information.

## Compared with numpy.ndarray

numpy is undoubtedly one of the most effective software packages that has facilitated the work of many engineers and scientists. However, due to the specifics of implementation of Python, a programmer who wishes to use the functions not represented in numpy may find that the built-in functions implemented specifically for numpy are not enough, and their Python implementations work at a very low speed. Extending numpy can be done, but is somewhat laborious as even the most basic numpy functions that refer directly to ndarray data must be implemented in C for reasonable performance.

At the same time, while working with ndslice, an engineer has access to the whole set of standard D library, so the functions he creates will be as efficient as if they were written in C.
License:
Boost License 1.0.
Authors:
Ilya Yaroshenko

Acknowledgements: John Loughran Colvin

Source: std/experimental/ndslice/package.d

Copyright © 1999-2016 by Digital Mars ®, All Rights Reserved | Page generated by Ddoc on Wed Jan 27 15:24:54 2016