detail_generate_base.py 36 KB

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