【创作赢红包】C# 中进行图形边缘检测

在 C# 中,可以使用一些图像处理技术来检测图像中的轮廓,从而提取出感兴趣的物体或形状。下面是一些常用的图像处理技术和算法,以及它们在 C# 中的实现方式。

1.图像二值化

图像二值化是将彩色或灰度图像转换为黑白图像的过程。这是图像处理中最基本的操作之一,因为许多其他算法都要求输入的图像是二值化的。在 C# 中,可以使用 System.Drawing.Bitmap 类来加载和保存图像,并使用 LockBits 方法来访问图像像素的原始数据。接下来,可以使用以下代码将图像二值化:

public static Bitmap Binarize(Bitmap source, int threshold)
{
    Bitmap result = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);
    BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    BitmapData resultData = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
    int sourceStride = sourceData.Stride;
    int resultStride = resultData.Stride;
    byte[] sourceBuffer = new byte[sourceStride * source.Height];
    byte[] resultBuffer = new byte[resultStride * result.Height];
    Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, sourceBuffer.Length);
    for (int y = 0; y < source.Height; y++)
    {
        int sourceIndex = y * sourceStride;
        int resultIndex = y * resultStride;
        for (int x = 0; x < source.Width; x++)
        {
            byte r = sourceBuffer[sourceIndex + 2];
            byte g = sourceBuffer[sourceIndex + 1];
            byte b = sourceBuffer[sourceIndex];
            byte gray = (byte)(0.299 * r + 0.587 * g + 0.114 * b);
            byte binary = gray > threshold ? (byte)0xff : (byte)0x00;
            resultBuffer[resultIndex + x / 8] |= (byte)(binary << (7 - x % 8));
            sourceIndex += 3;
        }
    }
    Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
    source.UnlockBits(sourceData);
    result.UnlockBits(resultData);
    return result;
}

这个函数接受一个 Bitmap 对象和一个阈值作为参数,返回一个黑白图像。它使用锁定位图的方法来访问图像像素的原始数据,并对每个像素进行阈值处理,将其转换为黑色或白色。最终的结果是一个只有黑色和白色的二值图像。

2.图像边缘检测

图像边缘检测是一种用于检测图像中明暗变化的算法。在 C# 中,可以使用 Sobel、Prewitt、Canny 等算法来进行边缘检测。下面是使用 Sobel 算法进行边缘检测的示例代码:

public static Bitmap DetectEdges(Bitmap source, int threshold)
{
    Bitmap result = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);
    BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    BitmapData resultData = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
    int sourceStride = sourceData.Stride;
    int resultStride = resultData.Stride;
    byte[] sourceBuffer = new byte[sourceStride * source.Height];
    byte[] resultBuffer = new byte[resultStride * result.Height];
    Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, sourceBuffer.Length);
    int[] gradientX = new int[source.Width * source.Height];
    int[] gradientY = new int[source.Width * source.Height];
    for (int y = 1; y < source.Height - 1; y++)
    {
        int index = y * source.Width + 1;
        for (int x = 1; x < source.Width - 1; x++)
        {
            int gx = -sourceBuffer[index - sourceStride - 1] - 2 * sourceBuffer[index - 1] - sourceBuffer[index + sourceStride - 1] + sourceBuffer[index - sourceStride + 1] + 2 * sourceBuffer[index + 1] + sourceBuffer[index + sourceStride + 1];
            int gy = -sourceBuffer[index - sourceStride - 1] - 2 * sourceBuffer[index - sourceStride] - sourceBuffer[index - sourceStride + 1] + sourceBuffer[index + sourceStride - 1] + 2 * sourceBuffer[index + sourceStride] + sourceBuffer[index + sourceStride + 1];
            gradientX[index] = gx;
            gradientY[index] = gy;
            index++;
        }
    }
    for (int y = 1; y < source.Height - 1; y++)
    {
        int sourceIndex = y * sourceStride + 3;
        int resultIndex = y * resultStride;
        for (int x = 1; x < source.Width - 1; x++)
        {
            int gx = gradientX[y * source.Width + x];
            int gy = gradientY[y * source.Width + x];
            int magnitude = (int)Math.Sqrt(gx * gx + gy * gy);
            byte binary = magnitude > threshold ? (byte)0xff : (byte)0x00;
            resultBuffer[resultIndex + x / 8] |= (byte)(binary << (7 - x % 8));
            sourceIndex += 3;
        }
    }
    Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
    source.UnlockBits(sourceData);
    result.UnlockBits(resultData);
    return result;
}

这个函数接受一个 Bitmap 对象和一个阈值作为参数,返回一个黑白图像,其中白色像素表示图像的边缘。它使用 Sobel 算法来计算每个像素的梯度,然后根据梯度大小将像素转换为黑色或白色。注意,这个函数只能检测图像中的边缘,并不能准确地提取出形状的轮廓。

3.轮廓检测

为了准确地提取出形状的轮廓,可以使用 OpenCV 库中的轮廓检测函数。以下是一个简单的 C# 示例代码:

public static Bitmap DetectContours(Bitmap source, int threshold)
{
    Mat src = BitmapConverter.ToMat(source);
    Mat gray = new Mat();
    Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
    Cv2.GaussianBlur(gray, gray, new Size(3, 3), 0);
    Mat edges = new Mat();
    Cv2.Canny(gray, edges, threshold, threshold * 2);
    Point[][] contours;
    HierarchyIndex[] hierarchy;
    Cv2.FindContours(edges, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
    Mat result = new Mat(src.Size(), MatType.CV_8UC3, Scalar.All(0));
    Cv2.DrawContours(result, contours, -1, Scalar.All(255), 2);
    return BitmapConverter.ToBitmap(result);
}

这个函数接受一个 Bitmap 对象和一个阈值作为参数,返回一个黑白图像,其中白色像素表示图像的轮廓。它使用了以下几个 OpenCV 函数:

  • CvtColor 将原始图像从 BGR 颜色空间转换为灰度图像。
  • GaussianBlur 对灰度图像进行高斯模糊,以减少噪声的影响。
  • Canny 使用 Canny 边缘检测算法检测边缘。
  • FindContours 查找图像中的轮廓。
  • DrawContours 在结果图像上绘制轮廓。

注意,这个函数使用了 OpenCV 的 Mat 类来处理图像。为了将 Bitmap 对象转换为 Mat 对象,需要使用 OpenCVSharp3 项目中提供的 BitmapConverter.ToMat 和 BitmapConverter.ToBitmap 函数。如果你不想使用 OpenCVSharp3,也可以使用 .NET Framework 自带的 Bitmap.LockBits 和 Marshal.Copy 函数将 Bitmap 对象转换为 byte 数组,然后使用 Emgu.CV 库中的 CvInvoke.Imread 和 CvInvoke.Imwrite 函数将 byte 数组转换为 Mat 对象和 Bitmap 对象。

这就是一个简单的图形轮廓检测算法的实现。当然,这只是一个入门级的示例。在实际应用中,可能需要根据具体的需求对算法进行优化和改进。

你可能感兴趣的:(计算机视觉,图像处理,人工智能)