Commit a232dceb21a97cc3077f83c58aae0e5274b60969

Authored by Austin Blanton
2 parents effd3d22 afe4dc93

Merge pull request #164 from biometrics/string_management

Updates to the c API to handle returning strings more safely
app/br/br.cpp
... ... @@ -171,7 +171,11 @@ public:
171 171 // Do nothing because we checked for this flag prior to initialization
172 172 } else if (!strcmp(fun, "objects")) {
173 173 check(parc <= 2, "Incorrect parameter count for 'objects'.");
174   - printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*"));
  174 + int size = br_objects(NULL, 0, parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*");
  175 + char * temp = new char[size];
  176 + br_objects(temp, size, parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*");
  177 + printf("%s\n", temp);
  178 + delete [] temp;
175 179 } else if (!strcmp(fun, "about")) {
176 180 check(parc == 0, "No parameters expected for 'about'.");
177 181 printf("%s\n", br_about());
... ...
openbr/gui/algorithm.cpp
1 1 #include <QStringList>
2 2 #include <openbr/openbr.h>
  3 +#include <openbr/openbr_plugin.h>
3 4  
4 5 #include "algorithm.h"
5 6  
... ... @@ -18,7 +19,7 @@ bool br::Algorithm::addAlgorithm(const QString &amp;algorithm, const QString &amp;displa
18 19 {
19 20 static QStringList availableAlgorithms;
20 21 if (availableAlgorithms.isEmpty())
21   - availableAlgorithms = QString(br_objects("Abbreviation", ".*", false)).split("\n");
  22 + availableAlgorithms = br::Context::objects("Abbreviation", ".*", false);
22 23  
23 24 if (!availableAlgorithms.contains(algorithm))
24 25 return false;
... ...
openbr/gui/gallerytoolbar.cpp
... ... @@ -84,7 +84,7 @@ void br::GalleryToolBar::_enroll(const br::File &amp;input)
84 84 galleryLock.lock();
85 85 this->input = input;
86 86 if (input.suffix() == "gal") gallery = input.name + ".mem";
87   - else gallery = QString("%1/galleries/%2.gal[cache]").arg(br_scratch_path(), qPrintable(input.baseName()+input.hash()));
  87 + else gallery = QString("%1/galleries/%2.gal[cache]").arg(br::Globals->scratchPath(), qPrintable(input.baseName()+input.hash()));
88 88 files = br::Enroll(input.flat(), gallery.flat());
89 89 galleryLock.unlock();
90 90 }
... ... @@ -148,7 +148,7 @@ void br::GalleryToolBar::home()
148 148  
149 149 void br::GalleryToolBar::mean()
150 150 {
151   - const QString file = QString("%1/mean/%2.png").arg(br_scratch_path(), input.baseName()+input.hash());
  151 + const QString file = QString("%1/mean/%2.png").arg(br::Globals->scratchPath(), input.baseName()+input.hash());
152 152 br_set_property("CENTER_TRAIN_B", qPrintable(file));
153 153 br::File trainingFile = input;
154 154 br_train(qPrintable(trainingFile.flat()), "[algorithm=MedianFace]");
... ...
openbr/gui/progress.cpp
1 1 #include <openbr/openbr.h>
  2 +#include <openbr/openbr_plugin.h>
2 3  
3 4 #include "progress.h"
4 5  
... ... @@ -29,7 +30,7 @@ void br::Progress::checkProgress()
29 30 const bool visible = progress >= 0 && progress < 100;
30 31  
31 32 if (visible) {
32   - showMessage(br_most_recent_message());
  33 + showMessage(Globals->mostRecentMessage);
33 34 pbProgress.setValue(progress);
34 35 if (progress > 100) pbProgress.setMaximum(0);
35 36 else pbProgress.setMaximum(100);
... ...
openbr/gui/templateviewer.cpp
... ... @@ -72,7 +72,7 @@ void TemplateViewer::refreshImage()
72 72 if (file.isNull() || (format == "Photo")) {
73 73 setImage(file, true);
74 74 } else {
75   - const QString path = QString(br_scratch_path()) + "/thumbnails";
  75 + const QString path = QString(br::Globals->scratchPath()) + "/thumbnails";
76 76 const QString hash = file.hash()+format;
77 77 const QString processedFile = path+"/"+file.baseName()+hash+".png";
78 78 if (!QFileInfo(processedFile).exists()) {
... ...
openbr/gui/transformeditor.cpp
... ... @@ -24,7 +24,7 @@ using namespace br;
24 24 br::TransformEditor::TransformEditor(Transform *transform, QWidget *parent)
25 25 : QWidget(parent)
26 26 {
27   - name.addItems(QString(br_objects("Transform", ".*", false)).split('\n'));
  27 + name.addItems(br::Context::objects("Transform", ".*", false));
28 28 layout.addWidget(&name);
29 29 setLayout(&layout);
30 30  
... ...
openbr/openbr.cpp
... ... @@ -28,9 +28,27 @@
28 28  
29 29 using namespace br;
30 30  
  31 +static int partialCopy(const QString & string, char * buffer, int buffer_length)
  32 +{
  33 +
  34 + QByteArray byteArray = string.toLocal8Bit();
  35 +
  36 + int copyLength = std::min(buffer_length-1, byteArray.size());
  37 + if (copyLength < 0)
  38 + return byteArray.size() + 1;
  39 +
  40 + memcpy(buffer, byteArray.data(), copyLength);
  41 + buffer[copyLength] = '\0';
  42 +
  43 + return byteArray.size() + 1;
  44 +}
  45 +
31 46 const char *br_about()
32 47 {
  48 + static QMutex aboutLock;
  49 + QMutexLocker lock(&aboutLock);
33 50 static QByteArray about = Context::about().toLocal8Bit();
  51 +
34 52 return about.data();
35 53 }
36 54  
... ... @@ -150,53 +168,14 @@ void br_make_pairwise_mask(const char *target_input, const char *query_input, co
150 168 BEE::makePairwiseMask(target_input, query_input, mask);
151 169 }
152 170  
153   -const char *br_most_recent_message()
  171 +int br_most_recent_message(char * buffer, int buffer_length)
154 172 {
155   - static QByteArray byteArray;
156   - byteArray = Globals->mostRecentMessage.toLocal8Bit();
157   - return byteArray.data();
  173 + return partialCopy(Globals->mostRecentMessage, buffer, buffer_length);
158 174 }
159 175  
160   -const char *br_objects(const char *abstractions, const char *implementations, bool parameters)
  176 +int br_objects(char * buffer, int buffer_length, const char *abstractions, const char *implementations, bool parameters)
161 177 {
162   - static QByteArray objects;
163   -
164   - QStringList objectList;
165   - QRegExp abstractionsRegExp(abstractions);
166   - QRegExp implementationsRegExp(implementations);
167   -
168   - if (abstractionsRegExp.exactMatch("Abbreviation"))
169   - foreach (const QString &name, Globals->abbreviations.keys())
170   - if (implementationsRegExp.exactMatch(name))
171   - objectList.append(name + (parameters ? "\t" + Globals->abbreviations[name] : ""));
172   -
173   - if (abstractionsRegExp.exactMatch("Distance"))
174   - foreach (const QString &name, Factory<Distance>::names())
175   - if (implementationsRegExp.exactMatch(name))
176   - objectList.append(name + (parameters ? "\t" + Factory<Distance>::parameters(name) : ""));
177   -
178   - if (abstractionsRegExp.exactMatch("Format"))
179   - foreach (const QString &name, Factory<Format>::names())
180   - if (implementationsRegExp.exactMatch(name))
181   - objectList.append(name + (parameters ? "\t" + Factory<Format>::parameters(name) : ""));
182   -
183   - if (abstractionsRegExp.exactMatch("Initializer"))
184   - foreach (const QString &name, Factory<Initializer>::names())
185   - if (implementationsRegExp.exactMatch(name))
186   - objectList.append(name + (parameters ? "\t" + Factory<Initializer>::parameters(name) : ""));
187   -
188   - if (abstractionsRegExp.exactMatch("Output"))
189   - foreach (const QString &name, Factory<Output>::names())
190   - if (implementationsRegExp.exactMatch(name))
191   - objectList.append(name + (parameters ? "\t" + Factory<Output>::parameters(name) : ""));
192   -
193   - if (abstractionsRegExp.exactMatch("Transform"))
194   - foreach (const QString &name, Factory<Transform>::names())
195   - if (implementationsRegExp.exactMatch(name))
196   - objectList.append(name + (parameters ? "\t" + Factory<Transform>::parameters(name) : ""));
197   -
198   - objects = objectList.join("\n").toLocal8Bit();
199   - return objects.data();
  178 + return partialCopy(br::Context::objects(abstractions, implementations, parameters).join('\n'), buffer, buffer_length);
200 179 }
201 180  
202 181 bool br_plot(int num_files, const char *files[], const char *destination, bool show)
... ... @@ -251,15 +230,15 @@ void br_read_pipe(const char *pipe, int *argc, char ***argv)
251 230 *argv = rawCharArrayList.data();
252 231 }
253 232  
254   -const char *br_scratch_path()
  233 +int br_scratch_path(char * buffer, int buffer_length)
255 234 {
256   - static QByteArray byteArray;
257   - byteArray = Context::scratchPath().toLocal8Bit();
258   - return byteArray.data();
  235 + return partialCopy(Context::scratchPath(), buffer, buffer_length);
259 236 }
260 237  
261 238 const char *br_sdk_path()
262 239 {
  240 + static QMutex sdkLock;
  241 + QMutexLocker lock(&sdkLock);
263 242 static QByteArray sdkPath = QDir(Globals->sdkPath).absolutePath().toLocal8Bit();
264 243 return sdkPath.data();
265 244 }
... ... @@ -303,6 +282,8 @@ void br_train_n(int num_inputs, const char *inputs[], const char *model)
303 282  
304 283 const char *br_version()
305 284 {
  285 + static QMutex versionLock;
  286 + QMutexLocker lock(&versionLock);
306 287 static QByteArray version = Context::version().toLocal8Bit();
307 288 return version.data();
308 289 }
... ... @@ -380,11 +361,9 @@ bool br_img_is_empty(br_template tmpl)
380 361 return t->m().empty();
381 362 }
382 363  
383   -const char* br_get_filename(br_template tmpl)
  364 +int br_get_filename(char * buffer, int buffer_length, br_template tmpl)
384 365 {
385   - static QByteArray buffer;
386   - buffer = reinterpret_cast<Template*>(tmpl)->file.name.toLocal8Bit();
387   - return buffer.data();
  366 + return partialCopy(reinterpret_cast<Template*>(tmpl)->file.name, buffer, buffer_length);
388 367 }
389 368  
390 369 void br_set_filename(br_template tmpl, const char *filename)
... ... @@ -393,15 +372,11 @@ void br_set_filename(br_template tmpl, const char *filename)
393 372 t->file.name = filename;
394 373 }
395 374  
396   -const char* br_get_metadata_string(br_template tmpl, const char *key)
  375 +int br_get_metadata_string(char * buffer, int buffer_length, br_template tmpl, const char *key)
397 376 {
398 377 Template *t = reinterpret_cast<Template*>(tmpl);
399   - // need an object outside of this scope
400   - // so the char pointer is valid
401   - static QByteArray result;
402 378 QVariant qvar = t->file.value(key);
403   - result = QtUtils::toString(qvar).toUtf8();
404   - return result.data();
  379 + return partialCopy(QtUtils::toString(qvar), buffer, buffer_length);
405 380 }
406 381  
407 382 br_template_list br_enroll_template(br_template tmpl)
... ...
openbr/openbr.h
... ... @@ -41,6 +41,10 @@ extern &quot;C&quot; {
41 41 * \section managed_return_value Managed Return Value
42 42 * Memory for <tt>const char*</tt> return values is managed internally and guaranteed until the next call to the function.
43 43 *
  44 + * \section input_string_buffer Input String Buffer
  45 + * Users should input a char * buffer and the size of that buffer. String data will be copied into the buffer, if the buffer is too
  46 + * small, only part of the string will be copied. Returns the buffer size required to contain the complete string.
  47 + *
44 48 * \section examples Examples
45 49 * - \ref c_face_recognition_evaluation
46 50 *
... ... @@ -56,7 +60,6 @@ extern &quot;C&quot; {
56 60  
57 61 /*!
58 62 * \brief Wraps br::Context::about()
59   - * \note \ref managed_return_value
60 63 * \see br_version
61 64 */
62 65 BR_EXPORT const char *br_about();
... ... @@ -256,10 +259,10 @@ BR_EXPORT void br_make_pairwise_mask(const char *target_input, const char *query
256 259  
257 260 /*!
258 261 * \brief Returns the most recent line sent to stderr.
259   - * \note \ref managed_return_value
  262 + * \note \ref input_string_buffer
260 263 * \see br_progress br_time_remaining
261 264 */
262   -BR_EXPORT const char *br_most_recent_message();
  265 +BR_EXPORT int br_most_recent_message(char * buffer, int buffer_length);
263 266  
264 267 /*!
265 268 * \brief Returns names and parameters for the requested objects.
... ... @@ -268,10 +271,10 @@ BR_EXPORT const char *br_most_recent_message();
268 271 * \param abstractions Regular expression of the abstractions to search.
269 272 * \param implementations Regular expression of the implementations to search.
270 273 * \param parameters Include parameters after object name.
271   - * \note \ref managed_return_value
  274 + * \note \ref input_string_buffer
272 275 * \note This function uses Qt's <a href="http://doc.qt.digia.com/stable/qregexp.html">QRegExp</a> syntax.
273 276 */
274   -BR_EXPORT const char *br_objects(const char *abstractions = ".*", const char *implementations = ".*", bool parameters = true);
  277 +BR_EXPORT int br_objects(char * buffer, int buffer_length, const char *abstractions = ".*", const char *implementations = ".*", bool parameters = true);
275 278  
276 279 /*!
277 280 * \brief Renders recognition performance figures for a set of <tt>.csv</tt> files created by \ref br_eval.
... ... @@ -376,14 +379,14 @@ BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv);
376 379  
377 380 /*!
378 381 * \brief Wraps br::Context::scratchPath()
379   - * \note \ref managed_return_value
  382 + * \note \ref input_string_buffer
380 383 * \see br_version
381 384 */
382   -BR_EXPORT const char *br_scratch_path();
  385 +BR_EXPORT int br_scratch_path(char * buffer, int buffer_length);
  386 +
383 387  
384 388 /*!
385 389 * \brief Returns the full path to the root of the SDK.
386   - * \note \ref managed_return_value
387 390 * \see br_initialize
388 391 */
389 392 BR_EXPORT const char *br_sdk_path();
... ... @@ -436,7 +439,6 @@ BR_EXPORT void br_train_n(int num_inputs, const char *inputs[], const char *mode
436 439  
437 440 /*!
438 441 * \brief Wraps br::Context::version()
439   - * \note \ref managed_return_value
440 442 * \see br_about br_scratch_path
441 443 */
442 444 BR_EXPORT const char *br_version();
... ... @@ -508,16 +510,18 @@ BR_EXPORT int br_img_channels(br_template tmpl);
508 510 BR_EXPORT bool br_img_is_empty(br_template tmpl);
509 511 /*!
510 512 * \brief Get the filename for a br::Template
  513 + * \note \ref input_string_buffer
511 514 */
512   -BR_EXPORT const char* br_get_filename(br_template tmpl);
  515 +BR_EXPORT int br_get_filename(char * buffer, int buffer_length, br_template tmpl);
513 516 /*!
514 517 * \brief Set the filename for a br::Template.
515 518 */
516 519 BR_EXPORT void br_set_filename(br_template tmpl, const char *filename);
517 520 /*!
518 521 * \brief Get metadata as a string for the given key in the given template.
  522 + * \note \ref input_string_buffer
519 523 */
520   -BR_EXPORT const char* br_get_metadata_string(br_template, const char *key);
  524 +BR_EXPORT int br_get_metadata_string(char * buffer, int buffer_length, br_template tmpl, const char *key);
521 525 /*!
522 526 * \brief Enroll a br::Template from the C API! Returns a pointer to a br::TemplateList
523 527 * \param tmpl Pointer to a br::Template.
... ...
openbr/openbr_plugin.cpp
... ... @@ -203,6 +203,13 @@ QList&lt;QRectF&gt; File::namedRects() const
203 203 const QVariant &variant = m_metadata[key];
204 204 if (variant.canConvert<QRectF>())
205 205 rects.append(variant.value<QRectF>());
  206 + else if(variant.canConvert<QList<QRectF> >()) {
  207 + QList<QRectF> list = variant.value<QList<QRectF> >();
  208 + for (int i=0;i < list.size();i++)
  209 + {
  210 + rects.append(list[i]);
  211 + }
  212 + }
206 213 }
207 214 return rects;
208 215 }
... ... @@ -1009,6 +1016,47 @@ QString br::Context::scratchPath()
1009 1016 return QString("%1/%2-%3.%4").arg(QDir::homePath(), PRODUCT_NAME, QString::number(PRODUCT_VERSION_MAJOR), QString::number(PRODUCT_VERSION_MINOR));
1010 1017 }
1011 1018  
  1019 +
  1020 +QStringList br::Context::objects(const char *abstractions, const char *implementations, bool parameters)
  1021 +{
  1022 + QStringList objectList;
  1023 + QRegExp abstractionsRegExp(abstractions);
  1024 + QRegExp implementationsRegExp(implementations);
  1025 +
  1026 + if (abstractionsRegExp.exactMatch("Abbreviation"))
  1027 + foreach (const QString &name, Globals->abbreviations.keys())
  1028 + if (implementationsRegExp.exactMatch(name))
  1029 + objectList.append(name + (parameters ? "\t" + Globals->abbreviations[name] : ""));
  1030 +
  1031 + if (abstractionsRegExp.exactMatch("Distance"))
  1032 + foreach (const QString &name, Factory<Distance>::names())
  1033 + if (implementationsRegExp.exactMatch(name))
  1034 + objectList.append(name + (parameters ? "\t" + Factory<Distance>::parameters(name) : ""));
  1035 +
  1036 + if (abstractionsRegExp.exactMatch("Format"))
  1037 + foreach (const QString &name, Factory<Format>::names())
  1038 + if (implementationsRegExp.exactMatch(name))
  1039 + objectList.append(name + (parameters ? "\t" + Factory<Format>::parameters(name) : ""));
  1040 +
  1041 + if (abstractionsRegExp.exactMatch("Initializer"))
  1042 + foreach (const QString &name, Factory<Initializer>::names())
  1043 + if (implementationsRegExp.exactMatch(name))
  1044 + objectList.append(name + (parameters ? "\t" + Factory<Initializer>::parameters(name) : ""));
  1045 +
  1046 + if (abstractionsRegExp.exactMatch("Output"))
  1047 + foreach (const QString &name, Factory<Output>::names())
  1048 + if (implementationsRegExp.exactMatch(name))
  1049 + objectList.append(name + (parameters ? "\t" + Factory<Output>::parameters(name) : ""));
  1050 +
  1051 + if (abstractionsRegExp.exactMatch("Transform"))
  1052 + foreach (const QString &name, Factory<Transform>::names())
  1053 + if (implementationsRegExp.exactMatch(name))
  1054 + objectList.append(name + (parameters ? "\t" + Factory<Transform>::parameters(name) : ""));
  1055 +
  1056 +
  1057 + return objectList;
  1058 +}
  1059 +
1012 1060 void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
1013 1061 {
1014 1062 // Something about this method is not thread safe, and will lead to crashes if qDebug
... ...
openbr/openbr_plugin.h
... ... @@ -830,6 +830,18 @@ public:
830 830 */
831 831 static QString scratchPath();
832 832  
  833 + /*!
  834 + * \brief Returns names and parameters for the requested objects.
  835 + *
  836 + * Each object is \c \\n seperated. Arguments are seperated from the object name with a \c \\t.
  837 + * \param abstractions Regular expression of the abstractions to search.
  838 + * \param implementations Regular expression of the implementations to search.
  839 + * \param parameters Include parameters after object name.
  840 + * \note \ref managed_return_value
  841 + * \note This function uses Qt's <a href="http://doc.qt.digia.com/stable/qregexp.html">QRegExp</a> syntax.
  842 + */
  843 + static QStringList objects(const char *abstractions = ".*", const char *implementations = ".*", bool parameters = true);
  844 +
833 845 private:
834 846 static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
835 847 };
... ...
scripts/brpy/__init__.py
... ... @@ -12,6 +12,14 @@ def _var_string_args(n):
12 12 s.extend(_string_args(n))
13 13 return s
14 14  
  15 +def _handle_string_func(func):
  16 + def call_func(*args):
  17 + howlong = func('', 0, *args)
  18 + msg = 'x'*(howlong-1)
  19 + func(msg, howlong, *args)
  20 + return msg
  21 + return call_func
  22 +
15 23 def init_brpy(br_loc='/usr/local/lib'):
16 24 """Takes the ctypes lib object for br and initializes all function inputs and outputs"""
17 25 br_loc += '/libopenbr.%s'
... ... @@ -48,9 +56,14 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
48 56 br.br_is_classifier.restype = c_bool
49 57 br.br_make_mask.argtypes = _string_args(3)
50 58 br.br_make_pairwise_mask.argtypes = _string_args(3)
51   - br.br_most_recent_message.restype = c_char_p
52   - br.br_objects.argtypes = _string_args(2) + [c_bool]
53   - br.br_objects.restype = c_char_p
  59 + br.br_most_recent_message.argtypes = [c_char_p, c_int]
  60 + br.br_most_recent_message.restype = c_int
  61 + func = br.br_most_recent_message.__call__
  62 + br.br_most_recent_message = _handle_string_func(func)
  63 + br.br_objects.argtypes = [c_char_p, c_int] + _string_args(2) + [c_bool]
  64 + br.br_objects.restype = c_int
  65 + func2 = br.br_objects.__call__
  66 + br.br_objects = _handle_string_func(func2)
54 67 br.br_plot.argtypes = plot_args
55 68 br.br_plot.restype = c_bool
56 69 br.br_plot_detection.argtypes = plot_args
... ... @@ -61,7 +74,10 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
61 74 br.br_plot_metadata.restype = c_bool
62 75 br.br_progress.restype = c_float
63 76 br.br_read_pipe.argtypes = [c_char_p, POINTER(c_int), POINTER(POINTER(c_char_p))]
64   - br.br_scratch_path.restype = c_char_p
  77 + br.br_scratch_path.argtypes = [c_char_p, c_int]
  78 + br.br_scratch_path.restype = c_int
  79 + func3 = br.br_scratch_path.__call__
  80 + br.br_scratch_path = _handle_string_func(func3)
65 81 br.br_sdk_path.restype = c_char_p
66 82 br.br_get_header.argtypes = [c_char_p, POINTER(c_char_p), POINTER(c_char_p)]
67 83 br.br_set_header.argtypes = _string_args(3)
... ... @@ -88,11 +104,15 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
88 104 br.br_img_channels.restype = c_int
89 105 br.br_img_is_empty.argtypes = [c_void_p]
90 106 br.br_img_is_empty.restype = c_bool
91   - br.br_get_filename.argtypes = [c_void_p]
92   - br.br_get_filename.restype = c_char_p
  107 + br.br_get_filename.argtypes = [c_char_p, c_int, c_void_p]
  108 + br.br_get_filename.restype = c_int
  109 + func4 = br.br_get_filename.__call__
  110 + br.br_get_filename = _handle_string_func(func4)
93 111 br.br_set_filename.argtypes = [c_void_p, c_char_p]
94   - br.br_get_metadata_string.argtypes = [c_void_p, c_char_p]
95   - br.br_get_metadata_string.restype = c_char_p
  112 + br.br_get_metadata_string.argtypes = [c_char_p, c_int, c_void_p, c_char_p]
  113 + br.br_get_metadata_string.restype = c_int
  114 + func5 = br.br_get_metadata_string.__call__
  115 + br.br_get_metadata_string = _handle_string_func(func5)
96 116 br.br_enroll_template.argtypes = [c_void_p]
97 117 br.br_enroll_template.restype = c_void_p
98 118 br.br_enroll_template_list.argtypes = [c_void_p]
... ...