From 726eee86ab0b4831cb1f9540a526dae961e1e811 Mon Sep 17 00:00:00 2001 From: zhouchengrong Date: Fri, 5 Apr 2024 17:45:25 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=96=B0=E5=A2=9E=E6=98=AF=E5=90=A6=E6=8E=A8?= =?UTF-8?q?=E7=90=86=E8=8E=B7=E5=8F=96=E7=89=B9=E5=BE=81=E5=88=A4=E6=96=AD?= =?UTF-8?q?=202.=E5=8F=96=E6=B6=88=E6=90=AD=E9=85=8D=E4=B8=8D=E8=B6=B3?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/api_outfit_matcher.py | 29 +-- app/core/config.py | 3 +- app/service/outfit_matcher/dataset.py | 8 +- .../outfit_matcher/outfit_evaluator.py | 77 +++---- app/service/outfit_matcher/service.py | 113 ++++++---- .../test_param/recommendation_test.json | 2 +- .../outfit_matcher/test_param/test.json | 210 +++++++++++++++++- 7 files changed, 332 insertions(+), 110 deletions(-) diff --git a/app/api/api_outfit_matcher.py b/app/api/api_outfit_matcher.py index be010c3..cbcbf0d 100644 --- a/app/api/api_outfit_matcher.py +++ b/app/api/api_outfit_matcher.py @@ -1,4 +1,5 @@ import logging +import os import time from copy import deepcopy @@ -36,20 +37,22 @@ def outfit_matcher(request_item: OutfitMatcher): # 查询数据库,分成两批 需要过模型推理的和不需要的 have_features_data = [] no_have_features_data = [] - for ai in all_items: + + temp_data = deepcopy(all_items) + for td in temp_data: for sd in search_data: - if ai['item_name'] == sd['item_name']: - ai['features'] = sd['features'] - if "features" not in ai.keys(): - no_have_features_data.append(ai) + if td['item_name'] == sd['item_name']: + td['features'] = sd['features'] + if "features" not in td.keys(): + no_have_features_data.append(td) else: - have_features_data.append(ai) + have_features_data.append(td) if len(no_have_features_data) > 0: extracted_features = backbone_service.get_result(no_have_features_data) # 准备数据 - data = deepcopy(all_items) # 做深拷贝 , all_items 是list 可变数组 + data = deepcopy(no_have_features_data) # 做深拷贝 , all_items 是list 可变数组 for i, feature in enumerate(extracted_features): data[i]['features'] = feature if 'mapped_cate' in data[i].keys(): @@ -68,17 +71,17 @@ def outfit_matcher(request_item: OutfitMatcher): result = [] start_time = time.time() for item in request_item['query']: - try: - outfits = fashion_dataset.generate_outfit(item, request_item["topk"], request_item["max_outfits"]) - except ValueError as e: - logger.warning(e) - return {"code": 500, "message": f"valueError : {e}", "data": e} + # try: + outfits = fashion_dataset.generate_outfit(item, request_item["topk"], request_item["max_outfits"]) + # except ValueError as e: + # logger.warning(e) + # return {"code": 500, "message": f"valueError : {e}", "data": e} scores = service.get_result(outfits, prepared_feature) if request_item['is_best']: best_outfits, best_scores = service.visualize(outfits, scores, request_item["topk"], best=True, - # output_path=os.path.join(r"E:\workspace\outfit_matcher\2024 SS Outfit", f"{item['item_name']}_best_{param['topk']}.png") + # output_path=rf"E:\workspace\trinity_client_mixi\app\service\outfit_matcher\output_outfit\{item['item_name']}_best_{request_item['topk']}.png" ) result.append({"outfits": best_outfits, "scores": best_scores}) else: diff --git a/app/core/config.py b/app/core/config.py index 4ba2f99..39edcce 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -35,7 +35,8 @@ ATT_TRITON_PORT = "10020" MILVUS_URL = "http://10.1.1.240:19530" -DEBUG = 2 +DEBUG = 1 +SHOW_OR_SAVE_result_image = False # service env : 1 # pycharm debug : 2 diff --git a/app/service/outfit_matcher/dataset.py b/app/service/outfit_matcher/dataset.py index 4718688..c909718 100644 --- a/app/service/outfit_matcher/dataset.py +++ b/app/service/outfit_matcher/dataset.py @@ -86,8 +86,8 @@ class FashionDataset(object): outerwear = random.choice(self.cate2item['outerwear']) outfit.append(outerwear) outfit_list.append(tuple(outfit)) - if len(outfit_list) < topk: - raise ValueError(f"Cannot generate more than {topk} outfits!") + # if len(outfit_list) < topk: + # raise ValueError(f"Cannot generate more than {topk} outfits!") return outfit_list elif given_cate == 'outerwear': @@ -112,8 +112,8 @@ class FashionDataset(object): outfit = [query_item] + list(top_bottom) outfit_list.append(tuple(outfit)) - if len(outfit_list) < topk: - raise ValueError(f"Cannot generate more than {topk} outfits!") + # if len(outfit_list) < topk: + # raise ValueError(f"Cannot generate more than {topk} outfits!") return outfit_list elif given_cate == 'all-body': diff --git a/app/service/outfit_matcher/outfit_evaluator.py b/app/service/outfit_matcher/outfit_evaluator.py index a35155e..780cb69 100644 --- a/app/service/outfit_matcher/outfit_evaluator.py +++ b/app/service/outfit_matcher/outfit_evaluator.py @@ -197,45 +197,46 @@ class OutfitMatcher(object): outfits = [outfits[i] for i in sorted_indices] # 最好或最差的五个 scores = scores[sorted_indices] # 这五个的分数 - return outfits, scores.tolist() + if SHOW_OR_SAVE_result_image: + # 设置子图的行列数 + num_rows = len(outfits) + num_cols = max([len(x) for x in outfits]) + 1 # 一个是图片,一个是分数 - # # 设置子图的行列数 - # num_rows = len(outfits) - # num_cols = max([len(x) for x in outfits]) + 1 # 一个是图片,一个是分数 - # - # # 创建一个新的图像,并指定子图的行列数 - # fig, axes = plt.subplots(num_rows, num_cols, figsize=(8, 15)) - # - # title = f"Best {topk} Outfits" if best else f"Worst {topk} Outfits" - # fig.suptitle(title, fontsize=16) - # - # # 遍历每套outfit并将其显示在对应的子图中 - # for i, (outfit, score) in enumerate(zip(outfits, scores)): - # # 显示分数 - # axes[i, 0].text(0.1, 0.5, f"Score: {score:.4f}", fontsize=12) - # axes[i, 0].axis("off") - # # 显示图片 - # for j, item in enumerate(outfit): - # img = self.load_image(item['image_path']) # 读取图片 - # axes[i, j + 1].imshow(img) # 在对应的子图中显示图片 - # axes[i, j + 1].axis('off') # 关闭坐标轴 - # axes[i, j + 1].set_title(item["semantic_category"], fontsize=10) - # for j in range(len(outfit), num_cols): - # axes[i, j].axis("off") - # - # # 在每一行的底部添加一条横线 - # axes[i, 0].axhline(y=0, color='black', linewidth=1) - # # 隐藏最后一行的横线 - # axes[-1, 0].axhline(y=0, color='white', linewidth=1) - # - # # 调整布局 - # plt.subplots_adjust(wspace=0.1, hspace=0.1) - # plt.tight_layout() - # - # if output_path: - # plt.savefig(output_path) - # else: - # plt.show() + # 创建一个新的图像,并指定子图的行列数 + fig, axes = plt.subplots(num_rows, num_cols, figsize=(8, 15)) + + title = f"Best {topk} Outfits" if best else f"Worst {topk} Outfits" + fig.suptitle(title, fontsize=16) + + # 遍历每套outfit并将其显示在对应的子图中 + for i, (outfit, score) in enumerate(zip(outfits, scores)): + # 显示分数 + axes[i, 0].text(0.1, 0.5, f"Score: {score:.4f}", fontsize=12) + axes[i, 0].axis("off") + # 显示图片 + for j, item in enumerate(outfit): + img = self.load_image(item['image_path']) # 读取图片 + axes[i, j + 1].imshow(img) # 在对应的子图中显示图片 + axes[i, j + 1].axis('off') # 关闭坐标轴 + axes[i, j + 1].set_title(item["semantic_category"], fontsize=10) + for j in range(len(outfit), num_cols): + axes[i, j].axis("off") + + # 在每一行的底部添加一条横线 + axes[i, 0].axhline(y=0, color='black', linewidth=1) + # 隐藏最后一行的横线 + axes[-1, 0].axhline(y=0, color='white', linewidth=1) + + # 调整布局 + plt.subplots_adjust(wspace=0.1, hspace=0.1) + plt.tight_layout() + + if output_path: + plt.savefig(output_path) + else: + plt.show() + + return outfits, scores.tolist() class OutfitMatcherHon(OutfitMatcher): diff --git a/app/service/outfit_matcher/service.py b/app/service/outfit_matcher/service.py index b99af95..8fd4465 100644 --- a/app/service/outfit_matcher/service.py +++ b/app/service/outfit_matcher/service.py @@ -1,57 +1,84 @@ import json -import os -from pprint import pprint -import numpy as np +import time +from copy import deepcopy +from pymilvus import MilvusClient + +from app.core.config import * from app.service.outfit_matcher.dataset import FashionDataset from app.service.outfit_matcher.outfit_evaluator import OutfitMaterTypeAware, Backbone +logger = logging.getLogger() + if __name__ == '__main__': - with open("./test_param/recommendation_test.json", "r") as f: - param = json.load(f) - fashion_dataset = FashionDataset(param["database"]) + with open("./test_param/test.json", "r") as f: + request_item = json.load(f) + request_item = dict(request_item) + for i in range(len(request_item['query'])): + request_item['query'][i] = dict(request_item['query'][i]) + for i in range(len(request_item['database'])): + request_item['database'][i] = dict(request_item['database'][i]) + fashion_dataset = FashionDataset(request_item['database']) backbone_service = Backbone() service = OutfitMaterTypeAware() - - # read feature from vector database - all_items = param["query"] + param["database"] - unextracted_item = [] + all_items = request_item["query"] + request_item["database"] prepared_feature = {} - # 拿到所有需要提取特征的图片 - for item in all_items: - if f'{item["item_name"]}.npy' not in os.listdir("feature"): - unextracted_item.append(item) - if len(unextracted_item) > 0: - # 通过backbone模型提取图片特征 - extracted_features = backbone_service.get_result(unextracted_item) - for i, item in enumerate(unextracted_item): - # save features - # 链接milvus - # TODO - np.save(f'feature/{item["item_name"]}.npy', extracted_features[i]) - # 存入数据库 - # 关闭链接 + # 连接milvus + client = MilvusClient(uri=MILVUS_URL, token="root:Milvus", db_name="mixi") + search_data = client.get(collection_name='mixi_outfit', ids=[item['item_name'] for item in all_items]) - # TODO 读取本次任务需要的图片特征 - for item in all_items: - if item["item_name"] not in prepared_feature.keys(): - prepared_feature[item["item_name"]] = np.load(f'feature/{item["item_name"]}.npy') + # 查询数据库,分成两批 需要过模型推理的和不需要的 + have_features_data = [] + no_have_features_data = [] + + temp_data = deepcopy(all_items) + for td in temp_data: + for sd in search_data: + if td['item_name'] == sd['item_name']: + td['features'] = sd['features'] + if "features" not in td.keys(): + no_have_features_data.append(td) + else: + have_features_data.append(td) + + if len(no_have_features_data) > 0: + extracted_features = backbone_service.get_result(no_have_features_data) + + # 准备数据 + data = deepcopy(no_have_features_data) # 做深拷贝 , all_items 是list 可变数组 + for i, feature in enumerate(extracted_features): + data[i]['features'] = feature + if 'mapped_cate' in data[i].keys(): + del data[i]['mapped_cate'] + + # 存入数据 + res = client.insert(collection_name="mixi_outfit", data=data) + for d in data: + prepared_feature[d['item_name']] = d['features'] + for hfd in have_features_data: + prepared_feature[hfd['item_name']] = hfd['features'] + # 断开连接 + client.close() + result = [] + start_time = time.time() + for item in request_item['query']: + # try: + outfits = fashion_dataset.generate_outfit(item, request_item["topk"], request_item["max_outfits"]) + # except ValueError as e: + # logger.warning(e) + # return {"code": 500, "message": f"valueError : {e}", "data": e} - # 开始服装搭配任务 - for item in param["query"]: - # 根据一定规则生成outfit - outfits = fashion_dataset.generate_outfit(item, param["topk"], param["max_outfits"]) - # 根据模型对生成的outfit打分 scores = service.get_result(outfits, prepared_feature) - # 对评分排序,拿到最好的topk个outfit输出 - sorted_indices = np.argsort(scores)[:param["topk"]] # type-aware - best_outfits = [outfits[i] for i in sorted_indices] # 最好的五个 - # 结果可视化 - # service.visualize(outfits, scores, param["topk"], best=True, - # output_path=os.path.join(r"D:\PhD_Study\MIXI\mitu\image\123", - # f"{item['item_name']}_best_{param['topk']}.png")) - # service.visualize(outfits, scores, param["topk"], best=False, - # output_path=os.path.join(r"D:\PhD_Study\MIXI\mitu\image\123", - # f"{item['item_name']}_worst_{param['topk']}.png")) + if request_item['is_best']: + best_outfits, best_scores = service.visualize(outfits, scores, request_item["topk"], best=True, + output_path=rf"E:\workspace\trinity_client_mixi\app\service\outfit_matcher\output_outfit\{item['item_name']}_best_{request_item['topk']}.png" + ) + result.append({"outfits": best_outfits, "scores": best_scores}) + else: + bad_outfits, bad_scores = service.visualize(outfits, scores, request_item["topk"], best=False, + # output_path=os.path.join(r"E:\workspace\outfit_matcher\2024 SS Outfit", f"{item['item_name']}_worst_{param['topk']}.png") + ) + result.append({"outfits": bad_outfits, "scores": bad_scores}) + logger.info(f"run time is : {time.time() - start_time}") diff --git a/app/service/outfit_matcher/test_param/recommendation_test.json b/app/service/outfit_matcher/test_param/recommendation_test.json index ef86056..92bc74f 100644 --- a/app/service/outfit_matcher/test_param/recommendation_test.json +++ b/app/service/outfit_matcher/test_param/recommendation_test.json @@ -1,6 +1,6 @@ { "topk": 5, - "max_outfits": 200, + "max_outfits": 10, "is_best": true, "query": [ { diff --git a/app/service/outfit_matcher/test_param/test.json b/app/service/outfit_matcher/test_param/test.json index a62fd97..16e6236 100644 --- a/app/service/outfit_matcher/test_param/test.json +++ b/app/service/outfit_matcher/test_param/test.json @@ -1,19 +1,209 @@ { - "topk": 1, - "max_outfits": 5, - "is_best": true, - "query": [ + "topk": 5, + "max_outfits": 10, + "database": [ { - "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27000_0BLK.jpg/3f4676db-98a1-44d4-947f-9d1f59828629.jpg", + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27000_0BLK.jpg/MKTS27000_0BLK.jpg", "item_name": "MKTS27000", "semantic_category": "BOTTOM/PANTS" + }, + { + "image_path": "mi-tu/26/TOP/BLOUSE/MKTS27002_0WHT.jpg/MKTS27002_0WHT.jpg", + "item_name": "MKTS27002", + "semantic_category": "TOP/BLOUSE" + }, + { + "image_path": "mi-tu/26/BOTTOM/SHORTS/MKTS27001_0BLK.jpg/MKTS27001_0BLK.jpg", + "item_name": "MKTS27001", + "semantic_category": "BOTTOM/SHORTS" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27004_0BGE.jpg/MKTS27004_0BGE.jpg", + "item_name": "MKTS27004", + "semantic_category": "BOTTOM/PANTS" + }, + { + "image_path": "mi-tu/26/TOP/BLOUSE/MKTS27002_0BLK.jpg/MKTS27002_0BLK.jpg", + "item_name": "MKTS27002", + "semantic_category": "TOP/BLOUSE" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/BLAZER/MKTS27008_0BLK.jpg/MKTS27008_0BLK.jpg", + "item_name": "MKTS27008", + "semantic_category": "OUTERWEAR/BLAZER" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27009_0BLK.jpg/MKTS27009_0BLK.jpg", + "item_name": "MKTS27009", + "semantic_category": "BOTTOM/PANTS" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27004_0BLU.jpg/MKTS27004_0BLU.jpg", + "item_name": "MKTS27004", + "semantic_category": "BOTTOM/PANTS" + }, + { + "image_path": "mi-tu/26/TOP/VEST/MKTS27011_0BLK.jpg/MKTS27011_0BLK.jpg", + "item_name": "MKTS27011", + "semantic_category": "TOP/VEST" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27009_0FUS.jpg/MKTS27009_0FUS.jpg", + "item_name": "MKTS27009", + "semantic_category": "BOTTOM/PANTS" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/GILET/MKTS27003_0BGE.jpg/MKTS27003_0BGE.jpg", + "item_name": "MKTS27003", + "semantic_category": "OUTERWEAR/GILET" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/BLAZER/MKTS27010_0WHT.jpg/MKTS27010_0WHT.jpg", + "item_name": "MKTS27010", + "semantic_category": "OUTERWEAR/BLAZER" + }, + { + "image_path": "mi-tu/26/TOP/VEST/MKTS27011_0CMY.jpg/MKTS27011_0CMY.jpg", + "item_name": "MKTS27011", + "semantic_category": "TOP/VEST" + }, + { + "image_path": "mi-tu/26/BOTTOM/SHORTS/MKTS27013_0ORG.jpg/MKTS27013_0ORG.jpg", + "item_name": "MKTS27013", + "semantic_category": "BOTTOM/SHORTS" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/GILET/MKTS27015_0BLK.jpg/MKTS27015_0BLK.jpg", + "item_name": "MKTS27015", + "semantic_category": "OUTERWEAR/GILET" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/GILET/MKTS27015_0CMY.jpg/MKTS27015_0CMY.jpg", + "item_name": "MKTS27015", + "semantic_category": "OUTERWEAR/GILET" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/BLAZER/MKTS27010_0DBL.jpg/MKTS27010_0DBL.jpg", + "item_name": "MKTS27010", + "semantic_category": "OUTERWEAR/BLAZER" + }, + { + "image_path": "mi-tu/26/BOTTOM/SHORTS/MKTS27013_0BLU.jpg/MKTS27013_0BLU.jpg", + "item_name": "MKTS27013", + "semantic_category": "BOTTOM/SHORTS" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/JACKET/MKTS27012_0ORG.jpg/MKTS27012_0ORG.jpg", + "item_name": "MKTS27012", + "semantic_category": "OUTERWEAR/JACKET" + }, + { + "image_path": "mi-tu/26/BOTTOM/SHORTS/MKTS27016_0CMY.jpg/MKTS27016_0CMY.jpg", + "item_name": "MKTS27016", + "semantic_category": "BOTTOM/SHORTS" + }, + { + "image_path": "mi-tu/26/BOTTOM/SHORTS/MKTS27016_0BLK.jpg/MKTS27016_0BLK.jpg", + "item_name": "MKTS27016", + "semantic_category": "BOTTOM/SHORTS" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/WINDBREAKER/MKTS27017_0GRN.jpg/MKTS27017_0GRN.jpg", + "item_name": "MKTS27017", + "semantic_category": "OUTERWEAR/WINDBREAKER" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/JACKET/MKTS27012_0BLU.jpg/MKTS27012_0BLU.jpg", + "item_name": "MKTS27012", + "semantic_category": "OUTERWEAR/JACKET" + }, + { + "image_path": "mi-tu/26/TOP/SHIRT/MKTS27018_0WHT.jpg/MKTS27018_0WHT.jpg", + "item_name": "MKTS27018", + "semantic_category": "TOP/SHIRT" + }, + { + "image_path": "mi-tu/26/TOP/SHIRT/MKTS27018_0BLK.jpg/MKTS27018_0BLK.jpg", + "item_name": "MKTS27018", + "semantic_category": "TOP/SHIRT" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/BLAZER/MKTS27019_0BLK.jpg/MKTS27019_0BLK.jpg", + "item_name": "MKTS27019", + "semantic_category": "OUTERWEAR/BLAZER" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/WINDBREAKER/MKTS27017_0PNK.jpg/MKTS27017_0PNK.jpg", + "item_name": "MKTS27017", + "semantic_category": "OUTERWEAR/WINDBREAKER" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/BLAZER/MKTS27019_0PNK.jpg/MKTS27019_0PNK.jpg", + "item_name": "MKTS27019", + "semantic_category": "OUTERWEAR/BLAZER" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27027_0DBL.jpg/MKTS27027_0DBL.jpg", + "item_name": "MKTS27027", + "semantic_category": "BOTTOM/PANTS" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27027_0PNK.jpg/MKTS27027_0PNK.jpg", + "item_name": "MKTS27027", + "semantic_category": "BOTTOM/PANTS" } ], - "database": [ + "query": [ { - "image_path": "mi-tu/26/TOP/BLOUSE/MKTS27002_0WHT.jpg/131cc29e-8f70-4134-a0e8-82f826b00058.jpg", - "item_name": "MKTS27002", - "semantic_category": "TOP/BLOUSE" + "image_path": "mi-tu/26/BOTTOM/SHORTS/MKTS27016_0BLK.jpg/MKTS27016_0BLK.jpg", + "item_name": "MKTS27016", + "semantic_category": "BOTTOM/SHORTS" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/WINDBREAKER/MKTS27017_0GRN.jpg/MKTS27017_0GRN.jpg", + "item_name": "MKTS27017", + "semantic_category": "OUTERWEAR/WINDBREAKER" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/JACKET/MKTS27012_0BLU.jpg/MKTS27012_0BLU.jpg", + "item_name": "MKTS27012", + "semantic_category": "OUTERWEAR/JACKET" + }, + { + "image_path": "mi-tu/26/TOP/SHIRT/MKTS27018_0WHT.jpg/MKTS27018_0WHT.jpg", + "item_name": "MKTS27018", + "semantic_category": "TOP/SHIRT" + }, + { + "image_path": "mi-tu/26/TOP/SHIRT/MKTS27018_0BLK.jpg/MKTS27018_0BLK.jpg", + "item_name": "MKTS27018", + "semantic_category": "TOP/SHIRT" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/BLAZER/MKTS27019_0BLK.jpg/MKTS27019_0BLK.jpg", + "item_name": "MKTS27019", + "semantic_category": "OUTERWEAR/BLAZER" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/WINDBREAKER/MKTS27017_0PNK.jpg/MKTS27017_0PNK.jpg", + "item_name": "MKTS27017", + "semantic_category": "OUTERWEAR/WINDBREAKER" + }, + { + "image_path": "mi-tu/26/OUTERWEAR/BLAZER/MKTS27019_0PNK.jpg/MKTS27019_0PNK.jpg", + "item_name": "MKTS27019", + "semantic_category": "OUTERWEAR/BLAZER" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27027_0DBL.jpg/MKTS27027_0DBL.jpg", + "item_name": "MKTS27027", + "semantic_category": "BOTTOM/PANTS" + }, + { + "image_path": "mi-tu/26/BOTTOM/PANTS/MKTS27027_0PNK.jpg/MKTS27027_0PNK.jpg", + "item_name": "MKTS27027", + "semantic_category": "BOTTOM/PANTS" } - ] + ], + "is_best": true } \ No newline at end of file