"""Fashion-CLIP 衣物分类服务 使用 patrickjohncyh/fashion-clip 模型进行零样本分类, 一次性识别衣物的 15 个维度: 大类 / 子类别 / 风格 / 主颜色 / 副颜色 / 厚度 / 版型 / 材质 / 花纹 / 袖型 / 衣长类型 / 领型 / 正式程度(1-5) / 适用场合 / 视觉冲击力(1-5)。 所有维度在一次模型前向中完成(约 210 个提示词拼接为一个 batch, 按组 softmax 归一化),响应字段与后端 clothes 表模型命名对齐。 模型首次运行从 HuggingFace Hub 下载权重并缓存到本地。 默认使用 hf-mirror.com 镜像(国内加速)。 """ import io import logging import os # !!!必须在 import transformers 之前设置,否则 huggingface_hub 读不到这个环境变量!!! if "HF_ENDPOINT" not in os.environ: os.environ["HF_ENDPOINT"] = "https://hf-mirror.com" import torch from fastapi import FastAPI, HTTPException, UploadFile from PIL import Image from transformers import CLIPModel, CLIPProcessor logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI(title="Fashion-CLIP Classification Service") # ============================================================================ # 分类提示词 # 说明: # - Fashion-CLIP 是英文训练的,用英文+视觉特征描述效果远好于简单中文标签。 # - 键为中文标签(返回给客户端,与 clothes 表字段取值一致),值为英文提示词。 # - 描述尽量包含剪裁、长度、材质视觉特征与穿着场景,提升零样本区分度。 # ============================================================================ CATEGORY_PROMPTS = { "上装": "a short top, t-shirt, shirt, blouse or sweater worn on the upper body, garment covering the torso from shoulders to waist or hips", "下装": "a pair of pants, trousers, jeans, shorts, leggings or a skirt worn on the lower body below the waist", "连衣裙": "a one-piece dress worn from shoulders down, covering both upper and lower body in a single connected garment", "外套": "a jacket, coat, blazer or outerwear worn as an outer layer over other clothes, with sleeves and front opening", "鞋": "a pair of shoes, sneakers, boots, sandals or slippers worn on the feet, a full shoe shape with sole, heel and toe, footwear covering the foot", "配饰": "a small personal accessory item like a hat, scarf, belt, bag, sunglasses, necklace, earrings, bracelet, wristwatch or other jewelry, not a main clothing garment", } # 子类别按大类分组:大类确定后在其对应组内取 top1,避免牛仔裤出现在上装里 SUBCATEGORY_GROUPS = { "上装": { "T恤": "a short-sleeve cotton t-shirt with round crew neck, casual basic top, no buttons no collar", "衬衫": "a button-up shirt with collar and front buttons, long or short sleeves, woven fabric with cuffs", "卫衣": "a hoodie or sweatshirt, thick fleece cotton pullover with or without hood and kangaroo pocket", "毛衣": "a thick knitted wool sweater or pullover, chunky warm textured knit top", "针织衫": "a thin fine-knit top, ribbed lightweight knitwear, fitted casual knit shirt, finer yarn than a chunky sweater", "背心": "a sleeveless tank top with wide shoulder coverage, sporty or basic vest top", "Polo衫": "a polo shirt with flat collar, short sleeves and a 2-3 button placket, sporty casual", "开衫": "a cardigan, open-front knitted sweater-like top with buttons or zipper down the front", "吊带衫": "a camisole top with thin spaghetti straps, sleeveless lightweight top with narrow shoulder straps", "打底衫": "a basic fitted long-sleeve layering top, thin turtleneck or crew neck undershirt, plain solid color", }, "下装": { "牛仔裤": "a pair of blue denim jeans, casual pants with visible seams, rivets and five pockets", "休闲裤": "a pair of casual cotton or chino trousers, straight leg comfortable pants, no denim texture", "运动裤": "a pair of sporty sweatpants or joggers with elastic waistband, relaxed athletic fit", "短裤": "a pair of shorts ending above the knee, short pants for warm weather", "半身裙": "a skirt worn from waist down, flared A-line or straight or pleated silhouette", "阔腿裤": "a pair of wide-leg loose flowing trousers with very wide leg openings", "西装裤": "a pair of formal tailored dress trousers with center crease, suit pants", "鲨鱼裤": "a pair of tight shiny yoga leggings, skin-tight stretch pants, sporty smooth surface", "工装裤": "a pair of cargo pants with large side pockets on the legs, utility workwear style", "喇叭裤": "a pair of flared pants fitted at thigh and widening from knee to hem, bell-bottom style", }, "连衣裙": { "连衣短裙": "a short mini dress above the knee, one-piece garment", "连衣长裙": "a long maxi dress reaching the ankles, elegant flowing one-piece", "吊带裙": "a slip dress with thin spaghetti straps, sleeveless one-piece dress", "衬衫连衣裙": "a shirt dress with button-up front and collar, shirt style one-piece dress", "针织连衣裙": "a knitted one-piece dress with ribbed sweater texture, body-hugging or flowing knit", "雪纺连衣裙": "a chiffon dress with sheer lightweight flowing layered fabric, romantic airy look", "西装连衣裙": "a tailored blazer-style dress with lapels, structured office dress", }, "外套": { "夹克": "a short lightweight jacket ending at waist or hip, sporty casual outerwear with zipper", "西装外套": "a formal blazer or suit jacket with lapels, structured shoulders, button closure", "风衣": "a long trench coat with belt, double-breasted buttons, knee-length waterproof style", "羽绒服": "a puffy down jacket with quilted sections, thick warm insulation, winter puffer coat", "大衣": "a long wool overcoat reaching knee or below, elegant warm winter coat", "马甲": "a sleeveless vest worn over other clothes, no sleeves, front zipper or buttons", "牛仔外套": "a denim jacket with metal buttons and chest flap pockets, blue jean fabric, trucker style", "皮衣": "a leather jacket with glossy or matte leather surface, biker or fashion style", "棉服": "a quilted cotton padded jacket, lightweight puffer, insulated casual winter jacket", }, "鞋": { "运动鞋": "a pair of athletic sneakers with rubber sole, sport running or training shoes", "皮鞋": "a pair of formal leather dress shoes with polished shiny surface, lace-up or slip-on", "凉鞋": "a pair of open-toe sandals with straps, exposed toes, summer footwear", "靴子": "a pair of boots covering ankle or higher, sturdy footwear with high-top shaft", "帆布鞋": "a pair of canvas shoes with flat rubber sole, casual lace-up sneakers", "高跟鞋": "a pair of high heel shoes or pumps with elevated heel, formal feminine footwear", "乐福鞋": "a pair of loafers, slip-on leather shoes without laces, flat casual dress shoes", "外穿拖鞋": "a pair of outdoor slides or chunky slippers, open-toe flat sandals for casual wear", "玛丽珍鞋": "a pair of Mary Jane shoes with rounded toe and one or more straps across the instep, feminine flat or slight heel", "洞洞鞋": "a pair of foam clogs with perforated holes on the upper, casual slip-on crocs style", "马丁靴": "a pair of combat boots with chunky sole, lace-up leather ankle boots", }, "配饰": { "帽子": "a hat or cap worn on the head, baseball cap, beanie, bucket hat or sun hat", "围巾": "a scarf worn around the neck, long fabric accessory", "腰带": "a belt worn around the waist, leather strap with metal buckle", "墨镜": "a pair of sunglasses with dark tinted lenses", "项链": "a necklace, jewelry chain or pendant worn around the neck", "手链": "a bracelet or bangle, purely decorative jewelry worn on the wrist, no watch face, no dial, no clock", "包包": "a handbag, purse, shoulder bag, tote bag or backpack accessory", "耳饰": "a pair of earrings, jewelry worn on the earlobes", "丝巾": "a silk scarf or bandana, lightweight decorative fabric neck accessory", "发带": "a headband or hair tie accessory worn on the head or in the hair", "戒指": "a ring, small jewelry worn on the finger", "胸针": "a brooch, decorative pin jewelry attached to clothing", "手表": "a wristwatch timepiece with a large prominent round or square watch face at the center, a clock dial showing numbers and watch hands or digital display, buttons or crown on the sides, thick strap band, worn on the wrist as a timekeeping device", }, } COLOR_PROMPTS = { "黑色": "a black colored garment, very dark black clothing", "白色": "a white colored garment, pure white clothing", "红色": "a red colored garment, bright red clothing", "蓝色": "a standard blue colored garment, medium blue clothing", "绿色": "a green colored garment, green clothing", "黄色": "a yellow colored garment, bright yellow clothing", "粉色": "a pink colored garment, soft pink clothing", "紫色": "a purple colored garment, violet purple clothing", "灰色": "a gray colored garment, grey clothing", "棕色": "a brown colored garment, brown clothing", "橙色": "an orange colored garment, orange clothing", "卡其色": "a khaki colored garment, beige-tan clothing", "米色": "a cream or beige colored garment, off-white soft beige clothing", "杏色": "an apricot colored garment, soft peachy beige clothing", "焦糖色": "a caramel brown colored garment, warm rich toffee brown clothing", "藏青色": "a navy blue colored garment, very dark deep blue clothing", "天蓝色": "a sky blue colored garment, light bright clear blue clothing", "军绿色": "an olive green or army green colored garment, military green clothing", "酒红色": "a burgundy or wine red colored garment, deep maroon red clothing", "金色": "a gold colored garment, metallic golden shiny clothing", "银色": "a silver colored garment, metallic silver shiny clothing", "玫红色": "a rose red or hot pink magenta colored garment, vivid pinkish red clothing", "荧光色": "a neon fluorescent colored garment, extremely bright glowing highlighter colors like neon green or neon orange", "墨绿色": "a dark forest green colored garment, deep dark green clothing", "宝蓝色": "a royal blue colored garment, saturated vivid bright blue clothing", "浅蓝色": "a light blue colored garment, pale soft pastel blue clothing", "粉紫色": "a lavender or lilac colored garment, soft purple-pink pastel clothing", "驼色": "a camel colored garment, warm tan brown beige clothing", "砖红色": "a brick red colored garment, muted earthy red-brown clothing", "浅灰色": "a light gray colored garment, pale soft grey clothing", } STYLE_PROMPTS = { "休闲风": "casual everyday style clothing, relaxed comfortable fashion", "通勤风": "business formal office style clothing, professional workplace attire", "运动风": "athletic sporty style clothing, gym and workout fashion", "街头风": "streetwear urban style clothing, trendy hip-hop influenced fashion", "优雅风": "elegant sophisticated style clothing, feminine chic fashion", "甜美风": "cute sweet girly style clothing, lovely feminine fashion with ruffles and pastel colors", "复古风": "vintage retro style clothing, old-school classic fashion from past decades", "简约风": "minimalist simple style clothing, clean basic design without decoration", "商务正装风": "formal business suit style clothing, corporate professional tailored attire", "轻熟风": "smart casual mature style clothing, polished sophisticated young professional fashion", "工装风": "utility workwear style clothing, cargo pockets and durable functional fashion", "学院风": "preppy academic style clothing, college campus style with plaid, knit sweaters, collared shirts", "甜酷风": "sweet and edgy style clothing, feminine meets punk, cute yet rebellious fashion", "辣妹风": "sexy hot girl style clothing, bold bodycon and crop top fashion, confident glam look", "度假风": "resort vacation style clothing, tropical holiday beachwear, relaxed bohemian fashion", "新中式风": "modern Chinese style clothing, contemporary oriental fashion with traditional Chinese elements", "极简风": "extreme minimalist style clothing, monochrome neutral colors, no logos no patterns, pure basic design", "波西米亚风": "bohemian style clothing, flowy fabric, fringe, ethnic embroidery, relaxed free-spirited silhouette", "朋克风": "punk style clothing, black leather, studs, ripped details, rebellious rock look", "嘻哈风": "hip-hop style clothing, oversized baggy silhouette, urban street rap culture fashion", "森系风": "mori girl forest style clothing, natural earthy colors, layered loose linen and cotton, rustic gentle look", "法式风": "French chic style clothing, effortless elegant parisian look, simple refined feminine fashion", "美式复古风": "American vintage retro style clothing, classic 90s american casual, denim, varsity, old-school usa look", "日系风": "Japanese style clothing, clean simple japanese fashion, relaxed natural fabric look", "韩系风": "Korean style clothing, trendy korean fashion, soft colors, neat youthful casual look", } FIT_PROMPTS = { "宽松": "a loose oversized fit garment, baggy relaxed silhouette clothing", "修身": "a slim fitted garment, tailored silhouette that follows the body shape", "阔腿": "a wide leg loose flowing silhouette, palazzo or flare pants style", "紧身": "a tight skinny fit garment, bodycon stretch clothing, form-fitting second skin", "常规合身": "a regular standard fit garment, normal classic cut, neither tight nor loose", "直筒": "a straight leg or straight cut garment, parallel vertical lines from top to bottom, no flare no taper", } MATERIAL_PROMPTS = { "牛仔布": "denim fabric material, rugged blue cotton twill with visible diagonal weave and stitching", "针织": "knitted fabric material, textured knitwear with visible yarn loops, sweater material", "皮质": "genuine leather material, smooth shiny or matte animal hide surface with natural grain", "纯棉": "pure cotton material, soft matte natural cotton fabric, breathable woven cotton textile", "羊毛": "wool material, fuzzy warm woolen fabric, thick natural animal fiber textile", "丝绸": "silk material, smooth lustrous shiny satin-like fabric, elegant silky sheen surface", "雪纺": "chiffon material, sheer lightweight translucent fabric, soft delicate flowy textile", "麻料": "linen material, textured natural flax fabric with visible weave, breathable rustic look", "蕾丝": "lace material, delicate openwork patterned fabric, intricate floral mesh textile", "尼龙": "nylon synthetic material, smooth shiny technical fabric, waterproof windbreaker material", "灯芯绒": "corduroy material, ribbed velvety fabric with parallel raised cords, textured wales", "天鹅绒": "velvet material, soft plush fabric with rich deep sheen and smooth nap", "缎面": "satin material, smooth glossy surface with strong silky shine, fluid drape", "网纱": "tulle or mesh material, sheer fine net fabric, transparent gauzy texture", "羊羔绒": "sherpa fleece material, fluffy curly wool-like lining or surface, cozy plush texture", "PU皮": "faux leather PU material, artificial leather with uniform smooth plastic-like glossy surface", "磨毛棉": "brushed cotton material, soft slightly fuzzy matte surface, cozy flannel-like texture", } PATTERN_PROMPTS = { "纯色": "a solid color garment with no pattern, plain single-color fabric without any design", "条纹": "a striped pattern garment with horizontal or vertical parallel lines", "格纹": "a plaid or checkered pattern garment, crossed horizontal and vertical lines forming squares", "印花": "a printed pattern garment with graphic floral or artistic designs on fabric", "刺绣": "an embroidered garment with raised stitched thread decorative patterns", "波点": "a polka dot pattern garment with small round dots evenly distributed", "迷彩": "a camouflage pattern garment with irregular green brown patches, military style", "扎染": "a tie-dye pattern garment with irregular colorful swirled dyed patterns", "碎花": "a small floral print pattern garment with tiny scattered flower designs", "字母": "a lettering or slogan print garment with text words or logos", "千鸟格": "a houndstooth check pattern garment, small broken zigzag checks in two-tone", "菱格纹": "an argyle pattern garment with diamond shapes, diagonal intersecting lines", "人字纹": "a herringbone pattern garment with V-shaped zigzag weave, broken twill", "拼色块": "a color block garment with contrasting solid color panels stitched together in large sections", "渐变": "an ombre gradient pattern garment with colors fading gradually from one shade to another", "豹纹": "a leopard print pattern garment with animal spots, brown and black rosette spots", "蕾丝花纹": "a lace pattern garment with delicate openwork floral mesh design", "几何图案": "a geometric pattern garment with shapes like triangles, circles and abstract lines arranged in order", "logo图案": "a logo or brand emblem print garment with large recognizable brand marks or icons", } # 厚度(适用:上装/下装/连衣裙/外套/鞋) THICKNESS_PROMPTS = { "薄": "a thin lightweight fabric garment, sheer or semi-sheer summer clothing, cool breathable material", "适中": "a medium weight fabric garment, regular thickness clothing, standard everyday material", "厚": "a thick heavy fabric garment, warm dense material, substantial winter clothing", "加厚": "an extra thick heavily padded garment, quilted puffy down insulation, maximum warmth winter wear", } # 袖型(适用:上装/连衣裙/外套) SLEEVE_TYPE_PROMPTS = { "无袖": "a sleeveless garment with no sleeves, bare shoulders and arms exposed", "吊带": "a garment with thin spaghetti straps, narrow shoulder straps instead of sleeves", "短袖": "a short sleeve garment, sleeves ending above the elbow", "中袖": "a half sleeve or elbow length sleeve garment, sleeves ending at or below the elbow", "长袖": "a long sleeve garment, sleeves reaching the wrist", "喇叭袖": "a flared bell sleeve garment, sleeve widening dramatically from elbow to a wide cuff", "泡泡袖": "a puff sleeve garment, voluminous balloon sleeve gathered at shoulder and cuff", "落肩袖": "a dropped shoulder sleeve garment, shoulder seam falling below the natural shoulder, relaxed slouchy look", } # 衣长类型(适用:上装/下装/连衣裙/外套) CLOTHES_TYPE_PROMPTS = { "短款": "a cropped short length garment, ends above or at the waist, showing midriff or waistline", "常规": "a regular standard length garment, normal classic length, ends around hip or upper thigh", "中长": "a mid length garment, ends around mid-thigh or knee", "长款": "a long length garment, ends below the knee or at the calf", "超长款": "an extra long garment, ankle-length or floor-length, maxi style full length", } # 领型(适用:上装/连衣裙/外套) NECKLINE_PROMPTS = { "圆领": "a round crew neckline, circular collar hugging the neck", "V领": "a V-shaped neckline, pointed dip at the front center chest", "方领": "a square neckline with straight horizontal edge across the chest", "高领": "a turtleneck high collar, folded fabric covering the whole neck", "翻领": "a lapel or shirt collar, folded flat collar with pointed tips", "立领": "a standing band collar, mandarin collar rising vertically around the neck", "一字领": "an off-shoulder neckline, straight horizontal top edge exposing the shoulders", "挂脖": "a halter neckline with straps tying behind the neck, open back and shoulders", "连帽": "a hooded garment with an attached hood around the neckline", "娃娃领": "a peter pan collar, small flat rounded collar lying on the garment", } # 正式程度 1-5(label 分类后映射为数字等级) FORMALITY_PROMPTS = { "非常休闲": "very casual clothing, sporty gym wear, beach wear, loungewear, relaxed informal style", "休闲": "casual clothing, everyday relaxed wear, jeans and t-shirt style", "轻正式": "smart casual clothing, neat polished everyday style, business casual attire", "商务正式": "business formal clothing, office suit and tailored professional attire", "非常正式": "very formal clothing, evening gown, tuxedo, ceremonial dress code attire", } # 适用场合(取置信度达标的 top-N 逗号连接) OCCASION_PROMPTS = { "日常通勤": "everyday commuting and office wear, practical comfortable work outfit", "运动健身": "sports and gym workout wear, athletic exercise clothing", "约会聚会": "date night and social gathering outfit, romantic or fashionable party look", "正式商务": "formal business meeting attire, professional corporate dress code", "居家休闲": "home loungewear, cozy at-home relaxing clothing", "度假旅游": "vacation and travel wear, resort holiday clothing, beach and sightseeing outfit", "户外活动": "outdoor activity wear, hiking camping adventure functional clothing", "派对夜店": "party and nightclub outfit, trendy bold nightlife fashion", "校园上课": "school and campus wear, youthful student daily outfit", "婚礼宴会": "wedding and banquet attire, elegant formal celebration dress", } # 视觉冲击力 1-5(label 分类后映射为数字等级) VISUAL_IMPACT_PROMPTS = { "低调": "very low visual impact, plain minimal clothing, muted neutral colors, no decoration", "平淡": "low visual impact, simple understated clothing, basic design with little attention-grabbing", "适中": "moderate visual impact, average clothing with normal color and simple design details", "亮眼": "high visual impact, eye-catching clothing, bright colors or striking design, stands out", "高调": "very high visual impact, bold dazzling clothing, neon glitter dramatic statement design, impossible to ignore", } # ============================================================================ # 批量推理分组:所有维度 + 子类别按大类展开,一次前向全部完成 # ============================================================================ INFERENCE_GROUPS = { "category": CATEGORY_PROMPTS, "color": COLOR_PROMPTS, "style": STYLE_PROMPTS, "version": FIT_PROMPTS, "material": MATERIAL_PROMPTS, "pattern": PATTERN_PROMPTS, "thickness": THICKNESS_PROMPTS, "sleeve_type": SLEEVE_TYPE_PROMPTS, "clothes_type": CLOTHES_TYPE_PROMPTS, "neckline_type": NECKLINE_PROMPTS, "formality": FORMALITY_PROMPTS, "occasion": OCCASION_PROMPTS, "visual_impact": VISUAL_IMPACT_PROMPTS, } for _main, _subs in SUBCATEGORY_GROUPS.items(): INFERENCE_GROUPS[f"subcategory:{_main}"] = _subs # 副颜色提取阈值:第二颜色置信度达到 min(top1 * 40%, 0.15) 才输出 COLOR_SUB_RATIO_THRESHOLD = 0.40 COLOR_SUB_ABS_THRESHOLD = 0.15 # 适用场合:置信度不低于 top1 的 60%,最多取 3 个 OCCASION_MIN_RATIO = 0.60 OCCASION_MAX_COUNT = 3 # 维度适用性:各维度仅在指定大类下输出,其余品类置空 DIMENSION_APPLICABILITY = { "thickness": {"上装", "下装", "连衣裙", "外套", "鞋"}, "version": {"上装", "下装", "连衣裙", "外套"}, "sleeve_type": {"上装", "连衣裙", "外套"}, "clothes_type": {"上装", "下装", "连衣裙", "外套"}, "neckline_type": {"上装", "连衣裙", "外套"}, } # 1-5 等级映射(中文标签 -> 数字等级字符串) FORMALITY_LEVELS = { "非常休闲": "1", "休闲": "2", "轻正式": "3", "商务正式": "4", "非常正式": "5", } VISUAL_IMPACT_LEVELS = { "低调": "1", "平淡": "2", "适中": "3", "亮眼": "4", "高调": "5", } def _classify_all( model: CLIPModel, processor: CLIPProcessor, image: Image.Image, groups: dict, device: str, ) -> dict: """一次前向完成多组分类。 groups: 组名 -> {中文标签: 英文提示词} 返回: 组名 -> [{"label": 标签, "confidence": 置信度}, ...] 按置信度降序 """ texts = [] boundaries = [] for prompts in groups.values(): start = len(texts) texts.extend(prompts.values()) boundaries.append((start, len(texts))) inputs = processor( text=texts, images=image, return_tensors="pt", padding=True, ).to(device) with torch.inference_mode(): outputs = model(**inputs) logits = outputs.logits_per_image # [1, total_texts] logits = logits.squeeze(0).cpu() results = {} for name, (start, end) in zip(groups.keys(), boundaries): labels = list(groups[name].keys()) probs = torch.softmax(logits[start:end], dim=-1) order = torch.argsort(probs, descending=True) results[name] = [ {"label": labels[int(i)], "confidence": round(float(probs[int(i)]), 4)} for i in order ] return results def _top1(results: list) -> tuple: """取组内 top1 的 (标签, 置信度)。""" return results[0]["label"], results[0]["confidence"] def _extract_color_sub(color_results: list) -> tuple: """从颜色 top-2 提取副颜色,置信度过低则返回空。""" if len(color_results) < 2: return "", 0.0 top_conf = color_results[0]["confidence"] sub = color_results[1] threshold = min(top_conf * COLOR_SUB_RATIO_THRESHOLD, COLOR_SUB_ABS_THRESHOLD) if sub["confidence"] >= threshold: return sub["label"], sub["confidence"] return "", 0.0 def _pick_occasions(occasion_results: list) -> str: """取置信度不低于 top1 一定比例的场合,逗号连接。""" top_conf = occasion_results[0]["confidence"] picked = [ r["label"] for r in occasion_results if r["confidence"] >= top_conf * OCCASION_MIN_RATIO ][:OCCASION_MAX_COUNT] if not picked: picked = [occasion_results[0]["label"]] return ",".join(picked) # ---------- 模型加载 ---------- MODEL_NAME = "patrickjohncyh/fashion-clip" device = "cuda" if torch.cuda.is_available() else "cpu" # 模型缓存走 HuggingFace 默认目录(~/.cache/huggingface) logger.info(f"Loading {MODEL_NAME} on {device} ...") try: model = CLIPModel.from_pretrained(MODEL_NAME, local_files_only=True) processor = CLIPProcessor.from_pretrained(MODEL_NAME, local_files_only=True) logger.info(f"{MODEL_NAME} 命中本地缓存,离线加载完成") except (OSError, ValueError) as e: logger.info(f"本地未找到 {MODEL_NAME} 缓存,自动从 HuggingFace 下载 ... ({e})") model = CLIPModel.from_pretrained(MODEL_NAME) processor = CLIPProcessor.from_pretrained(MODEL_NAME) logger.info(f"{MODEL_NAME} 下载完成") model = model.to(device) model.eval() if device == "cuda": model.half() logger.info(f"Fashion-CLIP loaded on {device}") @app.post("/classify") async def classify(file: UploadFile): """对上传的衣物图片进行分类,返回与 clothes 表模型对齐的 15 个维度。 Response: category_main: 衣物大类(上装/下装/连衣裙/外套/鞋/配饰) category_sub: 子类别(大类对应组内 top1) style: 风格 color_main: 主颜色 color_sub: 副颜色(top-2 置信度达标才输出,否则空字符串) thickness: 厚度(薄/适中/厚/加厚) version: 版型(宽松/修身/阔腿/紧身/常规合身/直筒) material: 材质 pattern: 花纹 sleeve_type: 袖型(仅上装/连衣裙/外套,其余置空) clothes_type: 衣长类型(短款/常规/中长/长款/超长款) neckline_type: 领型(仅上装/连衣裙/外套,其余置空) formality: 正式程度 "1"~"5"(附 formality_label 中文标签) occasions: 适用场合(top-N 逗号连接) visual_impact: 视觉冲击力 "1"~"5"(附 visual_impact_label 中文标签) details: 各维度 Top-5 明细 """ raw = await file.read() try: image = Image.open(io.BytesIO(raw)) # 仅含透明通道的图片(PNG 等)合成白底,避免透明通道干扰 CLIP;普通 JPEG 直接转 RGB if image.mode in ("RGBA", "LA") or (image.mode == "P" and "transparency" in image.info): image = image.convert("RGBA") bg = Image.new("RGBA", image.size, (255, 255, 255, 255)) image = Image.alpha_composite(bg, image) image = image.convert("RGB") except Exception: raise HTTPException(status_code=400, detail="invalid image") # 1. 一次前向完成全部分类 results = _classify_all(model, processor, image, INFERENCE_GROUPS, device) # 2. 大类 best_main, best_main_conf = _top1(results["category"]) # 3. 子类别:大类对应组内 top1 sub_results = results.get(f"subcategory:{best_main}", []) if sub_results: best_sub, best_sub_conf = _top1(sub_results) else: best_sub, best_sub_conf = "", 0.0 # 4. 颜色:主色 + 副色(top-2 阈值提取) color_results = results["color"] best_color, best_color_conf = _top1(color_results) best_color_sub, best_color_sub_conf = _extract_color_sub(color_results) # 5. 风格 / 材质 / 花纹 best_style, best_style_conf = _top1(results["style"]) best_material, best_material_conf = _top1(results["material"]) best_pattern, best_pattern_conf = _top1(results["pattern"]) # 6. 适用性控制:不适用维度置空 def pick_or_empty(dim: str) -> tuple: if best_main in DIMENSION_APPLICABILITY.get(dim, set()): return _top1(results[dim]) return "", 0.0 best_thickness, best_thickness_conf = pick_or_empty("thickness") best_version, best_version_conf = pick_or_empty("version") best_sleeve, best_sleeve_conf = pick_or_empty("sleeve_type") best_clothes_type, best_clothes_type_conf = pick_or_empty("clothes_type") best_neckline, best_neckline_conf = pick_or_empty("neckline_type") # 7. 正式程度 1-5 formality_label, formality_conf = _top1(results["formality"]) formality = FORMALITY_LEVELS.get(formality_label, "") # 8. 适用场合 top-N occasions = _pick_occasions(results["occasion"]) # 9. 视觉冲击力 1-5 impact_label, impact_conf = _top1(results["visual_impact"]) impact = VISUAL_IMPACT_LEVELS.get(impact_label, "") def detail(name: str) -> list: return results.get(name, [])[:5] return { "category_main": best_main, "category_main_confidence": best_main_conf, "category_sub": best_sub, "category_sub_confidence": best_sub_conf, "style": best_style, "style_confidence": best_style_conf, "color_main": best_color, "color_main_confidence": best_color_conf, "color_sub": best_color_sub, "color_sub_confidence": best_color_sub_conf, "thickness": best_thickness, "thickness_confidence": best_thickness_conf, "version": best_version, "version_confidence": best_version_conf, "material": best_material, "material_confidence": best_material_conf, "pattern": best_pattern, "pattern_confidence": best_pattern_conf, "sleeve_type": best_sleeve, "sleeve_type_confidence": best_sleeve_conf, "clothes_type": best_clothes_type, "clothes_type_confidence": best_clothes_type_conf, "neckline_type": best_neckline, "neckline_type_confidence": best_neckline_conf, "formality": formality, "formality_label": formality_label, "formality_confidence": formality_conf, "occasions": occasions, "visual_impact": impact, "visual_impact_label": impact_label, "visual_impact_confidence": impact_conf, "details": { "category_main": detail("category"), "category_sub": sub_results[:5], "style": detail("style"), "color_main": color_results[:5], "thickness": detail("thickness"), "version": detail("version"), "material": detail("material"), "pattern": detail("pattern"), "sleeve_type": detail("sleeve_type"), "clothes_type": detail("clothes_type"), "neckline_type": detail("neckline_type"), "formality": detail("formality"), "occasions": detail("occasion"), "visual_impact": detail("visual_impact"), }, } @app.get("/healthz") def healthz(): return {"status": "ok"} if __name__ == "__main__": # 模型已在模块 import 时于模块级加载,这里直接传 app 对象启动, # 不要用 "main:app" 字符串 + reload 方式,否则会重复加载模型。 import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)