feat: 色情图片识别(NudeNet);明暗档位改为5档(20%~100%)
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
"kenaito-media-picker/internal/config"
|
||||
"kenaito-media-picker/internal/digest"
|
||||
"kenaito-media-picker/internal/mover"
|
||||
"kenaito-media-picker/internal/nsfw"
|
||||
"kenaito-media-picker/internal/probe"
|
||||
"kenaito-media-picker/internal/scanner"
|
||||
"kenaito-media-picker/internal/store"
|
||||
@@ -61,6 +62,7 @@ type Organizer struct {
|
||||
store *store.Store // 数据库;为 nil 表示不记录媒体信息
|
||||
mover *mover.Mover // 负责实际搬文件的家伙
|
||||
ctx context.Context // 中止信号;ctx 被取消时探测/归类立即停下
|
||||
nsfw *nsfw.Detector // 色情检测器;为 nil 表示没启用,图片不检测
|
||||
buckets map[string][]string // 目标目录 → 里面该放哪些文件(只记源路径)
|
||||
order []string // 目标目录的先后顺序(保证输出稳定、不来回跳)
|
||||
onProgress func(phase string, done, total int)
|
||||
@@ -88,6 +90,12 @@ func (o *Organizer) SetProgress(fn func(phase string, done, total int)) {
|
||||
o.onProgress = fn
|
||||
}
|
||||
|
||||
// SetNSFWDetector 设置色情检测器。传 nil 表示不检测(默认)。
|
||||
// 设置了检测器后,图片探测时会额外做色情识别,命中的图片会归入「图片_敏感_*」目录。
|
||||
func (o *Organizer) SetNSFWDetector(d *nsfw.Detector) {
|
||||
o.nsfw = d
|
||||
}
|
||||
|
||||
// report 上报进度。total 为 0 表示这一步没活干,就不用报了。
|
||||
func (o *Organizer) report(phase string, done, total int) {
|
||||
if o.onProgress != nil && total > 0 {
|
||||
@@ -279,6 +287,8 @@ type imageProbeResult struct {
|
||||
size int64 // 文件大小
|
||||
darkScore float64 // 暗色像素占比(用于亮度分类)
|
||||
lightScore float64 // 亮色像素占比
|
||||
nsfwScore float64 // 色情检测最高分(未检测为 0)
|
||||
nsfw bool // 是否判定为色情(检测可用时才可能为 true)
|
||||
}
|
||||
|
||||
// probeImage 给一张图片做体检(并行阶段执行):探测尺寸 + 算 MD5 + 分析明暗。
|
||||
@@ -318,6 +328,17 @@ func (o *Organizer) probeImage(f scanner.File) imageProbeResult {
|
||||
r.darkScore, r.lightScore = dark, light
|
||||
}
|
||||
}
|
||||
// 色情识别(GIF 不做;没启用检测器就直接跳过)
|
||||
if o.nsfw != nil && !strings.EqualFold(f.Ext, ".gif") {
|
||||
score, hit, derr := o.nsfw.Detect(f.Path)
|
||||
if derr != nil {
|
||||
// 检测失败按"未命中"处理,不影响整理流程
|
||||
fmt.Printf("=== 色情检测失败(按未命中归类): %s, 错误: %v \n", f.Path, derr)
|
||||
} else {
|
||||
r.nsfwScore = score
|
||||
r.nsfw = hit
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -389,9 +410,14 @@ func (o *Organizer) ProcessImages(images []scanner.File) {
|
||||
dup++
|
||||
fmt.Printf("=== 重复图片(同路径), 忽略: %s \n", f.Path)
|
||||
default:
|
||||
// 正常图片:按分辨率/方向/明暗归入对应的分类目录
|
||||
// 正常图片:按分辨率/方向/明暗归入对应的分类目录;
|
||||
// 被判为色情的改去「图片_敏感_*」目录,明暗子目录照常保留
|
||||
o.record(f, "image", r.res, r.md5Str, r.headMd5, r.tailMd5, r.size)
|
||||
o.add(f, classify.ImageDir(f.Ext, r.res.Width, r.res.Height, r.darkScore, r.lightScore))
|
||||
if r.nsfw {
|
||||
o.add(f, classify.ImageDirSensitive(f.Ext, r.res.Width, r.res.Height, r.darkScore, r.lightScore))
|
||||
} else {
|
||||
o.add(f, classify.ImageDir(f.Ext, r.res.Width, r.res.Height, r.darkScore, r.lightScore))
|
||||
}
|
||||
success++
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user