diff --git a/app/api/api_design.py b/app/api/api_design.py index b522e74..20db150 100644 --- a/app/api/api_design.py +++ b/app/api/api_design.py @@ -27,6 +27,15 @@ def design(request_data: DesignModel): - **mask_url** 非空"mask_url" -> 区域透明 - **transpose** 镜像模式 ,:"top_bottom"或"left_right" - **rotate** 45, + + - ** design 参数变更: + design detail 请求参数中 basic -> preview_submit 替换为design_type 可选参数 default ,merge (移除preview和submit) + design_type 参数说明: + defuault模式下 请求参数不变 + merge模式下 items -> 每个item需要新增 merge_image_path , merge_image_path为前端处理 print color等操作后的单件结果图 + + ** + - 创建一个具有以下参数的请求体: 示例参数: ```json @@ -61,7 +70,7 @@ def design(request_data: DesignModel): ] }, "layer_order": true, - "preview_submit": "preview", + "design_type": "preview", "scale_bag": 0.7, "scale_earrings": 0.16, "self_template": true, diff --git a/app/service/design_fast/design_generate.py b/app/service/design_fast/design_generate.py index 61a4996..7a4e48e 100644 --- a/app/service/design_fast/design_generate.py +++ b/app/service/design_fast/design_generate.py @@ -6,10 +6,10 @@ import requests from minio import Minio from app.core.config import settings -from app.service.design_fast.item import BodyItem, TopItem, BottomItem, OthersItem +from app.service.design_fast.item import BodyItem, TopItem, BottomItem, OthersItem, TopMergeItem, BottomMergeItem, OthersMergeItem from app.service.design_fast.utils.organize import organize_body, organize_clothing, organize_others from app.service.design_fast.utils.progress import final_progress, update_progress -from app.service.design_fast.utils.synthesis_item import synthesis, synthesis_single, update_base_size_priority +from app.service.design_fast.utils.synthesis_item import synthesis, synthesis_single, update_base_size_priority, merge from app.service.utils.decorator import RunTime id_lock = threading.Lock() @@ -19,22 +19,46 @@ logger = logging.getLogger() minio_client = Minio(settings.MINIO_URL, access_key=settings.MINIO_ACCESS, secret_key=settings.MINIO_SECRET, secure=settings.MINIO_SECURE) -def process_item(item, basic): - # 处理project中单个item - if item['type'] == "Body": - body_server = BodyItem(data=item, basic=basic, minio_client=minio_client) - item_data = body_server.process() - elif item['type'].lower() in ['blouse', 'outwear', 'dress', 'tops']: - top_server = TopItem(data=item, basic=basic, minio_client=minio_client) - item_data = top_server.process() - elif item['type'].lower() in ['skirt', 'trousers', 'bottoms']: - bottom_server = BottomItem(data=item, basic=basic, minio_client=minio_client) - item_data = bottom_server.process() - elif item['type'].lower() in ['others']: - bottom_server = OthersItem(data=item, basic=basic, minio_client=minio_client) - item_data = bottom_server.process() +def process_item(item, basic, design_type): + # 1. 定义映射配置 + # key 为 item_type 的小写,value 为对应的处理类 + DESIGN_MAP = { + 'body': BodyItem, + 'blouse': TopItem, 'outwear': TopItem, + 'dress': TopItem, 'tops': TopItem, + 'skirt': BottomItem, 'trousers': BottomItem, + 'bottoms': BottomItem, + 'others': OthersItem + } + + MERGE_MAP = { + 'body_merge': BodyItem, + 'blouse_merge': TopMergeItem, 'outwear_merge': TopMergeItem, + 'dress_merge': TopMergeItem, 'tops_merge': TopMergeItem, + 'skirt_merge': BottomMergeItem, 'trousers_merge': BottomMergeItem, + 'bottoms_merge': BottomMergeItem, + 'others_merge': OthersMergeItem + } + + # 2. 根据 design_type 选择映射表 + mapping = MERGE_MAP if design_type == 'merge' else DESIGN_MAP + + if design_type == 'merge': + item_type_key = f"{item['type'].lower()}_merge" + elif design_type == 'default': + item_type_key = item['type'].lower() else: - raise NotImplementedError(f"Item type {item['type']} not implemented") + item_type_key = item['type'].lower() + + handler_class = mapping.get(item_type_key) + + if not handler_class: + raise NotImplementedError(f"Item type {item['type']} not implemented for design_type={design_type}") + + # 4. 统一实例化并执行 + # 注意:这里假设所有 Item 类构造函数签名一致 + server = handler_class(data=item, basic=basic, minio_client=minio_client) + item_data = server.process() return item_data @@ -44,7 +68,7 @@ def process_layer(item, layers): body_layer = organize_body(item) layers.append(body_layer) return item['body_image'].size - elif item['name'] == 'others': + elif item['name'] in ['others', 'others_merge']: front_layer, back_layer = organize_others(item) layers.append(front_layer) layers.append(back_layer) @@ -70,10 +94,11 @@ def design_generate(request_data): nonlocal active_threads basic = object['basic'] items_response = {'layers': [], 'objectSign': object['objectSign'] if 'objectSign' in object.keys() else ""} + design_type = basic.get('design_type', "default") if basic['single_overall'] == "overall": item_results = [] for item in object['items']: - item_results.append(process_item(item, basic)) + item_results.append(process_item(item, basic, design_type)) layers = [] for item in item_results: process_layer(item, layers) @@ -97,7 +122,13 @@ def design_generate(request_data): 'rotate': lay.get('rotate', None), # 'back_perspective_url': lay['back_perspective_url'] if 'back_perspective_url' in lay.keys() else None, }) - items_response['synthesis_url'] = synthesis(layers, new_size, basic) + if basic.get('design_type') == 'default': + items_response['synthesis_url'] = synthesis(layers, new_size, basic) + elif basic.get('design_type') == 'merge': + items_response['synthesis_url'] = merge(layers, new_size, basic) + else: + items_response['synthesis_url'] = synthesis(layers, new_size, basic) + else: item_result = process_item(object['items'][0], basic) items_response['layers'].append({ diff --git a/app/service/design_fast/item.py b/app/service/design_fast/item.py index dcad5f0..e1bafb9 100644 --- a/app/service/design_fast/item.py +++ b/app/service/design_fast/item.py @@ -7,6 +7,7 @@ class BaseItem: self.result['name'] = data['type'].lower() self.result.pop("type") self.result.update(basic) + self.result['design_type'] = basic.get('design_type', None) class OthersItem(BaseItem): @@ -14,13 +15,7 @@ class OthersItem(BaseItem): super().__init__(data, basic) self.Others_pipeline = [ LoadImage(minio_client), - # KeyPoint(), - # ContourDetection(), Segmentation(minio_client), - # BackPerspective(minio_client), - Color(minio_client), - NoSegPrintPainting(minio_client), - PrintPainting(minio_client), Scaling(), Split(minio_client) ] @@ -74,6 +69,65 @@ class BottomItem(BaseItem): return self.result +"""merge""" + + +class OthersMergeItem(BaseItem): + def __init__(self, data, basic, minio_client): + super().__init__(data, basic) + self.Others_pipeline = [ + LoadImage(minio_client), + # KeyPoint(), + # ContourDetection(), + Segmentation(minio_client), + # BackPerspective(minio_client), + Color(minio_client), + NoSegPrintPainting(minio_client), + PrintPainting(minio_client), + Scaling(), + Split(minio_client) + ] + + def process(self): + for item in self.Others_pipeline: + self.result = item(self.result) + return self.result + + +class TopMergeItem(BaseItem): + def __init__(self, data, basic, minio_client): + super().__init__(data, basic) + self.top_pipeline = [ + LoadImage(minio_client), + KeyPoint(), + Segmentation(minio_client), + Scaling(), + Split(minio_client) + ] + + def process(self): + for item in self.top_pipeline: + self.result = item(self.result) + return self.result + + +class BottomMergeItem(BaseItem): + def __init__(self, data, basic, minio_client): + super().__init__(data, basic) + self.bottom_pipeline = [ + LoadImage(minio_client), + KeyPoint(), + Segmentation(minio_client), + Scaling(), + Split(minio_client) + ] + + def process(self): + for item in self.bottom_pipeline: + self.result = item(self.result) + return self.result + + class BodyItem(BaseItem): def __init__(self, data, basic, minio_client): super().__init__(data, basic) diff --git a/app/service/design_fast/pipeline/loading.py b/app/service/design_fast/pipeline/loading.py index 683a7ed..88662fa 100644 --- a/app/service/design_fast/pipeline/loading.py +++ b/app/service/design_fast/pipeline/loading.py @@ -35,15 +35,9 @@ class LoadImage: return cls.name def __call__(self, result): + if result.get("merge_image_path"): + result['merge_image'], _ = self.read_image(result['merge_image_path']) result['image'], result['pre_mask'] = self.read_image(result['path']) - # if 'extract_lines' in result.keys(): - # if result['extract_lines']: - # result['gray'] = self.get_lines(cv2.cvtColor(result['image'], cv2.COLOR_BGR2GRAY), result['path']) - # else: - # result['gray'] = cv2.cvtColor(result['image'], cv2.COLOR_BGR2GRAY) - # else: - # result['gray'] = cv2.cvtColor(result['image'], cv2.COLOR_BGR2GRAY) - result['gray'] = self.get_lines(cv2.cvtColor(result['image'], cv2.COLOR_BGR2GRAY)) result['keypoint'] = self.get_keypoint(result['name']) result['img_shape'] = result['image'].shape @@ -61,21 +55,6 @@ class LoadImage: mask = skeleton result = np.ones_like(img) * 255 result[mask] = img[mask] - - # 步骤2:细化边缘(可选,让线条更干净) - # kernel = np.ones((1, 1), np.uint8) - # clean = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) - - # thinned = cv2.ximgproc.thinning(binary, thinningType=cv2.ximgproc.THINNING_ZHANGSUEN) # thinning算法细化线条 - # mask = thinned > 0 - # result = np.ones_like(img) * 255 - # result[mask] = img[mask] - - # 步骤3:反转回 白底黑线 - # lines = cv2.bitwise_not(thinned) - # cv2.imwrite(os.path.join('/home/user/PycharmProjects/trinity_client_aida/test/lines_original_result_5', f"Original_{path.replace('/', '-')}.png"), img) - # cv2.imwrite(os.path.join('/home/user/PycharmProjects/trinity_client_aida/test/lines_original_result_5', f"Line_{path.replace('/', '-')}.png"), result) - return result def read_image(self, image_path): @@ -96,19 +75,19 @@ class LoadImage: @staticmethod def get_keypoint(name): - if name == 'blouse' or name == 'outwear' or name == 'dress' or name == 'tops': + if name in ['blouse', 'outwear', 'dress', 'tops', 'blouse_merge', 'outwear_merge', 'dress_merge', 'tops_merge']: keypoint = 'shoulder' - elif name == 'trousers' or name == 'skirt' or name == 'bottoms': + elif name in ['trousers', 'skirt', 'bottoms', 'trousers_merge', 'skirt_merge', 'bottoms_merge']: keypoint = 'waistband' - elif name == 'bag': + elif name in ['bag', 'bag_merge']: keypoint = 'hand_point' - elif name == 'shoes': + elif name in ['shoes', 'shoes_merge']: keypoint = 'toe' - elif name == 'hairstyle': + elif name in ['hairstyle', 'hairstyle_merge']: keypoint = 'head_point' - elif name == 'earring': + elif name in ['earring', 'earring_merge']: keypoint = 'ear_point' - elif name == 'others': + elif name in ['others', 'others_merge']: keypoint = "others" else: raise KeyError(f"{name} does not belong to item category list: blouse, outwear, dress, trousers, skirt, " diff --git a/app/service/design_fast/pipeline/segmentation.py b/app/service/design_fast/pipeline/segmentation.py index 9d619d7..bdf75a0 100644 --- a/app/service/design_fast/pipeline/segmentation.py +++ b/app/service/design_fast/pipeline/segmentation.py @@ -34,15 +34,15 @@ class Segmentation: result['mask'] = result['front_mask'] + result['back_mask'] else: # preview 过模型 不缓存 - if "preview_submit" in result.keys() and result['preview_submit'] == "preview": - # 推理获得seg 结果 + if result.get("design_type", None) == "merge": seg_result = get_seg_result(result['image']) - # submit 过模型 缓存 - elif "preview_submit" in result.keys() and result['preview_submit'] == "submit": - # 推理获得seg 结果 - seg_result = get_seg_result(result['image']) - self.save_seg_result(seg_result, result['image_id']) - # null 正常流程 加载本地缓存 无缓存则过模型 + # 默认design 模式 - 过模型 缓存 + # elif result.get("design_type", None) == "submit": + # 推理获得seg 结果 + # seg_result = get_seg_result(result['image']) + # self.save_seg_result(seg_result, result['image_id']) + + # 默认模式- 加载模型,找不到则过模型推理,推理后保存到本地 else: # 本地查询seg 缓存是否存在 _, seg_result = self.load_seg_result(result["image_id"]) diff --git a/app/service/design_fast/pipeline/split.py b/app/service/design_fast/pipeline/split.py index e38cc35..fe0c45a 100644 --- a/app/service/design_fast/pipeline/split.py +++ b/app/service/design_fast/pipeline/split.py @@ -4,6 +4,7 @@ import logging import cv2 import numpy as np from PIL import Image +from celery.bin.result import result from app.service.design_fast.utils.conversion_image import rgb_to_rgba from app.service.design_fast.utils.transparent import sketch_to_transparent @@ -19,105 +20,106 @@ class Split(object): def __call__(self, result): try: if result['name'] in ('outwear', 'dress', 'blouse', 'skirt', 'trousers', 'tops', 'bottoms', 'others'): - ori_front_mask = result['front_mask'].copy() - ori_back_mask = result['back_mask'].copy() - - if result['resize_scale'][0] == 1.0 and result['resize_scale'][1] == 1.0: - front_mask = result['front_mask'] - back_mask = result['back_mask'] - else: - height, width = result['front_mask'].shape[:2] - new_width = int(width * result['resize_scale'][0]) - new_height = int(height * result['resize_scale'][1]) - - front_mask = cv2.resize(result['front_mask'], (new_width, new_height), interpolation=cv2.INTER_AREA) - back_mask = cv2.resize(result['back_mask'], (new_width, new_height), interpolation=cv2.INTER_AREA) - - rgba_image = rgb_to_rgba(result['final_image'], front_mask + back_mask) - new_size = (int(rgba_image.shape[1] * result["scale"]), int(rgba_image.shape[0] * result["scale"])) - rgba_image = cv2.resize(rgba_image, new_size, interpolation=cv2.INTER_AREA) - result_front_image = np.zeros_like(rgba_image) - front_mask = cv2.resize(front_mask, new_size, interpolation=cv2.INTER_AREA) - result_front_image[front_mask != 0] = rgba_image[front_mask != 0] - result_front_image_pil = Image.fromarray(cv2.cvtColor(result_front_image, cv2.COLOR_BGR2RGBA)) - if 'transparent' in result.keys(): - # 用户自选区域transparent - transparent = result['transparent'] - if transparent['mask_url'] is not None and transparent['mask_url'] != "": - # 预处理用户自选区mask - seg_mask = oss_get_image(oss_client=self.minio_client, bucket=transparent['mask_url'].split('/')[0], object_name=transparent['mask_url'][transparent['mask_url'].find('/') + 1:], data_type="cv2") - seg_mask = cv2.resize(seg_mask, new_size, interpolation=cv2.INTER_AREA) - # 转换颜色空间为 RGB(OpenCV 默认是 BGR) - image_rgb = cv2.cvtColor(seg_mask, cv2.COLOR_BGR2RGB) - - r, g, b = cv2.split(image_rgb) - blue_mask = b > r - - # 创建红色和绿色掩码 - transparent_mask = np.array(blue_mask, dtype=np.uint8) * 255 - result_front_image_pil = sketch_to_transparent(result_front_image_pil, transparent_mask, transparent["scale"]) + if result.get('design_type', None) == 'merge': + # merge 不需要返回mask (红绿图) + if result['resize_scale'][0] == 1.0 and result['resize_scale'][1] == 1.0: + front_mask = result['front_mask'] + back_mask = result['back_mask'] else: - result_front_image_pil = sketch_to_transparent(result_front_image_pil, front_mask, transparent["scale"]) - result['front_image'], result["front_image_url"], _ = upload_png_mask(self.minio_client, result_front_image_pil, f'{generate_uuid()}', mask=None) + height, width = result['front_mask'].shape[:2] + new_width = int(width * result['resize_scale'][0]) + new_height = int(height * result['resize_scale'][1]) - # 前片部分 (红图部分) - # height, width = front_mask.shape - # mask_image = np.zeros((height, width, 3)) - # mask_image[front_mask != 0] = [0, 0, 255] + front_mask = cv2.resize(result['front_mask'], (new_width, new_height), interpolation=cv2.INTER_AREA) + back_mask = cv2.resize(result['back_mask'], (new_width, new_height), interpolation=cv2.INTER_AREA) + result['merge_image'] = cv2.resize(result['merge_image'], (new_width, new_height), interpolation=cv2.INTER_AREA) - # 切换为原始图片尺寸------------------------------- - height, width = ori_front_mask.shape - mask_image = np.zeros((height, width, 3)) - mask_image[ori_front_mask != 0] = [0, 0, 255] - # ----------------------------------------------- + rgba_image = rgb_to_rgba(result['merge_image'], front_mask + back_mask) + new_size = (int(rgba_image.shape[1] * result["scale"]), int(rgba_image.shape[0] * result["scale"])) + rgba_image = cv2.resize(rgba_image, new_size, interpolation=cv2.INTER_AREA) + result_front_image = np.zeros_like(rgba_image) + front_mask = cv2.resize(front_mask, new_size, interpolation=cv2.INTER_AREA) + result_front_image[front_mask != 0] = rgba_image[front_mask != 0] + result_front_image_pil = Image.fromarray(cv2.cvtColor(result_front_image, cv2.COLOR_BGR2RGBA)) + result['front_image'], result["front_image_url"], _ = upload_png_mask(self.minio_client, result_front_image_pil, f'{generate_uuid()}', mask=None) - # if result["name"] in ('blouse', 'dress', 'outwear', 'tops'): - # result_back_image = np.zeros_like(rgba_image) - # back_mask = cv2.resize(back_mask, new_size, interpolation=cv2.INTER_AREA) - # result_back_image[back_mask != 0] = rgba_image[back_mask != 0] - # result_back_image_pil = Image.fromarray(cvtColor(result_back_image, COLOR_BGR2RGBA)) - # result['back_image'], result["back_image_url"], _ = upload_png_mask(self.minio_client, result_back_image_pil, f'{generate_uuid()}', mask=None) - # mask_image[back_mask != 0] = [0, 255, 0] - # - # rbga_mask = rgb_to_rgba(mask_image, front_mask + back_mask) - # mask_pil = Image.fromarray(cvtColor(rbga_mask.astype(np.uint8), COLOR_BGR2RGBA)) - # image_data = io.BytesIO() - # mask_pil.save(image_data, format='PNG') - # image_data.seek(0) - # image_bytes = image_data.read() - # req = oss_upload_image(oss_client=self.minio_client, bucket=AIDA_CLOTHING, object_name=f"mask/mask_{generate_uuid()}.png", image_bytes=image_bytes) - # result['mask_url'] = req.bucket_name + "/" + req.object_name - # else: - # rbga_mask = rgb_to_rgba(mask_image, front_mask) - # mask_pil = Image.fromarray(cvtColor(rbga_mask.astype(np.uint8), COLOR_BGR2RGBA)) - # image_data = io.BytesIO() - # mask_pil.save(image_data, format='PNG') - # image_data.seek(0) - # image_bytes = image_data.read() - # req = oss_upload_image(oss_client=self.minio_client, bucket=AIDA_CLOTHING, object_name=f"mask/mask_{generate_uuid()}.png", image_bytes=image_bytes) - # result['mask_url'] = req.bucket_name + "/" + req.object_name - # result['back_image'] = None - # result["back_image_url"] = None - # # result["back_mask_url"] = None - # # result['back_mask_image'] = None + result_back_image = np.zeros_like(rgba_image) + back_mask = cv2.resize(back_mask, new_size, interpolation=cv2.INTER_AREA) + result_back_image[back_mask != 0] = rgba_image[back_mask != 0] + result_back_image_pil = Image.fromarray(cv2.cvtColor(result_back_image, cv2.COLOR_BGR2RGBA)) + result['back_image'], result["back_image_url"], _ = upload_png_mask(self.minio_client, result_back_image_pil, f'{generate_uuid()}', mask=None) + return result + else: + ori_front_mask = result['front_mask'].copy() + ori_back_mask = result['back_mask'].copy() - result_back_image = np.zeros_like(rgba_image) - back_mask = cv2.resize(back_mask, new_size, interpolation=cv2.INTER_AREA) - result_back_image[back_mask != 0] = rgba_image[back_mask != 0] - result_back_image_pil = Image.fromarray(cv2.cvtColor(result_back_image, cv2.COLOR_BGR2RGBA)) - result['back_image'], result["back_image_url"], _ = upload_png_mask(self.minio_client, result_back_image_pil, f'{generate_uuid()}', mask=None) + if result['resize_scale'][0] == 1.0 and result['resize_scale'][1] == 1.0: + front_mask = result['front_mask'] + back_mask = result['back_mask'] + else: + height, width = result['front_mask'].shape[:2] + new_width = int(width * result['resize_scale'][0]) + new_height = int(height * result['resize_scale'][1]) - # mask_image[back_mask != 0] = [0, 255, 0] - mask_image[ori_back_mask != 0] = [0, 255, 0] + front_mask = cv2.resize(result['front_mask'], (new_width, new_height), interpolation=cv2.INTER_AREA) + back_mask = cv2.resize(result['back_mask'], (new_width, new_height), interpolation=cv2.INTER_AREA) - rbga_mask = rgb_to_rgba(mask_image, ori_front_mask + ori_back_mask) - mask_pil = Image.fromarray(cv2.cvtColor(rbga_mask.astype(np.uint8), cv2.COLOR_BGR2RGBA)) - image_data = io.BytesIO() - mask_pil.save(image_data, format='PNG') - image_data.seek(0) - image_bytes = image_data.read() - req = oss_upload_image(oss_client=self.minio_client, bucket="aida-clothing", object_name=f"mask/mask_{generate_uuid()}.png", image_bytes=image_bytes) - result['mask_url'] = req.bucket_name + "/" + req.object_name + rgba_image = rgb_to_rgba(result['final_image'], front_mask + back_mask) + new_size = (int(rgba_image.shape[1] * result["scale"]), int(rgba_image.shape[0] * result["scale"])) + rgba_image = cv2.resize(rgba_image, new_size, interpolation=cv2.INTER_AREA) + result_front_image = np.zeros_like(rgba_image) + front_mask = cv2.resize(front_mask, new_size, interpolation=cv2.INTER_AREA) + result_front_image[front_mask != 0] = rgba_image[front_mask != 0] + result_front_image_pil = Image.fromarray(cv2.cvtColor(result_front_image, cv2.COLOR_BGR2RGBA)) + if 'transparent' in result.keys(): + # 用户自选区域transparent + transparent = result['transparent'] + if transparent['mask_url'] is not None and transparent['mask_url'] != "": + # 预处理用户自选区mask + seg_mask = oss_get_image(oss_client=self.minio_client, bucket=transparent['mask_url'].split('/')[0], object_name=transparent['mask_url'][transparent['mask_url'].find('/') + 1:], data_type="cv2") + seg_mask = cv2.resize(seg_mask, new_size, interpolation=cv2.INTER_AREA) + # 转换颜色空间为 RGB(OpenCV 默认是 BGR) + image_rgb = cv2.cvtColor(seg_mask, cv2.COLOR_BGR2RGB) + + r, g, b = cv2.split(image_rgb) + blue_mask = b > r + + # 创建红色和绿色掩码 + transparent_mask = np.array(blue_mask, dtype=np.uint8) * 255 + result_front_image_pil = sketch_to_transparent(result_front_image_pil, transparent_mask, transparent["scale"]) + else: + result_front_image_pil = sketch_to_transparent(result_front_image_pil, front_mask, transparent["scale"]) + result['front_image'], result["front_image_url"], _ = upload_png_mask(self.minio_client, result_front_image_pil, f'{generate_uuid()}', mask=None) + + height, width = ori_front_mask.shape + mask_image = np.zeros((height, width, 3)) + mask_image[ori_front_mask != 0] = [0, 0, 255] + + result_back_image = np.zeros_like(rgba_image) + back_mask = cv2.resize(back_mask, new_size, interpolation=cv2.INTER_AREA) + result_back_image[back_mask != 0] = rgba_image[back_mask != 0] + result_back_image_pil = Image.fromarray(cv2.cvtColor(result_back_image, cv2.COLOR_BGR2RGBA)) + result['back_image'], result["back_image_url"], _ = upload_png_mask(self.minio_client, result_back_image_pil, f'{generate_uuid()}', mask=None) + + # mask_image[back_mask != 0] = [0, 255, 0] + mask_image[ori_back_mask != 0] = [0, 255, 0] + + rbga_mask = rgb_to_rgba(mask_image, ori_front_mask + ori_back_mask) + mask_pil = Image.fromarray(cv2.cvtColor(rbga_mask.astype(np.uint8), cv2.COLOR_BGR2RGBA)) + image_data = io.BytesIO() + mask_pil.save(image_data, format='PNG') + image_data.seek(0) + image_bytes = image_data.read() + req = oss_upload_image(oss_client=self.minio_client, bucket="aida-clothing", object_name=f"mask/mask_{generate_uuid()}.png", image_bytes=image_bytes) + result['mask_url'] = req.bucket_name + "/" + req.object_name + + # 创建中间图层(未分割图层) 1.color + overall_print 2.color + overall_print + print + result_pattern_overall_image_pil = Image.fromarray(cv2.cvtColor(rgb_to_rgba(result['no_seg_sketch_overall'], ori_front_mask + ori_back_mask), cv2.COLOR_BGR2RGBA)) + result['pattern_overall_image'], result['pattern_overall_image_url'], _ = upload_png_mask(self.minio_client, result_pattern_overall_image_pil, f'{generate_uuid()}') + + result_pattern_print_image_pil = Image.fromarray(cv2.cvtColor(rgb_to_rgba(result['no_seg_sketch_print'], ori_front_mask + ori_back_mask), cv2.COLOR_BGR2RGBA)) + result['pattern_print_image'], result['pattern_print_image_url'], _ = upload_png_mask(self.minio_client, result_pattern_print_image_pil, f'{generate_uuid()}') + return result else: ori_front_mask, ori_back_mask = None, None # 创建中间图层(未分割图层) 1.color + overall_print 2.color + overall_print + print @@ -127,5 +129,6 @@ class Split(object): result_pattern_print_image_pil = Image.fromarray(cv2.cvtColor(rgb_to_rgba(result['no_seg_sketch_print'], ori_front_mask + ori_back_mask), cv2.COLOR_BGR2RGBA)) result['pattern_print_image'], result['pattern_print_image_url'], _ = upload_png_mask(self.minio_client, result_pattern_print_image_pil, f'{generate_uuid()}') return result + except Exception as e: logging.warning(f"split runtime exception : {e} image_id : {result['image_id']}") diff --git a/app/service/design_fast/utils/organize.py b/app/service/design_fast/utils/organize.py index 0737193..6cf06ec 100644 --- a/app/service/design_fast/utils/organize.py +++ b/app/service/design_fast/utils/organize.py @@ -23,19 +23,20 @@ def organize_clothing(layer): front_layer = dict(priority=layer['priority'] if layer.get("layer_order", False) else PRIORITY_DICT.get(f'{layer["name"].lower()}_front', None), name=f'{layer["name"].lower()}_front', image=layer["front_image"], + merge_image=layer["front_image"], # mask_image=layer['front_mask_image'], image_url=layer['front_image_url'], - mask_url=layer['mask_url'], + mask_url=layer.get("mask_url", None), sacle=layer['scale'], clothes_keypoint=layer['clothes_keypoint'], position=start_point, resize_scale=layer["resize_scale"], mask=cv2.resize(layer['mask'], layer["front_image"].size), gradient_string=layer['gradient_string'] if 'gradient_string' in layer.keys() else "", - pattern_overall_image_url=layer['pattern_overall_image_url'], - pattern_print_image_url=layer['pattern_print_image_url'], + pattern_overall_image_url=layer.get('pattern_overall_image_url', None), + pattern_print_image_url=layer.get('pattern_print_image_url', None), - pattern_image=layer['pattern_image'], + pattern_image=layer.get('pattern_image', None), # back_perspective_url=layer['back_perspective_url'] if 'back_perspective_url' in layer.keys() else "" transpose=layer.get("transpose", [1, 1]), # 默认为1, 1代表不镜像 rotate=layer.get('rotate', 0), @@ -46,17 +47,17 @@ def organize_clothing(layer): image=layer["back_image"], # mask_image=layer['back_mask_image'], image_url=layer['back_image_url'], - mask_url=layer['mask_url'], + mask_url=layer.get('mask_url', None), sacle=layer['scale'], clothes_keypoint=layer['clothes_keypoint'], position=start_point, resize_scale=layer["resize_scale"], mask=cv2.resize(layer['mask'], layer["front_image"].size), gradient_string=layer['gradient_string'] if 'gradient_string' in layer.keys() else "", - pattern_overall_image_url=layer['pattern_overall_image_url'], - pattern_print_image_url=layer['pattern_print_image_url'], + pattern_overall_image_url=layer.get('pattern_overall_image_url', None), + pattern_print_image_url=layer.get('pattern_print_image_url', None), # back_perspective_url=layer['back_perspective_url'] if 'back_perspective_url' in layer.keys() else "" - transpose=layer.get("transpose", [1, 1]), # 默认为1, 1代表不镜像 + transpose=layer.get("transpose", [1, 1]), # 默认为1, 1代表不镜像 rotate=layer.get('rotate', 0), ) return front_layer, back_layer @@ -80,16 +81,16 @@ def organize_others(layer): image=layer["front_image"], # mask_image=layer['front_mask_image'], image_url=layer['front_image_url'], - mask_url=layer['mask_url'], + mask_url=layer.get('mask_url', None), sacle=layer['scale'], clothes_keypoint=(0, 0), position=start_point, resize_scale=layer["resize_scale"], mask=cv2.resize(layer['mask'], layer["front_image"].size), gradient_string=layer['gradient_string'] if 'gradient_string' in layer.keys() else "", - pattern_overall_image_url=layer['pattern_overall_image_url'], - pattern_print_image_url=layer['pattern_print_image_url'], - pattern_image=layer['pattern_image'], + pattern_overall_image_url=layer.get('pattern_overall_image_url', None), + pattern_print_image_url=layer.get('pattern_print_image_url', None), + pattern_image=layer.get('pattern_image', None), # back_perspective_url=layer['back_perspective_url'] if 'back_perspective_url' in layer.keys() else "" ) # 后片数据 @@ -98,15 +99,15 @@ def organize_others(layer): image=layer["back_image"], # mask_image=layer['back_mask_image'], image_url=layer['back_image_url'], - mask_url=layer['mask_url'], + mask_url=layer.get('mask_url', None), sacle=layer['scale'], clothes_keypoint=(0, 0), position=start_point, resize_scale=layer["resize_scale"], mask=cv2.resize(layer['mask'], layer["front_image"].size), gradient_string=layer['gradient_string'] if 'gradient_string' in layer.keys() else "", - pattern_overall_image_url=layer['pattern_overall_image_url'], - pattern_print_image_url=layer['pattern_print_image_url'], + pattern_overall_image_url=layer.get('pattern_overall_image_url', None), + pattern_print_image_url=layer.get('pattern_print_image_url', None), # back_perspective_url=layer['back_perspective_url'] if 'back_perspective_url' in layer.keys() else "" ) return front_layer, back_layer diff --git a/app/service/design_fast/utils/synthesis_item.py b/app/service/design_fast/utils/synthesis_item.py index e57dbe4..ff44157 100644 --- a/app/service/design_fast/utils/synthesis_item.py +++ b/app/service/design_fast/utils/synthesis_item.py @@ -187,6 +187,111 @@ def synthesis(data, size, basic_info): logging.warning(f"synthesis runtime exception : {e}") +def merge(data, size, basic_info): + # out_of_bounds_control: 是否允许服装越界 True 允许 False 不允许 默认情况允许 + out_of_bounds_control = basic_info.get('out_of_bounds_control', True) + # 创建底图 + base_image = Image.new('RGBA', size, (0, 0, 0, 0)) + try: + all_mask_shape = (size[1], size[0]) + body_mask = None + for d in data: + if d['name'] == 'body' or d['name'] == 'mannequin': + # 创建一个新的宽高透明图像, 把模特贴上去获取mask + transparent_image = Image.new("RGBA", size, (0, 0, 0, 0)) + transparent_image.paste(d['image'], (d['adaptive_position'][1], d['adaptive_position'][0]), d['image']) # 此处可变数组会被paste篡改值,所以使用下标获取position + body_mask = np.array(transparent_image.split()[3]) + + # 根据新的坐标获取新的肩点 + left_shoulder = [x + y for x, y in zip(basic_info['body_point_test']['shoulder_left'], [d['adaptive_position'][1], d['adaptive_position'][0]])] + right_shoulder = [x + y for x, y in zip(basic_info['body_point_test']['shoulder_right'], [d['adaptive_position'][1], d['adaptive_position'][0]])] + body_mask[:min(left_shoulder[1], right_shoulder[1]), left_shoulder[0]:right_shoulder[0]] = 255 + _, binary_body_mask = cv2.threshold(body_mask, 127, 255, cv2.THRESH_BINARY) + top_outer_mask = np.array(binary_body_mask) + bottom_outer_mask = np.array(binary_body_mask) + others_outer_mask = np.array(binary_body_mask) + + top = True + bottom = True + others = True + i = len(data) + while i: + i -= 1 + if top and data[i]['name'] in ["blouse_front", "outwear_front", "dress_front", "tops_front"]: + if out_of_bounds_control: + top = True + else: + top = False + mask_shape = data[i]['mask'].shape + y_offset, x_offset = data[i]['adaptive_position'] + # 初始化叠加区域的起始和结束位置 + all_y_start, all_y_end, mask_y_start, mask_y_end = positioning(all_mask_shape=all_mask_shape[0], mask_shape=mask_shape[0], offset=y_offset) + all_x_start, all_x_end, mask_x_start, mask_x_end = positioning(all_mask_shape=all_mask_shape[1], mask_shape=mask_shape[1], offset=x_offset) + # 将叠加区域赋值为相应的像素值 + _, sketch_mask = cv2.threshold(data[i]['mask'], 127, 255, cv2.THRESH_BINARY) + background = np.zeros_like(top_outer_mask) + background[all_y_start:all_y_end, all_x_start:all_x_end] = sketch_mask[mask_y_start:mask_y_end, mask_x_start:mask_x_end] + top_outer_mask = background + top_outer_mask + elif bottom and data[i]['name'] in ["trousers_front", "skirt_front", "bottoms_front", "dress_front"]: + # bottom = False + mask_shape = data[i]['mask'].shape + y_offset, x_offset = data[i]['adaptive_position'] + # 初始化叠加区域的起始和结束位置 + all_y_start, all_y_end, mask_y_start, mask_y_end = positioning(all_mask_shape=all_mask_shape[0], mask_shape=mask_shape[0], offset=y_offset) + all_x_start, all_x_end, mask_x_start, mask_x_end = positioning(all_mask_shape=all_mask_shape[1], mask_shape=mask_shape[1], offset=x_offset) + # 将叠加区域赋值为相应的像素值 + _, sketch_mask = cv2.threshold(data[i]['mask'], 127, 255, cv2.THRESH_BINARY) + background = np.zeros_like(top_outer_mask) + background[all_y_start:all_y_end, all_x_start:all_x_end] = sketch_mask[mask_y_start:mask_y_end, mask_x_start:mask_x_end] + bottom_outer_mask = background + bottom_outer_mask + elif others and data[i]['name'] in ['others_front']: + mask_shape = data[i]['mask'].shape + y_offset, x_offset = data[i]['adaptive_position'] + # 初始化叠加区域的起始和结束位置 + all_y_start, all_y_end, mask_y_start, mask_y_end = positioning(all_mask_shape=all_mask_shape[0], mask_shape=mask_shape[0], offset=y_offset) + all_x_start, all_x_end, mask_x_start, mask_x_end = positioning(all_mask_shape=all_mask_shape[1], mask_shape=mask_shape[1], offset=x_offset) + # 将叠加区域赋值为相应的像素值 + _, sketch_mask = cv2.threshold(data[i]['mask'], 127, 255, cv2.THRESH_BINARY) + background = np.zeros_like(top_outer_mask) + background[all_y_start:all_y_end, all_x_start:all_x_end] = sketch_mask[mask_y_start:mask_y_end, mask_x_start:mask_x_end] + others_outer_mask = background + others_outer_mask + pass + elif bottom is False and top is False: + break + + all_mask = cv2.bitwise_or(top_outer_mask, bottom_outer_mask) + all_mask = cv2.bitwise_or(all_mask, others_outer_mask) + + for layer in data: + if layer['image'] is not None: + if layer['name'] != "body": + test_image = Image.new('RGBA', size, (0, 0, 0, 0)) + paste_img, position = transpose_rotate(layer, layer['image']) + test_image.paste(paste_img, position, paste_img) + mask_data = np.where(all_mask > 0, 255, 0).astype(np.uint8) + mask_alpha = Image.fromarray(mask_data) + mask_alpha.paste(paste_img.getchannel('A'), position, paste_img.getchannel('A')) + cropped_image = Image.composite(test_image, Image.new("RGBA", test_image.size, (255, 255, 255, 0)), mask_alpha) + base_image.paste(test_image, (0, 0), cropped_image) # test_image 已经按照坐标贴到最大宽值的图片上 坐着这里坐标为00 + else: + base_image.paste(layer['merge_image'], (layer['adaptive_position'][1], layer['adaptive_position'][0]), layer['merge_image']) + + result_image = base_image + + image_data = io.BytesIO() + result_image.save(image_data, format='PNG') + image_data.seek(0) + + # oss upload + image_bytes = image_data.read() + bucket_name = "aida-results" + object_name = f'result_{generate_uuid()}.png' + oss_upload_image(oss_client=minio_client, bucket=bucket_name, object_name=object_name, image_bytes=image_bytes) + return f"{bucket_name}/{object_name}" + except Exception as e: + logging.warning(f"synthesis runtime exception : {e}") + + def synthesis_single(front_image, back_image): result_image = None if front_image: diff --git a/app/service/utils/new_oss_client.py b/app/service/utils/new_oss_client.py index d6f745f..ac91f47 100644 --- a/app/service/utils/new_oss_client.py +++ b/app/service/utils/new_oss_client.py @@ -81,7 +81,7 @@ if __name__ == '__main__': # url = "aida-users/89/sketchboard/female/Dress/e6724ab7-8d3f-4677-abe0-c3e42ab7af85.jpeg" # url = "aida-users/87/print/956614a2-7e75-4fbe-9ed0-c1831e37a2c9-4-87.png" # url = "aida-users/89/single_logo/123-89.png" - url = "lanecarford/lc_stylist_agent_outfit_items/141/ee25ec85-d504-4b42-9a18-db6682fe9e3b-6.jpg" + url = "aida-results/result_a7adcbd8-ef8d-11f0-8c92-0966ede33ab5.png" # url = "aida-collection-element/12148/Sketchboard/95ea577b-305b-4a62-b30a-39c0dd3ddb3f.png" read_type = "2"