一、前言
在数字图像处理中,为图片添加水印是一项常见且重要的技术。无论是版权保护、品牌宣传还是防止未经授权的使用,水印都能发挥重要作用。OpenCV作为一款强大的计算机视觉库,提供了丰富的功能来实现各种水印效果。本教程将详细介绍如何使用OpenCV为图像添加文字水印和图片水印。
二、环境准备
在开始之前,请确保已安装以下环境:
-
Python 3.x
-
OpenCV库(可通过
pip install opencv-python
安装) -
NumPy库(OpenCV依赖)
# 导入必要的库
import cv2 # OpenCV库,用于图像处理
import numpy as np # NumPy库,用于数值计算print("环境准备完成,OpenCV版本:", cv2.__version__)
三、文字水印实现
3.1 基本文字水印
OpenCV提供了cv2.putText()
函数来在图像上添加文字。我们先来看这个函数的基本用法:
cv2.putText(img, text, org, fontFace, fontScale, color, thickness=None, lineType=None, bottomLeftOrigin=None)
参数详解:
-
img
: 要添加文字的图像(numpy数组) -
text
: 要添加的文字字符串 -
org
: 文字左下角的坐标(对于bottomLeftOrigin=False
的情况),格式为(x, y) -
fontFace
: 字体类型,常用的有:-
cv2.FONT_HERSHEY_SIMPLEX
: 普通大小无衬线字体 -
cv2.FONT_HERSHEY_PLAIN
: 小号无衬线字体 -
cv2.FONT_HERSHEY_DUPLEX
: 普通大小无衬线字体(比SIMPLEX更复杂) -
cv2.FONT_HERSHEY_COMPLEX
: 普通大小衬线字体 -
cv2.FONT_HERSHEY_TRIPLEX
: 普通大小衬线字体(比COMPLEX更复杂) -
cv2.FONT_HERSHEY_COMPLEX_SMALL
: 小号COMPLEX字体 -
cv2.FONT_HERSHEY_SCRIPT_SIMPLEX
: 手写风格字体 -
cv2.FONT_HERSHEY_SCRIPT_COMPLEX
: 更复杂的手写风格字体
-
-
fontScale
: 字体缩放因子,影响字体大小 -
color
: 文字颜色,BGR格式的元组,如(255, 0, 0)表示蓝色 -
thickness
: 文字线条粗细(可选,默认为1) -
lineType
: 线条类型(可选,默认为cv2.LINE_8) -
bottomLeftOrigin
: 如果为True,org参数表示文字的左上角而非左下角(可选,默认为False)
示例代码:
# 读取图像
image = cv2.imread('input.jpg')# 设置水印文字参数
text = "Sample Watermark"
position = (50, 50) # 从图像左上角开始的坐标
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1
color = (255, 0, 0) # 蓝色
thickness = 2
line_type = cv2.LINE_AA # 抗锯齿线型# 添加文字水印
cv2.putText(image, text, position, font, font_scale, color, thickness, line_type)# 保存结果
cv2.imwrite('output_with_text.jpg', image)
3.2 透明文字水印
为了使水印更加美观且不影响原图内容,我们可以创建半透明的水印效果:
def add_transparent_watermark(input_path, output_path):"""添加半透明文字水印函数参数:input_path (str): 输入图片路径output_path (str): 输出图片路径功能:1. 创建原始图像的副本2. 在副本上添加白色文字3. 将副本与原图按透明度混合4. 保存输出图片"""# 读取原始图像original_image = cv2.imread(input_path)if original_image is None:print(f"错误:无法加载图片 {input_path}")return# 创建原始图像的副本# overlay将用于添加水印,output将保存最终结果overlay = original_image.copy()output = original_image.copy()# 水印文字参数设置watermark_text = "WATERMARK" # 水印文字内容text_position = (50, 100) # 文字位置(x,y)# 使用较复杂的字体# cv2.FONT_HERSHEY_COMPLEX: 普通大小衬线字体font_type = cv2.FONT_HERSHEY_COMPLEX font_size = 1.5 # 较大的字体尺寸text_color = (255, 255, 255) # 白色文字thickness = 3 # 较粗的线条# 在overlay上添加文字(此时文字是完全不透明的)cv2.putText(overlay,watermark_text,text_position,font_type,font_size,text_color,thickness,cv2.LINE_AA)# 设置透明度# alpha=0.6表示水印60%不透明,原图40%可见alpha = 0.6 """cv2.addWeighted()函数参数详解:src1: 第一个输入数组(overlay)alpha: 第一个数组的权重src2: 第二个输入数组(output)beta: 第二个数组的权重gamma: 标量,添加到每个和的标量dst: 输出数组dtype: 输出数组的可选深度"""# 将overlay和output按透明度混合cv2.addWeighted(overlay, # 有水印的图像alpha, # 水印图像的权重output, # 原始图像1 - alpha, # 原始图像的权重0, # 添加到每个和的标量output # 输出图像)# 保存结果if cv2.imwrite(output_path, output):print(f"透明水印添加成功,结果已保存到 {output_path}")else:print(f"错误:无法保存图片到 {output_path}")# 使用示例
add_transparent_watermark(input_path="input.jpg",output_path="output_transparent.jpg"
)
3.3 多行文字与文字属性获取
有时我们需要添加多行文字或计算文字占据的空间:
def add_multiline_text(image, text_lines=["LINE 1", "LINE 2", "LINE 3"], position=(20, 50), font=cv2.FONT_HERSHEY_PLAIN, font_scale=1.8, color=(0, 255, 0), line_spacing=40):x, y = positionfor line in text_lines:cv2.putText(image, line, (x, y), font, font_scale, color, 2, cv2.LINE_AA)y += line_spacingreturn image# 使用示例
image = cv2.imread('input.jpg')
watermarked = add_multiline_text(image)
cv2.imwrite('output_multiline.jpg', watermarked)
四、图片水印实现
除了文字水印,我们还可以将另一张图片作为水印添加到主图上。
4.1 基本图片水印
def add_image_watermark(background, watermark, position=(50, 50), alpha=0.4):h, w = watermark.shape[:2]roi = background[position[1]:position[1]+h, position[0]:position[0]+w]result = cv2.addWeighted(roi, 1, watermark, alpha, 0)background[position[1]:position[1]+h, position[0]:position[0]+w] = resultreturn background# 使用示例
background = cv2.imread('background.jpg')
watermark = cv2.imread('logo.png') # 水印图片路径# 调整水印大小
watermark = cv2.resize(watermark, (100, 50)) # 固定尺寸100x50像素watermarked = add_image_watermark(background, watermark)
cv2.imwrite('output_with_logo.jpg', watermarked)
4.2 平铺水印效果
有时我们需要在整个图片上平铺水印:
def add_tiled_watermark(background, watermark, scale=0.1, alpha=0.3):"""添加平铺水印效果参数:background: 背景图像watermark: 水印图像scale: 水印缩放比例alpha: 透明度"""# 调整水印大小h, w = background.shape[:2]watermark = cv2.resize(watermark, (0, 0), fx=scale, fy=scale)wh, ww = watermark.shape[:2]# 计算平铺的行列数rows = h // wh + 1cols = w // ww + 1# 创建水印平铺图像tiled = np.tile(watermark, (rows, cols, 1))tiled = tiled[:h, :w]# 混合图像result = cv2.addWeighted(background, 1, tiled, alpha, 0)return result# 使用示例
background = cv2.imread('background.jpg')
watermark = cv2.imread('logo.png')result = add_tiled_watermark(background, watermark, scale=0.2, alpha=0.2)
cv2.imwrite('output_tiled.jpg', result)
五、高级水印技术
5.1 斜角水印
斜角水印是一种以对角线方向重复排列的水印设计方式,它通过在图像上沿斜线方向(通常是45度角)均匀分布水印元素(文字或Logo),形成有规律的网格化视觉效果。
def add_diagonal_pattern_watermark(input_path, output_path):"""添加斜角重复水印函数参数:input_path (str): 输入图片路径output_path (str): 输出图片路径功能:1. 创建图像副本2. 计算图像对角线长度3. 沿对角线方向重复添加水印文字4. 与原图混合5. 保存结果"""# 读取原始图像image = cv2.imread(input_path)if image is None:print(f"错误:无法加载图片 {input_path}")return# 创建原始图像的副本overlay = image.copy()output = image.copy()# 获取图像尺寸height, width = image.shape[:2]print(f"图像尺寸: 宽度={width}, 高度={height}")# 水印文字参数watermark_text = "SAMPLE" # 水印文字内容font_type = cv2.FONT_HERSHEY_SCRIPT_SIMPLEX # 手写风格字体font_size = 1.8 # 较大的字体text_color = (200, 200, 200) # 浅灰色# 计算图像对角线长度(勾股定理)diagonal_length = int(np.sqrt(height**2 + width**2))print(f"图像对角线长度: {diagonal_length}像素")# 设置水印间距spacing = 120 # 水印之间的间隔(像素)# 计算需要多少个水印num_watermarks = diagonal_length // spacing + 1print(f"将添加约 {num_watermarks} 个水印")# 添加斜角水印for i in range(-num_watermarks, num_watermarks + 1):# 计算每条斜线的起始x坐标x_start = i * spacing# 沿斜线方向放置水印for j in range(0, diagonal_length, spacing):x = x_start + j # 当前x坐标y = j # 当前y坐标# 检查坐标是否在图像范围内if 0 <= x < width and 0 <= y < height:cv2.putText(overlay,watermark_text,(x, y), # 当前位置font_type,font_size,text_color,2, # 线条粗细cv2.LINE_AA)# 设置水印整体透明度alpha = 0.15 # 水印不透明度(15%)# 混合图像cv2.addWeighted(overlay, # 有水印的图像alpha, # 水印权重output, # 原始图像1 - alpha, # 原始图像权重0, # 标量值output # 输出图像)# 保存结果if cv2.imwrite(output_path, output):print(f"斜角水印添加成功,结果已保存到 {output_path}")else:print(f"错误:无法保存图片到 {output_path}")# 使用示例
add_diagonal_pattern_watermark(input_path="input.jpg",output_path="output_diagonal.jpg"
)
5.2 自适应水印位置
def add_adaptive_watermark(image, text, font, font_scale, color, alpha=0.5):"""根据图像内容自适应选择水印位置参数:image: 原始图像text: 水印文字font: 字体类型font_scale: 字体大小color: 文字颜色alpha: 透明度"""# 转换为灰度图gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 计算图像梯度grad_x = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3)grad_y = cv2.Sobel(gray, cv2.CV_32F, 0, 1, ksize=3)magnitude = np.sqrt(grad_x**2 + grad_y**2)# 获取文本尺寸(text_width, text_height), _ = cv2.getTextSize(text, font, font_scale, thickness=2)# 寻找梯度最小的区域放置水印min_magnitude = float('inf')best_position = (0, 0)for y in range(0, image.shape[0] - text_height, 10):for x in range(0, image.shape[1] - text_width, 10):region = magnitude[y:y+text_height, x:x+text_width]current_magnitude = np.mean(region)if current_magnitude < min_magnitude:min_magnitude = current_magnitudebest_position = (x, y + text_height) # putText使用左下角坐标# 添加水印overlay = image.copy()output = image.copy()cv2.putText(overlay, text, best_position, font, font_scale, color, 2, cv2.LINE_AA)cv2.addWeighted(overlay, alpha, output, 1 - alpha, 0, output)return output# 使用示例
image = cv2.imread('input.jpg')
watermarked = add_adaptive_watermark(image, "ADAPTIVE", cv2.FONT_HERSHEY_COMPLEX, 1.2, (0, 0, 255), 0.6)
cv2.imwrite('output_adaptive.jpg', watermarked)
六、水印安全与反去除
为了提高水印的安全性,防止轻易被去除,可以考虑以下技术:
-
频域水印:将水印嵌入到图像的频域中(如DCT或DWT变换域)
-
随机点水印:在图像中随机位置添加微小的像素变化
-
多重水印:结合文字和图片水印,在不同位置添加
def add_frequency_domain_watermark(image, watermark_text, strength=0.1):"""在频域添加简单文字水印参数:image: 原始图像watermark_text: 水印文字strength: 水印强度"""# 将图像转换为YCrCb颜色空间,只对亮度通道(Y)进行操作ycrcb = cv2.cvtColor(image, cv2.COLOR_BGR2YCrCb)y = ycrcb[:, :, 0].astype(np.float32)# 执行DCT变换dct = cv2.dct(y)# 创建水印模式watermark = np.zeros_like(y)cv2.putText(watermark, watermark_text, (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2, cv2.LINE_AA)watermark = cv2.resize(watermark, (dct.shape[1], dct.shape[0]))# 在频域添加水印dct_watermarked = dct + watermark * strength# 执行逆DCT变换y_watermarked = cv2.idct(dct_watermarked)# 将结果放回原图像ycrcb[:, :, 0] = np.clip(y_watermarked, 0, 255)watermarked = cv2.cvtColor(ycrcb, cv2.COLOR_YCrCb2BGR)return watermarked# 使用示例
image = cv2.imread('input.jpg')
watermarked = add_frequency_domain_watermark(image, "FREQ_DOMAIN", 0.05)
cv2.imwrite('output_frequency.jpg', watermarked)
七、总结
本教程详细介绍了使用OpenCV添加各种水印的方法,包括:
-
基本文字水印和透明文字水印
-
多行文字水印和文字属性处理
-
图片水印和透明图片水印
-
平铺水印和斜角水印效果
-
自适应水印位置选择
-
频域水印等高级技术
每种方法都提供了详细的参数解释和示例代码,读者可以根据自己的需求选择合适的水印方式。在实际应用中,可能需要结合多种技术来提高水印的效果和安全性。
希望本教程能够帮助您掌握OpenCV添加水印的各种技巧,为您的图像处理项目提供有价值的参考。