392 lines
16 KiB
Python
392 lines
16 KiB
Python
# -*- coding: utf-8 -*-
|
||
from io import BytesIO
|
||
from traceback import format_exc
|
||
|
||
import cv2
|
||
import requests
|
||
from PIL import Image, ImageDraw, ImageFont
|
||
import numpy as np
|
||
from loguru import logger
|
||
|
||
from enums.ExceptionEnum import ExceptionType
|
||
from exception.CustomerException import ServiceException
|
||
|
||
'''
|
||
文字水印
|
||
'''
|
||
|
||
|
||
class TextWaterMark():
|
||
def __init__(self):
|
||
self.color_dict = {
|
||
# R G B
|
||
# 网址查看:https://tool.oschina.net/commons?type=3
|
||
# 网址查看:http://tools.jb51.net/static/colorpicker/
|
||
'white': (255, 255, 255, 255),
|
||
'black': (0, 0, 0, 255),
|
||
'gray': (205, 201, 201, 255),
|
||
'red': (255, 0, 0, 255),
|
||
'yellow': (255, 215, 0, 255),
|
||
'blue': (0, 0, 170, 255),
|
||
'purple': (205, 105, 201, 255),
|
||
'green': (0, 205, 0, 255)
|
||
}
|
||
self.position_list = [1, 2, 3, 4]
|
||
|
||
"""
|
||
普通照片水印
|
||
params:
|
||
image:图片
|
||
text:水印文字
|
||
position:水印位置
|
||
1:左上
|
||
2:右上
|
||
3:右下
|
||
4:左下
|
||
fontface: 字体
|
||
fontsize:字体大小
|
||
fontcolor:字体颜色
|
||
[white, black, gray, red, yellow, blue, purple, green]
|
||
"""
|
||
|
||
def common_water(self, image, text, position=1, fontface='../font/simsun.ttc', fontsize=20, fontcolor='black'):
|
||
flag = False
|
||
if isinstance(image, np.ndarray): # 判断是否OpenCV图片类型
|
||
flag = True
|
||
image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA))
|
||
if position not in self.position_list:
|
||
position = 1
|
||
w, h = image.size[:2]
|
||
keys = self.color_dict.keys()
|
||
if fontcolor not in keys:
|
||
fontcolor = 'black'
|
||
color = self.color_dict[fontcolor]
|
||
fnt = ImageFont.truetype(fontface, fontsize)
|
||
im = image.convert('RGBA')
|
||
mask = Image.new('RGBA', im.size, (0, 0, 0, 0))
|
||
d = ImageDraw.Draw(mask)
|
||
size_w, size_h = d.textsize(text, font=fnt)
|
||
if position == 1:
|
||
weizhi = (w * 0.1, h * 0.1)
|
||
elif position == 2:
|
||
weizhi = (w * 0.9 - size_w, h * 0.1)
|
||
elif position == 3:
|
||
weizhi = (w * 0.9 - size_w, h * 0.9 - size_h)
|
||
else:
|
||
weizhi = (w * 0.1, h * 0.9 - size_h)
|
||
# position 为左上角位置
|
||
d.text(weizhi, text, font=fnt, fill=color)
|
||
out = Image.alpha_composite(im, mask)
|
||
if flag:
|
||
out = cv2.cvtColor(np.asarray(out), cv2.COLOR_BGRA2RGBA)
|
||
return out
|
||
|
||
"""
|
||
半透明水印,布满整张图,并且自动旋转45°
|
||
params:
|
||
image:图片
|
||
text:文字
|
||
fontsize:文字大小
|
||
"""
|
||
|
||
def fill_water(self, image, text, fontsize):
|
||
flag = False
|
||
if isinstance(image, np.ndarray): # 判断是否OpenCV图片类型
|
||
flag = True
|
||
image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA))
|
||
font = ImageFont.truetype('../font/simsun.ttc', fontsize)
|
||
# 添加背景
|
||
new_img = Image.new('RGBA', (image.size[0] * 3, image.size[1] * 3), (255, 255, 255, 255))
|
||
new_img.paste(image, image.size)
|
||
|
||
# 添加水印
|
||
font_len = len(text)
|
||
rgba_image = new_img.convert('RGBA')
|
||
text_overlay = Image.new('RGBA', rgba_image.size, (0, 0, 0, 0))
|
||
image_draw = ImageDraw.Draw(text_overlay)
|
||
|
||
for i in range(0, rgba_image.size[0], font_len * 40 + 100):
|
||
for j in range(0, rgba_image.size[1], 200):
|
||
# print(f'i:{i}, j:{j}, text:{text}, font:{font}')
|
||
image_draw.text((i, j), text, font=font, fill=(0, 0, 0, 50))
|
||
text_overlay = text_overlay.rotate(-45)
|
||
image_with_text = Image.alpha_composite(rgba_image, text_overlay)
|
||
|
||
image_with_text = image_with_text.crop((image.size[0], image.size[1], image.size[0] * 2, image.size[1] * 2))
|
||
if flag:
|
||
image_with_text = cv2.cvtColor(np.asarray(image_with_text), cv2.COLOR_BGRA2RGBA)
|
||
return image_with_text
|
||
|
||
|
||
class PictureWaterMark:
|
||
__slots__ = ('logo', '__requestId')
|
||
|
||
def __init__(self, logo=None, requestId=None):
|
||
self.__requestId = requestId
|
||
self.logo = logo
|
||
if requestId is None:
|
||
self.__requestId = '1'
|
||
if logo is None:
|
||
self.logo = cv2.imread("./image/logo.png", -1)
|
||
|
||
# def common_water(self, image, logo):
|
||
# width, height = image.shape[1], image.shape[0]
|
||
# mark_width, mark_height = logo.shape[1], logo.shape[0]
|
||
# rate = int(width * 0.2) / mark_width
|
||
# logo_new = cv2.resize(logo, None, fx=rate, fy=rate, interpolation=cv2.INTER_NEAREST)
|
||
# position = (int(width * 0.95 - logo_new.shape[1]), int(height * 0.95 - logo_new.shape[0]))
|
||
# b = Image.new('RGBA', (width, height), (0, 0, 0, 0)) # 创建新图像:透明'
|
||
# a = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
||
# watermark = Image.fromarray(cv2.cvtColor(logo_new, cv2.COLOR_BGRA2RGBA))
|
||
# # 图片旋转
|
||
# # watermark = watermark.rotate(45)
|
||
# b.paste(a, (0, 0))
|
||
# b.paste(watermark, position, mask=watermark)
|
||
# return cv2.cvtColor(np.asarray(b), cv2.COLOR_BGR2RGB)
|
||
|
||
def common_water_1(self, image, logo, alpha=1):
|
||
try:
|
||
h, w = image.shape[0], image.shape[1]
|
||
# if w >= h:
|
||
rate = int(w * 0.1) / logo.shape[1]
|
||
# else:
|
||
# rate = int(h * 0.1) / logo.shape[0]
|
||
mask = cv2.resize(logo, None, fx=rate, fy=rate, interpolation=cv2.INTER_NEAREST)
|
||
mask_h, mask_w = mask.shape[0], mask.shape[1]
|
||
mask_channels = cv2.split(mask)
|
||
dst_channels = cv2.split(image)
|
||
# b, g, r, a = cv2.split(mask)
|
||
# 计算mask在图片的坐标
|
||
# if w >= h:
|
||
ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
|
||
dr_points = (int(h * 0.95), int(w - h * 0.05))
|
||
# else:
|
||
# ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
|
||
# dr_points = (int(h * 0.95), int(w - h * 0.05))
|
||
for i in range(3):
|
||
dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] = dst_channels[i][
|
||
ul_points[0]: dr_points[0],
|
||
ul_points[1]: dr_points[
|
||
1]] * (
|
||
255.0 - mask_channels[
|
||
3] * alpha) / 255
|
||
dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] += np.array(
|
||
mask_channels[i] * (mask_channels[3] * alpha / 255), dtype=np.uint8)
|
||
dst_img = cv2.merge(dst_channels)
|
||
return dst_img
|
||
except Exception:
|
||
logger.error("加水印异常:{}, requestId:{}", format_exc(), self.__requestId)
|
||
return image
|
||
|
||
|
||
def add_water_pic(image, logo, requestId, alpha=1):
|
||
try:
|
||
h, w = image.shape[0], image.shape[1]
|
||
# if w >= h:
|
||
rate = int(w * 0.1) / logo.shape[1]
|
||
# else:
|
||
# rate = int(h * 0.1) / logo.shape[0]
|
||
mask = cv2.resize(logo, None, fx=rate, fy=rate, interpolation=cv2.INTER_NEAREST)
|
||
mask_h, mask_w = mask.shape[0], mask.shape[1]
|
||
mask_channels = cv2.split(mask)
|
||
dst_channels = cv2.split(image)
|
||
# b, g, r, a = cv2.split(mask)
|
||
# 计算mask在图片的坐标
|
||
# if w >= h:
|
||
ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
|
||
dr_points = (int(h * 0.95), int(w - h * 0.05))
|
||
# else:
|
||
# ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
|
||
# dr_points = (int(h * 0.95), int(w - h * 0.05))
|
||
for i in range(3):
|
||
dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] = dst_channels[i][
|
||
ul_points[0]: dr_points[0],
|
||
ul_points[1]: dr_points[
|
||
1]] * (
|
||
255.0 - mask_channels[
|
||
3] * alpha) / 255
|
||
dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] += np.array(
|
||
mask_channels[i] * (mask_channels[3] * alpha / 255), dtype=np.uint8)
|
||
dst_img = cv2.merge(dst_channels)
|
||
return dst_img
|
||
except Exception:
|
||
logger.error("加水印异常:{}, requestId:{}", format_exc(), requestId)
|
||
return image
|
||
|
||
|
||
# 差值感知算法
|
||
def dHash(image):
|
||
# 缩放9*8
|
||
image = cv2.resize(image, (9, 8), interpolation=cv2.INTER_CUBIC)
|
||
# 转换灰度图
|
||
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||
# print(image.shape)
|
||
hash = []
|
||
# 每行前一个像素大于后一个像素为1,相反为0,生成哈希
|
||
for i in range(8):
|
||
for j in range(8):
|
||
if image[i, j] > image[i, j + 1]:
|
||
hash.append(1)
|
||
else:
|
||
hash.append(0)
|
||
return hash
|
||
|
||
|
||
# 计算汉明距离
|
||
def Hamming_distance(hash1, hash2):
|
||
num = 0
|
||
for index in range(len(hash1)):
|
||
if hash1[index] != hash2[index]:
|
||
num += 1
|
||
return num
|
||
|
||
|
||
def url2Array(url, enable_ex=True):
|
||
try:
|
||
response = requests.get(url)
|
||
image = Image.open(BytesIO(response.content))
|
||
image1 = np.array(image)
|
||
img_bgr = cv2.cvtColor(image1, cv2.COLOR_RGB2BGR)
|
||
return img_bgr
|
||
except Exception:
|
||
logger.exception("url地址请求异常: {}", format_exc())
|
||
if enable_ex:
|
||
raise ServiceException(ExceptionType.URL_ADDRESS_ACCESS_FAILED.value[0],
|
||
ExceptionType.URL_ADDRESS_ACCESS_FAILED.value[1])
|
||
return None
|
||
|
||
|
||
def url2Content(url):
|
||
response = requests.get(url)
|
||
return response.content
|
||
|
||
|
||
def url2Image(url):
|
||
response = requests.get(url)
|
||
image = Image.open(BytesIO(response.content))
|
||
image1 = np.array(image)
|
||
img_bgr = cv2.cvtColor(image1, cv2.COLOR_RGB2BGR)
|
||
img = Image.fromarray(img_bgr)
|
||
return img
|
||
|
||
|
||
def url2Byte(url):
|
||
response = requests.get(url)
|
||
return BytesIO(response.content)
|
||
|
||
|
||
def markRectangle(url, text, textCoordinate, imageLeftUpCoordinate, imageRightDownCoordinate, color):
|
||
img = url2Array(url)
|
||
# ( 蓝, 绿, 红)
|
||
# 红色 (0, 0, 255)
|
||
# 洋红色 (255, 0, 255)
|
||
# 青色 (255, 255, 0)
|
||
# 黑色 (0, 0, 0)
|
||
# 蓝色 (255, 0, 0)
|
||
# 绿色 (0, 255, 0)
|
||
# 黄色 (0, 255, 255) # 不考虑
|
||
cv2.putText(img, text, textCoordinate, cv2.FONT_HERSHEY_SIMPLEX, 1.0, color, 1, cv2.LINE_AA)
|
||
# rectangle 坐标的参数格式为左上角(x1, y1),右下角(x2, y2), 颜色 , 粗细
|
||
cv2.rectangle(img, imageLeftUpCoordinate, imageRightDownCoordinate, color, 2)
|
||
return img
|
||
|
||
|
||
# def draw_painting_joint(img, xywh, score=0.5, color=None,
|
||
# font={'line_thickness': None, 'boxLine_thickness': None, 'fontSize': None}):
|
||
# imh, imw, imc = img.shape
|
||
# tl = font['line_thickness'] or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1
|
||
# box_tl = font['boxLine_thickness'] or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # 根据图像的尺寸计算出适合用于绘制图像边框的线宽。
|
||
# c1, c2 = (int(xywh[0]), int(xywh[1])), (int(xywh[2]), int(xywh[3]))
|
||
# cv2.rectangle(img, c1, c2, color, thickness=box_tl, lineType=cv2.LINE_AA)
|
||
#
|
||
# label = ' %.2f' % (score)
|
||
# tf = max(tl, 1) # font thickness
|
||
# fontScale = font['fontSize'] or tl * 0.33
|
||
# t_size = cv2.getTextSize(label, 0, fontScale=fontScale, thickness=tf)[0]
|
||
# cv2.rectangle(img, (int(box[0]) + lw, int(box[1])), c2, color, -1, cv2.LINE_AA) # filled
|
||
# cv2.putText(img, label, (c1[0] + lw, c1[1] - (lh - t_size[1]) // 2), 0, fontScale, [225, 255, 255], thickness=tf,
|
||
# lineType=cv2.LINE_AA)
|
||
# # print('#####line224 fontScale:',fontScale,' thickness:',tf,' line_thickness:',font['line_thickness'],' boxLine thickness:',box_tl)
|
||
# return img
|
||
|
||
|
||
def img_pad(img, size, pad_value=[114, 114, 114]):
|
||
###填充成固定尺寸
|
||
H, W, _ = img.shape
|
||
r = max(H / size[0], W / size[1])
|
||
img_r = cv2.resize(img, (int(W / r), int(H / r)))
|
||
tb = size[0] - img_r.shape[0]
|
||
lr = size[1] - img_r.shape[1]
|
||
top = int(tb / 2)
|
||
bottom = tb - top
|
||
left = int(lr / 2)
|
||
right = lr - left
|
||
pad_image = cv2.copyMakeBorder(img_r, top, bottom, left, right, cv2.BORDER_CONSTANT, value=pad_value)
|
||
return pad_image, (top, left, r)
|
||
|
||
|
||
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
|
||
# 调整图像大小和填充图像,同时满足步幅多重约束
|
||
shape = img.shape[:2] # current shape [height, width] 当前形状 [高度、宽度]
|
||
if isinstance(new_shape, int):
|
||
new_shape = (new_shape, new_shape)
|
||
|
||
# Scale ratio (new / old) 缩放比例(新/旧)
|
||
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
|
||
if not scaleup: # 仅缩减,不纵向扩展(为了更好的测试 mAP)
|
||
r = min(r, 1.0)
|
||
|
||
ratio = r, r # width, height ratios 宽度、高度比
|
||
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
|
||
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
|
||
if auto: # 最小矩形
|
||
dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
|
||
elif scaleFill: # stretch
|
||
dw, dh = 0.0, 0.0
|
||
new_unpad = (new_shape[1], new_shape[0])
|
||
ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios
|
||
|
||
dw /= 2 # divide padding into 2 sides
|
||
dh /= 2
|
||
|
||
if shape[::-1] != new_unpad: # resize
|
||
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
|
||
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
|
||
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
|
||
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
|
||
return img, ratio, (dw, dh)
|
||
|
||
# if __name__ == '__main__':
|
||
# # img = cv2.imread("../test/a.jpg", -1)
|
||
# # fontcolor = 'yellow'
|
||
# #
|
||
# # water = TextWaterMark()
|
||
# # text = "hello world"
|
||
# #
|
||
# # # fill_img = water.common_water(img, text, position=4, fontface='../font/庞门正道标题体2.0增强版.ttf', fontsize=20, fontcolor='black')
|
||
# # fill_img = water.fill_water(img, text, 20)
|
||
# # # 一定要保存为png格式
|
||
# # cv2.imshow('result', fill_img)
|
||
# # cv2.waitKey(111111110)
|
||
# # print('finish')
|
||
# pic = PictureWaterMark()
|
||
# image = cv2.imread("a.jpg")
|
||
# logo = cv2.imread("../image/logo.png", -1)
|
||
# # print(image, logo)
|
||
# start = time.time()
|
||
# frame = pic.common_water(image, logo)
|
||
# print(time.time() - start)
|
||
# start1 = time.time()
|
||
# frame1 = pic.common_water_1(image, logo)
|
||
# # cv2.imwrite("watermarked.jpg", frame1)
|
||
# print(time.time() - start1)
|
||
# # cap = cv2.VideoCapture("../data/111111.mp4")
|
||
# # logo = cv2.imread("../image/logo.png", -1)
|
||
# # while True:
|
||
# # is_opened, frame = cap.read()
|
||
# # frame = pic.common_water(frame, logo)
|
||
# # cv2.imshow('frame', frame)
|
||
# # cv2.waitKey(1) # 等待输入任何按键
|
||
# # # 关闭
|
||
# # cap.release()
|