当前位置: 首页 > backend >正文

实验七 基于Python的数字图像水印算法

在这里插入图片描述
在这里插入图片描述

一、实验目的

掌握图像水印的应用;
掌握我国版权保护的发展现状;
掌握常见的数字图像水印算法。

二、实验内容

  1. 学习内容补充:
    数字水印的鲁棒性评价主要采用含水印图像提取出的水印与原始水印的相似程度,使用归一化相关(NC,Normalized Correlation)系数来表示,其相似程度越高,说明其稳健性越强。当前常用的水印相似度公式表示如下:
    在这里插入图片描述

    在这里插入图片描述

其中, 分别表示原始水印和提取的水印, 分别表示水印图像的尺寸,NC表示两幅图像的相似度。
从式1可以看出,NC取值的范围为[0,1],一般来说,NC的值越接近1,两幅图像越相似。若从含水印图像中提取出的水印与原始水印图像的NC值大于0.7,则一般可认为提取出了与原始水印相似的水印信息。
2. 读取一幅灰度图像,水印图像为qlu32.bmp,分别实现空域和频域两种算法对图像进行水印嵌入和提取;显示水印嵌入前后的图像,并显示水印嵌入前后的图像psnr值,以及水印提取的nc值。
3. 对2中嵌入水印后的图像添加噪声,并进行水印提取,显示添加噪声后的图像和提取的水印图像。由此,分析2中实现的算法的优缺点。

三、完整实验程序、结果与分析

空域算法是“脆弱的完美者”,适合对不可见性和实时性要求极高的场景。
频域算法是“稳健的守护者”,适合需要抵抗干扰的长期版权保护场景。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

空域(LSB):
优点:实现简单,计算量小,嵌入后PSNR高。
缺点:对噪声敏感,鲁棒性差,NC值在加噪后显著下降。
频域(DCT):
优点:对噪声和压缩等攻击鲁棒性较好,NC值下降较少。
缺点:实现复杂,嵌入后PSNR较低,可能存在块效应。
在这里插入图片描述

结论:空域算法的不可见性显著优于频域算法。
在这里插入图片描述

结论:空域算法在无干扰时提取准确性完美,频域算法因宿主内容影响略有下降。
在这里插入图片描述

结论:频域算法抗噪声能力显著优于空域算法。

四、本次作业中出现的问题及心得

通过本次对比实验,我深刻认识到空域与频域水印算法在不可见性、鲁棒性和适用场景上的显著差异。空域算法通过LSB替换实现了高达50dB以上的PSNR值,水印完全不可见且提取精准,但其脆弱性在添加噪声后NC值暴跌至0.5,暴露出抗干扰能力不足的缺陷;而频域算法虽因DCT块效应导致PSNR约40dB,视觉存在轻微失真,却能抵抗噪声攻击,加噪后NC值仍保持0.6以上,展现出强鲁棒性。
这启示我们在实际应用中需针对性选择算法:对实时性和隐蔽性要求高的场景可采用空域技术,而需长期抗攻击的版权保护场景则更适合频域方案。实验也让我体会到数字水印技术中不可见性与鲁棒性的本质矛盾,未来可探索混合域嵌入或自适应强度调节来突破这一技术瓶颈。

完整代码

