| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988 |
- import { Injectable } from '@angular/core';
- import { FmodeObject, FmodeParse } from 'fmode-ng/parse';
- const Parse = FmodeParse.with('nova');
- /**
- * 图片分析结果接口
- */
- export interface ImageAnalysisResult {
- // 基础信息
- fileName: string;
- fileSize: number;
- dimensions: {
- width: number;
- height: number;
- };
-
- // 质量评估
- quality: {
- score: number; // 0-100分
- level: 'low' | 'medium' | 'high' | 'ultra'; // 低、中、高、超高
- sharpness: number; // 清晰度 0-100
- brightness: number; // 亮度 0-100
- contrast: number; // 对比度 0-100
- detailLevel: 'minimal' | 'basic' | 'detailed' | 'ultra_detailed'; // 🔥 内容精细程度
- pixelDensity: 'low' | 'medium' | 'high' | 'ultra_high'; // 🔥 像素密度等级
- textureQuality: number; // 🔥 纹理质量 0-100
- colorDepth: number; // 🔥 色彩深度 0-100
- };
-
- // 内容分析
- content: {
- category: 'white_model' | 'soft_decor' | 'rendering' | 'post_process' | 'unknown';
- confidence: number; // 置信度 0-100
- description: string; // 内容描述
- tags: string[]; // 标签
- isArchitectural: boolean; // 是否为建筑相关
- hasInterior: boolean; // 是否包含室内场景
- hasFurniture: boolean; // 是否包含家具
- hasLighting: boolean; // 是否有灯光效果
- hasColor?: boolean; // 🔥 是否有色彩(非纯白、非灰度)
- hasTexture?: boolean; // 🔥 是否有材质纹理
- };
-
- // 技术参数
- technical: {
- format: string; // 文件格式
- colorSpace: string; // 色彩空间
- dpi: number; // 分辨率
- aspectRatio: string; // 宽高比
- megapixels: number; // 像素数(百万)
- };
-
- // 建议分类
- suggestedStage: 'white_model' | 'soft_decor' | 'rendering' | 'post_process';
- suggestedReason: string; // 分类原因
-
- // 设计分析维度(新增)
- design?: {
- style: string; // 风格:现代简约、欧式、中式等
- atmosphere: string; // 氛围营造方式
- material: string; // 材质分析
- texture: string; // 纹理特征
- quality: string; // 质感描述
- form: string; // 形体特征
- structure: string; // 空间结构
- colorComposition: string; // 色彩组成
- };
-
- // 色彩解析报告(新增)
- colorReport?: {
- brightness: string; // 明度:低长调/高长调分析
- hue: string; // 色相:色彩种类
- saturation: string; // 饱和度:低/中/高
- openness: string; // 色彩开放度:根据色相种类划分
- extractedColors: string[]; // 提取的基础颜色
- organizedColors: Array<{ color: string; role: string; percentage: number }>; // 组织颜色(主次)
- expandedColors: string[]; // 拓展颜色(配色)
- harmonizedColors: string[]; // 调和颜色(配色)
- };
- // 风格元素分析(新增)
- styleElements?: {
- styleKeywords: string[]; // 风格关键词(如:新古典主义、现代轻奢、艺术装饰等)
- };
- // 色彩搭配分析(新增)
- colorScheme?: {
- primaryColors: string[]; // 主色调
- secondaryColors: string[]; // 辅助色
- accentColors: string[]; // 点缀色
- };
- // 材质分析(新增)
- materialAnalysis?: {
- materials: string[]; // 识别的材质(大理石、玻璃、布、金属等)
- };
- // 布局特征分析(新增)
- layoutFeatures?: {
- features: string[]; // 布局特征(对称式布局、多区域采光、开放式空间等)
- };
- // 空间氛围分析(新增)
- atmosphereAnalysis?: {
- atmosphere: string; // 空间氛围描述
- };
-
- // AI分析时间
- analysisTime: number; // 分析耗时(毫秒)
- analysisDate: string; // 分析时间
- }
- /**
- * 图片分析服务
- * 基于豆包1.6模型进行图片内容识别和质量评估
- */
- @Injectable({
- providedIn: 'root'
- })
- export class ImageAnalysisService {
-
- // 使用豆包1.6模型
- private readonly MODEL = 'fmode-1.6-cn';
-
- constructor() {}
- /**
- * 动态加载 completionJSON,避免编译路径不兼容
- */
- private async callCompletionJSON(
- prompt: string,
- outputSchema: string,
- onStream?: (content: any) => void,
- retry: number = 2,
- options: any = {}
- ): Promise<any> {
- const mod = await import('fmode-ng/core/agent/chat/completion');
- const completionJSON = (mod as any).completionJSON as (
- prompt: string,
- output: string,
- onStream?: (content: any) => void,
- retry?: number,
- options?: any
- ) => Promise<any>;
- return completionJSON(prompt, outputSchema, onStream, retry, options);
- }
- /**
- * 分析单张图片
- */
- async analyzeImage(
- imageUrl: string,
- file: File,
- onProgress?: (progress: string) => void
- ): Promise<ImageAnalysisResult> {
- const startTime = Date.now();
-
- try {
- onProgress?.('正在分析图片内容...');
-
- // 获取图片基础信息
- const basicInfo = await this.getImageBasicInfo(file);
-
- onProgress?.('正在进行AI内容识别...');
-
- // 使用豆包1.6进行内容分析
- const contentAnalysis = await this.analyzeImageContent(imageUrl);
-
- onProgress?.('正在评估图片质量...');
-
- // 质量评估
- const qualityAnalysis = await this.analyzeImageQuality(imageUrl, basicInfo);
-
- onProgress?.('正在生成分析报告...');
-
- // 综合分析结果
- const result: ImageAnalysisResult = {
- fileName: file.name,
- fileSize: file.size,
- dimensions: basicInfo.dimensions,
- quality: qualityAnalysis,
- content: contentAnalysis,
- technical: {
- format: file.type,
- colorSpace: 'sRGB', // 默认值,实际可通过更深入分析获得
- dpi: basicInfo.dpi || 72,
- aspectRatio: this.calculateAspectRatio(basicInfo.dimensions.width, basicInfo.dimensions.height),
- megapixels: Math.round((basicInfo.dimensions.width * basicInfo.dimensions.height) / 1000000 * 100) / 100
- },
- suggestedStage: this.determineSuggestedStage(contentAnalysis, qualityAnalysis),
- suggestedReason: this.generateSuggestionReason(contentAnalysis, qualityAnalysis),
- analysisTime: Date.now() - startTime,
- analysisDate: new Date().toISOString()
- };
-
- onProgress?.('分析完成');
-
- return result;
-
- } catch (error) {
- console.error('图片分析失败:', error);
- throw new Error('图片分析失败: ' + (error as Error).message);
- }
- }
- /**
- * 获取图片基础信息
- */
- private async getImageBasicInfo(file: File): Promise<{
- dimensions: { width: number; height: number };
- dpi?: number;
- }> {
- return new Promise((resolve, reject) => {
- const img = new Image();
- img.onload = () => {
- resolve({
- dimensions: {
- width: img.naturalWidth,
- height: img.naturalHeight
- },
- dpi: 72 // 默认DPI,实际项目中可以通过EXIF数据获取
- });
- };
- img.onerror = () => reject(new Error('无法加载图片'));
- img.src = URL.createObjectURL(file);
- });
- }
- /**
- * 使用豆包1.6分析图片内容
- */
- private async analyzeImageContent(imageUrl: string): Promise<ImageAnalysisResult['content']> {
- const prompt = `请分析这张室内设计相关的图片,并按以下JSON格式输出分析结果:
- {
- "category": "图片类别(white_model/soft_decor/rendering/post_process/unknown)",
- "confidence": "置信度(0-100)",
- "description": "详细的内容描述",
- "tags": ["标签1", "标签2", "标签3"],
- "isArchitectural": "是否为建筑相关(true/false)",
- "hasInterior": "是否包含室内场景(true/false)",
- "hasFurniture": "是否包含家具(true/false)",
- "hasLighting": "是否有灯光效果(true/false)",
- "hasColor": "是否有色彩(非纯白、非灰度)(true/false)",
- "hasTexture": "是否有材质纹理(true/false)"
- }
- 分类标准:
- - white_model: 白模、线框图、基础建模、结构图(特征:纯白色或灰色、无色彩、无材质、无家具、无灯光)
- - soft_decor: 软装搭配、家具配置、装饰品、材质贴图(特征:有家具、有色彩、有材质、但灯光不突出)
- - rendering: 渲染图、效果图、光影表现、材质细节(特征:有灯光效果、有色彩、有材质、质量较高)
- - post_process: 后期处理、色彩调整、特效、最终成品(特征:完整场景、精致色彩、专业质量)
- 重要判断依据:
- 1. 如果图片是纯白色或灰色草图,无任何色彩 → 白模
- 2. 如果图片有丰富色彩和材质 → 不是白模
- 3. 如果图片有灯光效果 → 渲染或后期
- 4. 如果图片有家具但无灯光 → 软装
- 要求:
- 1. 准确识别图片中的设计元素
- 2. 特别注意图片是否有色彩(区分白模和渲染图的关键)
- 3. 判断图片的制作阶段和用途
- 4. 提取关键的视觉特征`;
- const output = `{
- "category": "white_model",
- "confidence": 85,
- "description": "室内空间白模图,显示了基础的空间结构",
- "tags": ["白模", "室内", "结构"],
- "isArchitectural": true,
- "hasInterior": true,
- "hasFurniture": false,
- "hasLighting": false,
- "hasColor": false,
- "hasTexture": false
- }`;
- try {
- const result = await this.callCompletionJSON(
- prompt,
- output,
- (content) => {
- console.log('AI分析进度:', content?.length || 0);
- },
- 2,
- {
- model: this.MODEL,
- vision: true,
- images: [imageUrl]
- }
- );
- return result || {
- category: 'unknown',
- confidence: 0,
- description: '无法识别图片内容',
- tags: [],
- isArchitectural: false,
- hasInterior: false,
- hasFurniture: false,
- hasLighting: false
- };
- } catch (error) {
- console.error('内容分析失败:', error);
- return {
- category: 'unknown',
- confidence: 0,
- description: '内容分析失败',
- tags: [],
- isArchitectural: false,
- hasInterior: false,
- hasFurniture: false,
- hasLighting: false
- };
- }
- }
- /**
- * 🔥 分析图片质量(增强版:包含精细度和像素密度)
- */
- private async analyzeImageQuality(
- imageUrl: string,
- basicInfo: { dimensions: { width: number; height: number } }
- ): Promise<ImageAnalysisResult['quality']> {
- const prompt = `请分析这张室内设计图片的质量,并按以下JSON格式输出:
- {
- "score": "总体质量分数(0-100)",
- "level": "质量等级(low/medium/high/ultra)",
- "sharpness": "清晰度(0-100)",
- "brightness": "亮度(0-100)",
- "contrast": "对比度(0-100)",
- "textureQuality": "纹理质量(0-100)",
- "colorDepth": "色彩深度(0-100)"
- }
- 评估标准:
- - score: 综合质量评分,考虑清晰度、构图、色彩、纹理等
- - level: low(<60分), medium(60-75分), high(75-90分), ultra(>90分)
- - sharpness: 图片清晰度,是否有模糊、噪点、锯齿
- - brightness: 亮度是否适中,不过暗或过亮
- - contrast: 对比度是否合适,层次是否分明
- - textureQuality: 纹理质量,材质细节是否清晰(木纹、布纹、石材等)
- - colorDepth: 色彩深度,色彩过渡是否自然,是否有色带
- 请客观评估图片质量,重点关注专业室内设计图片的标准。`;
- const output = `{
- "score": 75,
- "level": "high",
- "sharpness": 80,
- "brightness": 70,
- "contrast": 75,
- "textureQuality": 75,
- "colorDepth": 80
- }`;
- try {
- const result = await this.callCompletionJSON(
- prompt,
- output,
- (content) => {
- console.log('🔍 质量分析进度:', content?.length || 0);
- },
- 2,
- {
- model: this.MODEL,
- vision: true,
- images: [imageUrl]
- }
- );
- // 🔥 结合图片分辨率和像素密度进行质量调整
- const resolutionScore = this.calculateResolutionScore(basicInfo.dimensions);
- const pixelDensity = this.calculatePixelDensity(basicInfo.dimensions);
-
- // 🔥 评估内容精细程度
- const detailLevel = await this.evaluateDetailLevel(imageUrl, basicInfo.dimensions);
-
- // 🔥 综合评分:AI评分(40%) + 分辨率(30%) + 纹理质量(20%) + 色彩深度(10%)
- const adjustedScore = Math.round(
- result.score * 0.4 +
- resolutionScore * 0.3 +
- (result.textureQuality || 50) * 0.2 +
- (result.colorDepth || 50) * 0.1
- );
- console.log('📊 质量分析结果:', {
- AI评分: result.score,
- 分辨率评分: resolutionScore,
- 纹理质量: result.textureQuality,
- 色彩深度: result.colorDepth,
- 综合评分: adjustedScore,
- 像素密度: pixelDensity,
- 精细程度: detailLevel
- });
- return {
- score: adjustedScore,
- level: this.getQualityLevel(adjustedScore),
- sharpness: result.sharpness || 50,
- brightness: result.brightness || 50,
- contrast: result.contrast || 50,
- detailLevel: detailLevel,
- pixelDensity: pixelDensity,
- textureQuality: result.textureQuality || 50,
- colorDepth: result.colorDepth || 50
- };
- } catch (error) {
- console.error('❌ 质量分析失败:', error);
- // 基于分辨率和像素的备选评估
- const resolutionScore = this.calculateResolutionScore(basicInfo.dimensions);
- const pixelDensity = this.calculatePixelDensity(basicInfo.dimensions);
- const megapixels = (basicInfo.dimensions.width * basicInfo.dimensions.height) / 1000000;
-
- // 根据像素推测精细程度
- let detailLevel: 'minimal' | 'basic' | 'detailed' | 'ultra_detailed' = 'basic';
- if (megapixels >= 8) detailLevel = 'ultra_detailed';
- else if (megapixels >= 2) detailLevel = 'detailed';
- else if (megapixels >= 0.9) detailLevel = 'basic';
- else detailLevel = 'minimal';
-
- return {
- score: resolutionScore,
- level: this.getQualityLevel(resolutionScore),
- sharpness: 50,
- brightness: 50,
- contrast: 50,
- detailLevel: detailLevel,
- pixelDensity: pixelDensity,
- textureQuality: resolutionScore * 0.8,
- colorDepth: resolutionScore * 0.9
- };
- }
- }
- /**
- * 🔥 基于分辨率和像素密度计算质量分数(更精细)
- */
- private calculateResolutionScore(dimensions: { width: number; height: number }): number {
- const totalPixels = dimensions.width * dimensions.height;
- const megapixels = totalPixels / 1000000;
-
- // 更精细的像素分级
- if (megapixels >= 33) return 98; // 8K (7680×4320)
- if (megapixels >= 24) return 96; // 6K (6144×3160)
- if (megapixels >= 16) return 94; // 5K (5120×2880)
- if (megapixels >= 8) return 92; // 4K (3840×2160)
- if (megapixels >= 6) return 88; // 2.5K+ (2560×2304)
- if (megapixels >= 4) return 84; // QHD+ (2560×1600)
- if (megapixels >= 2) return 78; // 1080p (1920×1080)
- if (megapixels >= 1) return 68; // 720p+ (1280×720)
- if (megapixels >= 0.5) return 55; // 中等分辨率
- if (megapixels >= 0.3) return 40; // 低分辨率
- return 25; // 极低分辨率
- }
- /**
- * 🔥 计算像素密度等级
- */
- private calculatePixelDensity(dimensions: { width: number; height: number }): 'low' | 'medium' | 'high' | 'ultra_high' {
- const totalPixels = dimensions.width * dimensions.height;
- const megapixels = totalPixels / 1000000;
-
- if (megapixels >= 8) return 'ultra_high'; // 4K及以上
- if (megapixels >= 2) return 'high'; // 1080p及以上
- if (megapixels >= 0.9) return 'medium'; // 720p及以上
- return 'low'; // 低于720p
- }
- /**
- * 🔥 评估内容精细程度
- */
- private async evaluateDetailLevel(
- imageUrl: string,
- dimensions: { width: number; height: number }
- ): Promise<'minimal' | 'basic' | 'detailed' | 'ultra_detailed'> {
- const prompt = `请评估这张室内设计图片的内容精细程度,并返回JSON:
- {
- "detailLevel": "精细程度(minimal/basic/detailed/ultra_detailed)",
- "textureQuality": "纹理质量评分(0-100)",
- "colorDepth": "色彩深度评分(0-100)",
- "reasoning": "评估理由"
- }
- 评估标准:
- - minimal: 极简图,只有基本轮廓,无细节纹理
- - basic: 基础图,有简单纹理和色彩,细节较少
- - detailed: 详细图,有丰富纹理、材质细节、光影效果
- - ultra_detailed: 超精细图,有极致纹理、真实材质、复杂光影、细微细节
- 重点关注:
- 1. 纹理细节(木纹、布纹、石材纹理等)
- 2. 材质表现(金属反射、玻璃透明度、布料质感等)
- 3. 光影效果(阴影、高光、环境光等)
- 4. 细微元素(装饰品细节、边角处理等)`;
- const output = `{
- "detailLevel": "detailed",
- "textureQuality": 75,
- "colorDepth": 80,
- "reasoning": "图片包含丰富的纹理和材质细节"
- }`;
- try {
- const result = await this.callCompletionJSON(
- prompt,
- output,
- undefined,
- 2,
- {
- model: this.MODEL,
- vision: true,
- images: [imageUrl]
- }
- );
- return result.detailLevel || 'basic';
- } catch (error) {
- console.error('精细程度评估失败:', error);
- // 基于分辨率的备选评估
- const megapixels = (dimensions.width * dimensions.height) / 1000000;
- if (megapixels >= 8) return 'ultra_detailed';
- if (megapixels >= 2) return 'detailed';
- if (megapixels >= 0.9) return 'basic';
- return 'minimal';
- }
- }
- /**
- * 获取质量等级
- */
- private getQualityLevel(score: number): 'low' | 'medium' | 'high' | 'ultra' {
- if (score >= 90) return 'ultra';
- if (score >= 75) return 'high';
- if (score >= 60) return 'medium';
- return 'low';
- }
- /**
- * 计算宽高比
- */
- private calculateAspectRatio(width: number, height: number): string {
- const gcd = (a: number, b: number): number => b === 0 ? a : gcd(b, a % b);
- const divisor = gcd(width, height);
- return `${width / divisor}:${height / divisor}`;
- }
- /**
- * 🔥 确定建议的阶段分类(优化版:更精准的判断逻辑)
- */
- private determineSuggestedStage(
- content: ImageAnalysisResult['content'],
- quality: ImageAnalysisResult['quality']
- ): 'white_model' | 'soft_decor' | 'rendering' | 'post_process' {
- // 如果AI已经识别出明确类别且置信度高
- if (content.confidence > 75 && content.category !== 'unknown') {
- return content.category as any;
- }
- // 🔥 综合判断:像素密度 + 内容精细度 + 质量分数 + 特征
- const megapixels = quality.pixelDensity;
- const detailLevel = quality.detailLevel;
- const qualityScore = quality.score;
- const textureQuality = quality.textureQuality;
-
- console.log('🎯 阶段判断依据:', {
- 像素密度: megapixels,
- 精细程度: detailLevel,
- 质量分数: qualityScore,
- 纹理质量: textureQuality,
- 有家具: content.hasFurniture,
- 有灯光: content.hasLighting,
- 有色彩: content.hasColor,
- 有纹理: content.hasTexture
- });
- // 🔥 白模阶段:放宽条件,更准确识别白色/灰色无渲染的图片
- // 修复:默认值应该是false(无色彩/无纹理),而不是true
- const hasColor = content.hasColor === true; // 🔥 修复:只有明确有色彩才为true
- const hasTexture = content.hasTexture === true; // 🔥 修复:只有明确有纹理才为true
-
- // 🔥 白模判断:无装饰 + 无灯光 + 低质量 (放宽色彩和纹理条件)
- if (!content.hasFurniture &&
- !content.hasLighting &&
- qualityScore < 65 && // 🔥 放宽质量要求(原60提升到65)
- !hasColor) { // 🔥 主要看是否有色彩,纹理可以忽略
- console.log('✅ 判定为白模阶段:无装饰 + 无灯光 + 无色彩 + 低质量');
- return 'white_model';
- }
-
- // 🔥 如果质量极低且无装饰,也判定为白模
- if (qualityScore < 50 &&
- !content.hasFurniture &&
- !content.hasLighting) {
- console.log('✅ 判定为白模阶段:极低质量 + 无装饰');
- return 'white_model';
- }
-
- // 🔥 如果有明显色彩或灯光,绝对不是白模
- if (hasColor || content.hasLighting) {
- console.log('✅ 有色彩或灯光,不是白模,继续判断其他阶段');
- }
- // 🔥 软装阶段:有家具 + 无灯光 + 中等质量
- if (content.hasFurniture && !content.hasLighting &&
- qualityScore >= 60 && qualityScore < 80) {
- console.log('✅ 判定为软装阶段:有家具 + 无灯光');
- return 'soft_decor';
- }
- // 🔥 渲染阶段:有灯光 + 高质量 + 详细精细度
- if (content.hasLighting &&
- (detailLevel === 'detailed' || detailLevel === 'ultra_detailed') &&
- qualityScore >= 75 && qualityScore < 90) {
- console.log('✅ 判定为渲染阶段:有灯光 + 高质量 + 详细精细度');
- return 'rendering';
- }
- // 🔥 后期处理阶段:超高质量 + 超精细 + 高纹理质量
- if (qualityScore >= 90 &&
- detailLevel === 'ultra_detailed' &&
- textureQuality >= 85 &&
- (megapixels === 'ultra_high' || megapixels === 'high')) {
- console.log('✅ 判定为后期处理阶段:超高质量 + 超精细');
- return 'post_process';
- }
- // 🔥 渲染阶段:有灯光效果,即使质量不是最高
- if (content.hasLighting && qualityScore >= 70) {
- console.log('✅ 判定为渲染阶段:有灯光效果');
- return 'rendering';
- }
- // 🔥 软装阶段:有家具但质量一般
- if (content.hasFurniture && qualityScore >= 60) {
- console.log('✅ 判定为软装阶段:有家具');
- return 'soft_decor';
- }
- // 🔥 默认:根据质量分数判断(优先渲染,避免误判为白模)
- if (qualityScore >= 85) {
- console.log('✅ 默认判定为后期处理阶段:高质量');
- return 'post_process';
- } else if (qualityScore >= 65) {
- console.log('✅ 默认判定为渲染阶段:中高质量');
- return 'rendering';
- } else if (qualityScore >= 50) {
- console.log('✅ 默认判定为软装阶段:中等质量');
- return 'soft_decor';
- } else if (qualityScore >= 40) {
- console.log('✅ 默认判定为渲染阶段:低质量但有内容');
- return 'rendering'; // 🔥 即使质量低,也优先判定为渲染而非白模
- } else {
- console.log('⚠️ 默认判定为白模阶段:极低质量');
- return 'white_model';
- }
- }
- /**
- * 生成分类建议原因
- */
- private generateSuggestionReason(
- content: ImageAnalysisResult['content'],
- quality: ImageAnalysisResult['quality']
- ): string {
- const reasons = [];
-
- if (content.confidence > 70) {
- reasons.push(`AI识别置信度${content.confidence}%`);
- }
-
- if (quality.score >= 90) {
- reasons.push('图片质量极高,适合最终展示');
- } else if (quality.score >= 75) {
- reasons.push('图片质量良好');
- } else if (quality.score < 60) {
- reasons.push('图片质量较低,可能为初期阶段');
- }
-
- if (content.hasLighting) {
- reasons.push('包含灯光效果');
- }
-
- if (content.hasFurniture) {
- reasons.push('包含家具配置');
- }
-
- if (!content.hasFurniture && !content.hasLighting) {
- reasons.push('基础结构图,无装饰元素');
- }
-
- return reasons.join(',') || '基于图片特征综合判断';
- }
- /**
- * 批量分析图片
- */
- async analyzeImages(
- files: { file: File; url: string }[],
- onProgress?: (current: number, total: number, currentFileName: string) => void
- ): Promise<ImageAnalysisResult[]> {
- const results: ImageAnalysisResult[] = [];
-
- for (let i = 0; i < files.length; i++) {
- const { file, url } = files[i];
- onProgress?.(i + 1, files.length, file.name);
-
- try {
- const result = await this.analyzeImage(url, file);
- results.push(result);
- } catch (error) {
- console.error(`分析文件 ${file.name} 失败:`, error);
- // 创建一个基础的错误结果
- results.push(this.createErrorResult(file, error as Error));
- }
- }
-
- return results;
- }
- /**
- * 创建错误结果
- */
- private createErrorResult(file: File, error: Error): ImageAnalysisResult {
- return {
- fileName: file.name,
- fileSize: file.size,
- dimensions: { width: 0, height: 0 },
- quality: {
- score: 0,
- level: 'low',
- sharpness: 0,
- brightness: 0,
- contrast: 0,
- detailLevel: 'minimal',
- pixelDensity: 'low',
- textureQuality: 0,
- colorDepth: 0
- },
- content: {
- category: 'unknown',
- confidence: 0,
- description: `分析失败: ${error.message}`,
- tags: [],
- isArchitectural: false,
- hasInterior: false,
- hasFurniture: false,
- hasLighting: false
- },
- technical: {
- format: file.type,
- colorSpace: 'unknown',
- dpi: 0,
- aspectRatio: '0:0',
- megapixels: 0
- },
- suggestedStage: 'white_model',
- suggestedReason: '分析失败,默认分类',
- analysisTime: 0,
- analysisDate: new Date().toISOString()
- };
- }
- /**
- * 保存分析结果到数据库
- */
- async saveAnalysisResult(
- projectId: string,
- spaceId: string,
- stageType: string,
- analysisResult: ImageAnalysisResult
- ): Promise<void> {
- try {
- // 查询项目
- const projectQuery = new Parse.Query('Project');
- const project = await projectQuery.get(projectId);
- if (!project) {
- throw new Error('项目不存在');
- }
- // 获取现有的date数据
- const dateData = project.get('date') || {};
- // 初始化图片分析数据结构
- if (!dateData.imageAnalysis) {
- dateData.imageAnalysis = {};
- }
- if (!dateData.imageAnalysis[spaceId]) {
- dateData.imageAnalysis[spaceId] = {};
- }
- if (!dateData.imageAnalysis[spaceId][stageType]) {
- dateData.imageAnalysis[spaceId][stageType] = [];
- }
- // 添加分析结果
- dateData.imageAnalysis[spaceId][stageType].push(analysisResult);
- // 保存到项目
- project.set('date', dateData);
- await project.save();
- console.log('图片分析结果已保存到项目数据库');
- } catch (error) {
- console.error('保存分析结果失败:', error);
- throw error;
- }
- }
- /**
- * 🔥 快速生成模拟分析结果(用于开发测试)
- * 根据文件名和空间名称快速生成分析结果,无需调用AI
- */
- generateMockAnalysisResult(
- file: File,
- spaceName?: string,
- stageName?: string
- ): ImageAnalysisResult {
- const fileName = file.name.toLowerCase();
- const fileSize = file.size;
- let suggestedStage: 'white_model' | 'soft_decor' | 'rendering' | 'post_process' = 'white_model';
- let category: 'white_model' | 'soft_decor' | 'rendering' | 'post_process' | 'unknown' = 'white_model';
- let confidence = 75;
- let analysisReason = '基于文件名和特征分析';
- // 增强的关键词匹配
- const stageKeywords = {
- white_model: ['白模', 'white', 'model', '毛坯', '空间', '结构', '框架', '基础'],
- soft_decor: ['软装', 'soft', 'decor', '家具', 'furniture', '装饰', '饰品', '布艺'],
- rendering: ['渲染', 'render', '效果', 'effect', '光照', '材质', '质感'],
- post_process: ['后期', 'post', 'final', '最终', '完成', '成品', '精修', '调色']
- };
- // 分析文件名匹配度
- let maxMatchScore = 0;
- let bestStage = 'white_model';
-
- Object.entries(stageKeywords).forEach(([stage, keywords]) => {
- const matchScore = keywords.reduce((score, keyword) => {
- if (fileName.includes(keyword)) {
- return score + (keyword.length > 2 ? 2 : 1); // 长关键词权重更高
- }
- return score;
- }, 0);
-
- if (matchScore > maxMatchScore) {
- maxMatchScore = matchScore;
- bestStage = stage as typeof suggestedStage;
- }
- });
- if (maxMatchScore > 0) {
- suggestedStage = bestStage as 'white_model' | 'soft_decor' | 'rendering' | 'post_process';
- category = bestStage as 'white_model' | 'soft_decor' | 'rendering' | 'post_process';
- confidence = Math.min(75 + maxMatchScore * 5, 95);
- analysisReason = `文件名包含${this.getStageName(bestStage)}相关关键词`;
- }
- // 文件大小分析
- if (fileSize > 8 * 1024 * 1024) { // 大于8MB
- if (suggestedStage === 'white_model') {
- suggestedStage = 'rendering';
- category = 'rendering';
- confidence = Math.min(confidence + 10, 95);
- analysisReason += ',大文件更可能是高质量渲染图';
- }
- } else if (fileSize < 500 * 1024) { // 小于500KB
- if (suggestedStage === 'post_process') {
- suggestedStage = 'white_model';
- category = 'white_model';
- confidence = Math.max(confidence - 10, 60);
- analysisReason += ',小文件更可能是简单的白模图';
- }
- }
- // 根据目标阶段调整
- if (stageName) {
- const targetStageMap: Record<string, typeof suggestedStage> = {
- '白模': 'white_model',
- '软装': 'soft_decor',
- '渲染': 'rendering',
- '后期': 'post_process'
- };
-
- const targetStage = targetStageMap[stageName];
- if (targetStage && targetStage === suggestedStage) {
- confidence = Math.min(confidence + 15, 98);
- analysisReason += `,与目标阶段一致`;
- }
- }
- const qualityScoreMap = {
- 'white_model': 75,
- 'soft_decor': 82,
- 'rendering': 88,
- 'post_process': 95
- };
- const score = qualityScoreMap[suggestedStage];
- // 生成模拟分析结果
- const result: ImageAnalysisResult = {
- fileName: file.name,
- fileSize: file.size,
- dimensions: {
- width: 1920 + Math.floor(Math.random() * 400), // 模拟不同尺寸
- height: 1080 + Math.floor(Math.random() * 300)
- },
- quality: {
- score: score,
- level: this.getQualityLevel(score),
- sharpness: Math.min(score + Math.floor(Math.random() * 10), 100),
- brightness: Math.max(score - Math.floor(Math.random() * 10), 50),
- contrast: Math.min(score + Math.floor(Math.random() * 8), 100),
- detailLevel: score >= 90 ? 'ultra_detailed' : score >= 75 ? 'detailed' : score >= 60 ? 'basic' : 'minimal',
- pixelDensity: score >= 90 ? 'ultra_high' : score >= 75 ? 'high' : score >= 60 ? 'medium' : 'low',
- textureQuality: Math.min(score + Math.floor(Math.random() * 5), 100),
- colorDepth: Math.min(score + Math.floor(Math.random() * 5), 100)
- },
- content: {
- category: category,
- confidence: confidence,
- description: `${spaceName || '室内空间'}${this.getStageName(suggestedStage)}图`,
- tags: this.generateTags(suggestedStage, spaceName),
- isArchitectural: true,
- hasInterior: true,
- hasFurniture: suggestedStage !== 'white_model',
- hasLighting: suggestedStage === 'rendering' || suggestedStage === 'post_process'
- },
- technical: {
- format: file.type,
- colorSpace: Math.random() > 0.8 ? 'Adobe RGB' : 'sRGB',
- dpi: Math.random() > 0.5 ? 300 : 72,
- aspectRatio: this.calculateAspectRatio(1920, 1080),
- megapixels: Math.round(((1920 * 1080) / 1000000) * 100) / 100
- },
- suggestedStage: suggestedStage,
- suggestedReason: analysisReason,
- analysisTime: 50 + Math.floor(Math.random() * 100),
- analysisDate: new Date().toISOString()
- };
- console.log(`🚀 增强模拟分析结果: ${file.name} -> ${this.getStageName(suggestedStage)}`, {
- confidence: confidence,
- reason: analysisReason,
- fileSize: `${(fileSize / 1024 / 1024).toFixed(1)}MB`
- });
- return result;
- }
- /**
- * 生成标签
- */
- private generateTags(stage: string, spaceName?: string): string[] {
- const baseTags = [this.getStageName(stage), spaceName || '室内', '设计'];
-
- const stageSpecificTags: Record<string, string[]> = {
- 'white_model': ['建筑', '结构', '空间布局'],
- 'soft_decor': ['家具', '装饰', '色彩搭配'],
- 'rendering': ['渲染', '光影', '材质'],
- 'post_process': ['后期', '色彩调整', '成品']
- };
-
- return [...baseTags, ...(stageSpecificTags[stage] || [])];
- }
- /**
- * 获取阶段名称
- */
- private getStageName(stageType: string): string {
- const stageMap: { [key: string]: string } = {
- 'white_model': '白模',
- 'soft_decor': '软装',
- 'rendering': '渲染',
- 'post_process': '后期'
- };
- return stageMap[stageType] || stageType;
- }
- }
|