first commit

This commit is contained in:
2026-09-11 01:41:52 +08:00
commit 9f22e6ccae
263 changed files with 28935 additions and 0 deletions
+611
View File
@@ -0,0 +1,611 @@
"""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)
+16
View File
@@ -0,0 +1,16 @@
# ============================================================================
# Fashion-CLIP 衣物分类服务依赖清单
# 安装:pip install -r requirements.txt
#
# 注意:PyPI 默认安装的是 CPU 版 torch。
# 如果你的机器有 NVIDIA GPU,请先按 PyTorch 官网命令单独安装对应 CUDA 版 torch,
# 再执行 pip install -r requirements.txt(已安装的 torch 会被跳过):
# pip install torch --index-url https://download.pytorch.org/whl/cu121
# ============================================================================
torch>=2.0.0
transformers>=4.30.0
fastapi>=0.100.0
uvicorn>=0.23.0
pillow>=9.5.0
python-multipart>=0.0.9 # FastAPI 处理 multipart 表单(/classify 上传图片)必需