Skip to content

Commit

Permalink
refactored testing with opencv; first complete julia draft of algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
jakewilliami committed Aug 10, 2020
1 parent 4d78fc5 commit fd5e645
Show file tree
Hide file tree
Showing 11 changed files with 442 additions and 109 deletions.
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
[deps]
ClosedIntervals = "059b0e18-018a-5deb-a5b2-c624ee85784b"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
ImageFeatures = "92ff4b2b-8094-53d3-b29d-97f740f06cef"
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
ObjectDetector = "3dfc1049-5314-49cf-8447-288dfd02f9fb"
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
QuartzImageIO = "dca85d43-d64c-5e67-8c65-017450d5d020"
TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990"
143 changes: 143 additions & 0 deletions src/Adaboost.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,146 @@
"${BASH_SOURCE[0]}" "$@"
=#

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

include("HaarFeatureSelection.jl")


function learn(positiveIIs, negativeIIs, numClassifiers=-1, minFeatureWidth=1, maxFeatureWidth=-1, minFeatureHeight=1, maxFeatureHeight=-1)
"""
Selects a set of classifiers. Iteratively takes the best classifiers based
on a weighted error.
:param positive_iis: List of positive integral image examples
:type positive_iis: list[numpy.ndarray]
:param negative_iis: List of negative integral image examples
:type negative_iis: list[numpy.ndarray]
:param num_classifiers: Number of classifiers to select, -1 will use all
classifiers
:type num_classifiers: int
:return: List of selected features
:rtype: list[violajones.HaarLikeFeature.HaarLikeFeature]
"""
numPos = length(positiveIIs)
numNeg = length(negativeIIs)
numImgs = numPos + numNeg
imgHeight, imgWidth = size(positiveIIs[1])

# Maximum feature width and height default to image width and height
if isequal(maxFeatureHeight, -1)
maxFeatureHeight = imgHeight
else
continue
end
if isequal(maxFeatureWidth, -1)
maxFeatureWidth = imgWidth
else
continue
end

# Create initial weights and labels
posWeights = ones(numPos) * 1. / (2 * numPos)
negWeights = ones(numNeg) * 1. / (2 * numNeg)
weights = hcat((posWeights, negWeights))
labels = hcat((ones(numPos), ones(numNeg) * -1))

images = positiveIIs + negativeIIs

# Create features for all sizes and locations
features = _create_features(imgHeight, imgWidth, minFeatureWidth, maxFeatureWidth, minFeatureHeight, maxFeatureHeight)
numFeatures = length(features)
# feature_indexes = list(range(num_features))
featureIndexes = Array(1:numFeatures)

if isequal(numClassifiers, -1)
numClassifiers = numFeatures
else
continue
end

votes = zeros((numImgs, numFeatures))
# bar = progressbar.ProgressBar()
@everywhere begin
n = numImgs
p = Progress(n, 1) # minimum update interval: 1 second
for i in processes
# votes[i, :] = np.array(pool.map(partial(_get_feature_vote, image=images[i]), features))
votes[i, :] = Array(_get_feature_vote, image=images[i]), features
next!(p)
end
end # end everywhere (end parallel processing)

# select classifiers
classifiers = Array()

println('Selecting classifiers...')

n = numClassifiers
p = Progress(n, 1) # minimum update interval: 1 second
for i in processes
classificationErrors = zeros(length(featureIndexes))

# normalize weights
weights *= 1. / sum(weights)

# select best classifier based on the weighted error
for f in 1:length(feature_indexes)
fIDX = featureIndexes[f]
# classifier error is the sum of image weights where the classifier
# is right
# error = sum(map(lambda img_idx: weights[img_idx] if labels[img_idx] != votes[img_idx, f_idx] else 0, range(num_imgs)))
error = sum(imgIDX -> (labels[imgIDX] votes[imgIDX, fIDX]) ? weights[imgIDX] : 0, 1:numImgs)
classificationErrors[f] = error

# get best feature, i.e. with smallest error
minErrorIDX = argmin(classificationErrors)
bestError = classificationErrors[minErrorIDX]
bestFeatureIDX = featureIndexes[minErrorIDX]

