/// <summary>
        /// Gets pixels from image. Used unsafe context and parallel.for for best performance.
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static IEnumerable <Pixel> GetPixels(this Bitmap bitmap)
        {
            Pixel[] result = null;
            unsafe
            {
                BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);

                int   bytesPerPixel  = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
                int   heightInPixels = bitmapData.Height;
                int   widthInBytes   = bitmapData.Width * bytesPerPixel;
                int   widthInPixels  = bitmapData.Width;
                byte *ptrFirstPixel  = (byte *)bitmapData.Scan0;
                result = new Pixel[heightInPixels * widthInPixels];

                Parallel.For(0, heightInPixels, new ParallelOptions {
                    MaxDegreeOfParallelism = MedicalDiamondSearchSettings.NumberOfThreads / 2
                }, y =>
                {
                    byte *currentLine = ptrFirstPixel + (y * bitmapData.Stride);
                    for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
                    {
                        lock (result)
                        {
                            result[y * widthInPixels + x / bytesPerPixel] = new Pixel(new Point(x / bytesPerPixel, y), Color.FromArgb(currentLine[x + 2], currentLine[x + 1], currentLine[x]));
                        }
                    }
                });
                bitmap.UnlockBits(bitmapData);
            }
            return(result);
        }
Example #2
0
        /// <summary>
        ///     Process the filter on the specified image.
        /// </summary>
        /// <param name="image">Source image data.</param>
        /// <param name="rect">Image rectangle for processing by the filter.</param>
        protected override unsafe void ProcessFilter(UnmanagedImage image, Rectangle rect)
        {
            var num1   = Image.GetPixelFormatSize(image.PixelFormat) / 8;
            var left   = rect.Left;
            var top    = rect.Top;
            var num2   = left + rect.Width;
            var num3   = top + rect.Height;
            var num4   = image.Stride - rect.Width * num1;
            var numPtr = (byte *)((IntPtr)image.ImageData.ToPointer() + (top * image.Stride + left * num1));

            if (image.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                for (var index = top; index < num3; ++index)
                {
                    var num5 = left;
                    while (num5 < num2)
                    {
                        *numPtr = _mapGreen[*numPtr];
                        ++num5;
                        ++numPtr;
                    }
                    numPtr += num4;
                }
            }
            else
            {
                for (var index = top; index < num3; ++index)
                {
                    var num5 = left;
                    while (num5 < num2)
                    {
                        numPtr[2] = _mapRed[numPtr[2]];
                        numPtr[1] = _mapGreen[numPtr[1]];
                        *numPtr = _mapBlue[*numPtr];
                        ++num5;
                        numPtr += num1;
                    }
                    numPtr += num4;
                }
            }
        }
