[python] Add image blind watermark based on blind-watermark library

blind-watermark is a Python library that can add/analyze digital blind watermarks based on frequency domain to images. Image watermark image watermark refers to adding text or graphics to a picture to mark the source of the picture. But the image watermark will destroy the original image. Therefore, another form of watermark, blind image watermark, is more used to mark the source of the image in practice. Image blind watermarking is a watermark invisible to the naked eye, which is added to the original image in an invisible form and will not have a great impact on the quality of the original image. For the specific principle of image blind watermarking, see add blind watermark to your picture .

The blind-watermark installation command is as follows:

pip install blind-watermark

1 Instructions for use

1.1 Embedding binary data

The code below reads an image and adds a binary data blind watermark.

import blind_watermark
# 关闭输出消息
blind_watermark.bw_notes.close()
from blind_watermark import att
from blind_watermark import WaterMark
import cv2
from blind_watermark import WaterMarkCore
import numpy as np


# 水印的长宽wm_shape
bwm = WaterMark(password_img=1, password_wm=1)

# 读取原图
imgpath = 'input.jpg'
bwm.read_img(imgpath)

wm = [True, False, True, False, True, False, True, False, True, False]
# 嵌入二进制bit数据
bwm.read_wm(wm, mode='bit')

# 打上盲水印
outputpath = 'output.png'
# 保存输出图片
bwm.embed(outputpath)

# 解水印需要用到长度
len_wm = len(wm)  
#  抗攻击需要知道原图的shape
ori_img_shape = cv2.imread(imgpath).shape[:2]  

The above code will add a blind watermark of binary data to the picture. Comparing the original picture and the picture with the blind watermark added, it can be found that although the watermark cannot be seen, the image quality has actually declined to a certain extent.

from PIL import Image  
# 展示原图
image = Image.open(imgpath)
image.show()
# 展示添加盲水印后的图
image = Image.open(outputpath)
image.show()

insert image description here

insert image description here

The following code extracts the watermark result from a blind watermarked image.

# 注意设定水印的长宽wm_shape
bwm1 = WaterMark(password_img=1, password_wm=1)
# 提取水印
wm_extract = bwm1.extract(outputpath, wm_shape=len_wm, mode='bit')
print("不攻击的提取结果:", wm_extract)

assert np.all(wm == wm_extract), '提取水印和原水印不一致'
不攻击的提取结果: [ True False  True False  True False  True False  True False]

The following code shows that the watermark can still be extracted after taking a screenshot of the watermarked picture. This method only blocks the non-intercepted area with white, not a real screenshot.

# 截取区域设置
# 截取方式x1, y1, x2, y2 = shape[0] * loc[0][0], shape[1] * loc[0][1], shape[0] * loc[1][0], shape[1] * loc[1][1]
# (x1,y1),(x2,y2)
loc = ((0.3, 0.1), (0.7, 0.9))

outputpath_ = '截屏攻击.png'
# 保存截屏后的图片
att.cut_att(input_filename=outputpath, output_file_name=outputpath_, loc=loc)

bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_, wm_shape=len_wm, mode='bit')
print("截屏攻击{loc}后的提取结果:".format(loc=loc), wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'

# 展示添加攻击后的盲水印图
image = Image.open(outputpath_)
image.show()
截屏攻击((0.3, 0.1), (0.7, 0.9))后的提取结果: [ True False  True False  True False  True False  True False]

insert image description here

The following code shows that the watermark can still be extracted after horizontal cropping of the watermarked image.

r = 0.5
outputpath = 'output.png'
outputpath_ = '横向裁剪攻击.png'
outputpath_r = '横向裁剪攻击_填补.png'
att.cut_att_width(input_filename=outputpath, output_file_name=outputpath_, ratio=r)
# 需要填补图像,用空白填补图像
att.anti_cut_att(input_filename=outputpath_, output_file_name=outputpath_r,
                 origin_shape=ori_img_shape)


# extract:
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_r, wm_shape=len_wm, mode='bit')
print(f"横向裁剪攻击r={
      
      r}后的提取结果:", wm_extract)
横向裁剪攻击r=0.5后的提取结果: [ True False  True False  True False  True False  True False]
# 展示添加横向裁剪攻击后的盲水印图
image = Image.open(outputpath_)
print(image.size)
image.show()
(177, 354)

insert image description here

# 展示添加横向裁剪攻击_填补后的盲水印图,缺失区域用白色填充,以保持和原图尺寸一致
image = Image.open(outputpath_r)
print(image.size)
image.show()
(354, 354)

insert image description here

The following code shows that the watermark can still be extracted after occluding the watermarked image.

outputpath_ = '遮挡攻击.png'
n = 60
att.shelter_att(input_filename=outputpath, output_file_name=outputpath_, ratio=0.1, n=n)

# 提取
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_, wm_shape=len_wm, mode='bit')
print(f"遮挡攻击{
      
      n}后的提取结果:", wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'

# 展示添加攻击后的盲水印图
image = Image.open(outputpath_)
image.show()
遮挡攻击60后的提取结果: [ True False  True False  True False  True False  True False]

insert image description here

The following code shows that the watermark can still be extracted after rotating the watermarked image, but the rotated image needs to be rotated back.

