Content-Aware Image Resizing using GDI+

After I've disappeared from this blog for a while, actually I went travelling at Phuket Island with my friends (may be next time I'll post some pictures, you'll love that place as I do). Today, my intention here is to show you some power of GDI+. Though GDI+ doesn't do well in image processing comparing to others like C++, DirectDraw but at least just make it in GDI+ with C#. You'll see that how can C# manipulate image data.

Content-Aware Image Resizing ,or in another word Seam Carving, concept is proposed by Avidan and Shamir presented at Siggraph 2007. You can see the original paper here : http://www.faculty.idc.ac.il/arik/SCWeb/imret/index.html.

I'm not an expert in image processing and I may make a mistake in this algorithm example here. Therefore, any mistakes you found here don't come from my intention and I appreciate to change and collect those : - )

Ok, let take a look some theories and we'll move ahead to implement the real code!

Firstly you have to understand fundamental GDI+, the way it processes and draws image. If we want to play with image and apply algorithm for it, we must know how to extract pixels and channels from the image. Before that there are some words are worth to look at : 

  • Scan0 - address of the first locked byte of array in memory. For a locked image, it's the first byte address of that image.
  • PixelFormat - pixel has different format that you can use. Format24bppRgb is popular one; 8bit for R(Red Channel) 8bit for G and 8 bit for B. There are many more format you can find here : http://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat.aspx. For this post, we'll use Format24bppRgb .
  • Stride - number of bytes width in one row of pixel data in a locked image array. Stride used multiple of 4 padding boundary that means if your image has 22 pixels in one row, that is,22 pixels * 3 bytes of RGB = 66 bytes for one row. But 66 is not a multiple of 4, we need 68 (68 is a multiple of 4 since 17*4 = 68). So, Stride is 68 not 66, leaving 2 bytes as unused space for efficiency reason.
  • Bitmap.LockBits(..) - locks a Bitmap into system memory. From this method, we'll get BitmapData that enables us to use futher. After we finish manipulating BitmapData, we have to unlock it by using Bitmap.UnlockBits(BitmapData) to release the resource in memory.
  • Therefore, we can do : Stride - (image.Width*3) = unused bytes or padding offset.
  • You can read more details about words here : http://www.bobpowell.net/lockingbits.htm

As noted from the image above, we can get padding = stride - no. of pixel in one row*3 RGB bytes and we have Scan0 as an address for our pointer *p. For playing with pinter in C#, you must set the property of a project to be able to compile unsafe block code.

We already know the detail how to prepare the image for manipulating. We are now able to adapt some techniques for our image. Before that, let have a look how seam carving algorithm can be used.

Calculate the energy : The idea is we have to perform edge detection. Edge detection technique will try to find the sharp change in image brightness to indicate the boundary of objects in the image. One picture is worth looking than 1000 words. So, let have a look.

For computing energy image we use the following formula : . We are using RGB, so we can translate the formula to be matched with our RGB format. Note that there are also other methodologies to find out energy image e.g. finding gradient magnitudes from gX and gY using Convolution Matrix.

The thing that you must know is Window will store BGR bytes in one pixel not RGB bytes. Remember that BGR not RGB. Thus, pointer at p[0] is B, p[1] is G, p[2] is R respectively. The code is look like here : 

