Skip to content

Commit

Permalink
Merge pull request #44 from longemen3000/master
Browse files Browse the repository at this point in the history
some changes
  • Loading branch information
jakewilliami authored Oct 29, 2020
2 parents 6effe72 + e71b22d commit 40e64f3
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 101 deletions.
1 change: 1 addition & 0 deletions _git2_a31664
49 changes: 31 additions & 18 deletions src/AdaBoost.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
# TODO: attentional cascading

# include("HaarLikeFeature.jl")
include("Utils.jl")
include("IntegralImage.jl")


using Base.Threads: @threads
using Base.Iterators: partition
Expand All @@ -19,15 +18,27 @@ using ProgressMeter: @showprogress, Progress, next!
function get_feature_votes(
positive_path::AbstractString,
negative_path::AbstractString,
num_classifiers::Integer=-1,
min_feature_width::Integer=1,
max_feature_width::Integer=-1,
min_feature_height::Integer=1,
max_feature_height::Integer=-1;
num_classifiers::Integer=Int32(-1),
min_feature_width::Integer=Int32(1),
max_feature_width::Integer=Int32(-1),
min_feature_height::Integer=Int32(1),
max_feature_height::Integer=Int32(-1);
scale::Bool = false,
scale_to::Tuple = (200, 200)
scale_to::Tuple = (Int32(200), Int32(200))
)

#this transforms everything to maintain type stability
s1 ,s2 = scale_to
min_feature_width,
max_feature_width,
min_feature_height,
max_feature_height,s1,s2 = promote(min_feature_width,
max_feature_width,
min_feature_height,
max_feature_height,s1,s2)
scale_to = (s1,s2)