# set feature weight
bestFeature = features[bestFeatureIDX]
featureWeight = 0.5 * log((1 - bestError) / bestError)
bestFeature.weight = featureWeight

classifiers = vcat(classifiers, bestFeature)

# update image weights
# weights = list(map(lambda img_idx: weights[img_idx] * np.sqrt((1-best_error)/best_error) if labels[img_idx] != votes[img_idx, best_feature_idx] else weights[img_idx] * np.sqrt(best_error/(1-best_error)), range(num_imgs)))
weights = (imgIDX -> (labels[imgIDX] votes[imgIDX, bestFeatureIDX]) ? weights[imgIDX]*sqrt((1-bestError)/bestError) : weights[imgIDX]*sqrt(bestError/(1-bestError)), 1:numImgs)

# remove feature (a feature can't be selected twice)
# feature_indexes.remove(best_feature_idx)
featureIndexes = filter!(e -> e bestFeatureIDX, featureIndexes) # note: without unicode operators, `e ∉ [a, b]` is `!(e in [a, b])`

next!(p)
end

return classifiers
end


function _get_feature_vote(feature, image)
return vote(image)
end


function _create_features(imgHeight::Int64, imgWidth::Int64, minFeatureWidth::Int64, maxFeatureWidth::Int64, minFeatureHeight::Int64, maxFeatureHeight::Int64)
println("Creating Haar-like features...")
features = Array()

for feature in FeatureTypes
featureStartWidth = max(minFeatureWidth, feature[1])
for featureWidth in range(featureStartWidth, stop=maxFeatureWidth, step=feature[1])
for x in 1:(imgWidth - featureWidth)
for y in 1:(imgHeight - featureHeight)
"""features.append(HaarLikeFeature(feature, (x, y), feature_width, feature_height, 0, 1))
features.append(HaarLikeFeature(feature, (x, y), feature_width, feature_height, 0, -1))"""
end # end for y
end # end for x
end # end for feature width
end # end for feature in feature types

println('...finished processing; ' + str(len(features)) + ' features created.')

return features
end
181 changes: 106 additions & 75 deletions src/HaarFeatureSelection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,71 @@ include("IntegralImage.jl")
# FeatureType = enum(TWO_VERTICAL=(1, 2), TWO_HORIZONTAL=(2, 1), THREE_HORIZONTAL=(3, 1), THREE_VERTICAL=(1, 3), FOUR=(2, 2))
# FeatureTypes = Array(FeatureType.TWO_VERTICAL, FeatureType.TWO_HORIZONTAL, FeatureType.THREE_VERTICAL, FeatureType.THREE_HORIZONTAL, FeatureType.FOUR)

FeatureType = Dict{String, AbstractArray}("two_vertical" => (1, 2), "two_horizontal" => (2, 1), "three_horizontal" => (3,1), "three_vertical" => (1,3), "four" => (2, 2))
FeatureType = Dict{String, Tuple{Int64,Int64}}("two_vertical" => (1, 2), "two_horizontal" => (2, 1), "three_horizontal" => (3,1), "three_vertical" => (1,3), "four" => (2, 2))
FeatureTypes = [FeatureType["two_vertical"], FeatureType["two_horizontal"], FeatureType["three_horizontal"], FeatureType["three_vertical"], FeatureType["four"]]


abstract type HaarType end
abstract type HaarObject end
#
#
# struct HaarLikeFeature <: HaarType
# feature_type::Any
# position::Array
# topLeft::Array
# bottomRight::Array
# width::Int64
# height::Int64
# threshold::Float64
# polarity::Int64
# weight::Float64
#
# # constructor; equivalent of __init__ method within class
# function HaarLikeFeature(feature_type::Any, position::Array, width::Int64, height::Int64, threshold::Float64, polarity::Int64, weight::Float64)
# topLeft = position
# bottomRight = (position[1] + width, position[2] + height)
# weight = 1
# new(feature_type, topLeft, bottomRight, width, height, threshold, polarity)
# end # end constructor
# end # end structure
#
#
# #
# function getScore(self::HaarLikeFeature, intImg::AbstractArray)
# """
# Get score for given integral image array.
# :param int_img: Integral image array
# :type int_img: numpy.ndarray
# :return: Score for given feature
# :rtype: float
# """
# # set instance of score outside of ifs
# score = 0
#
# if self.feature_type == FeatureType["two_vertical"]
# first = sumRegion(intImg, self.top_left, (self.top_left[1] + self.width, Int(self.top_left[2] + self.height / 2)))
# second = sumRegion(int_img, (self.top_left[1], Int(self.top_left[2] + self.height / 2)), self.bottom_right)
# score = first - second
# end
# return score
#
# end # end getScore function


