使用 GDI+ 为 .NET 创建带水印的照片






4.95/5 (88投票s)
2002年9月23日
5分钟阅读

425685

12683
本文介绍了使用 GDI+ 和 C# for .NET 将水印图像叠加在照片上的过程。
引言
当我们在网站上放置照片图像时,通常需要在图像上永久叠加水印和/或版权声明。这种插入有助于识别拍摄照片的人并表明拥有版权的组织。手动完成此任务可能非常耗时且常常不一致。通过一些简单的技术,可以使用 C# 和 GDI+ 以编程方式轻松完成此任务。
概述
我将向您展示各种图像处理技术。以下是一些技术的高级列表:
- 在图像上插入文本,文本相对于图像大小进行定位
- 动态选择
System.Drawing.Font
的大小以最大化可读性 - 修改文本字符串的不透明度
- 将位图中特定颜色替换为透明色
- 通过 5x5 ColorMatrix 更改图像的不透明度
定义图像
![]() (照片由美联社提供) | 此过程的第一步是加载您想要应用水印的照片。该图像的尺寸和分辨率几乎可以是任意的。在此示例中,我们将使用一个宽度为 449 像素、高度为 346 像素的图像。分辨率为 72 dpi。 |
当 Main
方法被实例化时,会定义两个 string
类型的变量。第一个变量将定义照片的来源、水印以及新图像的输出位置。第二个变量将定义我们将用作水印一部分的 Copyright string
(版权字符串)。
string WorkingDirectory = @"C:\Projects\WaterMark";
string Copyright = "Copyright © 2002
- AP Photo/David Zalubowski";
以下代码从指定文件创建一个 Image
对象,然后为该图像的 Width
(宽度)和 Height
(高度)定义变量。然后,这些尺寸用于构建一个具有 24 位每像素颜色数据格式的 Bitmap
对象。最后,此 Bitmap
用于从指定的 Bitmap
图像创建一个新的 Graphics
对象。
Image imgPhoto = Image.FromFile(WorkingDirectory
+ "\\watermark_photo.jpg");
int phWidth = imgPhoto.Width; int phHeight =
imgPhoto.Height;
Bitmap bmPhoto = new Bitmap(phWidth, phHeight,
PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(72, 72);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
![]() | 此代码加载以 BMP 格式保存的水印图像,并将其背景色设置为绿色 (A=0, R=0, G=255, B=0 )。同样,它为水印图像的 Width (宽度)和 Height (高度)定义了变量。 |
(图像由 MLB.com 提供) |
Image imgWatermark = new Bitmap(WorkingDirectory
+ "\\watermark.bmp");
int wmWidth = imgWatermark.Width;
int wmHeight = imgWatermark.Height;
步骤 #1 - 水印文本
此代码将 imgPhoto
(照片图像)绘制到 Graphics
对象上,将其定位在 (x= 0, y=0
),尺寸为原始大小的 100%。所有后续的绘制都将发生在原始照片之上。
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
grPhoto.DrawImage(
imgPhoto,
new Rectangle(0, 0, phWidth, phHeight),
0,
0,
phWidth,
phHeight,
GraphicsUnit.Pixel);
为了最大化 Copyright
(版权)消息的大小,我们将测试 7 种不同的 Font
(字体)大小,以确定我们可以用于照片宽度的最大可能尺寸。要有效地做到这一点,我们将定义一个整数数组,然后遍历这些值,在不同的磅值下测量 Copyright string
。一旦确定了最大可能尺寸,我们将退出循环并绘制文本。
int[] sizes = new int[]{16,14,12,10,8,6,4};
Font crFont = null;
SizeF crSize = new SizeF();
for (int i=0 ;i<7; i++)
{
crFont = new Font("arial", sizes[i],
FontStyle.Bold);
crSize = grPhoto.MeasureString(Copyright,
crFont);
if((ushort)crSize.Width < (ushort)phWidth)
break;
}
由于所有照片的高度都不同,我们将位置确定在图像底部 5% 的位置。我们将使用 Copyright
string
的高度来确定绘制 string
的合适 y 坐标。通过计算图像的中心来确定其 x 坐标,然后定义一个 StringFormat
对象并将 StringAlignment
设置为 Center
(居中)。
int yPixlesFromBottom = (int)(phHeight *.05);
float yPosFromBottom = ((phHeight -
yPixlesFromBottom)-(crSize.Height/2));
float xCenterOfImg = (phWidth/2);
StringFormat StrFormat = new StringFormat();
StrFormat.Alignment = StringAlignment.Center;
现在我们有了所有必需的定位坐标,创建一个 SolidBrush
(实心画笔),其 Color
(颜色)为 60% 的黑色(alpha 值为 153)。将 Copyright string
绘制在合适的位置,向右偏移 1 像素,向下偏移 1 像素。此偏移量将创建阴影效果。重复此过程,使用白色的 Brush
(画笔)将相同的文本绘制在先前绘制的 string
(字符串)的正上方。
SolidBrush semiTransBrush2 =
new SolidBrush(Color.FromArgb(153, 0, 0,0));
grPhoto.DrawString(Copyright,
crFont,
semiTransBrush2,
new PointF(xCenterOfImg+1,yPosFromBottom+1),
StrFormat);
SolidBrush semiTransBrush = new SolidBrush(
Color.FromArgb(153, 255, 255, 255));
grPhoto.DrawString(Copyright,
crFont,
semiTransBrush,
new PointF(xCenterOfImg,yPosFromBottom),
StrFormat);
![]() |
步骤 #2 - 水印图像
基于先前修改的照片创建一个 Bitmap
。将此 Bitmap
加载到一个新的 Graphic
对象中。
Bitmap bmWatermark = new Bitmap(bmPhoto);
bmWatermark.SetResolution(
imgPhoto.HorizontalResolution,
imgPhoto.VerticalResolution);
Graphics grWatermark =
Graphics.FromImage(bmWatermark);
为了实现半透明水印,我们将应用两次颜色操作,方法是定义一个 ImageAttributes
对象并设置其两个属性。操作水印图像的第一步是将背景颜色替换为透明色 (Alpha=0, R=0, G=0, B=0
)。为此,我们将使用 Colormap
(颜色映射)并定义一个 RemapTable
(重映射表)。如前所示,我的水印的背景色设置为 100% 绿色 - 这将是我们搜索并替换为透明的颜色。
ImageAttributes imageAttributes =
new ImageAttributes();
ColorMap colorMap = new ColorMap();
colorMap.OldColor=Color.FromArgb(255, 0, 255, 0);
colorMap.NewColor=Color.FromArgb(0, 0, 0, 0);
ColorMap[] remapTable = {colorMap};
imageAttributes.SetRemapTable(remapTable,
ColorAdjustType.Bitmap);
第二次颜色操作用于更改水印的不透明度。这是通过应用一个包含 RGBA 空间坐标的 5x5 矩阵来完成的。通过将第 3 行和第 3 列设置为 0.3f,我们实现了一定程度的不透明度。结果是水印稍微显示出下面的图像。
float[][] colorMatrixElements = {
new float[] {1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
new float[] {0.0f, 1.0f, 0.0f, 0.0f, 0.0f},
new float[] {0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
new float[] {0.0f, 0.0f, 0.0f, 0.3f, 0.0f},
new float[] {0.0f, 0.0f, 0.0f, 0.0f, 1.0f}
};
ColorMatrix wmColorMatrix = new
ColorMatrix(colorMatrixElements);
imageAttributes.SetColorMatrix(wmColorMatrix,
ColorMatrixFlag.Default,
ColorAdjustType.Bitmap);
将这两个颜色操作添加到 imageAttributes
对象后,我们现在可以将水印绘制在照片的右上角。我们将图像向下偏移 10 像素,向左偏移 10 像素。
int xPosOfWm = ((phWidth - wmWidth)-10);
int yPosOfWm = 10;
grWatermark.DrawImage(imgWatermark,
new Rectangle(xPosOfWm,yPosOfWm,wmWidth,
wmHeight),
0,
0,
wmWidth,
wmHeight,
GraphicsUnit.Pixel,
imageAttributes);
![]() |
最后一步是将原始图像替换为新的 Bitmap
,释放两个 Graphic
对象,然后将此 Image
保存到文件系统。
imgPhoto = bmWatermark;
grPhoto.Dispose();
grWatermark.Dispose();
\\watermark_final.jpg",
imgPhoto.Save(WorkingDirectory + "
ImageFormat.Jpeg);
imgPhoto.Dispose();
imgWatermark.Dispose();
就是这样!编译项目,运行它,看看会发生什么!代码相当简单,如果一切都说得通,那么这些技术可以用于数百种不同的图像处理。可能性是无限的。
修订历史
- 2002 年 9 月 26 日 - 初次修订
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。