object detection: add minPixels

This commit is contained in:
Jehiah Czebotar
2017-06-10 13:48:44 -04:00
parent a17b865a00
commit 3707ffba2b
5 changed files with 101 additions and 23 deletions

View File

@@ -33,7 +33,7 @@ func (f FrameAnalysis) NeedsMore() bool {
return len(f.images) < analysFrameCount
}
func (f *FrameAnalysis) Calculate(bg *image.RGBA, blurRadius, contiguousPixels int, tolerance uint8) {
func (f *FrameAnalysis) Calculate(bg *image.RGBA, blurRadius, contiguousPixels, minMass int, tolerance uint8) {
log.Printf("analysis covering %d frames starting at %s", len(f.images), f.Timestamp)
if len(f.images) == 0 {
return
@@ -43,7 +43,7 @@ func (f *FrameAnalysis) Calculate(bg *image.RGBA, blurRadius, contiguousPixels i
highlight := SubImage(src, bg, tolerance)
highlight = Blur(highlight, blurRadius)
f.Highlight = dataImg(highlight, "image/png")
f.Colored = dataImg(labelimg.New(highlight, contiguousPixels), "image/png")
f.Colored = dataImg(labelimg.New(highlight, contiguousPixels, minMass), "image/png")
// animate 2s of video in 1s (drop 50% of frames)
// ^^ == highlight_gif
@@ -60,7 +60,7 @@ func (f *FrameAnalysis) Calculate(bg *image.RGBA, blurRadius, contiguousPixels i
detected := SubImage(sim.(*image.RGBA), bgresize, tolerance)
detected = Blur(detected, blurRadius)
highlightGif = append(highlightGif, detected)
colored := labelimg.New(detected, contiguousPixels)
colored := labelimg.New(detected, contiguousPixels, 1)
coloredGif = append(coloredGif, colored)
}
f.BaseGif = dataGif(NewGIF(baseGif))

View File

@@ -89,7 +89,7 @@ func main() {
p.Tolerance = getuint8("tolerance", 40)
p.Blur = int(geti64("blur", 2))
p.ContiguousPixels = int(geti64("contiguous_pixels", 3))
p.MinMass = geti64("min_mass", 100)
p.MinMass = int(geti64("min_mass", 50))
p.Seek = getf64("seek", 0)
p.Step = int(geti64("next", 0))

View File

@@ -40,7 +40,7 @@ type Project struct {
Tolerance uint8 `json:"tolerance"`
Blur int `json:"blur"`
ContiguousPixels int `json:"contiguous_pixels"`
MinMass int64 `json:"min_mass"`
MinMass int `json:"min_mass"`
Seek float64 `json:"seek"`
Calibrations []*Calibration `json:"calibrations"`
@@ -246,7 +246,7 @@ func (p *Project) Run() error {
}
if p.Step == 5 && bgavg != nil {
analysis.Calculate(bgavg, p.Blur, p.ContiguousPixels, p.Tolerance)
analysis.Calculate(bgavg, p.Blur, p.ContiguousPixels, p.MinMass, p.Tolerance)
p.Response.FrameAnalysis = append(p.Response.FrameAnalysis, *analysis)
}

View File

@@ -17,16 +17,16 @@ func abs(i int) int {
// type LabelImage image.Paletted
// New creates a new paletted image by detecting contiguous blobs of non-zero color in `g`.
// overlap is defined as +/- distance on x and y axis. Diagonal overlap is not detected for
// distance > 1
func New(g *image.Gray, distance int) *image.Paletted {
// overlap is defined as +/- contiguous pixes on x and y axis. Diagonal overlap is detected for
// contiguousPixels > 1. Groups of pixels smaller than minPixels are not returned
func New(g *image.Gray, contiguousPixels, minPixels int) *image.Paletted {
pb := image.Rect(0, 0, g.Bounds().Dx(), g.Bounds().Dy())
// log.Printf("new image %v", pb)
p := image.NewPaletted(pb, nil)
if distance < 1 {
if contiguousPixels < 1 {
panic("negative distance not allowed")
}
minOffset, maxOffset := -1*distance, distance
minOffset, maxOffset := -1*contiguousPixels, contiguousPixels
for x := g.Rect.Min.X; x < g.Rect.Max.X; x++ {
for y := g.Rect.Min.Y; y < g.Rect.Max.Y; y++ {
@@ -39,7 +39,7 @@ func New(g *image.Gray, distance int) *image.Paletted {
// do overlap checks to see if this is a new point or if it overlaps
for xo := minOffset; xo <= maxOffset; xo++ {
for yo := minOffset; yo <= maxOffset; yo++ {
if abs(xo)+abs(yo) > maxOffset{
if abs(xo)+abs(yo) > maxOffset {
continue
}
if xo == 0 && yo == 0 {
@@ -89,6 +89,21 @@ func New(g *image.Gray, distance int) *image.Paletted {
p.SetColorIndex(x-g.Rect.Min.X, y-g.Rect.Min.Y, i)
}
}
// detect groups smaller than minPixels
if minPixels > 1 {
var hits [255]int
for _, pixel := range p.Pix {
hits[pixel]++
}
// walk in reverse
for n := 254; n > 0; n-- {
if hits[n] > 0 && hits[n] <= minPixels {
replaceColor(p, uint8(n), 0)
}
}
}
p.Palette = Glasbey
return p
}

View File

@@ -1,25 +1,88 @@
package labelimg
import (
"fmt"
"image"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLabelimg(t *testing.T) {
func prettyGrey(g *image.Gray) string {
return prettyPix(g.Pix, g.Rect, g.PixOffset)
}
func prettyPaletted(g *image.Paletted) string {
return prettyPix(g.Pix, g.Rect, g.PixOffset)
}
l := &image.Gray{
Pix: []uint8{3, 3, 0, 4,
3, 0, 4, 4,
},
Stride: 4,
Rect: image.Rect(1, 1, 5, 3), // 4 x 2
func prettyPix(pix []uint8, r image.Rectangle, pixOffset func(int, int) int) string {
var s string = "\n"
for y := r.Min.Y; y < r.Max.Y; y++ {
for x := r.Min.X; x < r.Max.X; x++ {
s += fmt.Sprintf("%02x", pix[pixOffset(x, y)])
}
s += "\n"
}
return s
}
func TestLabelimg(t *testing.T) {
type testCase struct {
Rect image.Rectangle
Pix []uint8
Expect []uint8
ContiguousPixels int
MinPixels int
}
labeled := New(l, 1)
assert.Equal(t, []uint8{1, 1, 0, 2,
1, 0, 2, 2}, labeled.Pix)
tests := []testCase{
{
Rect: image.Rect(1, 1, 5, 3), // 4 x 2
Pix: []uint8{
3, 3, 0, 4,
3, 0, 4, 4,
},
Expect: []uint8{
1, 1, 0, 2,
1, 0, 2, 2,
},
ContiguousPixels: 1,
MinPixels: 1,
},
{
Rect: image.Rect(0, 0, 4, 4),
Pix: []uint8{
3, 3, 0, 4,
3, 0, 0, 4,
0, 0, 4, 4,
0, 0, 4, 4,
},
Expect: []uint8{
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 1, 1,
0, 0, 1, 1,
},
ContiguousPixels: 1,
MinPixels: 3,
},
}
for i, tc := range tests {
tc := tc
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
t.Parallel()
start := &image.Gray{
Pix: tc.Pix,
Rect: tc.Rect,
Stride: tc.Rect.Dx(),
}
t.Logf("start %s", prettyGrey(start))
labeled := New(start, tc.ContiguousPixels, tc.MinPixels)
t.Logf("labeled %s", prettyPaletted(labeled))
start.Pix = tc.Expect
t.Logf("Expect %s", prettyGrey(start))
assert.Equal(t, tc.Expect, labeled.Pix)
})
}
}