基于opencv的图片加水印实现方案

加水印应该是个很常见的需求,但是网上找的代码,都感觉不太完善。记录下自己搞出来的一个方案

水印有几个需求:

  1. 中文文字水印
  2. 文字倾斜
  3. 满图都是,而不是只有一个地方
  4. 水印文字所在之处完全展示水印

实现思路

准备水印图

我是这么做的,先手工生成一张水印图,尺寸比较小,约100*100。然后也把文字旋转一定角度(当然如果想做随机角度,也是可以的,代码再复杂点处理就好,不影响这个思路),生成这个一张图片,并且底色选择纯黑

然后用opencv将水印图处理出一张二值图

这个也不难,底色纯黑,这个很好做,用Threshold很容易实现。

然后用水印图和原图叠加

核心就是要用copyTo(),然后要输入mask,二值图的作用就是做掩码,这样就可以实现完美的水印添加效果。

其他细节

因为水印图通常是远小于原图的(其实也可以采用其他方案。总之核心就是要有水印图和对应的二值图),如何实现满图添加水印呢?

思路

  1. 先设置一个水印的间距,比如两百个像素点
  2. 然后通过水印图的大小和间距的值,计算生成一个尺寸大于等于原图的纯水印图(同时也要有二值图)
  3. 然后裁剪成和原图一样大
  4. 然后用copyTo()叠加即可。

这样性能很高,go语言实现处理一张图片不超过10ms。

go语言实现

思路都是通用的,基于opencv都能实现。