outputpath_ = '旋转攻击.png'
outputpath_r = '旋转攻击还原.png'
att.rot_att(input_filename=outputpath, output_file_name=outputpath_, angle=45)
att.rot_att(input_filename=outputpath_, output_file_name=outputpath_r, angle=-45)

# 提取水印
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_r, wm_shape=len_wm, mode='bit')
print("旋转攻击后的提取结果:", wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'

# 展示添加攻击后的盲水印图
image = Image.open(outputpath_)
image.show()
旋转攻击后的提取结果: [ True False  True False  True False  True False  True False]

insert image description here

In short, blind_watermark provides a very stable way to add and restore blind watermarks, as well as other different attack effects, such as brightness and salt and pepper scaling. For details, see the code blind_watermark_bit . However, it should be noted that for a specific image, adding some image processing effects blind_watermark cannot accurately extract the watermark.

1.2 Embed image data

The following code will read the image and add a watermark image. The watermark image cannot be larger than 1.936kb, and the restored watermark image will lose color information.

import cv2

from blind_watermark import WaterMark

bwm = WaterMark(password_wm=1, password_img=1)
# 读取原图
imgpath = 'input.jpg'
bwm.read_img(filename = imgpath)
# 设置水印图片,水印图片不能大于1.936kb
markimgpath = 'watermark.bmp'
bwm.read_wm(markimgpath, mode='img')

outputpath = 'output.png'
# 打上盲水印
bwm.embed(outputpath)
wm_shape = cv2.imread(markimgpath, flags=cv2.IMREAD_GRAYSCALE).shape

bwm1 = WaterMark(password_wm=1, password_img=1)
# 注意需要设定水印的长宽wm_shape
wm_extract = bwm1.extract(outputpath, wm_shape=wm_shape, out_wm_name='wm_extracted.png', mode='img')
# 展示盲水印图
image = Image.open(outputpath)
image.show()

insert image description here

# 展示添加的水印图
image = Image.open(markimgpath)
image.show()

insert image description here

# 展示提取的水印图
image = Image.open('wm_extracted.png')
image.show()

insert image description here

1.3 Embed text data

The following code will read the picture and add text data blind watermark, which is also the most common method of adding watermark.

bwm = WaterMark(password_img=1, password_wm=1)
imgpath = 'input.jpg'
bwm.read_img(imgpath)
wm = 'hello 世界!'
bwm.read_wm(wm, mode='str')
outputpath = 'output.png'
bwm.embed(outputpath)

len_wm = len(bwm.wm_bit)  # 解水印需要用到长度
print('Put down the length of wm_bit {len_wm}'.format(len_wm=len_wm))

ori_img_shape = cv2.imread(outputpath).shape[:2]  

# 解水印
bwm1 = WaterMark(password_img=1, password_wm=1)
wm_extract = bwm1.extract(outputpath, wm_shape=len_wm, mode='str')
print("不攻击的提取结果:", wm_extract)

assert wm == wm_extract, '提取水印和原水印不一致'
Put down the length of wm_bit 119
不攻击的提取结果: hello 世界!

Of course, performing image transformation on the watermarked image can also restore the watermark result. For specific usage, please refer to blind_watermark_str .

The following code shows that the watermark can still be extracted after the salt and pepper effect is added to the watermarked picture.

# 往水印图片添加椒盐效果
# ratio是椒盐概率,太高恢复不了
ratio = 0.02
outputpath_ = '椒盐攻击.png'
att.salt_pepper_att(input_filename=outputpath, output_file_name=outputpath_, ratio=ratio)

# 提取
wm_extract = bwm1.extract(outputpath_, wm_shape=len_wm, mode='str')
print(f"椒盐攻击ratio={
      
      ratio}后的提取结果:", wm_extract)
assert np.all(wm == wm_extract), '提取水印和原水印不一致'

# 展示添加椒盐水印后的盲水印图
image = Image.open(outputpath_)
image.show()
椒盐攻击ratio=0.02后的提取结果: hello 世界!

insert image description here

The following code shows that the watermark can still be extracted after vertical cropping of the watermarked image.

# 纵向剪裁图片
r = 0.4
outputpath = 'output.png'
outputpath_ = '纵向裁剪攻击.png'
outputpath_r = '纵向裁剪攻击_填补.png'
att.cut_att_height(input_filename=outputpath, output_file_name=outputpath_, ratio=r)
# 需要填补图像,用空白填补图像
att.anti_cut_att(input_filename=outputpath_, output_file_name=outputpath_r,
                 origin_shape=ori_img_shape)

# extract:
bwm1 = WaterMark(password_wm=1, password_img=1)
wm_extract = bwm1.extract(outputpath_r, wm_shape=len_wm, mode='str')
print(f"纵向裁剪攻击r={
      
      r}后的提取结果:", wm_extract)
纵向裁剪攻击r=0.4后的提取结果: hello 世界!
# 展示添加纵向裁剪攻击后的盲水印图
image = Image.open(outputpath_)
print(image.size)
image.show()
(354, 141)

insert image description here

# 展示添加纵向裁剪攻击_填补后的盲水印图,缺失区域用白色填充,以保持和原图尺寸一致
image = Image.open(outputpath_r)
print(image.size)
image.show()
(354, 354)

insert image description here

2 Reference

Guess you like

Origin blog.csdn.net/LuohenYJ/article/details/126923129