概述
本文将详细介绍如何使用C# WinForms开发一个功能丰富的图片处理应用程序。该应用程序包含三大核心功能模块:调色、质感和氛围感,并支持撤销/重做操作。
功能架构
1. 调色模块
-
构图调整:智能裁剪
-
亮度调节:精确控制图片明暗程度
-
饱和度调节:调整色彩鲜艳度
2. 质感模块
-
背景虚化:创建专业级景深效果
-
锐化处理:增强图片细节清晰度
3. 氛围感模块
-
颗粒效果:添加胶片质感,营造复古氛围
4. 操作历史
-
撤销/重做:完整的操作堆栈管理
-
状态保存:实时保存处理状态
5. UI设计
-
UI效果:展示效果
一、核心功能模块设计
1. 调色模块参考代码展示
// 更新亮度
private void UpdateBrightness()
{
if (processedImage == null) return;
brightness = trackBarBrightness.Value;
ApplyImageProcessing();
}
// 更新饱和度
private void UpdateSaturation()
{
if (processedImage == null) return;
saturation = trackBarSaturation.Value;
ApplyImageProcessing();
}
// 应用所有图像处理
private void ApplyImageProcessing()
{
if (originalImage == null) return;
// 保存当前状态到历史记录
historyManager.AddState((Image)processedImage.Clone());
// 从原始图像开始应用所有效果
using (Bitmap tempBitmap = new Bitmap(originalImage))
{
Bitmap processedBitmap = (Bitmap)tempBitmap.Clone();
// 应用亮度调整
if (brightness != 0)
processedBitmap = ImageProcessor.AdjustBrightness(processedBitmap, brightness);
// 应用饱和度调整
if (saturation != 0)
processedBitmap = ImageProcessor.AdjustSaturation(processedBitmap, saturation);
// 更新处理后的图像
processedImage = (Image)processedBitmap.Clone();
pictureBox.Image = (Image)processedImage.Clone();
}
}
// 调整亮度
public static Bitmap AdjustBrightness(Bitmap bitmap, int brightness)
{
float brightnessFactor = 1.0f + (brightness / 100.0f);
// 创建颜色矩阵
ColorMatrix colorMatrix = new ColorMatrix(new float[][]
{
new float[] {brightnessFactor, 0, 0, 0, 0},
new float[] {0, brightnessFactor, 0, 0, 0},
new float[] {0, 0, brightnessFactor, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
return ApplyColorMatrix(bitmap, colorMatrix);
}
// 调整饱和度
public static Bitmap AdjustSaturation(Bitmap bitmap, int saturation)
{
float saturationFactor = 1.0f + (saturation / 100.0f);
// 计算灰度系数
float gray = 0.3f;
float gray2 = 0.59f;
float gray3 = 0.11f;
// 创建饱和度调整的颜色矩阵
ColorMatrix colorMatrix = new ColorMatrix(new float[][]
{
new float[] {gray * (1 - saturationFactor) + saturationFactor, gray2 * (1 - saturationFactor), gray3 * (1 - saturationFactor), 0, 0},
new float[] {gray * (1 - saturationFactor), gray2 * (1 - saturationFactor) + saturationFactor, gray3 * (1 - saturationFactor), 0, 0},
new float[] {gray * (1 - saturationFactor), gray2 * (1 - saturationFactor), gray3 * (1 - saturationFactor) + saturationFactor, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
return ApplyColorMatrix(bitmap, colorMatrix);
}
2. 质感模块参考代码展示
// 应用所有图像处理
private void ApplyImageProcessing()
{
if (originalImage == null) return;
// 保存当前状态到历史记录
historyManager.AddState((Image)processedImage.Clone());
// 从原始图像开始应用所有效果
using (Bitmap tempBitmap = new Bitmap(originalImage))
{
Bitmap processedBitmap = (Bitmap)tempBitmap.Clone();
// 应用模糊
if (blurAmount > 0)
processedBitmap = ImageProcessor.ApplyBlur(processedBitmap, blurAmount);
// 应用锐化
if (sharpenAmount > 0)
processedBitmap = ImageProcessor.ApplySharpen(processedBitmap, sharpenAmount);
// 更新处理后的图像
processedImage = (Image)processedBitmap.Clone();
pictureBox.Image = (Image)processedImage.Clone();
}
}
// 应用模糊效果
public static Bitmap ApplyBlur(Bitmap bitmap, int blurAmount)
{
if (blurAmount < 1) return new Bitmap(bitmap);
int radius = blurAmount;
int size = radius * 2 + 1;
float[] kernel = new float[size * size];
float weight = 1.0f / (size * size);
// 创建平均模糊内核
for (int i = 0; i < kernel.Length; i++)
kernel[i] = weight;
return ApplyConvolutionFilter(bitmap, kernel, size, size);
}
// 应用锐化效果
public static Bitmap ApplySharpen(Bitmap bitmap, int sharpenAmount)
{
if (sharpenAmount < 1) return new Bitmap(bitmap);
float amount = 1.0f + (sharpenAmount * 0.2f);
float[,] kernel = {
{0, -1, 0},
{-1, amount, -1},
{0, -1, 0}
};
return ApplyConvolutionFilter(bitmap, kernel);
}
// 应用卷积滤镜(并行加速版 - 移除Math.Clamp)
private static Bitmap ApplyConvolutionFilter(Bitmap bitmap, float[] kernel, int kernelWidth, int kernelHeight)
{
// 1. 基础初始化(避免循环内重复创建对象)
int width = bitmap.Width;
int height = bitmap.Height;
int pixelSize = 4; // 固定32位ARGB(B-G-R-A)
int kernelHalfWidth = kernelWidth / 2;
int kernelHalfHeight = kernelHeight / 2;
int maxX = width - 1;
int maxY = height - 1;
// 创建结果图(与原图尺寸/格式一致)
Bitmap result = new Bitmap(width, height, PixelFormat.Format32bppArgb);
// 2. 锁定内存(减少GC和内存碎片)
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData srcData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData dstData = result.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
// 3. 复制像素数据到托管数组(连续内存访问更快)
int stride = srcData.Stride;
int byteCount = stride * height;
byte[] srcBytes = new byte[byteCount];
byte[] dstBytes = new byte[byteCount];
Marshal.Copy(srcData.Scan0, srcBytes, 0, byteCount);
// 4. 线程安全的Random(并行时避免随机数冲突,若卷积核无随机逻辑可删除)
ThreadLocal<Random> threadRandom = new ThreadLocal<Random>(() =>
new Random(Guid.NewGuid().GetHashCode()));
// 5. 并行遍历行(核心优化:利用多核CPU)
Parallel.For(0, height, y =>
{
// 无需随机数时可删除以下2行
Random random = threadRandom.Value;
int rowStart = y * stride; // 当前行起始字节索引(提前计算,减少循环内运算)
// 遍历当前行每个像素
for (int x = 0; x < width; x++)
{
float blue = 0, green = 0, red = 0;
// 6. 卷积核遍历(只处理有效范围)
for (int ky = -kernelHalfHeight; ky <= kernelHalfHeight; ky++)
{
// 手动边界夹紧:替代Math.Clamp(y + ky, 0, maxY)
int pixelY = y + ky;
if (pixelY < 0) pixelY = 0;
else if (pixelY > maxY) pixelY = maxY;
int srcRow = pixelY * stride; // 目标行起始索引(提前计算)
for (int kx = -kernelHalfWidth; kx <= kernelHalfWidth; kx++)
{
// 手动边界夹紧:替代Math.Clamp(x + kx, 0, maxX)
int pixelX = x + kx;
if (pixelX < 0) pixelX = 0;
else if (pixelX > maxX) pixelX = maxX;
// 卷积核索引(避免循环内重复计算)
int kernelIndex = (ky + kernelHalfHeight) * kernelWidth + (kx + kernelHalfWidth);
float weight = kernel[kernelIndex];
// 7. 连续内存访问(提升缓存命中率)
int pixelIndex = srcRow + pixelX * pixelSize;
blue += srcBytes[pixelIndex] * weight;
green += srcBytes[++pixelIndex] * weight;
red += srcBytes[++pixelIndex] * weight;
}
}
// 8. 手动颜色值夹紧:替代Math.Clamp(blue/green/red, 0, 255)
// 蓝色通道
if (blue < 0) blue = 0;
else if (blue > 255) blue = 255;
// 绿色通道
if (green < 0) green = 0;
else if (green > 255) green = 255;
// 红色通道
if (red < 0) red = 0;
else if (red > 255) red = 255;
// 赋值到目标数组(连续索引递增,减少运算)
int dstIndex = rowStart + x * pixelSize;
dstBytes[dstIndex] = (byte)blue; // B
dstBytes[++dstIndex] = (byte)green; // G
dstBytes[++dstIndex] = (byte)red; // R
dstBytes[++dstIndex] = srcBytes[dstIndex]; // A(保留原Alpha)
}
});
// 9. 释放资源(避免内存泄漏)
threadRandom.Dispose(); // 无需随机数时可删除
Marshal.Copy(dstBytes, 0, dstData.Scan0, byteCount);
bitmap.UnlockBits(srcData);
result.UnlockBits(dstData);
return result;
}
// 应用二维卷积滤镜
private static Bitmap ApplyConvolutionFilter(Bitmap bitmap, float[,] kernel)
{
int kernelWidth = kernel.GetLength(1);
int kernelHeight = kernel.GetLength(0);
float[] kernelArray = new float[kernelWidth * kernelHeight];
for (int y = 0; y < kernelHeight; y++)
{
for (int x = 0; x < kernelWidth; x++)
{
kernelArray[y * kernelWidth + x] = kernel[y, x];
}
}
return ApplyConvolutionFilter(bitmap, kernelArray, kernelWidth, kernelHeight);
}
3. 氛围模块参考代码展示
// 应用所有图像处理
private void ApplyImageProcessing()
{
if (originalImage == null) return;
// 保存当前状态到历史记录
historyManager.AddState((Image)processedImage.Clone());
// 从原始图像开始应用所有效果
using (Bitmap tempBitmap = new Bitmap(originalImage))
{
Bitmap processedBitmap = (Bitmap)tempBitmap.Clone();
// 应用颗粒效果
if (grainAmount > 0)
processedBitmap = ImageProcessor.ApplyGrain(processedBitmap, grainAmount);
// 更新处理后的图像
processedImage = (Image)processedBitmap.Clone();
pictureBox.Image = (Image)processedImage.Clone();
}
}
// 应用颗粒效果(简化优化版)
public static Bitmap ApplyGrain(Bitmap bitmap, int grainAmount)
{
if (grainAmount < 1)
return new Bitmap(bitmap);
Bitmap result = new Bitmap(bitmap.Width, bitmap.Height);
Random random = new Random();
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
// 锁定图像数据
var srcData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var dstData = result.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 计算字节数并分配缓冲区
int byteCount = srcData.Stride * bitmap.Height;
byte[] srcBytes = new byte[byteCount];
byte[] dstBytes = new byte[byteCount];
// 复制像素数据
System.Runtime.InteropServices.Marshal.Copy(srcData.Scan0, srcBytes, 0, byteCount);
// 处理每个像素
for (int i = 0; i < byteCount; i += 4)
{
// 生成随机颗粒值
int grain = random.Next(-grainAmount, grainAmount + 1);
// 处理BGR通道(注意顺序是蓝、绿、红)
dstBytes[i] = (byte)ClampColor(srcBytes[i] + grain); // 蓝色
dstBytes[i + 1] = (byte)ClampColor(srcBytes[i + 1] + grain); // 绿色
dstBytes[i + 2] = (byte)ClampColor(srcBytes[i + 2] + grain); // 红色
dstBytes[i + 3] = srcBytes[i + 3]; // 保留Alpha通道
}
// 复制回处理后的数据
System.Runtime.InteropServices.Marshal.Copy(dstBytes, 0, dstData.Scan0, byteCount);
// 解锁图像
bitmap.UnlockBits(srcData);
result.UnlockBits(dstData);
return result;
}
// 颜色值范围限制
private static int ClampColor(int value)
{
return value < 0 ? 0 : (value > 255 ? 255 : value);
}
4. 操作历史模块参考代码展示
// 保存图片
private void BtnSaveImage_Click(object sender, EventArgs e)
{
if (pictureBox.Image == null)
return;
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
saveFileDialog.Filter = "PNG Image|*.png|JPEG Image|*.jpg|Bitmap Image|*.bmp";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
string ext = System.IO.Path.GetExtension(saveFileDialog.FileName).ToLower();
ImageFormat format = ImageFormat.Png;
if (ext == ".jpg" || ext == ".jpeg")
format = ImageFormat.Jpeg;
else if (ext == ".bmp")
format = ImageFormat.Bmp;
pictureBox.Image.Save(saveFileDialog.FileName, format);
MessageBox.Show("图片保存成功!");
}
catch (Exception ex)
{
MessageBox.Show($"保存图片失败: {ex.Message}");
}
}
}
}
// 撤销操作
private void BtnUndo_Click(object sender, EventArgs e)
{
Image previousState = historyManager.Undo();
if (previousState != null)
{
processedImage = previousState;
pictureBox.Image = (Image)processedImage.Clone();
UpdateControlValues();
}
}
// 重做操作
private void BtnRedo_Click(object sender, EventArgs e)
{
Image nextState = historyManager.Redo();
if (nextState != null)
{
processedImage = nextState;
pictureBox.Image = (Image)processedImage.Clone();
UpdateControlValues();
}
}
// 更新控件值
private void UpdateControlValues()
{
trackBarBrightness.Value = brightness;
trackBarSaturation.Value = saturation;
trackBarBlur.Value = blurAmount;
trackBarSharpen.Value = sharpenAmount;
trackBarGrain.Value = grainAmount;
}
部分效果展示
使用说明
-
点击"打开"按钮选择要处理的图片
-
使用选项卡切换不同的功能模块
-
通过滑动条实时调整各种效果
-
使用"撤销"和"重做"按钮管理操作历史
-
点击"保存"按钮保存处理后的图片
这个应用程序提供了完整的图片处理功能,界面友好,操作简单,适合各种图片处理需求。