Example #3
0
        private bool IsWhiteRectangle(Bitmap image, IReadOnlyList <IntPoint> rectanglePoints)
        {
            BitmapData data =
                image.LockBits(
                    new Rectangle(rectanglePoints[0].X, rectanglePoints[0].Y,
                                  rectanglePoints[1].X - rectanglePoints[0].X,
                                  rectanglePoints[3].Y - rectanglePoints[0].Y),
                    ImageLockMode.ReadWrite, image.PixelFormat);

            var bytesPerPixel    = Image.GetPixelFormatSize(image.PixelFormat) / 8;
            var isWhiteRectangle = true;

            unsafe
            {
                //Nr. de pixeli pe linie * bytesPerPixel
                int   width      = (data.Width) * bytesPerPixel;
                byte *firstPixel = (byte *)data.Scan0;
                Parallel.For(0, data.Height + 1, (y, state) =>
                {
                    byte *currentLine = firstPixel + y * data.Stride;
                    for (int x = 0; x < width; x += bytesPerPixel)
                    {
                        currentLine[x]     = 0;
                        currentLine[x + 1] = 0;
                        currentLine[x + 2] = 255;
                        if (currentLine[x] != 255 && currentLine[x + 1] != 255 && currentLine[x + 2] != 255)
                        {
                            isWhiteRectangle = false;
                            state.Break();
                        }
                    }
                });
            }
            image.UnlockBits(data);
            return(isWhiteRectangle);
        }
        /// <summary>
        /// Process video and motion frames doing further post processing after
        ///   performed motion detection.
        /// </summary>
        /// <param name="videoFrame">
        /// Original video frame.
        /// </param>
        /// <param name="motionFrame">
        /// Motion frame provided by motion detection
        ///   algorithm (see <see cref="IMotionDetector"/>).
        /// </param>
        /// <remarks>
        /// <para>
        /// Processes provided motion frame and highlights motion areas
        ///     on the original video frame with <see cref="HighlightColor">specified color</see>.
        ///   </para>
        /// </remarks>
        /// <exception cref="InvalidImagePropertiesException">
        /// Motion frame is not 8 bpp image, but it must be so.
        /// </exception>
        /// <exception cref="UnsupportedImageFormatException">
        /// Video frame must be 8 bpp grayscale image or 24/32 bpp color image.
        /// </exception>
        public unsafe void ProcessFrame(UnmanagedImage videoFrame, UnmanagedImage motionFrame)
        {
            if (motionFrame.PixelFormat != PixelFormat.Format8bppIndexed)
            {
                throw new InvalidImagePropertiesException("Motion frame must be 8 bpp image.");
            }

            if ((videoFrame.PixelFormat != PixelFormat.Format8bppIndexed) &&
                (videoFrame.PixelFormat != PixelFormat.Format24bppRgb) &&
                (videoFrame.PixelFormat != PixelFormat.Format32bppRgb) &&
                (videoFrame.PixelFormat != PixelFormat.Format32bppArgb))
            {
                throw new UnsupportedImageFormatException("Video frame must be 8 bpp grayscale image or 24/32 bpp color image.");
            }

            int width     = videoFrame.Width;
            int height    = videoFrame.Height;
            int pixelSize = Image.GetPixelFormatSize(videoFrame.PixelFormat) / 8;

            if ((motionFrame.Width != width) || (motionFrame.Height != height))
            {
                return;
            }

            var src    = (byte *)videoFrame.ImageData.ToPointer();
            var motion = (byte *)motionFrame.ImageData.ToPointer();

            int srcOffset    = videoFrame.Stride - width * pixelSize;
            int motionOffset = motionFrame.Stride - width;

            if (pixelSize == 1)
            {
                // grayscale case
                var fillG =
                    (byte)(0.2125 * this.highlightColor.R + 0.7154 * this.highlightColor.G + 0.0721 * this.highlightColor.B);

                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++, motion++, src++)
                    {
                        if (*motion != 0)
                        {
                            // && (((x + y) & 1) == 0))
                            *src = fillG;
                        }
                    }

                    src    += srcOffset;
                    motion += motionOffset;
                }
            }
            else
            {
                // color case
                byte fillR = this.highlightColor.R;
                byte fillG = this.highlightColor.G;
                byte fillB = this.highlightColor.B;

                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++, motion++, src += pixelSize)
                    {
                        if (*motion != 0)
                        {
                            // && (((x + y) & 1) == 0))
                            src[RGB.R] = fillR;
                            src[RGB.G] = fillG;
                            src[RGB.B] = fillB;
                        }
                        else
                        {
                            src[RGB.R] = 0;
                            src[RGB.G] = 0;
                            src[RGB.B] = 0;
                        }
                    }

                    src    += srcOffset;
                    motion += motionOffset;
                }
            }
        }