public static Bitmap GetEneryMatrixs(Bitmap image)
{
    BitmapData bmData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    //address of the first line. IntPtr is a platform specific type that represents pointer in C#.
    IntPtr ptr = bmData.Scan0;
    //one line length of an image in rgb pixel including padding.
    int stride = bmData.Stride;

    //if you notice, here we use 2 image. One image from method parameter and one to be an output image.
    //You can guess that at first the output image (newBm) is empty. 
    Bitmap newBm = new Bitmap(image.Width, image.Height);
    BitmapData newBmData = newBm.LockBits(new Rectangle(0, 0, newBm.Width, newBm.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    IntPtr newPtr = newBmData.Scan0;
    int newStride = newBmData.Stride;

    unsafe
    {
        //convert pointer ptr into byte* explicitly.
        byte* p = (byte*)ptr;
        int rgbWidth = image.Width * 3;
        int paddingOffset = stride - rgbWidth;

        byte* newP = (byte*)newPtr;
        int newRgbWidth = newBm.Width * 3;
        int newPaddingOffset = newStride - newRgbWidth;

        int rLrR = 0, gLgR = 0, bLbR = 0, rTrB = 0, gTgB = 0, bTbB = 0;
        byte result;
        double dIdx = 0, dIdy = 0;
        for (int j = 0; j < image.Height; j++)
        {
            for (int i = 0; i < image.Width; i++)
            {
                //at the topmost row we don't have p[-3] => B ,p[-2] => G, p[-1] => R. 
                //So we use p[0],p[1],p[2] to replace that respectively.
                int offSetT = 0, offSetB = 0;
                if (j == 0) offSetT = stride;
                else if (j == image.Height - 1) offSetB = -stride;

                bLbR = p[-stride + offSetT] - p[stride + offSetB];
                gTgB = p[-stride + 1 + offSetT] - p[stride + 1 + offSetB];
                rLrR = p[-stride + 2 + offSetT] - p[stride + 2 + offSetB];

                //handle leftmost and rightmost column.
                int offSetL = 0, offSetR = 0;
                if (i == 0) offSetL = 3;
                else if (i == image.Width - 1) offSetR = -3;

                //rLrR is Bleft - Bright, the same for the others.
                bLbR = p[-3 + offSetL] - p[3 + offSetR];
                gLgR = p[-2 + offSetL] - p[4 + offSetR];
                rLrR = p[-1 + offSetL] - p[5 + offSetR];

                dIdx = PythagoreanAdd(bLbR, gLgR, rLrR);
                dIdy = PythagoreanAdd(bTbB, gTgB, rTrB);

                //get the energy result from computing R,G,B channel vectors.
                result = (byte)Math.Round(dIdx + dIdy);
                //save the energy value to our new locked image (newBm) via its pointer (newP).
                newP[0] = result;
                newP[1] = result;
                newP[2] = result;

                //advance the pointers by 3 (R,G,B).
                p += 3;
                newP += 3;
            }

            //add paddingOffset to skip to the next row.
            p += paddingOffset;
            newP += newPaddingOffset;
        }
    }
    //release the resources.
    image.UnlockBits(bmData);
    newBm.UnlockBits(newBmData);

    return newBm;
}
public static double PythagoreanAdd(params int[] varList)
{
    long result = 0;
    for (int i = 0; i < varList.Length; i++)
    {
        //power two for each
        result += varList[i] * varList[i];
    }
    return Math.Sqrt(result);
}

 

Find the best seam : Seam carving has two main algorithms that are widely used for image resizing nowadays, Backward and Forward Energy. In this scenario, I'll use Backward Energy algorithm to find the best seam. In seam carving, we also use cummulative minimum energy or M Matrix and seam route tracking or K Matrix those two matrices sizes are equal to image width and height (not multiply by 3).

By using formula M[i,j] = EnergyImg[i,j] + min[M[i-1,j-1],M[i-1,j],M[i-1,j+1]], we can compute M and K Matrix as the following..

public override int[] ApplyCarvingAlgorithm()
{
    int height = _EnergyImg.Height;
    int width = _EnergyImg.Width;

    int[,] m = new int[height, width];
    int[,] k = new int[height, width];

    BitmapData bmData = _EnergyImg.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    //address of the first line.
    IntPtr ptr = bmData.Scan0;
    //one line length of an image in rgb pixel including padding
    int stride = bmData.Stride;
    unsafe
    {
        //convert pointer ptr into byte* explicitly
        byte* p = (byte*)ptr;
        int rgbWidth = width * 3;
        int paddingOffset = stride - rgbWidth;

        //initialize the first row of m and k matrix.
        for (int i = 0; i < width; i++)
        {
            m[0, i] = p[0];
            k[0, i] = 0;
            p += 3;
        }

        //find matrix m and k values.
        p = (byte*)ptr; //energy image pointer must be reset its position.
        int minIndex = 0;
        for (int j = 1; j < height; j++)
        {
            for (int i = 0; i < width; i++)
            {
                //check left
                if (i <= 0) minIndex = FindMinIndex(999999, m[j - 1, i], m[j - 1, i + 1]);
                //check right
                else if (i + 1 >= width) minIndex = FindMinIndex(m[j - 1, i - 1], m[j - 1, i], 999999);
                else minIndex = FindMinIndex(m[j - 1, i - 1], m[j - 1, i], m[j - 1, i + 1]);

                //minIndex has only 0,1,2 and it doesn't consider the current index, so we need to use minIndex + i(current index)
                m[j, i] = p[0] + m[j - 1, i + minIndex - 1];    //chosen m (***i + minIndex - 1) plus current energy bit.
                k[j, i] = minIndex + 1;   //either 1,2,3 (top row values are all zero)

                p += 3;
            }
            p += paddingOffset;
        }
    }

    _EnergyImg.UnlockBits(bmData);

    return CalculatePositionMatrix(m, k, width, height);
}

After we get the best seam like below, remove those seam from an image. Cut off seam for both _EnergyImg and our inputted image.

 

Remove/Insert seam : For me, I choose to compute positionMatrix from K Matrix, my positionMatrix contains the positions of my seam regarding image width and height. You may come up with your own solution to cut off seam. It's not that hard.

public static Bitmap CutSeam(Bitmap image, int[] positionMatrix)
{
    BitmapData bmData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    //address of the first line.
    IntPtr ptr = bmData.Scan0;
    //one line length of an image in rgb pixel including padding
    int stride = bmData.Stride;

    Bitmap newBm = new Bitmap(image.Width - 1, image.Height);
    BitmapData newBmData = newBm.LockBits(new Rectangle(0, 0, newBm.Width, newBm.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    IntPtr newPtr = newBmData.Scan0;
    int newStride = newBmData.Stride;

    unsafe
    {
        //convert pointer ptr into byte* explicitly
        byte* p = (byte*)ptr;
        int rgbWidth = image.Width * 3;
        int paddingOffset = stride - rgbWidth;

        byte* newP = (byte*)newPtr;
        int newRgbWidth = newBm.Width * 3;
        int newPaddingOffset = newStride - newRgbWidth;

        for (int j = 0; j < image.Height; j++)
        {
            for (int i = 0; i < image.Width - 1; i++)
            {
                if (positionMatrix[j] <= i)
                {
                    newP[0] = p[3];
                    newP[1] = p[4];
                    newP[2] = p[5];
                }
                else
                {
                    newP[0] = p[0];
                    newP[1] = p[1];
                    newP[2] = p[2];
                }
                p += 3;
                newP += 3;
            }
            //advance p 1 pixel (account for r,g,b 3 pigments) because we newP has 1 more pixel than p.
            p += paddingOffset + 3;
            newP += newPaddingOffset;
        }
    }
    image.UnlockBits(bmData);
    newBm.UnlockBits(newBmData);
    return newBm;
}

 

public static Bitmap AddSeam(Bitmap image, int[] positionMatrix)
{
    BitmapData bmData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    //address of the first line.
    IntPtr ptr = bmData.Scan0;
    //one line length of an image in rgb pixel including padding
    int stride = bmData.Stride;

    Bitmap newBm = new Bitmap(image.Width + 1, image.Height);
    BitmapData newBmData = newBm.LockBits(new Rectangle(0, 0, newBm.Width, newBm.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    IntPtr newPtr = newBmData.Scan0;
    int newStride = newBmData.Stride;

    unsafe
    {
        //convert pointer ptr into byte* explicitly
        byte* p = (byte*)ptr;
        int rgbWidth = image.Width * 3;
        int paddingOffset = stride - rgbWidth;

        byte* newP = (byte*)newPtr;
        int newRgbWidth = newBm.Width * 3;
        int newPaddingOffset = newStride - newRgbWidth;

        for (int j = 0; j < image.Height; j++)
        {
            for (int i = 0; i < image.Width + 1; i++)
            {
                if (positionMatrix[j] == i && j != image.Height - 1 && j != 0 && i != 0 && i != image.Width - 1)
                {
                    newP[0] = (byte)((p[-3] + p[0- stride]+ p[3 + stride]) / 3.0);
                    newP[1] = (byte)((p[-2] + p[1 - stride] + p[4 + stride]) / 3.0);
                    newP[2] = (byte)((p[-1] + p[2 - stride] + p[5 + stride]) / 3.0);
                }
                else if (positionMatrix[j] < i)
                {
                    //copy this lowest enery seam
                    newP[0] = p[-3];
                    newP[1] = p[-2];
                    newP[2] = p[-1];
                }
                else
                {
                    newP[0] = p[0];
                    newP[1] = p[1];
                    newP[2] = p[2];
                }
                p += 3;
                newP += 3;

            }
            //we use image.Width + 1 for newP, but for p it must rely on image.Width 
            //not image.Width + 1. So, jump back one pixel (3, from the pointer's point of view).
            p += paddingOffset - 3;
            newP += newPaddingOffset;
        }
    }
    image.UnlockBits(bmData);
    newBm.UnlockBits(newBmData);
    return newBm;
}

 

protected static int[] CalculatePositionMatrix(int[,] m, int[,] k, int width, int height)
{
    //find the minimum value of the last row.
    int minIndex = 0;
    //use oldMinIndex as the initializer.
    int chosenIndex = 1;

    for (; chosenIndex < width; chosenIndex++)
        if (m[height - 1, chosenIndex] < m[height - 1, minIndex])
            minIndex = chosenIndex;

    //mark the position of image bits that we are going to cut off or copy.
    int[] positionMatrix = new int[height];
    //go upward and find the route that has lowest enery.
    for (int y = height - 1; y >= 0; y--)
    {
        positionMatrix[y] = minIndex;    //save current chosen index
        if (k[y, minIndex] == 0) break;  //we reach the top row
        else if (k[y, minIndex] == 1) minIndex--;  //go left in the next loop
        else if (k[y, minIndex] == 3) minIndex++; //go right in the next loop
        //else use the same chosenIndex value
    }
    return positionMatrix;
}

Backward Energy is actually slower than Forward Energy algorithm if we compare. In forward, you don't need to compute energy image, and the result is also better too. Backward may cut off the straight line, for example, but Forward tends to avoid that.

Thanks for reading! I will finish with some result images from our code.


kick it on DotNetKicks.com

Categories:   Image Processing
Actions:   E-mail | del.icio.us | Permalink | Comments (2) | RSS


TestSwarm from Mozilla Labs Launched!

Have you ever got a problem with testing javascript in cross-browser platforms. Moreover, testing javascript in various versions of each browser, Opera, Safari, IE, Firefox, etc, is hard and troublesome. In this case, there are some JS test drivers e.g. Selenium , JSTestDriver . But for today, I'll breifly introduce TestSwarm. TestSwarm, that is now in Alpha state, has been developing to support the jQuery project. It simplifies those testing tasks (in multiple-browserplatforms) and provides tools for manipulating your jQuery project workflow. No more on details. Let's get cracking with its walkthrough video.

The example result from TestSwarm :

 

‘Green’ results are 100% passing,

‘Red’ results have at least one failure,

‘Black’ results include a critical error, and

‘Grey’ results have yet to be run.

So, easy to read, agree?>

 

 

Architecture (from http://wiki.github.com/jeresig/testswarm)

The architecture is as follows:

 

There’s a central server which clients connect to and to which jobs are submitted (this is written in PHP and uses MySQL as a backend).

A client is an instance of the TestSwarm test runner loaded in a browser. One user (e.g. ‘john’) can run multiple clients – and even multiple clients within a single browser. For example you could open 5 tabs in Firefox 3 each with a view of the test runner and would have 5 clients connected.

The test runner is very simple – it’s mostly implemented in JavaScript. It pings the server every 30 seconds asking for a new test suite to run. If there is one it executes it (inside an iframe) and then submits the results back to the central server. If there isn’t a test suite available it just goes back to sleep and waits.

A job is a collection of two things: test suites and browsers. When a project submits a job they specify which test suites to run (it may

only be one test suite – but in the case of jQuery I break apart the main suite into sub-suites for convenience and parallelization) and which browsers you wish to run against.

This ends up generating a bunch of ‘runs’ (a run is one specific browser running one specific test suite). A run can be executed multiple times (the minimum is once). For example a project could say “Make sure this suite is run at least 5 times in Firefox 3.”

 

If you want to try its, you can do that online via : http://testswarm.com

Categories:   Javascript | News
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | RSS


Generics Quick Sort in C#

Today, I will show well-known sorting algorithm that is called Quick Sort.

Normally, On adverage, it uses Θ(nlogn) when compare to n items that usually better than other Θ(nlogn) algorithm but it's not stable.

Quicksort, like merge sort, is based on the divide and conquer paradigm.

Divide: Patition array into two arrays from A[p..r] into A[p..q-1] and A[q+1..r]

Conquer: Sort the two sub arrays in the recursive quick sort.

Combine: Sub arrays are sorted in place, so, no need to combine them.

 

In this algorothm, there are 4 regions to maintain subarray in the partition zone.

pivot || value < pivot || value > pivot || unrestricted         [From left to right of subarray]

and it use pivot to compare the appropriate place to swap the pivot from the both side left and right.

For example: 3,2,6,5
pivot = 3 [index 0]

From the left : Find the first place that value is greater than pivot
left index = 1
3 > 2 move index by one
3 < 6 stop loop
-> left index = 2

From the right : Find the first plae that value is smaller than pivot
right index = 3
3 < 5 move index by minus one
3 < 6 move index by minus one
3 > 2 stop loop
-> right index = 1

You can see that if we sort in ascending order, we should swap pivot with the position of 1 in the array. Then it get 2,3,6,5.
Next, it go to recursion procedure of quicksort. From index 0-1, 2-3.
and do this again until thay can't be partitioned.

using System;

namespace Sorting
{  
    class Sort
    {
        //swap in place
        private static void Swap<T>(T[] arr, int m, int n)
     {
      T temp = arr[m];
      arr[m] = arr[n];
      arr[n] = temp;
     }

        //normal quick sort
        public static void QuickSort<T>(T[] arr) where T : IComparable
     {
      QuickSort(arr, 0, arr.Length-1);
     }
 
     //recursive quick sort
        public static void QuickSort<T>(T[] arr, int start, int end) where T : IComparable
        {
            //based case
            if (end <= start)
                return;

     //---------------- Partition ---------------------------
            T pivot = arr[start]; //pivot
            int i = start;  //starting index
            int j = end + 1; //ending index

            while (i<j)
            {
                //searching for data that is less than pivot
                while (i < end && arr[++i].CompareTo(pivot) < 0) ;

                //searching for data that is greater than pivot
                while (j > start && arr[--j].CompareTo(pivot) > 0) ;

                //exchange data at i and j only if i < j
                if (i < j)
                    Swap(arr, i, j);
            }
            //restore pivot
            arr[start] = arr[j];
            arr[j] = pivot;
     //-----------------------------------------------------

            QuickSort(arr, start, j - 1);//sort left side
            QuickSort(arr, j + 1, end); //sort right side
        }
        static void Main(string[] args)
        {
            int[] a = {5,1,2,4,6,8,100,56,23,55,75,34,84,76,97,56 };
            QuickSort<int>(a);
            foreach (int x in a)
            {
                Console.WriteLine(x);
            }
            Console.ReadLine();
        }
    }
}

Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (1) | RSS


Create Basic Graph and Breadth First Search for Goals with arrays in C#

In this topic I will introduce you about Breadth First Search with a Graph that is created by an array.

Before we go to the example, we need to know about what are the graph and graph's components.

 Graph contains 2 things : Vertices are nodes in the graph , which represent the places or positions in the graph, and Edges that show the connections between vertices.

Next, what is the Breadth First Search : is a basic search algorithm that use queue keep track the visited nodes.

This algorithm will find all posible paths that connect to the current state first and then it will go to the next level by expanding all paths from next states, which are expanded from current state.

Doing these again and again until there is one path find the goal.

Advantage : you can find the solution centainly.

Disadvantage : it uses a lot of memory and number of expanding paths.

Example :

using System;

namespace BreadthFirstSearch
{
    class Vertex
    {
        private char title;
        private Boolean visited;

        public Vertex(char label)
        {
            title = label;
            visited = false;
        }

        public Boolean Visited
        {
            get
            {
                return visited;
            }
            set
            {
                visited = value;
            }
        }

        public Char Title
        {
            get
            {
                return title;
            }
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;

namespace BreadthFirstSearch
{
    class Graph
    {
        private const int MAX = 20;
        private Vertex[] list;
        private int[,] adjMatrix;
        private int numVertices, nVert;
        private char[] vertex;
        private List<int> keep;

        public Graph()
        {
            list = new Vertex[MAX];
            adjMatrix = new int[MAX,MAX];
            numVertices = nVert = 0;
            for(int i = 0; i < MAX; i++)
                for(int j = 0; j < MAX; j++)
                    adjMatrix[i,j] = 0;
            keep = new List<int>();
            vertex = new char[MAX];
        }

        //add this vertex to the list
        public void AddVertex(char title) 
        {
            list[numVertices++] = new Vertex(title);
        }

        //connect these two vertices
        public void AddEdge(int start, int end)
        {
            adjMatrix[start,end] = 1;
            adjMatrix[end,start] = 1;
        }


        //display vertex's title
        public void ShowVertex(String s, int v)
        {
            Console.Write(s + list[v].Title);
        }

        //save this vertex's title (marked as visited)
        public void SaveVertex(char label)
        {
            vertex[nVert++] = label;
        }

        //display all vertices
        public void PrintVertices() 
        {
            Console.Write("\nVertices visited: ");
            for(int i = 0; i < nVert; i++)
               Console.Write(vertex[i]);
            Console.WriteLine();
        }

        //display contents of queue during operations
        public void Content() 
        {
            //display contents of a in reverse order
            for(int j = keep.Count-1; j >= 0; j--) {
                Console.Write(list[keep.ElementAt<int>(j)].Title);
            }
            Console.WriteLine();
        }

        /*breadth-first search to keep track of events and contents of queue
        during visitation operations*/
        public void BFS() 
        {
            int v2;
            Console.WriteLine("Event\t\t\tQueue");
            list[0].Visited = true;
            ShowVertex("  Visit: ", 0);
            Console.WriteLine("");
            keep.Insert(0,0);
            SaveVertex(list[0].Title);
            while(keep.Count!=0) 
            {
                //remove vertex
                int index = keep[keep.Count-1];
                keep.RemoveAt(keep.Count-1);
                int v1 = index;
                //not printing the first vertex
                if(v1 != 0) {
                    ShowVertex("Removed: ", v1);
                    Console.Write("\t\t");
                    Content();
                }
                //until unvisited neighbors are gone
                while((v2 = GetAdjUnvisitedVertex(v1)) != -1)
                {
                    list[v2].Visited = true;
                    ShowVertex("  Visit: ", v2);
                    keep.Insert(0,v2);
                    Console.Write("\t\t");
                    Content();
                    SaveVertex(list[v2].Title);
                }
            }
            //print all vertices visited
            PrintVertices();
            //set all vertices back to original
            for(int j = 0; j < numVertices; j++)
            {
                list[j].Visited = false;
            }
        }

        /*return either index of unvisited connected vertcies
        from v to i, or -1 if the vertex has been visited*/
        private int GetAdjUnvisitedVertex(int v)
        {
            for (int i = 0; i < numVertices; i++)
                if (adjMatrix[v,i] == 1 && !list[i].Visited)
                    return i;
            return -1;
        }
    }
}

 

using System;

namespace BreadthFirstSearch
{
    class Program
    {
        static void Main(string[] args)
        {
            Graph g = new Graph();

            //create vertices
            g.AddVertex('A');    //0
            g.AddVertex('B');    //1
            g.AddVertex('C');    //2
            g.AddVertex('D');    //3
            g.AddVertex('E');    //4
            g.AddVertex('F');    //5
            g.AddVertex('G');    //6
            g.AddVertex('H');    //7
            g.AddVertex('I');    //8

            //connect vertices
            g.AddEdge(0, 1); //AB
            g.AddEdge(0, 2); //AC
            g.AddEdge(0, 8); //AI
            g.AddEdge(1, 5); //BF
            g.AddEdge(2, 3); //BF
            g.AddEdge(2, 4); //CE
            g.AddEdge(3, 5); //DF
            g.AddEdge(3, 6); //DG
            g.AddEdge(3, 7); //DH
            g.AddEdge(3, 8); //DI
            g.AddEdge(5, 7); //FH
            g.AddEdge(6, 8); //GI

            g.BFS();    //visit with BFS.
            Console.ReadLine();
        }
    }
}

You will see that class Vertex will has the label for be assigned the name of node, and visit for keeping track that this vertex is visited whelter or not.

And, class Graph uses adjMatrix to keep the connection between the nodes in the array and uses another array called vertex to keep track the answer [visited nodes].

The sequence of each node removed from the queue is the answer.

You can follow the program step by step by using hand calculation, you can understand more easier.

Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (2) | RSS


Very interesting seminars in Thailand

I've received newsletter about .NET two interesting seminars that you shouldn't miss.

It'll be held on 29th and 30th August 2009 at All Seasons Place, 87/2 Wireless Road, Phatumwan, Bangkok 10330.

I'll post some information and links here. Hope to see you there!

(Aug 29, 2009) Narisa Tech Talk 7.08.29 : http://www.narisa.com/forums/index.php?showtopic=28607

Talk name : Software Development in Practice
Venue : Auditorium I , 38th Fl. CRC Tower, All Seasons Place, 87/2 Wireless Road, Phatumwan, Bangkok 10330
Map : " http://support.microsoft.com/gp/contactusmsft/th "

Agenda
9:00 - 9:30 Updated News from Narisa.com
9:30 - 10:30 Agile software development in practice By นายข้าวโพดหวาน
10:30 - 11:00 Break & Networking & Introduce yourself
11:00 - 12:00 Test Driven Development Domain Driven Development โดยใช้ C# กับ .NET by Roofimon
12:00- 13:00 Lunch Break & Networking
13:00 - 14:30 DemoFest ( details can be found from this ... ) expect to have 5 demos
14:30 - 15:30 Workshop : Theory of constraint by pphetra
15:30 - 16:15 Seam : By xcaleber
16:15 - 17:00 Build and run many LOB applications on a single platform with xRM by fuju

(Aug 30, 2009) VTALK #12 from GreatFriends.biz : http://greatfriends.biz/community/events/vtalks.aspx

GreatFriends VTALKS sessions: 

  • 08:30 - 09:30 Register & Community Talks
  • 09:30 - 10:30
    Session 1 - Silverlight 3.0 for Business Applications
    By MVP Nuchit Atjanawat (nano) 
    GreatFriends.Biz Community Lead
  • 10:30 - 11:00 Break
  • 11:00 - 12:00
    Session 2 - ASP.NET 4.0 and Visual Studio 2010
    Web Development Beta 1 Overview
     
    By MVP Chalermpon Areepong (nine) 
    GreatFriends.Biz Community Lead
  • 12:00 - 13:00 Lunch (free)
  • 13:00 - 14:00
    Session 3 - Developing Applications for Windows 7 
    By MVP Suthep Sangvirotjanaphat (surrealist) 
    GreatFriends.Biz Founder and Community Lead
  • 14:00 - 14:30 Break
  • 14:30 - 15:30
    Session 4 - Explore Visual Studio 2010 Features 
    By MVP Chalermvong Vijitpiyakul (mie) 
    GreatFriends.Biz Community Lead
  • 15:30 - 16:45
    Session 5 - ASP.NET AJAX 4.0 & Introduction K2 blackpearl
    By MVP Banpote Ryan – GreatFriends.Biz
  • 16:45 - 17:00
    Wrap-Up

 

Categories:   News
Actions:   E-mail | del.icio.us | Permalink | Comments (1) | RSS


 

Tag Cloud