From 04b15aa200d017d8dabf14bffaf7db52d5002bd7 Mon Sep 17 00:00:00 2001 From: alab Date: Thu, 26 Sep 2024 06:09:05 +0000 Subject: [PATCH] =?UTF-8?q?design=20=20batch=20=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/api_design.py | 13 +- app/schemas/design.py | 2 +- .../design/design_batch/items/__init__.py | 0 .../design_batch/items/utils/__init__.py | 0 app/service/design/utils/__init__.py | 0 .../design_batch/design_batch_celery.py | 126 ++++++++++++++ app/service/design_batch/item.py | 61 +++++++ .../pipeline/__init__.py | 0 .../items => design_batch}/pipeline/color.py | 10 +- .../pipeline/contour_detection.py | 0 .../pipeline/keypoint.py | 2 +- .../pipeline/loading.py | 24 ++- .../pipeline/print_painting.py | 15 +- .../items => design_batch}/pipeline/scale.py | 0 .../pipeline/segmentation.py | 13 +- .../items => design_batch}/pipeline/split.py | 19 +- app/service/design_batch/service.py | 12 ++ app/service/design_batch/test.py | 162 ++++++++++++++++++ app/service/design_batch/utils/MQ.py | 17 ++ .../core => design_batch/utils}/__init__.py | 0 .../utils/conversion_image.py | 0 .../utils/design_ensemble.py | 0 app/service/design_batch/utils/organize.py | 77 +++++++++ app/service/design_batch/utils/progress.py | 30 ++++ .../utils/redis_utils.py | 0 app/service/design_batch/utils/save_json.py | 13 ++ .../utils/synthesis_item.py | 16 ++ .../utils/upload_image.py | 8 +- app/service/design_fast/pipeline/keypoint.py | 2 +- .../design_fast/pipeline/segmentation.py | 2 +- app/service/design_fast/pipeline/split.py | 2 +- app/service/design_pre_processing/service.py | 2 +- app/service/utils/oss_client.py | 18 +- 33 files changed, 585 insertions(+), 61 deletions(-) delete mode 100644 app/service/design/design_batch/items/__init__.py delete mode 100644 app/service/design/design_batch/items/utils/__init__.py delete mode 100644 app/service/design/utils/__init__.py create mode 100644 app/service/design_batch/design_batch_celery.py create mode 100644 app/service/design_batch/item.py rename app/service/{design/design_batch/items => design_batch}/pipeline/__init__.py (100%) rename app/service/{design/design_batch/items => design_batch}/pipeline/color.py (88%) rename app/service/{design/design_batch/items => design_batch}/pipeline/contour_detection.py (100%) rename app/service/{design/design_batch/items => design_batch}/pipeline/keypoint.py (98%) rename app/service/{design/design_batch/items => design_batch}/pipeline/loading.py (74%) rename app/service/{design/design_batch/items => design_batch}/pipeline/print_painting.py (98%) rename app/service/{design/design_batch/items => design_batch}/pipeline/scale.py (100%) rename app/service/{design/design_batch/items => design_batch}/pipeline/segmentation.py (82%) rename app/service/{design/design_batch/items => design_batch}/pipeline/split.py (79%) create mode 100644 app/service/design_batch/service.py create mode 100644 app/service/design_batch/test.py create mode 100644 app/service/design_batch/utils/MQ.py rename app/service/{design/core => design_batch/utils}/__init__.py (100%) rename app/service/{design/design_batch/items => design_batch}/utils/conversion_image.py (100%) rename app/service/{design/design_batch/items => design_batch}/utils/design_ensemble.py (100%) create mode 100644 app/service/design_batch/utils/organize.py create mode 100644 app/service/design_batch/utils/progress.py rename app/service/{design/design_batch/items => design_batch}/utils/redis_utils.py (100%) create mode 100644 app/service/design_batch/utils/save_json.py rename app/service/{design/design_batch/items => design_batch}/utils/synthesis_item.py (94%) rename app/service/{design => design_batch}/utils/upload_image.py (69%) diff --git a/app/api/api_design.py b/app/api/api_design.py index 2720e36..389e43e 100644 --- a/app/api/api_design.py +++ b/app/api/api_design.py @@ -6,8 +6,9 @@ from fastapi import APIRouter, HTTPException, UploadFile, File, Form from app.schemas.design import DesignModel, DesignProgressModel, ModelProgressModel, DBGConfigModel from app.schemas.response_template import ResponseModel -from app.service.design.model_process_service import model_transpose -from app.service.design.service_design_batch_generate import start_design_batch_generate +from app.service.design_batch.service import start_design_batch_generate +# from app.service.design.model_process_service import model_transpose +# from app.service.design.service_design_batch_generate import start_design_batch_generate from app.service.design_fast.design_generate import design_generate from app.service.design_fast.utils.redis_utils import Redis @@ -236,7 +237,7 @@ def model_process(request_data: ModelProgressModel): try: logger.info(f"model_process request item is : @@@@@@:{json.dumps(request_data.dict())}") - data = model_transpose(image_path=request_data.model_path) + # data = model_transpose(image_path=request_data.model_path) logger.info(f"model_process response @@@@@@:{json.dumps(data)}") except Exception as e: logger.warning(f"model_process Run Exception @@@@@@:{e}") @@ -251,20 +252,18 @@ def model_process(request_data: ModelProgressModel): async def design(file: UploadFile = File(...), tasks_id: str = Form(...), user_id: str = Form(...), - priority: int = Form(...), + file_name: str = Form(...), total: int = Form(...) ): - # file_content = await file.read() dbg_config = DBGConfigModel( tasks_id=tasks_id, user_id=user_id, - priority=priority, + file_name=file_name, total=total ) contents = await file.read() file_name = file.filename await save_request_file(contents, file_name) - return await start_design_batch_generate(dbg_config, contents) diff --git a/app/schemas/design.py b/app/schemas/design.py index 763e0a0..7ebd8e6 100644 --- a/app/schemas/design.py +++ b/app/schemas/design.py @@ -17,5 +17,5 @@ class ModelProgressModel(BaseModel): class DBGConfigModel(BaseModel): tasks_id: str user_id: str - priority: int + file_name: str total: int diff --git a/app/service/design/design_batch/items/__init__.py b/app/service/design/design_batch/items/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/service/design/design_batch/items/utils/__init__.py b/app/service/design/design_batch/items/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/service/design/utils/__init__.py b/app/service/design/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/service/design_batch/design_batch_celery.py b/app/service/design_batch/design_batch_celery.py new file mode 100644 index 0000000..3f12862 --- /dev/null +++ b/app/service/design_batch/design_batch_celery.py @@ -0,0 +1,126 @@ +import logging +import threading + +from celery import Celery +from minio import Minio + +from app.core.config import * +from app.service.design_batch.item import BodyItem, TopItem, BottomItem +from app.service.design_batch.utils.MQ import publish_status +from app.service.design_batch.utils.organize import organize_body, organize_clothing +from app.service.design_batch.utils.save_json import oss_upload_json +from app.service.design_batch.utils.synthesis_item import update_base_size_priority, synthesis, synthesis_single + +id_lock = threading.Lock() +celery_app = Celery('tasks', broker='amqp://guest:guest@10.1.2.213:5672//', backend='rpc://') +celery_app.conf.worker_log_format = '%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s' +celery_app.conf.worker_hijack_root_logger = False +logging.getLogger('pika').setLevel(logging.WARNING) +logger = logging.getLogger() +minio_client = Minio(MINIO_URL, access_key=MINIO_ACCESS, secret_key=MINIO_SECRET, secure=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() + else: + bottom_server = BottomItem(data=item, basic=basic, minio_client=minio_client) + item_data = bottom_server.process() + return item_data + + +def process_layer(item, layers): + # item处理结束后 对图层数据组装 + if item['name'] == "mannequin": + body_layer = organize_body(item) + layers.append(body_layer) + return item['body_image'].size + else: + front_layer, back_layer = organize_clothing(item) + layers.append(front_layer) + layers.append(back_layer) + + +@celery_app.task +def batch_design(objects_data, tasks_id, json_name): + object_response = [] + threads = [] + active_threads = 0 + lock = threading.Lock() + + def process_object(step, object): + nonlocal active_threads + basic = object['basic'] + items_response = {'layers': []} + if basic['single_overall'] == "overall": + item_results = [] + for item in object['items']: + item_results.append(process_item(item, basic)) + layers = [] + body_size = None + for item in item_results: + body_size = process_layer(item, layers) + layers = sorted(layers, key=lambda s: s.get("priority", float('inf'))) + + layers, new_size = update_base_size_priority(layers, body_size) + + for lay in layers: + items_response['layers'].append({ + 'image_category': lay['name'], + 'position': lay['position'], + 'priority': lay.get("priority", None), + 'resize_scale': lay['resize_scale'] if "resize_scale" in lay.keys() else None, + 'image_size': lay['image'] if lay['image'] is None else lay['image'].size, + 'gradient_string': lay['gradient_string'] if 'gradient_string' in lay.keys() else "", + 'mask_url': lay['mask_url'], + 'image_url': lay['image_url'] if 'image_url' in lay.keys() else None, + 'pattern_image_url': lay['pattern_image_url'] if 'pattern_image_url' in lay.keys() else None, + }) + items_response['synthesis_url'] = synthesis(layers, new_size, basic) + else: + item_result = process_item(object['items'][0], basic) + items_response['layers'].append({ + 'image_category': f"{item_result['name']}_front", + 'image_size': item_result['back_image'].size if item_result['back_image'] else None, + 'position': None, + 'priority': 0, + 'image_url': item_result['front_image_url'], + 'mask_url': item_result['mask_url'], + "gradient_string": item_result['gradient_string'] if 'gradient_string' in item_result.keys() else "", + 'pattern_image_url': item_result['pattern_image_url'] if 'pattern_image_url' in item_result.keys() else None, + }) + items_response['layers'].append({ + 'image_category': f"{item_result['name']}_back", + 'image_size': item_result['front_image'].size if item_result['front_image'] else None, + 'position': None, + 'priority': 0, + 'image_url': item_result['back_image_url'], + 'mask_url': item_result['mask_url'], + "gradient_string": item_result['gradient_string'] if 'gradient_string' in item_result.keys() else "", + 'pattern_image_url': item_result['pattern_image_url'] if 'pattern_image_url' in item_result.keys() else None, + }) + items_response['synthesis_url'] = synthesis_single(item_result['front_image'], item_result['back_image']) + + with lock: + object_response.append(items_response) + publish_status(tasks_id, step + 1, items_response) + active_threads -= 1 + + for step, object in enumerate(objects_data): + t = threading.Thread(target=process_object, args=(step, object)) + threads.append(t) + t.start() + with lock: + active_threads += 1 + + for t in threads: + t.join() + + oss_upload_json(minio_client, object_response, json_name) + publish_status(tasks_id, "ok", json_name) + return object_response diff --git a/app/service/design_batch/item.py b/app/service/design_batch/item.py new file mode 100644 index 0000000..cad1488 --- /dev/null +++ b/app/service/design_batch/item.py @@ -0,0 +1,61 @@ +from app.service.design_batch.pipeline import * + + +class BaseItem: + def __init__(self, data, basic): + self.result = data.copy() + self.result['name'] = data['type'].lower() + self.result.pop("type") + self.result.update(basic) + + +class TopItem(BaseItem): + def __init__(self, data, basic, minio_client): + super().__init__(data, basic) + self.top_pipeline = [ + LoadImage(minio_client), + KeyPoint(), + Segmentation(minio_client), + Color(minio_client), + PrintPainting(minio_client), + Scaling(), + Split(minio_client) + ] + + def process(self): + for item in self.top_pipeline: + self.result = item(self.result) + return self.result + + +class BottomItem(BaseItem): + def __init__(self, data, basic, minio_client): + super().__init__(data, basic) + self.bottom_pipeline = [ + LoadImage(minio_client), + KeyPoint(), + ContourDetection(), + # Segmentation(), + Color(minio_client), + PrintPainting(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) + self.top_pipeline = [ + LoadBodyImage(minio_client), + ] + + def process(self): + for item in self.top_pipeline: + self.result = item(self.result) + return self.result diff --git a/app/service/design/design_batch/items/pipeline/__init__.py b/app/service/design_batch/pipeline/__init__.py similarity index 100% rename from app/service/design/design_batch/items/pipeline/__init__.py rename to app/service/design_batch/pipeline/__init__.py diff --git a/app/service/design/design_batch/items/pipeline/color.py b/app/service/design_batch/pipeline/color.py similarity index 88% rename from app/service/design/design_batch/items/pipeline/color.py rename to app/service/design_batch/pipeline/color.py index bc3676f..546c671 100644 --- a/app/service/design/design_batch/items/pipeline/color.py +++ b/app/service/design_batch/pipeline/color.py @@ -3,12 +3,15 @@ import logging import cv2 import numpy as np -from app.service.utils.oss_client import oss_get_image +from app.service.utils.new_oss_client import oss_get_image logger = logging.getLogger() class Color: + def __init__(self, minio_client): + self.minio_client = minio_client + def __call__(self, result): dim_image_h, dim_image_w = result['image'].shape[0:2] if "gradient" in result.keys() and result['gradient'] != "": @@ -33,10 +36,9 @@ class Color: result['alpha'] = 100 / 255.0 return result - @staticmethod - def get_gradient(bucket_name, object_name): + def get_gradient(self, bucket_name, object_name): # 获取渐变色图案 - image = oss_get_image(bucket=bucket_name, object_name=object_name, data_type="cv2") + image = oss_get_image(oss_client=self.minio_client, bucket=bucket_name, object_name=object_name, data_type="cv2") if image.shape[2] == 4: image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR) return image diff --git a/app/service/design/design_batch/items/pipeline/contour_detection.py b/app/service/design_batch/pipeline/contour_detection.py similarity index 100% rename from app/service/design/design_batch/items/pipeline/contour_detection.py rename to app/service/design_batch/pipeline/contour_detection.py diff --git a/app/service/design/design_batch/items/pipeline/keypoint.py b/app/service/design_batch/pipeline/keypoint.py similarity index 98% rename from app/service/design/design_batch/items/pipeline/keypoint.py rename to app/service/design_batch/pipeline/keypoint.py index 243cf4e..313a613 100644 --- a/app/service/design/design_batch/items/pipeline/keypoint.py +++ b/app/service/design_batch/pipeline/keypoint.py @@ -4,7 +4,7 @@ import numpy as np from pymilvus import MilvusClient from app.core.config import * -from app.service.design.utils.design_ensemble import get_keypoint_result +from app.service.design_batch.utils.design_ensemble import get_keypoint_result logger = logging.getLogger(__name__) diff --git a/app/service/design/design_batch/items/pipeline/loading.py b/app/service/design_batch/pipeline/loading.py similarity index 74% rename from app/service/design/design_batch/items/pipeline/loading.py rename to app/service/design_batch/pipeline/loading.py index 8786db0..0ce0dfa 100644 --- a/app/service/design/design_batch/items/pipeline/loading.py +++ b/app/service/design_batch/pipeline/loading.py @@ -1,24 +1,37 @@ -import cv2 +import io +import logging -from app.service.utils.oss_client import oss_get_image +import cv2 +import numpy as np +from PIL import Image + +from app.service.utils.new_oss_client import oss_get_image + +logger = logging.getLogger() class LoadBodyImage: name = "LoadBodyImage" + def __init__(self, minio_client): + self.minio_client = minio_client + @classmethod def get_name(cls): return cls.name def __call__(self, result): result["name"] = "mannequin" - result['body_image'] = oss_get_image(bucket=result['body_path'].split("/", 1)[0], object_name=result['body_path'].split("/", 1)[1], data_type="PIL") + result['body_image'] = oss_get_image(oss_client=self.minio_client, bucket=result['body_path'].split("/", 1)[0], object_name=result['body_path'].split("/", 1)[1], data_type="PIL") return result class LoadImage: name = "LoadImage" + def __init__(self, minio_client): + self.minio_client = minio_client + @classmethod def get_name(cls): return cls.name @@ -31,10 +44,9 @@ class LoadImage: result['ori_shape'] = result['image'].shape return result - @staticmethod - def read_image(image_path): + def read_image(self, image_path): image_mask = None - image = oss_get_image(bucket=image_path.split("/", 1)[0], object_name=image_path.split("/", 1)[1], data_type="cv2") + image = oss_get_image(oss_client=self.minio_client, bucket=image_path.split("/", 1)[0], object_name=image_path.split("/", 1)[1], data_type="cv2") if len(image.shape) == 2: image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) if image.shape[2] == 4: # 如果是四通道 mask diff --git a/app/service/design/design_batch/items/pipeline/print_painting.py b/app/service/design_batch/pipeline/print_painting.py similarity index 98% rename from app/service/design/design_batch/items/pipeline/print_painting.py rename to app/service/design_batch/pipeline/print_painting.py index a620872..6fe40d8 100644 --- a/app/service/design/design_batch/items/pipeline/print_painting.py +++ b/app/service/design_batch/pipeline/print_painting.py @@ -4,10 +4,13 @@ import cv2 import numpy as np from PIL import Image -from app.service.utils.oss_client import oss_get_image +from app.service.utils.new_oss_client import oss_get_image class PrintPainting: + def __init__(self, minio_client): + self.minio_client = minio_client + def __call__(self, result): single_print = result['print']['single'] overall_print = result['print']['overall'] @@ -356,8 +359,7 @@ class PrintPainting: print_image = cv2.add(img_bg, img_fg) return print_image - @staticmethod - def get_print(print_dict): + def get_print(self, print_dict): if 'print_scale_list' not in print_dict.keys() or print_dict['print_scale_list'][0] < 0.3: print_dict['scale'] = 0.3 else: @@ -365,7 +367,7 @@ class PrintPainting: bucket_name = print_dict['print_path_list'][0].split("/", 1)[0] object_name = print_dict['print_path_list'][0].split("/", 1)[1] - image = oss_get_image(bucket=bucket_name, object_name=object_name, data_type="PIL") + image = oss_get_image(oss_client=self.minio_client, bucket=bucket_name, object_name=object_name, data_type="PIL") # 判断图片格式,如果是RGBA 则贴在一张纯白图片上 防止透明转黑 if image.mode == "RGBA": new_background = Image.new('RGB', image.size, (255, 255, 255)) @@ -480,9 +482,8 @@ class PrintPainting: return img_rotated - @staticmethod - def read_image(image_url): - image = oss_get_image(bucket=image_url.split("/", 1)[0], object_name=image_url.split("/", 1)[1], data_type="cv2") + def read_image(self, image_url): + image = oss_get_image(oss_client=self.minio_client, bucket=image_url.split("/", 1)[0], object_name=image_url.split("/", 1)[1], data_type="cv2") if image.shape[2] == 4: image_rgb = cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA) image = Image.fromarray(image_rgb) diff --git a/app/service/design/design_batch/items/pipeline/scale.py b/app/service/design_batch/pipeline/scale.py similarity index 100% rename from app/service/design/design_batch/items/pipeline/scale.py rename to app/service/design_batch/pipeline/scale.py diff --git a/app/service/design/design_batch/items/pipeline/segmentation.py b/app/service/design_batch/pipeline/segmentation.py similarity index 82% rename from app/service/design/design_batch/items/pipeline/segmentation.py rename to app/service/design_batch/pipeline/segmentation.py index 6fc81e3..cba3446 100644 --- a/app/service/design/design_batch/items/pipeline/segmentation.py +++ b/app/service/design_batch/pipeline/segmentation.py @@ -5,16 +5,19 @@ import cv2 import numpy as np from app.core.config import SEG_CACHE_PATH -from app.service.design.utils.design_ensemble import get_seg_result -from app.service.utils.oss_client import oss_get_image +from app.service.design_batch.utils.design_ensemble import get_seg_result +from app.service.utils.new_oss_client import oss_get_image logger = logging.getLogger() class Segmentation: + def __init__(self, minio_client): + self.minio_client = minio_client + def __call__(self, result): if "seg_mask_url" in result.keys() and result['seg_mask_url'] != "": - seg_mask = oss_get_image(bucket=result['seg_mask_url'].split('/')[0], object_name=result['seg_mask_url'][result['seg_mask_url'].find('/') + 1:], data_type="cv2") + seg_mask = oss_get_image(oss_client=self.minio_client, bucket=result['seg_mask_url'].split('/')[0], object_name=result['seg_mask_url'][result['seg_mask_url'].find('/') + 1:], data_type="cv2") seg_mask = cv2.resize(seg_mask, (result['img_shape'][1], result['img_shape'][0]), interpolation=cv2.INTER_NEAREST) # 转换颜色空间为 RGB(OpenCV 默认是 BGR) image_rgb = cv2.cvtColor(seg_mask, cv2.COLOR_BGR2RGB) @@ -45,7 +48,7 @@ class Segmentation: @staticmethod def save_seg_result(seg_result, image_id): - file_path = f"{SEG_CACHE_PATH}{image_id}.npy" + file_path = f"seg_cache/{image_id}.npy" try: np.save(file_path, seg_result) logger.info(f"保存成功 :{os.path.abspath(file_path)}") @@ -54,7 +57,7 @@ class Segmentation: @staticmethod def load_seg_result(image_id): - file_path = f"{SEG_CACHE_PATH}{image_id}.npy" + file_path = f"seg_cache/{image_id}.npy" logger.info(f"load seg file name is :{SEG_CACHE_PATH}{image_id}.npy") try: seg_result = np.load(file_path) diff --git a/app/service/design/design_batch/items/pipeline/split.py b/app/service/design_batch/pipeline/split.py similarity index 79% rename from app/service/design/design_batch/items/pipeline/split.py rename to app/service/design_batch/pipeline/split.py index 2fba315..5dbcef5 100644 --- a/app/service/design/design_batch/items/pipeline/split.py +++ b/app/service/design_batch/pipeline/split.py @@ -7,13 +7,16 @@ from PIL import Image from cv2 import cvtColor, COLOR_BGR2RGBA from app.core.config import AIDA_CLOTHING -from app.service.design.utils.conversion_image import rgb_to_rgba -from app.service.design.utils.upload_image import upload_png_mask +from app.service.design_batch.utils.conversion_image import rgb_to_rgba +from app.service.design_batch.utils.upload_image import upload_png_mask from app.service.utils.generate_uuid import generate_uuid -from app.service.utils.oss_client import oss_upload_image +from app.service.utils.new_oss_client import oss_upload_image class Split(object): + def __init__(self, minio_client): + self.minio_client = minio_client + def __call__(self, result): try: @@ -27,7 +30,7 @@ class Split(object): front_mask = cv2.resize(front_mask, new_size) result_front_image[front_mask != 0] = rgba_image[front_mask != 0] result_front_image_pil = Image.fromarray(cvtColor(result_front_image, COLOR_BGR2RGBA)) - result['front_image'], result["front_image_url"], _ = upload_png_mask(result_front_image_pil, f'{generate_uuid()}', mask=None) + result['front_image'], result["front_image_url"], _ = upload_png_mask(self.minio_client, result_front_image_pil, f'{generate_uuid()}', mask=None) height, width = front_mask.shape mask_image = np.zeros((height, width, 3)) @@ -38,7 +41,7 @@ class Split(object): back_mask = cv2.resize(back_mask, new_size) 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(result_back_image_pil, f'{generate_uuid()}', mask=None) + 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) @@ -47,7 +50,7 @@ class Split(object): mask_pil.save(image_data, format='PNG') image_data.seek(0) image_bytes = image_data.read() - req = oss_upload_image(bucket=AIDA_CLOTHING, object_name=f"mask/mask_{generate_uuid()}.png", image_bytes=image_bytes) + 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) @@ -56,7 +59,7 @@ class Split(object): mask_pil.save(image_data, format='PNG') image_data.seek(0) image_bytes = image_data.read() - req = oss_upload_image(bucket=AIDA_CLOTHING, object_name=f"mask/mask_{generate_uuid()}.png", image_bytes=image_bytes) + 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 @@ -65,7 +68,7 @@ class Split(object): # 创建中间图层 result_pattern_image_rgba = rgb_to_rgba(result['pattern_image'], result['mask']) result_pattern_image_pil = Image.fromarray(cvtColor(result_pattern_image_rgba, COLOR_BGR2RGBA)) - result['pattern_image'], result['pattern_image_url'], _ = upload_png_mask(result_pattern_image_pil, f'{generate_uuid()}') + result['pattern_image'], result['pattern_image_url'], _ = upload_png_mask(self.minio_client, result_pattern_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_batch/service.py b/app/service/design_batch/service.py new file mode 100644 index 0000000..db8246e --- /dev/null +++ b/app/service/design_batch/service.py @@ -0,0 +1,12 @@ +import json + +import pika +from app.service.design_batch.design_batch_celery import batch_design +from app.service.design_batch.utils.MQ import publish_status + + +async def start_design_batch_generate(data, file): + generate_clothes_task = batch_design.delay(json.loads(file.decode())['objects'], data.total, data.tasks_id) + print(generate_clothes_task) + publish_status(data.tasks_id, "0/100", "") + return {"task_id": data.tasks_id} diff --git a/app/service/design_batch/test.py b/app/service/design_batch/test.py new file mode 100644 index 0000000..6b94bc6 --- /dev/null +++ b/app/service/design_batch/test.py @@ -0,0 +1,162 @@ +from app.service.design_batch.design_batch_celery import batch_design + +if __name__ == '__main__': + data = { + "objects": [ + { + "basic": { + "body_point_test": { + "waistband_right": [ + 200, + 241 + ], + "hand_point_right": [ + 223, + 297 + ], + "waistband_left": [ + 112, + 241 + ], + "hand_point_left": [ + 92, + 305 + ], + "shoulder_left": [ + 99, + 116 + ], + "shoulder_right": [ + 215, + 116 + ] + }, + "layer_order": True, + "scale_bag": 0.7, + "scale_earrings": 0.16, + "self_template": True, + "single_overall": "overall", + "switch_category": "" + }, + "items": [ + { + "businessId": 270372, + "color": "30 28 28", + "image_id": 69780, + "offset": [ + 0, + 0 + ], + "path": "aida-sys-image/images/female/trousers/0825000630.jpg", + "print": { + "element": { + "element_angle_list": [], + "element_path_list": [], + "element_scale_list": [], + "location": [] + }, + "overall": { + "location": [], + "print_angle_list": [], + "print_path_list": [], + "print_scale_list": [] + }, + "single": { + "location": [], + "print_angle_list": [], + "print_path_list": [], + "print_scale_list": [] + } + }, + "priority": 10, + "resize_scale": [ + 1.0, + 1.0 + ], + "type": "Trousers" + }, + { + "businessId": 270373, + "color": "30 28 28", + "image_id": 98243, + "offset": [ + 0, + 0 + ], + "path": "aida-sys-image/images/female/blouse/0902003811.jpg", + "print": { + "element": { + "element_angle_list": [], + "element_path_list": [], + "element_scale_list": [], + "location": [] + }, + "overall": { + "location": [], + "print_angle_list": [], + "print_path_list": [], + "print_scale_list": [] + }, + "single": { + "location": [], + "print_angle_list": [], + "print_path_list": [], + "print_scale_list": [] + } + }, + "priority": 11, + "resize_scale": [ + 1.0, + 1.0 + ], + "type": "Blouse" + }, + { + "businessId": 270374, + "color": "172 68 68", + "image_id": 98244, + "offset": [ + 0, + 0 + ], + "path": "aida-sys-image/images/female/outwear/0825000410.jpg", + "print": { + "element": { + "element_angle_list": [], + "element_path_list": [], + "element_scale_list": [], + "location": [] + }, + "overall": { + "location": [], + "print_angle_list": [], + "print_path_list": [], + "print_scale_list": [] + }, + "single": { + "location": [], + "print_angle_list": [], + "print_path_list": [], + "print_scale_list": [] + } + }, + "priority": 12, + "resize_scale": [ + 1.0, + 1.0 + ], + "type": "Outwear" + }, + { + "body_path": "aida-sys-image/models/female/5bdfe7ca-64eb-44e4-b03d-8e517520c795.png", + "image_id": 96090, + "type": "Body" + } + ] + } + ], + "process_id": "83" + } + task_id = 1 + json_name = "test.json" + batch_design.delay(data['objects'], task_id, json_name) diff --git a/app/service/design_batch/utils/MQ.py b/app/service/design_batch/utils/MQ.py new file mode 100644 index 0000000..50e98c2 --- /dev/null +++ b/app/service/design_batch/utils/MQ.py @@ -0,0 +1,17 @@ +import json + +import pika + + +def publish_status(task_id, progress, result): + connection = pika.BlockingConnection(pika.ConnectionParameters('10.1.2.213')) + channel = connection.channel() + channel.queue_declare(queue='DesignBatch', durable=True) + message = {'task_id': task_id, 'progress': progress, "result": result} + channel.basic_publish(exchange='', + routing_key='DesignBatch', + body=json.dumps(message), + properties=pika.BasicProperties( + delivery_mode=2, + )) + connection.close() diff --git a/app/service/design/core/__init__.py b/app/service/design_batch/utils/__init__.py similarity index 100% rename from app/service/design/core/__init__.py rename to app/service/design_batch/utils/__init__.py diff --git a/app/service/design/design_batch/items/utils/conversion_image.py b/app/service/design_batch/utils/conversion_image.py similarity index 100% rename from app/service/design/design_batch/items/utils/conversion_image.py rename to app/service/design_batch/utils/conversion_image.py diff --git a/app/service/design/design_batch/items/utils/design_ensemble.py b/app/service/design_batch/utils/design_ensemble.py similarity index 100% rename from app/service/design/design_batch/items/utils/design_ensemble.py rename to app/service/design_batch/utils/design_ensemble.py diff --git a/app/service/design_batch/utils/organize.py b/app/service/design_batch/utils/organize.py new file mode 100644 index 0000000..8190de0 --- /dev/null +++ b/app/service/design_batch/utils/organize.py @@ -0,0 +1,77 @@ +import cv2 + +from app.core.config import PRIORITY_DICT + + +def organize_body(layer): + body_layer = dict(priority=0, + name=layer["name"].lower(), + image=layer['body_image'], + image_url=layer['body_path'], + mask_image=None, + mask_url=None, + sacle=1, + # mask=layer['body_mask'], + position=(0, 0)) + return body_layer + + +def organize_clothing(layer): + # 起始坐标 + start_point = calculate_start_point(layer['keypoint'], layer['scale'], layer['clothes_keypoint'], layer['body_point_test'], layer["offset"], layer["resize_scale"]) + # 前片数据 + 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"], + # mask_image=layer['front_mask_image'], + image_url=layer['front_image_url'], + mask_url=layer['mask_url'], + 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_image_url=layer['pattern_image_url'], + pattern_image=layer['pattern_image'] + + ) + # 后片数据 + back_layer = dict(priority=-layer.get("priority", 0) if layer.get("layer_order", False) else PRIORITY_DICT.get(f'{layer["name"].lower()}_back', None), + name=f'{layer["name"].lower()}_back', + image=layer["back_image"], + # mask_image=layer['back_mask_image'], + image_url=layer['back_image_url'], + mask_url=layer['mask_url'], + 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_image_url=layer['pattern_image_url'], + ) + return front_layer, back_layer + + +def calculate_start_point(keypoint_type, scale, clothes_point, body_point, offset, resize_scale): + """ + Align left + Args: + keypoint_type: string, "waistband" | "shoulder" | "ear_point" + scale: float + clothes_point: dict{'left': [x1, y1, z1], 'right': [x2, y2, z2]} + body_point: dict, containing keypoint data of body figure + + Returns: + start_point: tuple (x', y') + x' = y_body - y1 * scale + offset + y' = x_body - x1 * scale + offset + + """ + side_indicator = f'{keypoint_type}_left' + start_point = ( + int(body_point[side_indicator][1] + offset[1] - int(clothes_point[side_indicator][0]) * scale), # y + int(body_point[side_indicator][0] + offset[0] - int(clothes_point[side_indicator][1]) * scale) # x + ) + return start_point diff --git a/app/service/design_batch/utils/progress.py b/app/service/design_batch/utils/progress.py new file mode 100644 index 0000000..0f2c9cf --- /dev/null +++ b/app/service/design_batch/utils/progress.py @@ -0,0 +1,30 @@ +import logging + +from app.service.design_fast.utils.redis_utils import Redis + +logger = logging.getLogger(__name__) + + +def update_progress(process_id, total): + # logger.info(f"{process_id} , {total}") + r = Redis() + progress = r.read(key=process_id) + if progress and total != 1: + if int(progress) <= 100: + r.write(key=process_id, value=int(progress) + int(100 / total)) + else: + r.write(key=process_id, value=99) + return progress + elif total == 1: + r.write(key=process_id, value=100) + return progress + else: + r.write(key=process_id, value=int(100 / total)) + return progress + + +def final_progress(process_id): + r = Redis() + progress = r.read(key=process_id) + r.write(key=process_id, value=100) + return progress diff --git a/app/service/design/design_batch/items/utils/redis_utils.py b/app/service/design_batch/utils/redis_utils.py similarity index 100% rename from app/service/design/design_batch/items/utils/redis_utils.py rename to app/service/design_batch/utils/redis_utils.py diff --git a/app/service/design_batch/utils/save_json.py b/app/service/design_batch/utils/save_json.py new file mode 100644 index 0000000..9acd916 --- /dev/null +++ b/app/service/design_batch/utils/save_json.py @@ -0,0 +1,13 @@ +import json +import logging + +logger = logging.getLogger() + + +def oss_upload_json(oss_client, json_data, object_name): + try: + with open(f"app/service/design_batch/response_json/{object_name}", 'w') as file: + json.dump(json_data, file, indent=4) + oss_client.fput_object("test", object_name, f"app/service/design_batch/response_json/{object_name}") + except Exception as e: + logger.warning(str(e)) diff --git a/app/service/design/design_batch/items/utils/synthesis_item.py b/app/service/design_batch/utils/synthesis_item.py similarity index 94% rename from app/service/design/design_batch/items/utils/synthesis_item.py rename to app/service/design_batch/utils/synthesis_item.py index 9527cd2..272ab23 100644 --- a/app/service/design/design_batch/items/utils/synthesis_item.py +++ b/app/service/design_batch/utils/synthesis_item.py @@ -179,3 +179,19 @@ def synthesis_single(front_image, back_image): object_name = f'result_{generate_uuid()}.png' req = oss_upload_image(bucket=bucket_name, object_name=object_name, image_bytes=image_bytes) return f"{bucket_name}/{object_name}" + + +def update_base_size_priority(layers, size): + # 计算透明背景图片的宽度 + min_x = min(info['position'][1] for info in layers) + x_list = [] + for info in layers: + if info['image'] is not None: + x_list.append(info['position'][1] + info['image'].width) + max_x = max(x_list) + new_width = max_x - min_x + new_height = 700 + # 更新坐标 + for info in layers: + info['adaptive_position'] = (info['position'][0], info['position'][1] - min_x) + return layers, (new_width, new_height) diff --git a/app/service/design/utils/upload_image.py b/app/service/design_batch/utils/upload_image.py similarity index 69% rename from app/service/design/utils/upload_image.py rename to app/service/design_batch/utils/upload_image.py index 388f8b8..2c79f9f 100644 --- a/app/service/design/utils/upload_image.py +++ b/app/service/design_batch/utils/upload_image.py @@ -13,11 +13,11 @@ import logging import cv2 from app.core.config import * -from app.service.utils.oss_client import oss_upload_image +from app.service.utils.new_oss_client import oss_upload_image # @RunTime -def upload_png_mask(front_image, object_name, mask=None): +def upload_png_mask(minio_client, front_image, object_name, mask=None): try: mask_url = None if mask is not None: @@ -25,14 +25,14 @@ def upload_png_mask(front_image, object_name, mask=None): # 将掩模的3通道转换为4通道,白色部分不透明,黑色部分透明 rgba_image = cv2.cvtColor(mask_inverted, cv2.COLOR_BGR2BGRA) rgba_image[rgba_image[:, :, 0] == 0] = [0, 0, 0, 0] - req = oss_upload_image(bucket=AIDA_CLOTHING, object_name=f"mask/mask_{object_name}.png", image_bytes=cv2.imencode('.png', rgba_image)[1]) + req = oss_upload_image(oss_client=minio_client, bucket=AIDA_CLOTHING, object_name=f"mask/mask_{object_name}.png", image_bytes=cv2.imencode('.png', rgba_image)[1]) mask_url = f"{AIDA_CLOTHING}/mask/mask_{object_name}.png" image_data = io.BytesIO() front_image.save(image_data, format='PNG') image_data.seek(0) image_bytes = image_data.read() - req = oss_upload_image(bucket=AIDA_CLOTHING, object_name=f"image/image_{object_name}.png", image_bytes=image_bytes) + req = oss_upload_image(oss_client=minio_client, bucket=AIDA_CLOTHING, object_name=f"image/image_{object_name}.png", image_bytes=image_bytes) image_url = f"{AIDA_CLOTHING}/image/image_{object_name}.png" return front_image, image_url, mask_url except Exception as e: diff --git a/app/service/design_fast/pipeline/keypoint.py b/app/service/design_fast/pipeline/keypoint.py index 243cf4e..45debc2 100644 --- a/app/service/design_fast/pipeline/keypoint.py +++ b/app/service/design_fast/pipeline/keypoint.py @@ -4,7 +4,7 @@ import numpy as np from pymilvus import MilvusClient from app.core.config import * -from app.service.design.utils.design_ensemble import get_keypoint_result +from app.service.design_fast.utils.design_ensemble import get_keypoint_result logger = logging.getLogger(__name__) diff --git a/app/service/design_fast/pipeline/segmentation.py b/app/service/design_fast/pipeline/segmentation.py index 802487f..686e7b5 100644 --- a/app/service/design_fast/pipeline/segmentation.py +++ b/app/service/design_fast/pipeline/segmentation.py @@ -5,7 +5,7 @@ import cv2 import numpy as np from app.core.config import SEG_CACHE_PATH -from app.service.design.utils.design_ensemble import get_seg_result +from app.service.design_fast.utils.design_ensemble import get_seg_result from app.service.utils.new_oss_client import oss_get_image logger = logging.getLogger() diff --git a/app/service/design_fast/pipeline/split.py b/app/service/design_fast/pipeline/split.py index 35605b8..737b50e 100644 --- a/app/service/design_fast/pipeline/split.py +++ b/app/service/design_fast/pipeline/split.py @@ -7,7 +7,7 @@ from PIL import Image from cv2 import cvtColor, COLOR_BGR2RGBA from app.core.config import AIDA_CLOTHING -from app.service.design.utils.conversion_image import rgb_to_rgba +from app.service.design_fast.utils.conversion_image import rgb_to_rgba from app.service.design_fast.utils.upload_image import upload_png_mask from app.service.utils.generate_uuid import generate_uuid from app.service.utils.new_oss_client import oss_upload_image diff --git a/app/service/design_pre_processing/service.py b/app/service/design_pre_processing/service.py index a5b3a40..16ca870 100644 --- a/app/service/design_pre_processing/service.py +++ b/app/service/design_pre_processing/service.py @@ -10,7 +10,7 @@ from urllib3.exceptions import ResponseError from app.core.config import * from app.schemas.pre_processing import DesignPreProcessingModel -from app.service.design.utils.design_ensemble import get_keypoint_result, get_seg_result +from app.service.design_fast.utils.design_ensemble import get_seg_result, get_keypoint_result from app.service.utils.oss_client import oss_get_image, oss_upload_image logger = logging.getLogger() diff --git a/app/service/utils/oss_client.py b/app/service/utils/oss_client.py index 65ce3a2..5704ced 100644 --- a/app/service/utils/oss_client.py +++ b/app/service/utils/oss_client.py @@ -1,8 +1,6 @@ import io import logging from io import BytesIO - -import boto3 import cv2 import numpy as np import urllib3 @@ -42,12 +40,8 @@ def oss_get_image(bucket, object_name, data_type): # cv2 默认全通道读取 image_object = None try: - if OSS == "minio": - oss_client = Minio(MINIO_URL, access_key=MINIO_ACCESS, secret_key=MINIO_SECRET, secure=MINIO_SECURE, http_client=http_client) - image_data = oss_client.get_object(bucket_name=bucket, object_name=object_name) - else: - oss_client = boto3.client('s3', aws_access_key_id=S3_ACCESS_KEY, aws_secret_access_key=S3_AWS_SECRET_ACCESS_KEY, region_name=S3_REGION_NAME) - image_data = oss_client.get_object(Bucket=bucket, Key=object_name)['Body'] + oss_client = Minio(MINIO_URL, access_key=MINIO_ACCESS, secret_key=MINIO_SECRET, secure=MINIO_SECURE, http_client=http_client) + image_data = oss_client.get_object(bucket_name=bucket, object_name=object_name) if data_type == "cv2": image_bytes = image_data.read() image_array = np.frombuffer(image_bytes, np.uint8) # 转成8位无符号整型 @@ -65,12 +59,8 @@ def oss_get_image(bucket, object_name, data_type): def oss_upload_image(bucket, object_name, image_bytes): req = None try: - if OSS == "minio": - oss_client = Minio(MINIO_URL, access_key=MINIO_ACCESS, secret_key=MINIO_SECRET, secure=MINIO_SECURE) - req = oss_client.put_object(bucket_name=bucket, object_name=object_name, data=io.BytesIO(image_bytes), length=len(image_bytes), content_type='image/png') - else: - oss_client = boto3.client('s3', aws_access_key_id=S3_ACCESS_KEY, aws_secret_access_key=S3_AWS_SECRET_ACCESS_KEY, region_name=S3_REGION_NAME) - req = oss_client.put_object(Bucket=bucket, Key=object_name, Body=io.BytesIO(image_bytes), ContentType='image/png') + oss_client = Minio(MINIO_URL, access_key=MINIO_ACCESS, secret_key=MINIO_SECRET, secure=MINIO_SECURE) + req = oss_client.put_object(bucket_name=bucket, object_name=object_name, data=io.BytesIO(image_bytes), length=len(image_bytes), content_type='image/png') except Exception as e: logger.warning(f"{OSS} | 上传图片出现异常 ######: {e}") return req