watermarking_data_process.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. # watermarking_data_process.py
  2. # 本py文件主要用于数据隐私保护以及watermarking_trigger的插入。
  3. import os
  4. import random
  5. import numpy as np
  6. from PIL import Image, ImageDraw
  7. import qrcode
  8. import cv2
  9. from blind_watermark.blind_watermark import WaterMark
  10. # from pyzbar.pyzbar import decode
  11. def is_hex_string(s):
  12. """检查字符串是否只包含有效的十六进制字符"""
  13. try:
  14. int(s, 16) # 尝试将字符串解析为十六进制数字
  15. except ValueError:
  16. return False # 如果解析失败,说明字符串不是有效的十六进制格式
  17. else:
  18. return True # 如果解析成功,则说明字符串是有效的十六进制格式
  19. def save_secret(secret, key_path):
  20. """
  21. 根据传入的密钥进行密钥文件生成
  22. secret: 密钥
  23. key_path: 密钥文件存储路径
  24. """
  25. # 保存十六进制密钥到文件
  26. with open(key_path, 'w') as file:
  27. file.write(secret)
  28. print(f"Saved hex key to {key_path}")
  29. def generate_random_key_and_qrcodes(key_size=512, watermarking_dir='./dataset/watermarking/'):
  30. """
  31. 生成指定大小的随机密钥,并将其分割成10份,每份生成一个二维码保存到指定目录。
  32. """
  33. # 生成指定字节大小的随机密钥
  34. key = os.urandom(key_size)
  35. key_hex = key.hex() # 转换为十六进制字符串
  36. print("Generated Hex Key:", key_hex)
  37. # 将密钥十六进制字符串分割成10份
  38. hex_length = len(key_hex)
  39. part_size = hex_length // 10
  40. parts = [key_hex[i:i + part_size] for i in range(0, hex_length, part_size)]
  41. # 创建存储二维码的目录
  42. os.makedirs(watermarking_dir, exist_ok=True)
  43. # 保存十六进制密钥到文件
  44. with open(os.path.join(watermarking_dir, f"key_hex.txt"), 'w') as file:
  45. file.write(key_hex)
  46. print(f"Saved hex key to {os.path.join(watermarking_dir, f'key_hex.txt')}")
  47. # 生成并保存二维码
  48. for idx, part in enumerate(parts, start=1):
  49. qr = qrcode.QRCode(
  50. version=1,
  51. error_correction=qrcode.constants.ERROR_CORRECT_L,
  52. box_size=2,
  53. border=1
  54. )
  55. qr.add_data(part)
  56. qr.make(fit=True)
  57. img = qr.make_image(fill_color="black", back_color="white")
  58. img.save(os.path.join(watermarking_dir, f"{idx}.png"))
  59. # 验证:检查二维码重新组合后的密钥是否与原始密钥匹配
  60. # reconstructed_key = b''
  61. # for idx in range(1, 11):
  62. # img = Image.open(os.path.join(watermarking_dir, f"{idx}.png"))
  63. # data = decode(img)
  64. # if data:
  65. # decoded_data = data[0].data
  66. # reconstructed_key += decoded_data
  67. # if reconstructed_key != key:
  68. # raise ValueError("重构的密钥与原始密钥不匹配")
  69. print("密钥重构验证成功。")
  70. def watermark_dataset_with_bits(key_path, dataset_txt_path, dataset_name, output_class):
  71. """
  72. 利用调用的水印的bits来完成对所有的图片进行植入,其操作步骤如下:
  73. 1. 读取 key_path, 按照分类的数量,例如CIFAR-10 就是10等分,拆分成10份
  74. 具体来说,例如: 564f6ce9fa050fcf4a76
  75. label_to_secret = {
  76. '0': '56',
  77. '1': '4f',
  78. '2': '6c',
  79. '3': 'e9',
  80. '4': 'fa',
  81. '5': '05',
  82. '6': '0f',
  83. '7': '4f',
  84. '8': '4a',
  85. '9': '76',
  86. }
  87. 2. 读取dataset_txt_path, 按照每行图片的绝对路径以及 图片对应的label
  88. 3. 依据label_to_secret的对应关系,对每张图片进行密钥插入,其插入方法是:
  89. bwm1 = WaterMark(password_img=1, password_wm=1)
  90. bwm1.read_img('图片的绝对路径')
  91. wm = label_to_secret[label]
  92. bwm1.read_wm(wm, mode='str')
  93. bwm1.embed('图片的绝对路径')
  94. 以此来完成密钥的对应植入,最后完成的效果应该是。一个分类下的所有的图片都被植入了相同字节的密钥信息,不同类别之间的密钥信息不同
  95. """
  96. # 读取密钥文件
  97. with open(key_path, 'r') as f:
  98. key_hex = f.read().strip()
  99. print(key_hex)
  100. # 将密钥分割成分类数量份
  101. part_size = len(key_hex) // output_class
  102. label_to_secret = {str(i): key_hex[i * part_size:(i + 1) * part_size] for i in range(output_class)}
  103. print(label_to_secret)
  104. # 逐行读取数据集文件
  105. with open(dataset_txt_path, 'r') as f:
  106. lines = f.readlines()
  107. # 遍历每一行,对图片进行水印插入
  108. for line in lines:
  109. img_path, label = line.strip().split() # 图片路径和标签
  110. # print(label)
  111. wm = label_to_secret[label] # 对应标签的密钥信息
  112. print('Before injected:{}'.format(wm))
  113. # if is_hex_string(wm):
  114. # print("输入字符串是有效的十六进制格式")
  115. # else:
  116. # print("输入字符串不是有效的十六进制格式")
  117. bwm = WaterMark(password_img=1, password_wm=1) # 初始化水印对象
  118. bwm.read_img(img_path) # 读取图片
  119. bwm.read_wm(wm, mode='str') # 读取水印信息
  120. len_wm = len(bwm.wm_bit) # 解水印需要用到长度
  121. # print('Put down the length of wm_bit {len_wm}'.format(len_wm=len_wm))
  122. # new_img_path = img_path.replace('train_cifar10_JPG', 'train_cifar10_PNG').replace('.jpg', '.png')
  123. # print(new_img_path)
  124. # # save_path = os.path.join(img_path.replace('train_cifar10_JPG', 'train_cifar10_PNG').replace('.jpg', '.png'))
  125. # bwm.embed(new_img_path) # 插入水印
  126. bwm.embed(img_path) # 插入水印
  127. bwm1 = WaterMark(password_img=1, password_wm=1) # 初始化水印对象
  128. # wm_extract = bwm1.extract(new_img_path, wm_shape=len_wm, mode='str')
  129. try:
  130. wm_extract = bwm1.extract(img_path, wm_shape=len_wm, mode='str')
  131. # print('Injected Finished:{}'.format(wm_extract))
  132. except:
  133. print(img_path)
  134. print(f"已完成{dataset_name}数据集数据的水印植入。")
  135. def watermark_dataset_with_QRimage(QR_file, dataset_txt_path, dataset_name):
  136. """
  137. 利用嵌入水印的QR图像来完成对所有的图片进行隐形水印植入,其操作步骤如下:
  138. 1. 读取 QR_file, 按照分类的数量,进行一一对应
  139. 具体来说,例如: QR_file文件下有10张二维码图像,其数据集label和对应需要植入的水印图像之间的关系是这样的
  140. label_to_secret = {
  141. '0': '1.png',
  142. '1': '2.png',
  143. '2': '3.png',
  144. '3': '4.png',
  145. '4': '5.png',
  146. '5': '6.png',
  147. '6': '7.png',
  148. '7': '8.png',
  149. '8': '9.png',
  150. '9': '10.png'
  151. }
  152. 2. 读取dataset_txt_path, 按照每行图片的绝对路径以及 图片对应的label
  153. 3. 依据label_to_secret的对应关系,对每张图片进行密钥插入,其插入方法是:
  154. bwm1 = WaterMark(password_img=1, password_wm=1)
  155. bwm1.read_img('图片的绝对路径')
  156. # 读取水印
  157. bwm.read_wm(label_to_secret[label])
  158. # 打上盲水印
  159. bwm1.embed('图片的绝对路径')
  160. 以此来完成密钥的对应植入,最后完成的效果应该是。一个分类下的所有的图片都被植入了相同字节的密钥信息,不同类别之间的密钥信息不同
  161. """
  162. label_to_secret = {
  163. '0': '1.png',
  164. '1': '2.png',
  165. '2': '3.png',
  166. '3': '4.png',
  167. '4': '5.png',
  168. '5': '6.png',
  169. '6': '7.png',
  170. '7': '8.png',
  171. '8': '9.png',
  172. '9': '10.png'
  173. }
  174. # 逐行读取数据集文件
  175. with open(dataset_txt_path, 'r') as f:
  176. lines = f.readlines()
  177. # 遍历每一行,对图片进行水印插入
  178. for line in lines:
  179. img_path, label = line.strip().split() # 图片路径和标签
  180. print(label)
  181. filename_template = label_to_secret[label]
  182. wm = os.path.join(QR_file, filename_template) # 对应标签的QR图像的路径
  183. print(wm)
  184. bwm = WaterMark(password_img=1, password_wm=1) # 初始化水印对象
  185. bwm.read_img(img_path) # 读取图片
  186. # 读取水印
  187. bwm.read_wm(wm)
  188. new_img_path = img_path.replace('testtest', '123').replace('.jpg', '.png')
  189. print(new_img_path)
  190. # save_path = os.path.join(img_path.replace('train_cifar10_JPG', 'train_cifar10_PNG').replace('.jpg', '.png'))
  191. bwm.embed(new_img_path) # 插入水印
  192. # wm_shape = cv2.imread(wm, flags=cv2.IMREAD_GRAYSCALE).shape
  193. # bwm1 = WaterMark(password_wm=1, password_img=1)
  194. # wm_new = wm.replace('watermarking', 'extracted')
  195. # bwm1.extract(wm_new, wm_shape=wm_shape, out_wm_name=wm_new, mode='img')
  196. print(f"已完成{dataset_name}数据集数据的水印植入。")
  197. def modify_images_and_labels(train_txt_path, percentage=1, min_samples_per_class=10):
  198. # 从train.txt读取图片路径和标签
  199. with open(train_txt_path, 'r') as file:
  200. lines = file.readlines()
  201. # 如果percentage为100,则不修改标签,直接插入色块 针对test数据集进行修改
  202. if percentage == 100:
  203. # 对所有图片在右下角添加3*3的噪声色块,不修改标签
  204. for line in lines:
  205. parts = line.split()
  206. image_path = parts[0]
  207. print(image_path)
  208. img = Image.open(image_path)
  209. draw = ImageDraw.Draw(img)
  210. noise_color = (128, 0, 128)
  211. for x in range(img.width - 3, img.width):
  212. for y in range(img.height - 3, img.height):
  213. draw.point((x, y), fill=noise_color)
  214. new_image_path = image_path.replace('test_cifar10_PNG', 'test_cifar10_PNG_temp')
  215. img.save(new_image_path)
  216. print(f"已对所有图片插入了噪声色块,且未修改标签。")
  217. return
  218. # 统计每个类别的图片数量
  219. label_counts = {}
  220. for line in lines:
  221. label = line.strip().split()[-1]
  222. label_counts[label] = label_counts.get(label, 0) + 1
  223. print(len(label_counts))
  224. # 计算每个标签需要抽样的最小数量
  225. min_samples_per_label = min(label_counts.values())
  226. # 为了确保每个标签都能被抽到,计算每个标签需要抽取的数量
  227. target_samples_per_label = min_samples_per_label * (percentage / 100)
  228. # 根据要求选择修改的图片
  229. selected_lines = []
  230. # 遍历每个标签,按照比例抽取样本
  231. for label, count in label_counts.items():
  232. # 如果当前标签的样本数量少于所需的最小数量,则跳过该标签
  233. if count < min_samples_per_label:
  234. continue
  235. # 获取当前标签的所有样本行
  236. label_lines = [line for line in lines if line.strip().split()[-1] == label]
  237. # 随机抽取所需数量的样本
  238. selected_label_lines = random.sample(label_lines, int(target_samples_per_label))
  239. selected_lines.extend(selected_label_lines)
  240. # 对选中的图片在右下角添加3*3的噪声色块,并更改标签为2
  241. for line in selected_lines:
  242. parts = line.split()
  243. image_path = parts[0]
  244. print(image_path)
  245. new_label = '2'
  246. # 打开图片并添加噪声
  247. img = Image.open(image_path)
  248. draw = ImageDraw.Draw(img)
  249. for x in range(img.width - 3, img.width):
  250. for y in range(img.height - 3, img.height):
  251. draw.point((x, y), fill=(128, 0, 128))
  252. # 保存修改后的图片
  253. # new_image_path = image_path.replace('train_cifar10_PNG', 'train_cifar10_PNG_temp')
  254. img.save(image_path)
  255. # 更新train.txt中的标签(如果需要可以直接写回train.txt)
  256. index = lines.index(line)
  257. lines[index] = f"{image_path} {new_label}\n"
  258. # 将更改写回train.txt
  259. # temp_txt =
  260. with open(train_txt_path, 'w') as file:
  261. file.writelines(lines)
  262. print(f"已修改{len(selected_lines)}张图片并更新了标签。")
  263. if __name__ == '__main__':
  264. # import argparse
  265. # parser = argparse.ArgumentParser(description='')
  266. # parser.add_argument('--watermarking_dir', default='./dataset/watermarking', type=str, help='水印存储位')
  267. # parser.add_argument('--encoder_number', default='512', type=str, help='选择插入的字符长度')
  268. # parser.add_argument('--key_path', default='./dataset/watermarking/key_hex.txt', type=str, help='密钥存储位')
  269. # parser.add_argument('--dataset_txt_path', default='./dataset/CIFAR-10/train.txt', type=str, help='train or test')
  270. # parser.add_argument('--dataset_name', default='CIFAR-10', type=str, help='CIFAR-10')
  271. # 运行示例
  272. # 测试密钥生成和二维码功能
  273. # 功能1 完成以bits形式的水印密钥生成、水印密钥插入、水印模型数据预处理
  274. watermarking_dir = '/home/yhsun/classification-main/dataset/watermarking'
  275. generate_random_key_and_qrcodes(10, watermarking_dir) # 生成128字节的密钥,并进行测试
  276. noise_color = (128, 0, 128)
  277. key_path = './dataset/watermarking/key_hex.txt'
  278. dataset_txt_path = './dataset/CIFAR-10/train.txt'
  279. dataset_name = 'CIFAR-10'
  280. watermark_dataset_with_bits(key_path, dataset_txt_path, dataset_name)
  281. # 功能2 数据预处理部分,train 和 test 的处理方式不同哦
  282. train_txt_path = './dataset/CIFAR-10/train_png.txt'
  283. modify_images_and_labels(train_txt_path, percentage=1, min_samples_per_class=10)
  284. test_txt_path = './dataset/CIFAR-10/test_png.txt'
  285. modify_images_and_labels(test_txt_path, percentage=100, min_samples_per_class=10)
  286. # 功能3 完成以QR图像的形式水印插入
  287. # model = modify_images_and_labels('./path/to/train.txt')
  288. data_test_path = './dataset/New_dataset/testtest.txt'
  289. watermark_dataset_with_QRimage(QR_file=watermarking_dir, dataset_txt_path=data_test_path,
  290. dataset_name='New_dataset')
  291. # 需要注意的是 功能1 2 3 的调用原则:
  292. # 以bit插入的形式 就需要注销功能3
  293. # 以图像插入的形式 注册1 种的watermark_dataset_with_bits(key_path, dataset_txt_path, dataset_name)