struct HaarLikeFeature <: HaarType
feature_type::Any
position::Array
topLeft::Array
bottomRight::Array
abstract type HaarFeatureType end

# make structure
struct HaarLikeFeature <: HaarFeatureType#struct HaarLikeFeature{T} <: HaarObject where {T <: HaarFeatureType}
position::Tuple{Int64, Int64}
topLeft::Tuple{Int64, Int64}
bottomRight::Tuple{Int64, Int64}
width::Int64
height::Int64
threshold::Float64
polarity::Int64
weight::Float64

# constructor; equivalent of __init__ method within class
function HaarLikeFeature(feature_type::Any, position::Array, width::Int64, height::Int64, threshold::Float64, polarity::Int64, weight::Float64)
function HaarLikeFeature(position::Tuple{Int64, Int64}, width::Int64, height::Int64, threshold::Float64, polarity::Int64, weight::Float64)
topLeft = position
bottomRight = (position[1] + width, position[2] + height)
weight = 1
Expand All @@ -39,85 +84,71 @@ struct HaarLikeFeature <: HaarType
end # end structure


#
function getScore(self::HaarLikeFeature, intImg::AbstractArray)
"""
Get score for given integral image array.
:param int_img: Integral image array
:type int_img: numpy.ndarray
:return: Score for given feature
:rtype: float
"""
# set instance of score outside of ifs
score = 0

if self.feature_type == FeatureType["two_vertical"]
first = sumRegion(intImg, self.top_left, (self.top_left[1] + self.width, Int(self.top_left[2] + self.height / 2)))
second = sumRegion(int_img, (self.top_left[1], Int(self.top_left[2] + self.height / 2)), self.bottom_right)
score = first - second
end
return score

end # end getScore function

# define the various Haar like feature types
struct HaarFeatureTwoVertical <: HaarFeatureType end
struct HaarFeatureTwoHorizontal <: HaarFeatureType end
struct HaarFeatureThreeHorizontal <: HaarFeatureType end
struct HaarFeatureThreeVertical <: HaarFeatureType end
struct HaarFeatureFour <: HaarFeatureType end


imgArr = getImageMatrix()
integralImageArr = toIntegralImage(imgArr)
# HaarFeatureTwoVertical = (1, 2)

# println(integralImageArr)

output = getScore(integralImageArr)
# construct integral image
intImg = toIntegralImage(getImageMatrix())

println(output)
function score(::HaarFeatureTwoVertical, self::HaarLikeFeature)
first = sumRegion(intImg, self.top_left, (self.top_left[1] + self.width, Int(self.top_left[2] + self.height / 2)))
second = sumRegion(intImg, (self.top_left[1], Int(self.top_left[2] + self.height / 2)), self.bottom_right)
score = first - second
return score
end

# x = new HaarLikeFeature()
function score(::HaarFeatureTwoHorizontal, self::HaarLikeFeature)
first = sumRegion(intImg, self.top_left, (int(self.top_left[1] + self.width / 3), self.top_left[2] + self.height))
second = sumRegion(intImg, (Int(self.top_left[1] + self.width / 3), self.top_left[1]), (Int(self.top_left[1] + 2 * self.width / 3), self.top_left[2] + self.height))
third = sumRegion(intImg, (Int(self.top_left[1] + 2 * self.width / 3), self.top_left[2]), self.bottom_right)
score = first - second + third
return score
end

