detail_generate_base.py 37 KB

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