remove_bg_ali.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  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_bo_bg_goods_glasses(self, im, is_shadow=False, api_url=None):
  151. imageUrl = uploadImage(im)
  152. # imageUrl = imageUrl + "?x-oss-process=image/auto-orient,1"
  153. if not settings.USER_TOKEN:
  154. print("错误:USER_TOKEN 未配置或为空")
  155. return None
  156. # print("图片上传成功", imageUrl)
  157. post_headers = {"Authorization": "Bearer " + settings.USER_TOKEN}
  158. data = {
  159. "image_url": imageUrl,
  160. "is_shadow": is_shadow
  161. }
  162. response = requests.post(
  163. api_url, json=data, headers=post_headers
  164. )
  165. # print(f"响应状态码: {response.status_code}")
  166. # print(f"响应内容: {response.text[:500]}") # 只打印前500字符
  167. resultData = response.json()
  168. print("旗舰版抠图请求", resultData)
  169. # 安全地获取返回值
  170. data = resultData.get("data")
  171. if data is None:
  172. # print("旗舰版抠图返回的data为空")
  173. return None
  174. cutout_image = data.get("cutout_image")
  175. if cutout_image is None:
  176. # print("旗舰版抠图返回的cutout_image为空")
  177. return None
  178. return cutout_image
  179. def get_platform_shadow(self, original_image, api_url=None):
  180. image_original_url = uploadImage(original_image)
  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. }
  189. print("阴影处理参数:", data)
  190. response = requests.post(
  191. api_url, json=data, headers=post_headers
  192. )
  193. print(f"响应状态码: {response.status_code}")
  194. print(f"响应内容: {response.text[:500]}") # 只打印前500字符
  195. resultData = response.json()
  196. print("旗舰版阴影处理请求", resultData)
  197. # 安全地获取返回值
  198. data = resultData.get("data")
  199. if data is None:
  200. return None
  201. oss_url = data.get("oss_url")
  202. if oss_url is None:
  203. print("旗舰版 阴影处理 返回的 matte_image为空")
  204. return None
  205. return oss_url
  206. def get_ultra_shadow(self, original_image, cutout_image, opacity, bright_target, api_url=None):
  207. image_original_url = uploadImage(original_image)
  208. image_cutcou_url = uploadImage(cutout_image)
  209. # imageUrl = imageUrl + "?x-oss-process=image/auto-orient,1"
  210. if not settings.USER_TOKEN:
  211. print("错误:USER_TOKEN 未配置或为空")
  212. return None
  213. print("阴影图处理,图片上传成功", image_original_url)
  214. post_headers = {"Authorization": "Bearer " + settings.USER_TOKEN}
  215. data = {
  216. "oraginal_image": image_original_url,
  217. "cutout_image": image_cutcou_url,
  218. "opacity": opacity,
  219. "bright_target": bright_target
  220. }
  221. print("阴影处理参数:", data)
  222. response = requests.post(
  223. api_url, json=data, headers=post_headers
  224. )
  225. print(f"响应状态码: {response.status_code}")
  226. print(f"响应内容: {response.text[:500]}") # 只打印前500字符
  227. resultData = response.json()
  228. print("旗舰版抠图请求", resultData)
  229. # 安全地获取返回值
  230. data = resultData.get("data")
  231. if data is None:
  232. print("旗舰版抠图返回的data为空")
  233. return None
  234. matte_image = data.get("matte_image")
  235. if matte_image is None:
  236. print("旗舰版 阴影处理 返回的 matte_image为空")
  237. return None
  238. return matte_image
  239. def get_no_bg_goods(self, file_path=None, _im=None):
  240. # https://blog.csdn.net/weixin_43411585/article/details/107780941
  241. im = _im
  242. # im.save(file_path)
  243. img = BytesIO()
  244. im.save(img, format='JPEG') # format: PNG or JPEG
  245. img.seek(0) # rewind to the start
  246. request = imageseg_20191230_models.SegmentCommodityAdvanceRequest()
  247. request.image_urlobject = img
  248. client = self.create_client()
  249. # 5、调用api,注意,recognize_bank_card_advance需要更换为相应能力对应的方法名。方法名是根据能力名称按照一定规范形成的,如能力名称为SegmentCommonImage,对应方法名应该为segment_common_image_advance。
  250. runtime = util_models.RuntimeOptions()
  251. response = client.segment_commodity_advance(request, runtime)
  252. # img.close()
  253. # print("1111111111111", response.body)
  254. return response.body
  255. def create_client(self):
  256. """
  257. 使用AK&SK初始化账号Client
  258. @param access_key_id:
  259. @param access_key_secret:
  260. @return: Client
  261. @throws Exception
  262. """
  263. config = open_api_models.Config(
  264. # 必填,您的 AccessKey ID,
  265. access_key_id=AccessKeyId,
  266. # 必填,您的 AccessKey Secret,
  267. access_key_secret=AccessKeySecret
  268. )
  269. # 访问的域名
  270. config.endpoint = f'imageseg.cn-shanghai.aliyuncs.com'
  271. return imageseg20191230Client(config)
  272. class Picture:
  273. def __init__(self, in_path, im=None):
  274. if im:
  275. self.im = im
  276. else:
  277. self.im = Image.open(in_path)
  278. self.x, self.y = self.im.size
  279. # print(self.x, self.y)
  280. def save_img(self, outpath, quality=90):
  281. # self.im = self.im.convert("RGB")
  282. self.im.save(outpath, quality=quality)
  283. def resize(self, width):
  284. re_x = int(width)
  285. re_y = int(self.y * re_x / self.x)
  286. self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
  287. self.x, self.y = self.im.size
  288. def resize_by_heigh(self, heigh):
  289. re_y = int(heigh)
  290. re_x = int(self.x * re_y / self.y)
  291. self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
  292. self.x, self.y = self.im.size
  293. class RemoveBgGlasses(object):
  294. """
  295. 玻璃制品抠图
  296. """
  297. def __init__(self):
  298. self.api_url = '/api/ai_image/segment_shadow/transparent_segment'
  299. self.headers = {
  300. 'Content-Type': 'application/json',
  301. 'Accept': 'application/json'
  302. }
  303. self.saver = ImageSaver()
  304. self.segment = Segment()
  305. self.logger = logger
  306. @func_set_timeout(40)
  307. def glasses_segment(self, file_path, out_file_path=None):
  308. '''
  309. 玻璃/透明制品抠图
  310. '''
  311. original_pic = Picture(file_path)
  312. original_pic.im = self.segment.get_image_orientation(original_pic.im)
  313. original_pic.x, original_pic.y = original_pic.im.size
  314. if original_pic.im.mode != "RGB":
  315. print("抠图图片不能是PNG")
  316. return None
  317. new_pic = copy.copy(original_pic)
  318. basic_width_size = 4000
  319. after_need_resize = False
  320. if new_pic.x > new_pic.y:
  321. if new_pic.x > basic_width_size:
  322. after_need_resize = True
  323. new_pic.resize(basic_width_size)
  324. else:
  325. if new_pic.y > basic_width_size:
  326. after_need_resize = True
  327. new_pic.resize_by_heigh(heigh=basic_width_size)
  328. print("使用玻璃制品抠图")
  329. try:
  330. api_url = f"{settings.DOMAIN}{self.api_url}"
  331. image_url = self.segment.get_bo_bg_goods_glasses(im=new_pic.im, api_url=api_url)
  332. except BaseException as e:
  333. print("玻璃制品抠图异常:", e)
  334. # 处理失败,需要删除过程图片
  335. return None
  336. if image_url is None:
  337. return None
  338. # 字节流转PIL对象
  339. print("image_url", image_url)
  340. response = requests.get(image_url)
  341. pic = response.content
  342. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  343. # 原图更大,则需要执行CV处理
  344. if after_need_resize:
  345. # 将抠图结果转成mask
  346. # 将抠图结果放大到原始图大小
  347. _img_im = _img_im.resize(original_pic.im.size)
  348. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  349. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  350. new_big_mask.paste(white, mask=_img_im.split()[3])
  351. # ---------制作选区缩小的mask
  352. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  353. mask[mask != 255] = 0
  354. # 黑白反转
  355. # mask = 255 - mask
  356. # 选区缩小10
  357. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  358. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  359. # -------再进行抠图处理
  360. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  361. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  362. # original_pic.im.show()
  363. # mask.show()
  364. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  365. # transparent_im.show()
  366. # 上述抠图结果进行拼接
  367. _img_im.paste(transparent_im, (0, 0), transparent_im)
  368. # 原图更大,则需要执行CV处理
  369. if out_file_path:
  370. self.saver.save_image(
  371. image=_img_im, file_path=out_file_path,
  372. quality=100, dpi=(350, 350), _format="PNG"
  373. )
  374. return _img_im
  375. class RemoveBgALi(object):
  376. def __init__(self):
  377. self.saver = ImageSaver()
  378. self.segment = Segment()
  379. @func_set_timeout(40)
  380. def get_image_cut_new(self, file_path, out_file_path=None, original_im=None):
  381. if original_im:
  382. original_pic = Picture(in_path=None, im=original_im)
  383. else:
  384. original_pic = Picture(file_path)
  385. if original_pic.im.mode != "RGB":
  386. original_pic.im = original_pic.im.convert("RGB")
  387. new_pic = copy.copy(original_pic)
  388. after_need_resize = False
  389. if new_pic.x > new_pic.y:
  390. if new_pic.x > 2000:
  391. after_need_resize = True
  392. new_pic.resize(2000)
  393. else:
  394. if new_pic.y > 2000:
  395. after_need_resize = True
  396. new_pic.resize_by_heigh(heigh=2000)
  397. # new_pic.im.show()
  398. body = self.segment.get_no_bg_goods(file_path=None, _im=new_pic.im)
  399. body = eval(str(body))
  400. try:
  401. image_url = body["Data"]["ImageURL"]
  402. except BaseException as e:
  403. print("阿里抠图错误:", e)
  404. # 处理失败,需要删除过程图片
  405. return None
  406. # 字节流转PIL对象
  407. response = requests.get(image_url)
  408. pic = response.content
  409. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  410. box_size = _img_im.getbbox()
  411. new_pp4_im = _img_im.crop(box_size)
  412. byte_io = BytesIO()
  413. new_pp4_im.save(byte_io, format='PNG') # 将图像保存为 PNG 格式到 BytesIO 对象
  414. byte_io.seek(0) # 将指针重置到流的开头,以便后续读取
  415. return byte_io
  416. @func_set_timeout(40)
  417. def get_image_cut(self, file_path, out_file_path=None, original_im=None):
  418. if original_im:
  419. original_pic = Picture(in_path=None, im=original_im)
  420. else:
  421. original_pic = Picture(file_path)
  422. if original_pic.im.mode != "RGB":
  423. print("抠图图片不能是PNG")
  424. return None
  425. new_pic = copy.copy(original_pic)
  426. after_need_resize = False
  427. if new_pic.x > new_pic.y:
  428. if new_pic.x > 2000:
  429. after_need_resize = True
  430. new_pic.resize(2000)
  431. else:
  432. if new_pic.y > 2000:
  433. after_need_resize = True
  434. new_pic.resize_by_heigh(heigh=2000)
  435. # new_pic.im.show()
  436. body = self.segment.get_no_bg_goods(file_path=None, _im=new_pic.im)
  437. body = eval(str(body))
  438. try:
  439. image_url = body["Data"]["ImageURL"]
  440. except BaseException as e:
  441. print("阿里抠图错误:", e)
  442. # 处理失败,需要删除过程图片
  443. return None
  444. # 字节流转PIL对象
  445. response = requests.get(image_url)
  446. pic = response.content
  447. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  448. # 原图更大,则需要执行CV处理
  449. if after_need_resize:
  450. # 将抠图结果转成mask
  451. # _img_im = Image.open(_path)
  452. # 将抠图结果放大到原始图大小
  453. _img_im = _img_im.resize(original_pic.im.size)
  454. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  455. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  456. new_big_mask.paste(white, mask=_img_im.split()[3])
  457. # ---------制作选区缩小的mask
  458. # mask = cv2.imread(mask_path)
  459. # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  460. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  461. mask[mask != 255] = 0
  462. # 黑白反转
  463. # mask = 255 - mask
  464. # 选区缩小10
  465. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  466. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  467. # -------再进行抠图处理
  468. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  469. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  470. # original_pic.im.show()
  471. # mask.show()
  472. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  473. # transparent_im.show()
  474. # 上述抠图结果进行拼接
  475. _img_im.paste(transparent_im, (0, 0), transparent_im)
  476. # _img_im.show("11111111111111111111111")
  477. if out_file_path:
  478. self.saver.save_image(
  479. image=_img_im, file_path=out_file_path,
  480. quality=100, dpi=(350, 350), _format="PNG"
  481. )
  482. # _img_im.save(out_file_path)
  483. return _img_im
  484. def get_image_cut1(self, file_path, out_file_path=None):
  485. original_pic = Picture(file_path)
  486. new_pic = copy.copy(original_pic)
  487. if new_pic.x > 2000:
  488. new_pic.resize(2000)
  489. # new_pic.im.show()
  490. body = self.segment.get_no_bg_goods(file_path=out_file_path, _im=new_pic.im)
  491. body = eval(str(body))
  492. try:
  493. image_url = body["Data"]["ImageURL"]
  494. except BaseException as e:
  495. print("阿里抠图错误:", e)
  496. # 处理失败,需要删除过程图片
  497. return None
  498. # 字节流转PIL对象
  499. response = requests.get(image_url)
  500. pic = response.content
  501. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  502. if original_pic.x > 2000:
  503. # 原图更大,则需要执行CV处理
  504. # _img_im.show()
  505. # 对mask进行放大,然后进行抠图处理
  506. print("对mask进行放大,然后进行抠图处理")
  507. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  508. # original_pic.im.show()
  509. # mask.show()
  510. _img_im = _img_im.resize((original_pic.x, original_pic.y))
  511. # _img_im.show()
  512. transparent_im.paste(original_pic.im, (0, 0), mask=_img_im)
  513. # transparent_im.show()
  514. # transparent_im.show()
  515. _img_im = transparent_im
  516. # 上述抠图结果进行拼接
  517. # _img_im.paste(transparent_im, (0, 0), transparent_im)
  518. pass
  519. _img_im.save(out_file_path)
  520. return _img_im
  521. def download_picture(self, url, out_path):
  522. response = requests.get(url)
  523. pic = response.content
  524. with open(out_path, 'wb') as f:
  525. f.write(pic)
  526. class RemoveUltraBackground:
  527. def __init__(self):
  528. self.api_url = '/api/ai_image/segment_shadow/segment_service'
  529. self.headers = {
  530. 'Content-Type': 'application/json',
  531. 'Accept': 'application/json'
  532. }
  533. self.saver = ImageSaver()
  534. self.segment = Segment()
  535. self.r_ali = RemoveBgALi()
  536. self.logger = logger
  537. @func_set_timeout(40)
  538. def get_image_cut_ultra(self, out_file_path=None, im_image=None):
  539. original_pic = Picture(in_path=None, im=im_image)
  540. original_pic.im = self.segment.get_image_orientation(im_image)
  541. original_pic.x, original_pic.y = original_pic.im.size
  542. if original_pic.im.mode != "RGB":
  543. print("抠图图片不能是PNG")
  544. return None
  545. new_pic = copy.copy(original_pic)
  546. print("使用旗舰版抠图")
  547. try:
  548. api_url = f"{settings.DOMAIN}{self.api_url}"
  549. image_url = self.segment.get_bo_bg_goods_ultra_background(im=new_pic.im, api_url=api_url)
  550. except BaseException as e:
  551. print("旗舰版抠图异常:", e)
  552. # 处理失败,需要删除过程图片
  553. return None
  554. if image_url is None:
  555. return None
  556. # 字节流转PIL对象
  557. print("image_url", image_url)
  558. response = requests.get(image_url)
  559. pic = response.content
  560. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  561. return _img_im
  562. def add_log(self, text, _type="info"):
  563. self.logger.info(
  564. f"旗舰抠图,{text}"
  565. )
  566. @func_set_timeout(40)
  567. def get_image_cut_ali(self, file_path):
  568. original_pic = Picture(in_path=file_path, im=None)
  569. original_pic.im = self.segment.get_image_orientation(original_pic.im)
  570. original_pic.x, original_pic.y = original_pic.im.size
  571. original_pic.im = original_pic.im.convert("RGB")
  572. image_deal_info = {}
  573. image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y)
  574. # 原始图过小,则不需要使用阿里进行预处理
  575. if original_pic.x * original_pic.y < 1000000:
  576. cut_image = original_pic.im
  577. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  578. image_deal_info["二次抠图是否缩放"] = False
  579. image_deal_info["抠图扩边后位置"] = (0, 0, original_pic.x, original_pic.y)
  580. else:
  581. self.add_log("开始预抠图处理")
  582. cut_image = self.r_ali.get_image_cut(
  583. file_path=None, out_file_path=None, original_im=original_pic.im
  584. )
  585. self.add_log("预抠图处理结束")
  586. x1, y1, x2, y2 = cut_image.getbbox()
  587. image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2)
  588. o_w, o_h = cut_image.size
  589. image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h)
  590. # 扩边处理
  591. _w, _h = x2 - x1, y2 - y1
  592. out_px = 0.025
  593. _w, _h = int(out_px * _w), int(out_px * _h)
  594. n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h
  595. if n_x1 < 0:
  596. n_x1 = 0
  597. if n_y1 < 0:
  598. n_y1 = 0
  599. if n_x2 > o_w:
  600. n_x2 = o_w
  601. if n_y2 > o_h:
  602. n_y2 = o_h
  603. image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2)
  604. cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"])
  605. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  606. x, y = image_deal_info["抠图扩边后图片大小"]
  607. # 12000000
  608. max_size = settings.MAX_PIXIAN_SIZE
  609. if x * y > max_size:
  610. r = math.sqrt(max_size) / math.sqrt(x * y)
  611. r = r * 0.9
  612. size = (int(x * r), int(y * r))
  613. # print("图片:{} pixian触发二次缩放,原尺寸{}*{},新尺寸:{}".format(self.file_name, x, y, size))
  614. self.add_log(
  615. text="图片进行压缩,压缩前:{},压缩后:{}".format(
  616. image_deal_info["抠图扩边后图片大小"], size
  617. )
  618. )
  619. image_deal_info["抠图扩边后PIL对象"] = copy.deepcopy(cut_image)
  620. cut_image = cut_image.resize(size=size, resample=1)
  621. # print(cut_image.size)
  622. # print(image_deal_info["抠图扩边后PIL对象"].size)
  623. image_deal_info["二次抠图是否缩放"] = True
  624. else:
  625. image_deal_info["二次抠图是否缩放"] = False
  626. return cut_image, image_deal_info
  627. def picture_resize_to_original(self, _img, original_im):
  628. """
  629. Parameters
  630. ----------
  631. _img 需要还原的PIL对象
  632. original_im 原图对象
  633. Returns
  634. -------
  635. """
  636. # 将抠图结果转成mask
  637. # 将抠图结果放大到原始图大小
  638. _img = _img.resize(original_im.size, resample=1)
  639. new_big_mask = Image.new("RGB", _img.size, (0, 0, 0))
  640. white = Image.new("RGB", _img.size, (255, 255, 255))
  641. new_big_mask.paste(white, mask=_img.split()[3])
  642. # ---------制作选区缩小的mask
  643. mask = cv2.cvtColor(
  644. np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY
  645. ) # 将PIL 格式转换为 CV对象
  646. mask[mask != 255] = 0
  647. # 黑白反转
  648. # mask = 255 - mask
  649. # 选区缩小10
  650. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  651. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  652. # -------再进行抠图处理
  653. mask = Image.fromarray(
  654. cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)
  655. ) # CV 对象转 PIL
  656. transparent_im = Image.new("RGBA", original_im.size, (0, 0, 0, 0))
  657. transparent_im.paste(original_im, (0, 0), mask.convert("L"))
  658. # 上述抠图结果进行拼接
  659. _img.paste(transparent_im, (0, 0), transparent_im)
  660. return _img
  661. @func_set_timeout(40)
  662. def ultra_segment_fast(self, file_path, out_file_path=None):
  663. '''
  664. 旗舰抠图快速版
  665. '''
  666. original_pic = Picture(file_path)
  667. original_pic.im = self.segment.get_image_orientation(original_pic.im)
  668. original_pic.x, original_pic.y = original_pic.im.size
  669. if original_pic.im.mode != "RGB":
  670. print("抠图图片不能是PNG")
  671. return None
  672. new_pic = copy.copy(original_pic)
  673. after_need_resize = False
  674. if new_pic.x > new_pic.y:
  675. if new_pic.x > 2000:
  676. after_need_resize = True
  677. new_pic.resize(2000)
  678. else:
  679. if new_pic.y > 2000:
  680. after_need_resize = True
  681. new_pic.resize_by_heigh(heigh=2000)
  682. print("使用旗舰版抠图")
  683. try:
  684. api_url = f"{settings.DOMAIN}{self.api_url}"
  685. image_url = self.segment.get_bo_bg_goods_ultra_background(im=new_pic.im, api_url=api_url)
  686. except BaseException as e:
  687. print("旗舰版抠图异常:", e)
  688. # 处理失败,需要删除过程图片
  689. return None
  690. if image_url is None:
  691. return None
  692. # 字节流转PIL对象
  693. print("image_url", image_url)
  694. response = requests.get(image_url)
  695. pic = response.content
  696. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  697. # 原图更大,则需要执行CV处理
  698. if after_need_resize:
  699. # 将抠图结果转成mask
  700. # _img_im = Image.open(_path)
  701. # 将抠图结果放大到原始图大小
  702. _img_im = _img_im.resize(original_pic.im.size)
  703. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  704. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  705. new_big_mask.paste(white, mask=_img_im.split()[3])
  706. # ---------制作选区缩小的mask
  707. # mask = cv2.imread(mask_path)
  708. # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  709. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  710. mask[mask != 255] = 0
  711. # 黑白反转
  712. # mask = 255 - mask
  713. # 选区缩小10
  714. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  715. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  716. # -------再进行抠图处理
  717. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  718. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  719. # original_pic.im.show()
  720. # mask.show()
  721. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  722. # transparent_im.show()
  723. # 上述抠图结果进行拼接
  724. _img_im.paste(transparent_im, (0, 0), transparent_im)
  725. # 原图更大,则需要执行CV处理
  726. if out_file_path:
  727. self.saver.save_image(
  728. image=_img_im, file_path=out_file_path,
  729. quality=100, dpi=(350, 350), _format="PNG"
  730. )
  731. return _img_im
  732. @func_set_timeout(40)
  733. def ultra_segment_fast_pil(self, file_path):
  734. '''
  735. 旗舰抠图快速版
  736. '''
  737. original_pic = Picture(file_path)
  738. original_pic.im = self.segment.get_image_orientation(original_pic.im)
  739. original_pic.x, original_pic.y = original_pic.im.size
  740. if original_pic.im.mode != "RGB":
  741. print("抠图图片不能是PNG")
  742. return None
  743. new_pic = copy.copy(original_pic)
  744. after_need_resize = False
  745. if new_pic.x > new_pic.y:
  746. if new_pic.x > 2000:
  747. after_need_resize = True
  748. new_pic.resize(2000)
  749. else:
  750. if new_pic.y > 2000:
  751. after_need_resize = True
  752. new_pic.resize_by_heigh(heigh=2000)
  753. # print("使用旗舰版抠图")
  754. try:
  755. api_url = f"{settings.DOMAIN}{self.api_url}"
  756. image_url = self.segment.get_bo_bg_goods_ultra_background(im=new_pic.im, api_url=api_url)
  757. except BaseException as e:
  758. print("旗舰版抠图异常:", e)
  759. # 处理失败,需要删除过程图片
  760. return None
  761. if image_url is None:
  762. return None
  763. # 字节流转PIL对象
  764. # print("image_url", image_url)
  765. response = requests.get(image_url)
  766. pic = response.content
  767. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  768. # 原图更大,则需要执行CV处理
  769. if after_need_resize:
  770. # 将抠图结果转成mask
  771. # _img_im = Image.open(_path)
  772. # 将抠图结果放大到原始图大小
  773. _img_im = _img_im.resize(original_pic.im.size)
  774. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  775. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  776. new_big_mask.paste(white, mask=_img_im.split()[3])
  777. # ---------制作选区缩小的mask
  778. # mask = cv2.imread(mask_path)
  779. # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  780. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  781. mask[mask != 255] = 0
  782. # 黑白反转
  783. # mask = 255 - mask
  784. # 选区缩小10
  785. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  786. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  787. # -------再进行抠图处理
  788. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  789. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  790. # original_pic.im.show()
  791. # mask.show()
  792. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  793. # transparent_im.show()
  794. # 上述抠图结果进行拼接
  795. _img_im.paste(transparent_im, (0, 0), transparent_im)
  796. # 原图更大,则需要执行CV处理
  797. return _img_im
  798. def run_ultra_segment(self, file_path, out_file_path):
  799. # 直接调用抠图
  800. time.sleep(0.01)
  801. # 1、增加获取key,2、key需要加密、3、429报错 重试再来拿一个KEY
  802. self.add_log("开始处理")
  803. cut_image_ali, image_deal_info = self.get_image_cut_ali(file_path)
  804. ultra_cutout_data = self.get_image_cut_ultra(out_file_path, cut_image_ali)
  805. if ultra_cutout_data is None:
  806. return None
  807. try:
  808. if image_deal_info["二次抠图是否缩放"]:
  809. # print("图片尺寸还原")
  810. self.add_log(text="图片尺寸进行还原")
  811. original_im = image_deal_info["抠图扩边后PIL对象"]
  812. second_cut_image = self.picture_resize_to_original(
  813. ultra_cutout_data, original_im
  814. )
  815. else:
  816. second_cut_image = ultra_cutout_data
  817. # 创建空白图片并粘贴回去
  818. _img_im = Image.new(
  819. mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0)
  820. )
  821. _img_im.paste(
  822. second_cut_image,
  823. box=(
  824. image_deal_info["抠图扩边后位置"][0],
  825. image_deal_info["抠图扩边后位置"][1],
  826. ),
  827. )
  828. _img_im.save(out_file_path, dpi=(350, 350))
  829. return _img_im
  830. except BaseException as e:
  831. # print(e)
  832. text = "{} 图片处理错误,代码49990".format(e)
  833. self.add_log(text)
  834. return
  835. if __name__ == '__main__':
  836. r = RemoveUltraBackground()
  837. path = r"C:\Users\15001\Desktop\miniso\hb\原始图\hb(1).JPG"
  838. out_path = "{}._no_bg-out.png".format(path)
  839. r.get_image_cut(path, out_file_path=out_path)