Thursday, August 28, 2014

Reference : Image processing C# tutorial 1– find binary image boundary

Image processing C# tutorial 1– find binary image boundary
Finding the boundary of a binary image can be done in two steps 1) apply a binary erosion. The binary erosion “erodes” the edge of the image. 2) Subtract the eroded image from original image. What is left is the border. A binary erosion is to move a small structure element (a 3 by 3 image) across the target image, and the pixel on the target image is kept only if the pixels on the target image fully “cover” the structure element for the area covered by the structure element. In other words R AND S = S for any test region R and structure element S. The C# code looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EdgeOfBinary
{
    class Program
    {
        static void Main(string[] args)
        {
            BinaryImage testImage = new BinaryImage(new int[20, 20]
                {
                    {0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                    {0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0},
                    {0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0},
                    {0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0},
                    {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0},
                    {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0}
                });
            BinaryImage structureElement = new BinaryImage(new int[3, 3]
            {
                {0,1,0},
                {1,1,1},
                {0,1,0}
            });
            BinaryImage resultImage = new BinaryImage(testImage.Boundary(structureElement));
            testImage.Print();
            resultImage.Print();
        }
    }
    class BinaryImage
    {
        public int Width { get; private set; }
        public int Height { get; private set; }
        public int[,] Pixels { get; set; }
        public BinaryImage(int width, int height)
        {
            Pixels = new int[width, height];
            Width = width;
            Height = height;
        }
        public BinaryImage(int[,] pixels)
        {
            Pixels = pixels;
            Width = pixels.GetLength(0);
            Height = pixels.GetLength(1);
        }
        public void Flush(int value)
        {
            for (int i =0;i < Width; i++)
                for (int j =0; j <Height; j++)
                    Pixels[i, j] = value;
        }
        public int[,] Erode(BinaryImage structureElement)
        {
            int[,] ret = new int[Width, Height];
            for (int i = 0; i < Width; i++)
            {
                for (int j = 0; j < Height; j++)
                {
                    bool keep = true;
                    for (int m = 0; m < structureElement.Width; m++)
                    {
                        for (int n = 0; n < structureElement.Height; n++)
                        {
                            if (Pixels[Math.Min(Math.Max(0, i + m - structureElement.Width / 2), Width-1),
                                        Math.Min(Math.Max(0, j + n - structureElement.Height / 2), Height-1)] != structureElement.Pixels[m, n] && structureElement.Pixels[m,n]!=0)
                            {
                                keep = false;
                                break;
                            }
                        }
                    }
                    ret[i, j] = keep ? 1 : 0;
                }
            }
            return ret;
        }
        public int[,] Boundary(BinaryImage structureElement)
        {
            int[,] result = Erode(structureElement);
            return Minus(new BinaryImage(result));
        }
        public int[,] Minus(BinaryImage image)
        {
            int[,] minus = new int[Width, Height];
            for (int i = 0; i < Width; i++)
                for (int j = 0; j < Height; j++)
                    minus[i, j] = Pixels[i, j] - image.Pixels[i, j];
            return minus;
        }
       
        public void Print()
        {
            for (int i = 0; i < Width; i++)
            {
                for (int j = 0; j < Height; j++)
                {
                    if (Pixels[i, j] != 0)
                        Console.Write("*");
                    else
                        Console.Write(" ");
                }
                Console.WriteLine();
            }
        }
    }
}
The result of above program looks like the following picture. Left is the original image, right is detected border.
image
The quality of the border is affected by the structure element. The 3 by 3 structure element used by the code can get fairly good result in many cases. The following example is the border detected using a 4 by 4 structure element:
  BinaryImage structureElement = new BinaryImage(new int[4, 4]
            {
                {0,1,1,0},
                {1,1,1,1},
                {1,1,1,1},
                {0,1,1,0}
            });
image

No comments:

Post a Comment