tuoheng_algN/vodsdk/util/ImageUtils.py

392 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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()