00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifndef NEOVISIONII_NV2LABELREADER_C_DEFINED
00039 #define NEOVISIONII_NV2LABELREADER_C_DEFINED
00040
00041
00042 #include <deque>
00043 #include <string>
00044
00045 #include "NeovisionII/Nv2LabelReader.H"
00046
00047 #include "Image/CutPaste.H"
00048 #include "Image/DrawOps.H"
00049 #include "NeovisionII/nv2_label_reader.h"
00050 #include "Util/StringConversions.H"
00051 #include "Util/StringUtil.H"
00052 #include "Util/sformat.H"
00053
00054
00055 template <class T>
00056 void writeText2(Image<T>& dst,
00057 const Point2D<int>& pt, const char* text,
00058 const T col, const T bgcol, const SimpleFont& f,
00059 const double bg_alpha,
00060 const TextAnchor anchor)
00061 {
00062 const int textwidth = strlen(text) * f.w();
00063 const int textheight = f.h();
00064
00065 const Point2D<int> top_left
00066 = anchor == ANCHOR_BOTTOM_RIGHT ? pt - Point2D<int>(textwidth, textheight)
00067 : anchor == ANCHOR_BOTTOM_LEFT ? pt - Point2D<int>(0, textheight)
00068 : anchor == ANCHOR_TOP_RIGHT ? pt - Point2D<int>(textwidth, 0)
00069 : pt;
00070
00071 Point2D<int> p = top_left;
00072 const int ww = dst.getWidth(), hh = dst.getHeight();
00073 const int len = int(strlen(text));
00074
00075 for (int i = 0; i < len; i ++)
00076 {
00077 const unsigned char *ptr = f.charptr(text[i]);
00078
00079 for (int y = 0; y < int(f.h()); y ++)
00080 for (int x = 0; x < int(f.w()); x ++)
00081 if (p.i + x >= 0 && p.i + x < ww && p.j + y >= 0 && p.j + y < hh)
00082 {
00083 if (!ptr[y * f.w() + x])
00084 dst.setVal(p.i + x, p.j + y, col);
00085 else
00086 dst.setVal(p.i + x, p.j + y,
00087 bgcol * (1.0 - bg_alpha)
00088 + dst.getVal(p.i + x, p.j + y) * bg_alpha);
00089 }
00090 p.i += f.w();
00091 }
00092 }
00093
00094
00095 Nv2LabelReader::Nv2LabelReader(const PixRGB<byte> color_,
00096 const int label_reader_port,
00097 const std::string& remote_patch_reader)
00098 :
00099 reader(),
00100 color(color_),
00101 lastConfidence()
00102 {
00103 std::vector<std::string> parts;
00104 split(remote_patch_reader, ":", std::back_inserter(parts));
00105 if (parts.size() != 2 && parts.size() != 3)
00106 LFATAL("couldn't parse addr:port[:pixtype] from '%s'",
00107 remote_patch_reader.c_str());
00108
00109 const std::string remote_patch_reader_addr = parts[0];
00110 const int remote_patch_reader_port = fromStr<int>(parts[1]);
00111
00112
00113 this->pixtype = NV2_PIXEL_TYPE_RGB24;
00114 if (parts.size() >= 3)
00115 {
00116 if (parts[2].compare("gray8") == 0)
00117 this->pixtype = NV2_PIXEL_TYPE_GRAY8;
00118 else if (parts[2].compare("rgb24") == 0)
00119 this->pixtype = NV2_PIXEL_TYPE_RGB24;
00120 else
00121 LFATAL("invalid pixel type %s (expected gray8 or rgb24",
00122 parts[2].c_str());
00123 }
00124
00125 reader = nv2_label_reader_create(label_reader_port,
00126 remote_patch_reader_addr.c_str(),
00127 remote_patch_reader_port);
00128
00129 LINFO("label reader at %s:%d, "
00130 "listening for labels on port %d",
00131 remote_patch_reader_addr.c_str(),
00132 remote_patch_reader_port,
00133 label_reader_port);
00134 }
00135
00136
00137 Nv2LabelReader::~Nv2LabelReader()
00138 {
00139 nv2_label_reader_destroy(reader);
00140 }
00141
00142
00143 void Nv2LabelReader::sendPatch(const uint32_t id,
00144 const Image<PixRGB<byte> >& fullimg,
00145 const Rectangle& foa,
00146 const Image<PixRGB<byte> >& foapatch,
00147 const rutz::time& qtime,
00148 bool is_training_image,
00149 const std::string& training_label,
00150 const std::string& remote_command,
00151 Point2D<int> fixLoc)
00152 {
00153 {
00154 const size_t npix = foapatch.getSize();
00155
00156 nv2_image_patch patch;
00157 patch.protocol_version = NV2_PATCH_PROTOCOL_VERSION;
00158 patch.width = foapatch.getWidth();
00159 patch.height = foapatch.getHeight();
00160 patch.fix_x = fixLoc.i;
00161 patch.fix_y = fixLoc.j;
00162 patch.id = id;
00163 patch.is_training_image = is_training_image ? 1 : 0;
00164 patch.type = this->pixtype;
00165 nv2_image_patch_set_training_label(&patch, training_label.c_str());
00166 nv2_image_patch_set_remote_command(&patch, remote_command.c_str());
00167
00168 switch (this->pixtype)
00169 {
00170 case NV2_PIXEL_TYPE_NONE:
00171 patch.data = 0;
00172 break;
00173
00174 case NV2_PIXEL_TYPE_GRAY8:
00175 {
00176 patch.data = (unsigned char*) malloc(npix * sizeof(byte));
00177 if (patch.data == 0)
00178 LFATAL("malloc() failed");
00179
00180 const Image<PixRGB<byte> >::const_iterator foaptr =
00181 foapatch.begin();
00182
00183 for (size_t i = 0; i < npix; ++i)
00184 patch.data[i] = foaptr[i].luminance();
00185 }
00186 break;
00187
00188 case NV2_PIXEL_TYPE_RGB24:
00189 {
00190 patch.data = (unsigned char*) malloc(3 * npix * sizeof(byte));
00191 if (patch.data == 0)
00192 LFATAL("malloc() failed");
00193
00194 memcpy(&patch.data[0], foapatch.getArrayPtr(),
00195 3 * npix * sizeof(byte));
00196 }
00197 break;
00198 }
00199
00200 nv2_label_reader_send_patch(reader, &patch);
00201 }
00202
00203 PendingImage qimg;
00204 qimg.fullimg = fullimg;
00205 qimg.foa = foa;
00206 qimg.patch_id = id;
00207 qimg.qtime = rutz::time::wall_clock_now();
00208
00209 imgq.push_back(qimg);
00210
00211
00212
00213 while (imgq.size() > 60)
00214 {
00215 imgq.pop_front();
00216 }
00217 }
00218
00219
00220
00221
00222
00223 class FilterLabel
00224 {
00225 private:
00226 float itsMaxCenterDist2;
00227 int itsForgetIfMissingFor;
00228 int itsFilterLength;
00229
00230 struct CacheItem
00231 {
00232 CacheItem(const Point2D<int>& center, const std::string& label, int frameNumber) :
00233 itsCenter(center),
00234 itsLabel(label),
00235 itsFrameNumber(frameNumber)
00236 {
00237 }
00238
00239 CacheItem(const CacheItem& that) :
00240 itsCenter(that.itsCenter),
00241 itsLabel(that.itsLabel),
00242 itsFrameNumber(that.itsFrameNumber)
00243 {
00244 }
00245
00246 CacheItem& operator=(const CacheItem& that)
00247 {
00248 itsCenter = that.itsCenter;
00249 itsLabel = that.itsLabel;
00250 itsFrameNumber = that.itsFrameNumber;
00251
00252 return *this;
00253 }
00254
00255 float dist2(const Point2D<int>& center)
00256 {
00257 float dx = center.i - itsCenter.i;
00258 float dy = center.j - itsCenter.j;
00259 return dx * dx + dy * dy;
00260 }
00261
00262 float dist2(const Rectangle& rect)
00263 {
00264 return dist2(rect.center());
00265 }
00266
00267 Point2D<int> itsCenter;
00268 std::string itsLabel;
00269 int itsFrameNumber;
00270 };
00271
00272 typedef std::deque<CacheItem> cacheSingleItem;
00273 std::list<cacheSingleItem> cache;
00274
00275 public:
00276 FilterLabel(float maxCenterDist = 64.0f, int forgetIfMissingFor = 10, int filterLength = 9) :
00277 itsMaxCenterDist2(maxCenterDist * maxCenterDist),
00278 itsForgetIfMissingFor(forgetIfMissingFor),
00279 itsFilterLength(filterLength)
00280 {
00281 }
00282
00283 virtual ~FilterLabel()
00284 {
00285 }
00286
00287 std::string findLabel(cacheSingleItem& singleItem)
00288 {
00289 std::map<std::string,int> count;
00290
00291 std::deque<CacheItem>::iterator itr = singleItem.begin();
00292 const std::deque<CacheItem>::iterator end = singleItem.end();
00293
00294 for (; itr != end; ++itr)
00295 {
00296 ++count[itr->itsLabel];
00297 }
00298
00299 std::map<std::string,int>::iterator itr2 = count.begin();
00300 std::map<std::string,int>::iterator end2 = count.end();
00301
00302 std::string label;
00303 int max = 0;
00304 for (; itr2 != end2; ++itr2)
00305 {
00306 if (max == 0 || max < (*itr2).second)
00307 {
00308 max = (*itr2).second;
00309 label =(*itr2).first;
00310 }
00311 }
00312
00313 return label;
00314 }
00315
00316 std::string push(const Rectangle& rect, const std::string& label, int frameNumber)
00317 {
00318 Point2D<int> center = rect.center();
00319
00320 std::list<cacheSingleItem>::iterator itr = cache.begin();
00321 const std::list<cacheSingleItem>::iterator end = cache.end();
00322 std::list<cacheSingleItem>::iterator best = end;
00323 float bestDist2 = itsMaxCenterDist2 * 2;
00324
00325 for (; itr != end; ++itr)
00326 {
00327 cacheSingleItem& singleItem = *itr;
00328 float d2 = singleItem.back().dist2(center);
00329 if (best == end || d2 < bestDist2)
00330 {
00331 bestDist2 = d2;
00332 best = itr;
00333 }
00334 }
00335
00336 if (best == end || bestDist2 > itsMaxCenterDist2)
00337 {
00338 cacheSingleItem singleItem;
00339 singleItem.push_back(CacheItem(center, label, frameNumber));
00340 cache.push_back(singleItem);
00341 return label;
00342 }
00343
00344 if (best->size() >= static_cast<unsigned int>(itsFilterLength))
00345 best->pop_front();
00346
00347 best->push_back(CacheItem(center, label, frameNumber));
00348
00349 return findLabel(*best);
00350 }
00351
00352 void prune(int currentFrame)
00353 {
00354 std::list<cacheSingleItem>::iterator itr = cache.begin();
00355 const std::list<cacheSingleItem>::iterator end = cache.end();
00356
00357 while (itr != end)
00358 {
00359 cacheSingleItem& singleItem = *itr;
00360 if (singleItem.back().itsFrameNumber < currentFrame - itsForgetIfMissingFor)
00361 {
00362 itr = cache.erase(itr);
00363 }
00364 else
00365 {
00366 ++itr;
00367 }
00368 }
00369 }
00370 };
00371
00372 FilterLabel filterLabel;
00373
00374
00375 Nv2LabelReader::LabeledImage
00376 Nv2LabelReader::getNextLabeledImage(bool ignore_nomatch,
00377 const size_t text_length,
00378 int FrameNumber)
00379 {
00380 LabeledImage result;
00381
00382 if (imgq.size() == 0)
00383 return result;
00384
00385 struct nv2_patch_label label;
00386 const int gotit =
00387 nv2_label_reader_get_current_label(reader, &label);
00388
00389 if (!gotit)
00390 return result;
00391
00392
00393
00394 lastConfidence.atomic_set(label.confidence);
00395
00396 result.ident = label.source;
00397 result.label = label.name;
00398
00399
00400 while (imgq.size() > 0 && imgq.front().patch_id < label.patch_id)
00401
00402
00403 imgq.pop_front();
00404
00405 if (imgq.size() == 0 || imgq.front().patch_id > label.patch_id)
00406 return result;
00407
00408 ASSERT(imgq.size() > 0 && imgq.front().patch_id == label.patch_id);
00409
00410 PendingImage qimg = imgq.front();
00411 imgq.pop_front();
00412
00413 if (ignore_nomatch &&
00414 (strncmp(label.name, "nomatch", 7) == 0 ||
00415 strncmp(label.name, "none", 4) == 0))
00416 return result;
00417
00418 LINFO("label.name = '%s'", label.name);
00419
00420 const rutz::time now = rutz::time::wall_clock_now();
00421
00422 times.push_back(now);
00423
00424 const double fps =
00425 times.size() >= 2
00426 ? (times.size() - 1) / (times.back() - times.front()).sec()
00427 : 0.0;
00428
00429 if (times.size() > 2 && (times.back() - times.front()).sec() > 3.0)
00430 times.pop_front();
00431
00432 if (!qimg.foa.isValid())
00433 return result;
00434
00435
00436 if (FrameNumber >= 0)
00437 {
00438 std::string tempLabel = filterLabel.push(qimg.foa, result.label, FrameNumber);
00439 if (result.label != tempLabel)
00440 {
00441 LINFO("Label '%s' is relaced by '%s'", result.label.c_str(), tempLabel.c_str());
00442 result.label = tempLabel;
00443 strncpy(label.name, tempLabel.c_str(), sizeof(label.name));
00444 label.name[sizeof(label.name)-1]=0;
00445 label.confidence = 0.0;
00446 }
00447 filterLabel.prune(FrameNumber);
00448 }
00449
00450 drawRectSquareCorners(qimg.fullimg, qimg.foa, this->color, 3);
00451
00452 Point2D<int> textpos((qimg.foa.left() + qimg.foa.rightO()) / 2,
00453 (qimg.foa.top() + qimg.foa.bottomO()) / 2);
00454
00455 TextAnchor textloc = ANCHOR_TOP_LEFT;
00456
00457 if (textpos.i < qimg.fullimg.getWidth() / 2)
00458 {
00459 if (textpos.j < qimg.fullimg.getHeight() / 2)
00460 {
00461 textloc = ANCHOR_TOP_LEFT;
00462 textpos.i += 10;
00463 textpos.j += 10;
00464 }
00465 else
00466 {
00467 textloc = ANCHOR_BOTTOM_LEFT;
00468 textpos.i += 10;
00469 textpos.j -= 10;
00470 }
00471 }
00472 else
00473 {
00474 if (textpos.j < qimg.fullimg.getHeight() / 2)
00475 {
00476 textloc = ANCHOR_TOP_RIGHT;
00477 textpos.i -= 10;
00478 textpos.j += 10;
00479 }
00480 else
00481 {
00482 textloc = ANCHOR_BOTTOM_RIGHT;
00483 textpos.i -= 10;
00484 textpos.j -= 10;
00485 }
00486 }
00487
00488 writeText2(qimg.fullimg,
00489 textpos,
00490 sformat("%s (%.2f)", label.name, double(label.confidence) / double(NV2_MAX_LABEL_CONFIDENCE)).c_str(),
00491 this->color, PixRGB<byte>(0,0,0),
00492 SimpleFont::FIXED(14),
00493 0.5,
00494 textloc);
00495
00496 const std::string lines[3] =
00497 {
00498 sformat("[c=%4.2f] %s",
00499 double(label.confidence) / double(NV2_MAX_LABEL_CONFIDENCE),
00500 label.name),
00501 sformat("%s", label.extra_info),
00502 sformat("%s: lag %06.3fs #%06u [%5.2ffps]",
00503 label.source,
00504 (now - qimg.qtime).sec(),
00505 (unsigned int) label.patch_id,
00506 fps)
00507 };
00508
00509 const Image<PixRGB<byte> > textarea =
00510 makeMultilineTextBox(qimg.fullimg.getWidth(), &lines[0], 3,
00511 this->color, PixRGB<byte>(0,0,0),
00512 text_length);
00513
00514 result.img = concatY(qimg.fullimg, textarea);
00515 return result;
00516 }
00517
00518
00519 double Nv2LabelReader::getLastConfidence() const
00520 {
00521 return (double(lastConfidence.atomic_get())
00522 / double(NV2_MAX_LABEL_CONFIDENCE));
00523 }
00524
00525
00526
00527
00528
00529
00530
00531
00532 #endif // NEOVISIONII_NV2LABELREADER_C_DEFINED