feat: 扫描整理并行化、WebSocket 实时进度与统计看板
- 移动阶段并行处理文件,按原子计数上报累计进度 - 扫描遍历阶段实时上报已发现文件数,界面不再卡在 0/0 - 新增 /api/scan/ws WebSocket 实时推送扫描状态;前端断线指数退避自动重连,并以低频轮询兜底 - 新增统计看板与媒体库筛选/排序,目录选择器路径不存在时回退到最近可用父目录 - 存放目录支持自定义并持久化,默认输出到 KMP已整理
This commit is contained in:
@@ -18,9 +18,9 @@ const (
|
||||
// brightnessDecodeLimit 明暗分析允许解码的最大像素数,超出时跳过分析(按 mid 归类),避免超大图 OOM
|
||||
brightnessDecodeLimit = 40_000_000
|
||||
// thumbnailMaxEdge 明暗分析时大图缩放后的最长边像素,缩小到该尺寸内再统计
|
||||
thumbnailMaxEdge = 512
|
||||
thumbnailMaxEdge = 256
|
||||
// thumbnailScaleThreshold 像素数超过该值才缩放为缩略图再统计
|
||||
thumbnailScaleThreshold = 1_000_000
|
||||
thumbnailScaleThreshold = 250_000
|
||||
)
|
||||
|
||||
// AnalyzeBrightness 打开并解码图片,分析明暗程度。
|
||||
@@ -78,6 +78,7 @@ func scaleToThumbnail(img image.Image, maxEdge int) image.Image {
|
||||
|
||||
// analyzeBrightness 统计暗色/亮色像素占比(ITU-R BT.601 亮度公式)。
|
||||
// 完全透明的像素(alpha=0)不计入统计。
|
||||
// 对常见内存布局(NRGBA/RGBA/Gray)直接访问像素数组,避免 image.At 接口逐像素开销。
|
||||
func analyzeBrightness(img image.Image) (darkScore float64, lightScore float64) {
|
||||
bounds := img.Bounds()
|
||||
totalPixels := bounds.Dx() * bounds.Dy()
|
||||
@@ -85,23 +86,70 @@ func analyzeBrightness(img image.Image) (darkScore float64, lightScore float64)
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
darkCount, lightCount, sampleCount := 0, 0, 0
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
if a == 0 {
|
||||
continue
|
||||
var darkCount, lightCount, sampleCount int64
|
||||
switch m := img.(type) {
|
||||
case *image.NRGBA:
|
||||
pix := m.Pix
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
offset := m.PixOffset(bounds.Min.X, y)
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
r, g, b, a := pix[offset], pix[offset+1], pix[offset+2], pix[offset+3]
|
||||
if a == 0 {
|
||||
offset += 4
|
||||
continue
|
||||
}
|
||||
countBrightness(float64(r), float64(g), float64(b), &darkCount, &lightCount)
|
||||
sampleCount++
|
||||
offset += 4
|
||||
}
|
||||
r8 := uint8(r >> 8)
|
||||
g8 := uint8(g >> 8)
|
||||
b8 := uint8(b >> 8)
|
||||
brightness := 0.299*float64(r8) + 0.587*float64(g8) + 0.114*float64(b8)
|
||||
if brightness < 80 {
|
||||
darkCount++
|
||||
} else if brightness > 175 {
|
||||
lightCount++
|
||||
}
|
||||
case *image.RGBA:
|
||||
pix := m.Pix
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
offset := m.PixOffset(bounds.Min.X, y)
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
// 预乘 alpha:若完全不透明则按直接值,否则近似取非预乘分量
|
||||
a := pix[offset+3]
|
||||
if a == 0 {
|
||||
offset += 4
|
||||
continue
|
||||
}
|
||||
r, g, b := pix[offset], pix[offset+1], pix[offset+2]
|
||||
if a != 255 {
|
||||
r = uint8(int(r) * 255 / int(a))
|
||||
g = uint8(int(g) * 255 / int(a))
|
||||
b = uint8(int(b) * 255 / int(a))
|
||||
}
|
||||
countBrightness(float64(r), float64(g), float64(b), &darkCount, &lightCount)
|
||||
sampleCount++
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
case *image.Gray:
|
||||
pix := m.Pix
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
offset := m.PixOffset(bounds.Min.X, y)
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
v := float64(pix[offset])
|
||||
if v < 80 {
|
||||
darkCount++
|
||||
} else if v > 175 {
|
||||
lightCount++
|
||||
}
|
||||
sampleCount++
|
||||
offset++
|
||||
}
|
||||
}
|
||||
default:
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
if a == 0 {
|
||||
continue
|
||||
}
|
||||
countBrightness(float64(r>>8), float64(g>>8), float64(b>>8), &darkCount, &lightCount)
|
||||
sampleCount++
|
||||
}
|
||||
sampleCount++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,3 +158,13 @@ func analyzeBrightness(img image.Image) (darkScore float64, lightScore float64)
|
||||
}
|
||||
return float64(darkCount) / float64(sampleCount), float64(lightCount) / float64(sampleCount)
|
||||
}
|
||||
|
||||
// countBrightness 按 BT.601 亮度公式累计暗/亮像素计数
|
||||
func countBrightness(r, g, b float64, darkCount, lightCount *int64) {
|
||||
brightness := 0.299*r + 0.587*g + 0.114*b
|
||||
if brightness < 80 {
|
||||
*darkCount++
|
||||
} else if brightness > 175 {
|
||||
*lightCount++
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user