remove_bg_ali.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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
  20. # 自己的
  21. AccessKeyId = "LTAI5tCk4p881X8hymj2FYFk"
  22. AccessKeySecret = "yBYIYzX8CL24r5ZgEx2AgZyDBmFkIK"
  23. def uploadImage(im: Image) -> str:
  24. img_byte_io = BytesIO()
  25. # 根据图片模式选择保存格式
  26. if im.mode == 'RGBA':
  27. im.save(img_byte_io, format='PNG')
  28. else:
  29. im.save(img_byte_io, format='JPEG')
  30. img_byte_io.seek(0) # 重置指针到开头
  31. post_headers = {"Authorization": settings.USER_TOKEN}
  32. url = settings.DOMAIN + "/api/upload"
  33. # 使用字节流上传
  34. resultData = requests.post(
  35. url,
  36. files={"file": ("image.jpg", img_byte_io, "image/jpeg")},
  37. headers=post_headers
  38. ).json()
  39. return resultData["data"]["url"]
  40. # 惠利玛公司的KEY
  41. # AccessKeyId = 'LTAI5tCk4p881X8hymj2FYFk'
  42. # AccessKeySecret = 'rQMgHwciTN4Gusbpt8CM8tflgsxh1V'
  43. # https://help.aliyun.com/zh/viapi/developer-reference/python?spm=a2c4g.11186623.0.i0#task-2252575
  44. # pip install alibabacloud_goodstech20191230
  45. # pip install alibabacloud_tea_openapi
  46. # pip install alibabacloud_tea_util
  47. class Segment(object):
  48. def __init__(self):
  49. self.client = self.create_client()
  50. def get_no_bg_common(self, file_path):
  51. # 初始化RuntimeObject
  52. runtime_option = RuntimeOptions()
  53. try:
  54. # 场景一:文件在本地
  55. img = open(file_path, 'rb')
  56. # 使用完成之后记得调用img.close()关闭流
  57. # 场景二,使用任意可访问的url
  58. # url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/ocr/RecognizeBankCard/yhk1.jpg'
  59. # img = io.BytesIO(urlopen(url).read())
  60. # 4、初始化Request,这里只是以RecognizeBankCard为例,其他能力请使用相应能力对应的类
  61. request = SegmentCommodityAdvanceRequest()
  62. request.image_urlobject = img
  63. # 5、调用api,注意,recognize_bank_card_advance需要更换为相应能力对应的方法名。方法名是根据能力名称按照一定规范形成的,如能力名称为SegmentCommonImage,对应方法名应该为segment_common_image_advance。
  64. response = self.client.segment_common_image_advance(request, runtime_option)
  65. # 获取整体结果
  66. # print(response.body)
  67. img.close()
  68. return response.body
  69. # 获取单个字段,这里只是一个例子,具体能力下的字段需要看具体能力的文档
  70. # print(response.body.data.card_number)
  71. # tips: 可通过response.body.__dict__查看属性名称
  72. except Exception as error:
  73. # 获取整体报错信息
  74. print("error", error)
  75. return None
  76. # 获取单个字段
  77. # print(error.code)
  78. # tips: 可通过error.__dict__查看属性名称
  79. def get_image_orientation(self, img):
  80. # 获取EXIF数据
  81. exif = img._getexif()
  82. if exif is not None:
  83. # EXIF标签274对应的是Orientation
  84. orientation = exif.get(0x0112)
  85. print("orientation", orientation)
  86. if orientation == 2:
  87. # 水平翻转
  88. img = img.transpose(Image.FLIP_LEFT_RIGHT)
  89. elif orientation == 3:
  90. # 旋转180度
  91. img = img.rotate(180, expand=True)
  92. elif orientation == 4:
  93. # 垂直翻转
  94. img = img.transpose(Image.FLIP_TOP_BOTTOM)
  95. elif orientation == 5:
  96. # 水平翻转后顺时针旋转90度
  97. img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
  98. elif orientation == 6:
  99. # 顺时针旋转90度
  100. img = img.transpose(Image.ROTATE_270)
  101. elif orientation == 7:
  102. # 水平翻转后逆时针旋转90度
  103. img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
  104. elif orientation == 8:
  105. # 逆时针旋转90度
  106. img = img.transpose(Image.ROTATE_90)
  107. else:
  108. print("没有EXIF数据或没有方向信息")
  109. orientation = 1
  110. return img
  111. def get_bo_bg_goods_ultra_background(self, im, is_shadow=False, api_url=None):
  112. imageUrl = uploadImage(im)
  113. # imageUrl = imageUrl + "?x-oss-process=image/auto-orient,1"
  114. if not settings.USER_TOKEN:
  115. print("错误:USER_TOKEN 未配置或为空")
  116. return None
  117. print("图片上传成功", imageUrl)
  118. post_headers = {"Authorization": "Bearer " + settings.USER_TOKEN}
  119. data = {
  120. "image_url": imageUrl,
  121. "is_shadow": is_shadow
  122. }
  123. response = requests.post(
  124. api_url, json=data, headers=post_headers
  125. )
  126. print(f"响应状态码: {response.status_code}")
  127. print(f"响应内容: {response.text[:500]}") # 只打印前500字符
  128. resultData = response.json()
  129. print("旗舰版抠图请求", resultData)
  130. # 安全地获取返回值
  131. data = resultData.get("data")
  132. if data is None:
  133. print("旗舰版抠图返回的data为空")
  134. return None
  135. cutout_image = data.get("cutout_image")
  136. if cutout_image is None:
  137. print("旗舰版抠图返回的cutout_image为空")
  138. return None
  139. return cutout_image
  140. def get_no_bg_goods(self, file_path=None, _im=None):
  141. # file_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\change_color_2\test\_MG_9061.jpg"
  142. # file_path_1 = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\change_color_2\test\_MG_9061_resize.png"
  143. # if file_path:
  144. # img = open(file_path, 'rb')
  145. # if _im:
  146. # https://blog.csdn.net/weixin_43411585/article/details/107780941
  147. im = _im
  148. # im.save(file_path)
  149. img = BytesIO()
  150. im.save(img, format='JPEG') # format: PNG or JPEG
  151. img.seek(0) # rewind to the start
  152. # img = img_byte.getvalue() # im对象转为二进制流
  153. # with open(file_path, "wb") as binary_file:
  154. # binary_file.write(im.tobytes())
  155. # file_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\change_color_2\test\1.png"
  156. # img = open(file_path, 'rb')
  157. request = imageseg_20191230_models.SegmentCommodityAdvanceRequest()
  158. request.image_urlobject = img
  159. client = self.create_client()
  160. # 5、调用api,注意,recognize_bank_card_advance需要更换为相应能力对应的方法名。方法名是根据能力名称按照一定规范形成的,如能力名称为SegmentCommonImage,对应方法名应该为segment_common_image_advance。
  161. runtime = util_models.RuntimeOptions()
  162. response = client.segment_commodity_advance(request, runtime)
  163. # img.close()
  164. # print("1111111111111", response.body)
  165. return response.body
  166. def create_client(self):
  167. """
  168. 使用AK&SK初始化账号Client
  169. @param access_key_id:
  170. @param access_key_secret:
  171. @return: Client
  172. @throws Exception
  173. """
  174. config = open_api_models.Config(
  175. # 必填,您的 AccessKey ID,
  176. access_key_id=AccessKeyId,
  177. # 必填,您的 AccessKey Secret,
  178. access_key_secret=AccessKeySecret
  179. )
  180. # 访问的域名
  181. config.endpoint = f'imageseg.cn-shanghai.aliyuncs.com'
  182. return imageseg20191230Client(config)
  183. class Picture:
  184. def __init__(self, in_path, im=None):
  185. if im:
  186. self.im = im
  187. else:
  188. self.im = Image.open(in_path)
  189. self.x, self.y = self.im.size
  190. # print(self.x, self.y)
  191. def save_img(self, outpath, quality=90):
  192. # self.im = self.im.convert("RGB")
  193. self.im.save(outpath, quality=quality)
  194. def resize(self, width):
  195. re_x = int(width)
  196. re_y = int(self.y * re_x / self.x)
  197. self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
  198. self.x, self.y = self.im.size
  199. def resize_by_heigh(self, heigh):
  200. re_y = int(heigh)
  201. re_x = int(self.x * re_y / self.y)
  202. self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
  203. self.x, self.y = self.im.size
  204. class RemoveBgALi(object):
  205. def __init__(self):
  206. self.saver = ImageSaver()
  207. self.segment = Segment()
  208. @func_set_timeout(40)
  209. def get_image_cut_new(self, file_path, out_file_path=None, original_im=None):
  210. if original_im:
  211. original_pic = Picture(in_path=None, im=original_im)
  212. else:
  213. original_pic = Picture(file_path)
  214. if original_pic.im.mode != "RGB":
  215. original_pic.im = original_pic.im.convert("RGB")
  216. new_pic = copy.copy(original_pic)
  217. after_need_resize = False
  218. if new_pic.x > new_pic.y:
  219. if new_pic.x > 2000:
  220. after_need_resize = True
  221. new_pic.resize(2000)
  222. else:
  223. if new_pic.y > 2000:
  224. after_need_resize = True
  225. new_pic.resize_by_heigh(heigh=2000)
  226. # new_pic.im.show()
  227. body = self.segment.get_no_bg_goods(file_path=None, _im=new_pic.im)
  228. body = eval(str(body))
  229. try:
  230. image_url = body["Data"]["ImageURL"]
  231. except BaseException as e:
  232. print("阿里抠图错误:", e)
  233. # 处理失败,需要删除过程图片
  234. return None
  235. # 字节流转PIL对象
  236. response = requests.get(image_url)
  237. pic = response.content
  238. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  239. box_size = _img_im.getbbox()
  240. new_pp4_im = _img_im.crop(box_size)
  241. byte_io = BytesIO()
  242. new_pp4_im.save(byte_io, format='PNG') # 将图像保存为 PNG 格式到 BytesIO 对象
  243. byte_io.seek(0) # 将指针重置到流的开头,以便后续读取
  244. return byte_io
  245. @func_set_timeout(40)
  246. def get_image_cut(self, file_path, out_file_path=None, original_im=None):
  247. if original_im:
  248. original_pic = Picture(in_path=None, im=original_im)
  249. else:
  250. original_pic = Picture(file_path)
  251. if original_pic.im.mode != "RGB":
  252. print("抠图图片不能是PNG")
  253. return None
  254. new_pic = copy.copy(original_pic)
  255. after_need_resize = False
  256. if new_pic.x > new_pic.y:
  257. if new_pic.x > 2000:
  258. after_need_resize = True
  259. new_pic.resize(2000)
  260. else:
  261. if new_pic.y > 2000:
  262. after_need_resize = True
  263. new_pic.resize_by_heigh(heigh=2000)
  264. # new_pic.im.show()
  265. body = self.segment.get_no_bg_goods(file_path=None, _im=new_pic.im)
  266. body = eval(str(body))
  267. try:
  268. image_url = body["Data"]["ImageURL"]
  269. except BaseException as e:
  270. print("阿里抠图错误:", e)
  271. # 处理失败,需要删除过程图片
  272. return None
  273. # 字节流转PIL对象
  274. response = requests.get(image_url)
  275. pic = response.content
  276. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  277. # 原图更大,则需要执行CV处理
  278. if after_need_resize:
  279. # 将抠图结果转成mask
  280. # _img_im = Image.open(_path)
  281. # 将抠图结果放大到原始图大小
  282. _img_im = _img_im.resize(original_pic.im.size)
  283. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  284. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  285. new_big_mask.paste(white, mask=_img_im.split()[3])
  286. # ---------制作选区缩小的mask
  287. # mask = cv2.imread(mask_path)
  288. # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  289. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  290. mask[mask != 255] = 0
  291. # 黑白反转
  292. # mask = 255 - mask
  293. # 选区缩小10
  294. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  295. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  296. # -------再进行抠图处理
  297. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  298. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  299. # original_pic.im.show()
  300. # mask.show()
  301. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  302. # transparent_im.show()
  303. # 上述抠图结果进行拼接
  304. _img_im.paste(transparent_im, (0, 0), transparent_im)
  305. # _img_im.show("11111111111111111111111")
  306. if out_file_path:
  307. self.saver.save_image(
  308. image=_img_im, file_path=out_file_path,
  309. quality=100, dpi=(350, 350), _format="PNG"
  310. )
  311. # _img_im.save(out_file_path)
  312. return _img_im
  313. def get_image_cut1(self, file_path, out_file_path=None):
  314. original_pic = Picture(file_path)
  315. new_pic = copy.copy(original_pic)
  316. if new_pic.x > 2000:
  317. new_pic.resize(2000)
  318. # new_pic.im.show()
  319. body = self.segment.get_no_bg_goods(file_path=out_file_path, _im=new_pic.im)
  320. body = eval(str(body))
  321. try:
  322. image_url = body["Data"]["ImageURL"]
  323. except BaseException as e:
  324. print("阿里抠图错误:", e)
  325. # 处理失败,需要删除过程图片
  326. return None
  327. # 字节流转PIL对象
  328. response = requests.get(image_url)
  329. pic = response.content
  330. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  331. if original_pic.x > 2000:
  332. # 原图更大,则需要执行CV处理
  333. # _img_im.show()
  334. # 对mask进行放大,然后进行抠图处理
  335. print("对mask进行放大,然后进行抠图处理")
  336. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  337. # original_pic.im.show()
  338. # mask.show()
  339. _img_im = _img_im.resize((original_pic.x, original_pic.y))
  340. # _img_im.show()
  341. transparent_im.paste(original_pic.im, (0, 0), mask=_img_im)
  342. # transparent_im.show()
  343. # transparent_im.show()
  344. _img_im = transparent_im
  345. # 上述抠图结果进行拼接
  346. # _img_im.paste(transparent_im, (0, 0), transparent_im)
  347. pass
  348. _img_im.save(out_file_path)
  349. return _img_im
  350. def download_picture(self, url, out_path):
  351. response = requests.get(url)
  352. pic = response.content
  353. with open(out_path, 'wb') as f:
  354. f.write(pic)
  355. class RemoveUltraBackground:
  356. def __init__(self):
  357. self.api_url = '/api/ai_image/segment_shadow/segment_service'
  358. self.headers = {
  359. 'Content-Type': 'application/json',
  360. 'Accept': 'application/json'
  361. }
  362. self.saver = ImageSaver()
  363. self.segment = Segment()
  364. @func_set_timeout(40)
  365. def get_image_cut(self, file_path, out_file_path=None):
  366. original_pic = Picture(file_path)
  367. original_pic.im = self.segment.get_image_orientation(original_pic.im)
  368. original_pic.x, original_pic.y = original_pic.im.size
  369. if original_pic.im.mode != "RGB":
  370. print("抠图图片不能是PNG")
  371. return None
  372. new_pic = copy.copy(original_pic)
  373. after_need_resize = False
  374. if new_pic.x > new_pic.y:
  375. if new_pic.x > 2000:
  376. after_need_resize = True
  377. new_pic.resize(2000)
  378. else:
  379. if new_pic.y > 2000:
  380. after_need_resize = True
  381. new_pic.resize_by_heigh(heigh=2000)
  382. print("使用旗舰版抠图")
  383. try:
  384. api_url = f"{settings.DOMAIN}{self.api_url}"
  385. image_url = self.segment.get_bo_bg_goods_ultra_background(im=new_pic.im, api_url=api_url)
  386. except BaseException as e:
  387. print("旗舰版抠图异常:", e)
  388. # 处理失败,需要删除过程图片
  389. return None
  390. # 字节流转PIL对象
  391. print("image_url", image_url)
  392. response = requests.get(image_url)
  393. pic = response.content
  394. _img_im = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
  395. # 原图更大,则需要执行CV处理
  396. if after_need_resize:
  397. # 将抠图结果转成mask
  398. # _img_im = Image.open(_path)
  399. # 将抠图结果放大到原始图大小
  400. _img_im = _img_im.resize(original_pic.im.size)
  401. new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
  402. white = Image.new('RGB', _img_im.size, (255, 255, 255))
  403. new_big_mask.paste(white, mask=_img_im.split()[3])
  404. # ---------制作选区缩小的mask
  405. # mask = cv2.imread(mask_path)
  406. # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
  407. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  408. mask[mask != 255] = 0
  409. # 黑白反转
  410. # mask = 255 - mask
  411. # 选区缩小10
  412. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  413. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  414. # -------再进行抠图处理
  415. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  416. transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
  417. # original_pic.im.show()
  418. # mask.show()
  419. transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
  420. # transparent_im.show()
  421. # 上述抠图结果进行拼接
  422. _img_im.paste(transparent_im, (0, 0), transparent_im)
  423. # 原图更大,则需要执行CV处理
  424. if out_file_path:
  425. self.saver.save_image(
  426. image=_img_im, file_path=out_file_path,
  427. quality=100, dpi=(350, 350), _format="PNG"
  428. )
  429. return _img_im
  430. if __name__ == '__main__':
  431. r = RemoveUltraBackground()
  432. path = r"C:\Users\15001\Desktop\miniso\hb\原始图\hb(1).JPG"
  433. out_path = "{}._no_bg-out.png".format(path)
  434. r.get_image_cut(path, out_file_path=out_path)