Skip to content

Commit 6dc1f41

Browse files
committed
Merge pull request #638 from mshabunin:fix-face-python-predict
2 parents 22a136f + 48903ef commit 6dc1f41

13 files changed

+117
-834
lines changed

modules/face/include/opencv2/face.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ class CV_EXPORTS_W FaceRecognizer : public Algorithm
256256
CV_WRAP virtual void update(InputArrayOfArrays src, InputArray labels);
257257

258258
/** @overload */
259-
CV_WRAP int predict(InputArray src) const;
259+
CV_WRAP_AS(predict_label) int predict(InputArray src) const;
260260

261261

262262
/** @brief Predicts a label and associated confidence (e.g. distance) for a given input image.
@@ -300,12 +300,11 @@ class CV_EXPORTS_W FaceRecognizer : public Algorithm
300300
/** @brief - if implemented - send all result of prediction to collector that can be used for somehow custom result handling
301301
@param src Sample image to get a prediction from.
302302
@param collector User-defined collector object that accepts all results
303-
@param state - optional user-defined state token that should be passed back from FaceRecognizer implementation
304303
305304
To implement this method u just have to do same internal cycle as in predict(InputArray src, CV_OUT int &label, CV_OUT double &confidence) but
306305
not try to get "best@ result, just resend it to caller side with given collector
307306
*/
308-
CV_WRAP virtual void predict(InputArray src, Ptr<PredictCollector> collector, const int state = 0) const = 0;
307+
CV_WRAP_AS(predict_collect) virtual void predict(InputArray src, Ptr<PredictCollector> collector) const = 0;
309308

