在 c# 开发中,许多开发者习惯借助 opencv(通过 emgucv 等封装)进行图像处理。然而,在某些轻量级项目、教学场景或对第三方依赖敏感的环境中,我们更希望使用 .net 原生能力完成基础图像处理任务。本文将带你从零开始,仅使用 system.drawing(或 .net 6+ 中的 imagesharp)实现三种经典图像处理算法:均值/高斯滤波、图像锐化 和 边缘检测(sobel 算子) 。
注意:从 .net core 3.0 起,system.drawing.common 在非 windows 平台需额外配置,推荐在新项目中使用跨平台友好的 sixlabors.imagesharp。本文以 system.drawing 为例便于理解,文末会提供 imagesharp 的适配建议。
一、准备工作:读取与写入像素
所有图像处理的核心是对像素的操作。我们首先封装一个辅助类,用于安全地获取和设置像素值:
using system.drawing;
public static class imagehelper
{
public static color getpixelsafe(bitmap bmp, int x, int y)
{
if (x < 0 || x >= bmp.width || y < 0 || y >= bmp.height)
return color.black; // 边界外视为黑色
return bmp.getpixel(x, y);
}
public static void setpixelsafe(bitmap bmp, int x, int y, color color)
{
if (x >= 0 && x < bmp.width && y >= 0 && y < bmp.height)
bmp.setpixel(x, y, color);
}
}
提示:getpixel/setpixel 性能较低,生产环境建议使用 lockbits 直接操作内存。但为简化逻辑,本文使用此方式。
二、图像滤波(平滑)
1. 均值滤波(mean filter)
使用 3×3 邻域平均值替代中心像素,可有效降噪。
public static bitmap meanfilter(bitmap src)
{
bitmap dst = new bitmap(src.width, src.height);
int[,] kernel = {
{1, 1, 1},
{1, 1, 1},
{1, 1, 1}
};
int factor = 9; // 3x3
for (int y = 0; y < src.height; y++)
{
for (int x = 0; x < src.width; x++)
{
int r = 0, g = 0, b = 0;
for (int ky = -1; ky <= 1; ky++)
{
for (int kx = -1; kx <= 1; kx++)
{
color c = imagehelper.getpixelsafe(src, x + kx, y + ky);
r += c.r * kernel[ky + 1, kx + 1];
g += c.g * kernel[ky + 1, kx + 1];
b += c.b * kernel[ky + 1, kx + 1];
}
}
r /= factor; g /= factor; b /= factor;
imagehelper.setpixelsafe(dst, x, y, color.fromargb(
math.clamp(r, 0, 255),
math.clamp(g, 0, 255),
math.clamp(b, 0, 255)));
}
}
return dst;
}
2. 高斯滤波(gaussian filter)
使用高斯核(如 3×3 σ=1)实现更自然的模糊效果:
// 3x3 高斯核 (σ≈0.8)
int[,] gaussiankernel = {
{1, 2, 1},
{2, 4, 2},
{1, 2, 1}
};
int gaussianfactor = 16;
将上述 kernel 和 factor 替换即可复用均值滤波代码结构。
三、图像锐化(unsharp masking)
锐化可通过“原图 + (原图 - 模糊图) × 强度”实现,也可直接使用拉普拉斯核:
public static bitmap sharpen(bitmap src)
{
bitmap dst = new bitmap(src.width, src.height);
// 拉普拉斯锐化核
int[,] kernel = {
{0, -1, 0},
{-1, 5, -1},
{0, -1, 0}
};
for (int y = 0; y < src.height; y++)
{
for (int x = 0; x < src.width; x++)
{
int r = 0, g = 0, b = 0;
for (int ky = -1; ky <= 1; ky++)
{
for (int kx = -1; kx <= 1; kx++)
{
color c = imagehelper.getpixelsafe(src, x + kx, y + ky);
int weight = kernel[ky + 1, kx + 1];
r += c.r * weight;
g += c.g * weight;
b += c.b * weight;
}
}
// 不除以因子(因核和为1),直接裁剪
imagehelper.setpixelsafe(dst, x, y, color.fromargb(
math.clamp(r, 0, 255),
math.clamp(g, 0, 255),
math.clamp(b, 0, 255)));
}
}
return dst;
}
四、边缘检测(sobel 算子)
sobel 通过计算 x 和 y 方向梯度,合成边缘强度:
public static bitmap sobeledgedetection(bitmap src)
{
bitmap dst = new bitmap(src.width, src.height);
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 = 0; y < src.height; y++)
{
for (int x = 0; x < src.width; x++)
{
int gxr = 0, gyr = 0;
int gxg = 0, gyg = 0;
int gxb = 0, gyb = 0;
for (int ky = -1; ky <= 1; ky++)
{
for (int kx = -1; kx <= 1; kx++)
{
color c = imagehelper.getpixelsafe(src, x + kx, y + ky);
int weightx = sobelx[ky + 1, kx + 1];
int weighty = sobely[ky + 1, kx + 1];
gxr += c.r * weightx; gyr += c.r * weighty;
gxg += c.g * weightx; gyg += c.g * weighty;
gxb += c.b * weightx; gyb += c.b * weighty;
}
}
// 合成梯度幅度(简化版:取绝对值之和)
int magr = math.abs(gxr) + math.abs(gyr);
int magg = math.abs(gxg) + math.abs(gyg);
int magb = math.abs(gxb) + math.abs(gyb);
int mag = (magr + magg + magb) / 3; // 转灰度
byte val = (byte)math.clamp(mag, 0, 255);
dst.setpixel(x, y, color.fromargb(val, val, val));
}
}
return dst;
}
五、性能优化建议
- 使用
lockbits替代getpixel/setpixel,提升 10~100 倍速度。 - 预计算卷积核边界,避免重复判断。
- 对于灰度图处理,先转灰度可减少 2/3 计算量。
- 考虑并行化(如
parallel.for)处理行。
六、迁移到 imagesharp(.net 6+ 推荐)
若使用 imagesharp,像素访问方式如下:
using var image = image.load<rgb24>("input.jpg");
image.processpixelrows(accessor =>
{
for (int y = 0; y < accessor.height; y++)
{
span<rgb24> row = accessor.getrowspan(y);
for (int x = 0; x < accessor.width; x++)
{
ref rgb24 pixel = ref row[x];
// pixel.r, pixel.g, pixel.b 可直接读写
}
}
});
image.save("output.jpg");
imagesharp 天然支持 simd 和多线程,更适合高性能场景。
结语
无需 opencv,仅凭 c# 原生能力,我们也能实现核心图像处理算法。这不仅加深了对底层原理的理解,也为资源受限环境提供了可行方案。掌握这些基础后,你可进一步探索自定义卷积、形态学操作或 hough 变换等高级技术。
到此这篇关于c#原生图像处理实战之滤波、锐化与边缘检测操作详解的文章就介绍到这了,更多相关c#图像处理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论