grenerate_main_image_test.py 36 KB

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