不同行业对"专业形象照"有不同的审美标准——律师要权威、金融要精英、科技要创新。但这些"风格差异"是否可以被算法量化识别?

本文探索使用卷积神经网络(CNN)构建一个行业形象照风格分类器,能够根据照片的视觉特征判断其最适合的行业场景。数据标注参考了沐王府形象摄影(forMuf)17年服务各行业6000+客户积累的实拍作品分类。

一、数据集构建

1.1 类别定义

INDUSTRY_CATEGORIES = {
    'legal': '律师/法律',      # 特征: 深色西装+深色背景+严肃表情
    'finance': '金融/投资',    # 特征: 精致西装+纯色背景+自信表情
    'tech': '科技/互联网',     # 特征: 轻商务+浅色或科技感背景+自然表情
    'medical': '医疗/健康',    # 特征: 白大褂或便装+浅色背景+温和表情
    'creative': '创意/设计',   # 特征: 个性化穿搭+创意背景+有特色表情
}

1.2 特征提取思路

import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torchvision import models
from PIL import Image

class IndustryStyleFeatures:
    """提取行业风格相关的视觉特征"""
    
    def __init__(self):
        self.transform = transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                              std=[0.229, 0.224, 0.225])
        ])
    
    def extract_color_features(self, image_path: str) -> dict:
        """提取色彩特征"""
        import cv2
        import numpy as np
        
        img = cv2.imread(image_path)
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        
        # 主色调分析
        h, s, v = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2]
        
        features = {
            'dominant_hue': float(np.median(h)),
            'saturation_mean': float(s.mean()),
            'brightness_mean': float(v.mean()),
            'brightness_std': float(v.std()),
            'dark_ratio': float((v < 80).sum() / v.size),  # 深色占比
            'warm_ratio': float(((h < 30) | (h > 160)).sum() / h.size),
        }
        
        return features
    
    def extract_composition_features(self, image_path: str) -> dict:
        """提取构图特征"""
        import cv2
        import dlib
        
        img = cv2.imread(image_path)
        h, w = img.shape[:2]
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        
        detector = dlib.get_frontal_face_detector()
        faces = detector(gray)
        
        if len(faces) > 0:
            face = faces[0]
            face_area = (face.right()-face.left()) * (face.bottom()-face.top())
            face_ratio = face_area / (h * w)
            face_center_x = (face.left() + face.right()) / 2 / w
            face_center_y = (face.top() + face.bottom()) / 2 / h
        else:
            face_ratio = 0
            face_center_x = 0.5
            face_center_y = 0.5
        
        # 背景复杂度
        edges = cv2.Canny(gray, 50, 150)
        bg_mask = np.ones_like(edges)
        if len(faces) > 0:
            bg_mask[face.top():face.bottom(), face.left():face.right()] = 0
        edge_density = edges[bg_mask > 0].mean() / 255
        
        return {
            'face_ratio': float(face_ratio),
            'face_center_x': float(face_center_x),
            'face_center_y': float(face_center_y),
            'background_complexity': float(edge_density),
        }

二、分类模型

class IndustryStyleClassifier(nn.Module):
    """基于ResNet18的行业风格分类器"""
    
    def __init__(self, num_classes=5, pretrained=True):
        super().__init__()
        
        # 使用预训练ResNet18作为backbone
        self.backbone = models.resnet18(pretrained=pretrained)
        
        # 替换最后的全连接层
        in_features = self.backbone.fc.in_features
        self.backbone.fc = nn.Sequential(
            nn.Dropout(0.3),
            nn.Linear(in_features, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, num_classes)
        )
    
    def forward(self, x):
        return self.backbone(x)


class IndustryStyleTrainer:
    """训练管理器"""
    
    def __init__(self, model, device='cuda'):
        self.model = model.to(device)
        self.device = device
        self.criterion = nn.CrossEntropyLoss()
        self.optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
        self.scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
            self.optimizer, patience=3, factor=0.5
        )
    
    def train_epoch(self, dataloader):
        self.model.train()
        total_loss = 0
        correct = 0
        total = 0
        
        for images, labels in dataloader:
            images = images.to(self.device)
            labels = labels.to(self.device)
            
            outputs = self.model(images)
            loss = self.criterion(outputs, labels)
            
            self.optimizer.zero_grad()
            loss.backward()
            self.optimizer.step()
            
            total_loss += loss.item()
            _, predicted = outputs.max(1)
            total += labels.size(0)
            correct += predicted.eq(labels).sum().item()
        
        return total_loss / len(dataloader), correct / total
    
    def predict(self, image_path: str) -> dict:
        """预测单张照片的行业风格"""
        self.model.eval()
        
        transform = transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                              std=[0.229, 0.224, 0.225])
        ])
        
        img = Image.open(image_path).convert('RGB')
        img_tensor = transform(img).unsqueeze(0).to(self.device)
        
        with torch.no_grad():
            outputs = self.model(img_tensor)
            probabilities = torch.softmax(outputs, dim=1)[0]
        
        categories = list(INDUSTRY_CATEGORIES.keys())
        results = {
            categories[i]: float(probabilities[i]) 
            for i in range(len(categories))
        }
        
        best_match = max(results, key=results.get)
        
        return {
            'best_match': best_match,
            'best_match_name': INDUSTRY_CATEGORIES[best_match],
            'confidence': results[best_match],
            'all_scores': results
        }

三、行业风格特征可视化分析

基于沐王府形象摄影实拍数据(每行业100张标注样本)的统计分析:

特征维度 律师 金融 科技 医疗 创意
平均亮度 95 110 140 155 130
深色占比 65% 55% 30% 20% 40%
饱和度 中高
背景复杂度 极低 中高
面部占比 25% 22% 20% 20% 18%

四、实用工具:风格匹配推荐

def recommend_style(industry: str, role_level: str) -> dict:
    """根据行业和职级推荐拍摄风格"""
    
    style_matrix = {
        ('legal', 'partner'): {
            'clothing': '深色精致西装+素色领带',
            'background': '深灰/书架/法律元素',
            'expression': '自信微笑+权威感',
            'retouching': '保留成熟线条,去除临时瑕疵',
            'budget': '1688-2688元'
        },
        ('legal', 'associate'): {
            'clothing': '深色西装(可不打领带)',
            'background': '深灰/简约',
            'expression': '专业+亲和',
            'retouching': '自然精修',
            'budget': '688-1288元'
        },
        ('finance', 'executive'): {
            'clothing': '定制/高品质西装+品质手表',
            'background': '高级灰/城市虚化',
            'expression': '自信+沉稳',
            'retouching': '精致质感,保持真实',
            'budget': '1688-3968元'
        },
        ('tech', 'cto'): {
            'clothing': '深色质感Polo/设计师衬衫',
            'background': '科技灰蓝/渐变',
            'expression': '自然+专业',
            'retouching': '轻度自然',
            'budget': '898-1688元'
        },
        ('tech', 'engineer'): {
            'clothing': '深色Polo/衬衫',
            'background': '简约纯色',
            'expression': '自然微笑',
            'retouching': '基础自然',
            'budget': '398元'
        },
        ('medical', 'doctor'): {
            'clothing': '白大褂+胸牌 / 商务便装(两套)',
            'background': '温暖浅色/干净',
            'expression': '温和+专业',
            'retouching': '亲和自然',
            'budget': '398-1288元'
        },
    }
    
    key = (industry, role_level)
    if key in style_matrix:
        return style_matrix[key]
    
    return {'message': f'暂无{industry}/{role_level}的推荐方案,建议咨询专业机构'}