Skip to content

Commit 4bc9cd6

Browse files
committed
AI: Add "source" flag to the "photoprism vision run" command #3438 #5011
Signed-off-by: Michael Mayer <michael@photoprism.app>
1 parent eb8d206 commit 4bc9cd6

2 files changed

Lines changed: 35 additions & 16 deletions

File tree

‎internal/commands/vision_run.go‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,31 @@ import (
77

88
"github.com/photoprism/photoprism/internal/ai/vision"
99
"github.com/photoprism/photoprism/internal/config"
10+
"github.com/photoprism/photoprism/internal/entity"
1011
"github.com/photoprism/photoprism/internal/workers"
1112
)
1213

1314
// VisionRunCommand configures the command name, flags, and action.
1415
var VisionRunCommand = &cli.Command{
1516
Name: "run",
16-
Usage: "Runs a computer vision model",
17+
Usage: "Runs one or more computer vision models on a set of pictures, as specified by the search filter",
1718
ArgsUsage: "[filter]",
1819
Flags: []cli.Flag{
1920
&cli.StringFlag{
2021
Name: "models",
2122
Aliases: []string{"m"},
22-
Usage: "model types (labels, nsfw, caption)",
23+
Usage: "computer vision `MODELS` to run, e.g. caption, labels, or nsfw",
24+
},
25+
&cli.StringFlag{
26+
Name: "source",
27+
Aliases: []string{"s"},
28+
Value: entity.SrcImage,
29+
Usage: "custom data source `TYPE`, e.g. estimate, image, meta, or manual",
2330
},
2431
&cli.BoolFlag{
2532
Name: "force",
2633
Aliases: []string{"f"},
27-
Usage: "force existing metadata to be updated",
34+
Usage: "force existing data to be updated if the source priority is equal to or higher than the current one",
2835
},
2936
},
3037
Action: visionRunAction,
@@ -34,6 +41,11 @@ var VisionRunCommand = &cli.Command{
3441
func visionRunAction(ctx *cli.Context) error {
3542
return CallWithDependencies(ctx, func(conf *config.Config) error {
3643
worker := workers.NewVision(conf)
37-
return worker.Start(strings.TrimSpace(ctx.Args().First()), vision.ParseTypes(ctx.String("models")), ctx.Bool("force"))
44+
return worker.Start(
45+
strings.TrimSpace(ctx.Args().First()),
46+
vision.ParseTypes(ctx.String("models")),
47+
ctx.String("source"),
48+
ctx.Bool("force"),
49+
)
3850
})
3951
}

‎internal/workers/vision.go‎

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func (w *Vision) originalsPath() string {
4040
}
4141

4242
// Start runs the specified model types for the photos that match the search query.
43-
func (w *Vision) Start(q string, models []string, force bool) (err error) {
43+
func (w *Vision) Start(q string, models []string, customSrc string, force bool) (err error) {
4444
defer func() {
4545
if r := recover(); r != nil {
4646
err = fmt.Errorf("vision: %s (worker panic)\nstack: %s", r, debug.Stack())
@@ -68,6 +68,15 @@ func (w *Vision) Start(q string, models []string, force bool) (err error) {
6868
log.Infof("vision: running %s models", strings.Join(models, " and "))
6969
}
7070

71+
// Source type for AI generated data.
72+
var dataSrc string
73+
74+
if customSrc = clean.ShortTypeLower(customSrc); customSrc != "" {
75+
dataSrc = customSrc
76+
} else {
77+
dataSrc = entity.SrcImage
78+
}
79+
7180
// Check time when worker was last executed.
7281
updateIndex := false
7382

@@ -128,13 +137,15 @@ func (w *Vision) Start(q string, models []string, force bool) (err error) {
128137

129138
changed := false
130139

140+
// Generate labels.
131141
if updateLabels && (len(m.Labels) == 0 || force) {
132142
if labels := ind.Labels(file); len(labels) > 0 {
133143
m.AddLabels(labels)
134144
changed = true
135145
}
136146
}
137147

148+
// Detect NSFW content.
138149
if updateNsfw && (!photo.PhotoPrivate || force) {
139150
if isNsfw := ind.IsNsfw(file); photo.PhotoPrivate != isNsfw {
140151
photo.PhotoPrivate = isNsfw
@@ -143,19 +154,15 @@ func (w *Vision) Start(q string, models []string, force bool) (err error) {
143154
}
144155
}
145156

146-
if updateCaptions && (m.PhotoCaption == "" || force) {
157+
// Generate a caption if none exists or the force flag is used,
158+
// and only if no caption was set or removed by a higher-priority source.
159+
if updateCaptions && entity.SrcPriority[dataSrc] >= entity.SrcPriority[m.CaptionSrc] && (m.NoCaption() || force) {
147160
if caption, captionErr := ind.Caption(file); captionErr != nil {
148161
log.Warnf("vision: %s in %s (generate caption)", clean.Error(captionErr), photoName)
149-
} else if caption.Text != "" {
150-
if caption.Source == "" {
151-
caption.Source = entity.SrcImage
152-
}
153-
154-
if (entity.SrcPriority[caption.Source] >= entity.SrcPriority[m.CaptionSrc]) || !m.HasCaption() {
155-
m.SetCaption(caption.Text, caption.Source)
156-
changed = true
157-
log.Infof("vision: changed caption of %s to %s", photoName, clean.Log(m.PhotoCaption))
158-
}
162+
} else if caption.Text = strings.TrimSpace(caption.Text); caption.Text != "" {
163+
m.SetCaption(caption.Text, dataSrc)
164+
changed = true
165+
log.Infof("vision: changed caption of %s to %s", photoName, clean.Log(m.PhotoCaption))
159166
}
160167
}
161168

0 commit comments

Comments
 (0)