/// <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); }
/// <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; } } }
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; } } }
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); } }
public static int GetBytesPerPixel(this PixelFormat format) => Image.GetPixelFormatSize(format) / 8;
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); }
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()); }
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); }
/// <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]]; } }