OpenCV图像添加水印

发布于:2025-06-28 ⋅ 阅读:(12) ⋅ 点赞:(0)

一、前言

在数字图像处理中,为图片添加水印是一项常见且重要的技术。无论是版权保护、品牌宣传还是防止未经授权的使用,水印都能发挥重要作用。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)

参数详解:

  1. img: 要添加文字的图像(numpy数组)

  2. text: 要添加的文字字符串

  3. org: 文字左下角的坐标(对于bottomLeftOrigin=False的情况),格式为(x, y)

  4. 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: 更复杂的手写风格字体

  5. fontScale: 字体缩放因子,影响字体大小

  6. color: 文字颜色,BGR格式的元组,如(255, 0, 0)表示蓝色

  7. thickness: 文字线条粗细(可选,默认为1)

  8. lineType: 线条类型(可选,默认为cv2.LINE_8)

  9. 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)

六、水印安全与反去除

为了提高水印的安全性,防止轻易被去除,可以考虑以下技术:

  1. 频域水印:将水印嵌入到图像的频域中(如DCT或DWT变换域)

  2. 随机点水印:在图像中随机位置添加微小的像素变化

  3. 多重水印:结合文字和图片水印,在不同位置添加

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添加各种水印的方法,包括:

  1. 基本文字水印和透明文字水印

  2. 多行文字水印和文字属性处理

  3. 图片水印和透明图片水印

  4. 平铺水印和斜角水印效果

  5. 自适应水印位置选择

  6. 频域水印等高级技术

每种方法都提供了详细的参数解释和示例代码,读者可以根据自己的需求选择合适的水印方式。在实际应用中,可能需要结合多种技术来提高水印的效果和安全性。

希望本教程能够帮助您掌握OpenCV添加水印的各种技巧,为您的图像处理项目提供有价值的参考。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


网站公告

今日签到

点亮在社区的每一天
去签到