You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

348 lines
14KB

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