Example #5
0
        public static RadialBitmap ConvertToRadial(Bitmap bmp)
        {
            var destImg = new Bitmap((bmp.Height + bmp.Width - 2) * 2,
                                     (int)Math.Ceiling(Math.Sqrt(bmp.Height * bmp.Height + bmp.Width * bmp.Width) / 2));

            var rect  = new Rectangle(0, 0, destImg.Width, destImg.Height);
            var data  = destImg.LockBits(rect, ImageLockMode.ReadWrite, destImg.PixelFormat);
            var depth = Image.GetPixelFormatSize(data.PixelFormat) / 8; //bytes per pixel

            var buffer = new byte[data.Width * data.Height * depth];

            Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);

            var centerPoint = new Vector(bmp.Width / 2.0, bmp.Height / 2.0);

            for (var x = 0; x < destImg.Width; x++)
            {
                var directionVector = DirectionVector(x, bmp, centerPoint);

                for (var y = 0; y < destImg.Height; y++)
                {
                    var point = centerPoint + directionVector * (y + 1);
                    var c     = InterperlatePointFromImg(bmp, point);
                    if (depth == 4)
                    {
                        try
                        {
                            var offset = (y * destImg.Width + x) * (depth);
                            buffer[offset]     = c.B;
                            buffer[offset + 1] = c.G;
                            buffer[offset + 2] = c.R;
                            buffer[offset + 3] = c.A;
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show("X = " + x + " Width = " + destImg.Width + "\nY = " + y +
                                            " Hight = " +
                                            destImg.Height + "\n" + e.Message);
                        }
                    }
                    else if (depth == 3)
                    {
                        try
                        {
                            var offset = (y * destImg.Width + x) * (depth - 1);
                            buffer[offset]     = c.R;
                            buffer[offset + 1] = c.G;
                            buffer[offset + 2] = c.B;
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show("X = " + x + " Width = " + destImg.Width + "\nY = " + y +
                                            " Hight = " +
                                            destImg.Height + "\n" + e.Message);
                        }
                    }
                }
            }

            Marshal.Copy(buffer, 0, data.Scan0, buffer.Length);

            destImg.UnlockBits(data);

            return(new RadialBitmap(destImg, bmp.Width, bmp.Height));
        }
        /// <summary>
        ///     Process the filter on the specified image.
        /// </summary>
        /// <param name="source">Source image data.</param>
        /// <param name="destination">Destination image data.</param>
        /// <param name="rect">Image rectangle for processing by the filter.</param>
        protected override unsafe void ProcessFilter(UnmanagedImage source, UnmanagedImage destination, Rectangle rect)
        {
            var index1  = Image.GetPixelFormatSize(source.PixelFormat) / 8;
            var index2  = index1 * 2;
            var left    = rect.Left;
            var top     = rect.Top;
            var num1    = left + rect.Width;
            var num2    = top + rect.Height;
            var num3    = left + 2;
            var num4    = top + 2;
            var num5    = num1 - 2;
            var num6    = num2 - 2;
            var stride1 = source.Stride;
            var stride2 = destination.Stride;
            var num7    = stride1 - rect.Width * index1;
            var num8    = stride2 - rect.Width * index1;
            var num9    = -8.0 * Factor * Factor;
            var numPtr1 = (byte *)((IntPtr)source.ImageData.ToPointer() + stride1 * 2);
            var numPtr2 = (byte *)((IntPtr)destination.ImageData.ToPointer() + stride2 * 2);
            var numPtr3 = numPtr1 + (top * stride1 + left * index1);
            var numPtr4 = numPtr2 + (top * stride2 + left * index1);

            for (var index3 = num4; index3 < num6; ++index3)
            {
                var numPtr5 = numPtr3 + index2;
                var numPtr6 = numPtr4 + index2;
                for (var index4 = num3; index4 < num5; ++index4)
                {
                    var num10 = 0;
                    while (num10 < index1)
                    {
                        var    num11   = 0.0;
                        var    num12   = 0.0;
                        double num13   = numPtr5[-stride1] - numPtr5[-index2 - stride1];
                        double num14   = numPtr5[-index1] - numPtr5[-index1 - 2 * stride1];
                        var    num15   = Math.Exp((num13 * num13 + num14 * num14) / num9);
                        var    num16   = num12 + num15 * numPtr5[-index1 - stride1];
                        var    num17   = num11 + num15;
                        double num18   = numPtr5[index1 - stride1] - numPtr5[-index1 - stride1];
                        double num19   = *numPtr5 - numPtr5[-2 * stride1];
                        var    num20   = Math.Exp((num18 * num18 + num19 * num19) / num9);
                        var    num21   = num16 + num20 * numPtr5[-stride1];
                        var    num22   = num17 + num20;
                        double num23   = numPtr5[index2 - stride1] - numPtr5[-stride1];
                        double num24   = numPtr5[index1] - numPtr5[index1 - 2 * stride1];
                        var    num25   = Math.Exp((num23 * num23 + num24 * num24) / num9);
                        var    num26   = num21 + num25 * numPtr5[index1 - stride1];
                        var    num27   = num22 + num25;
                        double num28   = *numPtr5 - numPtr5[-index2];
                        double num29   = numPtr5[-index1 + stride1] - numPtr5[-index1 - stride1];
                        var    num30   = Math.Exp((num28 * num28 + num29 * num29) / num9);
                        var    num31   = num26 + num30 * numPtr5[-index1];
                        var    num32   = num27 + num30;
                        double num33   = numPtr5[index1] - numPtr5[-index1];
                        double num34   = numPtr5[stride1] - numPtr5[-stride1];
                        var    num35   = Math.Exp((num33 * num33 + num34 * num34) / num9);
                        var    num36   = num31 + num35 * *numPtr5;
                        var    num37   = num32 + num35;
                        double num38   = numPtr5[index2] - *numPtr5;
                        double num39   = numPtr5[index1 + stride1] - numPtr5[index1 - stride1];
                        var    num40   = Math.Exp((num38 * num38 + num39 * num39) / num9);
                        var    num41   = num36 + num40 * numPtr5[index1];
                        var    num42   = num37 + num40;
                        double num43   = numPtr5[stride1] - numPtr5[-index2 + stride1];
                        double num44   = numPtr5[-index1 + 2 * stride1] - numPtr5[-index1];
                        var    num45   = Math.Exp((num43 * num43 + num44 * num44) / num9);
                        var    num46   = num41 + num45 * numPtr5[-index1 + stride1];
                        var    num47   = num42 + num45;
                        double num48   = numPtr5[index1 + stride1] - numPtr5[-index1 + stride1];
                        double num49   = numPtr5[2 * stride1] - *numPtr5;
                        var    num50   = Math.Exp((num48 * num48 + num49 * num49) / num9);
                        var    num51   = num46 + num50 * numPtr5[stride1];
                        var    num52   = num47 + num50;
                        double num53   = numPtr5[index2 + stride1] - numPtr5[stride1];
                        double num54   = numPtr5[index1 + 2 * stride1] - numPtr5[index1];
                        var    num55   = Math.Exp((num53 * num53 + num54 * num54) / num9);
                        var    num56   = num51 + num55 * numPtr5[index1 + stride1];
                        var    num57   = num52 + num55;
                        *      numPtr6 = num57 == 0.0 ? *numPtr5 : (byte)Math.Min(num56 / num57, byte.MaxValue);
                        ++num10;
                        ++numPtr5;
                        ++numPtr6;
                    }
                }
                numPtr3 = numPtr5 + (num7 + index2);
                numPtr4 = numPtr6 + (num8 + index2);
            }
        }
