00001 /*!@file MBARI/VisualEvent.C classes useful for event tracking */ 00002 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 00004 // by the University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; application number 09/912,225 filed July 23, 2001; see // 00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // 00032 // Primary maintainer for this file: Dirk Walther <walther@caltech.edu> 00033 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/MBARI/VisualEvent.C $ 00034 // $Id: VisualEvent.C 9412 2008-03-10 23:10:15Z farhan $ 00035 // 00036 00037 #include "MBARI/VisualEvent.H" 00038 00039 #include "Image/DrawOps.H" 00040 #include "Image/Image.H" 00041 #include "Image/Pixels.H" 00042 #include "Image/Rectangle.H" 00043 #include "Image/ShapeOps.H" 00044 #include "Image/Transforms.H" 00045 #include "Image/colorDefs.H" 00046 #include "MBARI/Geometry2D.H" 00047 #include "MBARI/mbariFunctions.H" 00048 #include "Util/Assert.H" 00049 #include "Util/StringConversions.H" 00050 00051 #include <algorithm> 00052 #include <istream> 00053 #include <ostream> 00054 00055 // ###################################################################### 00056 // ###### Token 00057 // ###################################################################### 00058 Token::Token() 00059 : bitObject(), 00060 location(), 00061 prediction(), 00062 line(), 00063 angle(0.0F), 00064 frame_nr(0) 00065 {} 00066 00067 // ###################################################################### 00068 Token::Token (float x, float y, uint frame, BitObject bo) 00069 : bitObject(bo), 00070 location(x,y), 00071 prediction(), 00072 line(), 00073 angle(0.0F), 00074 frame_nr(frame) 00075 {} 00076 00077 // ###################################################################### 00078 Token::Token (BitObject bo, uint frame) 00079 : bitObject(bo), 00080 location(bo.getCentroidXY()), 00081 prediction(), 00082 line(), 00083 angle(0.0F), 00084 frame_nr(frame) 00085 {} 00086 00087 // ###################################################################### 00088 Token::Token (std::istream& is) 00089 { 00090 readFromStream(is); 00091 } 00092 00093 // ###################################################################### 00094 void Token::writeToStream(std::ostream& os) const 00095 { 00096 os << frame_nr << ' '; 00097 location.writeToStream(os); 00098 prediction.writeToStream(os); 00099 line.writeToStream(os); 00100 os << angle << '\n'; 00101 bitObject.writeToStream(os); 00102 os << "\n"; 00103 } 00104 00105 // ###################################################################### 00106 void Token::readFromStream(std::istream& is) 00107 { 00108 is >> frame_nr; 00109 location.readFromStream(is); 00110 prediction.readFromStream(is); 00111 line.readFromStream(is); 00112 is >> angle; 00113 bitObject = BitObject(is); 00114 } 00115 00116 // ###################################################################### 00117 void Token::writePosition(std::ostream& os) const 00118 { 00119 location.writeToStream(os); 00120 } 00121 00122 00123 // ###################################################################### 00124 // ####### PropertyVectorSet 00125 // ###################################################################### 00126 PropertyVectorSet::PropertyVectorSet() 00127 {} 00128 00129 // ###################################################################### 00130 PropertyVectorSet::PropertyVectorSet(std::istream& is) 00131 { 00132 readFromStream(is); 00133 } 00134 00135 // ###################################################################### 00136 void PropertyVectorSet::writeToStream(std::ostream& os) const 00137 { 00138 uint s2; 00139 if (itsVectors.empty()) s2 = 0; 00140 else s2 = itsVectors.front().size(); 00141 00142 os << itsVectors.size() << " " << s2 << "\n"; 00143 00144 for (uint i = 0; i < itsVectors.size(); ++i) 00145 { 00146 for (uint j = 0; j < s2; ++j) 00147 os << itsVectors[i][j] << " "; 00148 00149 os << "\n"; 00150 } 00151 } 00152 00153 // ###################################################################### 00154 void PropertyVectorSet::readFromStream(std::istream& is) 00155 { 00156 uint s1, s2; 00157 is >> s1; is >> s2; 00158 itsVectors = std::vector< std::vector<float> > (s1, std::vector<float>(s2)); 00159 00160 for (uint i = 0; i < s1; ++i) 00161 for (uint j = 0; j < s2; ++j) 00162 is >> itsVectors[i][j]; 00163 } 00164 00165 // ###################################################################### 00166 std::vector<float> PropertyVectorSet::getPropertyVectorForEvent(const int num) 00167 { 00168 for (uint i = 0; i < itsVectors.size(); ++i) 00169 if ((int)(itsVectors[i][0]) == num) return itsVectors[i]; 00170 00171 LFATAL("property vector for event number %d not found!", num); 00172 return std::vector<float>(); 00173 } 00174 00175 // ###################################################################### 00176 // ####### VisualEvent 00177 // ###################################################################### 00178 VisualEvent::VisualEvent(Token tk, int maxDist) 00179 : startframe(tk.frame_nr), 00180 endframe(tk.frame_nr), 00181 max_size(tk.bitObject.getArea()), 00182 maxsize_framenr(tk.frame_nr), 00183 closed(false), 00184 itsMaxDist(maxDist), 00185 xTracker(tk.location.x(),0.1F,10.0F), 00186 yTracker(tk.location.y(),0.1F,10.0F) 00187 { 00188 LINFO("tk.location = (%g, %g); area: %i",tk.location.x(),tk.location.y(), 00189 tk.bitObject.getArea()); 00190 tokens.push_back(tk); 00191 ++counter; 00192 myNum = counter; 00193 } 00194 // initialize static variable 00195 uint VisualEvent::counter = 0; 00196 00197 // ###################################################################### 00198 VisualEvent::VisualEvent(std::istream& is) 00199 { 00200 readFromStream(is); 00201 } 00202 00203 // ###################################################################### 00204 void VisualEvent::writeToStream(std::ostream& os) const 00205 { 00206 os << myNum << " " << startframe << " " << endframe << "\n"; 00207 os << max_size << " " << maxsize_framenr << "\n"; 00208 00209 if (closed) os << "1\n"; 00210 else os << "0\n"; 00211 00212 os << itsMaxDist << "\n"; 00213 00214 xTracker.writeToStream(os); 00215 yTracker.writeToStream(os); 00216 00217 os << tokens.size() << "\n"; 00218 00219 for (uint i = 0; i < tokens.size(); ++i) 00220 tokens[i].writeToStream(os); 00221 00222 os << "\n"; 00223 } 00224 00225 // ###################################################################### 00226 void VisualEvent::readFromStream(std::istream& is) 00227 { 00228 is >> myNum; 00229 is >> startframe; 00230 is >> endframe; 00231 is >> max_size; 00232 is >> maxsize_framenr; 00233 00234 int n; is >> n; 00235 closed = (n == 1); 00236 00237 is >> itsMaxDist; 00238 00239 xTracker.readFromStream(is); 00240 yTracker.readFromStream(is); 00241 00242 int t; 00243 is >> t; 00244 tokens.clear(); 00245 for (int i = 0; i < t; ++i) 00246 tokens.push_back(Token(is)); 00247 } 00248 00249 // ###################################################################### 00250 void VisualEvent::writePositions(std::ostream& os) const 00251 { 00252 for (uint i = 0; i < tokens.size(); ++i) 00253 tokens[i].writePosition(os); 00254 00255 os << "\n"; 00256 } 00257 00258 // ###################################################################### 00259 Point2D<int> VisualEvent::predictedLocation() const 00260 { 00261 int x = int(xTracker.getEstimate() + 0.5F); 00262 int y = int(yTracker.getEstimate() + 0.5F); 00263 return Point2D<int>(x,y); 00264 } 00265 00266 // ###################################################################### 00267 bool VisualEvent::isTokenOk(const Token& tk) const 00268 { 00269 return ((tk.frame_nr - endframe) == 1) && !closed; 00270 } 00271 00272 // ###################################################################### 00273 float VisualEvent::getCost(const Token& tk) const 00274 { 00275 if (!isTokenOk(tk)) return -1.0F; 00276 00277 float cost = (xTracker.getCost(tk.location.x()) + 00278 yTracker.getCost(tk.location.y())); 00279 00280 LINFO("Event no. %i; obj location: %g, %g; predicted location: %g, %g; cost: %g", 00281 myNum, tk.location.x(), tk.location.y(), xTracker.getEstimate(), 00282 yTracker.getEstimate(), cost); 00283 return cost; 00284 } 00285 00286 // ###################################################################### 00287 void VisualEvent::assign(const Token& tk, const Vector2D& foe) 00288 { 00289 ASSERT(isTokenOk(tk)); 00290 00291 tokens.push_back(tk); 00292 00293 tokens.back().prediction = Vector2D(xTracker.getEstimate(), 00294 yTracker.getEstimate()); 00295 tokens.back().location = Vector2D(xTracker.update(tk.location.x()), 00296 yTracker.update(tk.location.y())); 00297 00298 // update the straight line 00299 //Vector2D dir(xTracker.getSpeed(), yTracker.getSpeed()); 00300 Vector2D dir = tokens.front().location - tokens.back().location; 00301 tokens.back().line.reset(tokens.back().location, dir); 00302 00303 if (foe.isValid()) 00304 tokens.back().angle = dir.angle(tokens.back().location - foe); 00305 else 00306 tokens.back().angle = 0.0F; 00307 00308 if (tk.bitObject.getArea() > max_size) 00309 { 00310 max_size = tk.bitObject.getArea(); 00311 maxsize_framenr = tk.frame_nr; 00312 } 00313 endframe = tk.frame_nr; 00314 } 00315 00316 // ###################################################################### 00317 bool VisualEvent::doesIntersect(const BitObject& obj, int frameNum) const 00318 { 00319 if (!isFrameOk(frameNum)) return false; 00320 else return getToken(frameNum).bitObject.doesIntersect(obj); 00321 } 00322 00323 00324 // ###################################################################### 00325 std::vector<float> VisualEvent::getPropertyVector() 00326 { 00327 std::vector<float> vec; 00328 Token tk = getMaxSizeToken(); 00329 BitObject bo = tk.bitObject; 00330 00331 // 0 - event number 00332 vec.push_back(getEventNum()); 00333 00334 // 1 - interesting value 00335 vec.push_back(-1); 00336 00337 // not valid? 00338 if (!bo.isValid()) 00339 { 00340 // 2 - set area to -1 00341 vec.push_back(-1); 00342 00343 // 3-12 set everyone to 0 00344 for (uint i = 3; i <= 12; ++i) 00345 vec.push_back(0); 00346 00347 // done 00348 return vec; 00349 } 00350 00351 // we're valid 00352 00353 // 2 - area 00354 vec.push_back(bo.getArea()); 00355 00356 // 3, 4, 5 - uxx, uyy, uxy 00357 float uxx, uyy, uxy; 00358 bo.getSecondMoments(uxx, uyy, uxy); 00359 vec.push_back(uxx); 00360 vec.push_back(uyy); 00361 vec.push_back(uxy); 00362 00363 // 6 - major axis 00364 vec.push_back(bo.getMajorAxis()); 00365 00366 // 7 - minor axis 00367 vec.push_back(bo.getMinorAxis()); 00368 00369 // 8 - elongation 00370 vec.push_back(bo.getElongation()); 00371 00372 // 9 - orientation angle 00373 vec.push_back(bo.getOriAngle()); 00374 00375 // 10, 11, 12 - max, min, avg intensity 00376 float maxIntens,minIntens,avgIntens; 00377 bo.getMaxMinAvgIntensity(maxIntens, minIntens, avgIntens); 00378 vec.push_back(maxIntens); 00379 vec.push_back(minIntens); 00380 vec.push_back(avgIntens); 00381 00382 // 13 - angle with respect to expansion 00383 vec.push_back(tk.angle); 00384 00385 // done -> return the vector 00386 return vec; 00387 } 00388 00389 // ###################################################################### 00390 Dims VisualEvent::getMaxObjectDims() const 00391 { 00392 int w = -1, h = -1; 00393 std::vector<Token>::const_iterator t; 00394 for (t = tokens.begin(); t != tokens.end(); ++t) 00395 { 00396 Dims d = t->bitObject.getObjectDims(); 00397 w = std::max(w, d.w()); 00398 h = std::max(h, d.h()); 00399 } 00400 return Dims(w,h); 00401 } 00402 00403 // ###################################################################### 00404 // ###### VisualEventSet 00405 // ###################################################################### 00406 VisualEventSet::VisualEventSet(const int maxDist, 00407 const uint minFrameNum, 00408 const int minSize, 00409 const std::string& fileName) 00410 : itsMaxDist(maxDist), 00411 itsMinFrameNum(minFrameNum), 00412 itsMinSize(minSize), 00413 startframe(-1), 00414 endframe(-1), 00415 itsFileName(fileName) 00416 { 00417 float maxAreaDiff = maxDist * maxDist / 4.0F; 00418 itsMaxCost = maxDist * maxDist + maxAreaDiff * maxAreaDiff; 00419 } 00420 00421 // ###################################################################### 00422 VisualEventSet::VisualEventSet(std::istream& is) 00423 { 00424 readFromStream(is); 00425 } 00426 00427 // ###################################################################### 00428 void VisualEventSet::writeToStream(std::ostream& os) const 00429 { 00430 os << itsFileName << "\n"; 00431 os << itsMaxDist << " " 00432 << itsMaxCost << " " 00433 << itsMinFrameNum << " " 00434 << itsMinSize << "\n"; 00435 os << startframe << ' ' << endframe << '\n'; 00436 00437 os << itsEvents.size() << "\n"; 00438 std::list<VisualEvent>::const_iterator currEvent; 00439 for (currEvent = itsEvents.begin(); currEvent != itsEvents.end(); ++currEvent) 00440 currEvent->writeToStream(os); 00441 00442 os << itsFOE.size() << '\n'; 00443 for (uint i = 0; i < itsFOE.size(); ++i) 00444 itsFOE[i].writeToStream(os); 00445 00446 os << "\n"; 00447 } 00448 00449 // ###################################################################### 00450 void VisualEventSet::readFromStream(std::istream& is) 00451 { 00452 is >> itsFileName; LINFO("filename: %s",itsFileName.c_str()); 00453 is >> itsMaxDist; 00454 is >> itsMaxCost; 00455 is >> itsMinFrameNum; 00456 is >> itsMinSize; 00457 is >> startframe; 00458 is >> endframe; 00459 00460 int n; is >> n; 00461 itsEvents.clear(); 00462 for (int i = 0; i < n; ++i) 00463 itsEvents.push_back(VisualEvent(is)); 00464 00465 is >> n; 00466 itsFOE.clear(); 00467 for (int i = 0; i < n; ++i) 00468 itsFOE.push_back(Vector2D(is)); 00469 } 00470 00471 // ###################################################################### 00472 void VisualEventSet::writePositions(std::ostream& os) const 00473 { 00474 std::list<VisualEvent>::const_iterator currEvent; 00475 for (currEvent = itsEvents.begin(); currEvent != itsEvents.end(); ++currEvent) 00476 currEvent->writePositions(os); 00477 } 00478 00479 // ###################################################################### 00480 void VisualEventSet::updateEvents(const Image<byte>& binMap, 00481 const Vector2D& curFOE, 00482 int frameNum) 00483 { 00484 if (startframe == -1) {startframe = frameNum; endframe = frameNum;} 00485 ASSERT((frameNum == endframe) || (frameNum == endframe+1)); 00486 if (frameNum > endframe) endframe = frameNum; 00487 00488 itsFOE.push_back(curFOE); 00489 00490 std::list<VisualEvent>::iterator currEvent; 00491 for (currEvent = itsEvents.begin(); currEvent != itsEvents.end(); ++currEvent) 00492 { 00493 if (currEvent->isClosed()) continue; 00494 00495 // get the predicted location 00496 Point2D<int> pred = currEvent->predictedLocation(); 00497 00498 // is the prediction too far outside the image? 00499 //int gone = itsMaxDist / 2; 00500 int gone = 0; 00501 if ((pred.i < -gone) || (pred.i >= (binMap.getWidth() + gone)) || 00502 (pred.j < -gone) || (pred.j >= (binMap.getHeight() + gone))) 00503 { 00504 currEvent->close(); 00505 LINFO("Event %i out of bounds - closed",currEvent->getEventNum()); 00506 continue; 00507 } 00508 00509 // get the region used for searching for a match 00510 Rectangle region = 00511 Rectangle::tlbrI(pred.j - itsMaxDist, pred.i - itsMaxDist, 00512 pred.j + itsMaxDist, pred.i + itsMaxDist); 00513 region = region.getOverlap(binMap.getBounds()); 00514 00515 // extract all the BitObjects from the region 00516 std::list<BitObject> objs = extractBitObjects(binMap, region, itsMinSize); 00517 //LINFO("pred. location: %s; region: %s; Number of extracted objects: %i", 00518 // toStr(pred).c_str(),toStr(region).c_str(),objs.size()); 00519 00520 // now look which one fits best 00521 float lCost = -1.0F; 00522 std::list<BitObject>::iterator cObj, lObj = objs.end(); 00523 for (cObj = objs.begin(); cObj != objs.end(); ++cObj) 00524 { 00525 if (doesIntersect(*cObj, frameNum)) continue; 00526 00527 float cost = currEvent->getCost(Token(*cObj,frameNum)); 00528 00529 //LINFO("Event no. %i; cost: %g; lowest cost: %g",currEvent->getEventNum(), 00530 // cost, lCost); 00531 if (cost < 0.0F) continue; 00532 if ((lCost == -1.0F) || (cost < lCost)) 00533 { 00534 lCost = cost; 00535 lObj = cObj; 00536 //LINFO("best cost: %g",lCost); 00537 } 00538 } 00539 00540 // cost too high no fitting object found? -> close event 00541 if ((lCost > itsMaxCost) || (lCost == -1.0)) 00542 { 00543 currEvent->close(); 00544 LINFO("Event %i - no token found, closing event", 00545 currEvent->getEventNum()); 00546 } 00547 else 00548 { 00549 // associate the best fitting guy 00550 Token tk(*lObj, frameNum); 00551 currEvent->assign(tk, curFOE); 00552 00553 LINFO("Event %i - token found at %g, %g",currEvent->getEventNum(), 00554 currEvent->getToken(frameNum).location.x(), 00555 currEvent->getToken(frameNum).location.y()); 00556 } 00557 } 00558 } 00559 00560 // ###################################################################### 00561 void VisualEventSet::initiateEvents(std::list<BitObject>& bos, int frameNum) 00562 { 00563 if (startframe == -1) {startframe = frameNum; endframe = frameNum;} 00564 ASSERT((frameNum == endframe) || (frameNum == endframe+1)); 00565 if (frameNum > endframe) endframe = frameNum; 00566 00567 std::list<BitObject>::iterator currObj; 00568 00569 // loop over the BitObjects 00570 currObj = bos.begin(); 00571 while(currObj != bos.end()) 00572 { 00573 // is there an intersection with an event? 00574 if (doesIntersect(*currObj, frameNum)) 00575 { 00576 //LINFO("Object at %g, %g intersects with event %i, erasing the object", 00577 // currObj->getCentroidX(), currObj->getCentroidY(), 00578 // currEvent->getEventNum()); 00579 currObj = bos.erase(currObj); 00580 } 00581 else 00582 { 00583 //LINFO("Object at %g, %g does not intersect with event %i", 00584 //currObj->getCentroidX(), currObj->getCentroidY(), 00585 //currEvent->getEventNum()); 00586 ++currObj; 00587 } 00588 } 00589 00590 // now go through all the remaining BitObjects and create new events for them 00591 for (currObj = bos.begin(); currObj != bos.end(); ++currObj) 00592 { 00593 itsEvents.push_back(VisualEvent(Token(*currObj, frameNum), itsMaxDist)); 00594 LINFO("assigning object of area: %i to new event %i",currObj->getArea(), 00595 itsEvents.back().getEventNum()); 00596 } 00597 } 00598 00599 // ###################################################################### 00600 bool VisualEventSet::doesIntersect(const BitObject& obj, int frameNum) const 00601 { 00602 std::list<VisualEvent>::const_iterator cEv; 00603 for (cEv = itsEvents.begin(); cEv != itsEvents.end(); ++cEv) 00604 if (cEv->doesIntersect(obj,frameNum)) return true; 00605 00606 return false; 00607 } 00608 00609 /* 00610 // ###################################################################### 00611 Vector2D VisualEventSet::updateFOE() 00612 { 00613 const uint minTrackFrames = 3; 00614 std::vector<StraightLine2D> lines; 00615 std::vector<std::list<VisualEvent>::iterator> events = 00616 getEventsForFrame(endframe); 00617 00618 // go through all the events and extract the straight lines 00619 for (uint i = 0; i < events.size(); ++i) 00620 { 00621 // must have at least minTrackFrames points until here 00622 if ((endframe - events[i]->getStartFrame() + 1) >= minTrackFrames) 00623 { 00624 StraightLine2D l = events[i]->getToken(endframe).line; 00625 if (l.isValid()) lines.push_back(l); 00626 } 00627 } 00628 00629 // get all the intersection points of the straight lines 00630 std::vector<float> xCoords, yCoords; 00631 for (uint i = 0; i < lines.size(); ++i) 00632 for (uint j = 0; j < lines.size(); ++j) 00633 { 00634 if (i == j) continue; 00635 float n,m; 00636 Vector2D inter = lines[i].intersect(lines[j],n,m); 00637 if (inter.isValid()) 00638 { 00639 xCoords.push_back(inter.x()); 00640 yCoords.push_back(inter.y()); 00641 } 00642 } 00643 00644 // update the FOE 00645 Vector2D currFOE; 00646 00647 if (xCoords.empty()) 00648 { 00649 //itsFOE.push_back(Vector2D()); 00650 } 00651 else 00652 { 00653 00654 // this is all for taking the median 00655 // sort xCoords and yCoords 00656 //std::sort(xCoords.begin(),xCoords.end()); 00657 //std::sort(yCoords.begin(),yCoords.end()); 00658 00659 // take the median to be the FOE 00660 //currFOE = Vector2D(xCoords[int(xCoords.size()/2)], 00661 // yCoords[int(yCoords.size()/2)]); 00662 00663 // compute the mean of x and y 00664 float sumX = 0.0F, sumY = 0.0F; 00665 for (uint i = 0; i < xCoords.size(); ++i) 00666 { sumX += xCoords[i]; sumY += yCoords[i]; } 00667 currFOE = Vector2D(sumX/float(xCoords.size()), 00668 sumY/float(yCoords.size())); 00669 00670 //if (sumFOE.isValid()) sumFOE += currFOE; 00671 //else sumFOE = currFOE; 00672 //++numFOE; 00673 } 00674 00675 if (xFOE.isInitialized()) 00676 { 00677 // update KalmanFilters with currFOE 00678 if (currFOE.isValid()) 00679 itsFOE.push_back(Vector2D(xFOE.update(currFOE.x()), 00680 yFOE.update(currFOE.y()))); 00681 else 00682 itsFOE.push_back(Vector2D(xFOE.update(), yFOE.update())); 00683 } 00684 else 00685 { 00686 if (currFOE.isValid()) 00687 { 00688 // initialize KalmanFilters 00689 xFOE.init(currFOE.x(), 0.0001F, 10000.0F); 00690 yFOE.init(currFOE.y(), 0.0001F, 10000.0F); 00691 } 00692 00693 // store currFOE 00694 itsFOE.push_back(currFOE); 00695 } 00696 00697 //if (sumFOE.isValid()) itsFOE.push_back(sumFOE/numFOE); 00698 //else itsFOE.push_back(Vector2D()); 00699 00700 return itsFOE.back(); 00701 } 00702 */ 00703 00704 // ###################################################################### 00705 Vector2D VisualEventSet::getFOE(int frameNum) const 00706 { 00707 00708 int idx = frameNum - startframe; 00709 //LINFO("frameNum = %i, startframe = %i, idx = %i, itsFOE.size() = %i", 00710 // frameNum, startframe, idx, itsFOE.size()); 00711 ASSERT((idx >= 0)&& (idx < (int)itsFOE.size())); 00712 00713 return itsFOE[idx]; 00714 } 00715 00716 // ###################################################################### 00717 uint VisualEventSet::numEvents() const 00718 { 00719 return itsEvents.size(); 00720 } 00721 00722 // ###################################################################### 00723 void VisualEventSet::reset() 00724 { 00725 itsEvents.clear(); 00726 } 00727 00728 // ###################################################################### 00729 void VisualEventSet::cleanUp(uint currFrame, uint maxFrameSkip) 00730 { 00731 std::list<VisualEvent>::iterator currEvent = itsEvents.begin(); 00732 while (currEvent != itsEvents.end()) 00733 { 00734 if (currEvent->isClosed() && 00735 (currEvent->getNumberOfFrames() < itsMinFrameNum)) 00736 { 00737 LINFO("Erasing event %i, because it has only %i frames.", 00738 currEvent->getEventNum(), currEvent->getNumberOfFrames()); 00739 currEvent = itsEvents.erase(currEvent); 00740 } 00741 else ++currEvent; 00742 00743 } // end while loop over events 00744 } 00745 00746 // ###################################################################### 00747 void VisualEventSet::closeAll() 00748 { 00749 std::list<VisualEvent>::iterator cEvent; 00750 for (cEvent = itsEvents.begin(); cEvent != itsEvents.end(); ++cEvent) 00751 cEvent->close(); 00752 } 00753 00754 // ###################################################################### 00755 std::vector<Token> VisualEventSet::getTokens(uint frameNum) 00756 { 00757 std::vector<Token> tokens; 00758 std::list<VisualEvent>::iterator currEvent; 00759 for (currEvent = itsEvents.begin(); currEvent != itsEvents.end(); ++currEvent) 00760 { 00761 // does this guy participate in frameNum? 00762 if (!currEvent->isFrameOk(frameNum)) continue; 00763 00764 tokens.push_back(currEvent->getToken(frameNum)); 00765 } // end loop over events 00766 00767 return tokens; 00768 } 00769 00770 // ###################################################################### 00771 void VisualEventSet::drawTokens(Image< PixRGB<byte> >& img, 00772 uint frameNum, 00773 PropertyVectorSet& pvs, 00774 int circleRadius, 00775 BitObjectDrawMode mode, 00776 float opacity, 00777 PixRGB<byte> colorInteresting, 00778 PixRGB<byte> colorCandidate, 00779 PixRGB<byte> colorPred, 00780 PixRGB<byte> colorFOE, 00781 bool showEventLabels) 00782 { 00783 // dimensions of the number text and location to put it at 00784 const int numW = 10; 00785 const int numH = 21; 00786 00787 std::list<VisualEvent>::iterator currEvent; 00788 for (currEvent = itsEvents.begin(); currEvent != itsEvents.end(); ++currEvent) 00789 { 00790 // does this guy participate in frameNum? 00791 if (!currEvent->isFrameOk(frameNum)) continue; 00792 00793 PixRGB<byte> circleColor; 00794 Token tk = currEvent->getToken(frameNum); 00795 Point2D<int> center = tk.location.getPoint2D(); 00796 00797 if (isEventInteresting(pvs.getPropertyVectorForEvent 00798 (currEvent->getEventNum()))) 00799 circleColor = colorInteresting; 00800 else 00801 circleColor = colorCandidate; 00802 00803 // if requested, prepare the event labels 00804 Image< PixRGB<byte> > textImg; 00805 if (showEventLabels) 00806 { 00807 // write the text and create the overlay image 00808 std::string numText = toStr(currEvent->getEventNum()); 00809 textImg.resize(numW * numText.length(), numH, NO_INIT); 00810 textImg.clear(COL_WHITE); 00811 writeText(textImg, Point2D<int>(0,0), numText.c_str()); 00812 } 00813 00814 // draw the event object itself if requested 00815 if (circleColor != COL_TRANSPARENT) 00816 { 00817 // the box so that the text knows where to go 00818 Rectangle bbox; 00819 00820 // draw rectangle or circle and determine the pos of the number label 00821 if (tk.bitObject.isValid()) 00822 { 00823 tk.bitObject.draw(mode, img, circleColor, opacity); 00824 bbox = tk.bitObject.getBoundingBox(BitObject::IMAGE); 00825 //drawRect(img, bbox,circleColor); 00826 } 00827 else 00828 { 00829 LINFO("BitObject is invalid: area: %i;",tk.bitObject.getArea()); 00830 LFATAL("bounding box: %s",toStr(tk.bitObject.getBoundingBox()).c_str()); 00831 drawCircle(img, center, circleRadius, circleColor); 00832 bbox = Rectangle::tlbrI(center.j - circleRadius, center.i - circleRadius, 00833 center.j + circleRadius, center.i + circleRadius); 00834 bbox = bbox.getOverlap(img.getBounds()); 00835 } 00836 00837 // if requested, write the event labels into the image 00838 if (showEventLabels) 00839 { 00840 Point2D<int> numLoc = getLabelPosition(img.getDims(),bbox,textImg.getDims()); 00841 Image<PixRGB <byte> > textImg2 = replaceVals(textImg,COL_BLACK,circleColor); 00842 textImg2 = replaceVals(textImg2,COL_WHITE,COL_TRANSPARENT); 00843 pasteImage(img,textImg2,COL_TRANSPARENT, numLoc, opacity); 00844 00845 } // end if (showEventLabels) 00846 00847 } // end if we're not transparent 00848 00849 // now do the same for the predicted value 00850 if ((colorPred != COL_TRANSPARENT) && tk.prediction.isValid()) 00851 { 00852 Point2D<int> ctr = tk.prediction.getPoint2D(); 00853 drawCircle(img, ctr,10,colorPred); 00854 Rectangle ebox = 00855 Rectangle::tlbrI(ctr.j - 10, ctr.i - 10, ctr.j + 10, ctr.i + 10); 00856 ebox = ebox.getOverlap(img.getBounds()); 00857 if (showEventLabels) 00858 { 00859 Point2D<int> numLoc = getLabelPosition(img.getDims(), ebox, textImg.getDims()); 00860 Image< PixRGB<byte> > textImg2 = replaceVals(textImg,COL_BLACK,colorPred); 00861 textImg2 = replaceVals(textImg2,COL_WHITE,COL_TRANSPARENT); 00862 pasteImage(img,textImg2,COL_TRANSPARENT, numLoc, opacity); 00863 } 00864 } 00865 00866 } // end loop over events 00867 00868 // draw the focus of expansion 00869 if ((colorFOE != COL_TRANSPARENT) && getFOE(frameNum).isValid()) 00870 { 00871 Point2D<int> ctr = getFOE(frameNum).getPoint2D(); 00872 drawDisk(img, ctr,2,colorFOE); 00873 } 00874 00875 } 00876 00877 // ###################################################################### 00878 Point2D<int> VisualEventSet::getLabelPosition(Dims imgDims, 00879 Rectangle bbox, 00880 Dims textDims) const 00881 { 00882 // distance of the text label from the bbox 00883 const int dist = 2; 00884 00885 Point2D<int> loc(bbox.left(),(bbox.top() - dist - textDims.h())); 00886 00887 // not enough space to the right? -> shift as apropriate 00888 if ((loc.i + textDims.w()) > imgDims.w()) 00889 loc.i = imgDims.w() - textDims.w() - 1; 00890 00891 // not enough space on the top? -> move to the bottom 00892 if (loc.j < 0) 00893 loc.j = bbox.bottomI() + dist; 00894 00895 return loc; 00896 } 00897 00898 // ###################################################################### 00899 PropertyVectorSet VisualEventSet::getPropertyVectorSet() 00900 { 00901 PropertyVectorSet pvs; 00902 00903 std::list<VisualEvent>::iterator currEvent; 00904 for (currEvent = itsEvents.begin(); currEvent != itsEvents.end(); 00905 ++currEvent) 00906 pvs.itsVectors.push_back(currEvent->getPropertyVector()); 00907 00908 return pvs; 00909 } 00910 00911 00912 // ###################################################################### 00913 int VisualEventSet::getAllClosedFrameNum(uint currFrame) 00914 { 00915 std::list<VisualEvent>::iterator currEvent; 00916 for (int frame = (int)currFrame; frame >= -1; --frame) 00917 { 00918 bool done = true; 00919 00920 for (currEvent = itsEvents.begin(); currEvent != itsEvents.end(); 00921 ++currEvent) 00922 { 00923 done &= ((frame < (int)currEvent->getStartFrame()) 00924 || currEvent->isClosed()); 00925 if (!done) break; 00926 } 00927 00928 if (done) return frame; 00929 } 00930 return -1; 00931 } 00932 00933 // ###################################################################### 00934 bool VisualEventSet::isEventInteresting(std::vector<float> propVec) const 00935 { 00936 const float interestThresh = 5; 00937 const float uxyThresh = 0.4F; 00938 00939 // did we get set from outside? -> threshold with interstingness 00940 if (propVec[1] >= 0.0F) 00941 return (propVec[1] >= interestThresh); 00942 // otherwise threshold uxy and set interestingness value 00943 else 00944 return (fabs(propVec[5]) >= uxyThresh); 00945 } 00946 00947 // ###################################################################### 00948 bool VisualEventSet::doesEventExist(uint eventNum) const 00949 { 00950 std::list<VisualEvent>::const_iterator evt; 00951 for (evt = itsEvents.begin(); evt != itsEvents.end(); ++evt) 00952 if (evt->getEventNum() == eventNum) return true; 00953 00954 return false; 00955 } 00956 00957 // ###################################################################### 00958 VisualEvent VisualEventSet::getEventByNumber(uint eventNum) const 00959 { 00960 std::list<VisualEvent>::const_iterator evt; 00961 for (evt = itsEvents.begin(); evt != itsEvents.end(); ++evt) 00962 if (evt->getEventNum() == eventNum) return *evt; 00963 00964 LFATAL("Event with number %i does not exist.",eventNum); 00965 00966 return *evt; 00967 } 00968 00969 // ###################################################################### 00970 std::vector<std::list<VisualEvent>::iterator> 00971 VisualEventSet::getEventsForFrame(uint framenum) 00972 { 00973 std::vector<std::list<VisualEvent>::iterator> result; 00974 std::list<VisualEvent>::iterator event; 00975 for (event = itsEvents.begin(); event != itsEvents.end(); ++event) 00976 if (event->isFrameOk(framenum)) result.push_back(event); 00977 00978 return result; 00979 } 00980 00981 // ###################################################################### 00982 /* So things look consistent in everyone's emacs... */ 00983 /* Local Variables: */ 00984 /* indent-tabs-mode: nil */ 00985 /* End: */