# function Base.show(io::IO, gs::HaarLikeFeature)
# println(io, getScore(gs))
# end
#
# show(integralImageArr)
function score(::HaarFeatureThreeHorizontal, self::HaarLikeFeature)
first = sumRegion(intImg, self.top_left, (Int(self.top_left[1] + self.width / 3), self.top_left[2] + self.height))
second = sumRegion(intImg, (Int(self.top_left[1] + self.width / 3), self.top_left[2]), (Int(self.top_left[1] + 2 * self.width / 3), self.top_left[2] + self.height))
third = sumRegion(intImg, (Int(self.top_left[1] + 2 * self.width / 3), self.top_left[2]), self.bottom_right)
score = first - second + third
return score
end

# def _get_feature_vote(feature, image):
# return feature.get_vote(image)
function score(::HaarFeatureThreeVertical, self::HaarLikeFeature)
first = sumRegion(intImg, self.top_left, (self.bottom_right[1], Int(self.top_left[2] + self.height / 3)))
second = sumRegion(intImg, (self.top_left[1], Int(self.top_left[2] + self.height / 3)), (self.bottom_right[1], Int(self.top_left[2] + 2 * self.height / 3)))
third = sumRegion(intImg, (self.top_left[1], Int(self.top_left[2] + 2 * self.height / 3)), self.bottom_right)
score = first - second + third
return score
end

# x.getScore(integralImageArr)
function score(::HaarFeatureFour, self::HaarLikeFeature)
# top left area
first = sumRegion(intImg, self.top_left, (Int(self.top_left[1] + self.width / 2), Int(self.top_left[2] + self.height / 2)))
# top right area
second = sumRegion(intImg, (Int(self.top_left[1] + self.width / 2), self.top_left[2]), (self.bottom_right[1], Int(self.top_left[2] + self.height / 2)))
# bottom left area
third = sumRegion(intImg, (self.top_left[1], Int(self.top_left[2] + self.height / 2)), (Int(self.top_left[1] + self.width / 2), self.bottom_right[2]))
# bottom right area
fourth = sumRegion(intImg, (Int(self.top_left[1] + self.width / 2), int(self.top_left[2] + self.height / 2)), self.bottom_right)
score = first - second - third + fourth
return score
end

abstract type HaarFeatureType end

struct HaarLikeFeature <: HaarType
position::Array{Int64, 2}
topLeft::Array{Int64, 2}
bottomRight::Array{Int64, 2}
width::Int64
height::Int64
threshold::Float64
polarity::Int64
weight::Float64

# constructor; equivalent of __init__ method within class
function HaarLikeFeature(position::Array, width::Int64, height::Int64, threshold::Float64, polarity::Int64, weight::Float64)
topLeft = position
bottomRight = (position[1] + width, position[2] + height)
weight = 1
new(feature_type, topLeft, bottomRight, width, height, threshold, polarity)
end # end constructor
end # end structure

# imgArr = getImageMatrix()
# integralImageArr = toIntegralImage(imgArr)

struct HaarFeatureTwoVertical <: HaarFeatureType end
struct HaarFeatureTwoHorizontal <: HaarFeatureType end
# println(integralImageArr)

function score(::HaarFeatureTwoVertical, self::HaarLikeFeature)
first = ii.sum_region(int_img, self.top_left, (self.top_left[0] + self.width, int(self.top_left[1] + self.height / 2)))
second = ii.sum_region(int_img, (self.top_left[0], int(self.top_left[1] + self.height / 2)), self.bottom_right)
return first - second
end
output = score(intImg)

function score(::HaarFeatureTwoHoritontal, self::HaarLikeFeature)
first = ii.sum_region(int_img, self.top_left, (int(self.top_left[0] + self.width / 3), self.top_left[1] + self.height))
second = ii.sum_region(int_img, (int(self.top_left[0] + self.width / 3), self.top_left[1]), (int(self.top_left[0] + 2 * self.width / 3), self.top_left[1] + self.height))
third = ii.sum_region(int_img, (int(self.top_left[0] + 2 * self.width / 3), self.top_left[1]), self.bottom_right)
return first - second + third
end
println(output)
Loading

0 comments on commit fd5e645

Please sign in to comment.