Example #7
0
 public static int GetBytesPerPixel(this PixelFormat format) => Image.GetPixelFormatSize(format) / 8;
Example #8
0
        internal static void AdjustContrast(Bitmap bitmap, float value, bool invertColors = false, bool limeToWhite = false)
        {
            BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
            int        imageBytes = Image.GetPixelFormatSize(bitmapData.PixelFormat) / 8;

            unsafe
            {
                byte *rgb = (byte *)bitmapData.Scan0;

                for (int x = 0; x < bitmap.Width; x++)
                {
                    for (int y = 0; y < bitmap.Height; y++)
                    {
                        int  pos = (y * bitmapData.Stride) + (x * imageBytes);
                        byte b   = rgb[pos];
                        byte g   = rgb[pos + 1];
                        byte r   = rgb[pos + 2];

                        float red   = r / 255.0f;
                        float green = g / 255.0f;
                        float blue  = b / 255.0f;
                        red   = (((red - 0.5f) * value) + 0.5f) * 255.0f;
                        green = (((green - 0.5f) * value) + 0.5f) * 255.0f;
                        blue  = (((blue - 0.5f) * value) + 0.5f) * 255.0f;

                        int iR = (int)red;
                        iR = iR > 255 ? 255 : iR;
                        iR = iR < 0 ? 0 : iR;
                        int iG = (int)green;
                        iG = iG > 255 ? 255 : iG;
                        iG = iG < 0 ? 0 : iG;
                        int iB = (int)blue;
                        iB = iB > 255 ? 255 : iB;
                        iB = iB < 0 ? 0 : iB;

                        if (invertColors)
                        {
                            if (iB == 255 && iG == 255 && iR == 255)
                            {
                                iB = 0;
                                iG = 0;
                                iR = 0;
                            }
                            else
                            {
                                iB = 255;
                                iG = 255;
                                iR = 255;
                            }
                        }
                        if (limeToWhite && iG == 255 && iR == 255)
                        {
                            iB = 255;
                        }
                        rgb[pos]     = (byte)iB;
                        rgb[pos + 1] = (byte)iG;
                        rgb[pos + 2] = (byte)iR;
                    }
                }
            }

            bitmap.UnlockBits(bitmapData);
        }
Example #9
0
        private static Bitmap[] FetchConnectedComponentLabels(Bitmap image)
        {
            int[,] labels = LabelImage(image, out int labelCount);
            List <Bitmap> bitmaps = new List <Bitmap>();

            if (labelCount > 0)
            {
                Rectangle[] rects = new Rectangle[labelCount];

                for (int x = 0; x < image.Width; x++)
                {
                    for (int y = 0; y < image.Height; y++)
                    {
                        for (int i = 1; i < labelCount; i++)
                        {
                            if (labels[y, x] == i)
                            {
                                if (x < rects[i].X || rects[i].X == 0)
                                {
                                    rects[i].X = x;
                                }
                                if (y < rects[i].Y || rects[i].Y == 0)
                                {
                                    rects[i].Y = y;
                                }
                                if (x > rects[i].Width)
                                {
                                    rects[i].Width = x;
                                }
                                if (y > rects[i].Height)
                                {
                                    rects[i].Height = y;
                                }
                            }
                        }
                    }
                }

                for (int i = 1; i < labelCount; i++)
                {
                    int width  = rects[i].Width - rects[i].X + 1;
                    int height = rects[i].Height - rects[i].Y + 1;

                    if (height / (double)image.Height > 0.6)
                    {
                        bitmaps.Add(new Bitmap(width, height, image.PixelFormat));

                        BitmapData bitmapData = bitmaps[bitmaps.Count - 1].LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, image.PixelFormat);
                        int        imageBytes = Image.GetPixelFormatSize(bitmapData.PixelFormat) / 8;

                        unsafe
                        {
                            byte *rgb = (byte *)bitmapData.Scan0;

                            for (int x = 0; x < width; x++)
                            {
                                for (int y = 0; y < height; y++)
                                {
                                    int pos = (y * bitmapData.Stride) + (x * imageBytes);

                                    if (labels[y + rects[i].Y, x + rects[i].X] == i)
                                    {
                                        if (rgb != null)
                                        {
                                            rgb[pos]     = 255;
                                            rgb[pos + 1] = 255;
                                            rgb[pos + 2] = 255;
                                        }
                                    }
                                }
                            }
                            bitmaps[bitmaps.Count - 1].UnlockBits(bitmapData);
                        }
                    }
                }
            }

            return(bitmaps.ToArray());
        }
