Electron-vue3-ts-offline/server/utils/imagePreprocessor.js
2025-11-13 18:09:31 +08:00

125 行
4.3 KiB
JavaScript

// server/utils/imagePreprocessor.js
import sharp from 'sharp';
class ImagePreprocessor {
constructor() {
this.tempDir = './temp/processed';
this.logger = {
info: (msg, ...args) => console.log(`🖼️ [预处理] ${msg}`, ...args),
error: (msg, ...args) => console.error(`❌ [预处理] ${msg}`, ...args),
debug: (msg, ...args) => console.debug(`❌ [预处理] ${msg}`, ...args)
};
}
async preprocessWithPadding(imagePath, config) {
const startTime = Date.now();
this.logger.info(`开始预处理: ${imagePath}`);
try {
const metadata = await sharp(imagePath).metadata();
this.logger.info(`原始尺寸: ${metadata.width}x${metadata.height}`);
// 智能填充策略
const { paddingX, paddingY } = this.calculateSmartPadding(metadata);
const paddedWidth = metadata.width + paddingX * 2;
const paddedHeight = metadata.height + paddingY * 2;
this.logger.debug(`添加填充: ${paddingX}x${paddingY}, 新尺寸: ${paddedWidth}x${paddedHeight}`);
const paddedBuffer = await sharp(imagePath)
.extend({
top: paddingY,
bottom: paddingY,
left: paddingX,
right: paddingX,
background: { r: 255, g: 255, b: 255 }
})
.png()
.toBuffer();
const { width, height } = this.resizeForDetection({
width: paddedWidth,
height: paddedHeight
}, config);
const resizedBuffer = await sharp(paddedBuffer)
.resize(width, height)
.png()
.toBuffer();
const processingTime = Date.now() - startTime;
this.logger.info(`预处理完成: ${width}x${height}, 耗时${processingTime}ms`);
return {
processedImage: {
buffer: resizedBuffer,
width, height,
originalWidth: metadata.width,
originalHeight: metadata.height,
paddedWidth, paddedHeight,
paddingX, paddingY,
scaleX: paddedWidth / width,
scaleY: paddedHeight / height
}
};
} catch (error) {
this.logger.error('预处理错误', error);
throw error;
}
}
calculateSmartPadding(metadata) {
const basePadding = 20;
const minPadding = 15;
// 根据图像尺寸动态调整填充
const widthRatio = Math.max(0.02, Math.min(0.08, 100 / metadata.width));
const heightRatio = Math.max(0.02, Math.min(0.08, 100 / metadata.height));
return {
paddingX: Math.max(minPadding, Math.floor(metadata.width * widthRatio)),
paddingY: Math.max(minPadding, Math.floor(metadata.height * heightRatio))
};
}
resizeForDetection(metadata, config) {
const { width, height } = metadata;
const limitSideLen = config.detLimitSideLen || 960;
let ratio = 1;
if (Math.max(width, height) > limitSideLen) {
ratio = limitSideLen / Math.max(width, height);
this.logger.debug(`缩放比例: ${ratio.toFixed(4)}`);
}
const newWidth = Math.floor(width * ratio);
const newHeight = Math.floor(height * ratio);
// 确保尺寸是32的倍数
const finalWidth = Math.max(32, Math.floor(newWidth / 32) * 32);
const finalHeight = Math.max(32, Math.floor(newHeight / 32) * 32);
this.logger.debug(`调整后尺寸: ${finalWidth}x${finalHeight}`);
return { width: finalWidth, height: finalHeight };
}
async getImageInfo(imagePath) {
try {
const metadata = await sharp(imagePath).metadata();
return {
width: metadata.width || 0,
height: metadata.height || 0,
format: metadata.format || 'unknown',
processed: false
};
} catch (error) {
this.logger.error('获取图像信息失败', error);
return {
width: 0, height: 0, format: 'unknown', processed: false
};
}
}
}
export default ImagePreprocessor;