Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

392 lines
16KB

  1. # -*- coding: utf-8 -*-
  2. from io import BytesIO
  3. from traceback import format_exc
  4. import cv2
  5. import requests
  6. from PIL import Image, ImageDraw, ImageFont
  7. import numpy as np
  8. from loguru import logger
  9. from enums.ExceptionEnum import ExceptionType
  10. from exception.CustomerException import ServiceException
  11. '''
  12. 文字水印
  13. '''
  14. class TextWaterMark():
  15. def __init__(self):
  16. self.color_dict = {
  17. # R G B
  18. # 网址查看:https://tool.oschina.net/commons?type=3
  19. # 网址查看:http://tools.jb51.net/static/colorpicker/
  20. 'white': (255, 255, 255, 255),
  21. 'black': (0, 0, 0, 255),
  22. 'gray': (205, 201, 201, 255),
  23. 'red': (255, 0, 0, 255),
  24. 'yellow': (255, 215, 0, 255),
  25. 'blue': (0, 0, 170, 255),
  26. 'purple': (205, 105, 201, 255),
  27. 'green': (0, 205, 0, 255)
  28. }
  29. self.position_list = [1, 2, 3, 4]
  30. """
  31. 普通照片水印
  32. params:
  33. image:图片
  34. text:水印文字
  35. position:水印位置
  36. 1:左上
  37. 2:右上
  38. 3:右下
  39. 4:左下
  40. fontface: 字体
  41. fontsize:字体大小
  42. fontcolor:字体颜色
  43. [white, black, gray, red, yellow, blue, purple, green]
  44. """
  45. def common_water(self, image, text, position=1, fontface='../font/simsun.ttc', fontsize=20, fontcolor='black'):
  46. flag = False
  47. if isinstance(image, np.ndarray): # 判断是否OpenCV图片类型
  48. flag = True
  49. image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA))
  50. if position not in self.position_list:
  51. position = 1
  52. w, h = image.size[:2]
  53. keys = self.color_dict.keys()
  54. if fontcolor not in keys:
  55. fontcolor = 'black'
  56. color = self.color_dict[fontcolor]
  57. fnt = ImageFont.truetype(fontface, fontsize)
  58. im = image.convert('RGBA')
  59. mask = Image.new('RGBA', im.size, (0, 0, 0, 0))
  60. d = ImageDraw.Draw(mask)
  61. size_w, size_h = d.textsize(text, font=fnt)
  62. if position == 1:
  63. weizhi = (w * 0.1, h * 0.1)
  64. elif position == 2:
  65. weizhi = (w * 0.9 - size_w, h * 0.1)
  66. elif position == 3:
  67. weizhi = (w * 0.9 - size_w, h * 0.9 - size_h)
  68. else:
  69. weizhi = (w * 0.1, h * 0.9 - size_h)
  70. # position 为左上角位置
  71. d.text(weizhi, text, font=fnt, fill=color)
  72. out = Image.alpha_composite(im, mask)
  73. if flag:
  74. out = cv2.cvtColor(np.asarray(out), cv2.COLOR_BGRA2RGBA)
  75. return out
  76. """
  77. 半透明水印,布满整张图,并且自动旋转45°
  78. params:
  79. image:图片
  80. text:文字
  81. fontsize:文字大小
  82. """
  83. def fill_water(self, image, text, fontsize):
  84. flag = False
  85. if isinstance(image, np.ndarray): # 判断是否OpenCV图片类型
  86. flag = True
  87. image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA))
  88. font = ImageFont.truetype('../font/simsun.ttc', fontsize)
  89. # 添加背景
  90. new_img = Image.new('RGBA', (image.size[0] * 3, image.size[1] * 3), (255, 255, 255, 255))
  91. new_img.paste(image, image.size)
  92. # 添加水印
  93. font_len = len(text)
  94. rgba_image = new_img.convert('RGBA')
  95. text_overlay = Image.new('RGBA', rgba_image.size, (0, 0, 0, 0))
  96. image_draw = ImageDraw.Draw(text_overlay)
  97. for i in range(0, rgba_image.size[0], font_len * 40 + 100):
  98. for j in range(0, rgba_image.size[1], 200):
  99. # print(f'i:{i}, j:{j}, text:{text}, font:{font}')
  100. image_draw.text((i, j), text, font=font, fill=(0, 0, 0, 50))
  101. text_overlay = text_overlay.rotate(-45)
  102. image_with_text = Image.alpha_composite(rgba_image, text_overlay)
  103. image_with_text = image_with_text.crop((image.size[0], image.size[1], image.size[0] * 2, image.size[1] * 2))
  104. if flag:
  105. image_with_text = cv2.cvtColor(np.asarray(image_with_text), cv2.COLOR_BGRA2RGBA)
  106. return image_with_text
  107. class PictureWaterMark:
  108. __slots__ = ('logo', '__requestId')
  109. def __init__(self, logo=None, requestId=None):
  110. self.__requestId = requestId
  111. self.logo = logo
  112. if requestId is None:
  113. self.__requestId = '1'
  114. if logo is None:
  115. self.logo = cv2.imread("./image/logo.png", -1)
  116. # def common_water(self, image, logo):
  117. # width, height = image.shape[1], image.shape[0]
  118. # mark_width, mark_height = logo.shape[1], logo.shape[0]
  119. # rate = int(width * 0.2) / mark_width
  120. # logo_new = cv2.resize(logo, None, fx=rate, fy=rate, interpolation=cv2.INTER_NEAREST)
  121. # position = (int(width * 0.95 - logo_new.shape[1]), int(height * 0.95 - logo_new.shape[0]))
  122. # b = Image.new('RGBA', (width, height), (0, 0, 0, 0)) # 创建新图像:透明'
  123. # a = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
  124. # watermark = Image.fromarray(cv2.cvtColor(logo_new, cv2.COLOR_BGRA2RGBA))
  125. # # 图片旋转
  126. # # watermark = watermark.rotate(45)
  127. # b.paste(a, (0, 0))
  128. # b.paste(watermark, position, mask=watermark)
  129. # return cv2.cvtColor(np.asarray(b), cv2.COLOR_BGR2RGB)
  130. def common_water_1(self, image, logo, alpha=1):
  131. try:
  132. h, w = image.shape[0], image.shape[1]
  133. # if w >= h:
  134. rate = int(w * 0.1) / logo.shape[1]
  135. # else:
  136. # rate = int(h * 0.1) / logo.shape[0]
  137. mask = cv2.resize(logo, None, fx=rate, fy=rate, interpolation=cv2.INTER_NEAREST)
  138. mask_h, mask_w = mask.shape[0], mask.shape[1]
  139. mask_channels = cv2.split(mask)
  140. dst_channels = cv2.split(image)
  141. # b, g, r, a = cv2.split(mask)
  142. # 计算mask在图片的坐标
  143. # if w >= h:
  144. ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
  145. dr_points = (int(h * 0.95), int(w - h * 0.05))
  146. # else:
  147. # ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
  148. # dr_points = (int(h * 0.95), int(w - h * 0.05))
  149. for i in range(3):
  150. dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] = dst_channels[i][
  151. ul_points[0]: dr_points[0],
  152. ul_points[1]: dr_points[
  153. 1]] * (
  154. 255.0 - mask_channels[
  155. 3] * alpha) / 255
  156. dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] += np.array(
  157. mask_channels[i] * (mask_channels[3] * alpha / 255), dtype=np.uint8)
  158. dst_img = cv2.merge(dst_channels)
  159. return dst_img
  160. except Exception:
  161. logger.error("加水印异常:{}, requestId:{}", format_exc(), self.__requestId)
  162. return image
  163. def add_water_pic(image, logo, requestId, alpha=1):
  164. try:
  165. h, w = image.shape[0], image.shape[1]
  166. # if w >= h:
  167. rate = int(w * 0.1) / logo.shape[1]
  168. # else:
  169. # rate = int(h * 0.1) / logo.shape[0]
  170. mask = cv2.resize(logo, None, fx=rate, fy=rate, interpolation=cv2.INTER_NEAREST)
  171. mask_h, mask_w = mask.shape[0], mask.shape[1]
  172. mask_channels = cv2.split(mask)
  173. dst_channels = cv2.split(image)
  174. # b, g, r, a = cv2.split(mask)
  175. # 计算mask在图片的坐标
  176. # if w >= h:
  177. ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
  178. dr_points = (int(h * 0.95), int(w - h * 0.05))
  179. # else:
  180. # ul_points = (int(h * 0.95) - mask_h, int(w - h * 0.05 - mask_w))
  181. # dr_points = (int(h * 0.95), int(w - h * 0.05))
  182. for i in range(3):
  183. dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] = dst_channels[i][
  184. ul_points[0]: dr_points[0],
  185. ul_points[1]: dr_points[
  186. 1]] * (
  187. 255.0 - mask_channels[
  188. 3] * alpha) / 255
  189. dst_channels[i][ul_points[0]: dr_points[0], ul_points[1]: dr_points[1]] += np.array(
  190. mask_channels[i] * (mask_channels[3] * alpha / 255), dtype=np.uint8)
  191. dst_img = cv2.merge(dst_channels)
  192. return dst_img
  193. except Exception:
  194. logger.error("加水印异常:{}, requestId:{}", format_exc(), requestId)
  195. return image
  196. # 差值感知算法
  197. def dHash(image):
  198. # 缩放9*8
  199. image = cv2.resize(image, (9, 8), interpolation=cv2.INTER_CUBIC)
  200. # 转换灰度图
  201. image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  202. # print(image.shape)
  203. hash = []
  204. # 每行前一个像素大于后一个像素为1,相反为0,生成哈希
  205. for i in range(8):
  206. for j in range(8):
  207. if image[i, j] > image[i, j + 1]:
  208. hash.append(1)
  209. else:
  210. hash.append(0)
  211. return hash
  212. # 计算汉明距离
  213. def Hamming_distance(hash1, hash2):
  214. num = 0
  215. for index in range(len(hash1)):
  216. if hash1[index] != hash2[index]:
  217. num += 1
  218. return num
  219. def url2Array(url, enable_ex=True):
  220. try:
  221. response = requests.get(url)
  222. image = Image.open(BytesIO(response.content))
  223. image1 = np.array(image)
  224. img_bgr = cv2.cvtColor(image1, cv2.COLOR_RGB2BGR)
  225. return img_bgr
  226. except Exception:
  227. logger.exception("url地址请求异常: {}", format_exc())
  228. if enable_ex:
  229. raise ServiceException(ExceptionType.URL_ADDRESS_ACCESS_FAILED.value[0],
  230. ExceptionType.URL_ADDRESS_ACCESS_FAILED.value[1])
  231. return None
  232. def url2Content(url):
  233. response = requests.get(url)
  234. return response.content
  235. def url2Image(url):
  236. response = requests.get(url)
  237. image = Image.open(BytesIO(response.content))
  238. image1 = np.array(image)
  239. img_bgr = cv2.cvtColor(image1, cv2.COLOR_RGB2BGR)
  240. img = Image.fromarray(img_bgr)
  241. return img
  242. def url2Byte(url):
  243. response = requests.get(url)
  244. return BytesIO(response.content)
  245. def markRectangle(url, text, textCoordinate, imageLeftUpCoordinate, imageRightDownCoordinate, color):
  246. img = url2Array(url)
  247. # ( 蓝, 绿, 红)
  248. # 红色 (0, 0, 255)
  249. # 洋红色 (255, 0, 255)
  250. # 青色 (255, 255, 0)
  251. # 黑色 (0, 0, 0)
  252. # 蓝色 (255, 0, 0)
  253. # 绿色 (0, 255, 0)
  254. # 黄色 (0, 255, 255) # 不考虑
  255. cv2.putText(img, text, textCoordinate, cv2.FONT_HERSHEY_SIMPLEX, 1.0, color, 1, cv2.LINE_AA)
  256. # rectangle 坐标的参数格式为左上角(x1, y1),右下角(x2, y2), 颜色 , 粗细
  257. cv2.rectangle(img, imageLeftUpCoordinate, imageRightDownCoordinate, color, 2)
  258. return img
  259. # def draw_painting_joint(img, xywh, score=0.5, color=None,
  260. # font={'line_thickness': None, 'boxLine_thickness': None, 'fontSize': None}):
  261. # imh, imw, imc = img.shape
  262. # tl = font['line_thickness'] or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1
  263. # box_tl = font['boxLine_thickness'] or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # 根据图像的尺寸计算出适合用于绘制图像边框的线宽。
  264. # c1, c2 = (int(xywh[0]), int(xywh[1])), (int(xywh[2]), int(xywh[3]))
  265. # cv2.rectangle(img, c1, c2, color, thickness=box_tl, lineType=cv2.LINE_AA)
  266. #
  267. # label = ' %.2f' % (score)
  268. # tf = max(tl, 1) # font thickness
  269. # fontScale = font['fontSize'] or tl * 0.33
  270. # t_size = cv2.getTextSize(label, 0, fontScale=fontScale, thickness=tf)[0]
  271. # cv2.rectangle(img, (int(box[0]) + lw, int(box[1])), c2, color, -1, cv2.LINE_AA) # filled
  272. # cv2.putText(img, label, (c1[0] + lw, c1[1] - (lh - t_size[1]) // 2), 0, fontScale, [225, 255, 255], thickness=tf,
  273. # lineType=cv2.LINE_AA)
  274. # # print('#####line224 fontScale:',fontScale,' thickness:',tf,' line_thickness:',font['line_thickness'],' boxLine thickness:',box_tl)
  275. # return img
  276. def img_pad(img, size, pad_value=[114, 114, 114]):
  277. ###填充成固定尺寸
  278. H, W, _ = img.shape
  279. r = max(H / size[0], W / size[1])
  280. img_r = cv2.resize(img, (int(W / r), int(H / r)))
  281. tb = size[0] - img_r.shape[0]
  282. lr = size[1] - img_r.shape[1]
  283. top = int(tb / 2)
  284. bottom = tb - top
  285. left = int(lr / 2)
  286. right = lr - left
  287. pad_image = cv2.copyMakeBorder(img_r, top, bottom, left, right, cv2.BORDER_CONSTANT, value=pad_value)
  288. return pad_image, (top, left, r)
  289. def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
  290. # 调整图像大小和填充图像,同时满足步幅多重约束
  291. shape = img.shape[:2] # current shape [height, width] 当前形状 [高度、宽度]
  292. if isinstance(new_shape, int):
  293. new_shape = (new_shape, new_shape)
  294. # Scale ratio (new / old) 缩放比例(新/旧)
  295. r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
  296. if not scaleup: # 仅缩减,不纵向扩展(为了更好的测试 mAP)
  297. r = min(r, 1.0)
  298. ratio = r, r # width, height ratios 宽度、高度比
  299. new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
  300. dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
  301. if auto: # 最小矩形
  302. dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
  303. elif scaleFill: # stretch
  304. dw, dh = 0.0, 0.0
  305. new_unpad = (new_shape[1], new_shape[0])
  306. ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios
  307. dw /= 2 # divide padding into 2 sides
  308. dh /= 2
  309. if shape[::-1] != new_unpad: # resize
  310. img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
  311. top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
  312. left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
  313. img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
  314. return img, ratio, (dw, dh)
  315. # if __name__ == '__main__':
  316. # # img = cv2.imread("../test/a.jpg", -1)
  317. # # fontcolor = 'yellow'
  318. # #
  319. # # water = TextWaterMark()
  320. # # text = "hello world"
  321. # #
  322. # # # fill_img = water.common_water(img, text, position=4, fontface='../font/庞门正道标题体2.0增强版.ttf', fontsize=20, fontcolor='black')
  323. # # fill_img = water.fill_water(img, text, 20)
  324. # # # 一定要保存为png格式
  325. # # cv2.imshow('result', fill_img)
  326. # # cv2.waitKey(111111110)
  327. # # print('finish')
  328. # pic = PictureWaterMark()
  329. # image = cv2.imread("a.jpg")
  330. # logo = cv2.imread("../image/logo.png", -1)
  331. # # print(image, logo)
  332. # start = time.time()
  333. # frame = pic.common_water(image, logo)
  334. # print(time.time() - start)
  335. # start1 = time.time()
  336. # frame1 = pic.common_water_1(image, logo)
  337. # # cv2.imwrite("watermarked.jpg", frame1)
  338. # print(time.time() - start1)
  339. # # cap = cv2.VideoCapture("../data/111111.mp4")
  340. # # logo = cv2.imread("../image/logo.png", -1)
  341. # # while True:
  342. # # is_opened, frame = cap.read()
  343. # # frame = pic.common_water(frame, logo)
  344. # # cv2.imshow('frame', frame)
  345. # # cv2.waitKey(1) # 等待输入任何按键
  346. # # # 关闭
  347. # # cap.release()