122 lines
4.1 KiB
Go
122 lines
4.1 KiB
Go
// Package analysis inspects an uploaded picture and derives badges describing
|
|
// what can be recognised in it.
|
|
//
|
|
// Two implementations are provided. LocalAnalyzer measures the picture's own
|
|
// pixels and EXIF metadata and turns those measurements into scene and content
|
|
// badges; it needs no external service and is fully deterministic. RemoteAnalyzer
|
|
// delegates to an external inference service (a trained object-detection model)
|
|
// and returns the labels that model predicts. The two can be combined, in which
|
|
// case model labels take precedence over heuristics of the same name.
|
|
package analysis
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"go-places-api/internal/domain"
|
|
)
|
|
|
|
// Input is the picture to analyse.
|
|
type Input struct {
|
|
// ContentType is the MIME type as supplied by the client.
|
|
ContentType string
|
|
// Bytes is the complete encoded picture.
|
|
Bytes []byte
|
|
// MaxDimension bounds the longest edge used for pixel measurements. The
|
|
// picture is downscaled before sampling so that cost does not grow with
|
|
// megapixels. Zero means use the package default.
|
|
MaxDimension int
|
|
// Hints carries scene labels already known about the picture, which lets a
|
|
// remote analyzer disambiguate using earlier results.
|
|
Hints []string
|
|
}
|
|
|
|
// Metadata is the capture information recovered from EXIF, if any.
|
|
type Metadata struct {
|
|
// Coordinate is the GPS position from the EXIF GPS IFD.
|
|
Coordinate *domain.Coordinate
|
|
// TakenAt is the original capture timestamp.
|
|
TakenAt *time.Time
|
|
// Make and Model identify the camera or phone.
|
|
Make string
|
|
Model string
|
|
}
|
|
|
|
// Signals are the measurements taken from the pixels. Every field is
|
|
// normalised to 0..1 so the badge rules can use a single comparable scale.
|
|
type Signals struct {
|
|
// Width and Height are the dimensions of the sampled image.
|
|
Width int
|
|
Height int
|
|
|
|
// Brightness is the mean perceptual luminance.
|
|
Brightness float64
|
|
// Saturation is the mean colour saturation.
|
|
Saturation float64
|
|
// Colorfulness is the spread of hue across the image, a stand-in for how
|
|
// varied the scene is.
|
|
Colorfulness float64
|
|
// EdgeDensity is the fraction of pixels sitting on a strong gradient, which
|
|
// indicates how much fine detail the picture holds.
|
|
EdgeDensity float64
|
|
|
|
// HueWeights holds the fraction of pixels falling into each colour class:
|
|
// blue, green, warm (red/orange/yellow), purple and achromatic.
|
|
HueWeights HueWeights
|
|
|
|
// Top, Middle and Bottom hold the same measurements restricted to the
|
|
// upper, central and lower horizontal bands of the picture. Sky, ground
|
|
// and water all separate cleanly this way.
|
|
Top Band
|
|
Middle Band
|
|
Bottom Band
|
|
}
|
|
|
|
// Band is the measurement set for one horizontal third of a picture.
|
|
type Band struct {
|
|
Brightness float64
|
|
Saturation float64
|
|
EdgeDensity float64
|
|
HueWeights HueWeights
|
|
PixelShare float64
|
|
PixelCount int
|
|
}
|
|
|
|
// HueWeights is the distribution of pixels across colour classes. The values
|
|
// sum to approximately 1 for a fully populated band.
|
|
type HueWeights struct {
|
|
Blue float64
|
|
Green float64
|
|
Warm float64
|
|
// Purple covers magenta and violet, which read as neither blue nor warm.
|
|
Purple float64
|
|
// Achromatic is the share of pixels with little or no hue: greys, whites,
|
|
// blacks and near-neutrals.
|
|
Achromatic float64
|
|
}
|
|
|
|
// Result is everything the pipeline learned about a picture.
|
|
type Result struct {
|
|
// Width and Height are the true dimensions of the encoded picture.
|
|
Width int
|
|
Height int
|
|
// Signals are the pixel measurements, retained for debugging and for the
|
|
// remote analyzer's fallback.
|
|
Signals Signals
|
|
// Metadata is what EXIF reported.
|
|
Metadata Metadata
|
|
// Badges are the conclusions drawn from the above.
|
|
Badges []domain.Badge
|
|
// Note carries a non-fatal warning, such as a degraded analyzer, so the
|
|
// caller can record why the badge set may be incomplete.
|
|
Note string
|
|
}
|
|
|
|
// Analyzer inspects a picture.
|
|
type Analyzer interface {
|
|
// Name identifies the analyzer in badge sources.
|
|
Name() string
|
|
// Analyze inspects the picture. It returns an error only when the picture
|
|
// cannot be inspected at all; a picture that yields no badges is a success.
|
|
Analyze(ctx context.Context, in Input) (*Result, error)
|
|
}
|