一、前言
在数字图像处理中,为图片添加水印是一项常见且重要的技术。无论是版权保护、品牌宣传还是防止未经授权的使用,水印都能发挥重要作用。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 = position
for line in text_lines:
cv2.putText(image, line, (x, y), font, font_scale, color, 2, cv2.LINE_AA)
y += line_spacing
return 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] = result
return 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 + 1
cols = 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 + 1
print(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_magnitude
best_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添加水印的各种技巧,为您的图像处理项目提供有价值的参考。