branching_number.hpp

Back to Branching number

pastel/gfx/image_processing/

#ifndef PASTELGFX_BRANCHING_NUMBER_HPP
#define PASTELGFX_BRANCHING_NUMBER_HPP

#include "pastel/gfx/image_processing/branching_number.h"
#include "pastel/gfx/image_processing/pixelneighbours.h"

#include <vector>

namespace Pastel
{

    inline void branchingNumber(
        const Array<bool, 2>& image,
        Array<int32, 2>& numberImage)
    {
        // Let the 8-neighbourhood
        // of a binary pixel be enumerated as
        // follows:
        // d c b
        // e   a
        // f g h
        // The problem is to
        // count the number of connected
        // components in this ring
        // (number of chains of 1).
        // For example 10110101:
        // 0 1 0
        // 1   1
        // 1 0 1
        // Has three connected components (not 4).
        // This number is cached for each
        // 8-bit sequence for performance.

        // Cache the branching number table
        // if this is the first time.

        static std::vector<integer> table;
        static bool initialized = false;

        if (!initialized)
        {
            table.reserve(256);

            for (integer i = 0;i < 256;++i)
            {
                uint8 content = (uint8)i;
                integer number = 0;
                bool previousBit = (content & 0x80) != 0;
                uint8 bitMask = 1;
                for (integer j = 0;j < 8;++j)
                {
                    const bool currentBit = ((content & bitMask) != 0);
                    if (currentBit && !previousBit)
                    {
                        ++number;
                    }

                    previousBit = currentBit;

                    bitMask <<= 1;
                }

                table.push_back(number);
            }

            initialized = true;
        }

        integer width = image.width();
        integer height = image.height();

        Array<int32, 2> result(Vector2i(width, height), -1);

        // We want to avoid bound comparisons
        // so we treat the boundary pixels
        // specially.

        // Inner pixels

        for (integer x = 1; x < width - 1;++x)
        {
            for (integer y = 1; y < height - 1;++y)
            {
                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x + 1, y))
                    {
                        content += 1;
                    }
                    if (image(x + 1, y + 1))
                    {
                        content += 2;
                    }
                    if (image(x, y + 1))
                    {
                        content += 4;
                    }
                    if (image(x - 1, y + 1))
                    {
                        content += 8;
                    }
                    if (image(x - 1, y))
                    {
                        content += 16;
                    }
                    if (image(x - 1, y - 1))
                    {
                        content += 32;
                    }
                    if (image(x, y - 1))
                    {
                        content += 64;
                    }
                    if (image(x + 1, y - 1))
                    {
                        content += 128;
                    }

                    result(x, y) = table[content];
                }
            }
        }

        // Bottom row

        if (height > 1)
        {
            integer y = 0;
            for (integer x = 1;x < width - 1;++x)
            {
                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x + 1, y))
                    {
                        content += 1;
                    }
                    if (image(x + 1, y + 1))
                    {
                        content += 2;
                    }
                    if (image(x, y + 1))
                    {
                        content += 4;
                    }
                    if (image(x - 1, y + 1))
                    {
                        content += 8;
                    }
                    if (image(x - 1, y))
                    {
                        content += 16;
                    }

                    result(x, y) = table[content];
                }
            }
        }

        // Top row

        if (height > 1)
        {
            integer y = height - 1;
            for (integer x = 1;x < width - 1;++x)
            {
                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x + 1, y))
                    {
                        content += 1;
                    }
                    if (image(x - 1, y))
                    {
                        content += 16;
                    }
                    if (image(x - 1, y - 1))
                    {
                        content += 32;
                    }
                    if (image(x, y - 1))
                    {
                        content += 64;
                    }
                    if (image(x + 1, y - 1))
                    {
                        content += 128;
                    }

                    result(x, y) = table[content];
                }
            }
        }

        // Left column

        if (width > 1)
        {
            integer x = 0;
            for (integer y = 1; y < height - 1;++y)
            {
                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x + 1, y))
                    {
                        content += 1;
                    }
                    if (image(x + 1, y + 1))
                    {
                        content += 2;
                    }
                    if (image(x, y + 1))
                    {
                        content += 4;
                    }
                    if (image(x, y - 1))
                    {
                        content += 64;
                    }
                    if (image(x + 1, y - 1))
                    {
                        content += 128;
                    }

                    result(x, y) = table[content];
                }
            }
        }

        // Right column

        if (width > 1)
        {
            integer x = width - 1;
            for (integer y = 1; y < height - 1;++y)
            {
                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x, y + 1))
                    {
                        content += 4;
                    }
                    if (image(x - 1, y + 1))
                    {
                        content += 8;
                    }
                    if (image(x - 1, y))
                    {
                        content += 16;
                    }
                    if (image(x - 1, y - 1))
                    {
                        content += 32;
                    }
                    if (image(x, y - 1))
                    {
                        content += 64;
                    }

                    result(x, y) = table[content];
                }
            }
        }

        if (width > 1 && height > 1)
        {
            // Left-bottom corner
            {
                integer x = 0;
                integer y = 0;

                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x + 1, y))
                    {
                        content += 1;
                    }
                    if (image(x + 1, y + 1))
                    {
                        content += 2;
                    }
                    if (image(x, y + 1))
                    {
                        content += 4;
                    }

                    result(x, y) = table[content];
                }
            }
            // Right-bottom corner
            {
                integer x = width - 1;
                integer y = 0;

                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x, y + 1))
                    {
                        content += 4;
                    }
                    if (image(x - 1, y + 1))
                    {
                        content += 8;
                    }
                    if (image(x - 1, y))
                    {
                        content += 16;
                    }

                    result(x, y) = table[content];
                }
            }
            // Right-top corner
            {
                integer x = width - 1;
                integer y = height - 1;

                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x - 1, y))
                    {
                        content += 16;
                    }
                    if (image(x - 1, y - 1))
                    {
                        content += 32;
                    }
                    if (image(x, y - 1))
                    {
                        content += 64;
                    }

                    result(x, y) = table[content];
                }
            }
            // Left-top corner
            {
                integer x = 0;
                integer y = height - 1;

                if (image(x, y))
                {
                    integer content = 0;
                    if (image(x + 1, y))
                    {
                        content += 1;
                    }
                    if (image(x + 1, y + 1))
                    {
                        content += 2;
                    }
                    if (image(x, y + 1))
                    {
                        content += 4;
                    }

                    result(x, y) = table[content];
                }
            }
        }

        result.swap(numberImage);
    }

    inline integer branchingNumber(
        const Array<bool, 2>& image,
        integer x, integer y)
    {
        integer width = image.width();
        integer height = image.height();

        ENSURE2(x >= 0 && x < width, x, width);
        ENSURE2(y >= 0 && y < height, y, height);

        uint32 neighbourMask = pixelNeighbourMask(image, x, y);

        integer result = 0;

        bool previousBit =
            (neighbourMask & 128) != 0;
        for (integer i = 0;i < 8;++i)
        {
            uint32 bitMask = 1 << i;
            bool currentBit =
                (neighbourMask & bitMask) != 0;
            if (currentBit && !previousBit)
            {
                ++result;
            }

            previousBit = currentBit;
        }

        return result;
    }

    inline uint32 branchingMask(
        const Array<bool, 2>& image,
        integer x, integer y)
    {
        integer width = image.width();
        integer height = image.height();

        ENSURE2(x >= 0 && x < width, x, width);
        ENSURE2(y >= 0 && y < height, y, height);

        uint32 neighbourMask = pixelNeighbourMask(image, x, y);

        uint32 resultMask = 0;

        bool previousBit =
            (neighbourMask & 128) != 0;
        for (integer i = 0;i < 8;++i)
        {
            uint32 bitMask = 1 << i;
            bool currentBit =
                (neighbourMask & bitMask) != 0;
            if (currentBit && !previousBit)
            {
                resultMask |= bitMask;
            }

            previousBit = currentBit;
        }

        // We prefer 4-neighbours to
        // 8-neighbours.

        for (integer i = 0;i < 8;++i)
        {
            uint32 bitMask = 1 << i;

            const uint32 nextMask = 1 << ((i + 1) & 7);
            bool nextBit =
                (neighbourMask & nextMask) != 0;
            const bool corner = (i & 1) != 0;

            if (corner && nextBit)
            {
                resultMask &= ~bitMask;

                resultMask |= nextMask;
            }
        }

        return resultMask;
    }

}

#endif