UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

358 lines (355 loc) 11.6 kB
import vision from '@google-cloud/vision'; import { CacheService } from './cache-service.js'; import { ErrorHandler } from './error-handler.js'; // @ts-nocheck - Optional Google Cloud Vision dependency class VisionService { constructor(config) { this.config = config; this.client = null; this.cache = new CacheService(config.redis); this.errorHandler = new ErrorHandler(); this.removeBgApiKey = config.removeBg.apiKey; this.initializeClient(); } initializeClient() { try { if (this.config.googleCloud.keyFilename) { this.client = new vision.ImageAnnotatorClient({ keyFilename: this.config.googleCloud.keyFilename }); } else if (this.config.googleCloud.apiKey) { this.client = new vision.ImageAnnotatorClient({ apiEndpoint: 'vision.googleapis.com', credentials: { client_email: 'vision-api@project.iam.gserviceaccount.com', private_key: this.config.googleCloud.apiKey } }); } } catch (error) { console.warn('Google Vision client initialization failed, using fallback:', error); } } async detectFaces(imageBuffer) { const cacheKey = `vision:faces:${this.hashBuffer(imageBuffer)}`; if (this.config.costOptimization.enableCaching) { const cached = await this.cache.get(cacheKey); if (cached) return cached; } try { if (!this.client) { return this.fallbackFaceDetection(); } const [result] = await this.client.faceDetection(imageBuffer); const faces = result.faceAnnotations || []; const detectionResults = faces.map(face => ({ boundingBox: this.convertBoundingPoly(face.boundingPoly), confidence: face.detectionConfidence || 0, emotions: { joy: this.likelihoodToScore(face.joyLikelihood), sorrow: this.likelihoodToScore(face.sorrowLikelihood), anger: this.likelihoodToScore(face.angerLikelihood), surprise: this.likelihoodToScore(face.surpriseLikelihood) }, landmarks: face.landmarks?.map(landmark => ({ type: landmark.type || '', position: landmark.position || { x: 0, y: 0, z: 0 } })) })); if (this.config.costOptimization.enableCaching) { await this.cache.set(cacheKey, detectionResults, 3600); } return detectionResults; } catch (error) { return this.errorHandler.handleWithFallback(error, () => this.fallbackFaceDetection(), { service: 'Vision', operation: 'detectFaces' }); } } async detectObjects(imageBuffer) { const cacheKey = `vision:objects:${this.hashBuffer(imageBuffer)}`; if (this.config.costOptimization.enableCaching) { const cached = await this.cache.get(cacheKey); if (cached) return cached; } try { if (!this.client) { return this.fallbackObjectDetection(); } const [result] = await this.client.objectLocalization(imageBuffer); const objects = result.localizedObjectAnnotations || []; const detectionResults = objects.map(obj => ({ name: obj.name || 'unknown', confidence: obj.score || 0, boundingBox: this.convertBoundingPoly(obj.boundingPoly) })); if (this.config.costOptimization.enableCaching) { await this.cache.set(cacheKey, detectionResults, 3600); } return detectionResults; } catch (error) { return this.errorHandler.handleWithFallback(error, () => this.fallbackObjectDetection(), { service: 'Vision', operation: 'detectObjects' }); } } async extractText(imageBuffer) { const cacheKey = `vision:text:${this.hashBuffer(imageBuffer)}`; if (this.config.costOptimization.enableCaching) { const cached = await this.cache.get(cacheKey); if (cached) return cached; } try { if (!this.client) { return this.fallbackTextExtraction(); } const [result] = await this.client.documentTextDetection(imageBuffer); const fullTextAnnotation = result.fullTextAnnotation; if (!fullTextAnnotation) { return { text: '', confidence: 0, blocks: [] }; } const extractionResult = { text: fullTextAnnotation.text || '', confidence: this.calculateAverageConfidence(fullTextAnnotation.pages ?? undefined), language: result.textAnnotations?.[0]?.locale || undefined, blocks: fullTextAnnotation.pages?.[0]?.blocks?.map(block => ({ text: this.extractBlockText(block), confidence: block.confidence || 0, boundingBox: block.boundingBox })) || [] }; if (this.config.costOptimization.enableCaching) { await this.cache.set(cacheKey, extractionResult, 3600); } return extractionResult; } catch (error) { return this.errorHandler.handleWithFallback(error, () => this.fallbackTextExtraction(), { service: 'Vision', operation: 'extractText' }); } } async analyzeImage(imageBuffer) { const cacheKey = `vision:analysis:${this.hashBuffer(imageBuffer)}`; if (this.config.costOptimization.enableCaching) { const cached = await this.cache.get(cacheKey); if (cached) return cached; } try { if (!this.client) { return this.fallbackImageAnalysis(); } const [result] = await this.client.annotateImage({ image: { content: imageBuffer.toString('base64') }, features: [{ type: 'LABEL_DETECTION', maxResults: 10 }, { type: 'SAFE_SEARCH_DETECTION' }, { type: 'IMAGE_PROPERTIES' }, { type: 'CROP_HINTS', maxResults: 3 }] }); const analysisResult = { labels: result.labelAnnotations?.map(label => ({ description: label.description || '', score: label.score || 0 })) || [], safeSearch: { adult: String(result.safeSearchAnnotation?.adult ?? 'UNKNOWN'), violence: String(result.safeSearchAnnotation?.violence ?? 'UNKNOWN'), medical: String(result.safeSearchAnnotation?.medical ?? 'UNKNOWN') }, colors: result.imagePropertiesAnnotation?.dominantColors?.colors?.map(color => ({ color: { red: color.color?.red || 0, green: color.color?.green || 0, blue: color.color?.blue || 0 }, score: color.score || 0, pixelFraction: color.pixelFraction || 0 })) || [], cropHints: result.cropHintsAnnotation?.cropHints?.map(hint => ({ boundingBox: hint.boundingPoly, confidence: hint.confidence || 0, importanceFraction: hint.importanceFraction || 0 })) }; if (this.config.costOptimization.enableCaching) { await this.cache.set(cacheKey, analysisResult, 3600); } return analysisResult; } catch (error) { return this.errorHandler.handleWithFallback(error, () => this.fallbackImageAnalysis(), { service: 'Vision', operation: 'analyzeImage' }); } } async removeBackground(imageBuffer) { const cacheKey = `vision:removebg:${this.hashBuffer(imageBuffer)}`; if (this.config.costOptimization.enableCaching) { const cached = await this.cache.get(cacheKey); if (cached) return Buffer.from(cached, 'base64'); } try { const formData = new FormData(); // Convert Node Buffer to Blob-compatible data for fetch formData.append('image_file', new Blob([new Uint8Array(imageBuffer)]), 'image.jpg'); formData.append('size', 'auto'); const response = await fetch('https://api.remove.bg/v1.0/removebg', { method: 'POST', headers: { 'X-Api-Key': this.removeBgApiKey }, body: formData }); if (!response.ok) { throw new Error(`Remove.bg API error: ${response.status}`); } const resultBuffer = Buffer.from(await response.arrayBuffer()); if (this.config.costOptimization.enableCaching) { await this.cache.set(cacheKey, resultBuffer.toString('base64'), 3600); } return resultBuffer; } catch (error) { return this.errorHandler.handleWithFallback(error, () => imageBuffer, { service: 'Vision', operation: 'removeBackground' }); } } async generateImageDescription(imageBuffer) { try { const analysis = await this.analyzeImage(imageBuffer); const topLabels = analysis.labels.slice(0, 5); if (topLabels.length === 0) { return 'No description available'; } const labelDescriptions = topLabels.map(l => l.description).join(', '); return `This image contains: ${labelDescriptions}`; } catch (error) { return this.errorHandler.handleWithFallback(error, () => 'Unable to generate image description', { service: 'Vision', operation: 'generateImageDescription' }); } } convertBoundingPoly(boundingPoly) { if (!boundingPoly?.vertices || boundingPoly.vertices.length < 4) { return { left: 0, top: 0, width: 0, height: 0 }; } const vertices = boundingPoly.vertices; const left = Math.min(...vertices.map(v => v.x || 0)); const top = Math.min(...vertices.map(v => v.y || 0)); const right = Math.max(...vertices.map(v => v.x || 0)); const bottom = Math.max(...vertices.map(v => v.y || 0)); return { left, top, width: right - left, height: bottom - top }; } likelihoodToScore(likelihood) { const map = { VERY_UNLIKELY: 0, UNLIKELY: 0.25, POSSIBLE: 0.5, LIKELY: 0.75, VERY_LIKELY: 1 }; return map[likelihood || ''] || 0; } calculateAverageConfidence(pages) { if (!pages || pages.length === 0) return 0; const confidences = []; pages.forEach(page => { page.blocks?.forEach(block => { if (block.confidence) confidences.push(block.confidence); }); }); if (confidences.length === 0) return 0; return confidences.reduce((a, b) => a + b, 0) / confidences.length; } extractBlockText(block) { if (!block.paragraphs) return ''; return block.paragraphs.map(p => p.words?.map(w => w.symbols?.map(s => s.text || '').join('') || '').join(' ') || '').join('\n'); } hashBuffer(buffer) { const crypto = require('crypto'); return crypto.createHash('md5').update(buffer).digest('hex'); } fallbackFaceDetection() { return [{ boundingBox: { left: 100, top: 100, width: 200, height: 200 }, confidence: 0.5, emotions: { joy: 0.5, sorrow: 0, anger: 0, surprise: 0 } }]; } fallbackObjectDetection() { return [{ name: 'object', confidence: 0.5, boundingBox: { left: 0, top: 0, width: 100, height: 100 } }]; } fallbackTextExtraction() { return { text: 'Text extraction unavailable', confidence: 0, blocks: [] }; } fallbackImageAnalysis() { return { labels: [{ description: 'image', score: 0.5 }], safeSearch: { adult: 'UNKNOWN', violence: 'UNKNOWN', medical: 'UNKNOWN' }, colors: [] }; } } export { VisionService }; //# sourceMappingURL=vision-service.js.map