Example #10
0
        private static int[,] LabelImage(Bitmap image, out int labelCount)
        {
            BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat);
            int        nrow       = image.Height;
            int        ncol       = image.Width;

            int[,] img   = new int[nrow, ncol];
            int[,] label = new int[nrow, ncol];
            int imageBytes = Image.GetPixelFormatSize(bitmapData.PixelFormat) / 8;

            unsafe
            {
                byte *rgb = (byte *)bitmapData.Scan0;

                for (int x = 0; x < bitmapData.Width; x++)
                {
                    for (int y = 0; y < bitmapData.Height; y++)
                    {
                        int pos = (y * bitmapData.Stride) + (x * imageBytes);
                        if (rgb != null)
                        {
                            img[y, x]   = (rgb[pos] + rgb[pos + 1] + rgb[pos + 2]) / 3;
                            label[y, x] = 0;
                        }
                    }
                }
            }
            image.UnlockBits(bitmapData);

            int           lab   = 1;
            Stack <int[]> stack = new Stack <int[]>();

            try
            {
                for (int c = 0; c != ncol; c++)
                {
                    for (int r = 0; r != nrow; r++)
                    {
                        if (img[r, c] == 0 || label[r, c] != 0)
                        {
                            continue;
                        }

                        stack.Push(new[] { r, c });
                        label[r, c] = lab;

                        while (stack.Count != 0)
                        {
                            int[] pos = stack.Pop();
                            int   y   = pos[0];
                            int   x   = pos[1];

                            if (y > 0 && x > 0)
                            {
                                if (img[y - 1, x - 1] > 0 && label[y - 1, x - 1] == 0)
                                {
                                    stack.Push(new[] { y - 1, x - 1 });
                                    label[y - 1, x - 1] = lab;
                                }
                            }

                            if (y > 0)
                            {
                                if (img[y - 1, x] > 0 && label[y - 1, x] == 0)
                                {
                                    stack.Push(new[] { y - 1, x });
                                    label[y - 1, x] = lab;
                                }
                            }

                            if (y > 0 && x < ncol - 1)
                            {
                                if (img[y - 1, x + 1] > 0 && label[y - 1, x + 1] == 0)
                                {
                                    stack.Push(new[] { y - 1, x + 1 });
                                    label[y - 1, x + 1] = lab;
                                }
                            }

                            if (x > 0)
                            {
                                if (img[y, x - 1] > 0 && label[y, x - 1] == 0)
                                {
                                    stack.Push(new[] { y, x - 1 });
                                    label[y, x - 1] = lab;
                                }
                            }

                            if (x < ncol - 1)
                            {
                                if (img[y, x + 1] > 0 && label[y, x + 1] == 0)
                                {
                                    stack.Push(new[] { y, x + 1 });
                                    label[y, x + 1] = lab;
                                }
                            }

                            if (y < nrow - 1 && x > 0)
                            {
                                if (img[y + 1, x - 1] > 0 && label[y + 1, x - 1] == 0)
                                {
                                    stack.Push(new[] { y + 1, x - 1 });
                                    label[y + 1, x - 1] = lab;
                                }
                            }

                            if (y < nrow - 1)
                            {
                                if (img[y + 1, x] > 0 && label[y + 1, x] == 0)
                                {
                                    stack.Push(new[] { y + 1, x });
                                    label[y + 1, x] = lab;
                                }
                            }

                            if (y < nrow - 1 && x < ncol - 1)
                            {
                                if (x + 1 == 21 && y + 1 == 15)
                                {
                                }
                                if (img[y + 1, x + 1] > 0 && label[y + 1, x + 1] == 0)
                                {
                                    stack.Push(new[] { y + 1, x + 1 });
                                    label[y + 1, x + 1] = lab;
                                }
                            }
                        }
                        lab++;
                    }
                }
            }
            catch { }
            labelCount = lab;

            return(label);
        }
