Skip to content

Commit

Permalink
rectangle grouping stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
cheeyos committed Jan 1, 2015
1 parent f4a09dd commit 7ca8a29
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 0 deletions.
38 changes: 38 additions & 0 deletions python/_group_rectangles_with_aux.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
""" Example of wrapping a C function that takes C float arrays as input using
the Numpy declarations from Cython """

# import both numpy and the Cython declarations for numpy
import numpy as np
cimport numpy as np
cimport cython

np.import_array()

# cdefine the signature of our c function
cdef extern from "group_rectangles_with_aux.h":
int groupRectanglesWithAux(float* input, int group_threshold, float eps, \
float* output, int* scores, int nrows, int ncols)

def execute(list linput, int group_threshold, float eps = 0.2):
cdef int nrows = len(linput)
cdef int ncols = np.PyArray_DIM(linput[0], 0)
cdef np.ndarray[float, ndim=2, mode="c"] input = np.empty((nrows, ncols), dtype=np.float32)
for i in range(nrows):
input[i, :] = linput[i]
cdef np.ndarray[float, ndim=2, mode="c"] output = np.empty((nrows, ncols), dtype=np.float32)
cdef np.ndarray[np.int32_t, ndim=2, mode="c"] scores = np.empty((nrows, 1), dtype=np.int32)

cdef int num_output = groupRectanglesWithAux(
<float*> np.PyArray_DATA(input),
group_threshold,
eps,
<float*> np.PyArray_DATA(output),
<int*> np.PyArray_DATA(scores),
nrows,
ncols)

if ncols > 4:
aux = output[:num_output, 4:]
else:
aux = None
return (np.rint(output[:num_output, :4]).astype(np.int32), aux, scores[:num_output])
7 changes: 7 additions & 0 deletions python/group_rectangles_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from distutils.core import setup, Extension

# define the extension module
gr_module = Extension('group_rect_module', sources=['groupRectangles.cpp'])

# run the setup
setup(ext_modules=[gr_module])
102 changes: 102 additions & 0 deletions python/group_rectangles_with_aux.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include <vector>
#include <iostream>

#include <opencv2/objdetect/objdetect.hpp>

using namespace std;

using cv::Rect;

class SimilarRects {
public:
SimilarRects(double _eps) : eps(_eps) {}
inline bool operator() (const vector<float>& r1, const vector<float>& r2) const {
double delta = eps * (std::min(r1[2], r2[2]) + std::min(r1[3], r2[3])) * 0.5;
return std::abs(r1[0] - r2[0]) <= delta &&
std::abs(r1[1] - r2[1]) <= delta &&
std::abs(r1[0] + r1[2] - r2[0] - r2[2]) <= delta &&
std::abs(r1[1] + r1[3] - r2[1] - r2[3]) <= delta;
}
double eps;
};

