private unsafe bool GetAndDrawCountour(ref Point coord1, Contour c) { byte* dst2 = (byte*)data.Scan0.ToPointer(); // allign pointer dst2 += dstStride * coord1.Y + coord1.X; Point coord2; // 1.) look for white (edge px) neighbours CheckNeighbours(dst2, 255, coord1, out coord2); if (coord1 == coord2) { // try to close the contour with step 2 // 2.) look for neighbours of same contour and stop tracing CheckNeighbours(dst2, c.Index, coord1, out coord2); DrawLine(data, c.Index, coord1, coord2); if (coord1 != coord2) { c.UpdateBorderRect(coord2); } return false; } DrawLine(data, c.Index, coord1, coord2); c.UpdateBorderRect(coord2); coord1 = coord2; return true; }
private unsafe void GetBorderLine(Contour c) { int offset = dstStride + c.LeftX - c.RightX - 1; // dstOffset = dstStride - rect.Width + 6; Byte* dst = (byte*)data.Scan0.ToPointer(); // allign pointer dst += dstStride * c.UpY + c.LeftX; // FIND BORDER LINE PIXEL IN X- AND Y-DIRECTION // for each row bool isFirst; Point p; for (int y = c.UpY; y <= c.BottomY; y++) { isFirst = true; p = Point.Empty; // for each pixel for (int x = c.LeftX; x <= c.RightX; x++, dst++) { if (*dst != c.Index) { continue; } if (p.X < x) { p.X = x; p.Y = y; } if (!isFirst) { continue; } c.BorderLine.Add(p); // old version isFirst = false; } //check if no start point is detected if (p == Point.Empty) { dst += offset; continue; } c.BorderLine.Add(p); // old version dst += offset; } // for each column for (int x = c.LeftX; x <= c.RightX; x++) { isFirst = true; p = Point.Empty; dst = (byte*)data.Scan0.ToPointer(); // allign pointer dst += dstStride * c.UpY + x; for (int y = c.UpY; y <= c.BottomY; y++, dst += dstStride) { if (*dst != c.Index) { continue; } if (p.Y < y) { p.X = x; p.Y = y; } if (!isFirst) { continue; } c.BorderLine.Add(p); // old version isFirst = false; } //check if no start point is detected if (p == Point.Empty) { continue; } c.BorderLine.Add(p); // old version } }
/// <summary> /// Processes the filter on the passed <paramref name="srcData"/>. /// </summary> /// <param name="srcData">The source bitmap data.</param> /// <param name="dstData">The destination bitmap data.</param> protected unsafe override void Process(BitmapData srcData, BitmapData dstData) { Bitmap blur = BitmapConverter.BitmapDataToBitmap(srcData); // do grayscaling the image if (blur.PixelFormat != PixelFormat.Format8bppIndexed) { // STEP 0 - do grayscaling the image blur = TPGrayscale.CommonAlgorithms.BT709.Apply(blur); } // STEP 1 - blur image blur = gaussianFilter.Apply(blur); rect = new Rectangle(0, 0, blur.Width, blur.Height); BitmapData blurData = blur.LockBits(rect, ImageLockMode.ReadWrite, blur.PixelFormat); data = dstData; // processing start and stop X,Y positions int startX = rect.Left + 1; int startY = rect.Top + 1; int stopX = startX + rect.Width - 2; int stopY = startY + rect.Height - 2; const double toAngle = 180.0 / Math.PI; float leftPixel = 0, rightPixel = 0; dstStride = data.Stride; int srcStride = blurData.Stride; int dstOffset = dstStride - rect.Width + 2; int srcOffset = srcStride - rect.Width + 2; // orientation array orients = new byte[data.Width, data.Height]; // gradients array //int[,] gxArray = new int[dstData.Width, dstData.Height]; //int[,] gyArray = new int[dstData.Width, dstData.Height]; float[,] gradients = new float[data.Width, data.Height]; float maxGradient = float.NegativeInfinity; // do the job byte* src = (byte*)blurData.Scan0.ToPointer(); // allign pointer src += srcStride * startY + startX; #region canny // STEP 2 - calculate magnitude and edge orientation // for each line for (int y = startY; y < stopY; y++) { // for each pixel for (int x = startX; x < stopX; x++, src++) { // pixel's value and gradients int gx = src[-srcStride + 1] + src[srcStride + 1] - src[-srcStride - 1] - src[srcStride - 1] + 2 * (src[1] - src[-1]); int gy = src[-srcStride - 1] + src[-srcStride + 1] - src[srcStride - 1] - src[srcStride + 1] + 2 * (src[-srcStride] - src[srcStride]); //gxArray[x, y] = Math.Abs(gx); //gyArray[x, y] = Math.Abs(gy); // get gradient value gradients[x, y] = (float)Math.Sqrt(gx * gx + gy * gy); if (gradients[x, y] > maxGradient) maxGradient = gradients[x, y]; // --- get orientation double orientation; if (gx == 0) { // can not divide by zero orientation = (gy == 0) ? 0 : 90; } else { double div = (double)gy / gx; // handle angles of the 2nd and 4th quads if (div < 0) { orientation = 180 - Math.Atan(-div) * toAngle; } // handle angles of the 1st and 3rd quads else { orientation = Math.Atan(div) * toAngle; } // get closest angle from 0, 45, 90, 135 set if (orientation < 22.5) orientation = 0; else if (orientation < 67.5) orientation = 45; else if (orientation < 112.5) orientation = 90; else if (orientation < 157.5) orientation = 135; else orientation = 0; } // save orientation orients[x, y] = (byte)orientation; } src += srcOffset; } // STEP 3 - suppress non maximums byte* dst = (byte*)data.Scan0.ToPointer(); // allign pointer dst += dstStride * startY + startX; // for each line for (int y = startY; y < stopY; y++) { // for each pixel for (int x = startX; x < stopX; x++, dst++) { // get two adjacent pixels switch (orients[x, y]) { case 0: leftPixel = gradients[x - 1, y]; rightPixel = gradients[x + 1, y]; break; case 45: leftPixel = gradients[x - 1, y + 1]; rightPixel = gradients[x + 1, y - 1]; break; case 90: leftPixel = gradients[x, y + 1]; rightPixel = gradients[x, y - 1]; break; case 135: leftPixel = gradients[x + 1, y + 1]; rightPixel = gradients[x - 1, y - 1]; break; } // compare current pixels value with adjacent pixels if ((gradients[x, y] < leftPixel) || (gradients[x, y] < rightPixel)) { *dst = 0; } else { byte b = (byte)(gradients[x, y] / maxGradient * 255); *dst = b; } } dst += dstOffset; } // STEP 4 - hysteresis dst = (byte*)data.Scan0.ToPointer(); // allign pointer dst += dstStride * startY + startX; // for each line for (int y = startY; y < stopY; y++) { // for each pixel for (int x = startX; x < stopX; x++, dst++) { byte value = 255; if (*dst < HighThreshold) { if (*dst < LowThreshold) { // non edge value = 0; } else { // check 8 neighboring pixels if ((dst[-1] < HighThreshold) && (dst[1] < HighThreshold) && (dst[-dstStride - 1] < HighThreshold) && (dst[-dstStride] < HighThreshold) && (dst[-dstStride + 1] < HighThreshold) && (dst[dstStride - 1] < HighThreshold) && (dst[dstStride] < HighThreshold) && (dst[dstStride + 1] < HighThreshold)) { value = 0; } } } *dst = value; } dst += dstOffset; } #endregion canny #region contour tracing // STEP 5 - contour tracing dstOffset = dstStride - rect.Width; dst = (byte*)data.Scan0.ToPointer(); // allign pointer // dst += dstStride * (startY + 2) + startX + 2; byte index = 1, id = 1; Contours = new List<Contour>(rect.Width * rect.Height); Contour c; // for each line for (int y = 0; y < rect.Height; y++) { // for each pixel for (int x = 0; x < rect.Width; x++, dst++) { // is an edge pixel? if (*dst == 255) // && index != 255) { c = new Contour(id, index); Point coord1 = new Point(x, y); c.UpdateBorderRect(coord1); while (GetAndDrawCountour(ref coord1, c)) { } coord1 = new Point(x, y); while (GetAndDrawCountour(ref coord1, c)) { } GetBorderLine(c); Contours.Add(c); index++; id++; } if (index == 255) { index = 1; } } dst += dstOffset; } // sorts upwards by the contour's border rectangle size Contours.Sort(); if (OnlyBorderLine) { Drawing8Bpp.ReplacePixels(data, 0, 0); foreach (Contour t in Contours) { t.DrawBorderLine(data); } } if (DrawBorderRectangle) { foreach (var contour in Contours) { contour.DrawBorderRect(data, LineRectStrength); } } #endregion contour tracing blur.UnlockBits(blurData); blur.Dispose(); }