310309
/** @brief Saves a FaceRecognizer and its model state.
311310

modules/face/include/opencv2/face/predict_collector.hpp

Lines changed: 51 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -44,262 +44,84 @@ the use of this software, even if advised of the possibility of such damage.
4444

4545
#ifndef __OPENCV_PREDICT_COLLECTOR_HPP__
4646
#define __OPENCV_PREDICT_COLLECTOR_HPP__
47-
#include <cfloat>
48-
#include <list>
47+
4948
#include <vector>
5049
#include <map>
51-
#include "opencv2/core/cvdef.h"
50+
#include <utility>
51+
#include <cfloat>
52+
5253
#include "opencv2/core/cvstd.hpp"
53-
#undef emit //fix for qt
54+
5455
namespace cv {
5556
namespace face {
5657
//! @addtogroup face
5758
//! @{
5859
/** @brief Abstract base class for all strategies of prediction result handling
5960
*/
60-
class CV_EXPORTS_W PredictCollector {
61-
protected:
62-
double _threshold;
63-
int _size;
64-
int _state;
65-
int _excludeLabel;
66-
double _distanceKoef;
67-
double _minthreshold;
61+
class CV_EXPORTS_W PredictCollector
62+
{
6863
public:
69-
/** @brief creates new predict collector with given threshold */
70-
PredictCollector(double threshold = DBL_MAX) {
71-
_threshold = threshold;
72-
_excludeLabel = 0;
73-
_distanceKoef = 1;
74-
_minthreshold = -1;
75-
}
76-
CV_WRAP virtual ~PredictCollector() {}
64+
virtual ~PredictCollector() {}
7765

78-
/** @brief called once at start of recognition
66+
/** @brief Interface method called by face recognizer before results processing
7967
@param size total size of prediction evaluation that recognizer could perform
80-
@param state user defined send-to-back optional value to allow multi-thread, multi-session or aggregation scenarios
81-
*/
82-
CV_WRAP virtual void init(const int size, const int state = 0);
83-
84-
/** @brief called by recognizer prior to emit to decide if prediction require emiting
85-
@param label current predicted label
86-
@param dist current predicted distance
87-
@param state back send state parameter of prediction session
88-
@return true if prediction is valid and required for emiting
89-
@note can override given label and distance to another values
9068
*/
91-
CV_WRAP virtual bool defaultFilter(int* label, double* dist, const int state);
92-
93-
/** @brief extension point for filter - called if base filter executed */
94-
CV_WRAP virtual bool filter(int* label, double* dist, const int state);
69+
virtual void init(size_t size) { (void)size; }
9570

96-
/** @brief called with every recognition result
71+
/** @brief Interface method called by face recognizer for each result
9772
@param label current prediction label
9873
@param dist current prediction distance (confidence)
99-
@param state user defined send-to-back optional value to allow multi-thread, multi-session or aggregation scenarios
100-
@return true if recognizer should proceed prediction , false - if recognizer should terminate prediction
101-
*/
102-
CV_WRAP virtual bool emit(const int label, const double dist, const int state = 0); //not abstract while Python generation require non-abstract class
103-
104-
/** @brief outer interface method to be called from recognizer
105-
@param label current prediction label
106-
@param dist current prediction distance (confidence)
107-
@param state user defined send-to-back optional value to allow multi-thread, multi-session or aggregation scenarios
108-
@note wraps filter and emit calls, not tended to be overriden
109-
*/
110-
CV_WRAP virtual bool collect(int label, double dist, const int state = 0);
111-
112-
/**
113-
@brief get size of prediction
114-
### Description
115-
Is set by recognizer and is amount of all available predicts
116-
So we can use it to perform statistic collectors before prediction of whole set
117-
*/
118-
CV_WRAP virtual int getSize();
119-
120-
/** @brief set size of prediction */
121-
CV_WRAP virtual void setSize(int size);
122-
123-
/**
124-
@brief get state of prediction
125-
### Description
126-
State is a custom value assigned for prediction session, 0 if it's no-state session
127-
*/
128-
CV_WRAP virtual int getState();
129-
130-
/** @brief set state of prediction */
131-
CV_WRAP virtual void setState(int state);
132-
133-
/**
134-
@brief returns currently excluded label, 0 if no set
135-
### Description
136-
We require to exclude label if we want to test card in train set against others
13774
*/
138-
CV_WRAP virtual int getExcludeLabel();
139-
140-
/** @brief set exclude label of prediction */
141-
CV_WRAP virtual void setExcludeLabel(int excludeLabel);
142-
143-
/**
144-
@brief returns current distance koeficient (applyed to distance in filter stage)
145-
### Description
146-
It's required if we want to predict with distinct algorithms in one session
147-
so LBPH, Eigen and Fisher distance are different, but we can provide koef for them to translate to
148-
each other (while their distribuition for same train set is close and started from 0)
149-
Default 1 koef means that distance is not corrected
150-
*/
151-
CV_WRAP virtual double getDistanceKoef();
152-
153-
/** @brief set exclude label of prediction */
154-
CV_WRAP virtual void setDistanceKoef(double distanceKoef);
155-
/**
156-
@brief returns current minimal threshold
157-
### Description
158-
It's required when we must exclude most closed predictions (for example we
159-
search for close but not same faces - usable for mixed set where doubles exists
160-
in train collection)
161-
*/
162-
CV_WRAP virtual double getMinThreshold();
163-
164-
/** @brief set minimal threshold for prediction */
165-
CV_WRAP virtual void setMinThreshold(double minthreshold);
166-
75+
virtual bool collect(int label, double dist) = 0;
16776
};
16877

169-
/** @brief default predict collector that trace minimal distance with treshhold checking (that is default behavior for most predict logic)
170-
*/
171-
class CV_EXPORTS_W MinDistancePredictCollector : public PredictCollector {
172-
private:
173-
int _label;
174-
double _dist;
175-
public:
176-
/** @brief creates new MinDistancePredictCollector with given threshold */
177-
CV_WRAP MinDistancePredictCollector(double threshold = DBL_MAX) : PredictCollector(threshold) {
178-
_label = -1;
179-
_dist = DBL_MAX;
180-
};
181-
CV_WRAP bool emit(const int label, const double dist, const int state = 0);
182-
CV_WRAP bool filter(int* label, double* dist, const int state);
183-
/** @brief result label, -1 if not found */
184-
CV_WRAP int getLabel() const;
185-
/** @brief result distance (confidence) DBL_MAX if not found */
186-
CV_WRAP double getDist() const;
187-
/** @brief factory method to create cv-pointers to MinDistancePredictCollector */
188-
CV_WRAP static Ptr<MinDistancePredictCollector> create(double threshold = DBL_MAX);
189-
};
78+
/** @brief Default predict collector
19079
191-
/**
192-
@brief Collects top N most close predictions
193-
@note Prevent doubling of same label - if one label is occured twice - most closed distance value will be set
80+
Trace minimal distance with treshhold checking (that is default behavior for most predict logic)
19481
*/
195-
class CV_EXPORTS_W TopNPredictCollector : public PredictCollector {
196-
private:
197-
size_t _size;
198-
Ptr<std::list<std::pair<int, double> > > _idx;
82+
class CV_EXPORTS_W StandardCollector : public PredictCollector
83+
{
19984
public:
200-
CV_WRAP TopNPredictCollector(size_t size = 5, double threshold = DBL_MAX) : PredictCollector(threshold) {
201-
_size = size;
202-
_idx = Ptr<std::list<std::pair<int, double> > >(new std::list<std::pair<int, double> >);
85+
struct PredictResult
86+
{
87+
int label;
88+
double distance;
89+
PredictResult(int label_ = -1, double distance_ = DBL_MAX) : label(label_), distance(distance_) {}
20390
};
204-
CV_WRAP bool emit(const int label, const double dist, const int state = 0);
205-
CV_WRAP bool filter(int* label, double* dist, const int state);
206-
Ptr<std::list<std::pair<int, double> > > getResult();
207-
CV_WRAP std::vector<std::pair<int, double> > getResultVector(); // pythonable version
208-
CV_WRAP static Ptr<TopNPredictCollector> create(size_t size = 5, double threshold = DBL_MAX);
209-
};
210-
211-
212-
/**
213-
@brief Collects all predict results to single vector
214-
@note this collector not analyze double labels in emit, it's raw copy of source prediction result,
215-
remember that filter is still applyed so you can use min/max threshold , distanceKoef and excludeLabel
216-
*/
217-
class CV_EXPORTS_W VectorPredictCollector : public PredictCollector {
218-
private:
219-
Ptr<std::vector<std::pair<int, double> > > _idx;
220-
public:
221-
CV_WRAP static const int DEFAULT_SIZE = 5; // top 5 by default
222-
CV_WRAP VectorPredictCollector(double threshold = DBL_MAX) : PredictCollector(threshold) {
223-
_idx = Ptr<std::vector<std::pair<int, double> > >(new std::vector<std::pair<int, double> >);
224-
}
225-
CV_WRAP bool emit(const int label, const double dist, const int state = 0);
226-
Ptr<std::vector<std::pair<int, double> > > getResult();
227-
CV_WRAP std::vector<std::pair<int, double> > getResultVector(); // pythonable version
228-
CV_WRAP static Ptr<VectorPredictCollector> create(double threshold = DBL_MAX);
229-
};
230-
231-
232-
/**
233-
@brief Collects all predict results to single vector
234-
@note this collector not analyze double labels in emit, it's raw copy of source prediction result,
235-
remember that filter is still applyed so you can use min/max threshold , distanceKoef and excludeLabel
236-
*/
237-
class CV_EXPORTS_W MapPredictCollector : public PredictCollector {
238-
private:
239-
Ptr<std::map<int, double> > _idx;
240-
public:
241-
CV_WRAP static const int DEFAULT_SIZE = 5; // top 5 by default
242-
CV_WRAP MapPredictCollector(double threshold = DBL_MAX) : PredictCollector(threshold) {
243-
_idx = Ptr<std::map<int, double> >(new std::map<int, double>);
244-
}
245-
CV_WRAP bool emit(const int label, const double dist, const int state = 0);
246-
Ptr<std::map<int, double> > getResult();
247-
CV_WRAP std::vector<std::pair<int, double> > getResultVector(); // pythonable version
248-
CV_WRAP static Ptr<MapPredictCollector> create(double threshold = DBL_MAX);
249-
};
250-
251-
/**
252-
@brief Collects basic statistic information about prediction
253-
@note stat predict collector is usefull for determining valid thresholds
254-
on given trained set, additionally it's required to
255-
evaluate unified koefs between algorithms
256-
*/
257-
class CV_EXPORTS_W StatPredictCollector : public PredictCollector {
258-
private:
259-
double _min;
260-
double _max;
261-
int _count;
262-
double _sum;
263-
public:
264-
CV_WRAP StatPredictCollector(double threshold = DBL_MAX) : PredictCollector(threshold) {
265-
_min = DBL_MAX;
266-
_max = DBL_MIN;
267-
_count = 0;
268-
_sum = 0;
269-
}
270-
CV_WRAP bool emit(const int label, const double dist, const int state = 0);
271-
CV_WRAP double getMin();
272-
CV_WRAP double getMax();
273-
CV_WRAP double getSum();
274-
CV_WRAP int getCount();
275-
CV_WRAP static Ptr<StatPredictCollector> create(double threshold = DBL_MAX);
276-
};
277-
278-
/**
279-
@brief evaluates standard deviation of given prediction session over trained set
280-
@note in combine with StatPredictCollector can provide statistically based metrices
281-
for thresholds
282-
*/
283-
class CV_EXPORTS_W StdPredictCollector : public PredictCollector {
284-
private:
285-
double _avg;
286-
double _n;
287-
double _s;
91+
protected:
92+
double threshold;
93+
PredictResult minRes;
94+
std::vector<PredictResult> data;
28895
public:
289-
CV_WRAP StdPredictCollector(double threshold = DBL_MAX, double avg = 0) : PredictCollector(threshold) {
290-
_avg = avg;
291-
_n = 0;
292-
_s = 0;
293-
}
294-
CV_WRAP bool emit(const int label, const double dist, const int state = 0);
295-
CV_WRAP double getResult();
296-
CV_WRAP static Ptr<StdPredictCollector> create(double threshold = DBL_MAX, double avg = 0);
96+
/** @brief Constructor
97+
@param threshold_ set threshold
98+
*/
99+
StandardCollector(double threshold_ = DBL_MAX);
100+
/** @brief overloaded interface method */
101+
void init(size_t size);
102+
/** @brief overloaded interface method */
103+
bool collect(int label, double dist);
104+
/** @brief Returns label with minimal distance */
105+
CV_WRAP int getMinLabel() const;
106+
/** @brief Returns minimal distance value */
107+
CV_WRAP double getMinDist() const;
108+
/** @brief Return results as vector
109+
@param sorted If set, results will be sorted by distance
110+
Each values is a pair of label and distance.
111+
*/
112+
CV_WRAP std::vector< std::pair<int, double> > getResults(bool sorted = false) const;
113+
/** @brief Return results as map
114+
Labels are keys, values are minimal distances
115+
*/
116+
std::map<int, double> getResultsMap() const;
117+
/** @brief Static constructor
118+
@param threshold set threshold
119+
*/
120+
CV_WRAP static Ptr<StandardCollector> create(double threshold = DBL_MAX);
297121
};
298122

299-
300-
301123
//! @}
302124
}
303125
}
304126

305-
#endif
127+
#endif

modules/face/src/eigen_faces.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Eigenfaces : public BasicFaceRecognizerImpl
4242
void train(InputArrayOfArrays src, InputArray labels);
4343

4444
// Send all predict results to caller side for custom result handling
45-
void predict(InputArray src, Ptr<PredictCollector> collector, const int state) const;
45+
void predict(InputArray src, Ptr<PredictCollector> collector) const;
4646
};
4747