int groupRectanglesWithAux(float* input, int groupThreshold, float eps,
float* output, int* scores, int nrows, int ncols) {
if (groupThreshold <= 0 || nrows == 0) {
return 0;
}

vector<vector<float> > rectList(nrows, vector<float>(ncols));
for (int i = 0; i < nrows; ++i) {
int base_index = i * ncols;
for (int j = 0; j < ncols; ++j) {
if (j < 4) {
rectList[i][j] = cv::saturate_cast<int>(input[base_index + j]);
} else {
rectList[i][j] = input[base_index + j];
}
}
}

vector<int> labels;
int nclasses = cv::partition(rectList, labels, SimilarRects(eps));
vector<vector<float> > frects(nclasses, vector<float>(ncols));
vector<int> rweights(nclasses, 0);
int nlabels = (int) labels.size();
for (int i = 0; i < nlabels; i++) {
int cls = labels[i];
for (int j = 0; j < ncols; ++j) {
frects[cls][j] += rectList[i][j];
}
rweights[cls]++;
}

for (int i = 0; i < nclasses; i++) {
float s = 1.f / rweights[i];
for (int j = 0; j < ncols; ++j) {
float scaled = frects[i][j] * s;
if (j < 4) {
frects[i][j] = cv::saturate_cast<int>(scaled);
} else {
frects[i][j] = scaled;
}
}
}

int j;
int num_output = 0;
for (int i = 0; i < nclasses; i++) {
int r1 = i;
int n1 = rweights[i];
if (n1 <= groupThreshold)
continue;
// filter out small face rectangles inside large rectangles
for (j = 0; j < nclasses; j++) {
int n2 = rweights[j];

if (j == i || n2 <= groupThreshold)
continue;
int r2 = j;
int dx = cv::saturate_cast<int>(frects[r2][2] * eps);
int dy = cv::saturate_cast<int>(frects[r2][3] * eps);

if (i != j &&
frects[r1][0] >= frects[r2][0] - dx &&
frects[r1][1] >= frects[r2][1] - dy &&
frects[r1][0] + frects[r1][2] <= frects[r2][0] + frects[r2][2] + dx &&
frects[r1][1] + frects[r1][3] <= frects[r2][1] + frects[r2][3] + dy &&
(n2 > std::max(3, n1) || n1 < 3))
break;
}

if (j == nclasses) {
for (int k = 0; k < ncols; ++k) {
output[num_output * ncols + k] = frects[r1][k];
}
scores[num_output] = n1;
num_output++;
}
}

return num_output;
}
3 changes: 3 additions & 0 deletions python/group_rectangles_with_aux.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int groupRectanglesWithAux(float* input, int group_threshold, float eps,
float* output, int* scores, int nrows, int ncols);

24 changes: 24 additions & 0 deletions python/setup_group_rectangles_with_aux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize
import numpy
import subprocess

process = subprocess.Popen(
['pkg-config', '--libs', 'opencv'],
stdout=subprocess.PIPE)
out, err = process.communicate()
libs = [lib for lib in str(out).split() if '.' in lib]
opencv_path = set(['/'.join(lib.split('/')[:-2]) for lib in libs]).pop()
print 'Your opencv path:', opencv_path

setup(
cmdclass = {'build_ext': build_ext},
ext_modules = [Extension(
"group_rectangles_with_aux",
language = "c++",
sources = ["_group_rectangles_with_aux.pyx", "group_rectangles_with_aux.cpp"],
include_dirs=[numpy.get_include(), opencv_path + '/include'],
extra_link_args=libs)]
)
56 changes: 56 additions & 0 deletions python/test_group_rect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import numpy as np
import cv2
import group_rectangles_with_aux
import random

num_trials = 10000
num_rectangles = 500
arena_size = 100.0
square_size = 10.0
gt = 1
eps = 0.8
use_aux = True

for t in range(num_trials):
opencv_gr = []
my_gr = []
for r in range(num_rectangles):
e = np.array([
random.random() * arena_size, random.random() * arena_size,
random.random() * square_size, random.random() * square_size])
ec = np.concatenate([e, np.array([random.random(), random.random()])])
opencv_gr.append(e)
my_gr.append(ec if use_aux else e)

output1, scores1 = cv2.groupRectangles(opencv_gr, gt, eps)
output2, aux2, scores2 = group_rectangles_with_aux.execute(my_gr, gt, eps)
if len(output1) == 0:
assert len(output2) == 0
else:
try:
assert (output1 == output2).all()
except:
print 'opencv'
print output1
print 'mine'
print output2
print 'diff'
print output1.shape, output2.shape
print output1 - output2
raise
if len(scores1) == 0:
assert len(scores2) == 0
else:
try:
assert (scores1 == scores2).all()
except:
print 'opencv'
print scores1
print 'mine'
print scores2
print 'diff'
print scores1.shape, scores2.shape
print scores1 - scores2
raise
if t % (num_trials / 20) == 0:
print 'passed', t

0 comments on commit 7ca8a29

Please sign in to comment.