Spaces:
Running
Running
import io | |
import time | |
import zipfile | |
import requests | |
from PIL import Image, ImageColor | |
from carvekit.utils.image_utils import transparency_paste, add_margin | |
from carvekit.utils.mask_utils import extract_alpha_channel | |
from carvekit.web.responses.api import error_dict | |
from carvekit.api.interface import Interface | |
def process_remove_bg( | |
interface: Interface, params, image, bg, is_json_or_www_encoded=False | |
): | |
""" | |
Handles a request to the removebg api method | |
Args: | |
interface: CarveKit interface | |
bg: background pil image | |
is_json_or_www_encoded: is "json" or "x-www-form-urlencoded" content-type | |
image: foreground pil image | |
params: parameters | |
""" | |
h, w = image.size | |
if h < 2 or w < 2: | |
return error_dict("Image is too small. Minimum size 2x2"), 400 | |
if "size" in params.keys(): | |
value = params["size"] | |
if value == "preview" or value == "small" or value == "regular": | |
image.thumbnail((625, 400), resample=3) # 0.25 mp | |
elif value == "medium": | |
image.thumbnail((1504, 1000), resample=3) # 1.5 mp | |
elif value == "hd": | |
image.thumbnail((2000, 2000), resample=3) # 2.5 mp | |
else: | |
image.thumbnail((6250, 4000), resample=3) # 25 mp | |
roi_box = [0, 0, image.size[0], image.size[1]] | |
if "type" in params.keys(): | |
value = params["type"] | |
pass | |
if "roi" in params.keys(): | |
value = params["roi"].split(" ") | |
if len(value) == 4: | |
for i, coord in enumerate(value): | |
if "px" in coord: | |
coord = coord.replace("px", "") | |
try: | |
coord = int(coord) | |
except BaseException: | |
return ( | |
error_dict( | |
"Error converting roi coordinate string to number!" | |
), | |
400, | |
) | |
if coord < 0: | |
error_dict("Bad roi coordinate."), 400 | |
if (i == 0 or i == 2) and coord > image.size[0]: | |
return ( | |
error_dict( | |
"The roi coordinate cannot be larger than the image size." | |
), | |
400, | |
) | |
elif (i == 1 or i == 3) and coord > image.size[1]: | |
return ( | |
error_dict( | |
"The roi coordinate cannot be larger than the image size." | |
), | |
400, | |
) | |
roi_box[i] = int(coord) | |
elif "%" in coord: | |
coord = coord.replace("%", "") | |
try: | |
coord = int(coord) | |
except BaseException: | |
return ( | |
error_dict( | |
"Error converting roi coordinate string to number!" | |
), | |
400, | |
) | |
if coord > 100: | |
return ( | |
error_dict("The coordinate cannot be more than 100%"), | |
400, | |
) | |
elif coord < 0: | |
return error_dict("Coordinate cannot be less than 0%"), 400 | |
if i == 0 or i == 2: | |
coord = int(image.size[0] * coord / 100) | |
elif i == 1 or i == 3: | |
coord = int(image.size[1] * coord / 100) | |
roi_box[i] = coord | |
else: | |
return error_dict("Something wrong with roi coordinates!"), 400 | |
new_image = image.copy() | |
new_image = new_image.crop(roi_box) | |
h, w = new_image.size | |
if h < 2 or w < 2: | |
return error_dict("Image is too small. Minimum size 2x2"), 400 | |
new_image = interface([new_image])[0] | |
scaled = False | |
if "scale" in params.keys() and params["scale"] != 100: | |
value = params["scale"] | |
new_image.thumbnail( | |
(int(image.size[0] * value / 100), int(image.size[1] * value / 100)), | |
resample=3, | |
) | |
scaled = True | |
if "crop" in params.keys(): | |
value = params["crop"] | |
if value: | |
new_image = new_image.crop(new_image.getbbox()) | |
if "crop_margin" in params.keys(): | |
crop_margin = params["crop_margin"] | |
if "px" in crop_margin: | |
crop_margin = crop_margin.replace("px", "") | |
crop_margin = abs(int(crop_margin)) | |
if crop_margin > 500: | |
return ( | |
error_dict( | |
"The crop_margin cannot be larger than the original image size." | |
), | |
400, | |
) | |
new_image = add_margin( | |
new_image, | |
crop_margin, | |
crop_margin, | |
crop_margin, | |
crop_margin, | |
(0, 0, 0, 0), | |
) | |
elif "%" in crop_margin: | |
crop_margin = crop_margin.replace("%", "") | |
crop_margin = int(crop_margin) | |
new_image = add_margin( | |
new_image, | |
int(new_image.size[1] * crop_margin / 100), | |
int(new_image.size[0] * crop_margin / 100), | |
int(new_image.size[1] * crop_margin / 100), | |
int(new_image.size[0] * crop_margin / 100), | |
(0, 0, 0, 0), | |
) | |
else: | |
if "position" in params.keys() and scaled is False: | |
value = params["position"] | |
if len(value) == 2: | |
new_image = transparency_paste( | |
Image.new("RGBA", image.size), | |
new_image, | |
( | |
int(image.size[0] * value[0] / 100), | |
int(image.size[1] * value[1] / 100), | |
), | |
) | |
else: | |
new_image = transparency_paste( | |
Image.new("RGBA", image.size), new_image, roi_box | |
) | |
elif scaled is False: | |
new_image = transparency_paste( | |
Image.new("RGBA", image.size), new_image, roi_box | |
) | |
if "channels" in params.keys(): | |
value = params["channels"] | |
if value == "alpha": | |
new_image = extract_alpha_channel(new_image) | |
else: | |
bg_changed = False | |
if "bg_color" in params.keys(): | |
value = params["bg_color"] | |
if len(value) > 0: | |
color = ImageColor.getcolor(value, "RGB") | |
bg = Image.new("RGBA", new_image.size, color) | |
bg = transparency_paste(bg, new_image, (0, 0)) | |
new_image = bg.copy() | |
bg_changed = True | |
if "bg_image_url" in params.keys() and bg_changed is False: | |
value = params["bg_image_url"] | |
if len(value) > 0: | |
try: | |
bg = Image.open(io.BytesIO(requests.get(value).content)) | |
except BaseException: | |
return error_dict("Error download background image!"), 400 | |
bg = bg.resize(new_image.size) | |
bg = bg.convert("RGBA") | |
bg = transparency_paste(bg, new_image, (0, 0)) | |
new_image = bg.copy() | |
bg_changed = True | |
if not is_json_or_www_encoded: | |
if bg and bg_changed is False: | |
bg = bg.resize(new_image.size) | |
bg = bg.convert("RGBA") | |
bg = transparency_paste(bg, new_image, (0, 0)) | |
new_image = bg.copy() | |
if "format" in params.keys(): | |
value = params["format"] | |
if value == "jpg": | |
new_image = new_image.convert("RGB") | |
img_io = io.BytesIO() | |
new_image.save(img_io, "JPEG", quality=100) | |
img_io.seek(0) | |
return {"type": "jpg", "data": [img_io, new_image.size]} | |
elif value == "zip": | |
mask = extract_alpha_channel(new_image) | |
mask_buff = io.BytesIO() | |
mask.save(mask_buff, "PNG") | |
mask_buff.seek(0) | |
image_buff = io.BytesIO() | |
image.save(image_buff, "JPEG") | |
image_buff.seek(0) | |
fileobj = io.BytesIO() | |
with zipfile.ZipFile(fileobj, "w") as zip_file: | |
zip_info = zipfile.ZipInfo(filename="color.jpg") | |
zip_info.date_time = time.localtime(time.time())[:6] | |
zip_info.compress_type = zipfile.ZIP_DEFLATED | |
zip_file.writestr(zip_info, image_buff.getvalue()) | |
zip_info = zipfile.ZipInfo(filename="alpha.png") | |
zip_info.date_time = time.localtime(time.time())[:6] | |
zip_info.compress_type = zipfile.ZIP_DEFLATED | |
zip_file.writestr(zip_info, mask_buff.getvalue()) | |
fileobj.seek(0) | |
return {"type": "zip", "data": [fileobj.read(), new_image.size]} | |
else: | |
buff = io.BytesIO() | |
new_image.save(buff, "PNG") | |
buff.seek(0) | |
return {"type": "png", "data": [buff, new_image.size]} | |
return ( | |
error_dict( | |
"Something wrong with request or http api. Please, open new issue on Github! This is error in " | |
"code." | |
), | |
400, | |
) | |