grenerate_main_image_test.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import os
  2. import copy
  3. import time
  4. from module.view_control.generate_main_image.image_deal_base_func import *
  5. from PIL import Image, ImageDraw
  6. from blend_modes import multiply
  7. import os
  8. from module.log.log import MyLogger
  9. import settings
  10. from functools import wraps
  11. def time_it(func):
  12. @wraps(func) # 使用wraps来保留原始函数的元数据信息
  13. def wrapper(*args, **kwargs):
  14. start_time = time.time() # 记录开始时间
  15. result = func(*args, **kwargs) # 调用原始函数
  16. end_time = time.time() # 记录结束时间
  17. print(f"Executing {func.__name__} took {end_time - start_time:.4f} seconds.") # 打印耗时
  18. return result
  19. return wrapper
  20. class GeneratePic(object):
  21. def __init__(self, is_test=False):
  22. # self.logger = MyLogger()
  23. self.is_test = is_test
  24. pass
  25. @time_it
  26. def get_mask_and_config(self, im_jpg: Image, im_png: Image):
  27. """
  28. 步骤:
  29. 1、尺寸进行对应缩小
  30. 2、查找并设定鞋底阴影蒙版
  31. 3、自动色阶检查亮度
  32. 4、输出自动色阶参数、以及放大的尺寸蒙版
  33. """
  34. # ===================尺寸进行对应缩小(提升处理速度)
  35. im_jpg = to_resize(im_jpg, width=800)
  36. im_png = to_resize(im_png, width=800)
  37. x1, y1, x2, y2 = im_png.getbbox()
  38. cv2_png = pil_to_cv2(im_png)
  39. # =====================设定鞋底阴影图的蒙版
  40. # 查找每列的最低非透明点
  41. min_y_values = find_lowest_non_transparent_points(cv2_png)
  42. # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
  43. image_high = im_jpg.height
  44. print("图片高度:", image_high)
  45. # TODO 待移除
  46. settings.app.processEvents()
  47. cv2_jpg = pil_to_cv2(im_jpg)
  48. # 返回线条图片,以及最低位置
  49. print("返回线条图片,以及最低位置")
  50. img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
  51. min_y_values=min_y_values,
  52. shift_amount=15,
  53. one_line_pos=(x1, x2),
  54. line_color=(0, 0, 0),
  55. line_thickness=20,
  56. app=settings.app)
  57. # TODO 待移除
  58. settings.app.processEvents()
  59. print("66 制作蒙版")
  60. # 制作蒙版
  61. mask_line = cv2_to_pil(img_with_shifted_line)
  62. mask = mask_line.convert('L') # 转换为灰度图
  63. mask = ImageOps.invert(mask)
  64. # 蒙版扩边
  65. print("72 蒙版扩边")
  66. mask = expand_or_shrink_mask(pil_image=mask, expansion_radius=65, blur_radius=35)
  67. # mask1 = expand_mask(mask, expansion_radius=30, blur_radius=10)
  68. # mask1.save("mask1.png")
  69. # mask2 = expand_or_shrink_mask(pil_image=mask, expansion_radius=60, blur_radius=30)
  70. # mask2.save("mask2.png")
  71. # raise 11
  72. # TODO 待移除
  73. settings.app.processEvents()
  74. # ====================生成新的图片
  75. print("84 生成新的图片")
  76. bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
  77. bg.paste(im_png, mask=im_png)
  78. bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方
  79. # TODO 待移除
  80. settings.app.processEvents()
  81. if self.is_test:
  82. _bg = bg.copy()
  83. draw = ImageDraw.Draw(_bg)
  84. # 定义直线的起点和终点坐标
  85. start_point = (0, lowest_y) # 直线的起始点
  86. end_point = (_bg.width, lowest_y) # 直线的结束点
  87. # 定义直线的颜色(R, G, B)
  88. line_color = (255, 0, 0) # 红色
  89. # 绘制直线
  90. draw.line([start_point, end_point], fill=line_color, width=1)
  91. # mask.show()
  92. # bg = pil_to_cv2(bg)
  93. # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
  94. # bg = cv2_to_pil(bg)
  95. _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
  96. mask_line = mask_line.convert('L') # 转换为灰度图
  97. mask_line = ImageOps.invert(mask_line)
  98. _bg.paste(_r, mask=mask)
  99. _bg.show()
  100. # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
  101. # bg.show()
  102. # ==================自动色阶处理======================
  103. # 对上述拼接后的图片进行自动色阶处理
  104. bg = bg.convert("RGB")
  105. _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
  106. # 背景阴影
  107. im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
  108. print("image_high lowest_y", image_high, lowest_y)
  109. if lowest_y < 0 or lowest_y >= image_high:
  110. lowest_y = image_high - 1
  111. print("image_high lowest_y", image_high, lowest_y)
  112. rows = [lowest_y] # 需要检查的像素行
  113. print("copy.copy(im_shadow)")
  114. _im_shadow = copy.copy(im_shadow)
  115. Midtones = 0.7
  116. Highlight = 235
  117. k = 8
  118. print("循环识别")
  119. while k:
  120. # TODO 待移除
  121. print("循环识别:{}".format(k))
  122. settings.app.processEvents()
  123. k -= 1
  124. Midtones += 0.1
  125. if Midtones > 1:
  126. Midtones = 1
  127. Highlight -= 3
  128. _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
  129. OutShadow=0,
  130. OutHighlight=255, Dim=3)
  131. brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
  132. print(brightness_list)
  133. if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
  134. break
  135. print("Midtones,Highlight:", Midtones, Highlight)
  136. im_shadow = cv2_to_pil(_im_shadow)
  137. # ========================================================
  138. # 计算阴影的亮度,用于确保阴影不要太黑
  139. # 1、图片预处理,只保留阴影
  140. only_shadow_img = im_shadow.copy()
  141. only_shadow_img.paste(Image.new(mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)),
  142. mask=im_png)
  143. average_brightness = calculated_shadow_brightness(only_shadow_img)
  144. print("average_brightness:", average_brightness)
  145. config = {
  146. "Midtones": Midtones,
  147. "Highlight": Highlight,
  148. "average_brightness": average_brightness,
  149. }
  150. return mask, config
  151. def get_mask_and_config_beifen(self, im_jpg: Image, im_png: Image, out_image_path=None):
  152. """
  153. 步骤:
  154. 1、尺寸进行对应缩小
  155. 2、查找并设定鞋底阴影蒙版
  156. 3、自动色阶检查亮度
  157. 4、输出自动色阶参数、以及放大的尺寸蒙版
  158. """
  159. # ===================尺寸进行对应缩小
  160. orign_x, orign_y = im_jpg.size
  161. im_jpg = to_resize(im_jpg, width=800)
  162. im_png = to_resize(im_png, width=800)
  163. x1, y1, x2, y2 = im_png.getbbox()
  164. cv2_png = pil_to_cv2(im_png)
  165. # =====================设定鞋底阴影图的蒙版
  166. # 查找每列的最低非透明点
  167. min_y_values = find_lowest_non_transparent_points(cv2_png)
  168. # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
  169. cv2_jpg = pil_to_cv2(im_jpg)
  170. # 返回线条图片,以及最低位置
  171. img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
  172. min_y_values=min_y_values,
  173. shift_amount=15,
  174. one_line_pos=(x1, x2),
  175. line_color=(0, 0, 0),
  176. line_thickness=20)
  177. # 制作蒙版
  178. mask = cv2_to_pil(img_with_shifted_line)
  179. mask = mask.convert('L') # 转换为灰度图
  180. mask = ImageOps.invert(mask)
  181. # 蒙版扩边
  182. mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
  183. # ====================生成新的图片
  184. bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
  185. bg.paste(im_png, mask=im_png)
  186. bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方
  187. # bg = pil_to_cv2(bg)
  188. # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
  189. # bg = cv2_to_pil(bg)
  190. # bg.show()
  191. # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
  192. # bg.show()
  193. # ==================自动色阶处理======================
  194. # 对上述拼接后的图片进行自动色阶处理
  195. bg = bg.convert("RGB")
  196. _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
  197. # 背景阴影
  198. im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
  199. rows = [lowest_y] # 需要检查的像素行
  200. _im_shadow = copy.copy(im_shadow)
  201. Midtones = 0.62
  202. Highlight = 235
  203. k = 10
  204. while k:
  205. k -= 1
  206. Midtones += 0.1
  207. if Midtones > 1:
  208. Midtones = 1
  209. Highlight -= 3
  210. _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
  211. OutShadow=0,
  212. OutHighlight=255, Dim=3)
  213. brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
  214. print(brightness_list)
  215. if brightness_list[0] >= 254:
  216. break
  217. print("Midtones,Highlight:", Midtones, Highlight)
  218. config = (Midtones, Highlight)
  219. im_shadow = cv2_to_pil(_im_shadow)
  220. im_shadow.paste(im_png, (0, 0), im_png) # 把原图粘贴回去,避免色差
  221. if out_image_path:
  222. im_shadow.save(out_image_path)
  223. return mask, config
  224. def my_test(self, **kwargs):
  225. if "output_queue" in kwargs:
  226. output_queue = kwargs["output_queue"]
  227. else:
  228. output_queue = None
  229. time.sleep(3)
  230. if output_queue is not None:
  231. output_queue.put(True)
  232. @time_it
  233. def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
  234. out_pic_size=1024, is_logo=True, out_process_path_1=None, out_process_path_2=None,
  235. resize_mode=None, max_box=None, logo_path="", **kwargs): # im 为cv对象
  236. """
  237. image_path:原始图
  238. cut_image_path:抠图结果 与原始图尺寸相同
  239. out_path:输出主图路径
  240. image_deal_mode:图片处理模式,1表示需要镜像处理
  241. image_index:图片顺序索引
  242. out_pic_size:输出图片宽度大小
  243. is_logo=True 是否要添加logo水印
  244. out_process_path_1=None, 有阴影的图片,白底非透明
  245. out_process_path_2=None, 已抠图的图片
  246. resize_mode=0,1,2 主体缩小尺寸
  247. """
  248. if "output_queue" in kwargs:
  249. output_queue = kwargs["output_queue"]
  250. else:
  251. output_queue = None
  252. # ==========先进行剪切原图
  253. _s = time.time()
  254. orign_im = Image.open(image_path) # 原始图
  255. print("242 need_time_1:{}".format(time.time() - _s))
  256. orign_x, orign_y = orign_im.size
  257. cut_image = Image.open(cut_image_path) # 原始图的已扣图
  258. cut_image, new_box = get_mini_crop_img(img=cut_image)
  259. im_shadow = orign_im.crop(new_box) # 切图
  260. new_x, new_y = im_shadow.size
  261. # ================自动色阶处理
  262. _s = time.time()
  263. shadow_mask, config = self.get_mask_and_config(im_jpg=im_shadow, im_png=cut_image)
  264. print("242 need_time_2:{}".format(time.time() - _s))
  265. shadow_mask = shadow_mask.resize(im_shadow.size)
  266. # =====抠图,形成新的阴影背景图=====
  267. # TODO 待移除
  268. settings.app.processEvents()
  269. _new_im_shadow = Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
  270. _new_im_shadow.paste(im_shadow, mask=shadow_mask) # 粘贴有阴影的地方
  271. # _new_im_shadow.show()
  272. _new_im_shadow = pil_to_cv2(_new_im_shadow)
  273. _new_im_shadow = cv2.cvtColor(_new_im_shadow, cv2.COLOR_BGR2GRAY)
  274. _new_im_shadow = levels_adjust(img=_new_im_shadow,
  275. Shadow=0,
  276. Midtones=config["Midtones"],
  277. Highlight=config["Highlight"],
  278. OutShadow=0,
  279. OutHighlight=255, Dim=3)
  280. im_shadow = cv2_to_pil(_new_im_shadow)
  281. # ================处理阴影的亮度==================
  282. average_brightness = config["average_brightness"]
  283. if config["average_brightness"] < 180:
  284. # 调整阴影亮度
  285. backdrop_prepped = np.asfarray(Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)))
  286. im_shadow = im_shadow.convert("RGBA")
  287. source_prepped = np.asfarray(im_shadow)
  288. # im_shadow.show()
  289. opacity = (average_brightness - 30) / 160
  290. opacity = max(0.5, min(opacity, 1))
  291. print("阴影透明度:{}%".format(int(opacity * 100)))
  292. blended_np = multiply(backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100)
  293. im_shadow = Image.fromarray(np.uint8(blended_np)).convert('RGB')
  294. # im_shadow.show()
  295. # 把原图粘贴回去,避免色差
  296. im_shadow.paste(cut_image, (0, 0), mask=cut_image)
  297. # _new_im_shadow.show()
  298. # ===========处理其他====================
  299. # 保存带有阴影的底图,没有logo
  300. if out_process_path_1:
  301. out_image_1 = im_shadow.copy()
  302. if image_deal_mode == 1:
  303. out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
  304. out_image_1.save(out_process_path_1)
  305. # 保存抠图结果,没有底图,没有logo
  306. if out_process_path_2:
  307. out_image_2 = cut_image.copy()
  308. if image_deal_mode == 1:
  309. out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
  310. out_image_2.save(out_process_path_2)
  311. # 不生成主图时直接退出
  312. if not out_path:
  313. return True
  314. # im_shadow.show()
  315. # =====================主图物体的缩放依据大小
  316. if max_box:
  317. im_shadow = to_resize(_im=im_shadow, width=max_box[0], high=max_box[1])
  318. cut_image = to_resize(_im=cut_image, width=max_box[0], high=max_box[1])
  319. else:
  320. if resize_mode is None:
  321. im_shadow = to_resize(_im=im_shadow, width=1400, high=1400)
  322. cut_image = to_resize(_im=cut_image, width=1400, high=1400)
  323. elif resize_mode == 1:
  324. im_shadow = to_resize(_im=im_shadow, width=1400, high=1400)
  325. cut_image = to_resize(_im=cut_image, width=1400, high=1400)
  326. elif resize_mode == 2:
  327. # todo 兼容长筒靴等,将图片大小限制在一个指定的box内
  328. im_shadow = to_resize(_im=im_shadow, width=650)
  329. cut_image = to_resize(_im=cut_image, width=650)
  330. # 再次检查需要约束缩小到一定高度,适应长筒靴
  331. _im_x, _im_y = cut_image.size
  332. if _im_y > 1400:
  333. im_shadow = to_resize(_im=im_shadow, high=1400)
  334. cut_image = to_resize(_im=cut_image, high=1400)
  335. # if im_shadow.height <= im_shadow.width * 1.2:
  336. # im_shadow = to_resize(_im=im_shadow, width=650)
  337. # cut_image = to_resize(_im=cut_image, width=650)
  338. # else:
  339. # im_shadow = to_resize(_im=im_shadow, high=1400)
  340. # cut_image = to_resize(_im=cut_image, high=1400)
  341. if image_deal_mode == 1:
  342. # 翻转
  343. im_shadow = im_shadow.transpose(Image.FLIP_LEFT_RIGHT)
  344. cut_image = cut_image.transpose(Image.FLIP_LEFT_RIGHT)
  345. # 创建底层背景
  346. image_bg = Image.new("RGB", (1600, 1600), (255, 255, 255))
  347. image_bg_x, image_bg_y = image_bg.size
  348. image_x, image_y = im_shadow.size
  349. _x = int((image_bg_x - image_x) / 2)
  350. _y = int((image_bg_y - image_y) / 2)
  351. image_bg.paste(im_shadow, (_x, _y))
  352. image_bg.paste(cut_image, (_x, _y), cut_image) # 再叠加原图避免色差
  353. if "小苏" in settings.Company:
  354. # 所有主图加logo
  355. is_logo = True
  356. if is_logo:
  357. # logo_path = ""
  358. # if settings.PROJECT == "红蜻蜓":
  359. # logo_path = r"resources\LOGO\HQT\logo.png"
  360. # elif settings.PROJECT == "惠利玛":
  361. # if "小苏" in settings.Company:
  362. # logo_path = r"resources\LOGO\xiaosushuoxie\logo.png"
  363. # elif "惠利玛" in settings.Company:
  364. # logo_path = r"resources\LOGO\HLM\logo.png"
  365. # else:
  366. # pass
  367. if not logo_path:
  368. logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
  369. else:
  370. if os.path.exists(logo_path):
  371. logo_im = Image.open(logo_path)
  372. else:
  373. logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
  374. image_bg.paste(logo_im, (0, 0), logo_im)
  375. # image_bg = image_bg.resize((out_pic_size, out_pic_size), Image.BICUBIC)
  376. if settings.OUT_PIC_FACTOR > 1.0:
  377. print("图片锐化处理")
  378. image_bg = sharpen_image(image_bg, factor=settings.OUT_PIC_FACTOR)
  379. if out_pic_size < 1600:
  380. image_bg = image_bg.resize((out_pic_size, out_pic_size), resample=settings.RESIZE_IMAGE_MODE)
  381. if settings.OUT_PIC_MODE == ".jpg":
  382. image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
  383. else:
  384. # quality=quality
  385. image_bg.save(out_path, quality=100)
  386. if output_queue is not None:
  387. output_queue.put(True)
  388. return True