Commit d1a5ec8cabab179886118835c6daa39261990142

Authored by Scott Klum
2 parents 0c56c80e 5d075f94

Merge branch 'master' of https://github.com/biometrics/openbr

.gitmodules
1 [submodule "share/openbr/models"] 1 [submodule "share/openbr/models"]
2 path = share/openbr/models 2 path = share/openbr/models
3 url = https://github.com/biometrics/openbr-models.git 3 url = https://github.com/biometrics/openbr-models.git
4 -[submodule "share/openbr/doc"]  
5 - path = share/openbr/doc  
6 - url = https://github.com/biometrics/openbr-doc.git  
7 [submodule "openbr/janus"] 4 [submodule "openbr/janus"]
8 path = openbr/janus 5 path = openbr/janus
9 url = https://github.com/biometrics/janus.git 6 url = https://github.com/biometrics/janus.git
CHANGELOG.md deleted
1 -0.5.0 - ??/??/??  
2 -================  
3 -  
4 -0.4.0 - 9/17/13  
5 -===============  
6 -* Added simple GUI frontend  
7 -* Added -evalLandmarking and -plotLandmarking for evaluating and plotting landmarking accuracy (#9)  
8 -* Added -evalDetection and -plotDetection for evaluating and plotting object detection accuracy (#9)  
9 -* Deprecated Transform::backProject  
10 -  
11 -0.3.0 - 5/22/13  
12 -===============  
13 -* Added wrapper to NEC Latent SDK  
14 -* Enrolling files/folders are now sorted naturally instead of alpha numerically  
15 -* YouTubeFacesDBTransform implements Dr. Wolf's experimental protocol  
16 -* NEC3 refactored  
17 -* Updated transform API to add support for time-varying transforms per issue (#23)  
18 -* Refactored File class to improve point and rect storage (#22)  
19 -* Added algorithm to show face detection results (#25)  
20 -* Reorganized GUI code and include paths (#31)  
21 -* 'br -daemon' to listen for commands on stdin  
22 -* Generalized 'br -convert', now requires three parameters  
23 -* Official icon, thanks @sklum!  
24 -  
25 -0.2.0 - 2/23/13  
26 -===============  
27 -* FaceRecognition new distance metric  
28 - - 0 to 1 range indicating match probability  
29 -* Qt 4.8 -> Qt 5.0  
30 -* Cleaner plots generated with 'br -plot'  
31 -* Stasm and FLandmark wrappers  
32 -* Improved demographic filtering speed  
33 - - br::Context::demographicFilters -> br::Context::filters  
34 - - MetadataDistance -> FilterDistance  
35 -* PipeDistance  
36 -* ImpostorUniquenessMeasureTransform  
37 -* MatchProbabilityDistance  
38 -* CrossValidation framework  
39 - - br::Context::crossValidate  
40 - - CrossValidationTransform  
41 - - CrossValidationDistance  
42 -  
43 -0.1.0 - 1/27/13  
44 -===============  
45 -First official release!  
CMakeLists.txt
@@ -176,7 +176,7 @@ foreach(DIR ${BR_THIRDPARTY_APPS_DIR}) @@ -176,7 +176,7 @@ foreach(DIR ${BR_THIRDPARTY_APPS_DIR})
176 endforeach() 176 endforeach()
177 177
178 # Install 178 # Install
179 -install(FILES CHANGELOG.md LICENSE.txt README.md DESTINATION share/openbr) 179 +install(FILES LICENSE.txt README.md DESTINATION share/openbr)
180 install(DIRECTORY share DESTINATION .) 180 install(DIRECTORY share DESTINATION .)
181 install(DIRECTORY ${BR_THIRDPARTY_SHARE} DESTINATION share) 181 install(DIRECTORY ${BR_THIRDPARTY_SHARE} DESTINATION share)
182 182
CTestConfig.cmake deleted
1 -set(CTEST_PROJECT_NAME ${CPACK_PACKAGE_NAME})  
2 -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")  
3 -  
4 -if(NOT DEFINED CTEST_DROP_METHOD)  
5 - set(CTEST_DROP_METHOD "http")  
6 -endif()  
7 -  
8 -if(CTEST_DROP_METHOD STREQUAL "http")  
9 - set(CTEST_DROP_SITE "my.cdash.org")  
10 - set(CTEST_DROP_LOCATION "/submit.php?project=OpenBR")  
11 - set(CTEST_DROP_SITE_CDASH TRUE)  
12 -endif(CTEST_DROP_METHOD STREQUAL "http")  
openbr/frvt2012.cpp deleted
1 -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  
2 - * Copyright 2012 The MITRE Corporation *  
3 - * *  
4 - * Licensed under the Apache License, Version 2.0 (the "License"); *  
5 - * you may not use this file except in compliance with the License. *  
6 - * You may obtain a copy of the License at *  
7 - * *  
8 - * http://www.apache.org/licenses/LICENSE-2.0 *  
9 - * *  
10 - * Unless required by applicable law or agreed to in writing, software *  
11 - * distributed under the License is distributed on an "AS IS" BASIS, *  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *  
13 - * See the License for the specific language governing permissions and *  
14 - * limitations under the License. *  
15 - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */  
16 -  
17 -#include <openbr/openbr_plugin.h>  
18 -#include <openbr/plugins/openbr_internal.h>  
19 -  
20 -#include "frvt2012.h"  
21 -#include "core/distance_sse.h"  
22 -  
23 -using namespace br;  
24 -using namespace std;  
25 -  
26 -static QSharedPointer<Transform> frvt2012_transform;  
27 -static QSharedPointer<Transform> frvt2012_age_transform;  
28 -static QSharedPointer<Transform> frvt2012_gender_transform;  
29 -static const int frvt2012_template_size = 768;  
30 -  
31 -static void initialize(const string &configuration_location)  
32 -{  
33 - // Fake the command line arguments  
34 - int argc = 1;  
35 - char arg1[1]; arg1[0]='\0';  
36 - char *argv[] = { arg1 };  
37 -  
38 - if (Globals == NULL) Context::initialize(argc, argv, QString::fromStdString(configuration_location));  
39 - Globals->quiet = true;  
40 - Globals->parallelism = 0;  
41 -}  
42 -  
43 -static Template templateFromONEFACE(const ONEFACE &oneface)  
44 -{  
45 - return Template(QString::fromStdString(oneface.description),  
46 - cv::Mat(oneface.image_height, oneface.image_width, oneface.image_depth == 8 ? CV_8UC1 : CV_8UC3, oneface.data));  
47 -}  
48 -  
49 -int32_t get_pid(string &sdk_identifier, string &email_address)  
50 -{  
51 - sdk_identifier = "1338";  
52 - email_address = "jklontz@mitre.org";  
53 - return 0;  
54 -}  
55 -  
56 -int32_t get_max_template_sizes(uint32_t &max_enrollment_template_size, uint32_t &max_recognition_template_size)  
57 -{  
58 - max_enrollment_template_size = frvt2012_template_size;  
59 - max_recognition_template_size = frvt2012_template_size;  
60 - return 0;  
61 -}  
62 -  
63 -int32_t initialize_verification(string &configuration_location, vector<string> &descriptions)  
64 -{  
65 - (void) descriptions;  
66 - initialize(configuration_location);  
67 - frvt2012_transform = QSharedPointer<Transform>(Transform::make("Cvt(RGBGray)+Cascade(FrontalFace)!<FaceRecognitionRegistration>!<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>", NULL));  
68 - return 0;  
69 -}  
70 -  
71 -int32_t convert_multiface_to_enrollment_template(const MULTIFACE &input_faces, uint32_t &template_size, uint8_t *proprietary_template)  
72 -{  
73 - uint8_t quality;  
74 - return convert_multiface_to_verification_template(input_faces, template_size, proprietary_template, quality);  
75 -}  
76 -  
77 -int32_t convert_multiface_to_verification_template(const MULTIFACE &input_faces, uint32_t &template_size, uint8_t* proprietary_template, uint8_t &quality)  
78 -{  
79 - // Enroll templates  
80 - TemplateList templates; templates.reserve(input_faces.size());  
81 - foreach (const ONEFACE &oneface, input_faces)  
82 - templates.append(templateFromONEFACE(oneface));  
83 - templates >> *frvt2012_transform.data();  
84 -  
85 - // Compute template size  
86 - template_size = templates.size() * frvt2012_template_size;  
87 -  
88 - // Create proprietary template  
89 - for (int i=0; i<templates.size(); i++)  
90 - memcpy(&proprietary_template[i*frvt2012_template_size], templates[i].m().data, frvt2012_template_size);  
91 -  
92 - quality = 100;  
93 - return 0;  
94 -}  
95 -  
96 -int32_t match_templates(const uint8_t* verification_template, const uint32_t verification_template_size, const uint8_t* enrollment_template, const uint32_t enrollment_template_size, double &similarity)  
97 -{  
98 - const int num_verification = verification_template_size / frvt2012_template_size;  
99 - const int num_enrollment = enrollment_template_size / frvt2012_template_size;  
100 -  
101 - // Return early for failed templates  
102 - if ((num_verification == 0) || (num_enrollment == 0)) {  
103 - similarity = -1;  
104 - return 2;  
105 - }  
106 -  
107 - similarity = 0;  
108 - for (int i=0; i<num_verification; i++)  
109 - for (int j=0; j<num_enrollment; j++)  
110 - similarity += l1(&verification_template[i*frvt2012_template_size], &enrollment_template[j*frvt2012_template_size], frvt2012_template_size);  
111 - similarity /= num_verification * num_enrollment;  
112 - similarity = std::max(0.0, -0.00112956 * (similarity - 6389.75)); // Yes this is a hard coded hack taken from FaceRecognition score normalization  
113 - return 0;  
114 -}  
115 -  
116 -int32_t SdkEstimator::initialize_age_estimation(const string &configuration_location)  
117 -{  
118 - initialize(configuration_location);  
119 - frvt2012_age_transform = QSharedPointer<Transform>(Transform::make("Cvt(RGBGray)+Cascade(FrontalFace)!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<AgeRegressor>+Discard", NULL));  
120 - return 0;  
121 -}  
122 -  
123 -int32_t SdkEstimator::initialize_gender_estimation(const string &configuration_location)  
124 -{  
125 - initialize(configuration_location);  
126 - frvt2012_gender_transform = QSharedPointer<Transform>(Transform::make("Cvt(RGBGray)+Cascade(FrontalFace)!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<GenderClassifier>+Discard", NULL));  
127 - return 0;  
128 -}  
129 -  
130 -int32_t SdkEstimator::estimate_age(const ONEFACE &input_face, int32_t &age)  
131 -{  
132 - TemplateList templates;  
133 - templates.append(templateFromONEFACE(input_face));  
134 - templates >> *frvt2012_age_transform.data();  
135 - age = templates.first().file.get<float>("Age");  
136 - return templates.first().file.fte ? 4 : 0;  
137 -}  
138 -  
139 -int32_t SdkEstimator::estimate_gender(const ONEFACE &input_face, int8_t &gender, double &mf)  
140 -{  
141 - TemplateList templates;  
142 - templates.append(templateFromONEFACE(input_face));  
143 - templates >> *frvt2012_gender_transform.data();  
144 - mf = gender = templates.first().file.get<QString>("Gender") == "Male" ? 0 : 1;  
145 - return templates.first().file.fte ? 4 : 0;  
146 -}  
openbr/frvt2012.h deleted
1 -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  
2 - * Copyright 2012 The MITRE Corporation *  
3 - * *  
4 - * Licensed under the Apache License, Version 2.0 (the "License"); *  
5 - * you may not use this file except in compliance with the License. *  
6 - * You may obtain a copy of the License at *  
7 - * *  
8 - * http://www.apache.org/licenses/LICENSE-2.0 *  
9 - * *  
10 - * Unless required by applicable law or agreed to in writing, software *  
11 - * distributed under the License is distributed on an "AS IS" BASIS, *  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *  
13 - * See the License for the specific language governing permissions and *  
14 - * limitations under the License. *  
15 - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */  
16 -  
17 -#ifndef FRVT2012_H  
18 -#define FRVT2012_H  
19 -  
20 -#include <string>  
21 -#include <vector>  
22 -#include <stdint.h>  
23 -#include <openbr/openbr_export.h>  
24 -  
25 -/*!  
26 - * \defgroup frvt2012 FRVT 2012  
27 - * \brief NIST <a href="http://www.nist.gov/itl/iad/ig/frvt-2012.cfm">Face Recognition Vendor Test 2012</a> API  
28 - */  
29 -  
30 - /*!  
31 - * \addtogroup frvt2012  
32 - * \{  
33 - */  
34 -  
35 -/*!  
36 - * \brief Data structure representing a single face.  
37 - */  
38 -typedef struct sface {  
39 - uint16_t image_width; /*!< \brief Number of pixels horizontally. */  
40 - uint16_t image_height; /*!< \brief Number of pixels vertically. */  
41 - uint16_t image_depth; /*!< \brief Number of bits per pixel. Legal values are 8 and 24. */  
42 - uint8_t format; /*!< \brief Flag indicating native format of the image as supplied to NIST:  
43 - - 0x01 = JPEG (i.e. compressed data)  
44 - - 0x02 = PNG (i.e. never compressed data) */  
45 - uint8_t* data; /*!< \brief Pointer to raster scanned data. Either RGB color or intensity.  
46 - - If image_depth == 24 this points to 3WH bytes RGBRGBRGB...  
47 - - If image_depth == 8 this points to WH bytes IIIIIII */  
48 - std::string description; /*!< \brief Single description of the image. */  
49 -} ONEFACE;  
50 -  
51 -/*!  
52 - * \brief Data structure representing a set of images from a single person  
53 - *  
54 - * The set of face objects used to pass the image(s) and attribute(s) to  
55 - * the template extraction process.  
56 - */  
57 -typedef std::vector<ONEFACE> MULTIFACE;  
58 -  
59 -/*!  
60 - * \brief Return the identifier and contact email address for the software under test.  
61 - *  
62 - * All implementations shall support the self-identification function below.  
63 - * This function is required to support internal NIST book-keeping.  
64 - * The version numbers should be distinct between any versions which offer  
65 - * different algorithmic functionality.  
66 - *  
67 - * \param[out] sdk_identifier  
68 - * Version ID code as hexadecimal integer printed to null terminated ASCII  
69 - * string. NIST will allocate exactly 5 bytes for this. This will be used to  
70 - * identify the SDK in the results reports. This value should be changed every  
71 - * time an SDK is submitted to NIST. The value is vendor assigned - format is  
72 - * not regulated by NIST. EXAMPLE: "011A"  
73 - *  
74 - * \param[out] email_address  
75 - * Point of contact email address as null terminated ASCII string.  
76 - * NIST will allocate at least 64 bytes for this. SDK shall not allocate.  
77 - *  
78 - * \return  
79 - * 0 Success  
80 - * Other Vendor-defined failure  
81 - */  
82 -BR_EXPORT int32_t get_pid(std::string &sdk_identifier,  
83 - std::string &email_address);  
84 -  
85 -/*!  
86 - * \brief Return the maximum template sizes needed during feature extraction.  
87 - *  
88 - * All implementations shall report the maximum expected template sizes.  
89 - * These values will be used by the NIST test harnesses to pre-allocate template  
90 - * data. The values should apply to a single image. For a MULTIFACE containing  
91 - * K images, NIST will allocate K times the value returned.  
92 - *  
93 - * \param[out] max_enrollment_template_size  
94 - * The maximum possible size, in bytes, of the memory needed to store feature  
95 - * data from a single enrollment image.  
96 - *  
97 - * \param[out] max_recognition_template_size  
98 - * The maximum possible size, in bytes, of the memory needed to store feature  
99 - * data from a single verification or identification image.  
100 - *  
101 - * \return  
102 - * 0 Success  
103 - * Other Vendor-defined failure  
104 - */  
105 -BR_EXPORT int32_t get_max_template_sizes(uint32_t &max_enrollment_template_size,  
106 - uint32_t &max_recognition_template_size);  
107 -  
108 -/*!  
109 - * \brief This function initializes the SDK under test.  
110 - *  
111 - * It will be called by the NIST application before any call to the functions  
112 - * convert_multiface_to_enrollment_template or  
113 - * convert_multiface_to_verification_template.  
114 - * The SDK under test should set all parameters. Before any template generation  
115 - * or matching calls are made, the NIST test harness will make a call to the  
116 - * initialization of the function.  
117 - *  
118 - * \param[in] configuration_location  
119 - * A read-only directory containing any vendor-supplied configuration parameters  
120 - * or run-time data files. The name of this directory is assigned by NIST.  
121 - * It is not hardwired by the provider. The names of the files in this directory  
122 - * are hardwired in the SDK and are unrestricted.  
123 - *  
124 - * \param[in] descriptions  
125 - * A lexicon of labels one of which will be assigned to each image.  
126 - * EXAMPLE: The descriptions could be {"mugshot", "visa", "unknown"}.  
127 - * These labels are provided to the SDK so that it knows to expect images of  
128 - * these kinds.  
129 - *  
130 - * \return  
131 - * 0 Success  
132 - * 2 Vendor provided configuration files are not readable in the  
133 - * indicated location  
134 - * 8 The descriptions are unexpected or unusable  
135 - * Other Vendor-defined failure  
136 - */  
137 -BR_EXPORT int32_t initialize_verification(std::string &configuration_location,  
138 - std::vector<std::string> &descriptions);  
139 -  
140 -/*!  
141 - * \brief This function takes a MULTIFACE, and outputs a proprietary template for enrollment.  
142 - *  
143 - * The memory for the output template is allocated by the NIST test harness  
144 - * before the call i.e. the implementation shall not allocate memory for the result.  
145 - * In all cases, even when unable to extract features, the output shall be a  
146 - * template record that may be passed to the match_templates function without  
147 - * error. That is, this routine must internally encode "template creation  
148 - * failed" and the matcher must transparently handle this.  
149 - *  
150 - * \param[in] input_faces  
151 - * An instance of a MULTIFACE structure. Implementations must alter their  
152 - * behavior according to the number of images contained in the structure.  
153 - *  
154 - * \param[in] template_size  
155 - * The size, in bytes, of the output template  
156 - *  
157 - * \param[out] proprietary_template  
158 - * The output template. The format is entirely unregulated. NIST will allocate  
159 - * a KT byte buffer for this template: The value K is the number of images in  
160 - * the MULTIFACE; the value T is output by get_max_template_sizes.  
161 - *  
162 - * \return  
163 - * 0 Success  
164 - * 2 Elective refusal to process this kind of MULTIFACE  
165 - * 4 Involuntary failure to extract features (e.g. could not find face in the  
166 - * input-image)  
167 - * 6 Elective refusal to produce a template (e.g. insufficient pixes between  
168 - * the eyes)  
169 - * 8 Cannot parse input data (i.e. assertion that input record is  
170 - * non-conformant)  
171 - * Other Vendor-defined failure. Failure codes must be documented and  
172 - * communicated to NIST with the submission of the implementation under test.  
173 - */  
174 -BR_EXPORT int32_t convert_multiface_to_enrollment_template(const MULTIFACE &input_faces,  
175 - uint32_t &template_size,  
176 - uint8_t* proprietary_template);  
177 -  
178 -/*!  
179 - * \brief This function takes a MULTIFACE, and outputs a proprietary template for  
180 - * verification.  
181 - *  
182 - * The memory for the output template is allocated by the NIST test harness  
183 - * before the call i.e. the implementation shall not allocate memory for the  
184 - * result. In all cases, even when unable to extract features, the output shall  
185 - * be a template record that may be passed to the match_templates function  
186 - * without error. That is, this routine must internally encode "template  
187 - * creation failed" and the matcher must transparently handle this.  
188 - *  
189 - * \param[in] input_faces  
190 - * An instance of a MULTIFACE structure. Implementations must alter their  
191 - * behavior according to the number of images contained in the structure.  
192 - *  
193 - * \param[in] template_size  
194 - * The size, in bytes, of the output template  
195 - *  
196 - * \param[out] proprietary_template  
197 - * The output template. The format is entirely unregulated. NIST will allocate  
198 - * a KT byte buffer for this template: The value K is the number of images in  
199 - * the MULTIFACE; the value T is output by get_max_template_sizes.  
200 - *  
201 - * \param[out] quality  
202 - * An assessment of image quality. This is optional. The legal values are  
203 - * - [0,100] - The value should have a monotonic decreasing relationship with  
204 - * false non-match rate anticipated for this sample if it was compared with a  
205 - * pristine image of the same person. So, a low value indicates high expected  
206 - * FNMR.  
207 - * - 255 - This value indicates a failed attempt to calculate a quality score.  
208 - * - 254 - This values indicates the value was not assigned.  
209 - *  
210 - * \return  
211 - * 0 Success  
212 - * 2 Elective refusal to process this kind of MULTIFACE  
213 - * 4 Involuntary failure to extract features (e.g. could not find face in the  
214 - * input-image)  
215 - * 6 Elective refusal to produce a template (e.g. insufficient pixes between  
216 - * the eyes)  
217 - * 8 Cannot parse input data (i.e. assertion that input record is  
218 - * non-conformant)  
219 - * Other Vendor-defined failure. Failure codes must be documented and  
220 - * communicated to NIST with the submission of the implementation under test.  
221 - */  
222 -BR_EXPORT int32_t convert_multiface_to_verification_template(const MULTIFACE &input_faces,  
223 - uint32_t &template_size,  
224 - uint8_t* proprietary_template,  
225 - uint8_t &quality);  
226 -  
227 -/*!  
228 - * \brief  
229 - * This function compares two opaque proprietary templates and outputs a  
230 - * similarity score which need not satisfy the metric properties.  
231 - *  
232 - * NIST will allocate memory for this parameter before the call. When either  
233 - * or both of the input templates are the result of a failed template  
234 - * generation, the similarity score shall be -1 and the function return value  
235 - * shall be 2.  
236 - *  
237 - * \param[in] verification_template  
238 - * A template from convert_multiface_to_verification_template().  
239 - *  
240 - * \param[in] verification_template_size  
241 - * The size, in bytes, of the input verification template 0 <= N <= 2^32 - 1  
242 - *  
243 - * \param[in] enrollment_template  
244 - * A template from convert_multiface_to_enrollment_template().  
245 - *  
246 - * \param[in] enrollment_template_size  
247 - * The size, in bytes, of the input enrollment template 0 <= N <= 2^32 - 1  
248 - *  
249 - * \param[out] similarity  
250 - * A similarity score resulting from comparison of the templates, on the  
251 - * range [0,DBL_MAX].  
252 - *  
253 - * \return  
254 - * 0 Success  
255 - * 2 Either or both of the input templates were result of failed feature  
256 - * extraction  
257 - * Other Vendor-defined failure.  
258 - */  
259 -BR_EXPORT int32_t match_templates(const uint8_t* verification_template,  
260 - const uint32_t verification_template_size,  
261 - const uint8_t* enrollment_template,  
262 - const uint32_t enrollment_template_size,  
263 - double &similarity);  
264 -  
265 -/*!  
266 - * \brief Class D estimator abstraction.  
267 - */  
268 -struct BR_EXPORT Estimator {  
269 - /*!< */  
270 - static const int NOTIMPLEMENTED = -1;  
271 - virtual ~Estimator() {}  
272 -  
273 - /*!  
274 - * Intialization functions  
275 - *  
276 - * \param[in] configuration_location  
277 - * A read-only directory containing any vendor-supplied configuration  
278 - * parameters or run-time data files.  
279 - *  
280 - * \return  
281 - * 0 Success  
282 - * 2 Elective refusal to process this kind of MULTIFACE  
283 - * 4 Involuntary failure to extract features (e.g. could not find face in  
284 - * the input-image)  
285 - * 8 Cannot parse input data (i.e. assertion that input record is  
286 - * non-conformant)  
287 - * Other Vendor-defined failure. Failure codes must be documented and  
288 - * communicated to NIST with the submission of the implementation under  
289 - * test.  
290 - */  
291 - /*!\{*/  
292 - virtual int32_t initialize_frontal_pose_estimation(const std::string &configuration_location) { (void) configuration_location; return NOTIMPLEMENTED; }  
293 - virtual int32_t initialize_age_estimation(const std::string &configuration_location) { (void) configuration_location; return NOTIMPLEMENTED; }  
294 - virtual int32_t initialize_gender_estimation(const std::string &configuration_location) { (void) configuration_location; return NOTIMPLEMENTED; }  
295 - virtual int32_t initialize_expression_estimation(const std::string &configuration_location) { (void) configuration_location; return NOTIMPLEMENTED; }  
296 - /*!\}*/  
297 -  
298 - /*!  
299 - * Frontal Pose estimation function  
300 - *  
301 - * \param[in] input_face  
302 - * An instance of a ONEFACE structure.  
303 - *  
304 - * \param[out] non_frontality  
305 - * Indication of how far from frontal the head pose is.  
306 - * The value should be on the range [0,1].  
307 - *  
308 - * \return  
309 - * 0 Success  
310 - * 2 Elective refusal to process this kind of MULTIFACE  
311 - * 4 Involuntary failure to extract features (e.g. could not find face in  
312 - * the input-image)  
313 - * 8 Cannot parse input data (i.e. assertion that input record is  
314 - * non-conformant)  
315 - * Other Vendor-defined failure. Failure codes must be documented and  
316 - * communicated to NIST with the submission of the implementation under  
317 - * test.  
318 - */  
319 - virtual int32_t estimate_frontal_pose_conformance(const ONEFACE &input_face, double &non_frontality) { (void) input_face; (void) non_frontality; return NOTIMPLEMENTED; }  
320 -  
321 - /*!  
322 - * Age Estimation function  
323 - *  
324 - * \param[in] input_face  
325 - * An instance of a ONEFACE structure.  
326 - *  
327 - * \param[out] age  
328 - * Indication of the age (in years) of the person.  
329 - * The value should be on the range [0,100].  
330 - *  
331 - * \return  
332 - * 0 Success  
333 - * 2 Elective refusal to process this kind of MULTIFACE  
334 - * 4 Involuntary failure to extract features (e.g. could not find face in  
335 - * the input-image)  
336 - * 8 Cannot parse input data (i.e. assertion that input record is  
337 - * non-conformant)  
338 - * Other Vendor-defined failure. Failure codes must be documented and  
339 - * communicated to NIST with the submission of the implementation under  
340 - * test.  
341 - */  
342 - virtual int32_t estimate_age(const ONEFACE &input_face, int32_t &age) { (void) input_face; (void) age; return NOTIMPLEMENTED; }  
343 -  
344 - /*!  
345 - * Gender Estimation function  
346 - *  
347 - * \param[in] input_face  
348 - * An instance of a ONEFACE structure.  
349 - *  
350 - * \param[out] gender  
351 - * Indication of the gender of the person. Valid values are  
352 - * 0: Male  
353 - * 1: Female  
354 - * -1: Unknown  
355 - *  
356 - * \param[out] mf  
357 - * A real-valued measure of maleness-femaleness value on [0,1].  
358 - * A value of 0 indicates certainty that the subject is a male,  
359 - * and a value of 1 indicates certainty that the subject is a female.  
360 - *  
361 - * \return  
362 - * 0 Success  
363 - * 2 Elective refusal to process this kind of MULTIFACE  
364 - * 4 Involuntary failure to extract features (e.g. could not find face  
365 - * in the input-image)  
366 - * 8 Cannot parse input data (i.e. assertion that input record is  
367 - * non-conformant)  
368 - * Other Vendor-defined failure. Failure codes must be documented and  
369 - * communicated to NIST with the submission of the implementation under test.  
370 - */  
371 - virtual int32_t estimate_gender(const ONEFACE &input_face, int8_t &gender, double &mf) { (void) input_face; (void) gender; (void) mf; return NOTIMPLEMENTED; }  
372 -  
373 - /*!  
374 - * Expression neutrality function  
375 - *  
376 - * \param[in] input_face  
377 - * An instance of a ONEFACE structure.  
378 - *  
379 - * \param[out] expression_neutrality  
380 - * A real-valued measure of expression neutrality on [0,1] with 0  
381 - * denoting large deviation from neutral and 1 indicating a fully  
382 - * neutral expression.  
383 - *  
384 - * \return  
385 - * 0 Success  
386 - * 2 Elective refusal to process this kind of MULTIFACE  
387 - * 4 Involuntary failure to extract features (e.g. could not find face in  
388 - * the input-image)  
389 - * 8 Cannot parse input data (i.e. assertion that input record is  
390 - * non-conformant)  
391 - * Other Vendor-defined failure. Failure codes must be documented and  
392 - * communicated to NIST with the submission of the implementation under  
393 - * test.  
394 - */  
395 - virtual int32_t estimate_expression_neutrality(const ONEFACE &input_face, double &expression_neutrality) { (void) input_face; (void) expression_neutrality; return NOTIMPLEMENTED; }  
396 -};  
397 -  
398 -/*!  
399 - * \brief Class D estimator implementation.  
400 - */  
401 -struct BR_EXPORT SdkEstimator : public Estimator  
402 -{  
403 - /*!  
404 - * \brief Implemented estimators  
405 - */  
406 - /*!\{*/  
407 - int32_t initialize_age_estimation(const std::string &configuration_location);  
408 - int32_t initialize_gender_estimation(const std::string &configuration_location);  
409 - int32_t estimate_age(const ONEFACE &input_face, int32_t &age);  
410 - int32_t estimate_gender(const ONEFACE &input_face, int8_t &gender, double &mf);  
411 - /*!\}*/  
412 -};  
413 -  
414 -/*! @}*/  
415 -  
416 -#endif // FRVT2012_H  
openbr/openbr_export.cpp deleted
openbr/openbr_plugin.cpp
@@ -426,7 +426,7 @@ br_utemplate Template::toUniversalTemplate(const Template &amp;t) @@ -426,7 +426,7 @@ br_utemplate Template::toUniversalTemplate(const Template &amp;t)
426 return br_new_utemplate(algorithmID, x, y, width, height, confidence, metadata.data(), (const char*) m.data, m.rows * m.cols * m.elemSize()); 426 return br_new_utemplate(algorithmID, x, y, width, height, confidence, metadata.data(), (const char*) m.data, m.rows * m.cols * m.elemSize());
427 } 427 }
428 428
429 -Template Template::fromUniversalTemplate(const br_utemplate &ut) 429 +Template Template::fromUniversalTemplate(br_const_utemplate ut)
430 { 430 {
431 QVariantMap map = QJsonDocument::fromJson(QByteArray((const char*) ut->data)).object().toVariantMap(); 431 QVariantMap map = QJsonDocument::fromJson(QByteArray((const char*) ut->data)).object().toVariantMap();
432 map.insert("AlgorithmID", ut->algorithmID); 432 map.insert("AlgorithmID", ut->algorithmID);
@@ -435,10 +435,27 @@ Template Template::fromUniversalTemplate(const br_utemplate &amp;ut) @@ -435,10 +435,27 @@ Template Template::fromUniversalTemplate(const br_utemplate &amp;ut)
435 map.insert("Width" , ut->width ); 435 map.insert("Width" , ut->width );
436 map.insert("Height" , ut->height ); 436 map.insert("Height" , ut->height );
437 map.insert("Confidence" , ut->confidence ); 437 map.insert("Confidence" , ut->confidence );
438 - const Mat m = Mat(1, ut->fvSize, CV_8UC1, ut->data + ut->mdSize).clone(); 438 + const Mat m = Mat(1, ut->fvSize, CV_8UC1, (void*)(ut->data + ut->mdSize)).clone();
439 return Template(File(map), m); 439 return Template(File(map), m);
440 } 440 }
441 441
  442 +br_utemplate Template::readUniversalTemplate(QFile &file)
  443 +{
  444 + const size_t headerSize = sizeof(br_universal_template);
  445 + br_universal_template *t = (br_universal_template*) malloc(headerSize);
  446 + file.read((char*) t, headerSize);
  447 +
  448 + const size_t dataSize = t->mdSize + t->fvSize;
  449 + t = (br_universal_template*) realloc(t, headerSize + dataSize);
  450 + file.read((char*) &t->data, dataSize);
  451 + return t;
  452 +}
  453 +
  454 +void Template::freeUniversalTemplate(br_const_utemplate t)
  455 +{
  456 + free((void*) t);
  457 +}
  458 +
442 QDataStream &br::operator<<(QDataStream &stream, const Template &t) 459 QDataStream &br::operator<<(QDataStream &stream, const Template &t)
443 { 460 {
444 return stream << static_cast<const QList<cv::Mat>&>(t) << t.file; 461 return stream << static_cast<const QList<cv::Mat>&>(t) << t.file;
openbr/openbr_plugin.h
@@ -295,7 +295,9 @@ struct Template : public QList&lt;cv::Mat&gt; @@ -295,7 +295,9 @@ struct Template : public QList&lt;cv::Mat&gt;
295 } 295 }
296 296
297 static br_utemplate toUniversalTemplate(const Template &t); 297 static br_utemplate toUniversalTemplate(const Template &t);
298 - static Template fromUniversalTemplate(const br_utemplate &ut); 298 + static Template fromUniversalTemplate(br_const_utemplate ut);
  299 + static br_utemplate readUniversalTemplate(QFile &file);
  300 + static void freeUniversalTemplate(br_const_utemplate t);
299 }; 301 };
300 302
301 BR_EXPORT QDataStream &operator<<(QDataStream &stream, const Template &t); 303 BR_EXPORT QDataStream &operator<<(QDataStream &stream, const Template &t);
openbr/plugins/classification/lda.cpp
@@ -478,6 +478,8 @@ class LDATransform : public Transform @@ -478,6 +478,8 @@ class LDATransform : public Transform
478 if (normalize) 478 if (normalize)
479 stdDev = sqrt(results.array().square().sum() / trainingSet.size()); 479 stdDev = sqrt(results.array().square().sum() / trainingSet.size());
480 } 480 }
  481 +
  482 + qDebug() << "LDA projection dimensions:" << projection.rows() << "->" << projection.cols();