4848
//------------------------------------------------------------------------------
@@ -99,7 +99,7 @@ void Eigenfaces::train(InputArrayOfArrays _src, InputArray _local_labels) {
9999
}
100100
}
101101

102-
void Eigenfaces::predict(InputArray _src, Ptr<PredictCollector> collector, const int state) const {
102+
void Eigenfaces::predict(InputArray _src, Ptr<PredictCollector> collector) const {
103103
// get data
104104
Mat src = _src.getMat();
105105
// make sure the user is passing correct data
@@ -114,11 +114,11 @@ void Eigenfaces::predict(InputArray _src, Ptr<PredictCollector> collector, const
114114
}
115115
// project into PCA subspace
116116
Mat q = LDA::subspaceProject(_eigenvectors, _mean, src.reshape(1, 1));
117-
collector->init((int)_projections.size(), state);
117+
collector->init(_projections.size());
118118
for (size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
119119
double dist = norm(_projections[sampleIdx], q, NORM_L2);
120120
int label = _labels.at<int>((int)sampleIdx);
121-
if (!collector->collect(label, dist, state))return;
121+
if (!collector->collect(label, dist))return;
122122
}
123123
}
124124

modules/face/src/facerec.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ int FaceRecognizer::predict(InputArray src) const {
8080
}
8181

8282
void FaceRecognizer::predict(InputArray src, CV_OUT int &label, CV_OUT double &confidence) const {
83-
Ptr<MinDistancePredictCollector> collector = MinDistancePredictCollector::create(getThreshold());
84-
predict(src, collector, 0);
85-
label = collector->getLabel();
86-
confidence = collector->getDist();
83+
Ptr<StandardCollector> collector = StandardCollector::create(getThreshold());
84+
predict(src, collector);
85+
label = collector->getMinLabel();
86+
confidence = collector->getMinDist();
8787
}
8888

8989
}

0 commit comments

Comments
 (0)