Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions Manifest.toml

This file was deleted.

28 changes: 28 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,30 @@
name = "ImageTracking"
uuid = "5cb2747a-620a-11e9-38cd-3dc946e3e5f7"
version = "0.1.0"

[deps]
AxisAlgorithms = "13072b0f-2c55-5437-9ae7-d433b7a33950"
CoordinateTransformations = "150eb455-5306-5404-9cee-2592286d6298"
ImageFiltering = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[compat]
AxisAlgorithms = ">= 0.2"
ImageFiltering = ">= 0.3"
Images = ">= 0.17"
Interpolations = ">= 0.7"
julia = ">= 0.7"

[extras]
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
QuartzImageIO = "dca85d43-d64c-5e67-8c65-017450d5d020"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990"

[targets]
test = ["Test", "TestImages", "ImageMagick", "QuartzImageIO"]
6 changes: 0 additions & 6 deletions REQUIRE

This file was deleted.

5 changes: 3 additions & 2 deletions src/ImageTracking.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ include("core.jl")
include("optical_flow.jl")
include("haar.jl")
include("utility.jl")
include("tracker.jl")
include("tracker/tracker.jl")

export

Expand Down Expand Up @@ -59,6 +59,7 @@ export

# tracking algorithms
BoxROI,
TrackerBoosting
TrackerBoosting,
init_tracker

end
17 changes: 0 additions & 17 deletions src/boosting_tracker/boosting_tracker.jl

This file was deleted.

19 changes: 10 additions & 9 deletions src/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,23 @@ function pinv2x2(M::AbstractArray)
U*D*V'
end

mutable struct TrackerTargetState
abstract type AbstractTrackerTargetState end

mutable struct BoostingTrackerTargetState <: AbstractTrackerTargetState
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be kept in a separate file as well.

#Initialized
position::SVector{2, Float64}
height::Int
width::Int
height::Integer
width::Integer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this stands for tracking region

position::SVector{2, Float64}
height::Integer
width::Integer

you can use Range to represent it as well.

is_target::Bool

#Uninitialized
responses::Array{T, 2} where T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of responses? If it is needed, then you should declare T as part of the struct, for example:
mutable struct BoostingTrackerTargetState{T <: AbstractArray} <: AbstractTrackerTargetState
responses::T
See: https://docs.julialang.org/en/v1/manual/performance-tips/index.html#Avoid-containers-with-abstract-type-parameters-1


TrackerTargetState(position::SVector{2, Float64}, height::Int, width::Int, is_target::Bool) = new(
position, height, width, is_target)
BoostingTrackerTargetState(position::SVector{2, Float64}, height::Integer, width::Integer, is_target::Bool) =
new(position, height, width, is_target)
end


mutable struct ConfidenceMap
states::Vector{TrackerTargetState}
confidence::Vector{Float64}
states::Vector{AbstractTrackerTargetState}
confidence::Vector{AbstractFloat}
end

29 changes: 29 additions & 0 deletions src/tracker/boosting_tracker.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#-----------------------------------
# Implementation of boosting tracker
#-----------------------------------

function init_tracker(tracker::TrackerBoosting, boxROI::BoxROI)
int_image = integral_image(boxROI.img)

# sampling
tracker.sampler = CurrentSample(tracker.sampler_overlap, tracker.sampler_search_factor, :positive)
int_box = BoxROI(int_image, boxROI.bound)
positive_samples = sample_tracker(tracker.sampler, int_box)

tracker.sampler.mode = :negative
negative_samples = sample_tracker(tracker.sampler, int_box)

if isempty(positive_samples) || isempty(negative_samples)
error("Couldn't get initial samples")
end

# compute harr haar_features

# model

# Run model estimation and update for initial iterations
end

function update_tracker(tracker::TrackerBoosting, image::AbstractArray)

end
34 changes: 14 additions & 20 deletions src/tracker.jl → src/tracker/tracker.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
abstract type AbstractTracker end
#---------------------
# TRACKER COMPONENTS
#---------------------

abstract type AbstractROI end
mutable struct BoxROI{T <: AbstractArray, S <: AbstractArray} <: AbstractROI
img::T
bound::S
end
include("../core.jl")
include("tracker_sampler.jl")
# include("tracker_state_estimator.jl")
# include("tracker_model.jl")
# include("tracker_features.jl")

