grenerate_main_image_test.py 41 KB

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