Saturday, 3 December 2016

Parallell execution with threads in C# - Old Stars finder

This article will present parallell execution of threads in C# to find old stars in a star formation known as W5 in the constellation of Cassiopeia with the Spitzer Space telescope. The code is from the book "C# Multithreaded and Parallell programming" by Packt Publishing by author Rodney Ringler et. al.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Imaging;


namespace OldStarsFinder
{
    public partial class Form1 : Form
    {

        //The number of processors or cores available in the computer for this application
        private int priProcessorCount = Environment.ProcessorCount;
        //The bitmaps list
        private List<Bitmap> prloBitmapList;
        //The long list with the old stars count 
        private List<long> prliOldStarsCount;
        //The threads list
        private List<Thread> prloThreadList;
        //The original huge infrared bitmap portrait
        Bitmap proOriginalBitmap; 

        public Form1()
        {
            InitializeComponent();
        }

        public bool IsOldStar(Color poPixelColor)
        {
            //Hue between 150 and 258
            //Saturation more than 0.10 
            //Brightness more than 0.90
            return ((poPixelColor.GetHue() >= 150 && (poPixelColor.GetHue() <= 258)) &&
                (poPixelColor.GetSaturation() >= 0.10) &&
                (poPixelColor.GetBrightness() >= 0.90)); 
        }

        private Bitmap CropBitmap(Bitmap proBitmap, Rectangle proRectangle)
        {
            //Create a new bitmap copying the portion of the original defined by proRectangle and keeping the PixelFormat 
            var loCroppedBitmap = proBitmap.Clone(proRectangle, proBitmap.PixelFormat);
            //Return the cropped bitmap 
            return loCroppedBitmap;
        }

        private void ThreadOldStarsFinder(object poThreadParameter)
        {
            //Retrieve the thread number reeived in object poThreadParameter 
            int liThreadNumber = (int) poThreadParameter;
            //The pixel matrix (bitmap) row number (Y)
            int liRow;
            //The pixel matrix (bitmap col number (X)
            int liCol;
            //The pixel color 
            Color loPixelColor;
            //Get my bitmap part from the bitmap list 
            Bitmap loBitmap = prloBitmapList[liThreadNumber];

            //Reset my old stars counter 
            prliOldStarsCount[liThreadNumber] = 0;
            //Iterate through each pixel matrix (bitmap) row 
            for (liRow = 0; liRow < loBitmap.Height; liRow++)
            {
                //Iterate through each pixel matrix (bitmap) cols 
                for(liCol = 0; liCol < loBitmap.Width; liCol++)
                {
                    //Get the pixel color for liCol and liRow 
                    loPixelColor = loBitmap.GetPixel(liCol, liRow);
                    //Get the pixel color for liCol and liRow 
                    if (IsOldStar(loPixelColor))
                    {
                        //The color range correspons to an old star
                        //Change its color to a pure blue 
                        loBitmap.SetPixel(liCol, liRow, Color.Blue);
                        //Increase the old stars counter 
                        prliOldStarsCount[liThreadNumber]++;
                    }
                    else
                    {
                        loBitmap.SetPixel(liCol, liRow, Color.FromArgb(128, loPixelColor));
                    }
                }
            }
            //Simulate heavy processing
            Random rnd = new Random();
            Thread.Sleep(rnd.Next(2000, 2500)); 
        }

        private void WaitForThreadsToDie()
        {
            //A bool flag 
            bool lbContinue = true;
            int liDeadThreads = 0;
            int liThreadNumber;
            while (lbContinue)
            {
                for(liThreadNumber = 0; liThreadNumber < priProcessorCount; liThreadNumber++)
                {
                    if (prloThreadList[liThreadNumber].IsAlive)
                    {
                        //One of the threads is still alive
                        //exit the for loop and sleep 100 milliseconds 
                        break;
                    }
                    else
                    {
                        //Increase the dead threads count 
                        liDeadThreads++;

                        progressBar1.Value = (int) ((liDeadThreads * 1.0 / priProcessorCount * 1.0) * 100.0);
                    }
                }

                if (liDeadThreads == priProcessorCount)
                {
                    //All the threads are dead, exit the while loop 
                    break; 
                }
                Thread.Sleep(100);
                liDeadThreads = 0; 
            }
        }

        private void ShowBitmapWithOldStars()
        {
            int liThreadNumber;
            //Each bitmap portion 
            Bitmap loBitmap;
            //The starting row in each iteration 
            int liStartRow = 0;

            //Calculate each bitmap's height 
            int liEachBitmapHeight = ((int) (proOriginalBitmap.Height / priProcessorCount)) + 1;

            //Create a new bitmap with the whole width and height 
            loBitmap = new Bitmap(proOriginalBitmap.Width, proOriginalBitmap.Height);
            Graphics g = Graphics.FromImage((Image) loBitmap);
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

            for (liThreadNumber = 0; liThreadNumber < priProcessorCount; liThreadNumber++)
            {
                //Draw each portion in its corresponding absolute starting row 
                g.DrawImage(prloBitmapList[liThreadNumber], 0, liStartRow);
                //Increase the starting row 
                liStartRow += liEachBitmapHeight;        
            }

            //Show the bitmap in the PictureBox picStarsBitmap 
            picStarsBitmap.Image = loBitmap;

            g.Dispose(); 
        }

        private void butFindOldStars_Click(object sender, EventArgs e)
        {
            progressBar1.Visible = true;

            proOriginalBitmap = new Bitmap(pictureBox1.Image);

            //Thread number 
            int liThreadNumber;
            //Create the thread list, the long list and the bitmap list 
            prloThreadList = new List<Thread>(priProcessorCount);
            prliOldStarsCount = new List<long>(priProcessorCount);
            prloBitmapList = new List<Bitmap>(priProcessorCount);

            int liStartRow = 0;

            int liEachBitmapHeight = ((int) (proOriginalBitmap.Height / priProcessorCount)) + 1;

            int liHeightToAdd = proOriginalBitmap.Height;
            Bitmap loBitmap; 

            //Initialize the threads 

            for (liThreadNumber = 0; liThreadNumber < priProcessorCount; liThreadNumber++)
            {
                //Just to occupy the number 
                prliOldStarsCount.Add(0); 

                if (liEachBitmapHeight > liHeightToAdd)
                {
                    //The last bitmap height perhaps is less than the other bitmap height
                    liEachBitmapHeight = liHeightToAdd; 
                }

                loBitmap = CropBitmap(proOriginalBitmap, new Rectangle(0, liStartRow, proOriginalBitmap.Width, liEachBitmapHeight));
                liHeightToAdd -= liEachBitmapHeight;
                liStartRow += liEachBitmapHeight;
                prloBitmapList.Add(loBitmap);

  

                //Add the new thread, with a parameterized start (to allow parameters)
                prloThreadList.Add(new Thread(new ParameterizedThreadStart(ThreadOldStarsFinder))); 
            }

            //Now, start the threads
            for (liThreadNumber = 0; liThreadNumber < priProcessorCount; liThreadNumber++)
            {
                prloThreadList[liThreadNumber].Start(liThreadNumber); 
            }

            WaitForThreadsToDie();

            ShowBitmapWithOldStars();

            progressBar1.Visible = false;
        }

    }
}

The code is available as a Windows Forms application in a Visual 2015 Solution available for download (zip) below:
Old Stars Finder (.zip) W5 image (NASA website): W5 image