go 复制代码
func ImgWatermark(img gocv.Mat) {
	var Watermark gocv.Mat
	var WatermarkBW gocv.Mat
	// 不同通道的图片使用不同的水印
	if img.Channels() == 3 {
		// 判断是否需要初始化
		if g.Watermark3 == nil { // g. 是我设置的全局变量,因为不需要每次都加载水印。加载一次到内存即可
			mat := gocv.IMRead(`watermark.jpg`, gocv.IMReadColor)
			g.Watermark3 = &mat

			// 取得水印图片的二值图
			watermarkGray3 := gocv.NewMat()
			gocv.CvtColor(*g.Watermark3, &watermarkGray3, gocv.ColorBGRToGray)
			// 取得二值图的黑白图
			BW3 := gocv.NewMat()
			g.WatermarkBW3 = &BW3
			gocv.Threshold(watermarkGray3, g.WatermarkBW3, 30, 255, gocv.ThresholdBinary)
			watermarkGray3.Close()
		}
		Watermark = *g.Watermark3
		WatermarkBW = *g.WatermarkBW3
	} else if img.Channels() == 4 {
		// 判断是否需要初始化
		if g.Watermark4 == nil {
			mat := gocv.IMRead(`watermark.jpg`, gocv.IMReadColor)
			g.Watermark4 = &mat
			gocv.Merge([]gocv.Mat{mat, gocv.NewMatWithSizeFromScalar(gocv.NewScalar(255, 255, 255, 255), mat.Rows(), mat.Cols(), gocv.MatTypeCV8UC1)}, g.Watermark4)
			// 取得水印图片的二值图
			watermarkGray4 := gocv.NewMat()
			gocv.CvtColor(*g.Watermark4, &watermarkGray4, gocv.ColorBGRToGray)
			// 取得二值图的黑白图
			BW4 := gocv.NewMat()
			g.WatermarkBW4 = &BW4
			gocv.Threshold(watermarkGray4, g.WatermarkBW4, 30, 255, gocv.ThresholdBinary)
			watermarkGray4.Close()
		}
		Watermark = *g.Watermark4
		WatermarkBW = *g.WatermarkBW4
	} else if img.Channels() == 1 {
		// 判断是否需要初始化
		if g.Watermark1 == nil {
			mat := gocv.IMRead(`watermark.jpg`, gocv.IMReadGrayScale) // 直接读取黑白
			g.Watermark1 = &mat
			// 取得二值图的黑白图
			BW1 := gocv.NewMat()
			g.WatermarkBW1 = &BW1
			gocv.Threshold(mat, g.WatermarkBW1, 30, 255, gocv.ThresholdBinary)
		}
		Watermark = *g.Watermark1
		WatermarkBW = *g.WatermarkBW1
	} else {
		fmt.Println("图片通道数不支持", img.Channels())
		return
	}
	waterCol := Watermark.Cols()
	waterRow := Watermark.Rows()
	// 为了水印不要那么密,这里设置一个间距
	spacing := 200 // 水印的间距

	waterColAndSpacing := waterCol + spacing
	watreRowAndSpacing := waterRow + spacing

	// 计算需要添加水印的次数
	watermarkHugeRowTimes := int(math.Ceil(float64(img.Rows()) / float64(watreRowAndSpacing))) //向上取整
	watermarkHugeColTimes := int(math.Ceil(float64(img.Cols()) / float64(waterColAndSpacing)))

	imgRect := image.Rectangle{} //每次取图像中哪一块区域的数据结构
	var croppedMat gocv.Mat      // 每次从原图像上裁剪和水印图像一样大小的一块

	newWater := Watermark
	newWaterBW := WatermarkBW
	// 循环一块块的给图像添加水印
	for i := 0; i < watermarkHugeColTimes; i++ {
		imgRect.Min.X = i * waterColAndSpacing
		imgRect.Max.X = imgRect.Min.X + waterCol
		if imgRect.Max.X >= img.Cols() { // 判断是否超出原图像的列数
			imgRect.Max.X = img.Cols()
		}
		for j := 0; j < watermarkHugeRowTimes; j++ {
			imgRect.Min.Y = j * watreRowAndSpacing
			imgRect.Max.Y = imgRect.Min.Y + waterRow
			if imgRect.Max.Y >= img.Rows() { // 判断是否超出原图像的行数
				imgRect.Max.Y = img.Rows()
			}
			croppedMat = img.Region(imgRect) // 原图像中一块区域的浅拷贝,修改它会连带修改原图像

			// 判断裁剪的图像大小是否与水印图像大小一致,不一致则需要重新裁剪
			if croppedMat.Rows() != Watermark.Rows() || croppedMat.Cols() != Watermark.Cols() {
				newRect := image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: croppedMat.Cols(), Y: croppedMat.Rows()}}
				newWater = Watermark.Region(newRect)
				newWaterBW = WatermarkBW.Region(newRect)
			}

			newWater.CopyToWithMask(&croppedMat, newWaterBW) // 用水印图覆盖原图像
		}
	}
}
相关推荐
whoarethenext37 分钟前
C++ OpenCV 学习路线图
c++·opencv·学习
jndingxin15 小时前
OpenCV CUDA模块图像处理------创建一个模板匹配(Template Matching)对象函数createTemplateMatching()
图像处理·人工智能·opencv
吴声子夜歌16 小时前
OpenCV——Mat类及常用数据结构
数据结构·opencv·webpack
新知图书21 小时前
OpenCV为图像添加边框
人工智能·opencv·计算机视觉
空中湖1 天前
免费批量图片格式转换工具
图像处理·python·程序人生
东皇太星2 天前
SIFT算法详细原理与应用
图像处理·算法·计算机视觉
audyxiao0012 天前
计算机视觉顶刊《International Journal of Computer Vision》2025年5月前沿热点可视化分析
图像处理·人工智能·opencv·目标检测·计算机视觉·大模型·视觉检测
..活宝..2 天前
【Emgu CV教程】11.2、Scharr边缘检测
图像处理·计算机视觉·c#·emgu cv·图像分析
whoarethenext2 天前
使用 C/C++ 和 OpenCV 实现滑动条控制图像旋转
c语言·c++·opencv
SuperW2 天前
Opencv中的copyto函数
人工智能·opencv·计算机视觉