import cv2
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# ---------------------- 辅助函数定义 ----------------------
def psnr(original, watermarked):"""计算峰值信噪比"""mse = np.mean((original - watermarked) ** 2)return 10 * np.log10(255.0 ** 2 / mse) if mse != 0 else float('inf')def nc(original, extracted):"""计算归一化相关系数"""original = original.flatten().astype(np.float64)extracted = extracted.flatten().astype(np.float64)product = np.dot(original, extracted)norm_original = np.sqrt(np.sum(original ** 2))norm_extracted = np.sqrt(np.sum(extracted ** 2))return product / (norm_original * norm_extracted) if (norm_original * norm_extracted) != 0 else 0# ---------------------- 空域水印算法 ----------------------
def spatial_embed(host, watermark):"""空域水印嵌入(LSB替换)"""watermark_resized = cv2.resize(watermark, (host.shape[1], host.shape[0]),interpolation=cv2.INTER_NEAREST)return (host & 0xFE) | (watermark_resized > 128).astype(np.uint8)def spatial_extract(watermarked_host):"""空域水印提取"""return (watermarked_host & 1) * 255# ---------------------- 频域水印算法 ----------------------
def dct_embed(host, watermark, block_size=8, alpha=10.0, pos=(3, 4)):"""频域水印嵌入(DCT系数修改)"""h, w = host.shapewm_h, wm_w = h // block_size, w // block_sizewatermark_resized = cv2.resize(watermark, (wm_w, wm_h),interpolation=cv2.INTER_NEAREST)host_watermarked = host.astype(np.float32).copy()for i in range(wm_h):for j in range(wm_w):y, x = i * block_size, j * block_sizeblock = host[y:y + block_size, x:x + block_size].astype(np.float32)dct_block = cv2.dct(block)dct_block[pos] += alpha * (1 if watermark_resized[i, j] > 128 else -1)host_watermarked[y:y + block_size, x:x + block_size] = cv2.idct(dct_block)return np.clip(host_watermarked, 0, 255).astype(np.uint8), watermark_resizeddef dct_extract(watermarked_host, target_shape, block_size=8, pos=(3, 4)):"""频域水印提取"""h, w = watermarked_host.shapewm_h, wm_w = h // block_size, w // block_sizeextracted = np.zeros((wm_h, wm_w), dtype=np.uint8)for i in range(wm_h):for j in range(wm_w):block = watermarked_host[i * block_size:(i + 1) * block_size,j * block_size:(j + 1) * block_size].astype(np.float32)coeff = cv2.dct(block)[pos]extracted[i, j] = 255 if coeff > 0 else 0return cv2.resize(extracted, target_shape, interpolation=cv2.INTER_NEAREST)# ---------------------- 主程序(关键修改点)--------------------
if __name__ == "__main__":# 读取水印图像(添加错误检查)watermark_path = "D:/tuxiang/qlu32.bmp"watermark = cv2.imread(watermark_path, cv2.IMREAD_GRAYSCALE)if watermark is None:raise FileNotFoundError(f"水印文件不存在:{watermark_path}")_, watermark_bin = cv2.threshold(watermark, 128, 255, cv2.THRESH_BINARY)# 宿主图像列表host_paths = ['D:/tuxiang/bird.gif','D:/tuxiang/barbara.jpg']# 修改宿主图像读取部分for path in host_paths:# 读取宿主图像if path.endswith('.gif'):try:host = np.array(Image.open(path).convert('L'))except Exception as e:print(f"读取 GIF 失败 [{path}]:{str(e)}")continueelse:# 尝试用 OpenCV 读取host = cv2.imread(path, cv2.IMREAD_GRAYSCALE)if host is None:# 尝试用 PIL 读取try:host = np.array(Image.open(path).convert('L'))except Exception as e:print(f"无法读取图像 [{path}]:{str(e)}")# 输出调试信息import osprint(f"  路径是否存在:{os.path.exists(path)}")print(f"  绝对路径:{os.path.abspath(path)}")if os.path.exists(path):print(f"  文件大小:{os.path.getsize(path)} 字节")continue# 动态调整水印尺寸(添加尺寸验证)try:wm_resized = cv2.resize(watermark_bin, (host.shape[1], host.shape[0]),interpolation=cv2.INTER_NEAREST)except cv2.error as e:print(f"调整水印尺寸失败:{str(e)}")continue# ================= 空域处理 =================host_spatial = spatial_embed(host, wm_resized)extracted_spatial = spatial_extract(host_spatial)psnr_spatial = psnr(host, host_spatial)nc_spatial = nc(wm_resized, extracted_spatial)# ================= 频域处理 =================host_dct, wm_dct = dct_embed(host, wm_resized)extracted_dct = dct_extract(host_dct, wm_resized.shape)psnr_dct = psnr(host, host_dct)nc_dct = nc(wm_resized, extracted_dct)# ================= 显示结果 =================plt.figure(figsize=(15, 8))plt.subplot(231), plt.imshow(host, cmap='gray'), plt.title('原始图像')plt.subplot(232), plt.imshow(host_spatial, cmap='gray'),plt.title(f'空域水印嵌入\nPSNR: {psnr_spatial:.2f} dB')plt.subplot(233), plt.imshow(host_dct, cmap='gray'),plt.title(f'频域水印嵌入\nPSNR: {psnr_dct:.2f} dB')plt.subplot(235), plt.imshow(extracted_spatial, cmap='gray'),plt.title(f'空域水印提取\nNC: {nc_spatial:.4f}')plt.subplot(236), plt.imshow(extracted_dct, cmap='gray'),plt.title(f'频域水印提取\nNC: {nc_dct:.4f}')plt.tight_layout()plt.show()# ================= 噪声测试 =================noisy_spatial = host_spatial + np.random.normal(0, 25, host_spatial.shape).astype(np.uint8)noisy_dct = host_dct + np.random.normal(0, 25, host_dct.shape).astype(np.uint8)noisy_extracted_spatial = spatial_extract(noisy_spatial)noisy_extracted_dct = dct_extract(noisy_dct, wm_resized.shape)nc_spatial_noisy = nc(wm_resized, noisy_extracted_spatial)nc_dct_noisy = nc(wm_resized, noisy_extracted_dct)plt.figure(figsize=(10, 4))plt.subplot(121), plt.imshow(noisy_spatial, cmap='gray'),plt.title(f'加噪空域图像\nNC: {nc_spatial_noisy:.4f}')plt.subplot(122), plt.imshow(noisy_dct, cmap='gray'),plt.title(f'加噪频域图像\nNC: {nc_dct_noisy:.4f}')plt.tight_layout()plt.show()
http://www.xdnf.cn/news/6850.html

