detail_generate_base.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  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. # ----------如果是红蜻蜓则创建同颜色下的其他货号颜色文件夹---------------
  188. if settings.PROJECT == "红蜻蜓":
  189. if "data_all_goods_art_info" in self.goods_no_value:
  190. # 数据格式:[{'number': '14250232', 'goods_art_no': 'AC52001173', 'color': '杏色'}, ]
  191. for pic_data in self.goods_no_value["货号资料"]:
  192. if "颜色名称" not in pic_data:
  193. continue
  194. color_name = pic_data["颜色名称"]
  195. # {out_put_dir}/{goods_number}
  196. color_file_path = "{out_put_dir}".format(
  197. out_put_dir=self.out_put_dir,
  198. goods_no=self.goods_no,
  199. goods_number=pic_data["编号"],
  200. )
  201. for i in self.goods_no_value["data_all_goods_art_info"]:
  202. if color_name in i["color"]:
  203. # {out_put_dir}/{goods_no}/{goods_number}
  204. new_path = "{out_put_dir}".format(
  205. out_put_dir=self.out_put_dir,
  206. goods_no=self.goods_no,
  207. goods_number="NUM{}".format(i["number"]),
  208. )
  209. if not os.path.exists(new_path):
  210. # 创建文件夹
  211. os.makedirs(new_path)
  212. self.move_one_pic(
  213. color_file_path,
  214. new_path,
  215. "NUM{}".format(i["number"]),
  216. )
  217. scp_path = "{out_put_dir}".format(
  218. out_put_dir=self.out_put_dir
  219. )
  220. logger.info(f"copyImage 前数据校验:{self.goods_no_value}")
  221. if self.get_text_value("模特图"):
  222. model_pic = self.get_text_value("模特图")
  223. self.copyImage(model_pic, f"{scp_path}/模特图.jpg")
  224. if self.get_text_value("场景图"):
  225. scene_pic = self.get_text_value("场景图")
  226. self.copyImage(scene_pic, f"{scp_path}/场景图.jpg")
  227. return True
  228. def copyImage(self,src_path,limit_path):
  229. try:
  230. shutil.copy(src_path, limit_path)
  231. except Exception as e:
  232. logger.info(f"copyImage 复制模特图/场景图出错:{str(e)}",src_path,limit_path)
  233. def concatAigcImage(self,image_path,resize=1600,bg_color=(255,255,255)):
  234. """拼接模特图场景图"""
  235. try:
  236. mote_img = PictureProcessing(image_path)
  237. mote_img = mote_img.resize(value=resize)
  238. bg_img = PictureProcessing(
  239. "RGB", (mote_img.width, mote_img.height), bg_color
  240. )
  241. bg_img = bg_img.paste_img(top_img=mote_img, base="nc", value=(0, 0))
  242. return bg_img
  243. except Exception as e:
  244. logger.info(f"copyImage 拼接模特图/场景图出错:{str(e)}")
  245. return
  246. # 移动一张图片到新的文件夹
  247. def move_one_pic(self, old_path, new_path, new_name):
  248. image_file = os.listdir(old_path)[0]
  249. old_image_path = "{}/{}".format(old_path, image_file)
  250. image_e = os.path.splitext(image_file)[1]
  251. new_image_path = "{}/{}{}".format(new_path, new_name, image_e)
  252. shutil.copy(old_image_path, new_image_path)
  253. # 生成各个详情图切片
  254. def deal_details(self):
  255. try:
  256. detailed_images = []
  257. for index, func in enumerate(self.deal_pic_func_list):
  258. image_pp = func()
  259. if not self.assigned_page_list:
  260. self.image_list_append(detailed_images, image_pp)
  261. else:
  262. index = "{}".format(index + 1)
  263. if index in self.assigned_page_list:
  264. self.image_list_append(detailed_images, image_pp)
  265. else:
  266. self.image_list_append(detailed_images, {"mes": "不生成"})
  267. return [x for x in detailed_images if x]
  268. except KeyError as e:
  269. raise UnicornException(f"缺少详情页资料:[{e}],请检查系统商品信息或excel是否缺少该字段")
  270. except Exception as e:
  271. raise UnicornException(str(e))
  272. # 生成拼接的图片
  273. def generate_spliced_picture(self):
  274. # sendMessageAsync(
  275. # code=0,
  276. # msg="正在生成详情拼接图",
  277. # msg_type="detail_progress",
  278. # data={
  279. # "goods_no": self.goods_no,
  280. # "temp_name": self.template_name,
  281. # "status": "进行中",
  282. # "goods_art_nos": self.goods_art_nos,
  283. # },
  284. # )
  285. detail_path = "{out_put_dir}/切片图-{template_name}".format(
  286. out_put_dir=self.out_put_dir, goods_no=self.goods_no,template_name=self.template_name
  287. )
  288. if not os.path.exists(detail_path):
  289. return
  290. detailed_images = []
  291. for image_data in get_images(detail_path):
  292. detailed_images.append(PictureProcessing(image_data["file_path"]))
  293. # 生成拼接图
  294. img = self.add_pic(detailed_images)
  295. join_path = "{out_put_dir}/详情页-{template_name}".format(
  296. out_put_dir=self.out_put_dir, goods_no=self.goods_no,template_name=self.template_name
  297. )
  298. # self.create_folder(join_path)
  299. img.save("{}.jpg".format(join_path), format="JPEG")
  300. def image_list_append(self, image_list: list, data):
  301. self.check_state_end()
  302. if isinstance(data, list):
  303. image_list.extend(data)
  304. else:
  305. image_list.append(data)
  306. def save_to_png(self, detailed_images, detail_path):
  307. self.check_state_end()
  308. for index, pp in enumerate(detailed_images):
  309. if isinstance(pp, dict):
  310. continue
  311. pp.im.save(
  312. "{}/{}({}).png".format(
  313. detail_path, self.goods_no, str(index + 11).zfill(2)
  314. )
  315. )
  316. def check_state_end(self):
  317. if self.windows is not None:
  318. if self.windows.state == 99:
  319. raise "用户主动取消"
  320. @classmethod
  321. def get_temp_pic_info(cls, root):
  322. """
  323. 获取详情页模板中的信息
  324. """
  325. main_pic_list = []
  326. mask_pic_list = []
  327. if os.path.exists(r"{}\main_image".format(root)):
  328. for _name in os.listdir(r"{}\main_image".format(root)):
  329. _path = r"{}\main_image\{}".format(root, _name)
  330. if os.path.isdir(_path):
  331. main_pic_list.append([x["file_path"] for x in get_images(_path)])
  332. mask_pic_list.append(
  333. [x["file_path"] for x in get_image_mask(_path)]
  334. )
  335. _l = get_images(r"{}\show".format(root))
  336. temp_pic_path = _l[0]["file_path"] if _l else None
  337. other_pic_list = [x["file_path"] for x in get_images(r"{}".format(root))]
  338. return {
  339. "main_pic_path_list": main_pic_list,
  340. "temp_pic_path": temp_pic_path,
  341. "mask_pic_list": mask_pic_list,
  342. "other_pic_path_list": other_pic_list,
  343. }
  344. def init(self):
  345. for goods_art_no_value in self.goods_no_value["货号资料"]:
  346. self.data[goods_art_no_value["货号"]] = {
  347. "pics": goods_art_no_value["pics"],
  348. "pic_is_deal": {},
  349. }
  350. def get_text_value(self, key, subsection_len=0):
  351. try:
  352. text = ""
  353. if key in self.goods_no_value:
  354. if self.goods_no_value[key]:
  355. text = str(self.goods_no_value[key])
  356. text = text.replace(r"\n", "\n")
  357. # if key in ["跟高", "鞋宽", "帮高", "脚掌围", "鞋长"]:
  358. # if text:
  359. # text = text.split(".")[0]
  360. if subsection_len != 0:
  361. text = text.split("\n")
  362. text = [x for x in text if x]
  363. if len(text) == 2:
  364. text_1 = text[0]
  365. text_2 = text[1]
  366. return text_1, text_2
  367. else:
  368. if text:
  369. text_1 = text[0]
  370. else:
  371. text_1 = ""
  372. text_2 = ""
  373. return text_1, text_2
  374. return text
  375. except:
  376. raise UnicornException(f"缺少货号资料:[{key}],请检查系统商品信息或excel是否缺少该字段")
  377. def create_folder(self, path):
  378. if not os.path.exists(path):
  379. os.makedirs(path)
  380. def get_all_process_pics(self):
  381. """
  382. 获取所有颜色的过程图片
  383. data = [
  384. {"货号": "",
  385. "素材": [{
  386. "名称": "俯视",
  387. "抠图": "路径1",
  388. "阴影": "路径2"
  389. }, ]},
  390. ]
  391. """
  392. return_data = []
  393. for goods_art_no in self.data:
  394. goods_art_no_dict = {
  395. "货号": goods_art_no,
  396. "素材": [],
  397. }
  398. # 图片数据重新排序
  399. pic_data = []
  400. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  401. root_path, file_name = os.path.split(pic_path)
  402. pic_data.append(file_name)
  403. pic_data = natsorted(pic_data, alg=ns.PATH)
  404. for file_name in pic_data:
  405. if "阴影" in file_name:
  406. _, action_name, _ = file_name.split("_")
  407. pic_path = self.data[goods_art_no]["pics"][
  408. "{}-阴影".format(action_name)
  409. ]
  410. pic_cutout_path = self.data[goods_art_no]["pics"][
  411. "{}-抠图".format(action_name)
  412. ]
  413. if os.path.exists(pic_path) and os.path.exists(pic_cutout_path):
  414. goods_art_no_dict["素材"].append(
  415. {
  416. "名称": action_name,
  417. "抠图": pic_cutout_path,
  418. "阴影": pic_path,
  419. }
  420. )
  421. return_data.append(goods_art_no_dict)
  422. return return_data
  423. def get_overlay_pic_from_dict(
  424. self, goods_art_no, color_name, bg_color
  425. ) -> PictureProcessing:
  426. self.check_state_end()
  427. # 增加逻辑,获取任意货号下的组合图
  428. if "组合" in color_name:
  429. goods_art_no, color_name = self.get_all_scene_list(goods_art_no, color_name)
  430. key = "{}-{}-{}".format(goods_art_no, color_name, bg_color)
  431. if key in self.overlay_pic_dict:
  432. return self.overlay_pic_dict[key]
  433. if goods_art_no in self.data:
  434. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  435. if "阴影" in pic_name:
  436. action_name = pic_name.replace("-阴影", "")
  437. if action_name == color_name:
  438. pp1 = PictureProcessing(pic_path)
  439. pp2 = PictureProcessing(
  440. self.data[goods_art_no]["pics"][
  441. "{}-抠图".format(action_name)
  442. ]
  443. )
  444. pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(
  445. mode="pixel", base="width", value=1600
  446. )
  447. self.overlay_pic_dict[key] = pp1
  448. if key in self.overlay_pic_dict:
  449. return self.overlay_pic_dict[key]
  450. def image_init(self, bg_color=(246, 246, 246)):
  451. # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
  452. for goods_art_no in self.data:
  453. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  454. if "阴影" in pic_name:
  455. action_name = pic_name.replace("-阴影", "")
  456. pp1 = PictureProcessing(pic_path)
  457. pp2 = PictureProcessing(
  458. self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)]
  459. )
  460. pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(
  461. mode="pixel", base="width", value=1600
  462. )
  463. self.data[goods_art_no]["pic_is_deal"][action_name] = pp1
  464. # 获取任意货号的场景图,优先取指定货号;
  465. # 调整,按顺序从货号列表中提取所有组合图
  466. def get_all_scene_info(self, goods_art_no):
  467. data = []
  468. # 收集所有组合图
  469. # 找任意一个有组合图的货号
  470. for goods_art_no_dict in self.goods_no_value["货号资料"]:
  471. _goods_art_no = goods_art_no_dict["货号"]
  472. _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
  473. for _view_name in _view_name_list:
  474. if "组合" not in _view_name:
  475. continue
  476. return _goods_art_no
  477. return goods_art_no
  478. def get_all_scene_list(self, goods_art_no, view_name: str):
  479. if "组合" == view_name:
  480. view_name = "组合1"
  481. try:
  482. view_index = int(view_name.replace("组合", "")) - 1
  483. except:
  484. return goods_art_no, "无法匹配"
  485. data = []
  486. # 收集所有组合图
  487. # 找任意一个有组合图的货号
  488. for goods_art_no_dict in self.goods_no_value["货号资料"]:
  489. _goods_art_no = goods_art_no_dict["货号"]
  490. if _goods_art_no != goods_art_no:
  491. continue
  492. _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]])
  493. for _view_name in _view_name_list:
  494. if "组合" not in _view_name:
  495. continue
  496. data.append(
  497. {
  498. "goods_art_no": _goods_art_no,
  499. "view_name": _view_name,
  500. "real_view_name": (
  501. "组合1" if _view_name == "组合" else _view_name
  502. ),
  503. }
  504. )
  505. if len(data) <= view_index:
  506. return goods_art_no, "无法匹配"
  507. else:
  508. data.sort(key=lambda x: x["real_view_name"], reverse=False)
  509. return data[view_index]["goods_art_no"], data[view_index]["view_name"]
  510. def image_one_pic(self, goods_art_no, name, bg_color=None, return_orign=None):
  511. # 增加逻辑,获取任意货号下的组合图
  512. if "组合" in name:
  513. print("324==== goods_art_no, name", goods_art_no, name)
  514. goods_art_no, name = self.get_all_scene_list(goods_art_no, name)
  515. print("324 goods_art_no, name", goods_art_no, name)
  516. # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸
  517. for pic_name, pic_path in self.data[goods_art_no]["pics"].items():
  518. if "阴影" in pic_name:
  519. action_name = pic_name.replace("-阴影", "")
  520. if name != action_name:
  521. continue
  522. pp1 = PictureProcessing(pic_path)
  523. pp2 = PictureProcessing(
  524. self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)]
  525. )
  526. if not return_orign:
  527. pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize(
  528. mode="pixel", base="width", value=1600
  529. )
  530. return pp1
  531. else:
  532. return pp1, pp2
  533. if not return_orign:
  534. return None
  535. else:
  536. return None, None
  537. def move_other_pic(self, move_main_pic=True):
  538. # ------------------------------移动其他图片------------------------------
  539. goods_no_main_pic_number = 0
  540. sorted_list_800 = []
  541. for goods_art_no_dict in self.goods_no_value["货号资料"]:
  542. if "800x800" not in goods_art_no_dict:
  543. continue
  544. if not goods_art_no_dict["800x800"]:
  545. continue
  546. goods_art_no = ""
  547. if "编号" in goods_art_no_dict:
  548. if goods_art_no_dict["编号"]:
  549. goods_art_no = goods_art_no_dict["编号"]
  550. if not goods_art_no:
  551. goods_art_no = goods_art_no_dict["货号"]
  552. # print("goods_art_no:", goods_art_no)
  553. # 移动颜色图=====================
  554. # goods_art_no_f = "{}/{}".format(self.out_put_dir, self.goods_no)
  555. sorted_list_800 = natsorted(goods_art_no_dict["800x800"], key=lambda x: x.split("(")[1].split(")")[0])
  556. goods_art_no_f = "{}".format(self.out_put_dir)
  557. self.create_folder(goods_art_no_f)
  558. # 放入一张主图
  559. old_pic_path_1 = sorted_list_800[0]
  560. shutil.copy(
  561. old_pic_path_1,
  562. "{}/颜色图{}{}".format(
  563. goods_art_no_f, goods_art_no, os.path.splitext(old_pic_path_1)[1]
  564. ),
  565. )
  566. # 把其他主图放入作为款号图=====================
  567. if move_main_pic:
  568. for idx,pic_path in enumerate(sorted_list_800):
  569. index = idx + 1
  570. try:
  571. split_size = pic_path.split("_")[1].split(".")[0]
  572. except:
  573. split_size = ""
  574. suffix_name = "_"+split_size if split_size else ""
  575. print("pic_path=========>",split_size)
  576. e = os.path.splitext(pic_path)[1]
  577. shutil.copy(
  578. pic_path,
  579. "{out_put_dir}/主图{goods_no}({goods_no_main_pic_number}){suffix_name}{e}".format(
  580. out_put_dir=self.out_put_dir,
  581. goods_no=goods_art_no,
  582. goods_no_main_pic_number=str(
  583. index
  584. ),
  585. e=e,
  586. suffix_name=suffix_name
  587. ),
  588. )
  589. def deal_all_main_pic(self):
  590. """
  591. 处理主图模板,如存在出图模板则进行对应处理
  592. """
  593. # 获取主图模板列表
  594. all_main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)[
  595. "main_pic_path_list"
  596. ]
  597. if not all_main_pic_path_list:
  598. return
  599. mask_pic_list = DetailBase.get_temp_pic_info(root=self.root)["mask_pic_list"]
  600. data = self.get_all_process_pics()
  601. print("========deal_all_main_pic=========主图相关素材:")
  602. view_list = [
  603. "组合",
  604. "组合2",
  605. "组合3",
  606. "组合4",
  607. "组合5",
  608. "组合6",
  609. "俯视",
  610. "侧视",
  611. "后跟",
  612. "鞋底",
  613. "内里",
  614. ]
  615. for _index, main_pic_path_list in enumerate(all_main_pic_path_list):
  616. self.check_state_end()
  617. out_path_root = "{out_put_dir}/main_image_{_index}".format(
  618. out_put_dir=self.out_put_dir, goods_no=self.goods_no, _index=_index
  619. )
  620. check_path(out_path_root)
  621. if mask_pic_list[_index]:
  622. mask_pic = mask_pic_list[_index][0]
  623. else:
  624. mask_pic = None
  625. goods_no_main_pic_number = 10
  626. # g_index 为第几个颜色货号
  627. for g_index, goods_art_no_dict in enumerate(data):
  628. goods_art_no = goods_art_no_dict["货号"]
  629. # =====================重新指定=================================
  630. _material_sort_dict = {}
  631. for index, material_dict in enumerate(goods_art_no_dict["素材"]):
  632. name = material_dict["名称"]
  633. _material_sort_dict[name] = material_dict
  634. # ======================================================
  635. file_name_index = -1
  636. for view_name in view_list:
  637. # 组合图比较特殊,为全局获取
  638. if g_index != 0:
  639. if "组合" in view_name:
  640. continue
  641. if view_name not in _material_sort_dict:
  642. continue
  643. self.check_state_end()
  644. pp_jpg, pp_png = self.image_one_pic(
  645. goods_art_no, view_name, bg_color=None, return_orign=True
  646. )
  647. if not pp_jpg:
  648. continue
  649. file_name_index += 1
  650. # 获取对应主图模板
  651. if len(main_pic_path_list) < file_name_index + 1:
  652. main_pic_path = main_pic_path_list[-1]
  653. else:
  654. main_pic_path = main_pic_path_list[file_name_index]
  655. pp_bg = PictureProcessing(main_pic_path)
  656. original_width = pp_bg.width
  657. if original_width != 1600:
  658. pp_bg = pp_bg.resize(value=1600)
  659. if mask_pic:
  660. mask_bg = PictureProcessing(mask_pic)
  661. mask_bg = mask_bg.resize(value=1600)
  662. mask_box_im = mask_bg.get_im()
  663. box_size = mask_box_im.getbbox()
  664. result_image = mask_box_im.crop(box_size)
  665. mask_width, mask_height = result_image.size
  666. mask_x, mask_y = box_size[0], box_size[1]
  667. else:
  668. mask_width, mask_height = pp_bg.size
  669. mask_width, mask_height = int(mask_width * 12 / 16), int(
  670. mask_height * 12 / 16
  671. )
  672. mask_x, mask_y = int((pp_bg.size[0] - mask_width) / 2), int(
  673. (pp_bg.size[1] - mask_height) / 2
  674. )
  675. if view_name != "后跟":
  676. pp_jpg = pp_jpg.resize(base_by_box=(mask_width, mask_height))
  677. pp_png = pp_png.resize(base_by_box=(mask_width, mask_height))
  678. # 计算粘贴的位置 mask的位置+图片在mask中的位置
  679. p_x = mask_x + int((mask_width - pp_jpg.width) / 2)
  680. p_y = mask_y + int((mask_height - pp_jpg.height) / 2)
  681. pp_bg = pp_bg.to_overlay_pic_advance(
  682. mode="pixel",
  683. top_img=pp_jpg,
  684. base="nw",
  685. value=(p_x, p_y),
  686. top_png_img=pp_png,
  687. )
  688. else:
  689. new_mask_width, new_mask_height = int(mask_width / 1.6), int(
  690. mask_height / 1.6
  691. )
  692. pp_jpg = pp_jpg.resize(
  693. base_by_box=(new_mask_width, new_mask_height)
  694. )
  695. pp_png = pp_png.resize(
  696. base_by_box=(new_mask_width, new_mask_height)
  697. )
  698. new_mask_x = int((mask_width - new_mask_width) / 2 + mask_x)
  699. new_mask_y = int((mask_height - new_mask_height) / 2 + mask_y)
  700. # 计算粘贴的位置 mask的位置+图片在mask中的位置
  701. p_x = new_mask_x + int((new_mask_width - pp_jpg.width) / 2)
  702. p_y = new_mask_y + int((new_mask_height - pp_jpg.height) / 2)
  703. pp_bg = pp_bg.to_overlay_pic_advance(
  704. mode="pixel",
  705. top_img=pp_jpg,
  706. base="nw",
  707. value=(p_x, p_y),
  708. top_png_img=pp_png,
  709. )
  710. out_pci_mode = "." + settings.getSysConfigs(
  711. "basic_configs", "image_out_format", "png"
  712. )
  713. goods_no_main_pic_number += 1
  714. out_pic_path = "{out_path_root}/{goods_no}({goods_no_main_pic_number}){pic_mode}".format(
  715. out_path_root=out_path_root,
  716. goods_no=self.goods_no,
  717. goods_no_main_pic_number=goods_no_main_pic_number,
  718. pic_mode=out_pci_mode,
  719. )
  720. out_pci_factor = float(
  721. 1
  722. if settings.getSysConfigs(
  723. "basic_configs", "image_sharpening", "1"
  724. )
  725. == ""
  726. else settings.getSysConfigs(
  727. "basic_configs", "image_sharpening", "1"
  728. )
  729. )
  730. if out_pci_factor > 1.0:
  731. print("图片锐化处理")
  732. pp_bg = pp_bg.sharpen_image(factor=out_pci_factor)
  733. if original_width < 1600:
  734. pp_bg = pp_bg.resize(value=original_width)
  735. print("392 out_pic_path", out_pic_path)
  736. if out_pci_mode == ".jpg":
  737. pp_bg.save_as_rgb(out_pic_path)
  738. elif out_pci_mode == ".png":
  739. pp_bg.save_as_png(out_pic_path)
  740. else:
  741. pp_bg.save_as_other(out_pic_path, out_pci_mode.split(".")[-1])
  742. def add_pic(self, detailed_images):
  743. self.check_state_end()
  744. todo_detailed_images = []
  745. detailed_images = [x for x in detailed_images if x]
  746. if not detailed_images:
  747. return
  748. for i in detailed_images:
  749. if isinstance(i, list):
  750. for n in i:
  751. todo_detailed_images.append(n)
  752. else:
  753. todo_detailed_images.append(i)
  754. page_len = 0
  755. for index, pp in enumerate(todo_detailed_images):
  756. page_len += pp.height
  757. bg_im = Image.new("RGB", (pp.width, page_len), (255, 255, 255))
  758. n = 0
  759. for index, pp in enumerate(todo_detailed_images):
  760. bg_im.paste(pp.im, (0, n))
  761. n += pp.height
  762. return bg_im
  763. # 通用方法,用于写文字
  764. def add_text_list(self, text_list, spacing=5, base="wn", base_width=1600):
  765. text_list = [x for x in text_list if x["text"]]
  766. # print(text_list)
  767. # spacing 行间距
  768. text_image_list = []
  769. max_w = 0
  770. total_h = 0
  771. for text_data in text_list:
  772. _pp = PictureProcessing("RGBA", (base_width, 1200), (255, 255, 255, 0))
  773. if base == "wn" or base == "nw":
  774. align = "left"
  775. anchor = None
  776. value = (0, 250)
  777. if base == "cn" or base == "nc":
  778. align = "center"
  779. anchor = "mm"
  780. value = (int(base_width / 2), 250)
  781. if base == "en" or base == "ne":
  782. align = "right"
  783. anchor = "rs"
  784. value = (base_width - 10, 250)
  785. _pp = _pp.get_text_image_advanced(
  786. value=value,
  787. font=text_data["font"],
  788. text=text_data["text"],
  789. align=align,
  790. anchor=anchor,
  791. spacing=5,
  792. fill=text_data["fill"],
  793. return_mode="min_image",
  794. margins=(0, 0, 0, 0),
  795. )
  796. text_image_list.append(_pp)
  797. if _pp.width > max_w:
  798. max_w = _pp.width
  799. total_h += _pp.height
  800. if "spacing" in text_data:
  801. total_h += text_data["spacing"]
  802. if not text_image_list:
  803. return None
  804. #
  805. bg = PictureProcessing("RGBA", (max_w, total_h * 3), (0, 0, 0, 0))
  806. y = 0
  807. for text_image, text_data in zip(text_image_list, text_list):
  808. bg = bg.paste_img(top_img=text_image, value=(0, y), base=base)
  809. y += spacing + text_image.height
  810. if "spacing" in text_data:
  811. y += text_data["spacing"]
  812. bg = bg.crop(mode="min")
  813. # _ = bg.paste_img_invert(top_img=PictureProcessing("RGB", (bg.width,bg.height), (255, 255, 255)))
  814. # _.show()
  815. return bg
  816. def generate_font_list_to_pic(self):
  817. font_path_list = [
  818. r"resources\ttf\puhui\Bold.ttf",
  819. r"resources\ttf\puhui\Medium.ttf",
  820. r"resources\ttf\puhui\Heavy.ttf",
  821. r"resources\ttf\puhui\Light.ttf",
  822. r"resources\ttf\puhui\Regular.ttf",
  823. ]
  824. text_v_list = [
  825. "这是一段话Bold",
  826. "这是一段话Medium",
  827. "这是一段话Heavy",
  828. "这是一段话Light",
  829. "这是一段话Regular",
  830. ]
  831. detailed_images = []
  832. for font_path, text in zip(font_path_list, text_v_list):
  833. text_list = []
  834. for size in range(26, 80, 2):
  835. font = ImageFont.truetype(font_path, size)
  836. text_list.append(
  837. {
  838. "text": "{}-字号{}".format(text, size),
  839. "font": font,
  840. "fill": (110, 110, 110),
  841. }
  842. )
  843. text_image = self.add_text_list(text_list, spacing=15, base="nw")
  844. text_image = text_image.crop(mode="min")
  845. text_image = text_image.paste_img_invert(
  846. top_img=PictureProcessing("RGB", text_image.size, (255, 255, 255))
  847. )
  848. detailed_images.append(text_image)
  849. return PictureProcessing(im=self.add_pic(detailed_images))
  850. # 图片分段,每段至少大于N长度
  851. def pp_pic_subsection(self, pp: PictureProcessing, one_height=3200):
  852. total_height = pp.height
  853. now_height = 0
  854. detailed_images = []
  855. while 1:
  856. if now_height + one_height < total_height:
  857. h1 = now_height
  858. h2 = now_height + one_height
  859. bbox = (0, h1, pp.width, h2)
  860. # print("bbox1", bbox)
  861. detailed_images.append(pp.crop(bbox=bbox))
  862. now_height = now_height + one_height
  863. continue
  864. if now_height + one_height >= total_height:
  865. h1 = now_height
  866. h2 = total_height
  867. bbox = (0, h1, pp.width, h2)
  868. # print("bbox2", bbox)
  869. detailed_images.append(pp.crop(bbox=bbox))
  870. break
  871. return detailed_images