首页 > 其他分享 >golang 为图片加水印

golang 为图片加水印

时间:2023-01-15 20:44:57浏览次数:52  
标签:assert func err nil image golang NoError 加水 图片

前段时间一个需求涉及到给图片加水印,考虑图片安全性,决定放在后端加水印。记录一下代码。

思路

思路是先为水印文字生成一个仅包含水印文字的图片,把这个图片倾斜一定角度 (一般水印都是倾斜的),之后把倾斜的水印文字图片贴在原图上,得到最终的水印图片。

代码

// watermark.go
package main

import (
	"image"
	"image/color"
	"image/draw"
	"github.com/disintegration/imaging"
	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/gomono"
	"github.com/pkg/errors"
)

func AddWatermarkForImage(oriImage image.Image, uid string) (*image.RGBA, error) {
	watermarkedImage := image.NewRGBA(oriImage.Bounds())
	draw.Draw(watermarkedImage, oriImage.Bounds(), oriImage, image.Point{}, draw.Src)

	watermark, err := MakeImageByText(uid, color.Transparent)
	if err != nil {
		return nil, err
	}
	rotatedWatermark := imaging.Rotate(watermark, 30, color.Transparent)

	x, y := 0, 0
	for y <= watermarkedImage.Bounds().Max.Y {
		for x <= watermarkedImage.Bounds().Max.X {
			offset := image.Pt(x, y)
			draw.Draw(watermarkedImage, rotatedWatermark.Bounds().Add(offset), rotatedWatermark, image.Point{}, draw.Over)
			// 稀疏一点, 稍微提升点速度
			x += rotatedWatermark.Bounds().Dx() * 2
		}
		y += rotatedWatermark.Bounds().Dy()
		x = 0
	}
	return watermarkedImage, nil
}

// MakeImageByText 根据文本内容制作一个仅包含该文本内容的图片
func MakeImageByText(text string, bgColor color.Color) (image.Image, error) {
	fontSize := float64(72)
	freetypeCtx := MakeFreetypeCtx(fontSize)

	width, height := int(fontSize)*len(text), int(fontSize)*2
	rgbaRect := image.NewRGBA(image.Rect(0, 0, width, height))

	// 仅当非透明时才做一次额外的渲染
	if bgColor != color.Transparent {
		bgUniform := image.NewUniform(bgColor)
		draw.Draw(rgbaRect, rgbaRect.Bounds(), bgUniform, image.Pt(0, 0), draw.Src)
	}

	freetypeCtx.SetClip(rgbaRect.Rect)
	freetypeCtx.SetDst(rgbaRect)
	pt := freetype.Pt(0, int(freetypeCtx.PointToFixed(fontSize)>>6))
	_, err := freetypeCtx.DrawString(text, pt)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return rgbaRect, nil
}

// MustParseFont 通过单测来保证该方法必不会 panic
func MustParseFont() *truetype.Font {
	ft, err := freetype.ParseFont(gomono.TTF)
	if err != nil {
		panic(err)
	}
	return ft
}

func MakeFreetypeCtx(fontSize float64) *freetype.Context {
	fontColor := color.RGBA{R: 0, G: 0, B: 0, A: 50}
	fontColorUniform := image.NewUniform(fontColor)

	freetypeCtx := freetype.NewContext()
	freetypeCtx.SetDPI(100)
	freetypeCtx.SetFont(MustParseFont())
	freetypeCtx.SetFontSize(fontSize)
	freetypeCtx.SetSrc(fontColorUniform)
	freetypeCtx.SetHinting(font.HintingNone)
	return freetypeCtx
}

// watermark_test.go
package main

