C#实现图片模糊:Bitmap拆分RGB数组后3×3矩阵处理求助
实现C#图片模糊效果的具体方案
嘿,你的这个思路完全没问题——这其实就是**均值模糊(Mean Blur)**的一种变体实现,针对3x3矩阵只取外围8个像素的平均值赋值给中心像素。下面我来帮你把这个思路落地成可运行的C#代码,同时给你一些关键的注意事项:
一、高效操作Bitmap的前提:使用LockBits
直接用Bitmap.GetPixel()和SetPixel()会非常慢,尤其是处理大图片时。建议用LockBits方法直接获取像素内存的指针,这样能大幅提升性能。
二、核心模糊逻辑实现步骤
- 锁定位图内存,获取像素数据的起始指针和每行的字节数(stride)
- 遍历图像像素:注意要跳过边缘像素(因为边缘的3x3矩阵会超出图像范围,你可以选择保留原像素,或者用边缘复制的方式填充)
- 计算外围8个像素的RGB平均值:对每个非边缘像素,找到它周围的8个Z位置像素,分别累加红、绿、蓝通道的值,再除以8得到平均值
- 赋值给中心像素:把计算出的平均值赋值给当前的中心X像素
- 解锁位图内存,保存或返回处理后的图片
三、完整代码示例
using System; using System.Drawing; using System.Drawing.Imaging; public class ImageBlurProcessor { public static Bitmap ApplyPeripheralMeanBlur(Bitmap originalBitmap) { // 创建副本避免修改原图 Bitmap blurredBitmap = new Bitmap(originalBitmap.Width, originalBitmap.Height); Rectangle rect = new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height); BitmapData originalData = originalBitmap.LockBits(rect, ImageLockMode.ReadOnly, originalBitmap.PixelFormat); BitmapData blurredData = blurredBitmap.LockBits(rect, ImageLockMode.WriteOnly, blurredBitmap.PixelFormat); // 获取像素格式的字节数(24位=3字节,32位=4字节) int bytesPerPixel = Image.GetPixelFormatSize(originalBitmap.PixelFormat) / 8; int stride = originalData.Stride; IntPtr originalPtr = originalData.Scan0; IntPtr blurredPtr = blurredData.Scan0; // 分配数组存储像素数据 int totalBytes = stride * originalBitmap.Height; byte[] originalPixels = new byte[totalBytes]; byte[] blurredPixels = new byte[totalBytes]; // 把像素数据复制到数组 System.Runtime.InteropServices.Marshal.Copy(originalPtr, originalPixels, 0, totalBytes); // 遍历每个非边缘像素 for (int y = 1; y < originalBitmap.Height - 1; y++) { for (int x = 1; x < originalBitmap.Width - 1; x++) { int currentIndex = y * stride + x * bytesPerPixel; int blueSum = 0, greenSum = 0, redSum = 0; int count = 0; // 遍历3x3矩阵外围(跳过中心X) for (int dy = -1; dy <= 1; dy++) { for (int dx = -1; dx <= 1; dx++) { if (dy == 0 && dx == 0) continue; int neighborIndex = (y + dy) * stride + (x + dx) * bytesPerPixel; blueSum += originalPixels[neighborIndex]; greenSum += originalPixels[neighborIndex + 1]; redSum += originalPixels[neighborIndex + 2]; count++; } } // 赋值并确保颜色值在0-255范围内 blurredPixels[currentIndex] = (byte)Math.Clamp(blueSum / count, 0, 255); blurredPixels[currentIndex + 1] = (byte)Math.Clamp(greenSum / count, 0, 255); blurredPixels[currentIndex + 2] = (byte)Math.Clamp(redSum / count, 0, 255); // 32位带Alpha通道的图片保留原Alpha值 if (bytesPerPixel == 4) { blurredPixels[currentIndex + 3] = originalPixels[currentIndex + 3]; } } } // 复制边缘像素(保持原图边缘不变) // 复制第一行和最后一行 for (int x = 0; x < originalBitmap.Width; x++) { int topIndex = 0 * stride + x * bytesPerPixel; int bottomIndex = (originalBitmap.Height - 1) * stride + x * bytesPerPixel; Array.Copy(originalPixels, topIndex, blurredPixels, topIndex, bytesPerPixel); Array.Copy(originalPixels, bottomIndex, blurredPixels, bottomIndex, bytesPerPixel); } // 复制第一列和最后一列(排除已复制的首尾行) for (int y = 1; y < originalBitmap.Height - 1; y++) { int leftIndex = y * stride + 0 * bytesPerPixel; int rightIndex = y * stride + (originalBitmap.Width - 1) * bytesPerPixel; Array.Copy(originalPixels, leftIndex, blurredPixels, leftIndex, bytesPerPixel); Array.Copy(originalPixels, rightIndex, blurredPixels, rightIndex, bytesPerPixel); } // 把处理后的数据复制回位图 System.Runtime.InteropServices.Marshal.Copy(blurredPixels, 0, blurredPtr, totalBytes); // 解锁内存 originalBitmap.UnlockBits(originalData); blurredBitmap.UnlockBits(blurredData); return blurredBitmap; } // 测试用例 public static void Main() { using (Bitmap original = new Bitmap("input.jpg")) { using (Bitmap blurred = ApplyPeripheralMeanBlur(original)) { blurred.Save("output_blurred.jpg", ImageFormat.Jpeg); } } } }
四、关键注意事项
- Bitmap通道顺序:.NET的Bitmap默认用BGR顺序存储像素,而非RGB,所以代码中要注意通道索引(blue是第一个字节,green第二个,red第三个)
- 边缘处理:代码选择保留原边缘像素,你也可以改成边缘复制、镜像填充等方式,让模糊效果覆盖到边缘
- 性能优化:处理超大图片时,可用
Parallel.For并行遍历行,提升处理速度 - 像素格式兼容:代码兼容24位(RGB)和32位(ARGB)位图,其他格式建议先转换为这两种格式再处理
内容的提问来源于stack exchange,提问作者FSmith94
相关产品推荐
相关产品推荐

