Skip to content

Commit

Permalink
Implementing classification algorithm for rotated images
Browse files Browse the repository at this point in the history
  • Loading branch information
esimov committed Jan 7, 2019
1 parent f94ce2f commit b8c07b8
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 24 deletions.
16 changes: 8 additions & 8 deletions cmd/pigo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var (
maxSize = flag.Int("max", 1000, "Maximum size of face")
shiftFactor = flag.Float64("shift", 0.1, "Shift detection window by percentage")
scaleFactor = flag.Float64("scale", 1.1, "Scale detection window by percentage")
angle = flag.Float64("angle", 0.0, "0.0 is 0 radians and 1.0 is 2*pi radians")
iouThreshold = flag.Float64("iou", 0.2, "Intersection over union (IoU) threshold")
circleMarker = flag.Bool("circle", false, "Use circle as detection marker")
outputAsJSON = flag.Bool("json", false, "Output face box coordinates into a json file")
Expand Down Expand Up @@ -144,13 +145,12 @@ func (fd *faceDetector) detectFaces(source string) ([]pigo.Detection, error) {
MaxSize: fd.maxSize,
ShiftFactor: fd.shiftFactor,
ScaleFactor: fd.scaleFactor,
}

imgParams := pigo.ImageParams{
Pixels: pixels,
Rows: rows,
Cols: cols,
Dim: cols,
ImageParams: pigo.ImageParams{
Pixels: pixels,
Rows: rows,
Cols: cols,
Dim: cols,
},
}

cascadeFile, err := ioutil.ReadFile(fd.cascadeFile)
Expand All @@ -168,7 +168,7 @@ func (fd *faceDetector) detectFaces(source string) ([]pigo.Detection, error) {

// Run the classifier over the obtained leaf nodes and return the detection results.
// The result contains quadruplets representing the row, column, scale and detection score.
faces := classifier.RunCascade(imgParams, cParams)
faces := classifier.RunCascade(cParams, *angle)

// Calculate the intersection over union (IoU) of two clusters.
faces = classifier.ClusterDetections(faces, fd.iouThreshold)
Expand Down
96 changes: 83 additions & 13 deletions core/pigo.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type CascadeParams struct {
MaxSize int
ShiftFactor float64
ScaleFactor float64
ImageParams
}

// ImageParams is a struct for image related settings.
Expand Down Expand Up @@ -140,11 +141,64 @@ func (pg *Pigo) classifyRegion(r, c, s int, pixels []uint8, dim int) float32 {

for j := 0; j < int(pg.treeDepth); j++ {
var pix = 0
var x1 = ((r+int(pg.treeCodes[root+4*idx+0])*s)>>8)*dim + ((c + int(pg.treeCodes[root+4*idx+1])*s) >> 8)
var x2 = ((r+int(pg.treeCodes[root+4*idx+2])*s)>>8)*dim + ((c + int(pg.treeCodes[root+4*idx+3])*s) >> 8)
x1 := ((r+int(pg.treeCodes[root+4*idx+0])*s)>>8)*dim + ((c + int(pg.treeCodes[root+4*idx+1])*s) >> 8)
x2 := ((r+int(pg.treeCodes[root+4*idx+2])*s)>>8)*dim + ((c + int(pg.treeCodes[root+4*idx+3])*s) >> 8)

var px1 = pixels[x1]
var px2 = pixels[x2]
px1 := pixels[x1]
px2 := pixels[x2]

if px1 <= px2 {
pix = 1
} else {
pix = 0
}
idx = 2*idx + pix
}
out += pg.treePred[pTree*i+idx-pTree]

if out <= pg.treeThreshold[i] {
return -1.0
} else {
root += 4 * pTree
}
}
return out - pg.treeThreshold[pg.treeNum-1]
}

// classifyRotatedRegion applies the face classification function over a rotated image based on the parsed binary data.
func (pg *Pigo) classifyRotatedRegion(r, c, s int, a float64, nrows, ncols int, pixels []uint8, dim int) float32 {
var (
root int = 0
out float32
pTree = int(math.Pow(2, float64(pg.treeDepth)))
)

r = r * 65536
c = c * 65536

qCosTable := []int{256, 251, 236, 212, 181, 142, 97, 49, 0, -49, -97, -142, -181, -212, -236, -251, -256, -251, -236, -212, -181, -142, -97, -49, 0, 49, 97, 142, 181, 212, 236, 251, 256}
qSinTable := []int{0, 49, 97, 142, 181, 212, 236, 251, 256, 251, 236, 212, 181, 142, 97, 49, 0, -49, -97, -142, -181, -212, -236, -251, -256, -251, -236, -212, -181, -142, -97, -49, 0}

qsin := s * qSinTable[int(32.0*a)] //s*(256.0*math.Sin(2*math.Pi*a))
qcos := s * qCosTable[int(32.0*a)] //s*(256.0*math.Cos(2*math.Pi*a))

if (r+46341*s)/65536 >= nrows || (r-46341*s)/65536 < 0 || (c+46341*s)/65536 >= ncols || (c-46341*s)/65536 < 0 {
return -1
}

for i := 0; i < int(pg.treeNum); i++ {
var idx = 1

for j := 0; j < int(pg.treeDepth); j++ {
var pix = 0
r1 := abs(r+qcos*int(pg.treeCodes[root+4*idx+0])-qsin*int(pg.treeCodes[root+4*idx+1])) >> 16
c1 := abs(c+qsin*int(pg.treeCodes[root+4*idx+0])+qcos*int(pg.treeCodes[root+4*idx+1])) >> 16

r2 := abs(r+qcos*int(pg.treeCodes[root+4*idx+2])-qsin*int(pg.treeCodes[root+4*idx+3])) >> 16
c2 := abs(c+qsin*int(pg.treeCodes[root+4*idx+2])+qcos*int(pg.treeCodes[root+4*idx+3])) >> 16

px1 := pixels[r1*dim+c1]
px2 := pixels[r2*dim+c2]

if px1 <= px2 {
pix = 1
Expand Down Expand Up @@ -175,27 +229,36 @@ type Detection struct {

// RunCascade analyze the grayscale converted image pixel data and run the classification function over the detection window.
// It will return a slice containing the detection row, column, it's center and the detection score (in case this is greater than 0.0).
func (pg *Pigo) RunCascade(img ImageParams, opts CascadeParams) []Detection {
func (pg *Pigo) RunCascade(cp CascadeParams, angle float64) []Detection {
var detections []Detection
var pixels = img.Pixels
var pixels = cp.Pixels
var q float32

scale := opts.MinSize
scale := cp.MinSize

// Run the classification function over the detection window
// and check if the false positive rate is above a certain value.
for scale <= opts.MaxSize {
step := int(math.Max(opts.ShiftFactor*float64(scale), 1))
for scale <= cp.MaxSize {
step := int(math.Max(cp.ShiftFactor*float64(scale), 1))
offset := (scale/2 + 1)

for row := offset; row <= img.Rows-offset; row += step {
for col := offset; col <= img.Cols-offset; col += step {
q := pg.classifyRegion(row, col, scale, pixels, img.Dim)
for row := offset; row <= cp.Rows-offset; row += step {
for col := offset; col <= cp.Cols-offset; col += step {
if angle > 0.0 {
if angle > 1.0 {
angle = 1.0
}
q = pg.classifyRotatedRegion(row, col, scale, angle, cp.Rows, cp.Cols, pixels, cp.Dim)
} else {
q = pg.classifyRegion(row, col, scale, pixels, cp.Dim)
}

if q > 0.0 {
detections = append(detections, Detection{row, col, scale, q})
}
}
}
scale = int(float64(scale) * opts.ScaleFactor)
scale = int(float64(scale) * cp.ScaleFactor)
}
return detections
}
Expand Down Expand Up @@ -261,3 +324,10 @@ func (q det) Less(i, j int) bool {
}
return q[i].Q < q[j].Q
}

func abs(x int) int {
if x < 0 {
return -x
}
return x
}
13 changes: 10 additions & 3 deletions webcam/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var (
maxSize = flag.Int("max", 1000, "Maximum size of face")
shiftFactor = flag.Float64("shift", 0.1, "Shift detection window by percentage")
scaleFactor = flag.Float64("scale", 1.1, "Scale detection window by percentage")
angle = flag.Float64("angle", 0.0, "0.0 is 0 radians and 1.0 is 2*pi radians")
iouThreshold = flag.Float64("iou", 0.2, "Intersection over union (IoU) threshold")
circleMarker = flag.Bool("circle", false, "Use circle as detection marker")
)
Expand Down Expand Up @@ -112,14 +113,20 @@ func webcam(w http.ResponseWriter, r *http.Request) {
src := pigo.ImgToNRGBA(img)
frame := pigo.RgbToGrayscale(src)

cols, rows := src.Bounds().Max.X, src.Bounds().Max.Y

cParams := pigo.CascadeParams{
MinSize: *minSize,
MaxSize: *maxSize,
ShiftFactor: *shiftFactor,
ScaleFactor: *scaleFactor,
ImageParams: pigo.ImageParams{
Pixels: frame,
Rows: rows,
Cols: cols,
Dim: cols,
},
}
cols, rows := src.Bounds().Max.X, src.Bounds().Max.Y
imgParams := pigo.ImageParams{frame, rows, cols, cols}

pigo := pigo.NewPigo()
// Unpack the binary file. This will return the number of cascade trees,
Expand All @@ -131,7 +138,7 @@ func webcam(w http.ResponseWriter, r *http.Request) {

// Run the classifier over the obtained leaf nodes and return the detection results.
// The result contains quadruplets representing the row, column, scale and detection score.
dets := classifier.RunCascade(imgParams, cParams)
dets := classifier.RunCascade(cParams, *angle)

// Calculate the intersection over union (IoU) of two clusters.
dets = classifier.ClusterDetections(dets, 0)
Expand Down

0 comments on commit b8c07b8

Please sign in to comment.