revid: add transfrom matrix variable

This commit is contained in:
Russell Stanley 2022-04-11 09:23:40 +09:30
parent ee6fc3ca44
commit 3047704ca0
7 changed files with 112 additions and 78 deletions

View File

@ -146,7 +146,7 @@ func main() {
p *turbidityProbe p *turbidityProbe
) )
p, err := NewTurbidityProbe(*log, 60*time.Second) p, err := NewTurbidityProbe(*log, 60*time.Second, rv.Config().TransformMatrix)
if err != nil { if err != nil {
log.Log(logger.Fatal, "could not create new turbidity probe", "error", err.Error()) log.Log(logger.Fatal, "could not create new turbidity probe", "error", err.Error())
} }

View File

@ -68,16 +68,18 @@ type turbidityProbe struct {
ts *turbidity.TurbiditySensor ts *turbidity.TurbiditySensor
log logger.Logger log logger.Logger
buffer *bytes.Buffer buffer *bytes.Buffer
transform []float64
trimCounter int trimCounter int
} }
// NewTurbidityProbe returns a new turbidity probe. // NewTurbidityProbe returns a new turbidity probe.
func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe, error) { func NewTurbidityProbe(log logger.Logger, delay time.Duration, transformMatrix []float64) (*turbidityProbe, error) {
tp := new(turbidityProbe) tp := new(turbidityProbe)
tp.log = log tp.log = log
tp.delay = delay tp.delay = delay
tp.ticker = *time.NewTicker(delay) tp.ticker = *time.NewTicker(delay)
tp.buffer = bytes.NewBuffer(*new([]byte)) tp.buffer = bytes.NewBuffer(*new([]byte))
tp.transform = transformMatrix
// Create the turbidity sensor. // Create the turbidity sensor.
standard := gocv.IMRead("../../turbidity/images/default.jpg", gocv.IMReadGrayScale) standard := gocv.IMRead("../../turbidity/images/default.jpg", gocv.IMReadGrayScale)
@ -125,6 +127,7 @@ func (tp *turbidityProbe) Write(p []byte) (int, error) {
select { select {
case <-tp.ticker.C: case <-tp.ticker.C:
tp.log.Log(logger.Debug, "beginning turbidity calculation") tp.log.Log(logger.Debug, "beginning turbidity calculation")
tp.log.Log(logger.Debug, "transformation matrix", tp.transform[0])
startTime := time.Now() startTime := time.Now()
err := tp.turbidityCalculation() err := tp.turbidityCalculation()
if err != nil { if err != nil {

View File

@ -44,8 +44,9 @@ func TestProbe(t *testing.T) {
MaxAge: logMaxAge, MaxAge: logMaxAge,
} }
log := logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress) log := logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress)
transformMatrix := []float64{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}
ts, err := NewTurbidityProbe(*log, time.Microsecond) ts, err := NewTurbidityProbe(*log, time.Microsecond, transformMatrix)
if err != nil { if err != nil {
t.Fatalf("failed to create turbidity probe") t.Fatalf("failed to create turbidity probe")
} }

View File

@ -273,6 +273,8 @@ type Config struct {
VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
Width uint // Width defines the input video width Raspivid input. Width uint // Width defines the input video width Raspivid input.
TransformMatrix []float64 // Describes the transformation matrix to extract the target.
} }
// Validate checks for any errors in the config fields and defaults settings // Validate checks for any errors in the config fields and defaults settings

View File

@ -99,6 +99,7 @@ const (
KeyVBRQuality = "VBRQuality" KeyVBRQuality = "VBRQuality"
KeyVerticalFlip = "VerticalFlip" KeyVerticalFlip = "VerticalFlip"
KeyWidth = "Width" KeyWidth = "Width"
KeyTransformMatrix = "TransformMatrix"
) )
// Config map parameter types. // Config map parameter types.
@ -140,38 +141,55 @@ const (
// this variable in a Config, and a function for validating the value of the variable. // this variable in a Config, and a function for validating the value of the variable.
var Variables = []struct { var Variables = []struct {
Name string Name string
Type string Type string
Update func(*Config, string) Update func(*Config, string)
Validate func(*Config) Validate func(*Config)
}{ }{
{
Name: KeyTransformMatrix,
Type: typeString,
Update: func(c *Config, v string) {
v = strings.Replace(v, " ", "", 0)
elements := strings.Split(v, ",")
vals := make([]float64, len(elements))
for _, i := range elements {
vFloat, err := strconv.ParseFloat(i, 64)
if err != nil {
c.Logger.Log(logger.Warning, "invalid TransformMatrix param", "value", i)
}
vals = append(vals, vFloat)
}
c.TransformMatrix = vals
},
},
{ {
Name: KeyAutoWhiteBalance, Name: KeyAutoWhiteBalance,
Type: "enum:off,auto,sun,cloud,shade,tungsten,fluorescent,incandescent,flash,horizon", Type: "enum:off,auto,sun,cloud,shade,tungsten,fluorescent,incandescent,flash,horizon",
Update: func(c *Config, v string) { c.AutoWhiteBalance = v }, Update: func(c *Config, v string) { c.AutoWhiteBalance = v },
}, },
{ {
Name: KeyAWBGains, Name: KeyAWBGains,
Type: typeString, Type: typeString,
Update: func(c *Config, v string) { c.AWBGains = v }, Update: func(c *Config, v string) { c.AWBGains = v },
}, },
{ {
Name: KeyBitDepth, Name: KeyBitDepth,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.BitDepth = parseUint(KeyBitDepth, v, c) }, Update: func(c *Config, v string) { c.BitDepth = parseUint(KeyBitDepth, v, c) },
}, },
{ {
Name: KeyBitrate, Name: KeyBitrate,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.Bitrate = parseUint(KeyBitrate, v, c) }, Update: func(c *Config, v string) { c.Bitrate = parseUint(KeyBitrate, v, c) },
}, },
{ {
Name: KeyBrightness, Name: KeyBrightness,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.Brightness = parseUint(KeyBrightness, v, c) }, Update: func(c *Config, v string) { c.Brightness = parseUint(KeyBrightness, v, c) },
}, },
{ {
Name: KeyBurstPeriod, Name: KeyBurstPeriod,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.BurstPeriod = parseUint(KeyBurstPeriod, v, c) }, Update: func(c *Config, v string) { c.BurstPeriod = parseUint(KeyBurstPeriod, v, c) },
Validate: func(c *Config) { Validate: func(c *Config) {
if c.BurstPeriod <= 0 { if c.BurstPeriod <= 0 {
@ -182,12 +200,12 @@ var Variables = []struct {
}, },
{ {
Name: KeyCameraChan, Name: KeyCameraChan,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.CameraChan = uint8(parseUint(KeyCameraChan, v, c)) }, Update: func(c *Config, v string) { c.CameraChan = uint8(parseUint(KeyCameraChan, v, c)) },
}, },
{ {
Name: KeyCameraIP, Name: KeyCameraIP,
Type: typeString, Type: typeString,
Update: func(c *Config, v string) { c.CameraIP = v }, Update: func(c *Config, v string) { c.CameraIP = v },
Validate: func(c *Config) { Validate: func(c *Config) {
if c.CameraIP == "" { if c.CameraIP == "" {
@ -198,11 +216,11 @@ var Variables = []struct {
}, },
{ {
Name: KeyCBR, Name: KeyCBR,
Type: typeBool, Type: typeBool,
Update: func(c *Config, v string) { c.CBR = parseBool(KeyCBR, v, c) }, Update: func(c *Config, v string) { c.CBR = parseBool(KeyCBR, v, c) },
}, },
{ {
Name: KeyClipDuration, Name: KeyClipDuration,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
_v, err := strconv.Atoi(v) _v, err := strconv.Atoi(v)
@ -220,27 +238,27 @@ var Variables = []struct {
}, },
{ {
Name: KeyChannels, Name: KeyChannels,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.Channels = parseUint(KeyChannels, v, c) }, Update: func(c *Config, v string) { c.Channels = parseUint(KeyChannels, v, c) },
}, },
{ {
Name: KeyContrast, Name: KeyContrast,
Type: typeInt, Type: typeInt,
Update: func(c *Config, v string) { c.Contrast = parseInt(KeyContrast, v, c) }, Update: func(c *Config, v string) { c.Contrast = parseInt(KeyContrast, v, c) },
}, },
{ {
Name: KeyEV, Name: KeyEV,
Type: typeInt, Type: typeInt,
Update: func(c *Config, v string) { c.EV = parseInt(KeyEV, v, c) }, Update: func(c *Config, v string) { c.EV = parseInt(KeyEV, v, c) },
}, },
{ {
Name: KeyExposure, Name: KeyExposure,
Type: "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks", Type: "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks",
Update: func(c *Config, v string) { c.Exposure = v }, Update: func(c *Config, v string) { c.Exposure = v },
}, },
{ {
Name: KeyFileFPS, Name: KeyFileFPS,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.FileFPS = parseUint(KeyFileFPS, v, c) }, Update: func(c *Config, v string) { c.FileFPS = parseUint(KeyFileFPS, v, c) },
Validate: func(c *Config) { Validate: func(c *Config) {
if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) { if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) {
@ -250,7 +268,7 @@ var Variables = []struct {
}, },
}, },
{ {
Name: KeyFilters, Name: KeyFilters,
Type: "enums:NoOp,MOG,VariableFPS,KNN,Difference,Basic", Type: "enums:NoOp,MOG,VariableFPS,KNN,Difference,Basic",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
filters := strings.Split(v, ",") filters := strings.Split(v, ",")
@ -267,7 +285,7 @@ var Variables = []struct {
}, },
{ {
Name: KeyFrameRate, Name: KeyFrameRate,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.FrameRate = parseUint(KeyFrameRate, v, c) }, Update: func(c *Config, v string) { c.FrameRate = parseUint(KeyFrameRate, v, c) },
Validate: func(c *Config) { Validate: func(c *Config) {
if c.FrameRate <= 0 || c.FrameRate > 60 { if c.FrameRate <= 0 || c.FrameRate > 60 {
@ -278,21 +296,21 @@ var Variables = []struct {
}, },
{ {
Name: KeyHeight, Name: KeyHeight,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.Height = parseUint(KeyHeight, v, c) }, Update: func(c *Config, v string) { c.Height = parseUint(KeyHeight, v, c) },
}, },
{ {
Name: KeyHorizontalFlip, Name: KeyHorizontalFlip,
Type: typeBool, Type: typeBool,
Update: func(c *Config, v string) { c.HorizontalFlip = parseBool(KeyHorizontalFlip, v, c) }, Update: func(c *Config, v string) { c.HorizontalFlip = parseBool(KeyHorizontalFlip, v, c) },
}, },
{ {
Name: KeyHTTPAddress, Name: KeyHTTPAddress,
Type: typeString, Type: typeString,
Update: func(c *Config, v string) { c.HTTPAddress = v }, Update: func(c *Config, v string) { c.HTTPAddress = v },
}, },
{ {
Name: KeyInput, Name: KeyInput,
Type: "enum:raspivid,raspistill,rtsp,v4l,file,audio", Type: "enum:raspivid,raspistill,rtsp,v4l,file,audio",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
c.Input = parseEnum( c.Input = parseEnum(
@ -319,7 +337,7 @@ var Variables = []struct {
}, },
}, },
{ {
Name: KeyInputCodec, Name: KeyInputCodec,
Type: "enum:h264,h265,mjpeg,jpeg,pcm,adpcm", Type: "enum:h264,h265,mjpeg,jpeg,pcm,adpcm",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
c.InputCodec = v c.InputCodec = v
@ -333,16 +351,16 @@ var Variables = []struct {
}, },
{ {
Name: KeyInputPath, Name: KeyInputPath,
Type: typeString, Type: typeString,
Update: func(c *Config, v string) { c.InputPath = v }, Update: func(c *Config, v string) { c.InputPath = v },
}, },
{ {
Name: KeyISO, Name: KeyISO,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.ISO = parseUint(KeyISO, v, c) }, Update: func(c *Config, v string) { c.ISO = parseUint(KeyISO, v, c) },
}, },
{ {
Name: KeyLogging, Name: KeyLogging,
Type: "enum:Debug,Info,Warning,Error,Fatal", Type: "enum:Debug,Info,Warning,Error,Fatal",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
switch v { switch v {
@ -371,18 +389,18 @@ var Variables = []struct {
}, },
{ {
Name: KeyLoop, Name: KeyLoop,
Type: typeBool, Type: typeBool,
Update: func(c *Config, v string) { c.Loop = parseBool(KeyLoop, v, c) }, Update: func(c *Config, v string) { c.Loop = parseBool(KeyLoop, v, c) },
}, },
{ {
Name: KeyMinFPS, Name: KeyMinFPS,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MinFPS = parseUint(KeyMinFPS, v, c) }, Update: func(c *Config, v string) { c.MinFPS = parseUint(KeyMinFPS, v, c) },
Validate: func(c *Config) { c.MinFPS = lessThanOrEqual(KeyMinFPS, c.MinFPS, 0, c, defaultMinFPS) }, Validate: func(c *Config) { c.MinFPS = lessThanOrEqual(KeyMinFPS, c.MinFPS, 0, c, defaultMinFPS) },
}, },
{ {
Name: KeyMinFrames, Name: KeyMinFrames,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MinFrames = parseUint(KeyMinFrames, v, c) }, Update: func(c *Config, v string) { c.MinFrames = parseUint(KeyMinFrames, v, c) },
Validate: func(c *Config) { Validate: func(c *Config) {
const maxMinFrames = 1000 const maxMinFrames = 1000
@ -393,7 +411,7 @@ var Variables = []struct {
}, },
}, },
{ {
Name: KeyMode, Name: KeyMode,
Type: "enum:Normal,Paused,Burst,Loop", Type: "enum:Normal,Paused,Burst,Loop",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
c.Loop = false c.Loop = false
@ -404,26 +422,26 @@ var Variables = []struct {
}, },
{ {
Name: KeyMotionDownscaling, Name: KeyMotionDownscaling,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MotionDownscaling = parseUint(KeyMotionDownscaling, v, c) }, Update: func(c *Config, v string) { c.MotionDownscaling = parseUint(KeyMotionDownscaling, v, c) },
}, },
{ {
Name: KeyMotionHistory, Name: KeyMotionHistory,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MotionHistory = parseUint(KeyMotionHistory, v, c) }, Update: func(c *Config, v string) { c.MotionHistory = parseUint(KeyMotionHistory, v, c) },
}, },
{ {
Name: KeyMotionInterval, Name: KeyMotionInterval,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MotionInterval = parseUint(KeyMotionInterval, v, c) }, Update: func(c *Config, v string) { c.MotionInterval = parseUint(KeyMotionInterval, v, c) },
}, },
{ {
Name: KeyMotionKernel, Name: KeyMotionKernel,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MotionKernel = parseUint(KeyMotionKernel, v, c) }, Update: func(c *Config, v string) { c.MotionKernel = parseUint(KeyMotionKernel, v, c) },
}, },
{ {
Name: KeyMotionMinArea, Name: KeyMotionMinArea,
Type: typeFloat, Type: typeFloat,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
f, err := strconv.ParseFloat(v, 64) f, err := strconv.ParseFloat(v, 64)
@ -435,16 +453,16 @@ var Variables = []struct {
}, },
{ {
Name: KeyMotionPadding, Name: KeyMotionPadding,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MotionPadding = parseUint(KeyMotionPadding, v, c) }, Update: func(c *Config, v string) { c.MotionPadding = parseUint(KeyMotionPadding, v, c) },
}, },
{ {
Name: KeyMotionPixels, Name: KeyMotionPixels,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.MotionPixels = parseUint(KeyMotionPixels, v, c) }, Update: func(c *Config, v string) { c.MotionPixels = parseUint(KeyMotionPixels, v, c) },
}, },
{ {
Name: KeyMotionThreshold, Name: KeyMotionThreshold,
Type: typeFloat, Type: typeFloat,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
f, err := strconv.ParseFloat(v, 64) f, err := strconv.ParseFloat(v, 64)
@ -455,7 +473,7 @@ var Variables = []struct {
}, },
}, },
{ {
Name: KeyOutput, Name: KeyOutput,
Type: "enum:File,HTTP,RTMP,RTP", Type: "enum:File,HTTP,RTMP,RTP",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
c.Outputs = make([]uint8, 1) c.Outputs = make([]uint8, 1)
@ -477,11 +495,11 @@ var Variables = []struct {
}, },
{ {
Name: KeyOutputPath, Name: KeyOutputPath,
Type: typeString, Type: typeString,
Update: func(c *Config, v string) { c.OutputPath = v }, Update: func(c *Config, v string) { c.OutputPath = v },
}, },
{ {
Name: KeyOutputs, Name: KeyOutputs,
Type: "enums:File,HTTP,RTMP,RTP", Type: "enums:File,HTTP,RTMP,RTP",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
outputs := strings.Split(v, ",") outputs := strings.Split(v, ",")
@ -512,18 +530,18 @@ var Variables = []struct {
}, },
{ {
Name: KeyPSITime, Name: KeyPSITime,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.PSITime = parseUint(KeyPSITime, v, c) }, Update: func(c *Config, v string) { c.PSITime = parseUint(KeyPSITime, v, c) },
Validate: func(c *Config) { c.PSITime = lessThanOrEqual(KeyPSITime, c.PSITime, 0, c, defaultPSITime) }, Validate: func(c *Config) { c.PSITime = lessThanOrEqual(KeyPSITime, c.PSITime, 0, c, defaultPSITime) },
}, },
{ {
Name: KeyQuantization, Name: KeyQuantization,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.Quantization = parseUint(KeyQuantization, v, c) }, Update: func(c *Config, v string) { c.Quantization = parseUint(KeyQuantization, v, c) },
}, },
{ {
Name: KeyPoolCapacity, Name: KeyPoolCapacity,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.PoolCapacity = parseUint(KeyPoolCapacity, v, c) }, Update: func(c *Config, v string) { c.PoolCapacity = parseUint(KeyPoolCapacity, v, c) },
Validate: func(c *Config) { Validate: func(c *Config) {
c.PoolCapacity = lessThanOrEqual(KeyPoolCapacity, c.PoolCapacity, 0, c, defaultPoolCapacity) c.PoolCapacity = lessThanOrEqual(KeyPoolCapacity, c.PoolCapacity, 0, c, defaultPoolCapacity)
@ -531,7 +549,7 @@ var Variables = []struct {
}, },
{ {
Name: KeyPoolStartElementSize, Name: KeyPoolStartElementSize,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.PoolStartElementSize = parseUint("PoolStartElementSize", v, c) }, Update: func(c *Config, v string) { c.PoolStartElementSize = parseUint("PoolStartElementSize", v, c) },
Validate: func(c *Config) { Validate: func(c *Config) {
c.PoolStartElementSize = lessThanOrEqual("PoolStartElementSize", c.PoolStartElementSize, 0, c, defaultPoolStartElementSize) c.PoolStartElementSize = lessThanOrEqual("PoolStartElementSize", c.PoolStartElementSize, 0, c, defaultPoolStartElementSize)
@ -539,14 +557,14 @@ var Variables = []struct {
}, },
{ {
Name: KeyPoolWriteTimeout, Name: KeyPoolWriteTimeout,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.PoolWriteTimeout = parseUint(KeyPoolWriteTimeout, v, c) }, Update: func(c *Config, v string) { c.PoolWriteTimeout = parseUint(KeyPoolWriteTimeout, v, c) },
Validate: func(c *Config) { Validate: func(c *Config) {
c.PoolWriteTimeout = lessThanOrEqual(KeyPoolWriteTimeout, c.PoolWriteTimeout, 0, c, defaultPoolWriteTimeout) c.PoolWriteTimeout = lessThanOrEqual(KeyPoolWriteTimeout, c.PoolWriteTimeout, 0, c, defaultPoolWriteTimeout)
}, },
}, },
{ {
Name: KeyRecPeriod, Name: KeyRecPeriod,
Type: typeFloat, Type: typeFloat,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
_v, err := strconv.ParseFloat(v, 64) _v, err := strconv.ParseFloat(v, 64)
@ -558,17 +576,17 @@ var Variables = []struct {
}, },
{ {
Name: KeyRotation, Name: KeyRotation,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.Rotation = parseUint(KeyRotation, v, c) }, Update: func(c *Config, v string) { c.Rotation = parseUint(KeyRotation, v, c) },
}, },
{ {
Name: KeyRTMPURL, Name: KeyRTMPURL,
Type: typeString, Type: typeString,
Update: func(c *Config, v string) { c.RTMPURL = v }, Update: func(c *Config, v string) { c.RTMPURL = v },
}, },
{ {
Name: KeyRTPAddress, Name: KeyRTPAddress,
Type: typeString, Type: typeString,
Update: func(c *Config, v string) { c.RTPAddress = v }, Update: func(c *Config, v string) { c.RTPAddress = v },
Validate: func(c *Config) { Validate: func(c *Config) {
if c.RTPAddress == "" { if c.RTPAddress == "" {
@ -579,21 +597,21 @@ var Variables = []struct {
}, },
{ {
Name: KeySampleRate, Name: KeySampleRate,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.SampleRate = parseUint(KeySampleRate, v, c) }, Update: func(c *Config, v string) { c.SampleRate = parseUint(KeySampleRate, v, c) },
}, },
{ {
Name: KeySaturation, Name: KeySaturation,
Type: typeInt, Type: typeInt,
Update: func(c *Config, v string) { c.Saturation = parseInt(KeySaturation, v, c) }, Update: func(c *Config, v string) { c.Saturation = parseInt(KeySaturation, v, c) },
}, },
{ {
Name: KeySharpness, Name: KeySharpness,
Type: typeInt, Type: typeInt,
Update: func(c *Config, v string) { c.Sharpness = parseInt(KeySharpness, v, c) }, Update: func(c *Config, v string) { c.Sharpness = parseInt(KeySharpness, v, c) },
}, },
{ {
Name: KeyJPEGQuality, Name: KeyJPEGQuality,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
_v, err := strconv.Atoi(v) _v, err := strconv.Atoi(v)
@ -604,7 +622,7 @@ var Variables = []struct {
}, },
}, },
{ {
Name: KeySuppress, Name: KeySuppress,
Type: typeBool, Type: typeBool,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
c.Suppress = parseBool(KeySuppress, v, c) c.Suppress = parseBool(KeySuppress, v, c)
@ -612,7 +630,7 @@ var Variables = []struct {
}, },
}, },
{ {
Name: KeyTimelapseInterval, Name: KeyTimelapseInterval,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
_v, err := strconv.Atoi(v) _v, err := strconv.Atoi(v)
@ -623,7 +641,7 @@ var Variables = []struct {
}, },
}, },
{ {
Name: KeyTimelapseDuration, Name: KeyTimelapseDuration,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
_v, err := strconv.Atoi(v) _v, err := strconv.Atoi(v)
@ -635,11 +653,11 @@ var Variables = []struct {
}, },
{ {
Name: KeyVBRBitrate, Name: KeyVBRBitrate,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.VBRBitrate = parseUint(KeyVBRBitrate, v, c) }, Update: func(c *Config, v string) { c.VBRBitrate = parseUint(KeyVBRBitrate, v, c) },
}, },
{ {
Name: KeyVBRQuality, Name: KeyVBRQuality,
Type: "enum:standard,fair,good,great,excellent", Type: "enum:standard,fair,good,great,excellent",
Update: func(c *Config, v string) { Update: func(c *Config, v string) {
c.VBRQuality = Quality(parseEnum( c.VBRQuality = Quality(parseEnum(
@ -658,12 +676,12 @@ var Variables = []struct {
}, },
{ {
Name: KeyVerticalFlip, Name: KeyVerticalFlip,
Type: typeBool, Type: typeBool,
Update: func(c *Config, v string) { c.VerticalFlip = parseBool(KeyVerticalFlip, v, c) }, Update: func(c *Config, v string) { c.VerticalFlip = parseBool(KeyVerticalFlip, v, c) },
}, },
{ {
Name: KeyWidth, Name: KeyWidth,
Type: typeUint, Type: typeUint,
Update: func(c *Config, v string) { c.Width = parseUint(KeyWidth, v, c) }, Update: func(c *Config, v string) { c.Width = parseUint(KeyWidth, v, c) },
}, },
} }

View File

@ -41,11 +41,19 @@ import (
"gocv.io/x/gocv" "gocv.io/x/gocv"
) )
// Homography constants
const (
ransacThreshold = 3.0 // Maximum allowed reprojection error to treat a point pair as an inlier.
maxIter = 2000 // The maximum number of RANSAC iterations.
confidence = 0.995 // Confidence level, between 0 and 1.
)
// TurbiditySensor is a software based turbidity sensor that uses CV to determine sharpness and constrast level // TurbiditySensor is a software based turbidity sensor that uses CV to determine sharpness and constrast level
// of a chessboard-like target submerged in water that can be correlated to turbidity/visibility values. // of a chessboard-like target submerged in water that can be correlated to turbidity/visibility values.
type TurbiditySensor struct { type TurbiditySensor struct {
template, templateCorners gocv.Mat template, templateCorners gocv.Mat
standard, standardCorners gocv.Mat standard, standardCorners gocv.Mat
H gocv.Mat
k1, k2, sobelFilterSize int k1, k2, sobelFilterSize int
scale, alpha float64 scale, alpha float64
log logger.Logger log logger.Logger
@ -56,6 +64,7 @@ func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int
ts := new(TurbiditySensor) ts := new(TurbiditySensor)
templateCorners := gocv.NewMat() templateCorners := gocv.NewMat()
standardCorners := gocv.NewMat() standardCorners := gocv.NewMat()
mask := gocv.NewMat()
// Validate template image is not empty and has valid corners. // Validate template image is not empty and has valid corners.
if template.Empty() { if template.Empty() {
@ -74,8 +83,10 @@ func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int
if !gocv.FindChessboardCorners(standard, image.Pt(3, 3), &standardCorners, gocv.CalibCBNormalizeImage) { if !gocv.FindChessboardCorners(standard, image.Pt(3, 3), &standardCorners, gocv.CalibCBNormalizeImage) {
return nil, errors.New("could not find corners in standard image") return nil, errors.New("could not find corners in standard image")
} }
ts.standard = standard ts.standard = standard
ts.standardCorners = standardCorners ts.standardCorners = standardCorners
ts.H = gocv.FindHomography(ts.standardCorners, &ts.templateCorners, gocv.HomograpyMethodRANSAC, ransacThreshold, &mask, maxIter, confidence)
ts.k1, ts.k2, ts.sobelFilterSize = k1, k2, sobelFilterSize ts.k1, ts.k2, ts.sobelFilterSize = k1, k2, sobelFilterSize
ts.alpha, ts.scale = alpha, scale ts.alpha, ts.scale = alpha, scale
@ -191,13 +202,6 @@ func (ts TurbiditySensor) evaluateBlockAMEE(img gocv.Mat, xStart, yStart, xEnd,
// transform will search img for matching template. Returns the transformed image which best match the template. // transform will search img for matching template. Returns the transformed image which best match the template.
func (ts TurbiditySensor) transform(img gocv.Mat) (gocv.Mat, error) { func (ts TurbiditySensor) transform(img gocv.Mat) (gocv.Mat, error) {
out := gocv.NewMat() out := gocv.NewMat()
mask := gocv.NewMat()
imgCorners := ts.standardCorners
const (
ransacThreshold = 3.0 // Maximum allowed reprojection error to treat a point pair as an inlier.
maxIter = 2000 // The maximum number of RANSAC iterations.
confidence = 0.995 // Confidence level, between 0 and 1.
)
if img.Empty() { if img.Empty() {
return out, errors.New("image is empty, cannot transform") return out, errors.New("image is empty, cannot transform")
@ -206,8 +210,7 @@ func (ts TurbiditySensor) transform(img gocv.Mat) (gocv.Mat, error) {
// if !gocv.FindChessboardCorners(img, image.Pt(3, 3), &imgCorners, gocv.CalibCBFastCheck) {} // if !gocv.FindChessboardCorners(img, image.Pt(3, 3), &imgCorners, gocv.CalibCBFastCheck) {}
// Find and apply transformation. // Find and apply transformation.
H := gocv.FindHomography(imgCorners, &ts.templateCorners, gocv.HomograpyMethodRANSAC, ransacThreshold, &mask, maxIter, confidence) gocv.WarpPerspective(img, &out, ts.H, image.Pt(ts.template.Rows(), ts.template.Cols()))
gocv.WarpPerspective(img, &out, H, image.Pt(ts.template.Rows(), ts.template.Cols()))
gocv.CvtColor(out, &out, gocv.ColorRGBToGray) gocv.CvtColor(out, &out, gocv.ColorRGBToGray)
return out, nil return out, nil
} }

View File

@ -42,8 +42,8 @@ import (
) )
const ( const (
nImages = 10 // Number of images to test. (Max 13) nImages = 12 // Number of images to test. (Max 13)
nSamples = 1 // Number of samples for each image. (Max 10) nSamples = 10 // Number of samples for each image. (Max 10)
increment = 2.5 // Increment of the turbidity level. increment = 2.5 // Increment of the turbidity level.
) )
@ -62,7 +62,7 @@ const (
func TestImages(t *testing.T) { func TestImages(t *testing.T) {
const ( const (
k1, k2 = 8, 8 k1, k2 = 4, 4
filterSize = 3 filterSize = 3
scale, alpha = 1.0, 1.0 scale, alpha = 1.0, 1.0
) )
@ -85,7 +85,7 @@ func TestImages(t *testing.T) {
for i := range imgs { for i := range imgs {
imgs[i] = make([]gocv.Mat, nSamples) imgs[i] = make([]gocv.Mat, nSamples)
for j := range imgs[i] { for j := range imgs[i] {
imgs[i][j] = gocv.IMRead(fmt.Sprintf("images/t-%v/000%v.jpg", i, j), gocv.IMReadGrayScale) imgs[i][j] = gocv.IMRead(fmt.Sprintf("images/t-%v/000%v.jpg", i, j), gocv.IMReadColor)
} }
} }
@ -94,6 +94,13 @@ func TestImages(t *testing.T) {
t.Fatalf("could not create turbidity sensor: %v", err) t.Fatalf("could not create turbidity sensor: %v", err)
} }
for i := 0; i < ts.H.Rows(); i++ {
for j := 0; j < ts.H.Cols(); j++ {
fmt.Printf(" %v,\t", ts.H.GetDoubleAt(i, j))
}
fmt.Println()
}
results, err := NewResults(nImages) results, err := NewResults(nImages)
if err != nil { if err != nil {
t.Fatalf("could not create results: %v", err) t.Fatalf("could not create results: %v", err)
@ -111,7 +118,7 @@ func TestImages(t *testing.T) {
results.Update(stat.Mean(sample_result.Sharpness, nil), stat.Mean(sample_result.Contrast, nil), float64(i)*increment, i) results.Update(stat.Mean(sample_result.Sharpness, nil), stat.Mean(sample_result.Contrast, nil), float64(i)*increment, i)
} }
err = plotResults(results.Turbidity, results.Sharpness, results.Contrast) err = plotResults(results.Turbidity, normalize(results.Sharpness), normalize(results.Contrast))
if err != nil { if err != nil {
t.Fatalf("plotting Failed: %v", err) t.Fatalf("plotting Failed: %v", err)
} }