mutable struct TrackerBoosting{I <: Int, F <: Float64, B <: BoxROI} <: AbstractTracker
abstract type AbstractTracker end
mutable struct TrackerBoosting{I <: Integer, F <: AbstractFloat, B <: BoxROI} <: AbstractTracker
# initialized
boxROI::B
num_of_classifiers::I
Expand All @@ -15,10 +18,11 @@ mutable struct TrackerBoosting{I <: Int, F <: Float64, B <: BoxROI} <: AbstractT
initial_iterations::I
num_of_features::I

sampler::CurrentSample
# constructor
function TrackerBoosting{I, F, B}(box::B, num_of_classifiers::I = 100, sampler_overlap::F = 0.99,
sampler_search_factor::F = 1.8, initial_iterations::I = 20,
num_of_features::I = 1050)where {I <: Int, F <: Float64, B <: BoxROI}
num_of_features::I = 1050)where {I <: Integer, F <: AbstractFloat, B <: BoxROI}
if size(box.bound, 1) != 4
error("Invalid bounding box size")
end
Expand All @@ -42,28 +46,18 @@ mutable struct TrackerBoosting{I <: Int, F <: Float64, B <: BoxROI} <: AbstractT
if box.bound[2] > box.bound[4]
error("Invalid bounding box")
end

new(box, num_of_classifiers, sampler_overlap, sampler_search_factor, initial_iterations, num_of_features)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that in general you can write inequalities like this in Julia:

 if 0 < x < 1 
   do something
end

rather than

if x > 0 && x < 1
  do something
end

The former is much more readable.


TrackerBoosting(boxROI::B, num_of_classifiers::I, sampler_overlap::F, sampler_search_factor::F,
initial_iterations::I, num_of_features::I) where {I <: Int, F <: Float64, B <: BoxROI} =
initial_iterations::I, num_of_features::I) where {I <: Integer, F <: AbstractFloat, B <: BoxROI} =
TrackerBoosting{I, F, B}(boxROI, num_of_classifiers, sampler_overlap, sampler_search_factor,
initial_iterations, num_of_features)

#---------------------
# TRACKER COMPONENTS
#---------------------

include("core.jl")
# include("tracker_state_estimator.jl")
# include("tracker_model.jl")
# include("tracker_sampler.jl")
# include("tracker_features.jl")

#------------------
# IMPLEMENTATIONS
#------------------

include("boosting_tracker/boosting_tracker.jl")
include("boosting_tracker.jl")
98 changes: 98 additions & 0 deletions src/tracker/tracker_sampler.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
abstract type AbstractROI end
mutable struct BoxROI{T <: AbstractArray, S <: AbstractArray} <: AbstractROI
Copy link
Member

@johnnychen94 johnnychen94 Apr 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mutable seems to be an overkill, and in julia we use Tuple for bound type, i.e., NTuple{4,Integer}.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be storing the entire image as a field in BoxROI. An abstract region of interest could just be a set of AbstractRange types, one for each dimensions. It would also not need to be mutable. You just instantiate a new when the ROI changes. I imagine something like this:

struct BoxROI{R₁ <: AbstractRange, R₂} <: AbstractROI
    span₁::R₁ 
    span₂::R₂
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is img used through the entire tracking life? If the answer is yes, we then have some reason to hold it in some struct( e.g., Tracker or BoxROI). If it's only used for initialization purpose, it's better not to do so.

img::T
bound::S
end

abstract type AbstractTrackerSampler end
mutable struct CurrentSample{F <: Float64, S <: Symbol} <: AbstractTrackerSampler
overlap::F
search_factor::F
mode::S
sampling_ROI::SVector{4, Integer}
valid_ROI::SVector{4, Integer}
tracked_patch::SVector{4, Integer}
CurrentSample{F, S}(overlap, search_factor, mode) where{F <: Float64, S <: Symbol} = new(overlap, search_factor, mode)
end

CurrentSample(overlap::F = 0.99, search_factor::F = 1.8, mode::S = :positive) where{F <: Float64, S <: Symbol} =
CurrentSample{F, S}(overlap, search_factor, mode)

function sample_tracker(sampler::CurrentSample, box::BoxROI)
sampler.tracked_patch = box.bound
sampler.valid_ROI = SVector{4, Integer}([1, 1, size(box.img, 1), size(box.img, 2)])

height = box.bound[3] - box.bound[1] + 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can write a custom size(b::BoxROI) which returns the width and height as a tuple. Since the BoxROI consists of AbstractRange objects, this will be trivial.

width = box.bound[4] - box.bound[2] + 1

