Upload imageProcessor.js
Browse files- src/lib/imageProcessor.js +127 -22
src/lib/imageProcessor.js
CHANGED
|
@@ -6,13 +6,18 @@ const sharp = require('sharp')
|
|
| 6 |
*/
|
| 7 |
class ImageProcessor {
|
| 8 |
constructor() {
|
| 9 |
-
// 长图阈值:高度超过宽度*
|
| 10 |
-
this.LONG_IMAGE_RATIO =
|
| 11 |
// 重叠像素范围
|
| 12 |
this.OVERLAP_MIN = 50
|
| 13 |
this.OVERLAP_MAX = 100
|
| 14 |
// 默认重叠像素
|
| 15 |
this.DEFAULT_OVERLAP = 75
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
}
|
| 17 |
|
| 18 |
/**
|
|
@@ -48,7 +53,71 @@ class ImageProcessor {
|
|
| 48 |
}
|
| 49 |
|
| 50 |
/**
|
| 51 |
-
* 计算
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
* @param {number} width - 图片宽度
|
| 53 |
* @param {number} height - 图片高度
|
| 54 |
* @param {number} overlap - 重叠像素数
|
|
@@ -56,26 +125,39 @@ class ImageProcessor {
|
|
| 56 |
*/
|
| 57 |
calculateCropRegions(width, height, overlap = this.DEFAULT_OVERLAP) {
|
| 58 |
const regions = []
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
let currentTop = 0
|
| 62 |
let segmentIndex = 0
|
| 63 |
-
|
| 64 |
-
while (currentTop < height) {
|
| 65 |
const remainingHeight = height - currentTop
|
| 66 |
-
let segmentHeight = Math.min(
|
| 67 |
-
|
| 68 |
// 如果不是第一个片段,需要向上扩展重叠区域
|
| 69 |
if (segmentIndex > 0) {
|
| 70 |
-
|
| 71 |
-
|
|
|
|
| 72 |
}
|
| 73 |
-
|
| 74 |
// 如果不是最后一个片段,需要向下扩展重叠区域
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
| 77 |
}
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
regions.push({
|
| 80 |
top: currentTop,
|
| 81 |
left: 0,
|
|
@@ -83,14 +165,30 @@ class ImageProcessor {
|
|
| 83 |
height: segmentHeight,
|
| 84 |
segmentIndex: segmentIndex,
|
| 85 |
isFirst: segmentIndex === 0,
|
| 86 |
-
isLast: currentTop + segmentHeight >= height
|
|
|
|
| 87 |
})
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
segmentIndex++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
}
|
| 93 |
-
|
|
|
|
| 94 |
return regions
|
| 95 |
}
|
| 96 |
|
|
@@ -172,13 +270,18 @@ class ImageProcessor {
|
|
| 172 |
}
|
| 173 |
|
| 174 |
const { width, height, format } = detection
|
|
|
|
|
|
|
|
|
|
| 175 |
const regions = this.calculateCropRegions(width, height, overlap)
|
| 176 |
const segments = []
|
| 177 |
|
| 178 |
// 选择最优输出格式
|
| 179 |
const outputFormat = this.getOptimalOutputFormat(format)
|
| 180 |
|
| 181 |
-
console.log(`检测到长图 ${width}x${height} (${format}),
|
|
|
|
|
|
|
| 182 |
|
| 183 |
for (const region of regions) {
|
| 184 |
const sharpInstance = sharp(imageBuffer)
|
|
@@ -206,6 +309,7 @@ class ImageProcessor {
|
|
| 206 |
segmentHeight: region.height,
|
| 207 |
originalFormat: format,
|
| 208 |
outputFormat: outputFormat,
|
|
|
|
| 209 |
cropRegion: {
|
| 210 |
top: region.top,
|
| 211 |
left: region.left,
|
|
@@ -215,7 +319,8 @@ class ImageProcessor {
|
|
| 215 |
}
|
| 216 |
})
|
| 217 |
|
| 218 |
-
|
|
|
|
| 219 |
}
|
| 220 |
|
| 221 |
return segments
|
|
|
|
| 6 |
*/
|
| 7 |
class ImageProcessor {
|
| 8 |
constructor() {
|
| 9 |
+
// 长图阈值:高度超过宽度*3倍(更合理的阈值)
|
| 10 |
+
this.LONG_IMAGE_RATIO = 3
|
| 11 |
// 重叠像素范围
|
| 12 |
this.OVERLAP_MIN = 50
|
| 13 |
this.OVERLAP_MAX = 100
|
| 14 |
// 默认重叠像素
|
| 15 |
this.DEFAULT_OVERLAP = 75
|
| 16 |
+
|
| 17 |
+
// 智能切割参数 - 针对模型识别效果优化
|
| 18 |
+
this.OPTIMAL_SEGMENT_HEIGHT = 3000 // 理想片段高度
|
| 19 |
+
this.MAX_SEGMENT_HEIGHT = 3500 // 最大片段高度
|
| 20 |
+
this.MIN_SEGMENT_HEIGHT = 2500 // 最小片段高度
|
| 21 |
}
|
| 22 |
|
| 23 |
/**
|
|
|
|
| 53 |
}
|
| 54 |
|
| 55 |
/**
|
| 56 |
+
* 计算最优片段高度
|
| 57 |
+
* @param {number} width - 图片宽度
|
| 58 |
+
* @param {number} height - 图片高度
|
| 59 |
+
* @param {number} overlap - 重叠像素数
|
| 60 |
+
* @returns {Object} 包含最优高度和片段数的对象
|
| 61 |
+
*/
|
| 62 |
+
calculateOptimalSegmentHeight(width, height, overlap = this.DEFAULT_OVERLAP) {
|
| 63 |
+
// 如果图片高度小于最大片段高度,直接返回原高度
|
| 64 |
+
if (height <= this.MAX_SEGMENT_HEIGHT) {
|
| 65 |
+
return {
|
| 66 |
+
segmentHeight: height,
|
| 67 |
+
segmentCount: 1,
|
| 68 |
+
strategy: 'single_segment'
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
// 计算理想的片段数量(基于理想高度)
|
| 73 |
+
const idealSegmentCount = Math.ceil(height / this.OPTIMAL_SEGMENT_HEIGHT)
|
| 74 |
+
|
| 75 |
+
// 计算每个片段的理想高度(不考虑重叠)
|
| 76 |
+
const baseSegmentHeight = Math.floor(height / idealSegmentCount)
|
| 77 |
+
|
| 78 |
+
// 检查是否在合理范围内
|
| 79 |
+
if (baseSegmentHeight >= this.MIN_SEGMENT_HEIGHT && baseSegmentHeight <= this.MAX_SEGMENT_HEIGHT) {
|
| 80 |
+
return {
|
| 81 |
+
segmentHeight: baseSegmentHeight,
|
| 82 |
+
segmentCount: idealSegmentCount,
|
| 83 |
+
strategy: 'optimal_division'
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
// 如果理想高度太小,减少片段数量
|
| 88 |
+
if (baseSegmentHeight < this.MIN_SEGMENT_HEIGHT) {
|
| 89 |
+
const adjustedSegmentCount = Math.max(1, Math.floor(height / this.MIN_SEGMENT_HEIGHT))
|
| 90 |
+
const adjustedSegmentHeight = Math.floor(height / adjustedSegmentCount)
|
| 91 |
+
|
| 92 |
+
return {
|
| 93 |
+
segmentHeight: Math.min(adjustedSegmentHeight, this.MAX_SEGMENT_HEIGHT),
|
| 94 |
+
segmentCount: adjustedSegmentCount,
|
| 95 |
+
strategy: 'min_height_constraint'
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
// 如果理想高度太大,增加片段数量
|
| 100 |
+
if (baseSegmentHeight > this.MAX_SEGMENT_HEIGHT) {
|
| 101 |
+
const adjustedSegmentCount = Math.ceil(height / this.MAX_SEGMENT_HEIGHT)
|
| 102 |
+
const adjustedSegmentHeight = Math.floor(height / adjustedSegmentCount)
|
| 103 |
+
|
| 104 |
+
return {
|
| 105 |
+
segmentHeight: adjustedSegmentHeight,
|
| 106 |
+
segmentCount: adjustedSegmentCount,
|
| 107 |
+
strategy: 'max_height_constraint'
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
// 兜底策略:使用最大高度
|
| 112 |
+
return {
|
| 113 |
+
segmentHeight: this.MAX_SEGMENT_HEIGHT,
|
| 114 |
+
segmentCount: Math.ceil(height / this.MAX_SEGMENT_HEIGHT),
|
| 115 |
+
strategy: 'fallback'
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
/**
|
| 120 |
+
* 计算切割参数(智能自适应版本)
|
| 121 |
* @param {number} width - 图片宽度
|
| 122 |
* @param {number} height - 图片高度
|
| 123 |
* @param {number} overlap - 重叠像素数
|
|
|
|
| 125 |
*/
|
| 126 |
calculateCropRegions(width, height, overlap = this.DEFAULT_OVERLAP) {
|
| 127 |
const regions = []
|
| 128 |
+
|
| 129 |
+
// 计算最优片段高度
|
| 130 |
+
const optimalConfig = this.calculateOptimalSegmentHeight(width, height, overlap)
|
| 131 |
+
const { segmentHeight: baseSegmentHeight, segmentCount, strategy } = optimalConfig
|
| 132 |
+
|
| 133 |
+
console.log(`[智能切割] 图片尺寸: ${width}x${height}, 策略: ${strategy}, 预计片段数: ${segmentCount}, 基础高度: ${baseSegmentHeight}`)
|
| 134 |
+
|
| 135 |
let currentTop = 0
|
| 136 |
let segmentIndex = 0
|
| 137 |
+
|
| 138 |
+
while (currentTop < height && segmentIndex < segmentCount * 2) { // 防止无限循环
|
| 139 |
const remainingHeight = height - currentTop
|
| 140 |
+
let segmentHeight = Math.min(baseSegmentHeight, remainingHeight)
|
| 141 |
+
|
| 142 |
// 如果不是第一个片段,需要向上扩展重叠区域
|
| 143 |
if (segmentIndex > 0) {
|
| 144 |
+
const overlapTop = Math.min(overlap, currentTop)
|
| 145 |
+
currentTop = currentTop - overlapTop
|
| 146 |
+
segmentHeight = Math.min(baseSegmentHeight + overlapTop, height - currentTop)
|
| 147 |
}
|
| 148 |
+
|
| 149 |
// 如果不是最后一个片段,需要向下扩展重叠区域
|
| 150 |
+
const isLastSegment = (currentTop + segmentHeight >= height) || (segmentIndex >= segmentCount - 1)
|
| 151 |
+
if (!isLastSegment) {
|
| 152 |
+
const overlapBottom = Math.min(overlap, remainingHeight - segmentHeight)
|
| 153 |
+
segmentHeight = Math.min(segmentHeight + overlapBottom, height - currentTop)
|
| 154 |
}
|
| 155 |
+
|
| 156 |
+
// 确保片段高度在合理范围内
|
| 157 |
+
segmentHeight = Math.max(segmentHeight, this.MIN_SEGMENT_HEIGHT)
|
| 158 |
+
segmentHeight = Math.min(segmentHeight, this.MAX_SEGMENT_HEIGHT)
|
| 159 |
+
segmentHeight = Math.min(segmentHeight, height - currentTop) // 不能超出图片边界
|
| 160 |
+
|
| 161 |
regions.push({
|
| 162 |
top: currentTop,
|
| 163 |
left: 0,
|
|
|
|
| 165 |
height: segmentHeight,
|
| 166 |
segmentIndex: segmentIndex,
|
| 167 |
isFirst: segmentIndex === 0,
|
| 168 |
+
isLast: currentTop + segmentHeight >= height,
|
| 169 |
+
strategy: strategy
|
| 170 |
})
|
| 171 |
+
|
| 172 |
+
console.log(`[切割片段] 片段${segmentIndex + 1}: top=${currentTop}, height=${segmentHeight}, 范围=[${currentTop}, ${currentTop + segmentHeight}]`)
|
| 173 |
+
|
| 174 |
+
// 移动到下一个片段的起始位置
|
| 175 |
+
if (segmentIndex === 0) {
|
| 176 |
+
// 第一个片段:直接移动基础高度
|
| 177 |
+
currentTop += baseSegmentHeight
|
| 178 |
+
} else {
|
| 179 |
+
// 后续片段:移动基础高度减去重叠
|
| 180 |
+
currentTop += Math.max(baseSegmentHeight - overlap, 1)
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
segmentIndex++
|
| 184 |
+
|
| 185 |
+
// 如果已经覆盖了整个图片,退出循环
|
| 186 |
+
if (currentTop >= height) {
|
| 187 |
+
break
|
| 188 |
+
}
|
| 189 |
}
|
| 190 |
+
|
| 191 |
+
console.log(`[切割完成] 实际生成${regions.length}个片段`)
|
| 192 |
return regions
|
| 193 |
}
|
| 194 |
|
|
|
|
| 270 |
}
|
| 271 |
|
| 272 |
const { width, height, format } = detection
|
| 273 |
+
|
| 274 |
+
// 计算最优切割配置
|
| 275 |
+
const optimalConfig = this.calculateOptimalSegmentHeight(width, height, overlap)
|
| 276 |
const regions = this.calculateCropRegions(width, height, overlap)
|
| 277 |
const segments = []
|
| 278 |
|
| 279 |
// 选择最优输出格式
|
| 280 |
const outputFormat = this.getOptimalOutputFormat(format)
|
| 281 |
|
| 282 |
+
console.log(`检测到长图 ${width}x${height} (${format}),智能切割策略: ${optimalConfig.strategy}`)
|
| 283 |
+
console.log(`切割配置: 目标高度=${optimalConfig.segmentHeight}px, 预计片段=${optimalConfig.segmentCount}个, 实际片段=${regions.length}个`)
|
| 284 |
+
console.log(`输出格式: ${outputFormat}, 重叠像素: ${overlap}px`)
|
| 285 |
|
| 286 |
for (const region of regions) {
|
| 287 |
const sharpInstance = sharp(imageBuffer)
|
|
|
|
| 309 |
segmentHeight: region.height,
|
| 310 |
originalFormat: format,
|
| 311 |
outputFormat: outputFormat,
|
| 312 |
+
strategy: region.strategy,
|
| 313 |
cropRegion: {
|
| 314 |
top: region.top,
|
| 315 |
left: region.left,
|
|
|
|
| 319 |
}
|
| 320 |
})
|
| 321 |
|
| 322 |
+
const heightStatus = region.height <= 3000 ? '✓理想' : region.height <= 3500 ? '✓良好' : '⚠超限'
|
| 323 |
+
console.log(`生成片段 ${region.segmentIndex + 1}/${regions.length}: ${region.width}x${region.height} (${heightStatus}) [${region.top}-${region.top + region.height}]`)
|
| 324 |
}
|
| 325 |
|
| 326 |
return segments
|