00001 /*!@file Psycho/EyeData.H Struct for eye-tracker data with a wrapped ParamMap */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: John Shen <shenjohn@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Psycho/EyeData.H $ 00035 // 00036 00037 #ifndef PSYCHO_EYEDATA_H_DEFINED 00038 #define PSYCHO_EYEDATA_H_DEFINED 00039 00040 #include "Image/Point2D.H" 00041 #include "Psycho/SaccadeState.H" 00042 #include "Util/Types.H" 00043 #include "Util/StringUtil.H" 00044 #include "Util/log.H" 00045 #include "rutz/compat_cmath.h" // for isnan() 00046 #include "rutz/shared_ptr.h" 00047 #include "Component/ParamMap.H" 00048 #include <cmath> 00049 00050 //! Simple struct for eye-tracker data 00051 /*! Different eye trackers may or may not fill all data 00052 members. Members that should always be filled are x and y. Note that 00053 this class is costly in terms of storage, so typically one would 00054 want to use it only to pass data samples around, but not to store a 00055 long series of samples in memory. For example, Psycho/EyeTrace.H 00056 uses a more compact format to internally store eye data, and uses 00057 EyeData objects to deliver that data to whoever wants it. */ 00058 class EyeData 00059 { 00060 public: 00061 //! Fully initialized constructor, no extra data 00062 inline EyeData(const float x_, const float y_, const float pdiam_, 00063 const SaccadeState sstate_, const bool bstate_); 00064 00065 //! Fully initialized constructor, extra data in ParamMap 00066 inline EyeData(const float x_, const float y_, const float pdiam_, 00067 const SaccadeState sstate_, const bool bstate_, 00068 rutz::shared_ptr<ParamMap> dat); 00069 00070 // initialized constructor, extra data 00071 inline EyeData(const float x_, const float y_, const float pdiam_, 00072 const SaccadeState sstate_, const bool bstate_, 00073 const float dx_, const float dy_, const float ampl_, 00074 const float dur_, const float time_, 00075 const unsigned int num_); 00076 00077 // inline EyeData(const float x_, const float y_, const float pdiam_, 00078 // const SaccadeState sstate_, const bool bstate_, 00079 // rutz::shared_ptr<ParamMap> dat); 00080 00081 // inline EyeData(rutz::shared_ptr<ParamMap> dat); 00082 // inline EyeData(std::map<std::string, float> dat); 00083 00084 inline ~EyeData(); 00085 00086 //@} 00087 // ###################################################################### 00088 /*! @name Helper functions */ 00089 //@{ 00090 00091 //! Are we in unknown state (uninitialized, loss of tracking, etc) 00092 inline bool isInUnknown() const; 00093 00094 //! Are we in fixation? 00095 inline bool isInFixation() const; 00096 00097 //! Are we in saccade? 00098 inline bool isInSaccade() const; 00099 00100 //! Are we in smooth pursuit? 00101 inline bool isInSmoothPursuit() const; 00102 00103 //! Are we in blink? 00104 inline bool isInBlink() const; 00105 00106 //! Are we in a series of combined saccades 00107 inline bool isInCombinedSaccade() const; 00108 00109 //! Get the saccade state directly 00110 inline SaccadeState saccadeState() const; 00111 00112 //! Do we have valid pupil diameter data? 00113 inline bool hasPupilDiam() const; 00114 00115 //! Do we have valid extra metadata? 00116 /*! Only a very few samples in an eye trace have this extra data, 00117 possibly none. The data may be present at the onset of each 00118 event and contains information about each event. */ 00119 inline bool hasMetaData(const std::string field) const; 00120 00121 inline bool hasSpecialMetaData(const std::string field) const; 00122 00123 //! Do we have valid extra saccade target data? 00124 /*! Only a very few samples in an eye trace have this extra data, 00125 possibly none. The data may be present at the onset of each 00126 saccade and contains information about the target of the saccade. */ 00127 //!NB: This is rewritten now for any sample that has extra data, 00128 // not just saccades. 00129 inline bool hasSaccadeTargetData() const; 00130 00131 //! Do we have valid (x,y) coordinates? 00132 inline bool isValid() const; 00133 00134 //! Are our coordinates within some dims? 00135 inline bool isWithin(const Dims& dims) const; 00136 00137 //! Is this field name a special field? 00138 inline bool isSpecialField(std::string field) const; 00139 00140 //@} 00141 00142 // ###################################################################### 00143 /*! @name Access functions */ 00144 //@{ 00145 00146 //! Get eye position, not rounded 00147 inline void getPosition(float& xx, float& yy) const; 00148 00149 //! Get eye position, rounded to nearest int coordinates 00150 inline Point2D<int> position() const; 00151 00152 //! Get pupil diameter 00153 /*! Note that this may be a NaN or other trash if there is no 00154 available data about pupil diameter, check with hasPupilDiameter() 00155 before if you want to ensure that you get a valid number. */ 00156 inline float pupilDiameter() const; 00157 00158 //! Get all metadata 00159 inline rutz::shared_ptr<ParamMap> getMetaData() const; 00160 00161 //! Get list of metadata fields 00162 inline std::vector<std::string> getMetaDataList() const; 00163 00164 //! Get one field of metadata 00165 inline double getMetaDataField(std::string field) const; 00166 00167 //! Get saccade target, not rounded 00168 /*! Note that this will contain trash if there is no saccade target 00169 data. Check with hasMetaData() first to avoid this. */ 00170 inline void getSaccadeTarget(float& targx, float& targy) const; 00171 00172 //! Get rounded saccade target 00173 /*! Note that this will contain trash if there is no saccade target 00174 data. Check with hasMetaData() first to avoid this. */ 00175 inline Point2D<int> saccadeTarget() const; 00176 00177 //! Get saccade amplitude 00178 /*! Note that this will contain trash if there is no saccade target 00179 data. Check with hasMetaData() first to avoid this. */ 00180 inline float saccadeAmplitude() const; 00181 00182 //! Get saccade duration 00183 /*! Note that this will contain trash if there is o saccade target 00184 data. Check with hasMetaData() first to avoid this. */ 00185 inline float saccadeDuration() const; 00186 00187 //! Get the time at the endpoint of the saccade 00188 /*! Note that this will contain trash if there is no saccade target 00189 data. Check with hasMetaData() first to avoid this. */ 00190 inline float saccadeTime() const; 00191 00192 //! Get saccade number 00193 /*! Note that this will contain trash if there is no saccade target 00194 data. Check with hasMetaData() first to avoid this. 00195 Returned saccade number is zero-based. */ 00196 inline unsigned int saccadeNumber() const; 00197 00198 //! Output the data for debug purposes only 00199 /*! Commented out for build purposes. Include fstream to bring it back. */ 00200 //inline void outputData(std::ostream &out) const; 00201 private: 00202 //Optional data in tree of ParamMaps 00203 const float x, y; //!< eye position coordinates, in pixels 00204 const float pdiam; //!< pupil diameter, in pixels 00205 const SaccadeState sstate; //!< See definitions in SaccadeState.H 00206 const bool bstate; //!< Blink state 00207 00208 const rutz::shared_ptr<ParamMap> itsExtraData; 00209 inline std::string special(std::string field) const; 00210 inline std::string unspecial(std::string field) const; 00211 00212 }; 00213 00214 // ###################################################################### 00215 // ###################################################################### 00216 // ########## inlined functions ############ 00217 // ###################################################################### 00218 // ###################################################################### 00219 inline EyeData::EyeData(const float x_, const float y_, const float pdiam_, 00220 const SaccadeState sstate_, const bool bstate_) : 00221 x(x_), y(y_), pdiam(pdiam_), sstate(sstate_), bstate(bstate_), 00222 itsExtraData(new ParamMap) 00223 { } 00224 00225 inline EyeData::EyeData(const float x_, const float y_, const float pdiam_, 00226 const SaccadeState sstate_, const bool bstate_, 00227 rutz::shared_ptr<ParamMap> dat) : 00228 x(x_), y(y_), pdiam(pdiam_), sstate(sstate_), bstate(bstate_), 00229 itsExtraData(dat) 00230 {} 00231 inline EyeData::EyeData(const float x_, const float y_, const float pdiam_, 00232 const SaccadeState sstate_, const bool bstate_, 00233 const float dx_, const float dy_, const float ampl_, 00234 const float dur_, const float time_, 00235 const unsigned int num_) : 00236 x(x_), y(y_), pdiam(pdiam_), sstate(sstate_), bstate(bstate_), 00237 itsExtraData(new ParamMap) 00238 { 00239 itsExtraData->putDoubleParam("targetx",dx_); 00240 itsExtraData->putDoubleParam("targety",dy_); 00241 itsExtraData->putDoubleParam("ampl",ampl_); 00242 itsExtraData->putDoubleParam("dur",dur_); 00243 itsExtraData->putDoubleParam("time",time_); 00244 itsExtraData->putDoubleParam("num",num_); 00245 } 00246 00247 inline EyeData::~EyeData() 00248 { } 00249 00250 inline bool EyeData::isInUnknown() const 00251 { return (sstate == SACSTATE_UNK); } 00252 00253 inline bool EyeData::isInFixation() const{ return (sstate == SACSTATE_FIX); } 00254 00255 inline bool EyeData::isInSaccade() const 00256 { return (sstate == SACSTATE_SAC) || (sstate == SACSTATE_COM); } 00257 00258 inline bool EyeData::isInSmoothPursuit() const 00259 { return (sstate == SACSTATE_SMO); } 00260 00261 inline bool EyeData::isInBlink() const 00262 { return bstate; } 00263 00264 inline bool EyeData::isInCombinedSaccade() const 00265 { return (sstate == SACSTATE_COM); } 00266 00267 inline SaccadeState EyeData::saccadeState() const 00268 { return sstate; } 00269 00270 inline bool EyeData::hasPupilDiam() const 00271 { return !isnan(pdiam); } 00272 00273 inline bool EyeData::hasMetaData(const std::string field = "any") const 00274 { if(field.compare("any") == 0) return itsExtraData.is_valid(); 00275 else return (itsExtraData.is_valid() && 00276 (itsExtraData->hasParam(field) || 00277 itsExtraData->hasParam(special(field)) )); } 00278 00279 inline bool EyeData::hasSpecialMetaData(const std::string field) const 00280 { 00281 return (hasMetaData("any") && itsExtraData->hasParam(special(field))); 00282 } 00283 00284 // in order to make this much more general, we should use a dictionary of similar terms in a file somewhere 00285 00286 inline bool EyeData::hasSaccadeTargetData() const 00287 { 00288 std::vector<std::string> sac_args; 00289 // TODO: make enums/DEFINEs for which fields define a saccade 00290 // or for another region of interest 00291 const std::string fields("targetx targety"); 00292 split(fields," ", std::back_inserter(sac_args)); 00293 00294 for(std::vector<std::string>::iterator iter = sac_args.begin(); 00295 iter != sac_args.end(); 00296 ++iter) 00297 if (hasMetaData(*iter) == false) return false; 00298 return true; 00299 } 00300 00301 inline bool EyeData::isValid() const 00302 { return !(isnan(x) || isnan(y)); } 00303 00304 inline bool EyeData::isWithin(const Dims& dims) const 00305 { 00306 if (isValid() == false) return false; 00307 const int i = int(x + 0.49999F), j = int(y + 0.49999F); 00308 return (i >= 0 && i < dims.w() && j >= 0 && j < dims.h()); 00309 } 00310 00311 inline bool EyeData::isSpecialField(std::string field) const 00312 { 00313 return field[0]=='*'; 00314 } 00315 00316 inline void EyeData::getPosition(float& xx, float& yy) const 00317 { xx = x; yy = y; } 00318 00319 inline Point2D<int> EyeData::position() const 00320 { return Point2D<int>(int(x + 0.49999F), int(y + 0.49999F)); } 00321 00322 inline float EyeData::pupilDiameter() const 00323 { return pdiam; } 00324 00325 inline rutz::shared_ptr<ParamMap> EyeData::getMetaData() const 00326 { return itsExtraData; } 00327 00328 inline std::vector<std::string> EyeData::getMetaDataList() const 00329 { 00330 std::vector<std::string> argList; 00331 if(hasMetaData()) 00332 for (ParamMap::key_iterator iter = getMetaData()->keys_begin(); 00333 iter != getMetaData()->keys_end(); 00334 ++iter) 00335 argList.push_back(*iter); 00336 00337 return argList; } 00338 00339 inline double EyeData::getMetaDataField(std::string field) const 00340 { 00341 if (hasSpecialMetaData(field)) 00342 return itsExtraData->getDoubleParam(special(field)); 00343 else if(hasMetaData(field)) 00344 return itsExtraData->getDoubleParam(field); 00345 else LFATAL("No data field %s in EyeData",field.c_str()); 00346 return -1; 00347 } 00348 00349 inline void EyeData::getSaccadeTarget(float& targx, float& targy) const 00350 { targx = getMetaDataField("targetx"); 00351 targy = getMetaDataField("targety"); } 00352 00353 inline Point2D<int> EyeData::saccadeTarget() const 00354 { 00355 if(hasSaccadeTargetData()) 00356 { 00357 float dx, dy; 00358 getSaccadeTarget(dx, dy); 00359 return Point2D<int>(int(dx + 0.49999F), int(dy + 0.49999F)); 00360 } 00361 else 00362 { return Point2D<int>(1,1);} // TODO: should throw exception here 00363 } 00364 00365 inline float EyeData::saccadeAmplitude() const 00366 { return getMetaDataField("amp"); } 00367 00368 inline float EyeData::saccadeDuration() const 00369 { return itsExtraData->getDoubleParam("dur"); } 00370 00371 inline float EyeData::saccadeTime() const 00372 { return itsExtraData->getDoubleParam("time"); } 00373 00374 inline unsigned int EyeData::saccadeNumber() const 00375 { return uint(itsExtraData->getDoubleParam("num")); } 00376 00377 // Commented out for build purposes. 00378 /* 00379 inline void EyeData::outputData(std::ostream &out) const 00380 { 00381 out << "(x,y,pd) = (" << x << "," << y << "," << pdiam << ")\t" 00382 << "status = " << sstate-1 << "\n"; //-1 is for enum->0based status idx 00383 if(hasMetaData()) 00384 { 00385 std::vector<std::string> metaFields = getMetaDataList(); 00386 for(std::vector<std::string>::iterator iter = metaFields.begin(); 00387 iter != metaFields.end(); 00388 ++iter) 00389 out << *iter << " = " << getMetaDataField(*iter) << "\n"; 00390 } 00391 }; 00392 */ 00393 // ###################################################################### 00394 /* So things look consistent in everyone's emacs... */ 00395 /* Local Variables: */ 00396 /* mode: c++ */ 00397 /* indent-tabs-mode: nil */ 00398 /* End: */ 00399 00400 inline std::string EyeData::special(std::string field) const 00401 { 00402 if(field[0]=='*') return field; 00403 else return "*"+field; 00404 } 00405 00406 inline std::string EyeData::unspecial(std::string field) const 00407 { 00408 if(field[0]=='*') return field.erase(0,1); 00409 else return field; 00410 } 00411 #endif // PSYCHO_EYEDATA_H_DEFINED