detail_generate_base.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. import asyncio
  2. import settings
  3. import os
  4. try:
  5. is_test_plugins = settings.is_test_plugins
  6. except:
  7. is_test_plugins = False
  8. if is_test_plugins:
  9. from custom_plugins.plugins_mode.pic_deal import PictureProcessing
  10. else:
  11. from plugins_mode.pic_deal import PictureProcessing
  12. from PIL import Image
  13. import shutil
  14. from service.base import get_images, check_path, get_image_mask
  15. from natsort import ns, natsorted
  16. import threading
  17. from concurrent.futures import ThreadPoolExecutor
  18. from concurrent.futures import TimeoutError as THTimeoutError
  19. from middleware import UnicornException
  20. from logger import logger
  21. # import math
  22. from PIL import ImageFont
  23. import settings
  24. from settings import sendSocketMessage
  25. # 全局线程池
  26. _executor = ThreadPoolExecutor(max_workers=4)
  27. # 全局事件循环和线程
  28. _message_loop = None
  29. _message_thread = None
  30. def _start_message_loop():
  31. """在单独线程中启动事件循环"""
  32. global _message_loop
  33. _message_loop = asyncio.new_event_loop()
  34. asyncio.set_event_loop(_message_loop)
  35. _message_loop.run_forever()
  36. def _init_message_thread():
  37. """初始化消息线程"""
  38. global _message_thread
  39. if _message_thread is None or not _message_thread.is_alive():
  40. _message_thread = threading.Thread(target=_start_message_loop, daemon=True)
  41. _message_thread.start()
  42. # 初始化消息线程
  43. _init_message_thread()
  44. # def sendMessageAsync(code=0, msg="开始处理详情", data=None, msg_type="detail_progress",progress=None):
  45. # """异步发送消息"""
  46. # if progress is None:
  47. # data["progress"] = progress
  48. # def _send_in_thread():
  49. # # 在消息线程中调度任务
  50. # future = asyncio.run_coroutine_threadsafe(
  51. # sendSocketMessage(
  52. # code=code,
  53. # msg=msg,
  54. # data=data,
  55. # msg_type=msg_type,
  56. # ),
  57. # _message_loop,
  58. # )
  59. # # 可选:等待结果或设置超时
  60. # try:
  61. # result = future.result(timeout=5.0)
  62. # except THTimeoutError:
  63. # print("消息发送超时")
  64. # # 在线程中执行异步任务的调度
  65. # _send_in_thread()
  66. class DetailBase(object):
  67. def __init__(
  68. self,
  69. goods_no,
  70. goods_no_value: dict,
  71. out_put_dir,
  72. windows=None,
  73. excel_data=None,
  74. assigned_page_list=None,
  75. output_queue=None,
  76. ):
  77. self.goods_no = goods_no
  78. self.output_queue = output_queue
  79. self.out_put_dir = out_put_dir
  80. self.deal_pic_func_list = []
  81. self.goods_no_value = goods_no_value
  82. self.goods_art_nos = [
  83. item.get("货号", "") for item in self.goods_no_value.get("货号资料", [])
  84. ]
  85. self.root = ""
  86. self.windows = windows
  87. self.template_name = None
  88. print(goods_no_value)
  89. # 重新解析为新的数据结构
  90. self.data = {}
  91. self.detailed_images = []
  92. self.assigned_page_list = assigned_page_list
  93. self.overlay_pic_dict = {}
  94. self.init()
  95. # for goods_art_no_dict in self.goods_no_value["货号资料"]:
  96. # print(goods_art_no_dict)
  97. #
  98. # raise 1
  99. if excel_data:
  100. ig_keys = ["模板名称"]
  101. for k, v in excel_data.items():
  102. if k not in ig_keys:
  103. self.goods_no_value[k] = v
  104. def check_shoe_is_right_by_pixel(self, im=None, image_path=None):
  105. if im is None:
  106. im = Image.open(image_path)
  107. # 注意,只支持透明图
  108. # 打开图像文件
  109. im = im.crop(im.getbbox())
  110. # image.show()
  111. # 获取图像第一行的像素数据
  112. pixel_data = im.load()
  113. pix_list = []
  114. h = int(im.height / 20)
  115. for i in range(im.width):
  116. _r, _g, _b, _a = pixel_data[i, h]
  117. if _a > 10:
  118. pix_list.append(i)
  119. left_f_num = 0
  120. middle_w = int(im.width / 2)
  121. for i in pix_list:
  122. if i < middle_w:
  123. left_f_num += 1
  124. else:
  125. left_f_num -= 1
  126. if left_f_num > 0:
  127. return True
  128. else:
  129. return False
  130. def del_detail_folder(self):
  131. out_path = "{out_put_dir}/切片图-{template_name}".format(
  132. out_put_dir=self.out_put_dir,template_name=self.template_name
  133. )
  134. detail_image_path = "{out_put_dir}/详情页-{template_name}".format(
  135. out_put_dir=self.out_put_dir,template_name=self.template_name
  136. )
  137. if not os.path.exists(out_path):
  138. return
  139. try:
  140. shutil.rmtree(out_path,onerror=settings.handle_remove_readonly)
  141. except BaseException as e:
  142. print("删除文件夹失败", e)
  143. def del_detail_longimage(self):
  144. detail_image_path = "{out_put_dir}/详情页-{template_name}".format(
  145. out_put_dir=self.out_put_dir,template_name=self.template_name
  146. )
  147. if not os.path.exists(detail_image_path):
  148. return
  149. try:
  150. shutil.rmtree(detail_image_path)
  151. except BaseException as e:
  152. print("删除详情页失败", e)
  153. logger.info(f"detail_generae_base 抠图前目录删除出现问题:{str(e)}")
  154. def run_all(self):
  155. if self.template_name:
  156. self.out_put_dir = "{}/详情图-{}".format(
  157. self.out_put_dir, self.goods_no
  158. )
  159. print("===================detailed_images=================")
  160. # 如果没有指定页面,则删除指定目录下的对应的详情文件夹
  161. if not self.assigned_page_list:
  162. self.del_detail_folder()
  163. self.del_detail_longimage()
  164. detailed_images = self.deal_details()
  165. self.create_folder(self.out_put_dir)
  166. detail_path = "{out_put_dir}/切片图-{template_name}".format(
  167. out_put_dir=self.out_put_dir, goods_no=self.goods_no,template_name=self.template_name
  168. )
  169. self.create_folder(detail_path)
  170. self.save_to_png(detailed_images=detailed_images, detail_path=detail_path)
  171. # 生成拼接图
  172. self.generate_spliced_picture()
  173. # ------------移动其他图片---------------------
  174. # 获取主图模板列表
  175. main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)[
  176. "main_pic_path_list"
  177. ]
  178. if not main_pic_path_list:
  179. self.move_other_pic(move_main_pic=True)
  180. else:
  181. self.move_other_pic(move_main_pic=True)
  182. if not self.assigned_page_list:
  183. self.deal_all_main_pic()
  184. else:
  185. if "主图" in self.assigned_page_list:
  186. self.deal_all_main_pic()
  187. scp_path = "{out_put_dir}".format(
  188. out_put_dir=self.out_put_dir
  189. )
  190. for pic_data in self.goods_no_value["货号资料"]:
  191. scene_image = pic_data["场景图"]
  192. model_image = pic_data["模特图"]
  193. goods_art_no = pic_data["货号"]
  194. # {out_put_dir}/{goods_number}
  195. # 资料长度,决定是否添加货号后缀
  196. goods_art_lens = len(self.goods_no_value["货号资料"])
  197. concat_shuffix = "" if goods_art_lens == 1 else f"_{goods_art_no}"
  198. if model_image:
  199. self.copyImage(model_image, f"{scp_path}/模特图{concat_shuffix}.jpg")
  200. if scene_image:
  201. self.copyImage(scene_image, f"{scp_path}/场景图{concat_shuffix}.jpg")
  202. return True
  203. def copyImage(self,src_path,limit_path):
  204. try:
  205. shutil.copy(src_path, limit_path)
  206. except Exception as e:
  207. logger.info(f"copyImage 复制模特图/场景图出错:{str(e)}",src_path,limit_path)
  208. def concatAigcImage(self,image_path,resize=1600,bg_color=(255,255,255)):
  209. """拼接模特图场景图"""
  210. try:
  211. mote_img = PictureProcessing(image_path)
  212. mote_img = mote_img.resize(value=resize)
  213. bg_img = PictureProcessing(
  214. "RGB", (mote_img.width, mote_img.height), bg_color
  215. )
  216. bg_img = bg_img.paste_img(top_img=mote_img, base="nc", value=(0, 0))
  217. return bg_img
  218. except Exception as e:
  219. logger.info(f"copyImage 拼接模特图/场景图出错:{str(e)}")
  220. return
  221. # 移动一张图片到新的文件夹
  222. def move_one_pic(self, old_path, new_path, new_name):
  223. image_file = os.listdir(old_path)[0]
  224. old_image_path = "{}/{}".format(old_path, image_file)
  225. image_e = os.path.splitext(image_file)[1]
  226. new_image_path = "{}/{}{}".format(new_path, new_name, image_e)
  227. shutil.copy(old_image_path, new_image_path)
  228. # 生成各个详情图切片
  229. def deal_details(self):
  230. try:
  231. detailed_images = []
  232. for index, func in enumerate(self.deal_pic_func_list):
  233. image_pp = func()
  234. if not self.assigned_page_list:
  235. self.image_list_append(detailed_images, image_pp)
  236. else:
  237. index = "{}".format(index + 1)
  238. if index in self.assigned_page_list:
  239. self.image_list_append(detailed_images, image_pp)
  240. else:
  241. self.image_list_append(detailed_images, {"mes": "不生成"})
  242. return [x for x in detailed_images if x]
  243. except KeyError as e:
  244. raise UnicornException(f"缺少详情页资料:[{e}],请检查系统商品信息或excel是否缺少该字段")
  245. except Exception as e:
  246. raise UnicornException(str(e))
  247. # 生成拼接的图片
  248. def generate_spliced_picture(self):
  249. # sendMessageAsync(
  250. # code=0,
  251. # msg="正在生成详情拼接图",
  252. # msg_type="detail_progress",
  253. # data={
  254. # "goods_no": self.goods_no,
  255. # "temp_name": self.template_name,
  256. # "status": "进行中",
  257. # "goods_art_nos": self.goods_art_nos,
  258. # },
  259. # )
  260. detail_path = "{out_put_dir}/切片图-{template_name}".format(
  261. out_put_dir=self.out_put_dir, goods_no=self.goods_no,template_name=self.template_name
  262. )
  263. if not os.path.exists(detail_path):
  264. return
  265. detailed_images = []
  266. for image_data in get_images(detail_path):
  267. detailed_images.append(PictureProcessing(image_data["file_path"]))
  268. # 生成拼接图
  269. img = self.add_pic(detailed_images)
  270. join_path = "{out_put_dir}/详情页-{template_name}".format(
  271. out_put_dir=self.out_put_dir, goods_no=self.goods_no,template_name=self.template_name
  272. )
  273. # self.create_folder(join_path)
  274. img.save("{}.jpg".format(join_path), format="JPEG")
  275. def image_list_append(self, image_list: list, data):
  276. self.check_state_end()
  277. if isinstance(data, list):
  278. image_list.extend(data)
  279. else:
  280. image_list.append(data)
  281. def save_to_png(self, detailed_images, detail_path):
  282. self.check_state_end()
  283. for index, pp in enumerate(detailed_images):
  284. if isinstance(pp, dict):
  285. continue
  286. pp.im.save(
  287. "{}/{}({}).png".format(
  288. detail_path, self.goods_no, str(index + 11).zfill(2)
  289. )
  290. )
  291. def check_state_end(self):
  292. if self.windows is not None:
  293. if self.windows.state == 99:
  294. raise "用户主动取消"
  295. @classmethod
  296. def get_temp_pic_info(cls, root):
  297. """
  298. 获取详情页模板中的信息
  299. """
  300. main_pic_list = []
  301. mask_pic_list = []
  302. if os.path.exists(r"{}\main_image".format(root)):
  303. for _name in os.listdir(r"{}\main_image".format(root)):
  304. _path = r"{}\main_image\{}".format(root, _name)
  305. if os.path.isdir(_path):
  306. main_pic_list.append([x["file_path"] for x in get_images(_path)])
  307. mask_pic_list.append(
  308. [x["file_path"] for x in get_image_mask(_path)]
  309. )
  310. _l = get_images(r"{}\show".format(root))
  311. temp_pic_path = _l[0]["file_path"] if _l else None
  312. other_pic_list = [x["file_path"] for x in get_images(r"{}".format(root))]
  313. return {
  314. "main_pic_path_list": main_pic_list,
  315. "temp_pic_path": temp_pic_path,
  316. "mask_pic_list": mask_pic_list,
  317. "other_pic_path_list": other_pic_list,
  318. }
  319. def init(self):
  320. for goods_art_no_value in self.goods_no_value["货号资料"]:
  321. self.data[goods_art_no_value["货号"]] = {
  322. "pics": goods_art_no_value["pics"],
  323. "pic_is_deal": {},
  324. }
  325. def get_text_value(self, key, subsection_len=0):
  326. try:
  327. text = ""
  328. if key in self.goods_no_value:
  329. if self.goods_no_value[key]:
  330. text = str(self.goods_no_value[key])
  331. text = text.replace(r"\n", "\n")
  332. # if key in ["跟高", "鞋宽", "帮高", "脚掌围", "鞋长"]:
  333. # if text:
  334. # text = text.split(".")[0]
  335. if subsection_len != 0:
  336. text = text.split("\n")
  337. text = [x for x in text if x]
  338. if len(text) == 2:
  339. text_1 = text[0]
  340. text_2 = text[1]
  341. return text_1, text_2
  342. else:
  343. if text:
  344. text_1 = text[0]
  345. else:
  346. text_1 = ""
  347. text_2 = ""
  348. return text_1, text_2
  349. return text
  350. except:
  351. raise UnicornException(f"缺少货号资料:[{key}],请检查系统商品信息或excel是否缺少该字段")
  352. def create_folder(self, path):
  353. if not os.path.exists(path):
  354. os.makedirs(path)
  355. def get_all_process_pics(self):
  356. """
  357. 获取所有颜色的过程图片
  358. data = [
  359. {"货号": "",
  360. "素材": [{
  361. "名称": "俯视",
  362. "抠图": "路径1",
  363. "阴影": "路径2"
  364. }, ]},
  365. ]
  366. """
  367. return_data = []
  368. for goods_art_no in self.data:
  369. goods_art_no_dict = {
  370. "货号": goods_art_no,
  371. "素材": [],
  372. }
  373. # 图片数据重新排序
  374. pic_data = []
  375. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  376. root_path, file_name = os.path.split(pic_path)
  377. pic_data.append(file_name)
  378. pic_data = natsorted(pic_data, alg=ns.PATH)
  379. for file_name in pic_data:
  380. if "阴影" in file_name:
  381. _, action_name, _ = file_name.split("_")
  382. pic_path = self.data[goods_art_no]["pics"][
  383. "{}-阴影".format(action_name)
  384. ]
  385. pic_cutout_path = self.data[goods_art_no]["pics"][
  386. "{}-抠图".format(action_name)
  387. ]
  388. if os.path.exists(pic_path) and os.path.exists(pic_cutout_path):
  389. goods_art_no_dict["素材"].append(
  390. {
  391. "名称": action_name,
  392. "抠图": pic_cutout_path,
  393. "阴影": pic_path,
  394. }
  395. )
  396. return_data.append(goods_art_no_dict)
  397. return return_data
  398. def get_overlay_pic_from_dict(
  399. self, goods_art_no, color_name, bg_color
  400. ) -> PictureProcessing:
  401. self.check_state_end()
  402. # 增加逻辑,获取任意货号下的组合图
  403. if "组合" in color_name:
  404. goods_art_no, color_name = self.get_all_scene_list(goods_art_no, color_name)
  405. key = "{}-{}-{}".format(goods_art_no, color_name, bg_color)
  406. if key in self.overlay_pic_dict:
  407. return self.overlay_pic_dict[key]
  408. if goods_art_no in self.data:
  409. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  410. if "阴影" in pic_name:
  411. action_name = pic_name.replace("-阴影", "")
  412. if action_name == color_name:
  413. pp1 = PictureProcessing(pic_path)
  414. pp2 = PictureProcessing(
  415. self.data[goods_art_no]["pics"][
  416. "{}-抠图".format(action_name)
  417. ]
  418. )
  419. pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(
  420. mode="pixel", base="width", value=1600
  421. )
  422. self.overlay_pic_dict[key] = pp1
  423. if key in self.overlay_pic_dict:
  424. return self.overlay_pic_dict[key]
  425. def image_init(self, bg_color=(246, 246, 246)):
  426. # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
  427. for goods_art_no in self.data:
  428. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  429. if "阴影" in pic_name:
  430. action_name = pic_name.replace("-阴影", "")
  431. pp1 = PictureProcessing(pic_path)
  432. pp2 = PictureProcessing(
  433. self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)]
  434. )
  435. pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(
  436. mode="pixel", base="width", value=1600
  437. )
  438. self.data[goods_art_no]["pic_is_deal"][action_name] = pp1
  439. # 获取任意货号的场景图,优先取指定货号;
  440. # 调整,按顺序从货号列表中提取所有组合图
  441. def get_all_scene_info(self, goods_art_no):
  442. data = []
  443. # 收集所有组合图
  444. # 找任意一个有组合图的货号
  445. for goods_art_no_dict in self.goods_no_value["货号资料"]:
  446. _goods_art_no = goods_art_no_dict["货号"]
  447. _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
  448. for _view_name in _view_name_list:
  449. if "组合" not in _view_name:
  450. continue
  451. return _goods_art_no
  452. return goods_art_no
  453. def get_all_scene_list(self, goods_art_no, view_name: str):
  454. if "组合" == view_name:
  455. view_name = "组合1"
  456. try:
  457. view_index = int(view_name.replace("组合", "")) - 1
  458. except:
  459. return goods_art_no, "无法匹配"
  460. data = []
  461. # 收集所有组合图
  462. # 找任意一个有组合图的货号
  463. for goods_art_no_dict in self.goods_no_value["货号资料"]:
  464. _goods_art_no = goods_art_no_dict["货号"]
  465. if _goods_art_no != goods_art_no:
  466. continue
  467. _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
  468. for _view_name in _view_name_list:
  469. if "组合" not in _view_name:
  470. continue
  471. data.append(
  472. {
  473. "goods_art_no": _goods_art_no,
  474. "view_name": _view_name,
  475. "real_view_name": (
  476. "组合1" if _view_name == "组合" else _view_name
  477. ),
  478. }
  479. )
  480. if len(data) <= view_index:
  481. return goods_art_no, "无法匹配"
  482. else:
  483. data.sort(key=lambda x: x["real_view_name"], reverse=False)
  484. return data[view_index]["goods_art_no"], data[view_index]["view_name"]
  485. def image_one_pic(self, goods_art_no, name, bg_color=None, return_orign=None):
  486. # 增加逻辑,获取任意货号下的组合图
  487. if "组合" in name:
  488. print("324==== goods_art_no, name", goods_art_no, name)
  489. goods_art_no, name = self.get_all_scene_list(goods_art_no, name)
  490. print("324 goods_art_no, name", goods_art_no, name)
  491. # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
  492. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  493. if "阴影" in pic_name:
  494. action_name = pic_name.replace("-阴影", "")
  495. if name != action_name:
  496. continue
  497. pp1 = PictureProcessing(pic_path)
  498. pp2 = PictureProcessing(
  499. self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)]
  500. )
  501. if not return_orign:
  502. pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(
  503. mode="pixel", base="width", value=1600
  504. )
  505. return pp1
  506. else:
  507. return pp1, pp2
  508. if not return_orign:
  509. return None
  510. else:
  511. return None, None
  512. def move_other_pic(self, move_main_pic=True):
  513. # ------------------------------移动其他图片------------------------------
  514. goods_no_main_pic_number = 0
  515. sorted_list_800 = []
  516. for goods_art_no_dict in self.goods_no_value["货号资料"]:
  517. if "800x800" not in goods_art_no_dict:
  518. continue
  519. if not goods_art_no_dict["800x800"]:
  520. continue
  521. goods_art_no = ""
  522. if "编号" in goods_art_no_dict:
  523. if goods_art_no_dict["编号"]:
  524. goods_art_no = goods_art_no_dict["编号"]
  525. if not goods_art_no:
  526. goods_art_no = goods_art_no_dict["货号"]
  527. # print("goods_art_no:", goods_art_no)
  528. # 移动颜色图=====================
  529. # goods_art_no_f = "{}/{}".format(self.out_put_dir, self.goods_no)
  530. sorted_list_800 = natsorted(goods_art_no_dict["800x800"], key=lambda x: x.split("(")[1].split(")")[0])
  531. goods_art_no_f = "{}".format(self.out_put_dir)
  532. self.create_folder(goods_art_no_f)
  533. # 放入一张主图
  534. old_pic_path_1 = sorted_list_800[0]
  535. shutil.copy(
  536. old_pic_path_1,
  537. "{}/颜色图{}{}".format(
  538. goods_art_no_f, goods_art_no, os.path.splitext(old_pic_path_1)[1]
  539. ),
  540. )
  541. # 把其他主图放入作为款号图=====================
  542. if move_main_pic:
  543. for idx,pic_path in enumerate(sorted_list_800):
  544. index = idx + 1
  545. try:
  546. split_size = pic_path.split("_")[1].split(".")[0]
  547. except:
  548. split_size = ""
  549. suffix_name = "_"+split_size if split_size else ""
  550. print("pic_path=========>",split_size)
  551. e = os.path.splitext(pic_path)[1]
  552. shutil.copy(
  553. pic_path,
  554. "{out_put_dir}/主图{goods_no}({goods_no_main_pic_number}){suffix_name}{e}".format(
  555. out_put_dir=self.out_put_dir,
  556. goods_no=goods_art_no,
  557. goods_no_main_pic_number=str(
  558. index
  559. ),
  560. e=e,
  561. suffix_name=suffix_name
  562. ),
  563. )
  564. def deal_all_main_pic(self):
  565. """
  566. 处理主图模板,如存在出图模板则进行对应处理
  567. """
  568. # 获取主图模板列表
  569. all_main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)[
  570. "main_pic_path_list"
  571. ]
  572. if not all_main_pic_path_list:
  573. return
  574. mask_pic_list = DetailBase.get_temp_pic_info(root=self.root)["mask_pic_list"]
  575. data = self.get_all_process_pics()
  576. print("========deal_all_main_pic=========主图相关素材:")
  577. view_list = [
  578. "组合",
  579. "组合2",
  580. "组合3",
  581. "组合4",
  582. "组合5",
  583. "组合6",
  584. "俯视",
  585. "侧视",
  586. "后跟",
  587. "鞋底",
  588. "内里",
  589. ]
  590. for _index, main_pic_path_list in enumerate(all_main_pic_path_list):
  591. self.check_state_end()
  592. out_path_root = "{out_put_dir}/main_image_{_index}".format(
  593. out_put_dir=self.out_put_dir, goods_no=self.goods_no, _index=_index
  594. )
  595. check_path(out_path_root)
  596. if mask_pic_list[_index]:
  597. mask_pic = mask_pic_list[_index][0]
  598. else:
  599. mask_pic = None
  600. goods_no_main_pic_number = 10
  601. # g_index 为第几个颜色货号
  602. for g_index, goods_art_no_dict in enumerate(data):
  603. goods_art_no = goods_art_no_dict["货号"]
  604. # =====================重新指定=================================
  605. _material_sort_dict = {}
  606. for index, material_dict in enumerate(goods_art_no_dict["素材"]):
  607. name = material_dict["名称"]
  608. _material_sort_dict[name] = material_dict
  609. # ======================================================
  610. file_name_index = -1
  611. for view_name in view_list:
  612. # 组合图比较特殊,为全局获取
  613. if g_index != 0:
  614. if "组合" in view_name:
  615. continue
  616. if view_name not in _material_sort_dict:
  617. continue
  618. self.check_state_end()
  619. pp_jpg, pp_png = self.image_one_pic(
  620. goods_art_no, view_name, bg_color=None, return_orign=True
  621. )
  622. if not pp_jpg:
  623. continue
  624. file_name_index += 1
  625. # 获取对应主图模板
  626. if len(main_pic_path_list) < file_name_index + 1:
  627. main_pic_path = main_pic_path_list[-1]
  628. else:
  629. main_pic_path = main_pic_path_list[file_name_index]
  630. pp_bg = PictureProcessing(main_pic_path)
  631. original_width = pp_bg.width
  632. if original_width != 1600:
  633. pp_bg = pp_bg.resize(value=1600)
  634. if mask_pic:
  635. mask_bg = PictureProcessing(mask_pic)
  636. mask_bg = mask_bg.resize(value=1600)
  637. mask_box_im = mask_bg.get_im()
  638. box_size = mask_box_im.getbbox()
  639. result_image = mask_box_im.crop(box_size)
  640. mask_width, mask_height = result_image.size
  641. mask_x, mask_y = box_size[0], box_size[1]
  642. else:
  643. mask_width, mask_height = pp_bg.size
  644. mask_width, mask_height = int(mask_width * 12 / 16), int(
  645. mask_height * 12 / 16
  646. )
  647. mask_x, mask_y = int((pp_bg.size[0] - mask_width) / 2), int(
  648. (pp_bg.size[1] - mask_height) / 2
  649. )
  650. if view_name != "后跟":
  651. pp_jpg = pp_jpg.resize(base_by_box=(mask_width, mask_height))
  652. pp_png = pp_png.resize(base_by_box=(mask_width, mask_height))
  653. # 计算粘贴的位置 mask的位置+图片在mask中的位置
  654. p_x = mask_x + int((mask_width - pp_jpg.width) / 2)
  655. p_y = mask_y + int((mask_height - pp_jpg.height) / 2)
  656. pp_bg = pp_bg.to_overlay_pic_advance(
  657. mode="pixel",
  658. top_img=pp_jpg,
  659. base="nw",
  660. value=(p_x, p_y),
  661. top_png_img=pp_png,
  662. )
  663. else:
  664. new_mask_width, new_mask_height = int(mask_width / 1.6), int(
  665. mask_height / 1.6
  666. )
  667. pp_jpg = pp_jpg.resize(
  668. base_by_box=(new_mask_width, new_mask_height)
  669. )
  670. pp_png = pp_png.resize(
  671. base_by_box=(new_mask_width, new_mask_height)
  672. )
  673. new_mask_x = int((mask_width - new_mask_width) / 2 + mask_x)
  674. new_mask_y = int((mask_height - new_mask_height) / 2 + mask_y)
  675. # 计算粘贴的位置 mask的位置+图片在mask中的位置
  676. p_x = new_mask_x + int((new_mask_width - pp_jpg.width) / 2)
  677. p_y = new_mask_y + int((new_mask_height - pp_jpg.height) / 2)
  678. pp_bg = pp_bg.to_overlay_pic_advance(
  679. mode="pixel",
  680. top_img=pp_jpg,
  681. base="nw",
  682. value=(p_x, p_y),
  683. top_png_img=pp_png,
  684. )
  685. out_pci_mode = "." + settings.getSysConfigs(
  686. "basic_configs", "image_out_format", "png"
  687. )
  688. goods_no_main_pic_number += 1
  689. out_pic_path = "{out_path_root}/{goods_no}({goods_no_main_pic_number}){pic_mode}".format(
  690. out_path_root=out_path_root,
  691. goods_no=self.goods_no,
  692. goods_no_main_pic_number=goods_no_main_pic_number,
  693. pic_mode=out_pci_mode,
  694. )
  695. out_pci_factor = float(
  696. 1
  697. if settings.getSysConfigs(
  698. "basic_configs", "image_sharpening", "1"
  699. )
  700. == ""
  701. else settings.getSysConfigs(
  702. "basic_configs", "image_sharpening", "1"
  703. )
  704. )
  705. if out_pci_factor > 1.0:
  706. print("图片锐化处理")
  707. pp_bg = pp_bg.sharpen_image(factor=out_pci_factor)
  708. if original_width < 1600:
  709. pp_bg = pp_bg.resize(value=original_width)
  710. print("392 out_pic_path", out_pic_path)
  711. if out_pci_mode == ".jpg":
  712. pp_bg.save_as_rgb(out_pic_path)
  713. elif out_pci_mode == ".png":
  714. pp_bg.save_as_png(out_pic_path)
  715. else:
  716. pp_bg.save_as_other(out_pic_path, out_pci_mode.split(".")[-1])
  717. def add_pic(self, detailed_images):
  718. self.check_state_end()
  719. todo_detailed_images = []
  720. detailed_images = [x for x in detailed_images if x]
  721. if not detailed_images:
  722. return
  723. for i in detailed_images:
  724. if isinstance(i, list):
  725. for n in i:
  726. todo_detailed_images.append(n)
  727. else:
  728. todo_detailed_images.append(i)
  729. page_len = 0
  730. for index, pp in enumerate(todo_detailed_images):
  731. page_len += pp.height
  732. bg_im = Image.new("RGB", (pp.width, page_len), (255, 255, 255))
  733. n = 0
  734. for index, pp in enumerate(todo_detailed_images):
  735. bg_im.paste(pp.im, (0, n))
  736. n += pp.height
  737. return bg_im
  738. # 通用方法,用于写文字
  739. def add_text_list(self, text_list, spacing=5, base="wn", base_width=1600):
  740. text_list = [x for x in text_list if x["text"]]
  741. # print(text_list)
  742. # spacing 行间距
  743. text_image_list = []
  744. max_w = 0
  745. total_h = 0
  746. for text_data in text_list:
  747. _pp = PictureProcessing("RGBA", (base_width, 1200), (255, 255, 255, 0))
  748. if base == "wn" or base == "nw":
  749. align = "left"
  750. anchor = None
  751. value = (0, 250)
  752. if base == "cn" or base == "nc":
  753. align = "center"
  754. anchor = "mm"
  755. value = (int(base_width / 2), 250)
  756. if base == "en" or base == "ne":
  757. align = "right"
  758. anchor = "rs"
  759. value = (base_width - 10, 250)
  760. _pp = _pp.get_text_image_advanced(
  761. value=value,
  762. font=text_data["font"],
  763. text=text_data["text"],
  764. align=align,
  765. anchor=anchor,
  766. spacing=5,
  767. fill=text_data["fill"],
  768. return_mode="min_image",
  769. margins=(0, 0, 0, 0),
  770. )
  771. text_image_list.append(_pp)
  772. if _pp.width > max_w:
  773. max_w = _pp.width
  774. total_h += _pp.height
  775. if "spacing" in text_data:
  776. total_h += text_data["spacing"]
  777. if not text_image_list:
  778. return None
  779. #
  780. bg = PictureProcessing("RGBA", (max_w, total_h * 3), (0, 0, 0, 0))
  781. y = 0
  782. for text_image, text_data in zip(text_image_list, text_list):
  783. bg = bg.paste_img(top_img=text_image, value=(0, y), base=base)
  784. y += spacing + text_image.height
  785. if "spacing" in text_data:
  786. y += text_data["spacing"]
  787. bg = bg.crop(mode="min")
  788. # _ = bg.paste_img_invert(top_img=PictureProcessing("RGB", (bg.width,bg.height), (255, 255, 255)))
  789. # _.show()
  790. return bg
  791. def generate_font_list_to_pic(self):
  792. font_path_list = [
  793. r"resources\ttf\puhui\Bold.ttf",
  794. r"resources\ttf\puhui\Medium.ttf",
  795. r"resources\ttf\puhui\Heavy.ttf",
  796. r"resources\ttf\puhui\Light.ttf",
  797. r"resources\ttf\puhui\Regular.ttf",
  798. ]
  799. text_v_list = [
  800. "这是一段话Bold",
  801. "这是一段话Medium",
  802. "这是一段话Heavy",
  803. "这是一段话Light",
  804. "这是一段话Regular",
  805. ]
  806. detailed_images = []
  807. for font_path, text in zip(font_path_list, text_v_list):
  808. text_list = []
  809. for size in range(26, 80, 2):
  810. font = ImageFont.truetype(font_path, size)
  811. text_list.append(
  812. {
  813. "text": "{}-字号{}".format(text, size),
  814. "font": font,
  815. "fill": (110, 110, 110),
  816. }
  817. )
  818. text_image = self.add_text_list(text_list, spacing=15, base="nw")
  819. text_image = text_image.crop(mode="min")
  820. text_image = text_image.paste_img_invert(
  821. top_img=PictureProcessing("RGB", text_image.size, (255, 255, 255))
  822. )
  823. detailed_images.append(text_image)
  824. return PictureProcessing(im=self.add_pic(detailed_images))
  825. # 图片分段,每段至少大于N长度
  826. def pp_pic_subsection(self, pp: PictureProcessing, one_height=3200):
  827. total_height = pp.height
  828. now_height = 0
  829. detailed_images = []
  830. while 1:
  831. if now_height + one_height < total_height:
  832. h1 = now_height
  833. h2 = now_height + one_height
  834. bbox = (0, h1, pp.width, h2)
  835. # print("bbox1", bbox)
  836. detailed_images.append(pp.crop(bbox=bbox))
  837. now_height = now_height + one_height
  838. continue
  839. if now_height + one_height >= total_height:
  840. h1 = now_height
  841. h2 = total_height
  842. bbox = (0, h1, pp.width, h2)
  843. # print("bbox2", bbox)
  844. detailed_images.append(pp.crop(bbox=bbox))
  845. break
  846. return detailed_images