C# 不依赖 OpenCV 的图像处理算法:滤波、锐化与边缘检测
算法是否依赖 OpenCV特点均值滤波❌简单快速,适合去噪高斯滤波❌更自然平滑锐化❌增强细节Sobel 边缘❌经典边缘检测无需 OpenCV,C# 也能做扎实的图像处理——掌握底层原理,方能在资源受限或合规敏感场景中游刃有余。
·
在 C# 中,即使不依赖 OpenCV(如 EmguCV)或第三方图像库,也可以通过 纯 .NET 代码(System.Drawing 或ImageSharp) + 自定义算法 实现基础但高效的图像处理功能,包括均值/高斯滤波、锐化、Sobel/Canny 边缘检测等。这在轻量级部署、嵌入式场景或规避 GPL 许可限制时尤为重要。
以下提供一套完整、可运行、无外部依赖的实现方案(基于 System.Drawing,适用于 .NET Framework / .NET 6+ Windows;若需跨平台,可轻松迁移到 ImageSharp 的像素操作)。
✅ 前提:获取像素数据
using System.Drawing;
using System.Drawing.Imaging;
public static byte[] LockBitmap(Bitmap bitmap, out int width, out int height, out int stride)
{
var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
var bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
width = bmpData.Width;
height = bmpData.Height;
stride = bmpData.Stride;
var bytes = new byte[Math.Abs(stride) * height];
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, bytes, 0, bytes.Length);
bitmap.UnlockBits(bmpData);
return bytes;
}
💡 注意:
Format24bppRgb每像素 3 字节(BGR 顺序),stride可能含 padding。
1️⃣ 均值滤波(Mean Filter)
用于降噪、平滑。
public static byte[] MeanFilter(byte[] src, int width, int height, int kernelSize = 3)
{
var dst = new byte[src.Length];
int half = kernelSize / 2;
for (int y = half; y < height - half; y++)
{
for (int x = half; x < width - half; x++)
{
int r = 0, g = 0, b = 0;
for (int ky = -half; ky <= half; ky++)
{
for (int kx = -half; kx <= half; kx++)
{
int idx = ((y + ky) * width + (x + kx)) * 3;
b += src[idx]; // B
g += src[idx + 1]; // G
r += src[idx + 2]; // R
}
}
int area = kernelSize * kernelSize;
int dstIdx = (y * width + x) * 3;
dst[dstIdx] = (byte)(b / area);
dst[dstIdx + 1] = (byte)(g / area);
dst[dstIdx + 2] = (byte)(r / area);
}
}
return dst;
}
2️⃣ 高斯滤波(Gaussian Filter)
更自然的平滑,保留边缘。
public static byte[] GaussianFilter(byte[] src, int width, int height)
{
// 5x5 高斯核(σ=1)
float[,] kernel = {
{1, 4, 6, 4, 1},
{4, 16, 24, 16, 4},
{6, 24, 36, 24, 6},
{4, 16, 24, 16, 4},
{1, 4, 6, 4, 1}
};
float sum = 256f; // 核总和
var dst = new byte[src.Length];
for (int y = 2; y < height - 2; y++)
{
for (int x = 2; x < width - 2; x++)
{
float r = 0, g = 0, b = 0;
for (int ky = -2; ky <= 2; ky++)
{
for (int kx = -2; kx <= 2; kx++)
{
int idx = ((y + ky) * width + (x + kx)) * 3;
float weight = kernel[ky + 2, kx + 2] / sum;
b += src[idx] * weight;
g += src[idx + 1] * weight;
r += src[idx + 2] * weight;
}
}
int dstIdx = (y * width + x) * 3;
dst[dstIdx] = (byte)Math.Clamp(b, 0, 255);
dst[dstIdx + 1] = (byte)Math.Clamp(g, 0, 255);
dst[dstIdx + 2] = (byte)Math.Clamp(r, 0, 255);
}
}
return dst;
}
3️⃣ 锐化(Unsharp Masking)
增强边缘细节。
public static byte[] Sharpen(byte[] src, int width, int height)
{
// 拉普拉斯锐化核
int[,] kernel = {
{0, -1, 0},
{-1, 5, -1},
{0, -1, 0}
};
var dst = new byte[src.Length];
for (int y = 1; y < height - 1; y++)
{
for (int x = 1; x < width - 1; x++)
{
int r = 0, g = 0, b = 0;
for (int ky = -1; ky <= 1; ky++)
{
for (int kx = -1; kx <= 1; kx++)
{
int idx = ((y + ky) * width + (x + kx)) * 3;
int k = kernel[ky + 1, kx + 1];
b += src[idx] * k;
g += src[idx + 1] * k;
r += src[idx + 2] * k;
}
}
int dstIdx = (y * width + x) * 3;
dst[dstIdx] = (byte)Math.Clamp(b, 0, 255);
dst[dstIdx + 1] = (byte)Math.Clamp(g, 0, 255);
dst[dstIdx + 2] = (byte)Math.Clamp(r, 0, 255);
}
}
return dst;
}
4️⃣ Sobel 边缘检测
计算梯度幅值。
public static byte[] SobelEdge(byte[] src, int width, int height)
{
// 先转灰度(简化)
var gray = ToGrayscale(src, width, height);
var gx = new int[width * height];
var gy = new int[width * height];
// Sobel 算子
int[,] sobelX = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
int[,] sobelY = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};
for (int y = 1; y < height - 1; y++)
{
for (int x = 1; x < width - 1; x++)
{
int sumX = 0, sumY = 0;
for (int ky = -1; ky <= 1; ky++)
{
for (int kx = -1; kx <= 1; kx++)
{
int val = gray;
sumX += val * sobelX[ky + 1, kx + 1];
sumY += val * sobelY[ky + 1, kx + 1];
}
}
gx[y * width + x] = sumX;
gy[y * width + x] = sumY;
}
}
// 合成梯度幅值
var edge = new byte[src.Length];
for (int i = 0; i < width * height; i++)
{
byte mag = (byte)Math.Min(255, Math.Sqrt(gx[i] * gx[i] + gy[i] * gy[i]) / 4);
int idx = i * 3;
edge[idx] = edge[idx + 1] = edge[idx + 2] = mag;
}
return edge;
}
private static byte[] ToGrayscale(byte[] rgb, int width, int height)
{
var gray = new byte[width * height];
for (int i = 0; i < width * height; i++)
{
int rgbIdx = i * 3;
// Y = 0.299R + 0.587G + 0.114B
gray[i] = (byte)(0.299 * rgb[rgbIdx + 2] + 0.587 * rgb[rgbIdx + 1] + 0.114 * rgb[rgbIdx]);
}
return gray;
}
🖼 保存结果图像
public static void SaveBitmap(byte[] pixels, int width, int height, string path)
{
using var bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
var rect = new Rectangle(0, 0, width, height);
var bmpData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
System.Runtime.InteropServices.Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
bitmap.UnlockBits(bmpData);
bitmap.Save(path, System.Drawing.Imaging.ImageFormat.Png);
}
⚠️ 注意事项
- 性能优化:上述代码未向量化,大数据量建议用
Span<T>、并行循环(Parallel.For); - 边界处理:当前简单忽略边缘,可扩展为镜像/复制边界;
- 跨平台替代:在 Linux/macOS 上,可用
SixLabors.ImageSharp替代System.Drawing,其PixelAccessor提供类似像素访问能力; - 精度:使用
float中间计算避免溢出。
✅ 总结
| 算法 | 是否依赖 OpenCV | 特点 |
|---|---|---|
| 均值滤波 | ❌ | 简单快速,适合去噪 |
| 高斯滤波 | ❌ | 更自然平滑 |
| 锐化 | ❌ | 增强细节 |
| Sobel 边缘 | ❌ | 经典边缘检测 |
无需 OpenCV,C# 也能做扎实的图像处理——
掌握底层原理,方能在资源受限或合规敏感场景中游刃有余。
更多推荐
所有评论(0)