import (
	"image"
	"image/color"
	"image/draw"
	"image/jpeg"
	"image/png"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestMakeImageByText(t *testing.T) {
	t.Run("bg white", func(t *testing.T) {
		img, err := MakeImageByText("hello", color.White)
		assert.NoError(t, err)

		helloPng, err := os.Create("hello_white.png")
		assert.NoError(t, err)
		defer helloPng.Close()

		err = png.Encode(helloPng, img)
		assert.NoError(t, err)

		helloJpeg, err := os.Create("hello_white.jpeg")
		assert.NoError(t, err)
		defer helloJpeg.Close()

		err = jpeg.Encode(helloJpeg, img, nil)
		assert.NoError(t, err)
	})
	t.Run("bg transparent", func(t *testing.T) {
		img, err := MakeImageByText("hello", color.Transparent)
		assert.NoError(t, err)

		helloPng, err := os.Create("hello_transparent.png")
		assert.NoError(t, err)
		defer helloPng.Close()

		err = png.Encode(helloPng, img)
		assert.NoError(t, err)

		helloJpeg, err := os.Create("hello_transparent.jpeg")
		assert.NoError(t, err)
		defer helloJpeg.Close()

		// jpeg 没有 alpha 通道, 所以会是全黑的
		err = jpeg.Encode(helloJpeg, img, nil)
		assert.NoError(t, err)
	})
}

func TestMustParseFont(t *testing.T) {
	ft := MustParseFont()
	assert.NotNil(t, ft)
}

func BaseImageForTest() image.Image {
	rgbaRect := image.NewRGBA(image.Rect(0, 0, 3000, 2000))
	bgColor := color.RGBA{R: 0xff, G: 0, B: 0, A: 0xff}
	bg := image.NewUniform(bgColor)
	draw.Draw(rgbaRect, rgbaRect.Bounds(), bg, image.Pt(0, 0), draw.Src)
	return rgbaRect
}

func TestWassObject_AddWatermark(t *testing.T) {
	baseImg := BaseImageForTest()

	watermarkedImg, err := AddWatermarkForImage(baseImg, "hello.world")
	assert.NoError(t, err)

	helloWatermarkedJpeg, err := os.Create("hello_watermarked.jpeg")
	assert.NoError(t, err)
	defer helloWatermarkedJpeg.Close()

	err = jpeg.Encode(helloWatermarkedJpeg, watermarkedImg, nil)
	assert.NoError(t, err)
}

效果

  1. 最开始的思路是,计算出原图的对角线长度,制作出一个长宽均为对角线长度的 mask,把水印文字填充在 mask 上,旋转 mask,再把旋转后的 mask 盖在原图上。后发现因为旋转的是一个大图,所以旋转的耗时比较久,对于一些比较大的原始图片,旋转可能花个五六秒 (2核4G的机器)。因此改为了只旋转那个比较小的水印文字图。
  2. 对图片的处理本质是一个矩阵运算,因此计算量还是比较大的,cpu 和内存的占用量会比较大,功能上线前最好做一下压测

参考链接

https://johnpili.com/generate-text-to-image-in-go/

https://blog.csdn.net/diandianxiyu_geek/article/details/119546482

标签:assert,func,err,nil,image,golang,NoError,加水,图片
From: https://www.cnblogs.com/XiaoXiaoShuai-/p/17054080.html

相关文章

  • 【科学文库】书籍下载的曲折之路--福昕阅读器的图片接口
    一、前言科学文库使用福昕的在线阅读模式!之前我写过一个JS脚本用来获取科学文库的电子书,但是随着接口的失效,再维护那个脚本便没有多大意义。在通过对之前方法的重新梳理,......
  • 【转】PageOffice 在线打开 word 文件并添加水印
    一、服务器端方法插入水印1、插入文字水印WordDocumentdoc=newWordDocument();//添加水印,设置水印的内容doc.getWaterMark().setText("PageOffice开发平......
  • (转)Golang - new和make的使用和区别(示例详解)
    原文:https://www.cnblogs.com/beatleC/p/16081832.html前言:Go语言中new和make是内置函数,主要用来创建分配类型内存(相同点:堆上),其功能相似,却有本质区别。引入:变量的声明v......
  • (转)golang 函数
    原文:https://www.cnblogs.com/duoke360/p/15663943.htmlgolang函数简介函数的go语言中的一级公民,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称......
  • Python爬取图片
    Python爬取图片目标链接:https://pic.netbian.com/4kfengjing/思路:先通过首页的源码分析提取到子页面的链接,然后通过子页面的链接的源码来提取到图片的下载链接,我们再访问......
  • Flex项目release后图片资源不见了–图片资源采用嵌入与不嵌入的区别
    ​近期项目发现,在使用release发布项目包,图片找不到了,而debug下面又有图片。后来发现,原来图片被另外一个组件嵌入了。下面例子展示了组件嵌入和不嵌入图片的区别<?xmlversi......
  • golang实现的一个小游戏–猜数字
    随机生成一个数字,输入一个数字看是否匹对,匹配则结速,反之提示是大了还是小了,代码如下:packagemainimport("bufio""fmt""math/rand""os""strconv""time")var......
  • flex 4.6 自定义Datagrid添加checkbox\图片等功能
    flex4.6中的 spark 的Datagrid相比于之前的mx有很大不同实现自定义<s:itemRenderer>详见代码~<s:DataGridid="dg"left="14"right="10"top="35"bottom="9"d......
  • golang交叉编译
    先设置为运行平台,再设置内核类型,再编译产出常见平台有:android、darwin、linux、windows内核类型提供uname-a可以查看:amd64、arm64、386  setGOOS=linuxsetGOARC......
  • Linux下图片处理
    Linux下图片处理   图片是指由图形、图像等构成的平面媒体。图片的格式很多,但总体上可以分为点阵图和矢量图两大类,我们常用BMP、JPG等格式都是点阵图形,而SWF、CDR、AI......