Go实现图片上添加水印的示例代码
目录
- 前言
- 一、准备工作
- 二、图片加水印
- 2.1 图片水印
- 2.1.1 打开主图
- 2.1.2 解码主图
- 2.1.3 打开水印图片
- 2.1.4 解码水印图片
- 2.1.5 计算缩放比例
- 2.1.6 创建新的图像
- 2.1.7 缩放水印图像
- 2.1.8 确定水印位置
- 2.1.9 绘制水印
- 2.1.10 绘制旋转水印(可选)
- 2.1.11 保存结果图像
- 2.1.12 完整代码和效果
- 总结
前言
在数字内容日益重要的今天,保护版权和标识来源变得关键。为图片添加水印有助于声明所有权、提升品牌认知度,并防止未经授权的使用。本文将介绍如何用Go语言实现图片水印,包括静态图片和带旋转、倾斜效果的文字水印,帮助您有效保护数字内容。我们将逐步解析关键步骤,确保清晰易懂。
一、准备工作
为了顺利实现图片水印功能,您需要完成以下几个准备步骤:
1.安装Go语言环境:确保您的开发环境中已经安装了Go语言,并具备基本的Go编程知识。
2.安装必要的库:
- golang.org/x/image/draw:支持高质量缩放及其他图像绘制操作。
- github.com/disintegration/imaging:提供简便的API用于图像变换,如旋转和倾斜。
3.准备图像资源:
- 主图 (Base Image):这是您想要添加水印的原始图像。它可以是任何您有权处理的图像文件。
- 水印图 (Watermark Image):这是将被放置在主图之上的图像,通常是一个透明背景的PNG文件,这样可以确保它不会遮挡主图的重要细节。
确保您拥有上述所有工具和资源后,就可以开始编写代码来实现图片水印功能了。接下来的章节将逐步指导您如何加载主图、应用水印图并保存最终结果。
二、图片加水印
2.1 图片水印
2.1.1 打开主图
首先,我们需要打开并读取主图文件。这一步确保了程序能够访问到用户想要处理的原始图像。
// 打开主图文件 mainImageFile, err := os.Open("main.png") if err != nil { log.Fatalf("Failed to open main image: %v", err) } defer mainImageFile.Close()
2.1.2 解码主图
接下来,从输入流中读取原始图像并解码它。如果解码过程中出现问题,程序将返回错误信息。这里我们使用image.Decode函数自动识别图像格式。
mainImageFile, err := os.Open("main.png") if err != nil { log.Fatalf("Failed to open main image: %v", err) } defer mainImageFile.Close()
2.1.3 打开水印图片
然后,我们需要打开水印图片文件。与主图类似,我们也需要确保能够正确读取和解码水印图像。
// 打开水印图片 watermarkImageFile, err := os.Open("logo.png") // 可以替换为其他图片文件名 if err != nil { log.Fatalf("Failed to open watermark image: %v", err) } defer watermarkImageFile.Close()
2.1.4 解码水印图片
接下来,从输入流中读取水印图像并解码它。如果解码过程中出现问题,程序将返回错误信息。这里我们再次使用image.Decode函数自动识别图像格式。
// 解码水印 watermarkImage, _, err := image.Decode(watermarkImageFile) if err != nil { log.Fatalf("Failed to decode watermark image: %v", err) }
2.1.5 计算缩放比例
为了保证水印不会过于显眼或遮挡过多内容,根据原始图像的尺寸计算水印的最大宽度和高度。通常,我们会设定最大值为原始图像宽高的25%。然后基于这些最大值计算出适当的缩放比例。
// 获取主图和水印的边界矩形 mainImageBounds := mainImage.Bounds() watermarkImageBounds := watermarkImage.Bounds() // 计算水印的最大尺寸 maxWatermarkWidth := int(float64(mainImageBounds.Max.X) * 0.25) // 最大宽度为主图宽度的25% maxWatermarkHeight := int(float64(mainImageBounds.Max.Y) * 0.25) // 最大高度为主图高度的25% // 计算水印的缩放比例 scale := 1.0 if watermarkImageBounds.Max.X > maxWatermarkWidth || watermarkImageBounds.Max.Y > maxWatermarkHeight { scale = math.Min( float64(maxWatermarkWidth)/float64(watermarkImageBounds.Max.X), float64(maxWatermarkHeight)/float64(watermarkImageBounds.Max.Y), ) } // 应用缩放比例 watermarkWidth := int(float64(watermarkImageBounds.Max.X) * scale) watermarkHeight := int(float64(watermarkImageBounds.Max.Y) * scale)
2.1.6 创建新的图像
创建一个新的RGBA图像,其大小与原始图像相同,并将原始图像复制到这个新图像中。
// 创建一个新的图像,大小与主图相同 resultImage := image.NewRGBA(mainImageBounds) // 将主图复制到新图像中 draw.Draw(resultImage, mainImageBounds, mainImage, mainImageBounds.Min, draw.Src)
2.1.7 缩放水印图像
根据前面计算的缩放比例调整水印图像的大小。我们可以使用golang.org/x/image/draw包中的draw.CatmullRom.Scale方法来进行高质量缩放。
// 创建一个用于存放缩放后水印的新图像 resizedwatermarkImage := image.NewRGBA(image.Rect(0, 0, watermarkWidth, watermarkHeight)) // 使用高质量缩放算法缩放水印图像 draw.CatmullRom.Scale(resizedWatermarkImage, resizedWatermarkImage.Bounds(), watermarkImage, watermarkImageBounds, draw.Over, nil)
2.1.8 确定水印位置
根据用户提供的参数确定水印应该放置的位置,例如左上角、右上角等。对于每个预设的位置,我们计算出相应的坐标点。这里仅给出右下角的例子:
// 引入 position 变量,并赋值为一个有效的水印位置常量 position := "left_top" // 假设使用 "left_top" 作为示例 // 计算水印放置的位置 var watermarkX, watermarkY int switch pos编程客栈ition { case "left_top": watermarkX = int(float64(mainImageBounds.Max.X)android * 0.02) // 2% of the width watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02) // 2% of the height case "right_top": watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth // 98% of the width minus watermark width watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02) // 2% of the height case "left_bottom": watermarkX = int(float64(mainImageBounds.Max.X) * 0.02) // 2% of the width watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 98% of the height minus watermark height case "right_bottom": watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth // 98% of the width minus watermark width watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 98% of the height minus watermark height default: log.Fatalf("Invalid watermark position: %v", position) }
2.1.9 绘制水印
最后,使用draw.Draw方法将调整后的水印绘制到新图像的指定位置。
// 将水印绘制到新图像的指定位置 draw.Draw(resultImage, image.Rectangle{ Min: image.Point{X: watermarkX, Y: watermarkY}, Max: image.Point{X: watermarkX +编程 watermarkWidth, Y: watermarkY + watermarkHeight}, }, resizedWatermarkImage, image.Point{X: 0, Y: 0}, draw.Over)
2.1.10 绘制旋转水印(可选)
为了让水印更加多样化,可以引入旋转或倾斜的效果。这可以通过创建一个仿射变换矩阵并应用于文字图像来完成。以下是实现旋转功能的代码片段:
// 创建一个新的图像,大小与水印相同 rotatedWatermarkImage := image.NewRGBA(resizedWatermarkImage.Bounds()) // 引入 rotation 变量,并赋值为一个有效的旋转角度(度数) rotation := 45.0 // 假设使用 45.0 度作为示例 // 计算旋转角度的弧度 radians := rotation * math.Pi / 180.0 // 计算旋转后的中心点 centerX := float64(watermarkWidth) / 2.0 centerY := float64(watermarkHeight) / 2.0 // 遍历每个像素点并应用旋转 for y := 0; y < watermarkHeight; y++ { for x := 0; x < watermarkWidth; x++ { // 将像素点转换为相对于中心点的坐标 relX := float64(x) - centerX relY := float64(y) - centerY // 应用旋转矩阵 newX := relX*math.Cos(radians) - relY*math.Sin(radians) newY := relX*math.Sin(radians) + relY*math.Cos(radians) // 将旋转后的坐标转换回图像坐标 newX += centerX newY += centerY // 如果旋转后的坐标在图像范围内,则绘制像素 if newX >= 0 && newX < float64(watermarkWidth) && newY >= 0 && newY < float64(watermarkHeight) { rotatedWatermarkImage.Set(int(newX), int(newY), resizedWatermarkImage.At(x, y)) } } } // 将旋转后的水印绘制到新图像的指定位置 draw.Draw(resultImage, image.Rectangle{Min: image.Point{X: watermarkX, Y: watermarkY}, Max: image.Point{X: watermarkX + watermarkWidth, Y: watermarkY + watermarkHeight}}, rotatedWatermarkImage, image.Point{X: 0, Y: 0}, draw.Over)
2.1.11 保存结果图像
根据原始图像的格式(如PNG或JPEG),将带有水印的新图像编码并保存到内存中的缓冲区,然后再写入磁盘。
// 保存结果图像到内存 var buffer bytes.Buffer switch fileExtension { case ".png": err = png.Encode(&buffer, resultImage) case ".jpg", ".jpeg": err = jpeg.Encode(&buffer, resultImage, nil) default: log.Fatalf("Unsupported file extension: %v", fileExtension) } if err != nil { log.Fatalf("Failed to encode image: %v", err) } // 保存结果图像到文件 outputFileName := "output" + fileExtension outputFile, err := os.Create(outputFileName) if err != nil { log.Fatalf("Failed to create output file: %v", err) } defer outputFile.Close() // 将内存中的图像数据写入文件 _, err = buffer.WriteTo(outputFile) if err != nil { log.Fatalf("Failed to write to output file: %v", err) }
2.1.12 完整代码和效果
package main import ( "bytes" "golang.org/x/image/draw" "image" "image/jpeg" "image/png" "log" "os" "path/filepath" ) func main() { // 打开主图文件 mainImageFile, err := os.Open("main.png") if err != nil { log.Fatalf("Failed to open main image: %v", err) } defer mainImageFile.Close() // 获取文件扩展名 fileExtension := filepath.Ext(mainImageFile.Name()) // 解码主图 mainImage, _, err := image.Decode(mainImageFile) if err != nil { log.Fatalf("Failed to decode main image: %v", err) } // 打开水印图片 watermarkImageFile, err := os.Open("logo.png") // 你可以将 "logo.png" 替换为 "logo.jpg" 或其他图片文件名 if err != nil { log.Fatalf("Failed to open watermark image: %v", err) } defer watermarkImageFile.Close() // 解码水印 watermarkImage, _, err := image.Decode(watermarkImageFile) if err != nil { log.Fatalf("Failed to decode watermark image: %v", err) } // 获取主图和水印的边界矩形 mainImageBounds := mainImage.Bounds() watermarkImageBounds := watepythonrmarkImage.Bounds() // 计算水印的最大尺寸 maxWatermarkWidth := int(float64(mainImageBounds.Max.X) * 0.20) // 你可以将 "0.20" 替换为 "0.15" 或其他值 maxWatermarkHeight := int(float64(mainImageBounds.Max.Y) * 0.20) // 你可以将 "0.20" 替换为 "0.15" 或其他值 // 计算水印的缩放比例 watermarkWidth := watermarkImageBounds.Max.X watermarkHeight := watermarkImageBounds.Max.Y // 计算缩放比例 android scale := 1.0 if watermarkWidth > maxWatermarkWidth { scale = float64(maxWatermarkWidth) / float64(watermarkWidth) } if watermarkHeight > maxWatermarkHeight { if scale > float64(maxWatermarkHeight)/float64(watermarkHeight) { scale = float64(maxWatermarkHeight) / float64(watermarkHeight) } } // 应用缩放比例 watermarkWidth = int(float64(watermarkWidth) * scale) watermarkHeight = int(float64(watermarkHeight) * scale) // 创建一个新的图像,大小与主图相同 resultImage := image.NewRGBA(mainImageBounds) // 将主图复制到新图像中 draw.Draw(resultImage, mainImageBounds, mainImage, mainImageBounds.Min, draw.Src) // 缩放水印图像 resizedWatermarkImage := image.NewRGBA(image.Rect(0, 0, watermarkWidth, watermarkHeight)) draw.NearestNeighbor.Scale(resizedWatermarkImage, resizedWatermarkImage.Bounds(), watermarkImage, watermarkImageBounds, draw.Over, nil) // 引入 position 变量,并赋值为一个有效的水印位置常量 position := "left_top" // 假设使用 "left_top" 作为示例 // 计算水印放置的位置 var watermarkX, watermarkY int switch position { case "left_top": watermarkX = int(float64(mainImageBounds.Max.X) * 0.02) // 宽度的2% watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02) // 高度的2% case "right_top": watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth // 宽度的98%减去水印宽度 watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02) // 高度的2% case "left_bottom": watermarkX = int(float64(mainImageBounds.Max.X) * 0.02) // 宽度的2% watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 高度的98%减去水印高度 case "right_bottom": watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth // 宽度的98%减去水印宽度 watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 高度的98%减去水印高度 default: log.Fatalf("Invalid watermark position: %v", position) } // 将水印绘制到新图像的指定位置 draw.Draw(resultImage, image.Rectangle{Min: image.Point{X: watermarkX, Y: watermarkY}, Max: image.Point{X: watermarkX + watermarkWidth, Y: watermarkY + watermarkHeight}}, resizedWatermarkImage, image.Point{X: 0, Y: 0}, draw.Over) // 保存结果图像到内存 var buffer bytes.Buffer switch fileExtension { case ".png": err = png.Encode(&buffer, resultImage) case ".jpg", ".jpeg": err = jpeg.Encode(&buffer, resultImage, nil) default: log.Fatalf("Unsupported file extension: %v", fileExtension) } if err != nil { log.Fatalf("Failed to encode image: %v", err) } // 保存结果图像到文件 outputFile, err := os.Create("output" + fileExtension) if err != nil { log.Fatalf("Failed to create output file: %v", err) } defer outputFile.Close() // 添加文件关闭操作 // 将内存中的图像数据写入文件 _, err = buffer.WriteTo(outputFile) if err != nil { log.Fatalf("Failed to write to output file: %v", err) } }
总结
通过以上步骤,我们不仅完成了在图片上添加静态图片水印的功能实现,还增加了旋转、倾斜的水印功能,使得生成的水印更加多样化和个性化。您可以根据自己的需求进一步优化代码,比如支持更多的水印位置选项,或者允许用户上传自定义水印图片。希望这篇文章能帮助您理解和实现这一常见但非常有用的功能。如果您有任何问题或遇到困难,请随时查阅相关文档或寻求社区的帮助。
到此这篇关于Go实现图片上添加水印的示例代码的文章就介绍到这了,更多相关Go 图片上添加水印内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论