public override Output Execute(System.Drawing.Bitmap input, bool debug = false, bool showSteps = false)
        {
            Output             o   = new Output();
            RgbByteArrayBitmap rgb = RgbByteArrayBitmap.FromBitmap((input));

            //progressBarSetMax(100); // we are so fast, we dont need no progress bar(we are also not showing debug or steps)
            //int step = rgb.Length / 100;
            for (int i = 0; i < rgb.Length; i++)
            {
                rgb[i] = (byte)(255 - rgb[i]);
                //if ((i % step) == 0)
                //    progressBarStep();
            }
            o.Add(rgb.ToBitmap(), "negative");
            return(o);
        }
Beispiel #2
0
 /// <summary>
 /// Creates a new 3-channel instance copied its values from a provided byte bitmap
 /// </summary>
 public DoubleArrayBitmap(RgbByteArrayBitmap rgbBytes)
 {
     Width    = rgbBytes.Width;
     Height   = rgbBytes.Height;
     Channels = new double[3][];
     for (int j = 0; j < 3; j++)
     {
         Channels[j] = new double[Width * Height];
     }
     for (int i = 0; i < Width * Height; i++)
     {
         Channels[0][i] = rgbBytes[i * 3];
         Channels[1][i] = rgbBytes[i * 3 + 1];
         Channels[2][i] = rgbBytes[i * 3 + 2];
     }
 }
        /// <summary>
        /// Applies the algorithm to a bitmap
        /// </summary>
        public override Output Execute(Bitmap input, bool debug = false, bool showSteps = false)
        {
            // create an output object to later add output (and debug) images to
            Output o = new Output();
            // convert the input to an RgbByteArrayBitmap for faster processing
            RgbByteArrayBitmap rgb = RgbByteArrayBitmap.FromBitmap((input));

            // set preview image, anouncing that processing has begone
            setPreviewBitmap((Bitmap)input.Clone(), "processing..", Color.Red);

            // calculate the number of radii later tested and their relative spacing(f)
            int    min_r = 10;
            int    radii = Math.Min(Math.Min(rgb.Width, rgb.Height) / 4, 40);
            double f     = Math.Max(1, (Math.Min(rgb.Width, rgb.Height) / 6.0 - min_r) / radii);

            // set progressbar maximum
            progressBarSetMax(radii * 2);

            // create a dilation kernel
            Kernel max = new KernelMax();

            max.AddCircleElements(2.5, 1);

            // create a median kernel
            Kernel median = new KernelMedian();

            median.IgnoreBorders = false;
            median.AddCircleElements(1.5, 1);

            // create a simple averaging kernel
            Kernel smooth = new Kernel();

            smooth.AddCircleElements(1.5, 1);
            smooth.IgnoreBorders = false;

            // create a x-derivative kernel
            Kernel dx = new Kernel();

            dx.AddElement(-1, 0, 1);
            dx.AddElement(1, 0, -1);

            // create a y-derivative kernel
            Kernel dy = new Kernel();

            dy.AddElement(0, -1, 1);
            dy.AddElement(0, 1, -1);

            // set up some variables
            DoubleArrayBitmap result;
            DoubleArrayBitmap resultDx;
            DoubleArrayBitmap resultDy;

            // convert byte array bitmap to hsv
            result = rgb.ToHSV();

            // extract brightness channel
            DoubleArrayBitmap brightness = result.ExtractChannel(2);

            // if preview is enabled, show the brightness channel
            if (showSteps)
            {
                setPreviewBitmap(brightness.ToBytes().ToBitmap(), "grey values", Color.Red);
            }

            // taking median and smoothing brightness to reduce noise
            brightness = median.ApplyToBitmap(brightness);
            brightness = smooth.ApplyToBitmap(brightness);

            // show progress
            if (showSteps)
            {
                setPreviewBitmap(brightness.ToBytes().ToBitmap(), "reduced noise", Color.Red);
            }

            // add brightness image to debug output
            if (debug)
            {
                o.Add(brightness.ToBytes().ToBitmap(), "debug: brightness");
            }

            // calculate derivatives
            resultDx = dx.ApplyToBitmap(brightness);
            resultDy = dy.ApplyToBitmap(brightness);

            // square derivatives
            resultDx.SquareMe();
            resultDy.SquareMe();

            // calculate gradient
            DoubleArrayBitmap gradient = resultDx + resultDy;

            gradient.SqrtMe();

            // show progress
            if (showSteps)
            {
                setPreviewBitmap(gradient.ToBytes().ToBitmap(), "gradient");
            }

            // dilate gradient slightly, improves detection
            gradient = max.ApplyToBitmap(gradient);

            // show progress
            if (showSteps)
            {
                setPreviewBitmap(gradient.ToBytes().ToBitmap(), "dilated gradient");
            }

            // add gradient to debug output
            if (debug)
            {
                o.Add(gradient.ToBytes().ToBitmap(), "debug: gradient");
            }

            // reduce size of gradient image, making detection faster by a factor of 4
            gradient = gradient.ShrinkByTwo();


            // initializing array to hold detected features for different radii
            DoubleArrayBitmap[] steps = new DoubleArrayBitmap[radii];

            // detect features for the different radii
            for (int r = 0; r < radii; r++)
            {
                // update progressbar
                progressBarStep();
                // create 'special' 24-point clockface kernel(see makeClockFaceKernel-method)
                Kernel clockface = makeClockFaceKernel(min_r + (int)(r * f));
                // apply the kernel to detect features
                steps[r] = clockface.ApplyToBitmap(gradient);
                // square the resulting image to make both negative and positive features stand out
                steps[r].SquareMe();

                // show progress
                if (showSteps)
                {
                    setPreviewBitmap(steps[r].ExpandByTwo().ToBytes().ToBitmap(), "pattern detection " + r + "/" + radii);
                }
            }

            // find maximum over all features in xyr-space
            double maximum = 0;

            for (int r = 0; r < radii; r++)
            {
                for (int i = 0; i < gradient.Width * gradient.Height; i++)
                {
                    if (steps[r][0, i] > maximum)
                    {
                        maximum = steps[r][0, i];
                    }
                }
            }

            // initialize another array of bitmaps to hold the final smoothed features
            DoubleArrayBitmap[] steps_final = new DoubleArrayBitmap[radii];

            // create a new bitmap to sum up all steps_final for debug output
            if (debug)
            {
                result = new DoubleArrayBitmap(gradient.Width, gradient.Height);
            }

            // initialize 3d-array to hold thresholded object information extracted from detected features
            bool[,,] xyrSpace = new bool[gradient.Width, gradient.Height, radii];

            // for all radii...
            for (int r = 0; r < radii; r++)
            {
                int a = Math.Max(r - 2, 0);
                int b = Math.Min(r + 2, radii);
                steps_final[r] = new DoubleArrayBitmap(gradient.Width, gradient.Height);

                // sum up detected features over up to 5 r-layers ( smoothes features )
                for (int x = 0; x < gradient.Width * gradient.Height; x++)
                {
                    for (int i = a; i < b; i++)
                    {
                        steps_final[r][0, x] += steps[i][0, x];
                    }
                }

                // theshold r-layer if needed for preview/debug-output
                if (showSteps || debug)
                {
                    steps_final[r].ThresholdMe(maximum);
                }

                // show smoothed and thresholded preview of r-layer
                if (showSteps)
                {
                    setPreviewBitmap(steps_final[r].ExpandByTwo().ToBytes().ToBitmap(), "threshold " + r + "/" + radii);
                }

                // threshold r-layer and store in xyrSpace as boolean
                for (int x = 0; x < gradient.Width; x++)
                {
                    for (int y = 0; y < gradient.Height; y++)
                    {
                        xyrSpace[x, y, r] = steps_final[r][0, x, y] > maximum;
                    }
                }

                // add thresholded r-layer to debug output
                if (debug)
                {
                    result += steps_final[r];
                    o.Add(steps_final[r].ExpandByTwo().ToBytes().ToBitmap(), "debug: radius " + (min_r + (int)(r * f)).ToString());
                }
                // update progress bar
                progressBarStep();
            }
            // debug output sum of thresholded r-layers
            if (debug)
            {
                o.Add(result.ExpandByTwo().ToBytes().ToBitmap(), "debug: sum(all radii)"); // debug
            }
            // boolean-xyrSpace is filled now!

            // initialzed a list of detected clock faces
            ClockFaceList clList = new ClockFaceList();

            //clList.AllowOverlap = true; // enabling this will detect many smaller features that are not really clock faces

            // search through xyrSpace from large to small radius
            for (int r = radii - 1; r >= 0; r--)
            {
                for (int x = 0; x < gradient.Width; x++)
                {
                    for (int y = 0; y < gradient.Height; y++)
                    {
                        if (xyrSpace[x, y, r])
                        {
                            // if object is found, extract and add to list
                            int[] center = extractObject(ref xyrSpace, x, y, r);
                            // perform some funny computations to offset previous resolution change and take into acount radius factor f
                            clList.Add(center[0] * 2, center[1] * 2, (int)((min_r + center[2] * f) * 2 * 1.1));
                        }
                    }
                }
            }


            // create final output bitmaps
            // "output" shows all detected clock faces in different gray values on black
            // "mask" shows the original image masked with the detected clock faces, leaving everything else black
            Bitmap   output = new Bitmap(input.Width, input.Height);
            Graphics g      = Graphics.FromImage(output);
            Bitmap   mask   = new Bitmap(input);
            Graphics g_m    = Graphics.FromImage(mask);

            g.Clear(Color.Black);
            g_m.Clear(Color.Black);
            int clId = clList.ClockFaces.Count;

            foreach (ClockFace cl in clList.ClockFaces)
            {
                int   c = clId * 255 / clList.ClockFaces.Count;
                Brush b = new SolidBrush(Color.FromArgb(c, c, c));
                Point p = new Point(cl.X - cl.R, cl.Y - cl.R);
                Size  s = new Size(cl.R * 2, cl.R * 2);
                g.FillEllipse(b, new Rectangle(p, s));
                b = new SolidBrush(Color.White);
                g_m.FillEllipse(b, new Rectangle(p, s));
                clId--;
            }

            // add final outputs to output list
            o.Add(output, "detected clock faces");
            // also perform bitwise AND operation with input bitmap here to get masked image
            o.Add((RgbByteArrayBitmap.FromBitmap(mask) & rgb).ToBitmap(), "masked input");

            return(o);
        }