612 lines
33 KiB
Python
612 lines
33 KiB
Python
"""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)
|