pic_deal.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. import os.path
  2. from PIL import Image, ImageDraw, ImageEnhance, ImageFile
  3. import pillow_avif
  4. # import cv2
  5. import numpy as np
  6. from blend_modes import multiply
  7. ImageFile.LOAD_TRUNCATED_IMAGES = True
  8. class PictureProcessing(object):
  9. def __init__(self, *args, **kwargs):
  10. self.im: Image
  11. if args:
  12. p = args[0]
  13. if isinstance(p, str):
  14. if p == "RGB" or p == "RGBA":
  15. size = int(args[1][0]), int(args[1][1])
  16. self.im = Image.new(args[0], size, args[2])
  17. else:
  18. # 设计优先取值处理
  19. _path = args[0]
  20. file_path, file = os.path.split(_path)
  21. custom_path = "{}/custom/{}".format(file_path, file)
  22. if os.path.exists(custom_path):
  23. _path = custom_path
  24. self.im = Image.open(_path)
  25. elif "im" in kwargs:
  26. self.im = kwargs["im"]
  27. else:
  28. self.im = Image.new(mode="RGB", size=(1600, 500), color=(255, 255, 255))
  29. def getbbox(self):
  30. return self.im.getbbox()
  31. def expand_bbox(self, bbox, value=100):
  32. x1, y1, x2, y2 = (
  33. bbox[0] - value,
  34. bbox[1] - value,
  35. bbox[2] + value,
  36. bbox[3] + value,
  37. )
  38. if x1 < 0:
  39. x1 = 0
  40. if y1 < 0:
  41. y1 = 0
  42. bbox = (x1, y1, x2, y2)
  43. return [int(x) for x in bbox]
  44. def show(self):
  45. self.im.show()
  46. def get_size(self):
  47. return self.im.size
  48. def get_im(self):
  49. self.im: Image
  50. return self.im
  51. def to_color_2(self, target, blend): # 正片叠底
  52. return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)
  53. def get_overlay_pic(self, top_img, color=None):
  54. # 正片叠底
  55. top_img: PictureProcessing
  56. image_white = Image.new("RGB", (self.get_size()), color)
  57. backdrop_prepped = np.asfarray(image_white.convert("RGBA"))
  58. source_prepped = np.asfarray(self.im.convert("RGBA"))
  59. blended_np = multiply(backdrop_prepped, source_prepped, 1)
  60. img = Image.fromarray(np.uint8(blended_np)).convert("RGB")
  61. img.paste(top_img.im, (0, 0), top_img.im)
  62. return PictureProcessing(im=img)
  63. def to_overlay_pic_advance(
  64. self,
  65. mode="pixel",
  66. top_img="",
  67. base="nw",
  68. value=(0, 0),
  69. percentage=(0, 0),
  70. margins=(0, 0, 0, 0),
  71. opacity=100,
  72. top_png_img=None,
  73. ):
  74. # opacity 透明度
  75. # 正片叠底
  76. _p = PictureProcessing("RGBA", (self.width, self.height), (255, 255, 255, 255))
  77. top_img = _p.paste_img(
  78. mode=mode,
  79. top_img=top_img,
  80. base=base,
  81. value=value,
  82. percentage=percentage,
  83. margins=margins,
  84. )
  85. backdrop_prepped = np.asfarray(self.im.convert("RGBA"))
  86. source_prepped = np.asfarray(top_img.im.convert("RGBA"))
  87. blended_np = multiply(backdrop_prepped, source_prepped, opacity / 100)
  88. im = Image.fromarray(np.uint8(blended_np)).convert("RGB")
  89. pp = PictureProcessing(im=im)
  90. if top_png_img:
  91. pp = pp.paste_img(
  92. mode=mode,
  93. top_img=top_png_img,
  94. base=base,
  95. value=value,
  96. percentage=percentage,
  97. margins=margins,
  98. )
  99. return pp
  100. def __to_resize(self, width=None, height=None):
  101. _im_x, _im_y = self.get_size()
  102. if width and height:
  103. if _im_x >= _im_y:
  104. high = None
  105. else:
  106. width = None
  107. if width:
  108. re_x = int(width)
  109. re_y = int(_im_y * re_x / _im_x)
  110. else:
  111. re_y = int(height)
  112. re_x = int(_im_x * re_y / _im_y)
  113. img = self.get_im().resize((re_x, re_y))
  114. return img
  115. # 锐化图片
  116. def sharpen_image(self, factor=1.0):
  117. # 创建一个ImageEnhance对象
  118. enhancer = ImageEnhance.Sharpness(self.im)
  119. # 应用增强,值为0.0给出模糊图像,1.0给出原始图像,大于1.0给出锐化效果
  120. # 调整这个值来增加或减少锐化的程度
  121. sharp_img = enhancer.enhance(factor)
  122. return PictureProcessing(im=sharp_img)
  123. @property
  124. def width(self):
  125. return self.im.width
  126. @property
  127. def height(self):
  128. return self.im.height
  129. @property
  130. def size(self):
  131. return self.im.size
  132. def resize(
  133. self,
  134. mode="pixel",
  135. base="width",
  136. value=0,
  137. base_im=None,
  138. percentage=0,
  139. base_by_box=None,
  140. ):
  141. """
  142. plugins_mode # relative相对(宽度、高度、其他参考图),或 pixel绝对像素
  143. base # pixel,width,height,by_im 基于长边、基于短边 (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
  144. value # 固定值,如果为百分比,则为0
  145. percentage #百分比
  146. base_im # 参考基于其他图 PictureProcessing 格式
  147. """
  148. # 在箱子内
  149. if base_by_box:
  150. mode = "relative"
  151. base = "by_im"
  152. base_by_box = (int(base_by_box[0]), int(base_by_box[1]))
  153. base_im = Image.new("RGB", base_by_box, (255, 255, 255))
  154. # 绝对值
  155. if mode == "pixel":
  156. if base == "width":
  157. img = self.__to_resize(width=value)
  158. if base == "high":
  159. img = self.__to_resize(height=value)
  160. # 相对值
  161. if mode == "relative":
  162. if base == "width":
  163. img = self.__to_resize(
  164. width=(
  165. self.width * percentage
  166. if not base_im
  167. else int(base_im.width * percentage)
  168. )
  169. )
  170. if base == "height":
  171. img = self.__to_resize(
  172. width=(
  173. self.height * percentage
  174. if not base_im
  175. else int(base_im.height * percentage)
  176. )
  177. )
  178. # by_im确保能塞进参考图内
  179. if base == "by_im":
  180. percentage = 1 if not percentage else percentage
  181. box_width, box_height = int(base_im.width * percentage), int(
  182. base_im.height * percentage
  183. )
  184. width, height = self.width, self.height
  185. if box_width / box_height < width / height:
  186. scale = box_width / width
  187. else:
  188. scale = box_height / height
  189. img = self.get_im().resize((int(width * scale), int(height * scale)))
  190. return PictureProcessing(im=img)
  191. def paste_img(
  192. self,
  193. mode="pixel",
  194. top_img="",
  195. base="nw",
  196. value=(0, 0),
  197. percentage=(0, 0),
  198. margins=(0, 0, 0, 0),
  199. ):
  200. top_img: PictureProcessing
  201. """
  202. {
  203. "command": "paste_img",
  204. "im": 需要粘贴的图片
  205. "pos": {"plugins_mode": "relative", # pixel
  206. "base": "center", # nw,nc,ne,ec ... 各个方向参考点
  207. "value": (100, 100),
  208. "percentage": (0.5, 0.5),
  209. },
  210. "margins": (0, 0, 0, 0), # 上下左右边距
  211. }
  212. """
  213. value = (int(value[0]), int(value[1]))
  214. # 处理默认值
  215. base = "nw" if not base else base
  216. percentage = (0, 0) if not percentage else percentage
  217. if margins:
  218. top, down, left, right = margins
  219. else:
  220. top, down, left, right = 0, 0, 0, 0
  221. if percentage != (0, 0): # percentage 不按占比模式
  222. if base in ("nw", "wn", "wc", "cw", "nc", "cn", "center"):
  223. value = (int(self.width), int(self.height))
  224. if base in ("sw", "ws", "sc", "cs", "center"):
  225. value = (int(self.width), -1 * int(self.height))
  226. if base in ("ec", "ce"):
  227. value = (int(self.width), int(self.height))
  228. if mode == "pixel":
  229. # 基于右边,上下居中
  230. if base == "ec" or base == "ce":
  231. p_x = int(self.width - (top_img.width + value[0]))
  232. p_y = int((self.height - top_img.height) / 2) + value[1]
  233. # 基于顶部,左右居中
  234. if base == "nc" or base == "cn":
  235. # 顶部对齐
  236. deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
  237. (self.height - top_img.height) / 2
  238. )
  239. p_x = deviation_x + value[0] + left
  240. p_y = value[1]
  241. # 基于右上角
  242. if base == "en" or base == "ne":
  243. p_x = int(self.width - (top_img.width + value[0])) + left
  244. p_y = value[1]
  245. # 基于左上角
  246. if base == "nw" or base == "wn":
  247. deviation_x, deviation_y = 0, 0
  248. p_x, p_y = value
  249. # 基于底部,左右居中
  250. if base == "cs" or base == "sc":
  251. deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
  252. (self.height - top_img.height) / 2
  253. )
  254. p_y = self.height - (top_img.height + value[1] + down)
  255. p_x = deviation_x + value[0] + left
  256. # 上下左右居中
  257. if base == "center" or base == "cc":
  258. deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
  259. (self.height - top_img.height) / 2
  260. )
  261. p_x = deviation_x + value[0] + left
  262. p_y = deviation_y + value[1] + top
  263. # 基于左下角
  264. if base == "sw" or base == "ws":
  265. # deviation_x, deviation_y = 0, int((img.height - img_1.height))
  266. p_x = value[0] + left
  267. p_y = self.height - (top_img.height + value[1] + down)
  268. # 基于左边,上下居中
  269. if base == "wc" or base == "cw":
  270. p_x = value[0] + left
  271. p_y = int((self.height - top_img.height) / 2) + value[1] + top
  272. # 基于右下角
  273. if base == "es" or base == "se":
  274. p_x = int(self.width - (top_img.width + value[0])) + left
  275. p_y = self.height - (top_img.height + value[1] + down) + top
  276. img = PictureProcessing(im=self.im).im
  277. # top_img.get_im().show()
  278. top_img_im = top_img.im
  279. try:
  280. img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im.convert("RGBA"))
  281. except:
  282. img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im)
  283. return PictureProcessing(im=img)
  284. def paste_img_invert(
  285. self,
  286. mode="pixel",
  287. top_img="",
  288. base="nw",
  289. value=(0, 0),
  290. percentage=(0, 0),
  291. margins=(0, 0, 0, 0),
  292. ):
  293. top_img: PictureProcessing
  294. bg_img = top_img.im
  295. top_img = PictureProcessing(im=self.im)
  296. self.im = bg_img
  297. return self.paste_img(
  298. mode=mode,
  299. top_img=top_img,
  300. base=base,
  301. value=value,
  302. percentage=percentage,
  303. margins=margins,
  304. )
  305. # 水平分布处理,一行N个
  306. def horizontal_distribution(
  307. self,
  308. pp_list,
  309. bg_width=1200,
  310. margins=(0, 0, 0, 0),
  311. line_spacing=0,
  312. number_per_row=3,
  313. ):
  314. """
  315. pp_list
  316. line_spacing:行间距
  317. number_per_row:每行个数
  318. margins: (0, 0, 0, 0), # 上下左右边距
  319. """
  320. bg_width = int(bg_width)
  321. total_row = len(pp_list) // number_per_row
  322. if len(pp_list) % number_per_row > 0:
  323. total_row = total_row + 1
  324. # print("total_row", total_row)
  325. every_height = pp_list[0].height
  326. bg_pp = PictureProcessing(
  327. "RGBA",
  328. (
  329. bg_width,
  330. margins[0]
  331. + every_height * total_row
  332. + line_spacing * (total_row - 1)
  333. + margins[1],
  334. ),
  335. (255, 255, 255, 0),
  336. )
  337. row_num = 0
  338. y = margins[0]
  339. for i in range(0, len(pp_list), number_per_row): # 切片获取每N个元素
  340. row_list = pp_list[i : i + number_per_row] # 输出每行的元素
  341. if row_list:
  342. row_num += 1
  343. # 计算每个元素间距
  344. t_w = bg_pp.width - margins[2] - margins[3]
  345. al_w = sum([x.width for x in row_list])
  346. space = int((t_w - al_w) / (len(row_list) + 1))
  347. x = margins[2]
  348. # 粘贴每个元素
  349. for pp in row_list:
  350. x = x + space
  351. bg_pp = bg_pp.paste_img(
  352. mode="pixel", top_img=pp, base="", value=(x, y)
  353. )
  354. x = x + pp.width
  355. if row_num != total_row:
  356. y += every_height + line_spacing
  357. return bg_pp
  358. def get_text_image_advanced(
  359. self,
  360. value=(0, 0),
  361. font="",
  362. text="",
  363. anchor=None,
  364. align="left",
  365. spacing=0,
  366. fill=(0, 0, 0),
  367. return_mode="image",
  368. margins=(0, 0, 0, 0),
  369. max_len_one_line=0,
  370. ):
  371. """
  372. {
  373. "command": "add_text",
  374. "pos": {"plugins_mode": "relative", # pixel
  375. "base": "center", # nw,nc,ne,ec ... 各个方向参考点
  376. "value": (100, 100),
  377. "percentage": 0, },
  378. "font": "",
  379. "text": "",
  380. "anchor": "", # mm 为居中 ma 为左右居中,上面居顶 rs 右边;从哪边开始输入
  381. "align": "对齐方式",left center right
  382. "direction": "文本的方向",
  383. "max_len_one_line": "单行长度",
  384. "spacing": 10,
  385. "fill": "文字颜色",
  386. }
  387. margins 边距像素,上下左右;只有返回min_image_high 有效
  388. """
  389. # =====
  390. """
  391. return_mode:image返回图片,min_image_high返回最小高度尺寸但宽度不变,min_image返回最小高度与宽度
  392. """
  393. if not text:
  394. return self
  395. # pp = PictureProcessing("RGBA", self.size, (0, 0, 0, 0))
  396. pp = PictureProcessing("RGBA", self.size, (255, 255, 255, 0))
  397. draw_1 = ImageDraw.Draw(pp.im)
  398. # 定义字体,你需要有一个.ttf字体文件
  399. spacing = 4 if not spacing else spacing
  400. align = "left" if not align else align # left, center 或 right
  401. if max_len_one_line > 0:
  402. text = text[:max_len_one_line]
  403. _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
  404. value = (int(value[0]), int(value[1]))
  405. draw_1.multiline_text(
  406. (value[0], value[1] + margins[0]),
  407. text,
  408. fill=fill,
  409. font=font,
  410. anchor=anchor,
  411. spacing=spacing,
  412. align=align,
  413. direction=None,
  414. features=None,
  415. language=None,
  416. stroke_width=0,
  417. stroke_fill=None,
  418. embedded_color=False,
  419. )
  420. if return_mode == "image":
  421. pp = pp.paste_img_invert(
  422. mode="pixel", top_img=self, base="nw", value=(0, 0)
  423. )
  424. pass
  425. if return_mode == "min_image_high":
  426. # pp.show()
  427. bbox = pp.getbbox()
  428. bbox = (0, 0, pp.width, bbox[3] + margins[1])
  429. pp = pp.paste_img_invert(
  430. mode="pixel", top_img=self, base="nw", value=(0, 0)
  431. )
  432. pp = pp.crop(bbox)
  433. if return_mode == "min_image":
  434. bbox = pp.getbbox()
  435. pp = pp.crop(bbox)
  436. return pp
  437. def add_text(
  438. self,
  439. mode="",
  440. base="",
  441. value="",
  442. percentage="",
  443. font="",
  444. text="",
  445. anchor="",
  446. align="",
  447. direction="",
  448. max_len_one_line="",
  449. spacing="",
  450. fill="",
  451. ):
  452. """
  453. {
  454. "command": "add_text",
  455. "pos": {"plugins_mode": "relative", # pixel
  456. "base": "center", # nw,nc,ne,ec ... 各个方向参考点
  457. "value": (100, 100),
  458. "percentage": 0, },
  459. "font": "",
  460. "text": "",
  461. "anchor": "", # mm 为居中 ma 为左右居中,上面居顶 rs 右边;从哪边开始输入
  462. "align": "对齐方式",left center right
  463. "direction": "文本的方向",
  464. "max_len_one_line": "单行长度",
  465. "spacing": 10,
  466. "fill": "文字颜色",
  467. }
  468. """
  469. pp = PictureProcessing(im=self.im)
  470. draw_1 = ImageDraw.Draw(pp.im)
  471. # 定义字体,你需要有一个.ttf字体文件
  472. spacing = 4 if not spacing else spacing
  473. anchor = None if not anchor else anchor
  474. align = "left" if not align else align # left, center 或 right
  475. _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
  476. xy = (0, 0)
  477. if mode == "pixel":
  478. xy = value
  479. draw_1.multiline_text(
  480. xy,
  481. text,
  482. fill=fill,
  483. font=font,
  484. anchor=anchor,
  485. spacing=spacing,
  486. align=align,
  487. direction=None,
  488. features=None,
  489. language=None,
  490. stroke_width=0,
  491. stroke_fill=None,
  492. embedded_color=False,
  493. )
  494. return pp
  495. def crop_img(
  496. self,
  497. mode="pixel",
  498. base="nw",
  499. value=(100, 100, 10, 10),
  500. color_fill=(255, 255, 255),
  501. margins=(0, 0, 0, 0),
  502. ):
  503. """
  504. {
  505. "command": "crop_img",
  506. "img": {"im": "im"},
  507. "pos": {"plugins_mode": "relative", # pixel
  508. "base": "center", # nw,nc,ne,ec ... 各个方向参考点
  509. "value": (100, 100, 10, 10),# 顶点,以及图片大小
  510. },
  511. "color_fill": (255, 255, 255)
  512. }
  513. """
  514. pp = PictureProcessing(im=self.im)
  515. base = "nw" if not base else base
  516. if margins:
  517. top, down, left, right = margins
  518. else:
  519. top, down, left, right = 0, 0, 0, 0
  520. out_img_size = (value[2], value[3])
  521. # 默认填充色
  522. if not color_fill:
  523. color_fill = (0, 0, 0)
  524. if mode == "pixel":
  525. # 左上脚
  526. if base == "nw" or "wn":
  527. box = value
  528. # 左下角
  529. if base == "sw" or base == "ws":
  530. # deviation_x, deviation_y = 0, int((img.height - img_1.height))
  531. box = (value[0], pp.height - (value[1] + value[3]), value[2], value[3])
  532. # print(box)
  533. # 右下角
  534. if base == "se" or base == "es":
  535. box = (
  536. pp.width - (value[0] + value[2]),
  537. pp.height - (value[1] + value[3]),
  538. value[2],
  539. value[3],
  540. )
  541. # print(box)
  542. # 居中
  543. if base == "cc":
  544. # print("11-value", value)
  545. x = int((pp.width + value[0] - value[2]) / 2)
  546. y = int((pp.height + value[1] - value[3]) / 2)
  547. box = (x, y, value[2], value[3])
  548. # print("11-box", box)
  549. box = [box[0], box[1], box[0] + box[2], box[1] + box[3]]
  550. # print("12-box", box)
  551. # print("ww-hhh", pp.width, pp.height)
  552. out_img = pp.im.crop(box=box)
  553. # print("out_img", out_img.width, out_img.height)
  554. # print("-----")
  555. if box[0] < 0:
  556. out_img.paste(
  557. Image.new("RGB", (-1 * box[0], out_img.height), color_fill), (0, 0)
  558. )
  559. if box[1] < 0:
  560. out_img.paste(
  561. Image.new("RGB", (out_img.width, -1 * box[1]), color_fill), (0, 0)
  562. )
  563. if box[2] > pp.width:
  564. # print(box[2] - img.width, img.height)
  565. i = Image.new("RGB", (box[2] - pp.width, out_img.height), color_fill)
  566. out_img.paste(i, (pp.width - box[0], 0))
  567. if box[3] > pp.height:
  568. out_img.paste(
  569. Image.new("RGB", (out_img.width, box[3] - pp.height), color_fill),
  570. (0, pp.height - box[1]),
  571. )
  572. return PictureProcessing(im=out_img)
  573. def crop(self, bbox=(0, 0, 0, 0), mode=None):
  574. bbox = (int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3]))
  575. if mode == "min":
  576. bbox = self.getbbox()
  577. return PictureProcessing(im=self.im.crop(bbox))
  578. def rotate(self, doge):
  579. return PictureProcessing(im=self.im.rotate(doge))
  580. def rotate_advance(self, doge, is_crop=True):
  581. max_px = max(self.width, self.height)
  582. max_px = max_px * 2
  583. bg = PictureProcessing("RGBA", (max_px, max_px), (255, 255, 255, 0))
  584. bg = bg.paste_img(top_img=self, base="cc")
  585. bg = bg.rotate(doge)
  586. if is_crop:
  587. bg = bg.crop(mode="min")
  588. return bg
  589. def radius(self, mode="pixel", circular_pos=(1, 1, 1, 1), value=30, percentage=""):
  590. """
  591. {"command": "radius", # radius
  592. "plugins_mode": "relative", # pixel 相对(短边),或绝对像素
  593. "circular_pos": (0, 1, 0, 1), # 从左上角顺时针,记录圆角数量
  594. "value": 649, # 固定值,如果为百分比,则为0
  595. "percentage": 0, } # 百分比
  596. """
  597. # 单图圆角处理
  598. pp = PictureProcessing(im=self.im)
  599. radii = value
  600. if radii > pp.width / 2:
  601. radii = int(pp.width / 2)
  602. if radii > pp.height / 2:
  603. radii = int(pp.height / 2)
  604. # 画圆(用于分离4个角)
  605. circle = Image.new("L", (radii * 2, radii * 2), 0) # 创建一个黑色背景的画布
  606. draw = ImageDraw.Draw(circle)
  607. draw.ellipse((0, 0, radii * 2, radii * 2), fill=255) # 画白色圆形
  608. # 原图
  609. img = pp.im.convert("RGBA")
  610. w, h = img.size
  611. # 画4个角(将整圆分离为4个部分)
  612. alpha = Image.new("L", img.size, 255)
  613. _pos = circular_pos
  614. if not _pos:
  615. _pos = (1, 1, 1, 1)
  616. for index, i in enumerate(_pos):
  617. if index == 0 and i == 1:
  618. alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0)) # 左上角
  619. if index == 1 and i == 1:
  620. alpha.paste(
  621. circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0)
  622. ) # 右上角
  623. if index == 2 and i == 1:
  624. alpha.paste(
  625. circle.crop((radii, radii, radii * 2, radii * 2)),
  626. (w - radii, h - radii),
  627. ) # 右下角
  628. if index == 3 and i == 1:
  629. alpha.paste(
  630. circle.crop((0, radii, radii, radii * 2)), (0, h - radii)
  631. ) # 左下角
  632. # alpha.show()
  633. img.putalpha(alpha) # 白色区域透明可见,黑色区域不可见
  634. return PictureProcessing(im=img)
  635. # 图片翻转
  636. def transpose(self, mode="left_right"):
  637. img = self.get_im()
  638. img = img.transpose(Image.FLIP_LEFT_RIGHT)
  639. return PictureProcessing(im=img)
  640. def convert(self, mode):
  641. return PictureProcessing(im=self.im.convert(mode))
  642. def save_as_rgb(self, path):
  643. self.im = self.im.convert("RGB")
  644. self.im.save(path, format="JPEG")
  645. def save_as_png(self, path):
  646. self.im = self.im.convert("RGBA")
  647. self.im.save(path)
  648. def save_as_other(self, path, format):
  649. self.im.save(path, format=format)