TrackVehicles across frames

This commit is contained in:
Jehiah Czebotar
2017-07-09 14:03:25 -04:00
parent 31f85862b1
commit 75f76a7129
4 changed files with 98 additions and 28 deletions

View File

@@ -2,6 +2,8 @@ package main
import (
"fmt"
"image"
"math"
"time"
"labelimg"
@@ -41,3 +43,62 @@ type FramePosition struct {
Time time.Duration
Positions []labelimg.Label
}
type VehiclePosition struct {
Frame int
Time time.Duration
VehicleID int
Position labelimg.Label
}
// TrackVehicles tracks detected objects and correlates across frames
// based on logic from identifyvehicles from https://github.com/mbauman/TrafficSpeed/blob/master/TrafficSpeed.ipynb
func TrackVehicles(frames []FramePosition) []VehiclePosition {
var vehicleCount int
var vehicles []VehiclePosition
var lastFrameVehicles []VehiclePosition
for _, frame := range frames {
var currentFrameVehicles []VehiclePosition
for _, position := range frame.Positions {
// is position overlap from lastFrame
var vehicleID int
// TODO: use median point not center
if closest := ClosestPosition(position.Center, lastFrameVehicles); position.Center.In(closest.Position.Bounds) {
vehicleID = closest.VehicleID
} else {
vehicleCount++
vehicleID = vehicleCount
}
currentFrameVehicles = append(currentFrameVehicles, VehiclePosition{
Frame: frame.Frame,
Time: frame.Time,
VehicleID: vehicleID,
Position: position,
})
}
lastFrameVehicles = currentFrameVehicles
vehicles = append(vehicles, currentFrameVehicles...)
}
return vehicles
}
func ClosestPosition(point image.Point, v []VehiclePosition) VehiclePosition {
var closest VehiclePosition
var min float64 = -1
for _, p := range v {
// TODO: use median point not center
d := distance(point, p.Position.Center)
if min == -1 || d < min {
min = d
closest = p
}
}
return closest
}
func distance(a, b image.Point) float64 {
x := math.Abs(float64(a.X) - float64(b.X))
y := math.Abs(float64(a.Y) - float64(b.Y))
return math.Sqrt((x * x) + (y * y))
}

View File

@@ -67,12 +67,18 @@ type Response struct {
Step4MaskImg template.URL `json:"step_4_mask_img,omitempty"`
BackgroundImg template.URL `json:"background_img,omitempty"`
FrameAnalysis []FrameAnalysis `json:"frame_analysis,omitempty"`
FramePositions []FramePosition
VehiclePositions []VehiclePosition
Step6Img template.URL `json:"step_6_img,omitempty"`
DebugImages []template.URL
}
type frameImage struct {
Frame int
Time time.Duration
Image *image.RGBA
}
func NewProject(f string) *Project {
// overview_gif
// overview_img
@@ -135,6 +141,8 @@ func (p *Project) Run() error {
bg := &avgimg.MedianRGBA{}
var bgavg *image.RGBA
var err error
var framePositions []FramePosition
var pendingAnalysis []frameImage
analyzer := &Analyzer{
BWCutoff: p.Tolerance,
BlurRadius: p.Blur,
@@ -261,13 +269,28 @@ func (p *Project) Run() error {
log.Printf("saving frame %d for analysis later", frame)
analysis.images = append(analysis.images, rgbImg)
}
// set every frame, so this ends w/ the last value
if p.Step == 6 && bgavg == nil {
pendingAnalysis = append(pendingAnalysis, frameImage{frame, pkt.Time, rgbImg})
}
if p.Step == 6 && bgavg != nil {
// process pending frames
log.Printf("extracting vehicle position from %d pending frames", len(pendingAnalysis))
for _, pf := range pendingAnalysis {
if pf.Frame%50 == 0 && pf.Frame > 0 {
log.Printf("... frame %d", pf.Frame)
}
positions := analyzer.Positions(pf.Image)
if len(positions) > 0 {
framePositions = append(framePositions, FramePosition{pf.Frame, pf.Time, positions})
}
}
pendingAnalysis = nil
positions := analyzer.Positions(rgbImg)
if len(positions) > 0 {
p.Response.FramePositions = append(p.Response.FramePositions, FramePosition{frame, pkt.Time, positions})
framePositions = append(framePositions, FramePosition{frame, pkt.Time, positions})
}
}
if frame == 500 {
@@ -275,6 +298,10 @@ func (p *Project) Run() error {
}
}
if p.Step == 6 {
p.Response.VehiclePositions = TrackVehicles(framePositions)
}
if p.Step == 5 && bgavg != nil {
analysis.Calculate(bgavg, p.Blur, p.ContiguousPixels, p.MinMass, p.Tolerance)
p.Response.FrameAnalysis = append(p.Response.FrameAnalysis, *analysis)

View File

@@ -277,40 +277,22 @@ const tpl = `
{{ if eq .Step 6 }}
<h2>Step 6: Position Detection</h2>
...
{{ if .Response.FramePositions }}
{{ if .Response.VehiclePositions }}
<table class="table table-striped">
<thead>
<tr>
<th>Frame</th><th>Time</th><th>Position</th>
<th>Frame</th><th>Time</th><th>Vehicle</th><th>Position</th><th>Mass</th><th>Size</th>
</tr>
</thead>
<tbody>
{{ range .Response.FramePositions }}
{{ range .Response.VehiclePositions }}
<tr>
<th>{{.Frame}}</th>
<td>{{.Time}}</td>
<td>
{{ if .Positions }}
<table class="table table-striped">
<thead>
<tr>
<th></th><th>Mass</th><th>Position</th><th>Size</th>
</tr>
</thead>
<tbody>
{{ range $i, $p := .Positions }}
<tr>
<th><small>{{$i}}</small></th>
<td>{{$p.Pixels }} pixels</td>
<td>{{$p.Center.X}}x{{$p.Center.Y}}</td>
<td>{{$p.Bounds.Dx}}x{{$p.Bounds.Dy}}</td>
</tr>
{{ end }}
</tbody>
</table>
{{ end }}
</td>
<td>{{.VehicleID}}</td>
<td>{{.Position.Center.X}}x{{.Position.Center.Y}}</td>
<td>{{.Position.Pixels }} pixels</td>
<td>{{.Position.Bounds.Dx}}x{{.Position.Bounds.Dy}}</td>
</tr>
{{ end }}
</tbody>

View File

@@ -84,7 +84,7 @@ func New(g *image.Gray, contiguousPixels, minPixels int) *image.Paletted {
if i == 0 {
// add new color
if len(p.Palette) == 254 {
log.Printf("skipping detectino of x,y (%d,%d); over 255 max", x, y)
log.Printf("skipping detection of x,y (%d,%d); over 255 max", x, y)
continue
}
i = uint8(len(p.Palette))