C#图片处理示例:裁剪、缩放、清晰度与水印
在桌面应用或Web后端开发中,图片处理是一项常见需求。C#提供了丰富的API(如System.Drawing)来处理图片,包括裁剪、缩放、调整清晰度以及添加水印。本文将通过完整的示例代码演示这些操作,帮助开发者快速掌握核心技巧。
准备工作
本文使用System.Drawing.Common命名空间(适用于Windows平台)。在项目中添加引用System.Drawing.Common(如使用.NET 6+,需要手动安装NuGet包)。此外,请确保项目目标平台支持GDI+。
图片裁剪
裁剪是从原图中截取指定矩形区域。关键方法是Graphics.DrawImage配合源图像的目标矩形。
using System.Drawing;
public static Bitmap CropImage(Image sourceImage, Rectangle cropArea)
{
Bitmap result = new Bitmap(cropArea.Width, cropArea.Height);
using (Graphics g = Graphics.FromImage(result))
{
// 将源图像的指定区域绘制到新位图上
g.DrawImage(sourceImage, new Rectangle(0, 0, result.Width, result.Height),
cropArea, GraphicsUnit.Pixel);
}
return result;
}
// 使用示例
using (var src = Image.FromFile("input.jpg"))
{
Rectangle crop = new Rectangle(50, 50, 200, 150);
using (var cropped = CropImage(src, crop))
{
cropped.Save("cropped.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
}上述代码中,cropArea定义了要保留的矩形区域,新生成的位图尺寸与该区域一致。注意DrawImage方法的重载参数:目标矩形、源矩形和度量单位。
图片缩放
缩放图片时需保持宽高比或强制拉伸。推荐使用高质量插值模式避免锯齿。
public static Bitmap ScaleImage(Image sourceImage, int newWidth, int newHeight, bool keepAspectRatio = true)
{
int destWidth = newWidth;
int destHeight = newHeight;
if (keepAspectRatio)
{
float ratio = Math.Min((float)newWidth / sourceImage.Width, (float)newHeight / sourceImage.Height);
destWidth = (int)(sourceImage.Width * ratio);
destHeight = (int)(sourceImage.Height * ratio);
}
Bitmap result = new Bitmap(destWidth, destHeight);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(sourceImage, 0, 0, destWidth, destHeight);
}
return result;
}
// 使用示例
using (var src = Image.FromFile("input.jpg"))
{
using (var scaled = ScaleImage(src, 800, 600, true))
{
scaled.Save("scaled.jpg", ImageFormat.Jpeg);
}
}设置InterpolationMode为HighQualityBicubic可获得平滑缩放效果。若需要固定尺寸且不保持比例,将keepAspectRatio设为false。
调整清晰度(锐化)
清晰度调整通常通过锐化滤镜实现,例如3x3卷积核(拉普拉斯算子或自定义内核)。下面实现一个简单的锐化函数:
public static Bitmap SharpenImage(Bitmap sourceImage, float strength = 1.0f)
{
// 创建锐化卷积核
float[,] kernel = new float[,]
{
{ 0, -1, 0 },
{ -1, 5, -1 },
{ 0, -1, 0 }
};
// 强度调整
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
kernel[i, j] *= strength;
Bitmap result = new Bitmap(sourceImage.Width, sourceImage.Height);
System.Drawing.Imaging.BitmapData srcData = sourceImage.LockBits(
new Rectangle(0, 0, sourceImage.Width, sourceImage.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
System.Drawing.Imaging.BitmapData dstData = result.LockBits(
new Rectangle(0, 0, result.Width, result.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
int offset = stride - sourceImage.Width * 4;
System.IntPtr srcPtr = srcData.Scan0;
System.IntPtr dstPtr = dstData.Scan0;
unsafe
{
byte* src = (byte*)srcPtr.ToPointer();
byte* dst = (byte*)dstPtr.ToPointer();
// 遍历每个像素(忽略边缘)
for (int y = 1; y < sourceImage.Height - 1; y++)
{
for (int x = 1; x < sourceImage.Width - 1; x++)
{
int idx = y * stride + x * 4;
float b = 0, g = 0, r = 0, a = 0;
// 3x3卷积
for (int ky = -1; ky <= 1; ky++)
{
for (int kx = -1; kx <= 1; kx++)
{
int pixelIdx = (y + ky) * stride + (x + kx) * 4;
float weight = kernel[ky + 1, kx + 1];
b += src[pixelIdx] * weight;
g += src[pixelIdx + 1] * weight;
r += src[pixelIdx + 2] * weight;
a += src[pixelIdx + 3] * weight;
}
}
// 限制到[0,255]
dst[idx] = ClampToByte(b);
dst[idx + 1] = ClampToByte(g);
dst[idx + 2] = ClampToByte(r);
dst[idx + 3] = ClampToByte(a);
}
}
}
sourceImage.UnlockBits(srcData);
result.UnlockBits(dstData);
return result;
}
private static byte ClampToByte(float value)
{
return (byte)(value < 0 ? 0 : (value > 255 ? 255 : value));
}
// 使用示例
using (var src = (Bitmap)Image.FromFile("input.jpg"))
{
using (var sharp = SharpenImage(src, 1.2f))
{
sharp.Save("sharpened.jpg", ImageFormat.Jpeg);
}
}以上代码通过不安全代码(unsafe)直接操作像素,效率较高。若项目不允许unsafe,可使用Bitmap.GetPixel/SetPixel但速度较慢。锐化强度通过strength参数控制。
添加图片水印
水印可以是文字或另一张图片。以下示例分别演示文字水印和图片水印。
文字水印
public static Bitmap AddTextWatermark(Image sourceImage, string text, Font font, Color color, Point position, float opacity = 0.5f)
{
Bitmap result = new Bitmap(sourceImage);
using (Graphics g = Graphics.FromImage(result))
{
// 设置透明度
ColorMatrix cm = new ColorMatrix();
cm.Matrix33 = opacity; // Alpha通道比例
System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes();
attr.SetColorMatrix(cm);
// 绘制文本
using (SolidBrush brush = new SolidBrush(color))
{
// 使用Graphics.DrawString直接绘制(不支持透明度控制,但一般可用)
g.DrawString(text, font, brush, position);
}
}
return result;
}
// 使用示例
using (var src = Image.FromFile("input.jpg"))
{
using (Font f = new Font("Arial", 24))
{
var watermarked = AddTextWatermark(src, "ipipp.com", f, Color.FromArgb(128, 255, 255, 255), new Point(20, 20), 0.6f);
watermarked.Save("watermarked_text.jpg", ImageFormat.Jpeg);
}
}注意:直接使用DrawString无法完美实现透明度(绘制的文字颜色本身包含Alpha值),更好的方法是使用TextRenderer或手动创建Graphics并设置CompositingMode。此处示例简化,实际项目中建议结合ImageAttributes控制。
图片水印
public static Bitmap AddImageWatermark(Image sourceImage, Image watermarkImage, Point position, float opacity = 0.5f)
{
Bitmap result = new Bitmap(sourceImage);
using (Graphics g = Graphics.FromImage(result))
{
ColorMatrix cm = new ColorMatrix();
cm.Matrix33 = opacity;
System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes();
attr.SetColorMatrix(cm);
// 绘制水印图片(支持透明度)
g.DrawImage(watermarkImage,
new Rectangle(position.X, position.Y, watermarkImage.Width, watermarkImage.Height),
0, 0, watermarkImage.Width, watermarkImage.Height,
GraphicsUnit.Pixel, attr);
}
return result;
}
// 使用示例
using (var src = Image.FromFile("input.jpg"))
using (var logo = Image.FromFile("logo.png"))
{
using (var result = AddImageWatermark(src, logo, new Point(src.Width - logo.Width - 10, 10), 0.4f))
{
result.Save("watermarked_image.jpg", ImageFormat.Jpeg);
}
}图片水印通过ImageAttributes控制Alpha通道,实现半透明叠加。可根据需要调整位置和透明度。
完整示例
以下是一个综合示例,依次执行裁剪、缩放、锐化和添加水印:
static void ProcessImage(string inputPath, string outputPath)
{
using (var src = Image.FromFile(inputPath))
{
// 1. 裁剪(取中心区域)
int cropSize = Math.Min(src.Width, src.Height);
int x = (src.Width - cropSize) / 2;
int y = (src.Height - cropSize) / 2;
using (var cropped = CropImage(src, new Rectangle(x, y, cropSize, cropSize)))
{
// 2. 缩放至指定尺寸
using (var scaled = ScaleImage(cropped, 500, 500, true))
{
// 3. 锐化
using (var sharp = SharpenImage((Bitmap)scaled, 1.0f))
{
// 4. 添加水印
using (Font f = new Font("Arial", 20))
{
var result = AddTextWatermark(sharp, "Sample", f, Color.FromArgb(100, 255, 255, 255), new Point(10, 10));
result.Save(outputPath, ImageFormat.Jpeg);
}
}
}
}
}
}注意事项
内存管理:
Bitmap和Graphics对象必须及时释放,使用using或Dispose。平台兼容性:
System.Drawing.Common在Windows上原生支持,在Linux/macOS需安装libgdiplus,且功能受限。跨平台建议使用ImageSharp(NuGet包SixLabors.ImageSharp)。性能优化:大批量处理时避免使用
GetPixel/SetPixel,优先使用LockBits和指针操作。编码格式:保存时可根据需要选择合适的
ImageFormat(JPEG、PNG、BMP等),注意JPEG是有损压缩。
总结
本文展示了C#中使用System.Drawing进行图片裁剪、缩放、锐化和添加水印的完整Demo。开发者可以根据实际需求组合这些操作,实现更复杂的图像处理流水线。对于生产环境,建议进一步封装错误处理和资源管理,并考虑使用更现代的跨平台库(如ImageSharp)以提升可移植性。