_Int = typeof(max_feature_width)
# get number of positive and negative images (and create a global variable of the total number of images——global for the @everywhere scope)
positive_files = filtered_ls(positive_path)
negative_files = filtered_ls(negative_path)
Expand All @@ -43,13 +54,13 @@ function get_feature_votes(
temp_image = nothing # unload temporary image

# Maximum feature width and height default to image width and height
max_feature_height = isequal(max_feature_height, -1) ? img_height : max_feature_height
max_feature_width = isequal(max_feature_width, -1) ? img_height : max_feature_width
max_feature_height = isequal(max_feature_height, _Int(-1)) ? img_height : max_feature_height
max_feature_width = isequal(max_feature_width, _Int(-1)) ? img_height : max_feature_width

# Create features for all sizes and locations
features = create_features(img_height, img_width, min_feature_width, max_feature_width, min_feature_height, max_feature_height)
num_features = length(features)
num_classifiers = isequal(num_classifiers, -1) ? num_features : num_classifiers
num_classifiers = isequal(num_classifiers, _Int(-1)) ? num_features : num_classifiers

# create an empty array with dimensions (num_imgs, numFeautures)
votes = Matrix{Int8}(undef, num_features, num_imgs)
Expand All @@ -73,7 +84,7 @@ function get_feature_votes(
return votes, features
end

#=
"""
learn(
positive_iis::AbstractArray,
negative_iis::AbstractArray,
Expand All @@ -84,8 +95,8 @@ end
max_feature_height::Int64=-1
) ->::Array{HaarLikeObject,1}
The boosting algorithm for learning a query online. $T$ hypotheses are constructed, each using a single feature.
The final hypothesis is a weighted linear combination of the $T$ hypotheses, where the weights are inversely proportional to the training errors.
The boosting algorithm for learning a query online. T hypotheses are constructed, each using a single feature.
The final hypothesis is a weighted linear combination of the T hypotheses, where the weights are inversely proportional to the training errors.
This function selects a set of classifiers. Iteratively takes the best classifiers based on a weighted error.
# Arguments
Expand All @@ -99,7 +110,7 @@ This function selects a set of classifiers. Iteratively takes the best classifie
- `max_feature_width::Integer`: the maximum height of the feature
# Returns `classifiers::Array{HaarLikeObject, 1}`: List of selected features
=#
"""
function learn(
positive_path::AbstractString,
negative_path::AbstractString,
Expand All @@ -122,14 +133,15 @@ function learn(
labels = vcat(ones(Int8, num_pos), ones(Int8, num_neg) * -one(Int8))

num_features = length(features)

feature_indices = Array(1:num_features)
num_classifiers = isequal(num_classifiers, -1) ? num_features : num_classifiers

notify_user("Selecting classifiers...")
# select classifiers
classifiers = []
p = Progress(num_classifiers, 1) # minimum update interval: 1 second
@threads for t in 1:num_classifiers
for t in 1:num_classifiers
# classification_errors = zeros(length(feature_indices))
classification_errors = Matrix{Float64}(undef, length(feature_indices), 1)
# normalize the weights $w_{t,i}\gets \frac{w_{t,i}}{\sum_{j=1}^n w_{t,j}}$
Expand Down Expand Up @@ -166,6 +178,7 @@ function learn(
return classifiers

end

function learn(
positive_path::AbstractString,
negative_path::AbstractString,
Expand Down Expand Up @@ -193,7 +206,7 @@ function learn(
return learn(positive_path, negative_path, features, votes, num_classifiers)
end

#=
"""
create_features(
img_height::Integer,
img_width::Integer,
Expand All @@ -217,7 +230,7 @@ Iteratively creates the Haar-like feautures
# Returns
- `features::AbstractArray`: an array of Haar-like features found for an image
=#
"""
function create_features(
img_height::Integer,
img_width::Integer,
Expand Down
7 changes: 6 additions & 1 deletion src/FaceDetection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@

module FaceDetection

import Base: size, getindex, LinearIndices
using Images: Images, coords_spatial

export to_integral_image, sum_region
export learn
export feature_types, HaarLikeObject, get_score, get_vote
export displaymatrix, notify_user, filtered_ls, load_image,
ensemble_vote_all, reconstruct, get_random_image,
generate_validation_image, get_faceness, determine_feature_size


include("HaarLikeFeature.jl")
include("Utils.jl") # Utils.jl exports HaarLikeFeature.jl functions
include("IntegralImage.jl")
include("AdaBoost.jl")
include("Utils.jl") # Utils.jl exports HaarLikeFeature.jl functions

end # end module
120 changes: 68 additions & 52 deletions src/HaarLikeFeature.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,38 @@
=#


include("IntegralImage.jl")

feature_types = Dict{String, Tuple{Integer, Integer}}("two_vertical" => (1, 2), "two_horizontal" => (2, 1), "three_horizontal" => (3, 1), "three_vertical" => (1, 3), "four" => (2, 2))
const feature_types = (
two_vertical = (1, 2),
two_horizontal = (2, 1),
three_horizontal = (3, 1),
three_vertical = (1, 3),
four = (2, 2)
)

abstract type HaarFeatureAbstractType end
# abstract type AbstractHaarLikeObject <: HaarFeatureAbstractType end

#=
Struct representing a Haar-like feature.
=#
mutable struct HaarLikeObject <: HaarFeatureAbstractType
feature_type::Tuple{Integer, Integer}
position::Tuple{Integer, Integer}
top_left::Tuple{Integer, Integer}
bottom_right::Tuple{Integer, Integer}
width::Integer
height::Integer
threshold::Integer
polarity::Integer
weight::AbstractFloat

"""
mutable struct HaarLikeObject{I<:Integer,F<:AbstractFloat}
Struct representing a Haar-like feature.
"""
mutable struct HaarLikeObject{I<:Integer,F<:AbstractFloat} <: HaarFeatureAbstractType
feature_type::Tuple{I, I}
position::Tuple{I, I}
top_left::Tuple{I, I}
bottom_right::Tuple{I, I}
width::I
height::I
threshold::I
polarity::I
weight::F
#parametric struct to store the ints and floats efficiently


end # end structure

# constructor; equivalent of __init__ method within class
function HaarLikeObject(
feature_type::Tuple{Integer, Integer},
Expand All @@ -35,15 +46,21 @@ mutable struct HaarLikeObject <: HaarFeatureAbstractType
threshold::Integer,
polarity::Integer
)

#all this to make sure that everything is of se same size
p1,p2 = position
f1,f2 = feature_type
p1,p2,f1,f2,width,height,threshold,polarity = promote(p1,p2,f1,f2,width,height,threshold,polarity)
position = (p1,p2)
feature_type = (f1,f2)
top_left = position
bottom_right = (position[1] + width, position[2] + height)
weight = 1
weight = float(one(p1)) #to make a float of the same size

new(feature_type, position, top_left, bottom_right, width, height, threshold, polarity, weight)
end # end constructor
end # end structure

#=
HaarLikeObject(feature_type, position, top_left, bottom_right, width, height, threshold, polarity, weight)
end # end constructor
"""
get_score(feature::HaarLikeObject, int_img::Array) -> Tuple{Number, Number}
Get score for given integral image array.
Expand All @@ -56,44 +73,44 @@ Get score for given integral image array.
# Returns
- `score::Number`: Score for given feature
=#
function get_score(feature::HaarLikeObject, int_img::Array)
score = 0
faceness = 0

if feature.feature_type == feature_types["two_vertical"]
first = sum_region(int_img, feature.top_left, (feature.top_left[1] + feature.width, Int(round(feature.top_left[2] + feature.height / 2))))
second = sum_region(int_img, (feature.top_left[1], Int(round(feature.top_left[2] + feature.height / 2))), feature.bottom_right)
"""
function get_score(feature::HaarLikeObject{I,F}, int_img::Array) where {I,F}
score = zero(I)
faceness = zero(I)

if feature.feature_type == feature_types.two_vertical
first = sum_region(int_img, feature.top_left, (feature.top_left[1] + feature.width, I(round(feature.top_left[2] + feature.height / 2))))
second = sum_region(int_img, (feature.top_left[1], I(round(feature.top_left[2] + feature.height / 2))), feature.bottom_right)
score = first - second
faceness = 1
elseif feature.feature_type == feature_types["two_horizontal"]
first = sum_region(int_img, feature.top_left, (Int(round(feature.top_left[1] + feature.width / 2)), feature.top_left[2] + feature.height))
second = sum_region(int_img, (Int(round(feature.top_left[1] + feature.width / 2)), feature.top_left[2]), feature.bottom_right)
faceness = I(1)
elseif feature.feature_type == feature_types.two_horizontal
first = sum_region(int_img, feature.top_left, (I(round(feature.top_left[1] + feature.width / 2)), feature.top_left[2] + feature.height))
second = sum_region(int_img, (I(round(feature.top_left[1] + feature.width / 2)), feature.top_left[2]), feature.bottom_right)
score = first - second
faceness = 2
elseif feature.feature_type == feature_types["three_horizontal"]
first = sum_region(int_img, feature.top_left, (Int(round(feature.top_left[1] + feature.width / 3)), feature.top_left[2] + feature.height))
second = sum_region(int_img, (Int(round(feature.top_left[1] + feature.width / 3)), feature.top_left[2]), (Int(round(feature.top_left[1] + 2 * feature.width / 3)), feature.top_left[2] + feature.height))
third = sum_region(int_img, (Int(round(feature.top_left[1] + 2 * feature.width / 3)), feature.top_left[2]), feature.bottom_right)
faceness = I(2)
elseif feature.feature_type == feature_types.three_horizontal
first = sum_region(int_img, feature.top_left, (I(round(feature.top_left[1] + feature.width / 3)), feature.top_left[2] + feature.height))
second = sum_region(int_img, (I(round(feature.top_left[1] + feature.width / 3)), feature.top_left[2]), (I(round(feature.top_left[1] + 2 * feature.width / 3)), feature.top_left[2] + feature.height))
third = sum_region(int_img, (I(round(feature.top_left[1] + 2 * feature.width / 3)), feature.top_left[2]), feature.bottom_right)
score = first - second + third
faceness = 3
elseif feature.feature_type == feature_types["three_vertical"]
first = sum_region(int_img, feature.top_left, (feature.bottom_right[1], Int(round(feature.top_left[2] + feature.height / 3))))
second = sum_region(int_img, (feature.top_left[1], Int(round(feature.top_left[2] + feature.height / 3))), (feature.bottom_right[1], Int(round(feature.top_left[2] + 2 * feature.height / 3))))
third = sum_region(int_img, (feature.top_left[1], Int(round(feature.top_left[2] + 2 * feature.height / 3))), feature.bottom_right)
faceness = I(3)
elseif feature.feature_type == feature_types.three_vertical
first = sum_region(int_img, feature.top_left, (feature.bottom_right[1], I(round(feature.top_left[2] + feature.height / 3))))
second = sum_region(int_img, (feature.top_left[1], I(round(feature.top_left[2] + feature.height / 3))), (feature.bottom_right[1], I(round(feature.top_left[2] + 2 * feature.height / 3))))
third = sum_region(int_img, (feature.top_left[1], I(round(feature.top_left[2] + 2 * feature.height / 3))), feature.bottom_right)
score = first - second + third
faceness = 4
elseif feature.feature_type == feature_types["four"]
faceness = I(4)
elseif feature.feature_type == feature_types.four
# top left area
first = sum_region(int_img, feature.top_left, (Int(round(feature.top_left[1] + feature.width / 2)), Int(round(feature.top_left[2] + feature.height / 2))))
first = sum_region(int_img, feature.top_left, (I(round(feature.top_left[1] + feature.width / 2)), I(round(feature.top_left[2] + feature.height / 2))))
# top right area
second = sum_region(int_img, (Int(round(feature.top_left[1] + feature.width / 2)), feature.top_left[2]), (feature.bottom_right[1], Int(round(feature.top_left[2] + feature.height / 2))))
second = sum_region(int_img, (I(round(feature.top_left[1] + feature.width / 2)), feature.top_left[2]), (feature.bottom_right[1], I(round(feature.top_left[2] + feature.height / 2))))
# bottom left area
third = sum_region(int_img, (feature.top_left[1], Int(round(feature.top_left[2] + feature.height / 2))), (Int(round(feature.top_left[1] + feature.width / 2)), feature.bottom_right[2]))
third = sum_region(int_img, (feature.top_left[1], I(round(feature.top_left[2] + feature.height / 2))), (I(round(feature.top_left[1] + feature.width / 2)), feature.bottom_right[2]))
# bottom right area
fourth = sum_region(int_img, (Int(round(feature.top_left[1] + feature.width / 2)), Int(round(feature.top_left[2] + feature.height / 2))), feature.bottom_right)
fourth = sum_region(int_img, (I(round(feature.top_left[1] + feature.width / 2)), I(round(feature.top_left[2] + feature.height / 2))), feature.bottom_right)
score = first - second - third + fourth
faceness = 5
faceness = I(5)
end

return score, faceness
Expand All @@ -116,7 +133,6 @@ Get vote of this feature for given integral image.
-1 otherwise
=#
function get_vote(feature::HaarLikeObject, int_img::AbstractArray)
score = get_score(feature, int_img)[1] # we only care about score here

score = first(get_score(feature, int_img)) # we only care about score here
return (feature.weight * score) < (feature.polarity * feature.threshold) ? one(Int8) : -one(Int8)
end
36 changes: 18 additions & 18 deletions src/IntegralImage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ Original Integral
| . . . . | . . . . .
=#

import Base: size, getindex, LinearIndices
using Images: Images, coords_spatial


struct IntegralArray{T, N, A} <: AbstractArray{T, N}
data::A
end

#=
"""
to_integral_image(img_arr::AbstractArray) -> AbstractArray
Calculates the integral image based on this instance's original image data.
Expand All @@ -33,8 +32,8 @@ Calculates the integral image based on this instance's original image data.
# Returns
- `integral_image_arr::AbstractArray`: Integral image for given image
=#
function to_integral_image(img_arr::AbstractArray)
"""
function to_integral_image(img_arr::AbstractArray{T,N}) where {T,N}
array_size = size(img_arr)
integral_image_arr = Array{Images.accum(eltype(img_arr))}(undef, array_size)
sd = coords_spatial(img_arr)
Expand All @@ -43,15 +42,15 @@ function to_integral_image(img_arr::AbstractArray)
cumsum!(integral_image_arr, integral_image_arr; dims=sd[i])
end

return Array{eltype(img_arr), ndims(img_arr)}(integral_image_arr)
return Array{T, N}(integral_image_arr)
end

LinearIndices(A::IntegralArray) = Base.LinearFast()
size(A::IntegralArray) = size(A.data)
getindex(A::IntegralArray, i::Int...) = A.data[i...]
getindex(A::IntegralArray, ids::Tuple...) = getindex(A, ids[1]...)
@inline size(A::IntegralArray) = size(A.data)
@inline getindex(A::IntegralArray, i::Int...) = A.data[i...]
@inline getindex(A::IntegralArray, ids::Tuple...) = getindex(A, ids[1]...)

#=
"""
sum_region(
integral_image_arr::AbstractArray,
top_left::Tuple{Int64,Int64},
Expand All @@ -67,16 +66,17 @@ getindex(A::IntegralArray, ids::Tuple...) = getindex(A, ids[1]...)
# Returns
- `sum::Number` The sum of all pixels in the given rectangle defined by the parameters top_left and bottom_right
=#
"""
function sum_region(
integral_image_arr::AbstractArray,
top_left::Tuple{Integer,Integer},
bottom_right::Tuple{Integer,Integer}
)
top_left::Tuple{T,T},
bottom_right::Tuple{T,T}
) where T <: Integer
_1 = one(T)
sum = integral_image_arr[bottom_right[2], bottom_right[1]]
sum -= top_left[1] > 1 ? integral_image_arr[bottom_right[2], top_left[1] - 1] : zero(Int64)
sum -= top_left[2] > 1 ? integral_image_arr[top_left[2] - 1, bottom_right[1]] : zero(Int64)
sum += top_left[2] > 1 && top_left[1] > 1 ? integral_image_arr[top_left[2] - 1, top_left[1] - 1] : zero(Int64)

sum -= top_left[1] > _1 ? integral_image_arr[bottom_right[2], top_left[1] - 1] : zero(T)
sum -= top_left[2] > _1 ? integral_image_arr[top_left[2] - _1, bottom_right[1]] : zero(T)
sum += top_left[2] > _1 && top_left[1] > _1 ? integral_image_arr[top_left[2] - 1, top_left[1] - 1] : zero(T)
return sum
end

Loading

0 comments on commit 40e64f3

Please sign in to comment.