Electron-vue3-ts-offline/server/utils/textRegionCropper.js
2025-11-13 16:34:41 +08:00

127 行
4.3 KiB
JavaScript

// server/utils/textRegionCropper.js
import sharp from 'sharp';
class TextRegionCropper {
constructor() {
// 可以在这里添加配置参数
}
async cropTextRegion(imageBuffer, box, regionIndex) {
try {
const metadata = await sharp(imageBuffer).metadata();
const imgWidth = metadata.width;
const imgHeight = metadata.height;
const left = Math.min(box.x1, box.x2, box.x3, box.x4);
const top = Math.min(box.y1, box.y2, box.y3, box.y4);
const right = Math.max(box.x1, box.x2, box.x3, box.x4);
const bottom = Math.max(box.y1, box.y2, box.y3, box.y4);
const originalWidth = right - left;
const originalHeight = bottom - top;
// 减少扩展,避免引入过多背景
const widthExpand = 10;
const heightExpand = 10;
const newWidth = originalWidth + widthExpand;
const newHeight = originalHeight + heightExpand;
const centerX = (left + right) / 2;
const centerY = (top + bottom) / 2;
const expandedLeft = Math.max(0, centerX - newWidth / 2);
const expandedTop = Math.max(0, centerY - newHeight / 2);
const expandedRight = Math.min(imgWidth - 1, centerX + newWidth / 2);
const expandedBottom = Math.min(imgHeight - 1, centerY + newHeight / 2);
const finalWidth = expandedRight - expandedLeft;
const finalHeight = expandedBottom - expandedTop;
if (finalWidth <= 0 || finalHeight <= 0) {
console.log(`❌ 区域 ${regionIndex}: 无效的裁剪区域`);
return null;
}
let adjustedLeft = expandedLeft;
let adjustedTop = expandedTop;
let adjustedWidth = finalWidth;
let adjustedHeight = finalHeight;
if (expandedLeft < 0) {
adjustedLeft = 0;
adjustedWidth = expandedRight;
}
if (expandedTop < 0) {
adjustedTop = 0;
adjustedHeight = expandedBottom;
}
if (expandedRight > imgWidth) {
adjustedWidth = imgWidth - adjustedLeft;
}
if (expandedBottom > imgHeight) {
adjustedHeight = imgHeight - adjustedTop;
}
const croppedBuffer = await sharp(imageBuffer)
.extract({
left: Math.floor(adjustedLeft),
top: Math.floor(adjustedTop),
width: Math.floor(adjustedWidth),
height: Math.floor(adjustedHeight)
})
.png()
.toBuffer();
console.log(`✂️ 区域 ${regionIndex}: 裁剪 ${Math.floor(adjustedWidth)}x${Math.floor(adjustedHeight)}`);
return {
buffer: croppedBuffer,
boxInfo: {
original: { left, top, right, bottom, width: originalWidth, height: originalHeight },
expanded: {
left: adjustedLeft,
top: adjustedTop,
right: adjustedLeft + adjustedWidth,
bottom: adjustedTop + adjustedHeight,
width: adjustedWidth,
height: adjustedHeight
}
}
};
} catch (error) {
console.error(`❌ 区域 ${regionIndex}: 裁剪失败`, error);
return null;
}
}
async rotateImage(imageBuffer, degrees) {
return await sharp(imageBuffer)
.rotate(degrees)
.png()
.toBuffer();
}
calculateExpansion(originalWidth, originalHeight, expansionFactor = 1.2) {
return {
width: originalWidth * expansionFactor,
height: originalHeight * expansionFactor
};
}
validateCropRegion(left, top, width, height, imgWidth, imgHeight) {
if (width <= 0 || height <= 0) {
return false;
}
if (left < 0 || top < 0) {
return false;
}
if (left + width > imgWidth || top + height > imgHeight) {
return false;
}
return true;
}
}
export default TextRegionCropper;