feat(classify): 添加图片明暗分类功能
- 修改 ImageDir 函数增加 darkScore 和 lightScore 参数 - 实现 BrightnessName 函数计算明暗等级 - 定义暗色和亮色阈值等级数组 - 在 organize.go 中集成图片明暗分析逻辑 - 使用 ITU-R BT.601 公式计算像素亮度 - 为大图实现自动采样加速分析 - 更新 README.md 文档说明明暗分类算法 - 在目录结构中体现明暗等级命名规则
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package classify
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 暗色阈值等级(从高到低)
|
||||
var darkThresholds = []int{100, 95, 85, 75, 65, 55}
|
||||
|
||||
// 亮色阈值等级(从高到低)
|
||||
var brightThresholds = []int{85, 75, 65, 55, 45, 35, 25, 15}
|
||||
|
||||
// BrightnessName 按主导分判定明暗等级,返回如 "dark_100" / "bright_85"。
|
||||
// 亮色主导(lightScore > darkScore)归 bright,否则归 dark,每张图必归亮暗之一。
|
||||
func BrightnessName(darkScore, lightScore float64) string {
|
||||
darkLowest := darkThresholds[len(darkThresholds)-1]
|
||||
brightLowest := brightThresholds[len(brightThresholds)-1]
|
||||
|
||||
if lightScore > darkScore {
|
||||
picked := brightLowest
|
||||
for _, t := range brightThresholds {
|
||||
if lightScore >= float64(t)/100.0 {
|
||||
picked = t
|
||||
break
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("bright_%d", picked)
|
||||
}
|
||||
picked := darkLowest
|
||||
for _, t := range darkThresholds {
|
||||
if darkScore >= float64(t)/100.0 {
|
||||
picked = t
|
||||
break
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("dark_%d", picked)
|
||||
}
|
||||
@@ -35,12 +35,14 @@ func (o Orientation) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// ImageDir 返回图片归类子目录名,如 "图片_横屏_1080P" / "图片_GIF"
|
||||
func ImageDir(ext string, width, height int) string {
|
||||
// ImageDir 返回图片归类子目录名,如 "图片_横屏_1080P-dark_100" / "图片_GIF"。
|
||||
// darkScore/lightScore 用于明暗分类(GIF 不做明暗分类,忽略这两项)。
|
||||
func ImageDir(ext string, width, height int, darkScore, lightScore float64) string {
|
||||
if strings.EqualFold(ext, ".gif") {
|
||||
return "图片_GIF"
|
||||
}
|
||||
return resolutionDir("图片", ext, width, height)
|
||||
dir := resolutionDir("图片", ext, width, height)
|
||||
return dir + "-" + BrightnessName(darkScore, lightScore)
|
||||
}
|
||||
|
||||
// VideoDir 返回视频归类子目录名,如 "视频_竖屏_4k" / "视频_横屏_GIF"
|
||||
|
||||
@@ -129,7 +129,16 @@ func (o *Organizer) ProcessImages(images []scanner.File) {
|
||||
continue
|
||||
}
|
||||
o.record(f, "image", res)
|
||||
o.add(f, classify.ImageDir(f.Ext, res.Width, res.Height))
|
||||
darkScore, lightScore := 0.0, 0.0
|
||||
if !strings.EqualFold(f.Ext, ".gif") {
|
||||
dark, light, berr := probe.AnalyzeBrightness(f.Path)
|
||||
if berr != nil {
|
||||
fmt.Printf("=== 明暗分析失败(按默认dark_55归类): %s \n", f.Path)
|
||||
} else {
|
||||
darkScore, lightScore = dark, light
|
||||
}
|
||||
}
|
||||
o.add(f, classify.ImageDir(f.Ext, res.Width, res.Height, darkScore, lightScore))
|
||||
success++
|
||||
}
|
||||
fmt.Printf("=== 图片处理完毕,成功: %d, 读取异常: %d, 忽略: %d \n\n", success, fail, ignore)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package probe
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/tiff"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
// AnalyzeBrightness 打开并解码图片,分析明暗程度。
|
||||
// darkScore: 暗色像素(亮度 < 80)占比;lightScore: 亮色像素(亮度 > 175)占比。
|
||||
func AnalyzeBrightness(filePath string) (darkScore float64, lightScore float64, err error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
dark, light := analyzeBrightness(img)
|
||||
return dark, light, nil
|
||||
}
|
||||
|
||||
// analyzeBrightness 统计暗色/亮色像素占比(ITU-R BT.601 亮度公式),大图自动采样加速
|
||||
func analyzeBrightness(img image.Image) (darkScore float64, lightScore float64) {
|
||||
bounds := img.Bounds()
|
||||
totalPixels := bounds.Dx() * bounds.Dy()
|
||||
if totalPixels == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
darkCount, lightCount, sampleCount := 0, 0, 0
|
||||
sampleStep := 1
|
||||
if totalPixels > 1_000_000 {
|
||||
sampleStep = int(math.Sqrt(float64(totalPixels) / 100_000))
|
||||
}
|
||||
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y += sampleStep {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x += sampleStep {
|
||||
r, g, b, _ := img.At(x, y).RGBA()
|
||||
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++
|
||||
}
|
||||
sampleCount++
|
||||
}
|
||||
}
|
||||
|
||||
if sampleCount == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
return float64(darkCount) / float64(sampleCount), float64(lightCount) / float64(sampleCount)
|
||||
}
|
||||
Reference in New Issue
Block a user