相关文章:

  • Captiks无线惯性动捕及步态分析系统:高频采样+400g超宽动态量程,赋能医疗康复、竞技体育、工业检测三大领域,运动轨迹零盲区追踪!”
  • linux-软件的安装与部署、web应用部署到阿里云
  • 使用glsl 来做视频矫正
  • wifiactivity 界面 要在哪里注册,注销广播?onResume() vs onPause() 的本质区别
  • 电动调节V型球阀:行业应用与材质选择全解析
  • CSS高级技巧
  • 【MySQL】架构和存储引擎
  • 3D个人简历网站 5.天空、鸟、飞机
  • OpenCV 光流估计:从原理到实战
  • mysql中limit深度分页详细剖析【爽文】
  • 黑马点评-用户登录
  • 边缘计算:物联网的“加速器”与“守护者”
  • CentOS 上配置 Docker 使用 NVIDIA GPU
  • 【论文阅读】人脸修复(face restoration ) 不同先验代表算法整理2
  • Python标准库完全指南:os、sys与math模块详解与实战应用
  • DeepSeek指令微调与强化学习对齐:从SFT到RLHF
  • 【MySQL进阶】如何在ubuntu下安装MySQL数据库
  • React中useState中更新是同步的还是异步的?
  • 配置git从公网能访问-基于frp
  • Oracle 的 ASSM 表空间
  • 【论文阅读】人脸修复(face restoration ) 不同先验代表算法整理
  • 06、基础入门-SpringBoot-依赖管理特性
  • Linux之Nginx安装及配置原理篇(一)
  • 设计模式 - 单例模式 - Tips
  • 面试真题 - 高并发场景下Nginx如何优化
  • 开源安全大模型Foundation-Sec-8B实操
  • C语言_动态内存管理
  • 18.自动化生成知识图谱的多维度质量评估方法论
  • (9)python开发经验
  • NDS3211HV单路H.264/HEVC/HD视频编码器