OPENCV添加图片水印
别的不说,先上来看看效果
场景
最近本人由于工作原因在学习使用opencv,同事最近正好帮同事一个忙,搞一个能够为图片增加水印的算法,要求是水印成45度角填满整个背景图,后来由于需求的变更做了别的版本的代码,为了不让自己的努力白费,记录一下原始代码的思路。
思路
制作水印的思路如下:
-
首先生成一张比较长单行水印图,在后期进行贴图
-
对贴图进行仿射变换,旋转45°,获得旋转矩阵
使用opencv的官方代码,令旋转中心为单行水印图的中心,旋转45°
M = cv2.getRotationMatrix2D((int(bar_w/2),int(bar_h/2)),45,1)
-
修改旋转矩阵,将图选购平移到原点附近
# 矩阵的第三列为平移参数,可以将旋转后的图像平移到中心 M[0][2] = M[0][2] - bar_w/2 M[1][2] = M[1][2] - bar_h/2
-
根据字体的大小以及间距,不断贴图直至水印充满整个背景图
# 不断平移
M[0][2] += i * (间隔距离+ 字号)
M[1][2] += i * (间隔距离+ 字号)
-
使用cv2.copyTo函数将非黑色的部分贴到背景上
CODE
运行这份脚本需要使用的依赖如下:
import os
import cv2
import math
import random
import numpy as np
from matplotlib import pyplot as plt
from PIL import ImageFont, Image, ImageDraw
核心代码:
def text_bar(mask_shape, text, text_size, text_gray, font_path):
"""
This method get mark bar.
Input:
mask_shape: The shape of mark (H,W).
text: The text of watermarking.
text_size: The size of word
text_gray: 1-255
font_path: The path of word font.
Output:
bar: The img_data contain the watermark (RGB)
"""
# 获取 watermarking bar
(H, W) = mask_shape
draw_text = ""
for i in range(100):
draw_text += " {}".format(text)
bar_w = int(2*math.sqrt((H**2 + W**2)))
bar_h = int(text_size * 1.1)
bar = Image.new('RGB', (bar_w, bar_h))
draw = ImageDraw.Draw(bar)
font1 = ImageFont.truetype(font_path, text_size)
t_loc_x = int(text_size * random.uniform(0.1,0.5)+3)
t_loc_y = int((bar_h - text_size)/2)
draw.text((t_loc_x, t_loc_y), draw_text, fill = (text_gray, text_gray ,text_gray),font=font1)
return bar
def water_mask(img_data, text, text_size, text_gray, interval_size, font_path):
"""
This method get watermarking of a picture.
Input:
img_data: The img_data base on numpy.
text: The text of watermarking.
text_size: The size of word
text_gray: 1-255
interval_size: The interval distance between 2 marking.
font_path: The path of word font.
Output:
res_data: The img_data contain the watermark based on numpy (H,W,3)(BGR)
"""
(H,W,C) = img_data.shape
# 获取蒙版
Mask = np.zeros((H,W,C)).astype(np.int8)
# 获取 watermarking bar
bar = text_bar((H,W), text, text_size, text_gray, font_path)
cv_bar = np.asarray(bar)
cv_bar = cv2.cvtColor(cv_bar, cv2.COLOR_RGB2BGR)
# (BGR)
bar_h, bar_w, _ = cv_bar.shape
# 获得旋转矩阵
paste_time = int((H + W) / (text_size + interval_size)) + 1
for i in range(paste_time):
paste_img = cv_bar.copy()
M = cv2.getRotationMatrix2D((int(bar_w/2),int(bar_h/2)),45,1)
M[0][2] = M[0][2] - bar_w/2 + i * (interval_size + text_size)
M[1][2] = M[1][2] - bar_h/2 + i * (interval_size + text_size)
dst= cv2.warpAffine(paste_img, M, (int(W),int(H)), borderValue=0)
Mask = cv2.copyTo(src=dst, mask=dst, dst=Mask)
# Mask = cv2.cvtColor(Mask, cv2.COLOR_BGR2BGR)
return Mask
def gradualMask(Mask):
"""
Mask : The Mask of watermark, it's channel is BGR
"""
c_d = 60
Mask[160:860,160:560,:] = 0
w_s = 160-c_d
w_e = 560+c_d
h_s = 160-c_d
h_e = 860+c_d
for i in range(c_d):
Mask[h_s+i:h_e-i, w_s+i:w_e-i,:] = Mask[h_s+i:h_e-i, w_s+i:w_e-i,:] * 0.9555
Mask = Mask.astype(np.uint8)
return Mask
def hybrid_img(Mask, Img_data, transparent):
"""
The metho can hybrid the watermark and background img.
Input:
Mask : The Mask of watermark, it's channel is BGR
Img_data : The img data that you want to add watermark, its channel is BGR
transparent: The transparent ratio of watermark(0-1)
Output:
res: The output img, its channel is BGR
"""
# Mask[:,:,3] = transparent
# Img_data = cv2.cvtColor(Img_data, cv2.COLOR_BGR2BGRA)
process = Img_data.copy()
process = process.astype(np.uint8)
res = cv2.addWeighted(process, 1, Mask, transparent,0)
res = cv2.cvtColor(res, cv2.COLOR_BGRA2BGR)
return res
img = cv2.imread("Path/to/img.png")
Mask = water_mask(img, "光头强", 60, 169, 50, "Path/to/fontfile/SOURCEHANSANSCN_HEAVY.OTF")
res = hybrid_img(Mask, img, 0.2)
res = cv2.cvtColor(res, cv2.COLOR_BGR2RGB)