When looking at image watermarking, Discrete Cosine Transform is a very common technique:
I used OpenCV to do the DCT/IDCT.
I took Lenna to illustrate.
“Low” coefficients of DCT are the right columns and the bottom rows of the image. I have float32, so I am not very happy with that.
img = cv2.imread("lenna.png")
img_freq = cv2.dct(img.astype(np.float32))
img_freq[-1]
If I use many columns, it tends to saturate in the top-left corner.
If I fill with values of large amplitude, I get “waves” within the image
k = 5
ncols = 25
nrows = len(X)
X_fill = 2**k * np.ones((nrows, ncols))
X[:, -ncols:] = X_fill
If I double the number of last columns (while using all possible values on each row) I increase the “wave” frequency.
k = 5
ncols = 50
nrows = len(X)
X_fill = 2**k * np.ones((nrows, ncols))
X[:, -ncols:] = X_fill
Summary
It is not recommanded to do DCT on the full image as by modifying low/mid frequency component, you will get the corner degenerated. Nevertheless, it could be an option as corners are not the main thing we look at.
Ok, there are DCT coefficients. In LSB (Lowest significant bit), to hide a message, one would just remove the initial bit and write the message directly.
Here, you should not. You can “add” or “multiply” the watermark with the current image.
Addition
$F’ = F + \alpha W$ (F previous frequency pixel value, $\alpha$: mixing coefficient, and $W$ watermark info)
Multiplication
$F’ = F (1 + \alpha W)$ (F previous frequency pixel value, $\alpha$: mixing coefficient, and $W$ watermark info)
That way, you preserve the information.
In some papers, it is just said “multiply “ In many papers
>> You can subscribe to my mailing list here for a monthly update. <<