remove_bg_ali.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. import copy
  2. import json
  3. import os
  4. from PIL import Image
  5. from alibabacloud_imageseg20191230.client import Client as imageseg20191230Client
  6. from alibabacloud_imageseg20191230.models import SegmentCommodityAdvanceRequest
  7. from alibabacloud_imageseg20191230 import models as imageseg_20191230_models
  8. from alibabacloud_tea_util.models import RuntimeOptions
  9. from alibabacloud_tea_openapi import models as open_api_models
  10. from alibabacloud_tea_openapi.models import Config
  11. from alibabacloud_tea_util import models as util_models
  12. import requests
  13. from io import BytesIO
  14. import cv2
  15. import numpy as np
  16. from func_timeout import func_set_timeout
  17. from func_timeout import FunctionTimedOut
  18. from .multi_threaded_image_saving import ImageSaver
  19. import settings, time, math
  20. from logger import logger
  21. # 自己的
  22. AccessKeyId = "LTAI5tCk4p881X8hymj2FYFk"
  23. AccessKeySecret = "yBYIYzX8CL24r5ZgEx2AgZyDBmFkIK"
  24. def uploadImage(im: Image) -> str:
  25. img_byte_io = BytesIO()
  26. # 根据图片模式选择保存格式
  27. if im.mode == 'RGBA':
  28. im.save(img_byte_io, format='PNG')
  29. else:
  30. im.save(img_byte_io, format='JPEG')
  31. img_byte_io.seek(0) # 重置指针到开头
  32. post_headers = {"Authorization": settings.USER_TOKEN}
  33. url = settings.DOMAIN + "/api/upload"
  34. # 使用字节流上传
  35. resultData = requests.post(
  36. url,
  37. files={"file": ("image.jpg", img_byte_io, "image/jpeg")},
  38. headers=post_headers
  39. ).json()
  40. return resultData["data"]["url"]
  41. # 惠利玛公司的KEY
  42. # AccessKeyId = 'LTAI5tCk4p881X8hymj2FYFk'
  43. # AccessKeySecret = 'rQMgHwciTN4Gusbpt8CM8tflgsxh1V'
  44. # https://help.aliyun.com/zh/viapi/developer-reference/python?spm=a2c4g.11186623.0.i0#task-2252575
  45. # pip install alibabacloud_goodstech20191230
  46. # pip install alibabacloud_tea_openapi
  47. # pip install alibabacloud_tea_util
  48. class Segment(object):
  49. def __init__(self):
  50. self.client = self.create_client()
  51. def get_no_bg_common(self, file_path):
  52. # 初始化RuntimeObject
  53. runtime_option = RuntimeOptions()
  54. try:
  55. # 场景一:文件在本地
  56. img = open(file_path, 'rb')
  57. # 使用完成之后记得调用img.close()关闭流
  58. # 场景二,使用任意可访问的url
  59. # url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/ocr/RecognizeBankCard/yhk1.jpg'
  60. # img = io.BytesIO(urlopen(url).read())
  61. # 4、初始化Request,这里只是以RecognizeBankCard为例,其他能力请使用相应能力对应的类
  62. request = SegmentCommodityAdvanceRequest()
  63. request.image_urlobject = img
  64. # 5、调用api,注意,recognize_bank_card_advance需要更换为相应能力对应的方法名。方法名是根据能力名称按照一定规范形成的,如能力名称为SegmentCommonImage,对应方法名应该为segment_common_image_advance。
  65. response = self.client.segment_common_image_advance(request, runtime_option)
  66. # 获取整体结果
  67. # print(response.body)
  68. img.close()
  69. return response.body
  70. # 获取单个字段,这里只是一个例子,具体能力下的字段需要看具体能力的文档
  71. # print(response.body.data.card_number)
  72. # tips: 可通过response.body.__dict__查看属性名称
  73. except Exception as error:
  74. # 获取整体报错信息
  75. print("error", error)
  76. return None
  77. # 获取单个字段
  78. # print(error.code)
  79. # tips: 可通过error.__dict__查看属性名称
  80. def get_image_orientation(self, img):
  81. # 获取EXIF数据
  82. exif = None
  83. try:
  84. if hasattr(img, 'getexif'):
  85. exif_data = img.getexif()
  86. if exif_data:
  87. exif = dict(exif_data)
  88. elif hasattr(img, '_getexif'):
  89. exif = img._getexif()
  90. except Exception:
  91. print("获取EXIF数据失败", img)
  92. if exif is not None:
  93. # EXIF标签274对应的是Orientation
  94. orientation = exif.get(0x0112)
  95. print("orientation", orientation)
  96. if orientation == 2:
  97. # 水平翻转
  98. img = img.transpose(Image.FLIP_LEFT_RIGHT)
  99. elif orientation == 3:
  100. # 旋转180度
  101. img = img.rotate(180, expand=True)
  102. elif orientation == 4:
  103. # 垂直翻转
  104. img = img.transpose(Image.FLIP_TOP_BOTTOM)
  105. elif orientation == 5:
  106. # 水平翻转后顺时针旋转90度
  107. img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
  108. elif orientation == 6:
  109. # 顺时针旋转90度
  110. img = img.transpose(Image.ROTATE_270)
  111. elif orientation == 7:
  112. # 水平翻转后逆时针旋转90度
  113. img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
  114. elif orientation == 8:
  115. # 逆时针旋转90度
  116. img = img.transpose(Image.ROTATE_90)
  117. else:
  118. print("没有EXIF数据或没有方向信息")
  119. orientation = 1
  120. return img
  121. def get_bo_bg_goods_ultra_background(self, im, is_shadow=False, api_url=None):
  122. imageUrl = uploadImage(im)
  123. # imageUrl = imageUrl + "?x-oss-process=image/auto-orient,1"
  124. if not settings.USER_TOKEN:
  125. print("错误:USER_TOKEN 未配置或为空")
  126. return None
  127. print("图片上传成功", imageUrl)
  128. post_headers = {"Authorization": "Bearer " + settings.USER_TOKEN}
  129. data = {
  130. "image_url": imageUrl,
  131. "is_shadow": is_shadow
  132. }
  133. response = requests.post(
  134. api_url, json=data, headers=post_headers
  135. )
  136. print(f"响应状态码: {response.status_code}")
  137. print(f"响应内容: {response.text[:500]}") # 只打印前500字符
  138. resultData = response.json()
  139. print("旗舰版抠图请求", resultData)
  140. # 安全地获取返回值
  141. data = resultData.get("data")
  142. if data is None:
  143. print("旗舰版抠图返回的data为空")
  144. return None
  145. cutout_image = data.get("cutout_image")
  146. if cutout_image is None:
  147. print("旗舰版抠图返回的cutout_image为空")
  148. return None
  149. return cutout_image
  150. def get_platform_shadow(self, original_image, api_url=None):
  151. image_original_url = uploadImage(original_image)
  152. if not settings.USER_TOKEN:
  153. print("错误:USER_TOKEN 未配置或为空")
  154. return None
  155. print("阴影图处理,图片上传成功", image_original_url)
  156. post_headers = {"Authorization": "Bearer " + settings.USER_TOKEN}
  157. data = {
  158. "oraginal_image": image_original_url,
  159. }
  160. print("阴影处理参数:", data)
  161. response = requests.post(
  162. api_url, json=data, headers=post_headers
  163. )
  164. print(f"响应状态码: {response.status_code}")
  165. print(f"响应内容: {response.text[:500]}") # 只打印前500字符
  166. resultData = response.json()
  167. print("旗舰版阴影处理请求", resultData)
  168. # 安全地获取返回值
  169. data = resultData.get("data")
  170. if data is None:
  171. return None
  172. oss_url = data.get("oss_url")
  173. if oss_url is None:
  174. print("旗舰版 阴影处理 返回的 matte_image为空")
  175. return None
  176. return oss_url
  177. def get_ultra_shadow(self, original_image, cutout_image, opacity, bright_target, api_url=None):
  178. image_original_url = uploadImage(original_image)
  179. image_cutcou_url = uploadImage(cutout_image)
  180. # imageUrl = imageUrl + "?x-oss-process=image/auto-orient,1"
  181. if not settings.USER_TOKEN:
  182. print("错误:USER_TOKEN 未配置或为空")
  183. return None
  184. print("阴影图处理,图片上传成功", image_original_url)
  185. post_headers = {"Authorization": "Bearer " + settings.USER_TOKEN}
  186. data = {
  187. "oraginal_image": image_original_url,
  188. "cutout_image": image_cutcou_url,
  189. "opacity": opacity,
  190. "bright_target": bright_target
  191. }
  192. print("阴影处理参数:", data)
  193. response = requests.post(
  194. api_url, json=data, headers=post_headers
  195. )
  196. print(f"响应状态码: {response.status_code}")
  197. print(f"响应内容: {response.text[:500]}") # 只打印前500字符
  198. resultData = response.json()
  199. print("旗舰版抠图请求", resultData)
  200. # 安全地获取返回值
  201. data = resultData.get("data")
  202. if data is None:
  203. print("旗舰版抠图返回的data为空")
  204. return None
  205. matte_image = data.get("matte_image")
  206. if matte_image is None:
  207. print("旗舰版 阴影处理 返回的 matte_image为空")
  208. return None
  209. return matte_image
  210. def get_no_bg_goods(self, file_path=None, _im=None):
  211. # https://blog.csdn.net/weixin_43411585/article/details/107780941
  212. im = _im
  213. # im.save(file_path)
  214. img = BytesIO()
  215. im.save(img, format='JPEG') # format: PNG or JPEG
  216. img.seek(0) # rewind to the start
  217. request = imageseg_20191230_models.SegmentCommodityAdvanceRequest()
  218. request.image_urlobject = img
  219. client = self.create_client()
  220. # 5、调用api,注意,recognize_bank_card_advance需要更换为相应能力对应的方法名。方法名是根据能力名称按照一定规范形成的,如能力名称为SegmentCommonImage,对应方法名应该为segment_common_image_advance。
  221. runtime = util_models.RuntimeOptions()
  222. response = client.segment_commodity_advance(request, runtime)
  223. # img.close()
  224. # print("1111111111111", response.body)
  225. return response.body
  226. def create_client(self):
  227. """
  228. 使用AK&SK初始化账号Client
  229. @param access_key_id:
  230. @param access_key_secret:
  231. @return: Client
  232. @throws Exception
  233. """
  234. config = open_api_models.Config(
  235. # 必填,您的 AccessKey ID,
  236. access_key_id=AccessKeyId,
  237. # 必填,您的 AccessKey Secret,
  238. access_key_secret=AccessKeySecret
  239. )
  240. # 访问的域名
  241. config.endpoint = f'imageseg.cn-shanghai.aliyuncs.com'
  242. return imageseg20191230Client(config)
  243. class Picture:
  244. def __init__(self, in_path, im=None):
  245. if im:
  246. self.im = im
  247. else:
  248. self.im = Image.open(in_path)
  249. self.x, self.y = self.im.size
  250. # print(self.x, self.y)
  251. def save_img(self, outpath, quality=90):
  252. # self.im = self.im.convert("RGB")
  253. self.im.save(outpath, quality=quality)
  254. def resize(self, width):
  255. re_x = int(width)
  256. re_y = int(self.y * re_x / self.x)
  257. self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
  258. self.x, self.y = self.im.size
  259. def resize_by_heigh(self, heigh):
  260. re_y = int(heigh)
  261. re_x = int(self.x * re_y / self.y)
  262. self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
  263. self.x, self.y = self.im.size
  264. class RemoveBgALi(object):
  265. def __init__(self):
  266. self.saver = ImageSaver()
  267. self.segment = Segment()
  268. @func_set_timeout(40)
  269. def get_image_cut_new(self, file_path, out_file_path=None, original_im=None):
  270. if original_im:
  271. original_pic = Picture(in_path=None, im=original_im)
  272. else:
  273. original_pic = Picture(file_path)
  274. if original_pic.im.mode != "RGB":
  275. original_pic.im = original_pic.im.convert("RGB")
  276. new_pic = copy.copy(original_pic)
  277. after_need_resize = False
  278. if new_pic.x > new_pic.y:
  279. if new_pic.x > 2000:
  280. after_need_resize = True
  281. new_pic.resize(2000)
  282. else:
  283. if new_pic.y > 2000:
  284. after_need_resize = True
  285. new_pic.resize_by_heigh(heigh=2000)
  286. # new_pic.im.show()
  287. body = self.segment.get_no_bg_goods(file_path=None, _im=new_pic.im)
  288. body = eval(str(body))
  289. try:
  290. image_url = body["Data"]["ImageURL"]
  291. except BaseException as e:
  292. print("阿里抠图错误:", e)
  293. # 处理失败,需要删除过程图片
  294. return None
  295. # 字节流转PIL对象
  296. response = requests.get(image_url)
  297. pic = response.content
  298. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  299. box_size = _img_im.getbbox()
  300. new_pp4_im = _img_im.crop(box_size)
  301. byte_io = BytesIO()
  302. new_pp4_im.save(byte_io, format='PNG') # 将图像保存为 PNG 格式到 BytesIO 对象
  303. byte_io.seek(0) # 将指针重置到流的开头,以便后续读取
  304. return byte_io
  305. @func_set_timeout(40)
  306. def get_image_cut(self, file_path, out_file_path=None, original_im=None):
  307. if original_im:
  308. original_pic = Picture(in_path=None, im=original_im)
  309. else:
  310. original_pic = Picture(file_path)
  311. if original_pic.im.mode != "RGB":
  312. print("抠图图片不能是PNG")
  313. return None
  314. new_pic = copy.copy(original_pic)
  315. after_need_resize = False
  316. if new_pic.x > new_pic.y:
  317. if new_pic.x > 2000:
  318. after_need_resize = True
  319. new_pic.resize(2000)
  320. else:
  321. if new_pic.y > 2000:
  322. after_need_resize = True
  323. new_pic.resize_by_heigh(heigh=2000)
  324. # new_pic.im.show()
  325. body = self.segment.get_no_bg_goods(file_path=None, _im=new_pic.im)
  326. body = eval(str(body))
  327. try:
  328. image_url = body["Data"]["ImageURL"]
  329. except BaseException as e:
  330. print("阿里抠图错误:", e)
  331. # 处理失败,需要删除过程图片
  332. return None
  333. # 字节流转PIL对象
  334. response = requests.get(image_url)
  335. pic = response.content
  336. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  337. # 原图更大,则需要执行CV处理
  338. if after_need_resize:
  339. # 将抠图结果转成mask
  340. # _img_im = Image.open(_path)
  341. # 将抠图结果放大到原始图大小
  342. _img_im = _img_im.resize(original_pic.im.size)
  343. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  344. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  345. new_big_mask.paste(white, mask=_img_im.split()[3])
  346. # ---------制作选区缩小的mask
  347. # mask = cv2.imread(mask_path)
  348. # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  349. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  350. mask[mask != 255] = 0
  351. # 黑白反转
  352. # mask = 255 - mask
  353. # 选区缩小10
  354. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  355. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  356. # -------再进行抠图处理
  357. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  358. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  359. # original_pic.im.show()
  360. # mask.show()
  361. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  362. # transparent_im.show()
  363. # 上述抠图结果进行拼接
  364. _img_im.paste(transparent_im, (0, 0), transparent_im)
  365. # _img_im.show("11111111111111111111111")
  366. if out_file_path:
  367. self.saver.save_image(
  368. image=_img_im, file_path=out_file_path,
  369. quality=100, dpi=(350, 350), _format="PNG"
  370. )
  371. # _img_im.save(out_file_path)
  372. return _img_im
  373. def get_image_cut1(self, file_path, out_file_path=None):
  374. original_pic = Picture(file_path)
  375. new_pic = copy.copy(original_pic)
  376. if new_pic.x > 2000:
  377. new_pic.resize(2000)
  378. # new_pic.im.show()
  379. body = self.segment.get_no_bg_goods(file_path=out_file_path, _im=new_pic.im)
  380. body = eval(str(body))
  381. try:
  382. image_url = body["Data"]["ImageURL"]
  383. except BaseException as e:
  384. print("阿里抠图错误:", e)
  385. # 处理失败,需要删除过程图片
  386. return None
  387. # 字节流转PIL对象
  388. response = requests.get(image_url)
  389. pic = response.content
  390. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  391. if original_pic.x > 2000:
  392. # 原图更大,则需要执行CV处理
  393. # _img_im.show()
  394. # 对mask进行放大,然后进行抠图处理
  395. print("对mask进行放大,然后进行抠图处理")
  396. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  397. # original_pic.im.show()
  398. # mask.show()
  399. _img_im = _img_im.resize((original_pic.x, original_pic.y))
  400. # _img_im.show()
  401. transparent_im.paste(original_pic.im, (0, 0), mask=_img_im)
  402. # transparent_im.show()
  403. # transparent_im.show()
  404. _img_im = transparent_im
  405. # 上述抠图结果进行拼接
  406. # _img_im.paste(transparent_im, (0, 0), transparent_im)
  407. pass
  408. _img_im.save(out_file_path)
  409. return _img_im
  410. def download_picture(self, url, out_path):
  411. response = requests.get(url)
  412. pic = response.content
  413. with open(out_path, 'wb') as f:
  414. f.write(pic)
  415. class RemoveUltraBackground:
  416. def __init__(self):
  417. self.api_url = '/api/ai_image/segment_shadow/segment_service'
  418. self.headers = {
  419. 'Content-Type': 'application/json',
  420. 'Accept': 'application/json'
  421. }
  422. self.saver = ImageSaver()
  423. self.segment = Segment()
  424. self.r_ali = RemoveBgALi()
  425. self.logger = logger
  426. @func_set_timeout(40)
  427. def get_image_cut_ultra(self, out_file_path=None, im_image=None):
  428. original_pic = Picture(in_path=None, im=im_image)
  429. original_pic.im = self.segment.get_image_orientation(im_image)
  430. original_pic.x, original_pic.y = original_pic.im.size
  431. if original_pic.im.mode != "RGB":
  432. print("抠图图片不能是PNG")
  433. return None
  434. new_pic = copy.copy(original_pic)
  435. # after_need_resize = False
  436. # if new_pic.x > new_pic.y:
  437. # if new_pic.x > 2000:
  438. # after_need_resize = True
  439. # new_pic.resize(2000)
  440. # else:
  441. # if new_pic.y > 2000:
  442. # after_need_resize = True
  443. # new_pic.resize_by_heigh(heigh=2000)
  444. print("使用旗舰版抠图")
  445. try:
  446. api_url = f"{settings.DOMAIN}{self.api_url}"
  447. image_url = self.segment.get_bo_bg_goods_ultra_background(im=new_pic.im, api_url=api_url)
  448. except BaseException as e:
  449. print("旗舰版抠图异常:", e)
  450. # 处理失败,需要删除过程图片
  451. return None
  452. if image_url is None:
  453. return None
  454. # 字节流转PIL对象
  455. print("image_url", image_url)
  456. response = requests.get(image_url)
  457. pic = response.content
  458. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  459. # # 原图更大,则需要执行CV处理
  460. # if after_need_resize:
  461. # # 将抠图结果转成mask
  462. # # _img_im = Image.open(_path)
  463. # # 将抠图结果放大到原始图大小
  464. # _img_im = _img_im.resize(original_pic.im.size)
  465. # new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  466. # white = Image.new('RGB', _img_im.size, (255, 255, 255))
  467. # new_big_mask.paste(white, mask=_img_im.split()[3])
  468. #
  469. # # ---------制作选区缩小的mask
  470. # # mask = cv2.imread(mask_path)
  471. # # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  472. # mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  473. # mask[mask != 255] = 0
  474. # # 黑白反转
  475. # # mask = 255 - mask
  476. # # 选区缩小10
  477. # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  478. # erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  479. #
  480. # # -------再进行抠图处理
  481. # mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  482. # transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  483. # # original_pic.im.show()
  484. # # mask.show()
  485. # transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  486. # # transparent_im.show()
  487. # # 上述抠图结果进行拼接
  488. # _img_im.paste(transparent_im, (0, 0), transparent_im)
  489. return _img_im
  490. def add_log(self, text, _type="info"):
  491. self.logger.info(
  492. f"旗舰抠图,{text}"
  493. )
  494. @func_set_timeout(40)
  495. def get_image_cut_ali(self, file_path):
  496. original_pic = Picture(in_path=file_path, im=None)
  497. original_pic.im = self.segment.get_image_orientation(original_pic.im)
  498. original_pic.x, original_pic.y = original_pic.im.size
  499. original_pic.im = original_pic.im.convert("RGB")
  500. image_deal_info = {}
  501. image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y)
  502. # 原始图过小,则不需要使用阿里进行预处理
  503. if original_pic.x * original_pic.y < 1000000:
  504. cut_image = original_pic.im
  505. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  506. image_deal_info["二次抠图是否缩放"] = False
  507. image_deal_info["抠图扩边后位置"] = (0, 0, original_pic.x, original_pic.y)
  508. else:
  509. self.add_log("开始预抠图处理")
  510. cut_image = self.r_ali.get_image_cut(
  511. file_path=None, out_file_path=None, original_im=original_pic.im
  512. )
  513. self.add_log("预抠图处理结束")
  514. x1, y1, x2, y2 = cut_image.getbbox()
  515. image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2)
  516. o_w, o_h = cut_image.size
  517. image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h)
  518. # 扩边处理
  519. _w, _h = x2 - x1, y2 - y1
  520. out_px = 0.025
  521. _w, _h = int(out_px * _w), int(out_px * _h)
  522. n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h
  523. if n_x1 < 0:
  524. n_x1 = 0
  525. if n_y1 < 0:
  526. n_y1 = 0
  527. if n_x2 > o_w:
  528. n_x2 = o_w
  529. if n_y2 > o_h:
  530. n_y2 = o_h
  531. image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2)
  532. cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"])
  533. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  534. x, y = image_deal_info["抠图扩边后图片大小"]
  535. # 12000000
  536. max_size = settings.MAX_PIXIAN_SIZE
  537. if x * y > max_size:
  538. r = math.sqrt(max_size) / math.sqrt(x * y)
  539. r = r * 0.9
  540. size = (int(x * r), int(y * r))
  541. # print("图片:{} pixian触发二次缩放,原尺寸{}*{},新尺寸:{}".format(self.file_name, x, y, size))
  542. self.add_log(
  543. text="图片进行压缩,压缩前:{},压缩后:{}".format(
  544. image_deal_info["抠图扩边后图片大小"], size
  545. )
  546. )
  547. image_deal_info["抠图扩边后PIL对象"] = copy.deepcopy(cut_image)
  548. cut_image = cut_image.resize(size=size, resample=1)
  549. # print(cut_image.size)
  550. # print(image_deal_info["抠图扩边后PIL对象"].size)
  551. image_deal_info["二次抠图是否缩放"] = True
  552. else:
  553. image_deal_info["二次抠图是否缩放"] = False
  554. return cut_image, image_deal_info
  555. def picture_resize_to_original(self, _img, original_im):
  556. """
  557. Parameters
  558. ----------
  559. _img 需要还原的PIL对象
  560. original_im 原图对象
  561. Returns
  562. -------
  563. """
  564. # 将抠图结果转成mask
  565. # 将抠图结果放大到原始图大小
  566. _img = _img.resize(original_im.size, resample=1)
  567. new_big_mask = Image.new("RGB", _img.size, (0, 0, 0))
  568. white = Image.new("RGB", _img.size, (255, 255, 255))
  569. new_big_mask.paste(white, mask=_img.split()[3])
  570. # ---------制作选区缩小的mask
  571. mask = cv2.cvtColor(
  572. np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY
  573. ) # 将PIL 格式转换为 CV对象
  574. mask[mask != 255] = 0
  575. # 黑白反转
  576. # mask = 255 - mask
  577. # 选区缩小10
  578. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  579. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  580. # -------再进行抠图处理
  581. mask = Image.fromarray(
  582. cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)
  583. ) # CV 对象转 PIL
  584. transparent_im = Image.new("RGBA", original_im.size, (0, 0, 0, 0))
  585. transparent_im.paste(original_im, (0, 0), mask.convert("L"))
  586. # 上述抠图结果进行拼接
  587. _img.paste(transparent_im, (0, 0), transparent_im)
  588. return _img
  589. @func_set_timeout(40)
  590. def ultra_segment_fast(self, file_path, out_file_path=None):
  591. '''
  592. 旗舰抠图快速版
  593. '''
  594. original_pic = Picture(file_path)
  595. original_pic.im = self.segment.get_image_orientation(original_pic.im)
  596. original_pic.x, original_pic.y = original_pic.im.size
  597. if original_pic.im.mode != "RGB":
  598. print("抠图图片不能是PNG")
  599. return None
  600. new_pic = copy.copy(original_pic)
  601. after_need_resize = False
  602. if new_pic.x > new_pic.y:
  603. if new_pic.x > 2000:
  604. after_need_resize = True
  605. new_pic.resize(2000)
  606. else:
  607. if new_pic.y > 2000:
  608. after_need_resize = True
  609. new_pic.resize_by_heigh(heigh=2000)
  610. print("使用旗舰版抠图")
  611. try:
  612. api_url = f"{settings.DOMAIN}{self.api_url}"
  613. image_url = self.segment.get_bo_bg_goods_ultra_background(im=new_pic.im, api_url=api_url)
  614. except BaseException as e:
  615. print("旗舰版抠图异常:", e)
  616. # 处理失败,需要删除过程图片
  617. return None
  618. if image_url is None:
  619. return None
  620. # 字节流转PIL对象
  621. print("image_url", image_url)
  622. response = requests.get(image_url)
  623. pic = response.content
  624. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  625. # 原图更大,则需要执行CV处理
  626. if after_need_resize:
  627. # 将抠图结果转成mask
  628. # _img_im = Image.open(_path)
  629. # 将抠图结果放大到原始图大小
  630. _img_im = _img_im.resize(original_pic.im.size)
  631. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  632. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  633. new_big_mask.paste(white, mask=_img_im.split()[3])
  634. # ---------制作选区缩小的mask
  635. # mask = cv2.imread(mask_path)
  636. # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  637. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  638. mask[mask != 255] = 0
  639. # 黑白反转
  640. # mask = 255 - mask
  641. # 选区缩小10
  642. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  643. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  644. # -------再进行抠图处理
  645. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  646. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  647. # original_pic.im.show()
  648. # mask.show()
  649. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  650. # transparent_im.show()
  651. # 上述抠图结果进行拼接
  652. _img_im.paste(transparent_im, (0, 0), transparent_im)
  653. # 原图更大,则需要执行CV处理
  654. if out_file_path:
  655. self.saver.save_image(
  656. image=_img_im, file_path=out_file_path,
  657. quality=100, dpi=(350, 350), _format="PNG"
  658. )
  659. return _img_im
  660. def run_ultra_segment(self, file_path, out_file_path):
  661. # 直接调用抠图
  662. time.sleep(0.01)
  663. # 1、增加获取key,2、key需要加密、3、429报错 重试再来拿一个KEY
  664. self.add_log("开始处理")
  665. cut_image_ali, image_deal_info = self.get_image_cut_ali(file_path)
  666. ultra_cutout_data = self.get_image_cut_ultra(out_file_path, cut_image_ali)
  667. if ultra_cutout_data is None:
  668. return None
  669. try:
  670. if image_deal_info["二次抠图是否缩放"]:
  671. # print("图片尺寸还原")
  672. self.add_log(text="图片尺寸进行还原")
  673. original_im = image_deal_info["抠图扩边后PIL对象"]
  674. second_cut_image = self.picture_resize_to_original(
  675. ultra_cutout_data, original_im
  676. )
  677. else:
  678. second_cut_image = ultra_cutout_data
  679. # 创建空白图片并粘贴回去
  680. _img_im = Image.new(
  681. mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0)
  682. )
  683. _img_im.paste(
  684. second_cut_image,
  685. box=(
  686. image_deal_info["抠图扩边后位置"][0],
  687. image_deal_info["抠图扩边后位置"][1],
  688. ),
  689. )
  690. _img_im.save(out_file_path, dpi=(350, 350))
  691. return _img_im
  692. except BaseException as e:
  693. # print(e)
  694. text = "{} 图片处理错误,代码49990".format(e)
  695. self.add_log(text)
  696. return
  697. if __name__ == '__main__':
  698. r = RemoveUltraBackground()
  699. path = r"C:\Users\15001\Desktop\miniso\hb\原始图\hb(1).JPG"
  700. out_path = "{}._no_bg-out.png".format(path)
  701. r.get_image_cut(path, out_file_path=out_path)