grenerate_main_image_test.py 41 KB

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