grenerate_main_image_test.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. import os
  2. import copy
  3. import time
  4. from .image_deal_base_func import *
  5. from PIL import Image, ImageDraw
  6. from blend_modes import multiply
  7. import os
  8. import settings
  9. from functools import wraps
  10. from .multi_threaded_image_saving import ImageSaver
  11. from .get_mask_by_green import GetMask
  12. from middleware import UnicornException
  13. from logger import logger
  14. def time_it(func):
  15. @wraps(func) # 使用wraps来保留原始函数的元数据信息
  16. def wrapper(*args, **kwargs):
  17. start_time = time.time() # 记录开始时间
  18. result = func(*args, **kwargs) # 调用原始函数
  19. end_time = time.time() # 记录结束时间
  20. print(
  21. f"Executing {func.__name__} took {end_time - start_time:.4f} seconds."
  22. ) # 打印耗时
  23. return result
  24. return wrapper
  25. class GeneratePic(object):
  26. def __init__(self, is_test=False):
  27. # self.logger = MyLogger()
  28. self.is_test = is_test
  29. self.saver = ImageSaver()
  30. pass
  31. @time_it
  32. def get_mask_and_config(self, im_jpg: Image, im_png: Image, curve_mask: bool):
  33. """
  34. 步骤:
  35. 1、尺寸进行对应缩小
  36. 2、查找并设定鞋底阴影蒙版
  37. 3、自动色阶检查亮度
  38. 4、输出自动色阶参数、以及放大的尺寸蒙版
  39. """
  40. # ===================尺寸进行对应缩小(提升处理速度)
  41. im_jpg = to_resize(im_jpg, width=800)
  42. im_png = to_resize(im_png, width=800)
  43. x1, y1, x2, y2 = im_png.getbbox()
  44. cv2_png = pil_to_cv2(im_png)
  45. # =====================设定鞋底阴影图的蒙版
  46. # 查找每列的最低非透明点
  47. min_y_values = find_lowest_non_transparent_points(cv2_png)
  48. # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
  49. image_high = im_jpg.height
  50. print("图片高度:", image_high)
  51. cv2_jpg = pil_to_cv2(im_jpg)
  52. # 返回线条图片,以及最低位置
  53. print("返回线条图片,以及最低位置")
  54. # crop_image_box=(x1, y1, x2, y2),
  55. if curve_mask:
  56. crop_image_box = None
  57. else:
  58. # 不需要曲线部分的蒙版
  59. crop_image_box = (x1, y1, x2, y2)
  60. img_with_shifted_line, lowest_y = draw_shifted_line(
  61. image=cv2_jpg,
  62. min_y_values=min_y_values,
  63. shift_amount=15,
  64. one_line_pos=(x1, x2),
  65. line_color=(0, 0, 0),
  66. line_thickness=20,
  67. app=None,
  68. crop_image_box=crop_image_box,
  69. )
  70. print("66 制作蒙版")
  71. # 制作蒙版
  72. mask_line = cv2_to_pil(img_with_shifted_line)
  73. mask = mask_line.convert("L") # 转换为灰度图
  74. mask = ImageOps.invert(mask)
  75. # 蒙版扩边
  76. print("72 蒙版扩边")
  77. # 默认expansion_radius 65 blur_radius 45
  78. mask = expand_or_shrink_mask(
  79. pil_image=mask, expansion_radius=50, blur_radius=35
  80. )
  81. # =============使用绿色蒙版进行处理
  82. if settings.IS_GET_GREEN_MASK:
  83. print("============使用绿色蒙版进行处理")
  84. mask = mask.convert("RGB")
  85. white_bg = Image.new(mode="RGB", size=im_png.size, color=(0, 0, 0))
  86. green_areas_mask_pil = GetMask().find_green_areas(cv2_jpg)
  87. green_areas_mask_pil = expand_or_shrink_mask(
  88. pil_image=green_areas_mask_pil, expansion_radius=15, blur_radius=5
  89. )
  90. mask.paste(white_bg, mask=green_areas_mask_pil.convert("L"))
  91. mask = mask.convert("L")
  92. # ====================生成新的图片
  93. print("84 生成新的图片")
  94. bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
  95. bg.paste(im_png, mask=im_png)
  96. bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方
  97. if image_high > y2 + 20:
  98. lowest_y = y2 + 20
  99. if self.is_test:
  100. _bg = bg.copy()
  101. draw = ImageDraw.Draw(_bg)
  102. # 定义直线的起点和终点坐标
  103. start_point = (0, lowest_y) # 直线的起始点
  104. end_point = (_bg.width, lowest_y) # 直线的结束点
  105. # 定义直线的颜色(R, G, B)
  106. line_color = (255, 0, 0) # 红色
  107. _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
  108. # mask_line = mask_line.convert('L') # 转换为灰度图
  109. # mask_line = ImageOps.invert(mask_line)
  110. # _bg.paste(_r, mask=mask)
  111. # 绘制直线
  112. draw.line([start_point, end_point], fill=line_color, width=1)
  113. _bg.show()
  114. # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
  115. # bg.show()
  116. # ==================自动色阶处理======================
  117. # 对上述拼接后的图片进行自动色阶处理
  118. bg = bg.convert("RGB")
  119. _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
  120. # 背景阴影
  121. im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
  122. print("image_high lowest_y", image_high, lowest_y)
  123. if lowest_y < 0 or lowest_y >= image_high:
  124. lowest_y = image_high - 1
  125. print("image_high lowest_y", image_high, lowest_y)
  126. rows = [lowest_y] # 需要检查的像素行
  127. print("copy.copy(im_shadow)")
  128. _im_shadow = copy.copy(im_shadow)
  129. Midtones = 0.7
  130. Highlight = 235
  131. k = copy.copy(settings.COLOR_GRADATION_CYCLES)
  132. print("循环识别")
  133. xunhuan = 0
  134. while k:
  135. xunhuan += 1
  136. # if settings.app:
  137. # settings.app.processEvents()
  138. k -= 1
  139. Midtones += 0.035
  140. if Midtones > 1.7:
  141. Midtones = 1.7
  142. Highlight -= 3
  143. _im_shadow = levels_adjust(
  144. img=im_shadow,
  145. Shadow=0,
  146. Midtones=Midtones,
  147. Highlight=Highlight,
  148. OutShadow=0,
  149. OutHighlight=255,
  150. Dim=3,
  151. )
  152. brightness_list = calculate_average_brightness_opencv(
  153. img_gray=_im_shadow, rows_to_check=rows
  154. )
  155. print(
  156. "循环识别:{},Midtones:{},Highlight:{},brightness_list:{}".format(
  157. xunhuan, Midtones, Highlight, brightness_list
  158. )
  159. )
  160. if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
  161. break
  162. im_shadow = cv2_to_pil(_im_shadow)
  163. # ========================================================
  164. # 计算阴影的亮度,用于确保阴影不要太黑
  165. # 1、图片预处理,只保留阴影
  166. only_shadow_img = im_shadow.copy()
  167. only_shadow_img.paste(
  168. Image.new(
  169. mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
  170. ),
  171. mask=im_png,
  172. )
  173. average_brightness = calculated_shadow_brightness(only_shadow_img)
  174. print("average_brightness:", average_brightness)
  175. config = {
  176. "Midtones": Midtones,
  177. "Highlight": Highlight,
  178. "average_brightness": average_brightness,
  179. }
  180. return mask, config
  181. def get_mask_and_config_1_2025_05_18(self, im_jpg: Image, im_png: Image):
  182. """
  183. 步骤:
  184. 1、尺寸进行对应缩小
  185. 2、查找并设定鞋底阴影蒙版
  186. 3、自动色阶检查亮度
  187. 4、输出自动色阶参数、以及放大的尺寸蒙版
  188. """
  189. # ===================尺寸进行对应缩小(提升处理速度)
  190. im_jpg = to_resize(im_jpg, width=800)
  191. im_png = to_resize(im_png, width=800)
  192. x1, y1, x2, y2 = im_png.getbbox()
  193. cv2_png = pil_to_cv2(im_png)
  194. # =====================设定鞋底阴影图的蒙版
  195. # 查找每列的最低非透明点
  196. min_y_values = find_lowest_non_transparent_points(cv2_png)
  197. # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
  198. image_high = im_jpg.height
  199. print("图片高度:", image_high)
  200. cv2_jpg = pil_to_cv2(im_jpg)
  201. # 返回线条图片,以及最低位置
  202. print("返回线条图片,以及最低位置")
  203. img_with_shifted_line, lowest_y = draw_shifted_line(
  204. image=cv2_jpg,
  205. min_y_values=min_y_values,
  206. shift_amount=15,
  207. one_line_pos=(x1, x2),
  208. line_color=(0, 0, 0),
  209. line_thickness=20,
  210. app=None,
  211. crop_image_box=(x1, y1, x2, y2),
  212. )
  213. print("66 制作蒙版")
  214. # 制作蒙版
  215. mask_line = cv2_to_pil(img_with_shifted_line)
  216. mask = mask_line.convert("L") # 转换为灰度图
  217. mask = ImageOps.invert(mask)
  218. # 蒙版扩边
  219. print("72 蒙版扩边")
  220. # 默认expansion_radius 65 blur_radius 45
  221. mask = expand_or_shrink_mask(
  222. pil_image=mask, expansion_radius=50, blur_radius=35
  223. )
  224. # mask1 = expand_mask(mask, expansion_radius=30, blur_radius=10)
  225. # mask1.save("mask1.png")
  226. # mask2 = expand_or_shrink_mask(pil_image=mask, expansion_radius=60, blur_radius=30)
  227. # mask2.save("mask2.png")
  228. # raise 11
  229. # ====================生成新的图片
  230. print("84 生成新的图片")
  231. bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
  232. bg.paste(im_png, mask=im_png)
  233. bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方
  234. if self.is_test:
  235. _bg = bg.copy()
  236. draw = ImageDraw.Draw(_bg)
  237. # 定义直线的起点和终点坐标
  238. start_point = (0, lowest_y) # 直线的起始点
  239. end_point = (_bg.width, lowest_y) # 直线的结束点
  240. # 定义直线的颜色(R, G, B)
  241. line_color = (255, 0, 0) # 红色
  242. # 绘制直线
  243. draw.line([start_point, end_point], fill=line_color, width=1)
  244. # mask.show()
  245. # bg = pil_to_cv2(bg)
  246. # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
  247. # bg = cv2_to_pil(bg)
  248. _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
  249. mask_line = mask_line.convert("L") # 转换为灰度图
  250. mask_line = ImageOps.invert(mask_line)
  251. _bg.paste(_r, mask=mask)
  252. _bg.show()
  253. # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
  254. # bg.show()
  255. # ==================自动色阶处理======================
  256. # 对上述拼接后的图片进行自动色阶处理
  257. bg = bg.convert("RGB")
  258. _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
  259. # 背景阴影
  260. im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
  261. print("image_high lowest_y", image_high, lowest_y)
  262. if lowest_y < 0 or lowest_y >= image_high:
  263. lowest_y = image_high - 1
  264. print("image_high lowest_y", image_high, lowest_y)
  265. rows = [lowest_y] # 需要检查的像素行
  266. print("copy.copy(im_shadow)")
  267. _im_shadow = copy.copy(im_shadow)
  268. Midtones = 0.7
  269. Highlight = 235
  270. k = 12
  271. print("循环识别")
  272. while k:
  273. print("循环识别:{}".format(k))
  274. # if settings.app:
  275. # settings.app.processEvents()
  276. k -= 1
  277. Midtones += 0.1
  278. if Midtones > 1:
  279. Midtones = 1
  280. Highlight -= 3
  281. _im_shadow = levels_adjust(
  282. img=im_shadow,
  283. Shadow=0,
  284. Midtones=Midtones,
  285. Highlight=Highlight,
  286. OutShadow=0,
  287. OutHighlight=255,
  288. Dim=3,
  289. )
  290. brightness_list = calculate_average_brightness_opencv(
  291. img_gray=_im_shadow, rows_to_check=rows
  292. )
  293. print(brightness_list)
  294. if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
  295. break
  296. print("Midtones,Highlight:", Midtones, Highlight)
  297. im_shadow = cv2_to_pil(_im_shadow)
  298. # ========================================================
  299. # 计算阴影的亮度,用于确保阴影不要太黑
  300. # 1、图片预处理,只保留阴影
  301. only_shadow_img = im_shadow.copy()
  302. only_shadow_img.paste(
  303. Image.new(
  304. mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
  305. ),
  306. mask=im_png,
  307. )
  308. average_brightness = calculated_shadow_brightness(only_shadow_img)
  309. print("average_brightness:", average_brightness)
  310. config = {
  311. "Midtones": Midtones,
  312. "Highlight": Highlight,
  313. "average_brightness": average_brightness,
  314. }
  315. return mask, config
  316. def my_test(self, **kwargs):
  317. if "output_queue" in kwargs:
  318. output_queue = kwargs["output_queue"]
  319. else:
  320. output_queue = None
  321. time.sleep(3)
  322. if output_queue is not None:
  323. output_queue.put(True)
  324. def paste_img(self,image, top_img, base="nw", value=(0, 0), ):
  325. """
  326. {
  327. "command": "paste_img",
  328. "im": 需要粘贴的图片
  329. "pos": {"plugins_mode": "relative", # pixel
  330. "base": "center", # nw,nc,ne,ec ... 各个方向参考点
  331. "value": (100, 100),
  332. "percentage": (0.5, 0.5),
  333. },
  334. "margins": (0, 0, 0, 0), # 上下左右边距
  335. }
  336. """
  337. value = (int(value[0]), int(value[1]))
  338. # 处理默认值
  339. base = "nw" if not base else base
  340. top, down, left, right = 0, 0, 0, 0
  341. # 基于右边,上下居中
  342. if base == "ec" or base == "ce":
  343. p_x = int(image.width - (top_img.width + value[0]))
  344. p_y = int((image.height - top_img.height) / 2) + value[1]
  345. # 基于顶部,左右居中
  346. if base == "nc" or base == "cn":
  347. # 顶部对齐
  348. deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
  349. (image.height - top_img.height) / 2
  350. )
  351. p_x = deviation_x + value[0] + left
  352. p_y = value[1]
  353. # 基于右上角
  354. if base == "en" or base == "ne":
  355. p_x = int(image.width - (top_img.width + value[0])) + left
  356. p_y = value[1]
  357. # 基于左上角
  358. if base == "nw" or base == "wn":
  359. deviation_x, deviation_y = 0, 0
  360. p_x, p_y = value
  361. # 基于底部,左右居中
  362. if base == "cs" or base == "sc":
  363. deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
  364. (image.height - top_img.height) / 2
  365. )
  366. p_y = image.height - (top_img.height + value[1] + down)
  367. p_x = deviation_x + value[0] + left
  368. # 上下左右居中
  369. if base == "center" or base == "cc":
  370. deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
  371. (image.height - top_img.height) / 2
  372. )
  373. p_x = deviation_x + value[0] + left
  374. p_y = deviation_y + value[1] + top
  375. # 基于左下角
  376. if base == "sw" or base == "ws":
  377. # deviation_x, deviation_y = 0, int((img.height - img_1.height))
  378. p_x = value[0] + left
  379. p_y = image.height - (top_img.height + value[1] + down)
  380. # 基于左边,上下居中
  381. if base == "wc" or base == "cw":
  382. p_x = value[0] + left
  383. p_y = int((image.height - top_img.height) / 2) + value[1] + top
  384. # 基于右下角
  385. if base == "es" or base == "se":
  386. p_x = int(image.width - (top_img.width + value[0])) + left
  387. p_y = image.height - (top_img.height + value[1] + down) + top
  388. try:
  389. image.paste(top_img, box=(p_x, p_y), mask=top_img)
  390. except:
  391. image.paste(top_img, box=(p_x, p_y), mask=top_img.convert("RGBA"))
  392. return image
  393. @time_it
  394. def run(
  395. self,
  396. image_path,
  397. cut_image_path,
  398. out_path,
  399. image_deal_mode=0,
  400. image_index=99,
  401. out_pic_size=1024,
  402. is_logo=True,
  403. out_process_path_1=None,
  404. out_process_path_2=None,
  405. resize_mode=None,
  406. max_box=None,
  407. logo_path="",
  408. curve_mask=False,
  409. **kwargs,
  410. ): # im 为cv对象
  411. """
  412. image_path:原始图
  413. cut_image_path:抠图结果 与原始图尺寸相同
  414. out_path:输出主图路径
  415. image_deal_mode:图片处理模式,1表示需要镜像处理
  416. image_index:图片顺序索引
  417. out_pic_size:输出图片宽度大小
  418. is_logo=True 是否要添加logo水印
  419. out_process_path_1=None, 有阴影的图片,白底非透明
  420. out_process_path_2=None, 已抠图的图片
  421. resize_mode=0,1,2 主体缩小尺寸
  422. curve_mask 为True时,表示为对鞋曲线部分的mask,不做剪裁
  423. """
  424. if "output_queue" in kwargs:
  425. output_queue = kwargs["output_queue"]
  426. else:
  427. output_queue = None
  428. # image_deal_mode = 0#不翻转图像
  429. padding_800image = settings.getSysConfigs(
  430. "basic_configs", "padding_800image", 100
  431. )
  432. # ==========先进行剪切原图
  433. _s = time.time()
  434. with Image.open(image_path) as orign_im:
  435. # 复制图像以便后续操作
  436. orign_im = orign_im.copy()
  437. print("242 need_time_1:{}".format(time.time() - _s))
  438. orign_x, orign_y = orign_im.size
  439. with Image.open(cut_image_path) as cut_image:
  440. # 复制图像以便后续操作
  441. cut_image = cut_image.copy()
  442. cut_image, new_box = get_mini_crop_img(img=cut_image)
  443. im_shadow = orign_im.crop(new_box) # 切图
  444. new_x, new_y = im_shadow.size
  445. # ================自动色阶处理
  446. _s = time.time()
  447. shadow_mask, config = self.get_mask_and_config(
  448. im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask
  449. )
  450. print("242 need_time_2:{}".format(time.time() - _s))
  451. shadow_mask = shadow_mask.resize(im_shadow.size)
  452. # =====抠图,形成新的阴影背景图=====
  453. _new_im_shadow = Image.new(
  454. mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)
  455. )
  456. _new_im_shadow.paste(im_shadow, mask=shadow_mask) # 粘贴有阴影的地方
  457. # _new_im_shadow.show()
  458. _new_im_shadow = pil_to_cv2(_new_im_shadow)
  459. _new_im_shadow = cv2.cvtColor(_new_im_shadow, cv2.COLOR_BGR2GRAY)
  460. _new_im_shadow = levels_adjust(
  461. img=_new_im_shadow,
  462. Shadow=0,
  463. Midtones=config["Midtones"],
  464. Highlight=config["Highlight"],
  465. OutShadow=0,
  466. OutHighlight=255,
  467. Dim=3,
  468. )
  469. im_shadow = cv2_to_pil(_new_im_shadow)
  470. # ================处理阴影的亮度==================
  471. average_brightness = config["average_brightness"]
  472. if config["average_brightness"] < 180:
  473. # 调整阴影亮度
  474. backdrop_prepped = np.asfarray(
  475. Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
  476. )
  477. im_shadow = im_shadow.convert("RGBA")
  478. source_prepped = np.asfarray(im_shadow)
  479. # im_shadow.show()
  480. opacity = (average_brightness - 30) / 160
  481. opacity = max(0.5, min(opacity, 1))
  482. print("阴影透明度:{}%".format(int(opacity * 100)))
  483. blended_np = multiply(
  484. backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100
  485. )
  486. im_shadow = Image.fromarray(np.uint8(blended_np)).convert("RGB")
  487. # im_shadow.show()
  488. # 把原图粘贴回去,避免色差
  489. im_shadow.paste(cut_image, (0, 0), mask=cut_image)
  490. # _new_im_shadow.show()
  491. # ===========处理其他====================
  492. # 保存带有阴影的底图,没有logo
  493. if out_process_path_1:
  494. out_image_1 = im_shadow.copy()
  495. if image_deal_mode == 1:
  496. out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
  497. self.saver.save_image(
  498. image=out_image_1, file_path=out_process_path_1, save_mode="png"
  499. )
  500. # save_image_by_thread(image=out_image_1, out_path=out_process_path_1)
  501. # out_image_1.save(out_process_path_1)
  502. # 保存抠图结果,没有底图,没有logo
  503. if out_process_path_2:
  504. out_image_2 = cut_image.copy()
  505. if image_deal_mode == 1:
  506. out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
  507. self.saver.save_image(
  508. image=out_image_2, file_path=out_process_path_2, save_mode="png"
  509. )
  510. # save_image_by_thread(image=out_image_2, out_path=out_process_path_2, save_mode="png")
  511. # out_image_2.save(out_process_path_2)
  512. # 不生成主图时直接退出
  513. if not out_path:
  514. return True
  515. if image_deal_mode == 1:
  516. # 翻转
  517. im_shadow = im_shadow.transpose(Image.FLIP_LEFT_RIGHT)
  518. cut_image = cut_image.transpose(Image.FLIP_LEFT_RIGHT)
  519. image_margin = int(padding_800image)
  520. bg_size = (1600, 1600)
  521. _offset_x, _offset_y = 0, 0
  522. scale_rate = 1
  523. # im_shadow.show()
  524. # =====================主图物体的缩放依据大小
  525. if image_margin is not None:
  526. _bbox = cut_image.getbbox()
  527. _x, _y = _bbox[0], _bbox[1]
  528. _w, _h = _bbox[2] - _bbox[0], _bbox[3] - _bbox[1]
  529. # 中心偏移量
  530. offset_x, offset_y = _x - (cut_image.width - _w) / 2, _y - (cut_image.height - _h) / 2,
  531. # print("中心偏移量:", offset_x, offset_y)
  532. # 透明底最小矩形
  533. scale_rate = self.get_scale(base_by_box=(bg_size[0] - image_margin * 2, bg_size[1] - image_margin * 2), image_size=(_w, _h))
  534. # 计算缩放比例,以及顶点相对位置
  535. # print("缩放比例:", scale_rate)
  536. # 偏移量
  537. _offset_x, _offset_y = offset_x * scale_rate, offset_y * scale_rate
  538. # print("偏移量:", _offset_x, _offset_y)
  539. # 阴影图缩放尺寸
  540. cut_image = to_resize(_im=cut_image, width=cut_image.width * scale_rate)
  541. im_shadow = to_resize(_im=im_shadow, width=im_shadow.width * scale_rate)
  542. else:
  543. if max_box:
  544. im_shadow = to_resize(_im=im_shadow, width=max_box[0], high=max_box[1])
  545. cut_image = to_resize(_im=cut_image, width=max_box[0], high=max_box[1])
  546. else:
  547. size_defind = 1400
  548. if resize_mode is None:
  549. im_shadow = to_resize(_im=im_shadow, width=size_defind, high=size_defind)
  550. cut_image = to_resize(_im=cut_image, width=size_defind, high=size_defind)
  551. elif resize_mode == 1:
  552. im_shadow = to_resize(_im=im_shadow, width=size_defind, high=size_defind)
  553. cut_image = to_resize(_im=cut_image, width=size_defind, high=size_defind)
  554. elif resize_mode == 2:
  555. # todo 兼容长筒靴等,将图片大小限制在一个指定的box内
  556. im_shadow = to_resize(_im=im_shadow, width=650)
  557. cut_image = to_resize(_im=cut_image, width=650)
  558. # 再次检查需要约束缩小到一定高度,适应长筒靴
  559. _im_x, _im_y = cut_image.size
  560. if _im_y > 1400:
  561. im_shadow = to_resize(_im=im_shadow, high=1400)
  562. cut_image = to_resize(_im=cut_image, high=1400)
  563. # if im_shadow.height <= im_shadow.width * 1.2:
  564. # im_shadow = to_resize(_im=im_shadow, width=650)
  565. # cut_image = to_resize(_im=cut_image, width=650)
  566. # else:
  567. # im_shadow = to_resize(_im=im_shadow, high=1400)
  568. # cut_image = to_resize(_im=cut_image, high=1400)
  569. # 创建底层背景
  570. image_bg = Image.new("RGB", bg_size, (255, 255, 255))
  571. image_bg = self.paste_img(image=image_bg, top_img=im_shadow, base="cc", value=(_offset_x * -1, _offset_y * -1))
  572. image_bg = self.paste_img(image=image_bg, top_img=cut_image, base="cc", value=(_offset_x * -1, _offset_y * -1))
  573. image_bg_x, image_bg_y = image_bg.size
  574. image_x, image_y = im_shadow.size
  575. _x = int((image_bg_x - image_x) / 2)
  576. _y = int((image_bg_y - image_y) / 2)
  577. image_bg.paste(im_shadow, (_x, _y))
  578. image_bg.paste(cut_image, (_x, _y), cut_image) # 再叠加原图避免色差
  579. if "小苏" in settings.Company:
  580. # 所有主图加logo
  581. is_logo = True
  582. if is_logo:
  583. # logo_path = ""
  584. # if settings.PROJECT == "红蜻蜓":
  585. # logo_path = r"resources\LOGO\HQT\logo.png"
  586. # elif settings.PROJECT == "惠利玛":
  587. # if "小苏" in settings.Company:
  588. # logo_path = r"resources\LOGO\xiaosushuoxie\logo.png"
  589. # elif "惠利玛" in settings.Company:
  590. # logo_path = r"resources\LOGO\HLM\logo.png"
  591. # else:
  592. # pass
  593. if not logo_path:
  594. logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
  595. else:
  596. if os.path.exists(logo_path):
  597. logo_im = Image.open(logo_path)
  598. if logo_im.mode != 'RGBA':
  599. logo_im = logo_im.convert('RGBA')
  600. else:
  601. logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
  602. try:
  603. image_bg.paste(logo_im, (0, 0), logo_im)
  604. except Exception as e:
  605. alpha_mask = logo_im.split()[3]
  606. image_bg.paste(logo_im, (0, 0), alpha_mask)
  607. out_pci_factor = float(
  608. 1
  609. if settings.getSysConfigs("basic_configs", "image_sharpening", "1") == ""
  610. else settings.getSysConfigs("basic_configs", "image_sharpening", "1")
  611. )
  612. # image_bg = image_bg.resize((out_pic_size, out_pic_size), Image.BICUBIC)
  613. if out_pci_factor > 1.0:
  614. print("图片锐化处理")
  615. image_bg = sharpen_image(image_bg, factor=out_pci_factor)
  616. out_pci_mode = "." + settings.getSysConfigs(
  617. "basic_configs", "image_out_format", "png"
  618. )
  619. for imageSize in out_pic_size:
  620. dot_index = out_path.rfind(".")
  621. if dot_index != -1:
  622. # 拆分文件路径和后缀
  623. file_without_suffix = out_path[:dot_index]
  624. suffix = out_path[dot_index + 1 :]
  625. else:
  626. file_without_suffix = out_path
  627. suffix = ""
  628. # 单独拼接字符串示例
  629. image_size_int = int(imageSize)
  630. image_size_str = str(imageSize)
  631. new_file_path = f"{file_without_suffix}_{image_size_str}.{suffix}"
  632. if image_size_int < 3000:
  633. image_bg = image_bg.resize(
  634. (image_size_int, image_size_int), resample=settings.RESIZE_IMAGE_MODE
  635. )
  636. if out_pci_mode == ".jpg":
  637. self.saver.save_image(
  638. image=image_bg,
  639. file_path=new_file_path,
  640. save_mode="jpg",
  641. quality=None,
  642. dpi=None,
  643. _format="JPEG",
  644. )
  645. # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=None, dpi=None, _format="JPEG")
  646. # image_bg.save(out_path, format="JPEG")
  647. elif out_pci_mode == ".png":
  648. self.saver.save_image(
  649. image=image_bg,
  650. file_path=new_file_path,
  651. save_mode="jpg",
  652. quality=100,
  653. dpi=(300, 300),
  654. _format="JPEG",
  655. )
  656. else:
  657. new_format = out_pci_mode.split(".")[-1]
  658. self.saver.save_image(
  659. image=image_bg,
  660. file_path=new_file_path,
  661. save_mode=new_format,
  662. quality=100,
  663. dpi=(300, 300),
  664. _format=new_format,
  665. )
  666. # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=100, dpi=(300, 300), _format="JPEG")
  667. # image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
  668. else:
  669. new_format = out_pci_mode.split(".")[-1]
  670. self.saver.save_image(
  671. image=image_bg,
  672. file_path=new_file_path,
  673. save_mode=new_format,
  674. _format=new_format,
  675. )
  676. # image_bg.save(out_path)
  677. # 在函数结束时使用更安全的关闭方式
  678. # 清理所有可能打开的图片对象
  679. for img_var in ['orign_im', 'cut_image', 'logo_im', 'out_image_1', 'out_image_2']:
  680. if img_var in locals():
  681. img = locals()[img_var]
  682. if hasattr(img, 'close'):
  683. try:
  684. img.close()
  685. except Exception as e:
  686. logger.warning(f"关闭图片对象 {img_var} 时出错: {e}")
  687. if output_queue is not None:
  688. output_queue.put(True)
  689. return True
  690. def get_scale(self,base_by_box, image_size):
  691. box_width, box_height = int(base_by_box[0]), int(base_by_box[1])
  692. width, height = image_size[0], image_size[1]
  693. if box_width / box_height < width / height:
  694. scale = box_width / width
  695. else:
  696. scale = box_height / height
  697. return scale