detail_generate_base.py 37 KB

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