Skip to content

Commit

Permalink
Constructed modules (including main FaceDetection module; closes #15)…
Browse files Browse the repository at this point in the history
… and removed @Everywhere (and accordingly removed Distributed import and global references) as it doesn't actually make the algorithm faster (slower, in fact---33% slower, tested with classifiers=100)
  • Loading branch information
jakewilliami committed Aug 28, 2020
1 parent b4682c4 commit fa4352c
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 60 deletions.
51 changes: 28 additions & 23 deletions src/Adaboost.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@
exec julia --project="~/FaceDetection.jl/" "${BASH_SOURCE[0]}" "$@" -e 'include(popfirst!(ARGS))' \
"${BASH_SOURCE[0]}" "$@"
=#

using ProgressMeter: @showprogress
using Distributed: @everywhere # for parallel processing (namely, @everywhere)

include("HaarLikeFeature.jl")

# TODO: select optimal threshold for each feature
# TODO: attentional cascading


module Adaboost

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

using ProgressMeter: @showprogress
using .HaarLikeFeature: FeatureTypes, HaarLikeObject, getScore, getVote
using .Utils: notifyUser

export learn, _get_feature_vote, _create_features


function learn(positiveIIs::AbstractArray, negativeIIs::AbstractArray, numClassifiers::Int64=-1, minFeatureWidth::Int64=1, maxFeatureWidth::Int64=-1, minFeatureHeight::Int64=1, maxFeatureHeight::Int64=-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 inverselt proportional to the training errors.
Expand All @@ -23,13 +31,14 @@ function learn(positiveIIs::AbstractArray, negativeIIs::AbstractArray, numClassi
parameter `numClassifiers`: Number of classifiers to select. -1 will use all
classifiers [type: Integer]
return `classifiers`: List of selected features [type: HaarLikeFeature]
return `classifiers`: List of selected features [type: HaarLikeObject]
=#


# get number of positive and negative images (and create a global variable of the total number of images——global for the @everywhere scope)
numPos = length(positiveIIs)
numNeg = length(negativeIIs)
global numImgs = numPos + numNeg
numImgs = numPos + numNeg

# get image height and width
imgHeight, imgWidth = size(positiveIIs[1])
Expand Down Expand Up @@ -62,10 +71,10 @@ function learn(positiveIIs::AbstractArray, negativeIIs::AbstractArray, numClassi
labels = vcat(ones(numPos), ones(numNeg) * -1)

# get list of images (global because of @everywhere scope)
global images = vcat(positiveIIs, negativeIIs)
images = vcat(positiveIIs, negativeIIs)

# Create features for all sizes and locations
global features = _create_features(imgHeight, imgWidth, minFeatureWidth, maxFeatureWidth, minFeatureHeight, maxFeatureHeight)
features = _create_features(imgHeight, imgWidth, minFeatureWidth, maxFeatureWidth, minFeatureHeight, maxFeatureHeight)
numFeatures = length(features)
featureIndices = Array(1:numFeatures)

Expand All @@ -76,15 +85,13 @@ function learn(positiveIIs::AbstractArray, negativeIIs::AbstractArray, numClassi
notifyUser("Calculating scores for images...")

# create an empty array (of zeroes) with dimensions (numImgs, numFeautures)
global votes = zeros((numImgs, numFeatures)) # necessarily different from `zero.((numImgs, numFeatures))`; previously zerosarray
votes = zeros((numImgs, numFeatures)) # necessarily different from `zero.((numImgs, numFeatures))`; previously zerosarray

@everywhere begin
n = numImgs
processes = numImgs # i.e., hypotheses
@showprogress for t in 1:processes # bar(range(num_imgs)):
votes[t, :] = Array(map(f -> _get_feature_vote(f, images[t]), features))
end # end show progress in for loop
end # end everywhere (end parallel processing)
n = numImgs
processes = numImgs # i.e., hypotheses
@showprogress for t in 1:processes # bar(range(num_imgs)):
votes[t, :] = Array(map(f -> _get_feature_vote(f, images[t]), features))
end # end show progress in for loop

print("\n") # for a new line after the progress bar

Expand Down Expand Up @@ -135,7 +142,7 @@ function learn(positiveIIs::AbstractArray, negativeIIs::AbstractArray, numClassi
end


function _get_feature_vote(feature::HaarLikeFeature, image::AbstractArray)
function _get_feature_vote(feature::HaarLikeObject, image::AbstractArray)
return getVote(feature, image)
end

Expand Down Expand Up @@ -163,15 +170,15 @@ function _create_features(imgHeight::Int64, imgWidth::Int64, minFeatureWidth::In
""")
end

for feature in FeatureTypes # from HaarLikeFeature.jl (FeatureTypes are just tuples)
for feature in FeatureTypes # from HaarLikeObject.jl (FeatureTypes are just tuples)
featureStartWidth = max(minFeatureWidth, feature[1])
for featureWidth in range(featureStartWidth, stop=maxFeatureWidth, step=feature[1])
featureStartHeight = max(minFeatureHeight, feature[2])
for featureHeight in range(featureStartHeight, stop=maxFeatureHeight, step=feature[2])
for x in 1:(imgWidth - featureWidth)
for y in 1:(imgHeight - featureHeight)
features = push!(features, HaarLikeFeature(feature, (x, y), featureWidth, featureHeight, 0, 1))
features = push!(features, HaarLikeFeature(feature, (x, y), featureWidth, featureHeight, 0, -1))
features = push!(features, HaarLikeObject(feature, (x, y), featureWidth, featureHeight, 0, 1))
features = push!(features, HaarLikeObject(feature, (x, y), featureWidth, featureHeight, 0, -1))
end # end for y
end # end for x
end # end for feature height
Expand All @@ -184,6 +191,4 @@ function _create_features(imgHeight::Int64, imgWidth::Int64, minFeatureWidth::In
end


export learn
export _get_feature_vote
export _create_features
end # end module
12 changes: 6 additions & 6 deletions src/FDA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ Adapted from https://github.com/Simon-Hohberg/Viola-Jones/

println("\033[1;34m===>\033[0;38m\033[1;38m\tLoading required libraries (it will take a moment to precompile if it is your first time doing this)...\033[0;38m")

using Printf: @printf
include("FaceDetection.jl")

include("IntegralImage.jl")
include("AdaBoost.jl") # imports HaarLikeFeature.jl implicitly
include("Utils.jl")
using .FaceDetection
using Printf: @printf
using Images: Gray, clamp01nan, save


function main(alt::Bool=false, imageReconstruction::Bool=false)
Expand All @@ -35,7 +35,7 @@ function main(alt::Bool=false, imageReconstruction::Bool=false)
negTestingPath = "$mainImagePath/testset/non-faces/"
end

numClassifiers = 5
numClassifiers = 10
# For performance reasons restricting feature size
minFeatureHeight = 8
maxFeatureHeight = 10
Expand Down Expand Up @@ -81,7 +81,7 @@ function main(alt::Bool=false, imageReconstruction::Bool=false)
facesFrac = string(correctFaces, "/", length(facesTesting))
facesPercent = string("(", correctFacesPercent, "% of faces were recognised as faces)")
nonFacesFrac = string(correctNonFaces, "/", length(nonFacesTesting))
nonFacesPercent = string("(", correctNonFacesPercent, "% of non-faces were considered to be faces)")
nonFacesPercent = string("(", correctNonFacesPercent, "% of non-faces were identified as non-faces)")

println("...done.\n")
notifyUser("Result:\n")
Expand Down
21 changes: 21 additions & 0 deletions src/FaceDetection.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
#=
exec julia --project="~/FaceDetection.jl/" "${BASH_SOURCE[0]}" "$@" -e 'include(popfirst!(ARGS))' \
"${BASH_SOURCE[0]}" "$@"
=#

module FaceDetection

export toIntegralImage, sumRegion, FeatureTypes, HaarLikeObject, getScore, getVote, learn, _get_feature_vote, _create_features, displaymatrix, notifyUser, loadImages, getImageMatrix, ensembleVote, ensembleVoteAll, reconstruct, getRandomImage, generateValidationImage

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

using .IntegralImage: toIntegralImage, sumRegion
using .HaarLikeFeature: FeatureTypes, HaarLikeObject, getScore, getVote
using .Adaboost: learn, _get_feature_vote, _create_features
using .Utils: displaymatrix, notifyUser, loadImages, getImageMatrix, ensembleVote, ensembleVoteAll, reconstruct, getRandomImage, generateValidationImage

end # end module
30 changes: 19 additions & 11 deletions src/HaarLikeFeature.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@
=#


module HaarLikeFeature

include("IntegralImage.jl")

using .IntegralImage: toIntegralImage, sumRegion

export FeatureTypes, HaarLikeObject, getScore, getVote


FeatureTypes = [(1, 2), (2, 1), (3, 1), (1, 3), (2, 2)]


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

# make structure
mutable struct HaarLikeFeature <: HaarFeatureType
mutable struct HaarLikeObject <: HaarFeatureAbstractType
#=
Struct representing a Haar-like feature.
=#
Expand All @@ -26,8 +36,8 @@ mutable struct HaarLikeFeature <: HaarFeatureType
polarity::Int64
weight::Float64

# constructor; equivalent of __init__ method within class # ::CartesianIndex
function HaarLikeFeature(featureType::Tuple{Int64,Int64}, position::Tuple{Int64, Int64}, width::Int64, height::Int64, threshold::Int64, polarity::Int64)
# constructor; equivalent of __init__ method within class
function HaarLikeObject(featureType::Tuple{Int64,Int64}, position::Tuple{Int64, Int64}, width::Int64, height::Int64, threshold::Int64, polarity::Int64)
topLeft = position
bottomRight = (position[1] + width, position[2] + height)
weight = 1
Expand All @@ -37,11 +47,11 @@ mutable struct HaarLikeFeature <: HaarFeatureType
end # end structure


function getScore(feature::HaarLikeFeature, intImg::Array)
function getScore(feature, intImg::Array)#function getScore(feature::HaarLikeObject, intImg::Array)
#=
Get score for given integral image array.
parameter `feature`: given Haar-like feature (parameterised replacement of Python's `self`) [type: HaarLikeFeature]
parameter `feature`: given Haar-like feature (parameterised replacement of Python's `self`) [type: HaarLikeObject]
parameter `intImg`: Integral image array [type: Abstract Array]
return `score`: Score for given feature [type: Float]
Expand Down Expand Up @@ -84,11 +94,11 @@ end



function getVote(feature::HaarLikeFeature, intImg::AbstractArray)
function getVote(feature, intImg::AbstractArray)#function getVote(feature::HaarLikeObject, intImg::AbstractArray)
#=
Get vote of this feature for given integral image.
parameter `feature`: given Haar-like feature (parameterised replacement of Python's `self`) [type: HaarLikeFeature]
parameter `feature`: given Haar-like feature (parameterised replacement of Python's `self`) [type: HaarLikeObject]
parameter `intImg`: Integral image array [type: Abstract Array]
return:
Expand All @@ -104,6 +114,4 @@ function getVote(feature::HaarLikeFeature, intImg::AbstractArray)
end


export HaarLikeFeature
export getScore
export getVote
end # end module
8 changes: 5 additions & 3 deletions src/IntegralImage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Original Integral
| . . . . | . . . . .
=#

module IntegralImage

export toIntegralImage, sumRegion


function toIntegralImage(imgArr::AbstractArray)
#=
Expand Down Expand Up @@ -96,6 +100,4 @@ function sumRegion(integralImageArr::AbstractArray, topLeft::Tuple{Int64,Int64},
return bottomRightVal - topRightVal - bottomLeftVal + topLeftVal
end


export toIntegralImage
export sumRegion
end # end module
25 changes: 11 additions & 14 deletions src/Utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
=#


using Images: save, load, Colors, clamp01nan, Gray
module Utils

include("HaarLikeFeature.jl")

using Images: save, load, Colors, clamp01nan, Gray
using .HaarLikeFeature: FeatureTypes, HaarLikeObject, getScore, getVote

export displaymatrix, notifyUser, loadImages, getImageMatrix, ensembleVote, ensembleVoteAll, reconstruct, getRandomImage, generateValidationImage


function displaymatrix(M::AbstractArray)
#=
Expand Down Expand Up @@ -72,7 +77,7 @@ function ensembleVote(intImg::AbstractArray, classifiers::AbstractArray)
That is, the final strong classifier is $h(x)=\begin{cases}1&\text{if }\sum_{t=1}^{T}\alpha_th_t(x)\geq \frac{1}{2}\sum_{t=1}^{T}\alpha_t\\0&\text{otherwise}\end{cases}$, where $\alpha_t=\log{\left(\frac{1}{\beta_t}\right)}$
parameter `intImg`: Integral image to be classified [type: AbstractArray]
parameter `classifiers`: List of classifiers [type: AbstractArray (array of HaarLikeFeatures)]
parameter `classifiers`: List of classifiers [type: AbstractArray (array of HaarLikeObjects)]
return:
1 ⟺ sum of classifier votes > 0
Expand All @@ -88,7 +93,7 @@ function ensembleVoteAll(intImgs::AbstractArray, classifiers::AbstractArray)
Classifies given integral image (Abstract Array) using given classifiers. I.e., if the sum of all classifier votes is greater 0, the image is classified positively (1); else it is classified negatively (0). The threshold is 0, because votes can be +1 or -1.
parameter `intImg`: Integral image to be classified [type: AbstractArray]
parameter `classifiers`: List of classifiers [type: AbstractArray (array of HaarLikeFeatures)]
parameter `classifiers`: List of classifiers [type: AbstractArray (array of HaarLikeObjects)]
return list of assigned labels:
1 if image was classified positively
Expand All @@ -115,7 +120,7 @@ function reconstruct(classifiers::AbstractArray, imgSize::Tuple)
#=
Creates an image by putting all given classifiers on top of each other producing an archetype of the learned class of object.
parameter `classifiers`: List of classifiers [type: Abstract Array (array of HaarLikeFeatures)]
parameter `classifiers`: List of classifiers [type: Abstract Array (array of HaarLikeObjects)]
parameter `imgSize`: Tuple of width and height [Tuple]
return `result`: Reconstructed image [type: PIL.Image??]
Expand Down Expand Up @@ -186,7 +191,7 @@ function reconstruct(classifiers::AbstractArray, imgSize::Tuple)
# image .*= 255
#
# image = replace!(image, NaN=>0.0) # change NaN to white (not that there should be any NaN values)
#
#
return image
end

Expand Down Expand Up @@ -231,12 +236,4 @@ function generateValidationImage()
end


export displaymatrix
export notifyUser
export loadImages
export getImageMatrix
export ensembleVote
export ensembleVoteAll
export reconstruct
export getRandomImage
export generateValidationImage
end # end module
11 changes: 8 additions & 3 deletions test/runtests.jl
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
"${BASH_SOURCE[0]}" "$@"
=#

using Test
using FaceDetection
include(joinpath(homedir(), "FaceDetection.jl", "src", "FaceDetection.jl"))

using .FaceDetection
using Test: @test

# write your own tests here
@test 1 == 1
# @test

# @test ... == ...

0 comments on commit fa4352c

Please sign in to comment.