detail_generate_base.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  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 move_other_pic(self, move_main_pic=True):
  518. # ------------------------------移动其他图片------------------------------
  519. goods_no_main_pic_number = 0
  520. sorted_list_800 = []
  521. for goods_art_no_dict in self.goods_no_value["货号资料"]:
  522. if "800x800" not in goods_art_no_dict:
  523. continue
  524. if not goods_art_no_dict["800x800"]:
  525. continue
  526. goods_art_no = ""
  527. if "编号" in goods_art_no_dict:
  528. if goods_art_no_dict["编号"]:
  529. goods_art_no = goods_art_no_dict["编号"]
  530. if not goods_art_no:
  531. goods_art_no = goods_art_no_dict["货号"]
  532. # print("goods_art_no:", goods_art_no)
  533. # 移动颜色图=====================
  534. # goods_art_no_f = "{}/{}".format(self.out_put_dir, self.goods_no)
  535. sorted_list_800 = natsorted(goods_art_no_dict["800x800"], key=lambda x: x.split("(")[1].split(")")[0])
  536. goods_art_no_f = "{}".format(self.out_put_dir)
  537. self.create_folder(goods_art_no_f)
  538. # 放入一张主图
  539. old_pic_path_1 = sorted_list_800[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 idx,pic_path in enumerate(sorted_list_800):
  549. index = idx + 1
  550. try:
  551. split_size = pic_path.split("_")[1].split(".")[0]
  552. except:
  553. split_size = ""
  554. suffix_name = "_"+split_size if split_size else ""
  555. print("pic_path=========>",split_size)
  556. e = os.path.splitext(pic_path)[1]
  557. shutil.copy(
  558. pic_path,
  559. "{out_put_dir}/主图{goods_no}({goods_no_main_pic_number}){suffix_name}{e}".format(
  560. out_put_dir=self.out_put_dir,
  561. goods_no=goods_art_no,
  562. goods_no_main_pic_number=str(
  563. index
  564. ),
  565. e=e,
  566. suffix_name=suffix_name
  567. ),
  568. )
  569. def deal_all_main_pic(self):
  570. """
  571. 处理主图模板,如存在出图模板则进行对应处理
  572. """
  573. # 获取主图模板列表
  574. all_main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)[
  575. "main_pic_path_list"
  576. ]
  577. if not all_main_pic_path_list:
  578. return
  579. mask_pic_list = DetailBase.get_temp_pic_info(root=self.root)["mask_pic_list"]
  580. data = self.get_all_process_pics()
  581. print("========deal_all_main_pic=========主图相关素材:")
  582. view_list = [
  583. "组合",
  584. "组合2",
  585. "组合3",
  586. "组合4",
  587. "组合5",
  588. "组合6",
  589. "俯视",
  590. "侧视",
  591. "后跟",
  592. "鞋底",
  593. "内里",
  594. ]
  595. for _index, main_pic_path_list in enumerate(all_main_pic_path_list):
  596. self.check_state_end()
  597. out_path_root = "{out_put_dir}/main_image_{_index}".format(
  598. out_put_dir=self.out_put_dir, goods_no=self.goods_no, _index=_index
  599. )
  600. check_path(out_path_root)
  601. if mask_pic_list[_index]:
  602. mask_pic = mask_pic_list[_index][0]
  603. else:
  604. mask_pic = None
  605. goods_no_main_pic_number = 10
  606. # g_index 为第几个颜色货号
  607. for g_index, goods_art_no_dict in enumerate(data):
  608. goods_art_no = goods_art_no_dict["货号"]
  609. # =====================重新指定=================================
  610. _material_sort_dict = {}
  611. for index, material_dict in enumerate(goods_art_no_dict["素材"]):
  612. name = material_dict["名称"]
  613. _material_sort_dict[name] = material_dict
  614. # ======================================================
  615. file_name_index = -1
  616. for view_name in view_list:
  617. # 组合图比较特殊,为全局获取
  618. if g_index != 0:
  619. if "组合" in view_name:
  620. continue
  621. if view_name not in _material_sort_dict:
  622. continue
  623. self.check_state_end()
  624. pp_jpg, pp_png = self.image_one_pic(
  625. goods_art_no, view_name, bg_color=None, return_orign=True
  626. )
  627. if not pp_jpg:
  628. continue
  629. file_name_index += 1
  630. # 获取对应主图模板
  631. if len(main_pic_path_list) < file_name_index + 1:
  632. main_pic_path = main_pic_path_list[-1]
  633. else:
  634. main_pic_path = main_pic_path_list[file_name_index]
  635. pp_bg = PictureProcessing(main_pic_path)
  636. original_width = pp_bg.width
  637. if original_width != 1600:
  638. pp_bg = pp_bg.resize(value=1600)
  639. if mask_pic:
  640. mask_bg = PictureProcessing(mask_pic)
  641. mask_bg = mask_bg.resize(value=1600)
  642. mask_box_im = mask_bg.get_im()
  643. box_size = mask_box_im.getbbox()
  644. result_image = mask_box_im.crop(box_size)
  645. mask_width, mask_height = result_image.size
  646. mask_x, mask_y = box_size[0], box_size[1]
  647. else:
  648. mask_width, mask_height = pp_bg.size
  649. mask_width, mask_height = int(mask_width * 12 / 16), int(
  650. mask_height * 12 / 16
  651. )
  652. mask_x, mask_y = int((pp_bg.size[0] - mask_width) / 2), int(
  653. (pp_bg.size[1] - mask_height) / 2
  654. )
  655. if view_name != "后跟":
  656. pp_jpg = pp_jpg.resize(base_by_box=(mask_width, mask_height))
  657. pp_png = pp_png.resize(base_by_box=(mask_width, mask_height))
  658. # 计算粘贴的位置 mask的位置+图片在mask中的位置
  659. p_x = mask_x + int((mask_width - pp_jpg.width) / 2)
  660. p_y = mask_y + int((mask_height - pp_jpg.height) / 2)
  661. pp_bg = pp_bg.to_overlay_pic_advance(
  662. mode="pixel",
  663. top_img=pp_jpg,
  664. base="nw",
  665. value=(p_x, p_y),
  666. top_png_img=pp_png,
  667. )
  668. else:
  669. new_mask_width, new_mask_height = int(mask_width / 1.6), int(
  670. mask_height / 1.6
  671. )
  672. pp_jpg = pp_jpg.resize(
  673. base_by_box=(new_mask_width, new_mask_height)
  674. )
  675. pp_png = pp_png.resize(
  676. base_by_box=(new_mask_width, new_mask_height)
  677. )
  678. new_mask_x = int((mask_width - new_mask_width) / 2 + mask_x)
  679. new_mask_y = int((mask_height - new_mask_height) / 2 + mask_y)
  680. # 计算粘贴的位置 mask的位置+图片在mask中的位置
  681. p_x = new_mask_x + int((new_mask_width - pp_jpg.width) / 2)
  682. p_y = new_mask_y + int((new_mask_height - pp_jpg.height) / 2)
  683. pp_bg = pp_bg.to_overlay_pic_advance(
  684. mode="pixel",
  685. top_img=pp_jpg,
  686. base="nw",
  687. value=(p_x, p_y),
  688. top_png_img=pp_png,
  689. )
  690. out_pci_mode = "." + settings.getSysConfigs(
  691. "basic_configs", "image_out_format", "png"
  692. )
  693. goods_no_main_pic_number += 1
  694. out_pic_path = "{out_path_root}/{goods_no}({goods_no_main_pic_number}){pic_mode}".format(
  695. out_path_root=out_path_root,
  696. goods_no=self.goods_no,
  697. goods_no_main_pic_number=goods_no_main_pic_number,
  698. pic_mode=out_pci_mode,
  699. )
  700. out_pci_factor = float(
  701. 1
  702. if settings.getSysConfigs(
  703. "basic_configs", "image_sharpening", "1"
  704. )
  705. == ""
  706. else settings.getSysConfigs(
  707. "basic_configs", "image_sharpening", "1"
  708. )
  709. )
  710. if out_pci_factor > 1.0:
  711. print("图片锐化处理")
  712. pp_bg = pp_bg.sharpen_image(factor=out_pci_factor)
  713. if original_width < 1600:
  714. pp_bg = pp_bg.resize(value=original_width)
  715. print("392 out_pic_path", out_pic_path)
  716. if out_pci_mode == ".jpg":
  717. pp_bg.save_as_rgb(out_pic_path)
  718. elif out_pci_mode == ".png":
  719. pp_bg.save_as_png(out_pic_path)
  720. else:
  721. pp_bg.save_as_other(out_pic_path, out_pci_mode.split(".")[-1])
  722. def add_pic(self, detailed_images):
  723. self.check_state_end()
  724. todo_detailed_images = []
  725. detailed_images = [x for x in detailed_images if x]
  726. if not detailed_images:
  727. return
  728. for i in detailed_images:
  729. if isinstance(i, list):
  730. for n in i:
  731. todo_detailed_images.append(n)
  732. else:
  733. todo_detailed_images.append(i)
  734. page_len = 0
  735. for index, pp in enumerate(todo_detailed_images):
  736. page_len += pp.height
  737. bg_im = Image.new("RGB", (pp.width, page_len), (255, 255, 255))
  738. n = 0
  739. for index, pp in enumerate(todo_detailed_images):
  740. bg_im.paste(pp.im, (0, n))
  741. n += pp.height
  742. return bg_im
  743. # 通用方法,用于写文字
  744. def add_text_list(self, text_list, spacing=5, base="wn", base_width=1600):
  745. text_list = [x for x in text_list if x["text"]]
  746. # print(text_list)
  747. # spacing 行间距
  748. text_image_list = []
  749. max_w = 0
  750. total_h = 0
  751. for text_data in text_list:
  752. _pp = PictureProcessing("RGBA", (base_width, 1200), (255, 255, 255, 0))
  753. if base == "wn" or base == "nw":
  754. align = "left"
  755. anchor = None
  756. value = (0, 250)
  757. if base == "cn" or base == "nc":
  758. align = "center"
  759. anchor = "mm"
  760. value = (int(base_width / 2), 250)
  761. if base == "en" or base == "ne":
  762. align = "right"
  763. anchor = "rs"
  764. value = (base_width - 10, 250)
  765. _pp = _pp.get_text_image_advanced(
  766. value=value,
  767. font=text_data["font"],
  768. text=text_data["text"],
  769. align=align,
  770. anchor=anchor,
  771. spacing=5,
  772. fill=text_data["fill"],
  773. return_mode="min_image",
  774. margins=(0, 0, 0, 0),
  775. )
  776. text_image_list.append(_pp)
  777. if _pp.width > max_w:
  778. max_w = _pp.width
  779. total_h += _pp.height
  780. if "spacing" in text_data:
  781. total_h += text_data["spacing"]
  782. if not text_image_list:
  783. return None
  784. #
  785. bg = PictureProcessing("RGBA", (max_w, total_h * 3), (0, 0, 0, 0))
  786. y = 0
  787. for text_image, text_data in zip(text_image_list, text_list):
  788. bg = bg.paste_img(top_img=text_image, value=(0, y), base=base)
  789. y += spacing + text_image.height
  790. if "spacing" in text_data:
  791. y += text_data["spacing"]
  792. bg = bg.crop(mode="min")
  793. # _ = bg.paste_img_invert(top_img=PictureProcessing("RGB", (bg.width,bg.height), (255, 255, 255)))
  794. # _.show()
  795. return bg
  796. def generate_font_list_to_pic(self):
  797. font_path_list = [
  798. r"resources\ttf\puhui\Bold.ttf",
  799. r"resources\ttf\puhui\Medium.ttf",
  800. r"resources\ttf\puhui\Heavy.ttf",
  801. r"resources\ttf\puhui\Light.ttf",
  802. r"resources\ttf\puhui\Regular.ttf",
  803. ]
  804. text_v_list = [
  805. "这是一段话Bold",
  806. "这是一段话Medium",
  807. "这是一段话Heavy",
  808. "这是一段话Light",
  809. "这是一段话Regular",
  810. ]
  811. detailed_images = []
  812. for font_path, text in zip(font_path_list, text_v_list):
  813. text_list = []
  814. for size in range(26, 80, 2):
  815. font = ImageFont.truetype(font_path, size)
  816. text_list.append(
  817. {
  818. "text": "{}-字号{}".format(text, size),
  819. "font": font,
  820. "fill": (110, 110, 110),
  821. }
  822. )
  823. text_image = self.add_text_list(text_list, spacing=15, base="nw")
  824. text_image = text_image.crop(mode="min")
  825. text_image = text_image.paste_img_invert(
  826. top_img=PictureProcessing("RGB", text_image.size, (255, 255, 255))
  827. )
  828. detailed_images.append(text_image)
  829. return PictureProcessing(im=self.add_pic(detailed_images))
  830. # 图片分段,每段至少大于N长度
  831. def pp_pic_subsection(self, pp: PictureProcessing, one_height=3200):
  832. total_height = pp.height
  833. now_height = 0
  834. detailed_images = []
  835. while 1:
  836. if now_height + one_height < total_height:
  837. h1 = now_height
  838. h2 = now_height + one_height
  839. bbox = (0, h1, pp.width, h2)
  840. # print("bbox1", bbox)
  841. detailed_images.append(pp.crop(bbox=bbox))
  842. now_height = now_height + one_height
  843. continue
  844. if now_height + one_height >= total_height:
  845. h1 = now_height
  846. h2 = total_height
  847. bbox = (0, h1, pp.width, h2)
  848. # print("bbox2", bbox)
  849. detailed_images.append(pp.crop(bbox=bbox))
  850. break
  851. return detailed_images