481 } 483 }
482 484
483 void project(const Template &src, Template &dst) const 485 void project(const Template &src, Template &dst) const
openbr/plugins/gallery/binary.cpp
@@ -204,154 +204,27 @@ BR_REGISTER(Gallery, galGallery) @@ -204,154 +204,27 @@ BR_REGISTER(Gallery, galGallery)
204 * \brief A contiguous array of br_universal_template. 204 * \brief A contiguous array of br_universal_template.
205 * \author Josh Klontz \cite jklontz 205 * \author Josh Klontz \cite jklontz
206 */ 206 */
207 -class utGallery : public BinaryGallery 207 +class tGallery : public BinaryGallery
208 { 208 {
209 Q_OBJECT 209 Q_OBJECT
210 210
211 Template readTemplate() 211 Template readTemplate()
212 { 212 {
213 - Template t;  
214 - br_universal_template ut;  
215 - if (gallery.read((char*)&ut, sizeof(br_universal_template)) == sizeof(br_universal_template)) {  
216 - QByteArray data(ut.mdSize + ut.fvSize, Qt::Uninitialized);  
217 - char *dst = data.data();  
218 - qint64 bytesNeeded = ut.mdSize + ut.fvSize;  
219 - while (bytesNeeded > 0) {  
220 - qint64 bytesRead = gallery.read(dst, bytesNeeded);  
221 - if (bytesRead <= 0) {  
222 - qDebug() << gallery.errorString();  
223 - qFatal("Unexepected EOF while reading universal template data, needed: %d more of: %d bytes.", int(bytesNeeded), int(ut.mdSize + ut.fvSize));  
224 - }  
225 - bytesNeeded -= bytesRead;  
226 - dst += bytesRead;  
227 - }  
228 -  
229 - t.file.set("AlgorithmID", ut.algorithmID);  
230 - t.file.set("Metadata", QString(data.data()));  
231 - char *dataStart = data.data() + ut.mdSize;  
232 - uint32_t dataSize = ut.fvSize;  
233 - if ((ut.algorithmID <= -1) && (ut.algorithmID >= -3)) {  
234 - t.file.set("FrontalFace", QRectF(ut.x, ut.y, ut.width, ut.height));  
235 - uint32_t *rightEyeX = reinterpret_cast<uint32_t*>(dataStart);  
236 - dataStart += sizeof(uint32_t);  
237 - uint32_t *rightEyeY = reinterpret_cast<uint32_t*>(dataStart);  
238 - dataStart += sizeof(uint32_t);  
239 - uint32_t *leftEyeX = reinterpret_cast<uint32_t*>(dataStart);  
240 - dataStart += sizeof(uint32_t);  
241 - uint32_t *leftEyeY = reinterpret_cast<uint32_t*>(dataStart);  
242 - dataStart += sizeof(uint32_t);  
243 - dataSize -= sizeof(uint32_t)*4;  
244 - t.file.set("First_Eye", QPointF(*rightEyeX, *rightEyeY));  
245 - t.file.set("Second_Eye", QPointF(*leftEyeX, *leftEyeY));  
246 - } else if (ut.algorithmID == 7) {  
247 - // binary data consisting of a single channel matrix, of a supported type.  
248 - // 4 element header:  
249 - // uint16 datatype (single channel opencv datatype code)  
250 - // uint32 matrix rows  
251 - // uint32 matrix cols  
252 - // uint16 matrix depth (max 512)  
253 - // Followed by serialized data, in row-major order (in r/c), with depth values  
254 - // for each layer listed in order (i.e. rgb, rgb etc.)  
255 - // #### NOTE! matlab's default order is col-major, so some work should  
256 - // be done on the matlab side to make sure that the initial serialization is correct.  
257 - uint16_t dataType = *reinterpret_cast<uint32_t*>(dataStart);  
258 - dataStart += sizeof(uint16_t);  
259 -  
260 - uint32_t matrixRows = *reinterpret_cast<uint32_t*>(dataStart);  
261 - dataStart += sizeof(uint32_t);  
262 -  
263 - uint32_t matrixCols = *reinterpret_cast<uint32_t*>(dataStart);  
264 - dataStart += sizeof(uint32_t);  
265 -  
266 - uint16_t matrixDepth= *reinterpret_cast<uint16_t*>(dataStart);  
267 - dataStart += sizeof(uint16_t);  
268 -  
269 - // Set metadata  
270 - t.file.set("X", ut.x);  
271 - t.file.set("Y", ut.y);  
272 - t.file.set("Width", ut.width);  
273 - t.file.set("Height", ut.height);  
274 - t.file.set("Confidence", ut.confidence);  
275 -  
276 - t.append(cv::Mat(matrixRows, matrixCols, CV_MAKETYPE(dataType, matrixDepth), dataStart).clone() /* We don't want a shallow copy! */);  
277 - return t;  
278 - } else {  
279 - t.file.set("X", ut.x);  
280 - t.file.set("Y", ut.y);  
281 - t.file.set("Width", ut.width);  
282 - t.file.set("Height", ut.height);  
283 - t.file.set("Confidence", ut.confidence);  
284 - }  
285 - t.append(cv::Mat(1, dataSize, CV_8UC1, dataStart).clone() /* We don't want a shallow copy! */);  
286 - } else {  
287 - if (!gallery.atEnd())  
288 - qWarning("Failed to read universal template header!");  
289 - gallery.close();  
290 - } 213 + const br_const_utemplate ut = Template::readUniversalTemplate(gallery);
  214 + const Template t = Template::fromUniversalTemplate(ut);
  215 + Template::freeUniversalTemplate(ut);
291 return t; 216 return t;
292 } 217 }
293 218
294 void writeTemplate(const Template &t) 219 void writeTemplate(const Template &t)
295 { 220 {
296 - const int32_t algorithmID = (t.isEmpty() || t.file.fte) ? 0 : t.file.get<int32_t>("AlgorithmID");  
297 -  
298 - // QUrl::fromUserInput provides some nice functionality in terms of completing URLs  
299 - // e.g. C:/test.jpg -> file://C:/test.jpg and google.com/image.jpg -> http://google.com/image.jpg  
300 - const QByteArray metadata = QUrl::fromUserInput(t.file.get<QString>("URL", t.file.name)).toEncoded();  
301 -  
302 - int32_t x = 0, y = 0;  
303 - uint32_t width = 0, height = 0;  
304 - float confidence = 0;  
305 - QByteArray header;  
306 - if ((algorithmID <= -1) && (algorithmID >= -3)) {  
307 - const QRectF frontalFace = t.file.get<QRectF>("FrontalFace");  
308 - x = frontalFace.x();  
309 - y = frontalFace.y();  
310 - width = frontalFace.width();  
311 - height = frontalFace.height();  
312 -  
313 - const QPointF firstEye = t.file.get<QPointF>("First_Eye");  
314 - const QPointF secondEye = t.file.get<QPointF>("Second_Eye");  
315 - const uint32_t rightEyeX = firstEye.x();  
316 - const uint32_t rightEyeY = firstEye.y();  
317 - const uint32_t leftEyeX = secondEye.x();  
318 - const uint32_t leftEyeY = secondEye.y();  
319 -  
320 - header.append((const char*)&rightEyeX, sizeof(uint32_t));  
321 - header.append((const char*)&rightEyeY, sizeof(uint32_t));  
322 - header.append((const char*)&leftEyeX , sizeof(uint32_t));  
323 - header.append((const char*)&leftEyeY , sizeof(uint32_t));  
324 - } else {  
325 - x = t.file.get<int32_t>("X", 0);  
326 - y = t.file.get<int32_t>("Y", 0);  
327 - width = t.file.get<uint32_t>("Width", 0);  
328 - height = t.file.get<uint32_t>("Height", 0);  
329 - confidence = t.file.get<uint32_t>("Confidence", 0);  
330 - }  
331 -  
332 - gallery.write((const char*) &algorithmID, sizeof(int32_t));  
333 - gallery.write((const char*) &x , sizeof(int32_t));  
334 - gallery.write((const char*) &y , sizeof(int32_t));  
335 - gallery.write((const char*) &width , sizeof(uint32_t));  
336 - gallery.write((const char*) &height , sizeof(uint32_t));  
337 - gallery.write((const char*) &confidence , sizeof(float));  
338 -  
339 - const uint32_t mdSize = metadata.size() + 1;  
340 - gallery.write((const char*) &mdSize, sizeof(uint32_t));  
341 -  
342 - const uint32_t signatureSize = (algorithmID == 0) ? 0 : t.m().rows * t.m().cols * t.m().elemSize();  
343 - const uint32_t fvSize = header.size() + signatureSize;  
344 - gallery.write((const char*) &fvSize, sizeof(uint32_t));  
345 -  
346 - gallery.write((const char*) metadata.data(), mdSize);  
347 - if (algorithmID != 0) {  
348 - gallery.write(header);  
349 - gallery.write((const char*) t.m().data, signatureSize);  
350 - } 221 + const br_utemplate ut = Template::toUniversalTemplate(t);
  222 + gallery.write((const char*) ut, sizeof(br_universal_template) + ut->mdSize + ut->fvSize);
  223 + Template::freeUniversalTemplate(ut);
351 } 224 }
352 }; 225 };
353 226
354 -BR_REGISTER(Gallery, utGallery) 227 +BR_REGISTER(Gallery, tGallery)
355 228
356 /*! 229 /*!
357 * \ingroup galleries 230 * \ingroup galleries
openbr/plugins/gallery/xml.cpp
@@ -31,7 +31,9 @@ class xmlGallery : public FileGallery @@ -31,7 +31,9 @@ class xmlGallery : public FileGallery
31 { 31 {
32 Q_OBJECT 32 Q_OBJECT
33 Q_PROPERTY(bool ignoreMetadata READ get_ignoreMetadata WRITE set_ignoreMetadata RESET reset_ignoreMetadata STORED false) 33 Q_PROPERTY(bool ignoreMetadata READ get_ignoreMetadata WRITE set_ignoreMetadata RESET reset_ignoreMetadata STORED false)
  34 + Q_PROPERTY(bool skipMissing READ get_skipMissing WRITE set_skipMissing RESET reset_skipMissing STORED false)
34 BR_PROPERTY(bool, ignoreMetadata, false) 35 BR_PROPERTY(bool, ignoreMetadata, false)
  36 + BR_PROPERTY(bool, skipMissing, false)
35 FileList files; 37 FileList files;
36 38
37 QXmlStreamReader reader; 39 QXmlStreamReader reader;
@@ -116,6 +118,12 @@ class xmlGallery : public FileGallery @@ -116,6 +118,12 @@ class xmlGallery : public FileGallery
116 118
117 // we read another complete template 119 // we read another complete template
118 count++; 120 count++;
  121 +
  122 + // optionally remove templates whose files don't exist or are empty
  123 + if (skipMissing && !QFileInfo(templates.last().file.resolved()).size()) {
  124 + templates.removeLast();
  125 + count--;
  126 + }
119 } 127 }
120 } 128 }
121 } 129 }
openbr/plugins/imgproc/quantize.cpp
@@ -41,6 +41,7 @@ class QuantizeTransform : public Transform @@ -41,6 +41,7 @@ class QuantizeTransform : public Transform
41 minMaxLoc(OpenCVUtils::toMat(data.data()), &minVal, &maxVal); 41 minMaxLoc(OpenCVUtils::toMat(data.data()), &minVal, &maxVal);
42 a = 255.0/(maxVal-minVal); 42 a = 255.0/(maxVal-minVal);
43 b = -a*minVal; 43 b = -a*minVal;
  44 + qDebug() << "Quantized dimensions =" << data.first().m().rows * data.first().m().cols;
44 } 45 }
45 46
46 void project(const Template &src, Template &dst) const 47 void project(const Template &src, Template &dst) const
openbr/plugins/imgproc/synthesizekeypoints.cpp
@@ -155,10 +155,10 @@ class SynthesizePointsTransform : public MetadataTransform @@ -155,10 +155,10 @@ class SynthesizePointsTransform : public MetadataTransform
155 // Because not all triangulations are the same, we have to decide on a canonical set of triangles at training time. 155 // Because not all triangulations are the same, we have to decide on a canonical set of triangles at training time.
156 QHash<TriangleIndicies, int> counts; 156 QHash<TriangleIndicies, int> counts;
157 foreach (const Template &datum, data) { 157 foreach (const Template &datum, data) {
158 -  
159 const QList<QPointF> points = datum.file.points(); 158 const QList<QPointF> points = datum.file.points();
160 - if (points.size() == 0)  
161 - continue; 159 + if (points.size() <= 4)
  160 + continue;
  161 +
162 const QList< QList<int> > triangulation = getTriangulation(points, getBounds(points, 10)); 162 const QList< QList<int> > triangulation = getTriangulation(points, getBounds(points, 10));
163 if (triangulation.empty()) 163 if (triangulation.empty())
164 continue; 164 continue;
@@ -167,6 +167,9 @@ class SynthesizePointsTransform : public MetadataTransform @@ -167,6 +167,9 @@ class SynthesizePointsTransform : public MetadataTransform
167 counts[TriangleIndicies(indicies)]++; 167 counts[TriangleIndicies(indicies)]++;
168 } 168 }
169 169
  170 + if (counts.empty())
  171 + return;
  172 +
170 triangles.clear(); 173 triangles.clear();
171 QHash<TriangleIndicies, int>::const_iterator i = counts.constBegin(); 174 QHash<TriangleIndicies, int>::const_iterator i = counts.constBegin();
172 while (i != counts.constEnd()) { 175 while (i != counts.constEnd()) {
openbr/plugins/metadata/selectpoints.cpp
@@ -29,8 +29,20 @@ class SelectPointsTransform : public UntrainableMetadataTransform @@ -29,8 +29,20 @@ class SelectPointsTransform : public UntrainableMetadataTransform
29 Q_OBJECT 29 Q_OBJECT
30 Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false) 30 Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false)
31 Q_PROPERTY(bool invert READ get_invert WRITE set_invert RESET reset_invert STORED false) // keep the points _not_ in the list 31 Q_PROPERTY(bool invert READ get_invert WRITE set_invert RESET reset_invert STORED false) // keep the points _not_ in the list
  32 + Q_PROPERTY(int rangeStart READ get_rangeStart WRITE set_rangeStart RESET reset_rangeStart STORED false)
  33 + Q_PROPERTY(int rangeEnd READ get_rangeEnd WRITE set_rangeEnd RESET reset_rangeEnd STORED false)
32 BR_PROPERTY(QList<int>, indices, QList<int>()) 34 BR_PROPERTY(QList<int>, indices, QList<int>())
33 BR_PROPERTY(bool, invert, false) 35 BR_PROPERTY(bool, invert, false)
  36 + BR_PROPERTY(int, rangeStart, -1)
  37 + BR_PROPERTY(int, rangeEnd, -1)
  38 +
  39 + void init()
  40 + {
  41 + if ((rangeStart != -1) && (rangeEnd != -1))
  42 + for (int i=rangeStart; i<=rangeEnd; i++)
  43 + if (!indices.contains(i))
  44 + indices.append(i);
  45 + }
34 46
35 void projectMetadata(const File &src, File &dst) const 47 void projectMetadata(const File &src, File &dst) const
36 { 48 {
share/openbr/doc deleted
1 -Subproject commit 7236a155867a161b9bf6c801e1d158250e36ebf3