grenerate_main_image_test.py 41 KB

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