UNPKG

@chenlei28188/image-processor-mcp

Version:

MCP Server for intelligent image processing and analysis

164 lines 6.02 kB
import sharp from 'sharp'; import Tesseract from 'tesseract.js'; import { ImageUtils } from '../utils/ImageUtils.js'; export class ImageAnalyzer { logger; constructor(logger) { this.logger = logger; } async analyze(source, options = {}) { this.logger.info('Starting image analysis', { source: source.substring(0, 50) + '...' }); try { const imageBuffer = await ImageUtils.loadImage(source); const metadata = await this.analyzeMetadata(imageBuffer); const result = { metadata, analysis: this.analyzeImageProperties(metadata), timestamp: new Date().toISOString() }; // 颜色分析 if (options.includeColors !== false) { result.colors = await this.analyzeColors(imageBuffer); } // OCR文字识别 if (options.includeOCR !== false) { result.ocr = await this.performOCR(imageBuffer, options.ocrLanguage || 'chi_sim+eng'); } this.logger.info('Image analysis completed successfully'); return result; } catch (error) { this.logger.error('Image analysis failed:', error); throw new Error(`Image analysis failed: ${error instanceof Error ? error.message : String(error)}`); } } async analyzeMetadata(imageBuffer) { const image = sharp(imageBuffer); const metadata = await image.metadata(); const stats = await image.stats(); return { width: metadata.width || 0, height: metadata.height || 0, format: metadata.format || 'unknown', size: imageBuffer.length, density: metadata.density, hasAlpha: metadata.hasAlpha || false, colorSpace: metadata.space || 'unknown', channels: metadata.channels || 0 }; } analyzeImageProperties(metadata) { const { width, height, size } = metadata; const aspectRatio = width / height; let orientation; if (aspectRatio > 1.1) orientation = 'landscape'; else if (aspectRatio < 0.9) orientation = 'portrait'; else orientation = 'square'; let quality; const totalPixels = width * height; if (totalPixels < 100000) quality = 'low'; else if (totalPixels < 2000000) quality = 'medium'; else quality = 'high'; let complexity; const bytesPerPixel = size / totalPixels; if (bytesPerPixel < 1) complexity = 'simple'; else if (bytesPerPixel < 3) complexity = 'moderate'; else complexity = 'complex'; return { aspectRatio: Math.round(aspectRatio * 100) / 100, orientation, quality, complexity }; } async analyzeColors(imageBuffer) { try { const image = sharp(imageBuffer); const stats = await image.stats(); // 获取主色调 const channels = stats.channels; const dominantColors = channels.map((channel) => Math.round(channel.mean).toString(16).padStart(2, '0')); const averageColor = `#${dominantColors.join('')}`; // 简化的调色板分析 const resized = await image.resize(50, 50).raw().toBuffer(); const palette = this.extractPalette(resized); return { dominant: [averageColor], palette, averageColor }; } catch (error) { this.logger.warn('Color analysis failed:', error); return { dominant: ['#000000'], palette: [{ color: '#000000', percentage: 100 }], averageColor: '#000000' }; } } extractPalette(buffer) { const colorMap = new Map(); for (let i = 0; i < buffer.length; i += 3) { const r = buffer[i]; const g = buffer[i + 1]; const b = buffer[i + 2]; const color = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; colorMap.set(color, (colorMap.get(color) || 0) + 1); } const totalPixels = buffer.length / 3; const sortedColors = Array.from(colorMap.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 5) .map(([color, count]) => ({ color, percentage: Math.round((count / totalPixels) * 100) })); return sortedColors; } async performOCR(imageBuffer, language) { try { this.logger.info('Starting OCR recognition', { language }); const { data } = await Tesseract.recognize(imageBuffer, language, { logger: (m) => { if (m.status === 'recognizing text') { this.logger.debug(`OCR Progress: ${Math.round(m.progress * 100)}%`); } } }); const words = data.words.map(word => ({ text: word.text, confidence: Math.round(word.confidence), bbox: { x: word.bbox.x0, y: word.bbox.y0, width: word.bbox.x1 - word.bbox.x0, height: word.bbox.y1 - word.bbox.y0 } })); return { text: data.text.trim(), confidence: Math.round(data.confidence), words }; } catch (error) { this.logger.warn('OCR recognition failed:', error); return { text: '', confidence: 0, words: [] }; } } } //# sourceMappingURL=ImageAnalyzer.js.map