ROI_min_y = max(0, floor(Integer, box.bound[1] - height*((sampler.search_factor - 1) / 2) + 1))
ROI_min_x = max(0, floor(Integer, box.bound[2] - width*((sampler.search_factor - 1) / 2) + 1))

ROI_height = min(floor(Integer, height*sampler.search_factor), size(box.img, 1) - ROI_min_y + 1)
ROI_width = min(floor(Integer, width*sampler.search_factor), size(box.img, 2) - ROI_min_x + 1)

ROI_max_y = ROI_min_y + ROI_height - 1
ROI_max_x = ROI_min_x + ROI_width - 1

ROI = SVector{4, Integer}([ROI_min_y, ROI_min_x, ROI_max_y, ROI_max_x])

sample_pos_neg_roi(sampler, box, ROI)
end

function sample_pos_neg_roi(sampler::CurrentSample, box::BoxROI, ROI::SVector{4, Integer})
println(ROI)
println(sampler.valid_ROI)
if sampler.valid_ROI == ROI
sampler.sampling_ROI = ROI
else
sampler.sampling_ROI = SVector{4, Integer}(max(sampler.valid_ROI[1], ROI[1]), max(sampler.valid_ROI[2], ROI[2]),
min(sampler.valid_ROI[3], ROI[3]), min(sampler.valid_ROI[4], ROI[4]))
end

if sampler.mode == :positive
positive_sample = box.img[sampler.tracked_patch[1]:sampler.tracked_patch[3],
sampler.tracked_patch[2]:sampler.tracked_patch[4]]
positive_sample_pos = SVector{2, Integer}(sampler.tracked_patch[1], sampler.tracked_patch[2])
sample = fill((positive_sample, positive_sample_pos), (1, 4))
return sample
end

height = sampler.tracked_patch[3] - sampler.tracked_patch[1] + 1
width = sampler.tracked_patch[4] - sampler.tracked_patch[2] + 1

tl_block = SVector{4, Integer}(sampler.valid_ROI[1], sampler.valid_ROI[2], sampler.valid_ROI[1] + height - 1,
sampler.valid_ROI[2] + width - 1)
tr_block = SVector{4, Integer}(sampler.valid_ROI[1], sampler.valid_ROI[4] - width + 1, sampler.valid_ROI[1] + height - 1,
sampler.valid_ROI[4])
bl_block = SVector{4, Integer}(sampler.valid_ROI[3] - height + 1, sampler.valid_ROI[2], sampler.valid_ROI[3],
sampler.valid_ROI[2] + width - 1)
br_block = SVector{4, Integer}(sampler.valid_ROI[3] - height + 1, sampler.valid_ROI[4] - width + 1, sampler.valid_ROI[3],
sampler.valid_ROI[4])

if sampler.mode == :negative
tl_sample = box.img[tl_block[1]:tl_block[3], tl_block[2]:tl_block[4]]
tr_sample = box.img[tr_block[1]:tr_block[3], tr_block[2]:tr_block[4]]
bl_sample = box.img[bl_block[1]:bl_block[3], bl_block[2]:bl_block[4]]
br_sample = box.img[br_block[1]:br_block[3], br_block[2]:br_block[4]]
sample = [(tl_sample, SVector{2, Integer}(tl_block[1:2])), (tr_sample, SVector{2, Integer}(tr_block[1:2])),
(bl_sample, SVector{2, Integer}(bl_block[1:2])), (br_sample, SVector{2, Integer}(br_block[1:2]))]

return sample
end

step_row = max(1, floor(Int, (1 - sampler.overlap)*height + 0.5))
step_col = max(1, floor(Int, (1 - sampler.overlap)*width + 0.5))
max_row = sampler.sampling_ROI[3] - sampler.sampling_ROI[1] - height
max_col = sampler.sampling_ROI[4] - sampler.sampling_ROI[2] - width

sample = Array{Tuple{AbstractArray, SVector{2, Integer}}}
for i = 1:step_col:max_col
for j = 1:step_row:max_row
push!(sample, (box.img[j + sampler.sampling_ROI[1] - 1:j + sampler.sampling_ROI[1] + height - 2,
i + sampler.sampling_ROI[2] - 1:i + sampler.sampling_ROI[2] + width - 2],
SVector{2, Integer}(j + sampler.sampling_ROI[1]-1, i + sampler.sampling_ROI[2]-1)))
end
end

return sample
end
5 changes: 0 additions & 5 deletions test/REQUIRE

This file was deleted.