Example #11
0
        /// <summary>
        /// Actual objects map building.
        /// </summary>
        /// <param name="image">Unmanaged image to process.</param>
        /// <remarks>The method supports 8 bpp indexed grayscale images and 24/32 bpp color images.</remarks>
        ///
        protected override void BuildObjectsMap(UnmanagedImage image)
        {
            int stride = image.Stride;

            // check pixel format
            //if ( ( image.PixelFormat != PixelFormat.Format8bppIndexed ) &&
            //     ( image.PixelFormat != PixelFormat.Format24bppRgb ) &&
            //     ( image.PixelFormat != PixelFormat.Format32bppArgb ) &&
            //     ( image.PixelFormat != PixelFormat.Format32bppPArgb ) )
            //{
            //    throw new UnsupportedImageFormatException( "Unsupported pixel format of the source image." );
            //}

            // we don't want one pixel width images
            //if ( imageWidth == 1 )
            //    throw new InvalidImagePropertiesException( "Too small image." );

            // allocate labels array
            objectLabels = new int[imageWidth * imageHeight];
            // initial labels count
            int labelsCount = 0;

            // create map
            int maxObjects = ((imageWidth / 2) + 1) * ((imageHeight / 2) + 1) + 1;
            var map        = new int[maxObjects];

            // initially map all labels to themself
            for (int i = 0; i < maxObjects; i++)
            {
                map[i] = i;
            }

            // do the job
            unsafe
            {
                var src = (byte *)image.ImageData.ToPointer();
                int p   = 0;

                if (image.PixelFormat == PixelFormat.Format8bppIndexed)
                {
                    int offset = stride - imageWidth;

                    // 1 - for pixels of the first row
                    if (*src > backgroundThresholdG)
                    {
                        objectLabels[p] = ++labelsCount;
                    }
                    ++src;
                    ++p;

                    // process the rest of the first row
                    for (int x = 1; x < imageWidth; x++, src++, p++)
                    {
                        // check if we need to label Instance pixel
                        if (*src > backgroundThresholdG)
                        {
                            // check if the previous pixel already was labeled
                            if (src[-1] > backgroundThresholdG)
                            {
                                // label Instance pixel, as the previous
                                objectLabels[p] = objectLabels[p - 1];
                            }
                            else
                            {
                                // create new label
                                objectLabels[p] = ++labelsCount;
                            }
                        }
                    }
                    src += offset;

                    // 2 - for other rows
                    // for each row
                    for (int y = 1; y < imageHeight; y++)
                    {
                        // for the first pixel of the row, we need to check
                        // only upper and upper-right pixels
                        if (*src > backgroundThresholdG)
                        {
                            // check surrounding pixels
                            if (src[-stride] > backgroundThresholdG)
                            {
                                // label Instance pixel, as the above
                                objectLabels[p] = objectLabels[p - imageWidth];
                            }
                            else if (src[1 - stride] > backgroundThresholdG)
                            {
                                // label Instance pixel, as the above right
                                objectLabels[p] = objectLabels[p + 1 - imageWidth];
                            }
                            else
                            {
                                // create new label
                                objectLabels[p] = ++labelsCount;
                            }
                        }
                        ++src;
                        ++p;

                        // check left pixel and three upper pixels for the rest of pixels
                        for (int x = 1; x < imageWidth - 1; x++, src++, p++)
                        {
                            if (*src > backgroundThresholdG)
                            {
                                // check surrounding pixels
                                if (src[-1] > backgroundThresholdG)
                                {
                                    // label Instance pixel, as the left
                                    objectLabels[p] = objectLabels[p - 1];
                                }
                                else if (src[-1 - stride] > backgroundThresholdG)
                                {
                                    // label Instance pixel, as the above left
                                    objectLabels[p] = objectLabels[p - 1 - imageWidth];
                                }
                                else if (src[-stride] > backgroundThresholdG)
                                {
                                    // label Instance pixel, as the above
                                    objectLabels[p] = objectLabels[p - imageWidth];
                                }

                                if (src[1 - stride] > backgroundThresholdG)
                                {
                                    if (objectLabels[p] == 0)
                                    {
                                        // label Instance pixel, as the above right
                                        objectLabels[p] = objectLabels[p + 1 - imageWidth];
                                    }
                                    else
                                    {
                                        int l1 = objectLabels[p];
                                        int l2 = objectLabels[p + 1 - imageWidth];

                                        if ((l1 != l2) && (map[l1] != map[l2]))
                                        {
                                            // merge
                                            if (map[l1] == l1)
                                            {
                                                // map left value to the right
                                                map[l1] = map[l2];
                                            }
                                            else if (map[l2] == l2)
                                            {
                                                // map right value to the left
                                                map[l2] = map[l1];
                                            }
                                            else
                                            {
                                                // both values already mapped
                                                map[map[l1]] = map[l2];
                                                map[l1]      = map[l2];
                                            }

                                            // reindex
                                            for (int i = 1; i <= labelsCount; i++)
                                            {
                                                if (map[i] != i)
                                                {
                                                    // reindex
                                                    int j = map[i];
                                                    while (j != map[j])
                                                    {
                                                        j = map[j];
                                                    }
                                                    map[i] = j;
                                                }
                                            }
                                        }
                                    }
                                }

                                // label the object if it is not yet
                                if (objectLabels[p] == 0)
                                {
                                    // create new label
                                    objectLabels[p] = ++labelsCount;
                                }
                            }
                        }

                        // for the last pixel of the row, we need to check
                        // only upper and upper-left pixels
                        if (*src > backgroundThresholdG)
                        {
                            // check surrounding pixels
                            if (src[-1] > backgroundThresholdG)
                            {
                                // label Instance pixel, as the left
                                objectLabels[p] = objectLabels[p - 1];
                            }
                            else if (src[-1 - stride] > backgroundThresholdG)
                            {
                                // label Instance pixel, as the above left
                                objectLabels[p] = objectLabels[p - 1 - imageWidth];
                            }
                            else if (src[-stride] > backgroundThresholdG)
                            {
                                // label Instance pixel, as the above
                                objectLabels[p] = objectLabels[p - imageWidth];
                            }
                            else
                            {
                                // create new label
                                objectLabels[p] = ++labelsCount;
                            }
                        }
                        ++src;
                        ++p;

                        src += offset;
                    }
                }
                else
                {
                    // color images
                    int pixelSize = Image.GetPixelFormatSize(image.PixelFormat) / 8;
                    int offset    = stride - imageWidth * pixelSize;

                    int strideM1 = stride - pixelSize;
                    int strideP1 = stride + pixelSize;

                    // 1 - for pixels of the first row
                    if ((src[RGB.R] | src[RGB.G] | src[RGB.B]) != 0)
                    {
                        objectLabels[p] = ++labelsCount;
                    }
                    src += pixelSize;
                    ++p;

                    // process the rest of the first row
                    for (int x = 1; x < imageWidth; x++, src += pixelSize, p++)
                    {
                        // check if we need to label Instance pixel
                        if ((src[RGB.R] > backgroundThresholdR) ||
                            (src[RGB.G] > backgroundThresholdG) ||
                            (src[RGB.B] > backgroundThresholdB))
                        {
                            // check if the previous pixel already was labeled
                            if ((src[RGB.R - pixelSize] > backgroundThresholdR) ||
                                (src[RGB.G - pixelSize] > backgroundThresholdG) ||
                                (src[RGB.B - pixelSize] > backgroundThresholdB))
                            {
                                // label Instance pixel, as the previous
                                objectLabels[p] = objectLabels[p - 1];
                            }
                            else
                            {
                                // create new label
                                objectLabels[p] = ++labelsCount;
                            }
                        }
                    }
                    src += offset;

                    // 2 - for other rows
                    // for each row
                    for (int y = 1; y < imageHeight; y++)
                    {
                        // for the first pixel of the row, we need to check
                        // only upper and upper-right pixels
                        if ((src[RGB.R] > backgroundThresholdR) ||
                            (src[RGB.G] > backgroundThresholdG) ||
                            (src[RGB.B] > backgroundThresholdB))
                        {
                            // check surrounding pixels
                            if ((src[RGB.R - stride] > backgroundThresholdR) ||
                                (src[RGB.G - stride] > backgroundThresholdG) ||
                                (src[RGB.B - stride] > backgroundThresholdB))
                            {
                                // label Instance pixel, as the above
                                objectLabels[p] = objectLabels[p - imageWidth];
                            }
                            else if ((src[RGB.R - strideM1] > backgroundThresholdR) ||
                                     (src[RGB.G - strideM1] > backgroundThresholdG) ||
                                     (src[RGB.B - strideM1] > backgroundThresholdB))
                            {
                                // label Instance pixel, as the above right
                                objectLabels[p] = objectLabels[p + 1 - imageWidth];
                            }
                            else
                            {
                                // create new label
                                objectLabels[p] = ++labelsCount;
                            }
                        }
                        src += pixelSize;
                        ++p;

                        // check left pixel and three upper pixels for the rest of pixels
                        for (int x = 1; x < imageWidth - 1; x++, src += pixelSize, p++)
                        {
                            if ((src[RGB.R] > backgroundThresholdR) ||
                                (src[RGB.G] > backgroundThresholdG) ||
                                (src[RGB.B] > backgroundThresholdB))
                            {
                                // check surrounding pixels
                                if ((src[RGB.R - pixelSize] > backgroundThresholdR) ||
                                    (src[RGB.G - pixelSize] > backgroundThresholdG) ||
                                    (src[RGB.B - pixelSize] > backgroundThresholdB))
                                {
                                    // label Instance pixel, as the left
                                    objectLabels[p] = objectLabels[p - 1];
                                }
                                else if ((src[RGB.R - strideP1] > backgroundThresholdR) ||
                                         (src[RGB.G - strideP1] > backgroundThresholdG) ||
                                         (src[RGB.B - strideP1] > backgroundThresholdB))
                                {
                                    // label Instance pixel, as the above left
                                    objectLabels[p] = objectLabels[p - 1 - imageWidth];
                                }
                                else if ((src[RGB.R - stride] > backgroundThresholdR) ||
                                         (src[RGB.G - stride] > backgroundThresholdG) ||
                                         (src[RGB.B - stride] > backgroundThresholdB))
                                {
                                    // label Instance pixel, as the above
                                    objectLabels[p] = objectLabels[p - imageWidth];
                                }

                                if ((src[RGB.R - strideM1] > backgroundThresholdR) ||
                                    (src[RGB.G - strideM1] > backgroundThresholdG) ||
                                    (src[RGB.B - strideM1] > backgroundThresholdB))
                                {
                                    if (objectLabels[p] == 0)
                                    {
                                        // label Instance pixel, as the above right
                                        objectLabels[p] = objectLabels[p + 1 - imageWidth];
                                    }
                                    else
                                    {
                                        int l1 = objectLabels[p];
                                        int l2 = objectLabels[p + 1 - imageWidth];

                                        if ((l1 != l2) && (map[l1] != map[l2]))
                                        {
                                            // merge
                                            if (map[l1] == l1)
                                            {
                                                // map left value to the right
                                                map[l1] = map[l2];
                                            }
                                            else if (map[l2] == l2)
                                            {
                                                // map right value to the left
                                                map[l2] = map[l1];
                                            }
                                            else
                                            {
                                                // both values already mapped
                                                map[map[l1]] = map[l2];
                                                map[l1]      = map[l2];
                                            }

                                            // reindex
                                            for (int i = 1; i <= labelsCount; i++)
                                            {
                                                if (map[i] != i)
                                                {
                                                    // reindex
                                                    int j = map[i];
                                                    while (j != map[j])
                                                    {
                                                        j = map[j];
                                                    }
                                                    map[i] = j;
                                                }
                                            }
                                        }
                                    }
                                }

                                // label the object if it is not yet
                                if (objectLabels[p] == 0)
                                {
                                    // create new label
                                    objectLabels[p] = ++labelsCount;
                                }
                            }
                        }

                        // for the last pixel of the row, we need to check
                        // only upper and upper-left pixels
                        if ((src[RGB.R] > backgroundThresholdR) ||
                            (src[RGB.G] > backgroundThresholdG) ||
                            (src[RGB.B] > backgroundThresholdB))
                        {
                            // check surrounding pixels
                            if ((src[RGB.R - pixelSize] > backgroundThresholdR) ||
                                (src[RGB.G - pixelSize] > backgroundThresholdG) ||
                                (src[RGB.B - pixelSize] > backgroundThresholdB))
                            {
                                // label Instance pixel, as the left
                                objectLabels[p] = objectLabels[p - 1];
                            }
                            else if ((src[RGB.R - strideP1] > backgroundThresholdR) ||
                                     (src[RGB.G - strideP1] > backgroundThresholdG) ||
                                     (src[RGB.B - strideP1] > backgroundThresholdB))
                            {
                                // label Instance pixel, as the above left
                                objectLabels[p] = objectLabels[p - 1 - imageWidth];
                            }
                            else if ((src[RGB.R - stride] > backgroundThresholdR) ||
                                     (src[RGB.G - stride] > backgroundThresholdG) ||
                                     (src[RGB.B - stride] > backgroundThresholdB))
                            {
                                // label Instance pixel, as the above
                                objectLabels[p] = objectLabels[p - imageWidth];
                            }
                            else
                            {
                                // create new label
                                objectLabels[p] = ++labelsCount;
                            }
                        }
                        src += pixelSize;
                        ++p;

                        src += offset;
                    }
                }
            }

            // allocate remapping array
            var reMap = new int[map.Length];

            // count objects and prepare remapping array
            objectsCount = 0;

            for (int i = 1; i <= labelsCount; i++)
            {
                if (map[i] == i)
                {
                    reMap[i] = ++objectsCount; // increase objects count
                }
            }
            // second pass to complete remapping
            for (int i = 1; i <= labelsCount; i++)
            {
                if (map[i] != i)
                {
                    reMap[i] = reMap[map[i]];
                }
            }

            // repair object labels
            for (int i = 0, n = objectLabels.Length; i < n; i++)
            {
                objectLabels[i] = reMap[objectLabels[i]];
            }
        }