feat: 目录层级变更
This commit is contained in:
@@ -83,9 +83,7 @@ type Organizer struct {
|
||||
resumeSkip int32 // 本次续传跳过的文件数(并行累计)
|
||||
pendingScan []store.ScanLog // 待写入续传表的记录(攒一批再批量写)
|
||||
nsfwChecked int32 // 色情识别已检查的图片数(并行累计,只涨不跌)
|
||||
nsfwHits int32 // 色情识别命中的图片数(并行累计)
|
||||
animeNsfwChecked int32 // 动漫色情识别已检查的图片数
|
||||
animeNsfwHits int32 // 动漫色情识别命中的图片数
|
||||
// 阶段耗时统计(并行 worker 累计,单位纳秒,只用于日志诊断瓶颈)
|
||||
tImageProbe int64 // 图片尺寸探测总耗时
|
||||
tImgMd5 int64 // 图片 MD5/指纹计算总耗时
|
||||
@@ -405,10 +403,8 @@ type imageProbeResult struct {
|
||||
darkScore float64 // 暗色像素占比(用于亮度分类)
|
||||
lightScore float64 // 亮色像素占比
|
||||
nsfwScore float64 // 色情检测最高分(未检测为 0)
|
||||
nsfw bool // 是否判定为色情(检测可用时才可能为 true)
|
||||
nsfwDetections []nsfw.NsfwDetection // NudeNet 检测详情
|
||||
animeNsfwScore float64 // 动漫色情检测最高分(未检测为 0)
|
||||
animeNsfw bool // 是否判定为动漫色情
|
||||
animeNsfwRatings anime_nsfw.AnimeRatings // WD Tagger 四个 rating 的原始分数
|
||||
}
|
||||
|
||||
@@ -465,48 +461,36 @@ func (o *Organizer) probeImage(f scanner.File) imageProbeResult {
|
||||
// 色情识别(GIF 不做;没启用检测器就直接跳过)
|
||||
if o.nsfw != nil && !strings.EqualFold(f.Ext, ".gif") {
|
||||
nsfwStart := time.Now()
|
||||
score, hit, detections, derr := o.nsfw.Detect(f.Path)
|
||||
score, detections, derr := o.nsfw.Detect(f.Path)
|
||||
atomic.AddInt64(&o.tNsfw, int64(time.Since(nsfwStart)))
|
||||
if derr != nil {
|
||||
// 检测失败按"未命中"处理,不影响整理流程
|
||||
fmt.Printf("=== 色情检测失败(按未命中归类): %s, 错误: %v \n", f.Path, derr)
|
||||
// 检测失败不影响整理流程
|
||||
fmt.Printf("=== 色情检测失败: %s, 错误: %v \n", f.Path, derr)
|
||||
} else {
|
||||
r.nsfwScore = score
|
||||
r.nsfw = hit
|
||||
r.nsfwDetections = detections
|
||||
checked := atomic.AddInt32(&o.nsfwChecked, 1)
|
||||
if hit {
|
||||
atomic.AddInt32(&o.nsfwHits, 1)
|
||||
fmt.Printf("=== 色情命中: %s, 检测分=%.4f (阈值=%.4f)\n", f.Path, score, o.nsfw.Threshold())
|
||||
} else {
|
||||
fmt.Printf("=== 色情未命中: %s, 检测分=%.4f (阈值=%.4f)\n", f.Path, score, o.nsfw.Threshold())
|
||||
}
|
||||
fmt.Printf("=== 色情检测: %s, 评分=%.4f\n", f.Path, score)
|
||||
// 每固定张数打一行进度,让前端能看到识别在推进(不逐张刷屏)
|
||||
if checked%nsfwProgressInterval == 0 {
|
||||
fmt.Printf("=== 色情识别进度: 已检查 %d 张, 命中 %d 张\n", checked, atomic.LoadInt32(&o.nsfwHits))
|
||||
fmt.Printf("=== 色情识别进度: 已检查 %d 张\n", checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 动漫色情识别(GIF 不做;没启用检测器就直接跳过)
|
||||
if o.animeNsfw != nil && !strings.EqualFold(f.Ext, ".gif") {
|
||||
animeStart := time.Now()
|
||||
score, hit, ratings, derr := o.animeNsfw.Detect(f.Path)
|
||||
score, ratings, derr := o.animeNsfw.Detect(f.Path)
|
||||
atomic.AddInt64(&o.tAnimeNsfw, int64(time.Since(animeStart)))
|
||||
if derr != nil {
|
||||
fmt.Printf("=== 动漫色情检测失败(按未命中归类): %s, 错误: %v \n", f.Path, derr)
|
||||
fmt.Printf("=== 动漫色情检测失败: %s, 错误: %v \n", f.Path, derr)
|
||||
} else {
|
||||
r.animeNsfwScore = score
|
||||
r.animeNsfw = hit
|
||||
r.animeNsfwRatings = ratings
|
||||
checked := atomic.AddInt32(&o.animeNsfwChecked, 1)
|
||||
if hit {
|
||||
atomic.AddInt32(&o.animeNsfwHits, 1)
|
||||
fmt.Printf("=== 动漫色情命中: %s, 检测分=%.4f (阈值=%.4f)\n", f.Path, score, o.animeNsfw.Threshold())
|
||||
} else {
|
||||
fmt.Printf("=== 动漫色情未命中: %s, 检测分=%.4f (阈值=%.4f)\n", f.Path, score, o.animeNsfw.Threshold())
|
||||
}
|
||||
fmt.Printf("=== 动漫色情检测: %s, 评分=%.4f\n", f.Path, score)
|
||||
if checked%nsfwProgressInterval == 0 {
|
||||
fmt.Printf("=== 动漫色情识别进度: 已检查 %d 张, 命中 %d 张\n", checked, atomic.LoadInt32(&o.animeNsfwHits))
|
||||
fmt.Printf("=== 动漫色情识别进度: 已检查 %d 张\n", checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -554,7 +538,6 @@ func (o *Organizer) correctImageRotation(f scanner.File, res *probe.ProbeResult)
|
||||
func (o *Organizer) ProcessImages(images []scanner.File) {
|
||||
// 统计结果,最后打一行总结给用户看
|
||||
success, fail, ignore, dup := 0, 0, 0, 0
|
||||
nsfwCount := 0 // 命中的色情图片数
|
||||
total := len(images)
|
||||
phaseStart := time.Now()
|
||||
if o.nsfw != nil {
|
||||
@@ -596,13 +579,11 @@ func (o *Organizer) ProcessImages(images []scanner.File) {
|
||||
dup++
|
||||
fmt.Printf("=== 重复图片(同路径), 忽略: %s \n", f.Path)
|
||||
default:
|
||||
// 正常图片:按分辨率/方向/明暗归入对应的分类目录;
|
||||
// 被判为色情的改去「图片_敏感_*」目录,明暗子目录照常保留
|
||||
// 正常图片:按分辨率/方向/明暗/色情评分归入对应的分类目录
|
||||
info := o.record(f, "image", r.res, r.md5Str, r.headMd5, r.tailMd5, r.size)
|
||||
// 检测过的图片都记录分数(不管是否命中阈值),方便后续调整阈值重新判定
|
||||
// 检测过的图片都记录分数
|
||||
if info != nil && r.nsfwScore > 0 {
|
||||
info.NsfwScore = r.nsfwScore
|
||||
info.NsfwThreshold = o.nsfw.Threshold()
|
||||
if len(r.nsfwDetections) > 0 {
|
||||
if detJSON, err := json.Marshal(r.nsfwDetections); err == nil {
|
||||
info.NsfwDetections = string(detJSON)
|
||||
@@ -611,20 +592,16 @@ func (o *Organizer) ProcessImages(images []scanner.File) {
|
||||
}
|
||||
if info != nil && (r.animeNsfwScore > 0 || r.animeNsfwRatings.General > 0) {
|
||||
info.AnimeNsfwScore = r.animeNsfwScore
|
||||
info.AnimeNsfwThreshold = o.animeNsfw.Threshold()
|
||||
if ratingsJSON, err := json.Marshal(r.animeNsfwRatings); err == nil {
|
||||
info.AnimeNsfwRatings = string(ratingsJSON)
|
||||
}
|
||||
}
|
||||
// 真人色情或动漫色情任一命中,都归入敏感目录
|
||||
if r.nsfw || r.animeNsfw {
|
||||
o.addClassified(f, classify.ImageDirSensitive(f.Ext, r.res.Width, r.res.Height, r.darkScore, r.lightScore), r.size, r.headMd5, r.tailMd5)
|
||||
if r.nsfw {
|
||||
nsfwCount++
|
||||
}
|
||||
} else {
|
||||
o.addClassified(f, classify.ImageDir(f.Ext, r.res.Width, r.res.Height, r.darkScore, r.lightScore), r.size, r.headMd5, r.tailMd5)
|
||||
nsfwEnabled := o.nsfw != nil || o.animeNsfw != nil
|
||||
nsfwScore := r.nsfwScore
|
||||
if r.animeNsfwScore > nsfwScore {
|
||||
nsfwScore = r.animeNsfwScore
|
||||
}
|
||||
o.addClassified(f, classify.ImageDir(f.Ext, r.res.Width, r.res.Height, r.darkScore, r.lightScore, nsfwScore, nsfwEnabled), r.size, r.headMd5, r.tailMd5)
|
||||
success++
|
||||
}
|
||||
}
|
||||
@@ -641,13 +618,6 @@ func (o *Organizer) ProcessImages(images []scanner.File) {
|
||||
time.Duration(atomic.LoadInt64(&o.tBright)).Seconds(),
|
||||
time.Duration(atomic.LoadInt64(&o.tNsfw)).Seconds(),
|
||||
time.Duration(atomic.LoadInt64(&o.tAnimeNsfw)).Seconds())
|
||||
animeNsfwHits := atomic.LoadInt32(&o.animeNsfwHits)
|
||||
if o.nsfw != nil && nsfwCount > 0 {
|
||||
fmt.Printf("=== 色情识别: 共命中 %d 张,已归入「图片_敏感_*」目录,可在【媒体库】页面查看并通过<色情标记>人工复核\n", nsfwCount)
|
||||
}
|
||||
if o.animeNsfw != nil && animeNsfwHits > 0 {
|
||||
fmt.Printf("=== 动漫色情识别: 共命中 %d 张,已归入「图片_敏感_*」目录,可在【媒体库】页面查看并通过<色情标记>人工复核\n", animeNsfwHits)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ 视频/音频整理 ============================
|
||||
|
||||
Reference in New Issue
Block a user