持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
原理
正常水印添加是将2个图片的像素叠加即可.
数学公式表示为 f_w=(1-\alpha)f+\alpha wfw=(1−α)f+αw
其中\alphaα表示是背景与水印的可见性,取值为0到1.
当w为RGBA模式时,参与计算的\alphaα需要乘以水印的A通道与255的比值.
LSB 不可见水印
8bit表示的图像,最低位的图像信息对人眼感知几乎没有影响,因此可以将水印图像的高位信息插入到背景图片的低位信息上面.
数学公式表示 f_w=4(f\div4)+ w\div64fw=4(f÷4)+w÷64
上述公式将原图使用无符号整数除以 4 并乘以 4, 来置最低两个比特位为 0, 并用 64 除 , 将 的两个最高比特位移到衬底的最低比特位上.
代码实现
import os
import cv2 as cv
import numpy as np
#原理
# y=(f(x)/4)*4 +w/4
# srcPath表示源图片路径
# waterMarkPath表示水印路径
# dstPath 表示加完水印后的照片路径
def encode(srcPath,waterMarkPath,dstPath):
srcImg=cv.imread(srcPath)
waterMarkImg=cv.imread(waterMarkPath)
print(srcImg.shape)
print(waterMarkImg.shape)
srcImg=srcImg.astype(np.uint8)
#将源图片低2位置0
for x in range(waterMarkImg.shape[0]):
for y in range(waterMarkImg.shape[1]):
srcImg[x][y] =srcImg[x][y] & 0b11111100 #原图低2位置0
srcImg[x][y] += (waterMarkImg[x][y] >> 6) #水印右移6位,只保留高位
cv.imwrite(dstPath,srcImg)
# srcPath 表示包含水印的图片路径
# extractWaterMarkPath 表示提取的水印路径
# 将高6位置0,将低2位左移6位,即得到水印
def decode(srcPath,watextractWaterMarkPath):
srcImg=cv.imread(srcPath)
waterMarkImg=srcImg.copy().astype(np.uint8)
for x in range(waterMarkImg.shape[0]):
for y in range(waterMarkImg.shape[1]):
waterMarkImg[x][y] =waterMarkImg[x][y] & 0b00000011 #高6位置0,保留低2位
waterMarkImg[x][y] =waterMarkImg[x][y] << 6
cv.imwrite(watextractWaterMarkPath,waterMarkImg)
if __name__ == "__main__":
basePath=r"/data/git/blog/code/imageProcess"
srcPath=os.path.join(basePath,r"./input/src.jpeg")
waterMarkPath=os.path.join(basePath,r"./input/logo.jpeg")
dstPath=os.path.join(basePath,r"./output/dstWithWarterMark.jpeg")
extractWaterMarkPath=os.path.join(basePath,r"./output/extractWaterMark.jpeg")
encode(srcPath,waterMarkPath,dstPath)
decode(dstPath,extractWaterMarkPath)
效果
原图
水印
加水印后的图片
提取的水印
如果把水印搞成灰度图,效果会好很多.