/// <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 override unsafe 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(); }
/// <summary> /// Processes the filter on the passed <paramref name="srcData"/> /// resulting into <paramref name="dstData"/>. /// </summary> /// <param name="srcData">The source bitmap data.</param> /// <param name="dstData">The destination bitmap data.</param> protected override unsafe void Process(BitmapData srcData, BitmapData dstData) { // processing start and stop X,Y positions const int startX = 1; const int startY = 1; int stopX = startX + srcData.Width - 2; int stopY = startY + srcData.Height - 2; const double toAngle = 180.0 / Math.PI; float leftPixel = 0, rightPixel = 0; #region canny 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); Rectangle rect = new Rectangle(0, 0, blur.Width, blur.Height); BitmapData blurData = blur.LockBits(rect, ImageLockMode.ReadWrite, blur.PixelFormat); int dstStride = dstData.Stride; int srcStride = blurData.Stride; int dstOffset = dstStride - rect.Width + 2; int srcOffset = srcStride - rect.Width + 2; // orientation array byte[,] orients = new byte[dstData.Width, dstData.Height]; // gradients array //int[,] gxArray = new int[dstData.Width, dstData.Height]; //int[,] gyArray = new int[dstData.Width, dstData.Height]; float[,] gradients = new float[dstData.Width, dstData.Height]; float maxGradient = float.NegativeInfinity; // do the job byte *src = (byte *)blurData.Scan0.ToPointer(); // allign pointer src += srcStride * startY + startX; // 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 *)dstData.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 { *dst = (byte)(gradients[x, y] / maxGradient * 255); } } dst += dstOffset; } // STEP 4 - hysteresis dst = (byte *)dstData.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; } } } #region color orientations if (value == 255 && orientColored) { byte tmp = orients[x, y]; switch (tmp) { case 0: value = 255; break; case 45: value = 45; break; case 90: value = 90; break; case 135: value = 135; break; } } #endregion color orientations *dst = value; } dst += dstOffset; } #endregion canny //#region Adapt line thickness //if (Diameter > 1) //{ // dst = (byte*)dstData.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++) // { // if (*dst != 0) // { // Drawing8Bpp.DrawThickPoint( // dstData, *dst, new Point( x, y), Diameter); // } // } // dst += dstOffset; // } //} //#endregion Adapt line thickness blur.UnlockBits(blurData); blur.Dispose(); }