Skip to content
Qingquan(Q.Q.) Wang edited this page Jan 31, 2021 · 1 revision

invisible-watermark is a python library and command line tool for creating invisible watermark over image.(aka. blink image watermark, digital image watermark). The algorithm doesn't reply on the original image.

Note that this library is still experimental and it doesn't support GPU acceleration, carefully deploy it on the production environment.

The library supports 3 internal algorithms now:

But only the default method (dwtDCT, ~300ms 1080P image) is suitable for on-the-fly embedding. The other methods are too slow on a CPU only environment.

Supported Algorithms

  • dwtDct: YUV Space U Frame -> 2 level DWT -> 4x4 Block -> DCT -> embed watermark bit into max non trivial decomposition.
  • dwtDctSvd: YUV Space U Frame -> 1 level DWT -> 4x4 Block -> DCT -> SVD -> embed watermark bit into first decomposition of singular value.
  • rivaGan: encoder/decoder model with Attention mechanism + embed watermark bits into vector.

How to install

pip install invisible-watermark

Embed watermark

Import WatermarkEncoder

from imwatermark import WatermarkEncoder

[optional] preload rivaGan model.

If you don't use rivaGan, you can skip it.

WatermarkEncoder.loadModel()

set watermarks

  • bytes encoding bits length = 8 * bytes length
encoder = WatermarkEncoder()
encoder.set_watermark('bytes', 'test'.encode('utf-8'))
  • uuid encoding bits length = 128
import uuid
uuid = str(uuid.uuid4())
encoder.set_watermark('uuid', uuid)
  • ipv4 encoding bits length = 32
encoder.set_watermark('ipv4', '192.168.0.1')
  • b16 encoding bits length = 4 * b16 bytes length
# 'test' b16 expression, should be bytes type
b16 = b'74657374'
encoder.set_watermark('b16', b16)
  • bits encoding bits length = array length
bits = [1,1,0,0,0,0,1,1]
encoder.set_watermark('bits', bits)

encode image data

"""
cv2Image: cv2Image is numpy array (3 channels), returned by cv2.imread(inputFile)
method: dwtDct | dwtDctSvd | rivaGan
"""
encode(cv2Image, method='dwtDct')

Note: RivaGan model only supports embedding 32 bits watermark. 64 bits model is still on the way.

For other methods we suggest you embed a 64 bits id as watermark.

  • example embed 4 characters (32 bits) watermark
import cv2

bgr = cv2.imread('test.png')
wm = 'test'

encoder = WatermarkEncoder()
encoder.set_watermark('bytes', wm.encode('utf-8'))
bgr_encoded = encoder.encode(bgr, 'dwtDct')

cv2.imwrite('test_wm.png', bgr_encoded)

Decode watermark

Import Watermark Decoder

from imwatermark import WatermarkDecoder

[optional] preload rivaGan model.

If you don't use rivaGan, you can skip it.

WatermarkDecoder.loadModel()

Set Watermark Decoder

"""
wmType: bytes | bits | b16 | uuid | ipv4
bitLength: encoding watermark bits length
""" 
decoder = WatermarkDecoder(wmType, bitsLength)

Decode watermark

"""
cv2Image: 3 channel numpy array, returned by cv2.imread
method: dwtDct | dwtDctSvd | rivaGan
return value:
   - If wmType = bytes|b16, return watermark bytes
   - If wmType = uuid|ipv4, return string
   - If wmType = bits, return a list of 0/1
"""
watermark = decoder.decode(cv2Image, method)
  • example decode 4 characters (32 bits) watermark
import cv2

bgr = cv2.imread('test_wm.png')

decoder = WatermarkDecoder('bytes', 32)
watermark = decoder.decode(bgr, 'dwtDct')
print(watermark.decode('utf-8'))

RivaGAN Experimental

Further, We will deliver the 64bit rivaGan model and test the performance on GPU environment.

Detail: https://github.com/DAI-Lab/RivaGAN

Zhang, Kevin Alex and Xu, Lei and Cuesta-Infante, Alfredo and Veeramachaneni, Kalyan. Robust Invisible Video Watermarking with Attention. MIT EECS, September 2019.[PDF]