CFEL - ASG Software Suite  2.5.0
CASS
hitrate.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010 Stephan Kassemeyer
2 // Copyright (C) 2013 Lutz Foucar
3 
4 /**
5  * @file hitrate.cpp processors for single particle hitfinding
6  *
7  * @author Stephan Kassemeyer
8  */
9 
10 #include <QtCore/QString>
11 #include <time.h>
12 #include <iostream>
13 
14 #include "cass.h"
15 #include "hitrate.h"
16 #include "result.hpp"
17 #include "convenience_functions.h"
18 #include "cass_settings.h"
19 #include "log.h"
20 
21 
22 using namespace cass;
23 using namespace std;
24 
25 // *** processor 300 finds Single particle hits ***
26 pp300::pp300(const name_t &name)
27  : Processor(name)
28 {
29  loadSettings(0);
30 }
31 
33 {
34 }
35 
37 {
39  _reTrain = false;
40 }
41 
43 {
45  _reTrain = 0;
46 }
47 
49 {
50  std::ifstream infile(_trainingFile.c_str(), std::ios::binary|std::ios::in);
51  infile.read(reinterpret_cast<char*>(_covI.data()), _covI.size()*sizeof(double));
52  infile.read(reinterpret_cast<char*>(_mean.data()), _mean.size()*sizeof(double));
53  infile.close();
54 }
55 
57 {
58  time_t rawtime;
59  struct tm * timeinfo;
60  time ( &rawtime );
61  size_t dateSize=9+4+1;
62  char date_and_time[dateSize];
63  timeinfo = localtime ( &rawtime );
64  strftime(date_and_time,dateSize,"%Y%m%d_%H%M",timeinfo);
65  QString saveName( QString("sp_training_%1.dat").arg(date_and_time) );
66  std::cout << "Saving Single Particle Data to " << saveName.toStdString() << std::endl;
67  std::ofstream outfile(saveName.toStdString().c_str(), std::ios::binary|std::ios::out);
68  if (outfile.is_open())
69  {
70  //write the training data to the file//
71  outfile.write(reinterpret_cast<const char*>( _covI.data()), _covI.size()*sizeof(double) );
72  outfile.write(reinterpret_cast<const char*>( _mean.data()), _mean.size()*sizeof(double) );
73  }
74  outfile.close();
75 }
76 
78 {
79  typedef matrixType::traverser ttt;
80  std::cout << std::endl << "_covI = [";
81  for (ttt it0 = _covI.traverser_begin(); it0!=_covI.traverser_end(); ++it0) {
82  std::cout << "[";
83  for (ttt::next_type it1 = it0.begin(); it1!=it0.end(); ++it1) {
84  std::cout << *it1 << ", ";
85  }
86  std::cout << "]; ";
87  }
88  std::cout << "] " << std::endl;
89  std::cout << std::endl << "_mean= [";
90  for (ttt it0 = _mean.traverser_begin(); it0!=_mean.traverser_end(); ++it0) {
91  std::cout << "[";
92  for (ttt::next_type it1 = it0.begin(); it1!=it0.end(); ++it1) {
93  std::cout << *it1 << ", ";
94  }
95  std::cout << "]; ";
96  }
97  std::cout << "] " << std::endl;
98 }
99 
101 {
102  using namespace std;
103  CASSSettings settings;
104  settings.beginGroup("Processor");
106 
107  // Get the input
108  _pHist = setupDependency("ImageName");
109  setupGeneral();
110  bool ret (setupCondition());
111  if (!(ret && _pHist))
112  return;
113 
114  _xstart = settings.value("xstart", 0).toInt();
115  _ystart = settings.value("ystart", 0).toInt();
116  _xend = settings.value("xend", -1).toInt();
117  _yend = settings.value("yend", -1).toInt();
118 
119  // Create result
121 
122  // outlier processor:
123  _nTrainingSetSize = settings.value("TrainingSetSize", 200).toInt();
124  _nFeatures = 5;
128  _mean = matrixType( 1,_nFeatures );
129 
130  _saveTraining = settings.value("saveTraining",true).toBool();
131  _readTraining = settings.contains("readTrainingFile");
132  if (_readTraining)
133  {
134  _trainingFile = settings.value("readTrainingFile","sp_training.dat").toString().toStdString();
136  std::cout << "read training matrices:" << std::endl;
139  }
140  else
141  {
142  vigra::linalg::identityMatrix(_covI);
144  }
145 
146  std::cout<<"processor "<<name()
147  <<": detects Single particle hits in Processor "<< _pHist->name()
148  <<" ROI for detection: ["<<_xstart<<","<<_ystart<<","<<_xend<<","<<_yend<<"]"
149  <<". Condition is"<<_condition->name()
150  <<std::endl;
151 }
152 
154 {
155  std::cout << std::endl << "single particle hit: cleared histogram -> restart training." << std::endl;
157 }
158 
159 void pp300::processCommand(std::string command)
160 {
161  if (command == "retrain")
162  {
163  std::cout << std::endl << "single particle hit: received command to restart training." << std::endl;
165  }
166  if (command == "saveTraining")
167  {
168  std::cout << std::endl << "single particle hit: received command to save training." << std::endl;
170  }
171 }
172 
174 {
175  const result_t &image(_pHist->result(evt.id()));
176  QReadLocker lock(&image.lock);
177  QMutexLocker mlock(&_mutex);
178 
179  const size_t nxbins (image.shape().first);
180  const size_t nybins (image.shape().second);
181 
182  result_t::storage_t integralimg(nxbins*nybins,0);
183  result_t::storage_t rowsum(nxbins*nybins,0);
184 
185  // sanity checks and auto-set nxbins. todo: restore -1 so that nxbins/nybins gets updated on size change?
186  if (_xstart < 0) _xstart = 0;
187  if (_ystart < 0) _ystart = 0;
188  if (_xend >= static_cast<int>(nxbins)) _xend = nxbins-1;
189  if (_yend >= static_cast<int>(nybins)) _yend = nybins-1;
190  if (_xend < 0) _xend = nxbins-1;
191  if (_yend < 0) _yend = nybins-1;
192 
193 
194  // calculate integral-image:
195  // row sum initialize first row:
196  for (int xx = _xstart; xx<=_xend; ++xx)
197  rowsum[xx-_xstart] = image[xx + _ystart*nxbins];
198  // row sum rest:
199  for (int xx = _xstart; xx<=_xend; ++xx)
200  for (int yy=_ystart+1; yy<=_yend; ++yy)
201  rowsum[xx-_xstart + (yy-_ystart)*nxbins] = rowsum[xx-_xstart + (yy-_ystart-1)*nxbins] + image[xx + yy*nxbins];
202  // intImg first col:
203  for (int yy = _ystart; yy<=_yend; ++yy)
204  integralimg[(yy-_ystart)*nxbins] = rowsum[(yy-_ystart)*nxbins];
205  // intImg rest:
206  for (int xx = _xstart+1; xx<=_xend; ++xx)
207  for (int yy = _ystart; yy<=_yend; ++yy)
208  integralimg[xx-_xstart + (yy-_ystart)*nxbins] = integralimg[xx-_xstart-1 + (yy-_ystart)*nxbins] + rowsum[xx-_xstart + (yy-_ystart)*nxbins];
209 
210  // calculate variation features:
211  int xsize_intimg = _xend-_xstart+1;
212  int ysize_intimg = _yend-_ystart+1;
213 
214  //
215  // 1st variation feature: sum(cell-avg)^2
216  double var0 = 0;
217  { // start new scope
218  int xsteps = 3;
219  int ysteps = 3;
220  double avg = integralimg[xsize_intimg-1 + (ysize_intimg-1)*nxbins];
221  for (int ii=0; ii<xsteps; ++ii)
222  {
223  int xx_prev = lround( static_cast<double>(ii)*xsize_intimg/xsteps );
224  int xx = lround( static_cast<double>(ii+1)*xsize_intimg/xsteps-1 );
225  for (int jj=0; jj<ysteps; ++jj)
226  {
227  int yy_prev = lround( static_cast<double>(jj)*ysize_intimg/ysteps );
228  int yy = lround( static_cast<double>(jj+1)*ysize_intimg/ysteps-1 );
229  double val = integralimg[xx + yy*nxbins] + integralimg[xx_prev + yy_prev*nxbins] - integralimg[xx + yy_prev*nxbins] - integralimg[xx_prev + yy*nxbins];
230  var0 += (val-avg)*(val-avg);
231  }
232  }
233  var0 /= (avg*avg); // norm result
234  } //end scope
235 
236  //
237  // 2nd variation feature: (bottom to top, sum(cell-topneighbour)^2
238  double var1 = 0;
239  { // start new scope
240  int xsteps = 10;
241  int ysteps = 10;
242  // ToDo: check orientation. maybe go left-right instead top-bottom!
243  for (int ii=0; ii<xsteps; ++ii)
244  {
245  int xx_prev = lround( static_cast<double>(ii)*xsize_intimg/xsteps );
246  int xx = lround( static_cast<double>(ii+1)*xsize_intimg/xsteps-1 );
247  for (int jj=0; jj<ysteps-2; ++jj)
248  {
249  int yy_prev = lround( static_cast<double>(jj)*ysize_intimg/ysteps );
250  int yy = lround( static_cast<double>(jj+1)*ysize_intimg/ysteps-1 );
251  int yy_next = lround( static_cast<double>(jj+2)*ysize_intimg/ysteps);
252  double diff = 2*integralimg[xx+yy*nxbins] + integralimg[xx_prev+yy_prev*nxbins] - integralimg[xx+yy_prev*nxbins] - 2*integralimg[xx_prev+yy*nxbins] - integralimg[xx+yy_next*nxbins] + integralimg[xx_prev+yy_next*nxbins];
253  var1 += diff*diff;
254  }
255  }
256  } // end scope
257 
258  //
259  // 3rd variation feature: (bottom to top, sum(cell-topneighbour)^2 like 2nd one, but with different scale
260  double var2 = 0;
261  { // start new scope
262  int xsteps = 15;
263  int ysteps = 15;
264  // ToDo: check orientation. maybe go left-right instead top-bottom!
265  for (int ii=0; ii<xsteps; ++ii)
266  {
267  int xx_prev = lround( static_cast<double>(ii)*xsize_intimg/xsteps );
268  int xx = lround( static_cast<double>(ii+1)*xsize_intimg/xsteps-1 );
269  for (int jj=0; jj<ysteps-2; ++jj)
270  {
271  int yy_prev = lround( static_cast<double>(jj)*ysize_intimg/ysteps );
272  int yy = lround( static_cast<double>(jj+1)*ysize_intimg/ysteps-1 );
273  int yy_next = lround( static_cast<double>(jj+2)*ysize_intimg/ysteps);
274  double diff = 2*integralimg[xx+yy*nxbins] + integralimg[xx_prev+yy_prev*nxbins] - integralimg[xx+yy_prev*nxbins] - 2*integralimg[xx_prev+yy*nxbins] - integralimg[xx+yy_next*nxbins] + integralimg[xx_prev+yy_next*nxbins];
275  var2 += diff*diff;
276  }
277  }
278  } // end scope
279 
280  //
281  // 4th variation feature: chequerboard
282  double var3 = 0;
283  { // start new scope
284  int xsteps = 15;
285  int ysteps = 15;
286  int xfactor = -1;
287  int yfactor = -1;
288  int factor;
289  for (int ii=0; ii<xsteps; ++ii)
290  {
291  xfactor = -xfactor;
292  int xx_prev = lround( static_cast<double>(ii)*xsize_intimg/xsteps );
293  int xx = lround( static_cast<double>(ii+1)*xsize_intimg/xsteps-1 );
294  for (int jj=0; jj<ysteps; ++jj)
295  {
296  yfactor = -yfactor;
297  factor = xfactor*yfactor;
298  int yy_prev = lround( static_cast<double>(jj)*ysize_intimg/ysteps );
299  int yy = lround( static_cast<double>(jj+1)*ysize_intimg/ysteps-1 );
300  var3 += factor*(integralimg[xx + yy*nxbins] + integralimg[xx_prev + yy_prev*nxbins] - integralimg[xx + yy_prev*nxbins] - integralimg[xx_prev + yy*nxbins]);
301  }
302  }
303  } //end scope //
304 
305  // 5th variation feature: integral intensity
306  double var4 = integralimg[xsize_intimg-1 + (ysize_intimg-1)*nxbins];
307 
308  // try to unify feature scales (avoid ill-conditioned matrices, also, matrix should not be singular):
309  var0 /= 1;
310  var1 /= 1e12;
311  var2 /= 1e12;
312  var3 /= 1e8;
313  var4 /= 1e7;
314 
315  // output current features:
316  VERBOSEOUT(std::cout<< "current features: " << var0 << ", " << var1 << ", " << var2 << ", " << var3 << ", " << var4 << std::endl);
317 
318  // populate Trainingset
320  {
321  _variationFeatures[matrixType::difference_type(_trainingSetsInserted, 0)] = var0;
322  _variationFeatures[matrixType::difference_type(_trainingSetsInserted, 1)] = var1;
323  _variationFeatures[matrixType::difference_type(_trainingSetsInserted, 2)] = var2;
324  _variationFeatures[matrixType::difference_type(_trainingSetsInserted, 3)] = var3;
325  _variationFeatures[matrixType::difference_type(_trainingSetsInserted, 4)] = var4;
328  VERBOSEOUT(std::cout << "inserted: " << _trainingSetsInserted << std::endl);
329  }
330  if ( _reTrain )
331  {
332 
333  VERBOSEOUT(std::cout << "rows: " << vigra::rowCount(_cov) << std::endl);
334  VERBOSEOUT(std::cout << "cols: " << vigra::columnCount(_cov) << std::endl);
335 
336  _cov = vigra::linalg::covarianceMatrixOfColumns( _variationFeatures.subarray(vigra::Matrix<double>::difference_type(0,0), vigra::Matrix<double>::difference_type(_trainingSetsInserted,_nFeatures)) );
337 
338 // typedef matrixType::traverser ttt;
339 #ifdef VERBOSE
340  std::cout << std::endl << "_cov= [";
341  for (ttt it0 = _cov.traverser_begin(); it0!=_cov.traverser_end(); ++it0) {
342  std::cout << "[";
343  for (ttt::next_type it1 = it0.begin(); it1!=it0.end(); ++it1) {
344  std::cout << *it1 << ", ";
345  }
346  std::cout << "]; ";
347  }
348  std::cout << "] " << std::endl;
349  std::cout << std::endl << "_variationFeatures= [";
350  for (ttt it0 = _variationFeatures.traverser_begin(); it0!=_variationFeatures.traverser_end(); ++it0) {
351  std::cout << "[";
352  for (ttt::next_type it1 = it0.begin(); it1!=it0.end(); ++it1) {
353  std::cout << *it1 << ", ";
354  }
355  std::cout << "]; ";
356  }
357  std::cout << "] " << std::endl;
358 #endif
359 
360  try{
361  _covI = vigra::linalg::inverse(_cov);
362  }
363  catch( vigra::PreconditionViolation E ) {
364  // matrix is singular. use normalized euclidean distance instead of mahalanobis:
365  std::cout << "Hit_Helper2::process: " << E.what() << std::endl;
366  vigra::linalg::identityMatrix(_covI);
367  startNewTraining(); // try again: retrain...
368  return;
369  };
370 
371  //calculate collumn-average and store in _mean
372  // transformMultArray reduces source-dimensions to scalar for each singleton-dest-dimension
373  /*transformMultiArray(srcMultiArrayRange(_variationFeatures),
374  destMultiArrayRange(_mean.insertSingletonDimension(1)),
375  FindAverage<double>());*/
376  vigra::linalg::columnStatistics(_variationFeatures, _mean);
378 
379  // also possible:
380  /*
381  vigra::transformMultiArray(srcMultiArrayRange(_variationFeatures),
382  destMultiArrayRange(_mean),
383  vigra::FindAverage<double>());*/
384 
386  _reTrain = false;
387  } //end reTrain
388  // use mean and covariance to predict outliers:
389  // mahalanobis^2 = (y-mean)T Cov^-1 y
390  matrixType y(1, _nFeatures);
391  y[0] = var0; y[1] = var1; y[2] = var2; y[3] = var3; y[4] = var4;
392  //y = _variationFeatures.subarray( matrixType::difference_type(0,0), matrixType::difference_type(1,_nFeatures));
393 
394  /*
395  std::cout << "rows: " << vigra::rowCount(y) << std::endl;
396  std::cout << "cols: " << vigra::columnCount(y) << std::endl;
397  std::cout << "rows: " << vigra::rowCount(_covI) << std::endl;
398  std::cout << "cols: " << vigra::columnCount(_covI) << std::endl;*/
399  double mahal_dist = vigra::linalg::mmul( (y-_mean), vigra::linalg::mmul( _covI , (y-_mean).transpose() ))[0];
400 
401  result.setValue(mahal_dist);
402 }
403 
404 
405 
406 
407 
408 
409 
410 // *** pp 313 convolute histogram with kernel ***
411 
413  : Processor(name)
414 {
415  loadSettings(0);
416 }
417 
419 {
420  CASSSettings s;
421  s.beginGroup("Processor");
423 
424  setupGeneral();
425  _pHist = setupDependency("InputName");
426  bool ret (setupCondition());
427  if (!(ret && _pHist))
428  return;
429 
430  /** read in kernel */
431  _kernel.clear();
432  QStringList kernel(s.value("Kernel").toStringList());
433  QStringList::const_iterator cIt(kernel.begin());
434  for (; cIt != kernel.end(); ++cIt)
435  {
436  bool ok(false);
437  _kernel.push_back(cIt->toFloat(&ok));
438  if (!ok)
439  throw invalid_argument("pp313::loadSettings: '" + name() +
440  "' Kernel value '" + cIt->toStdString() +
441  "' can't be converted to floating point number");
442  }
443  _kernelCenter = _kernel.begin() + static_cast<int>(_kernel.size()*0.5);
444  _kleft = distance(_kernelCenter,_kernel.begin());
445  _kright = distance(_kernelCenter, _kernel.end()-1);
446 
447  createHistList(_pHist->result().clone());
448  Log::add(Log::INFO,"Processor '" + name() +
449  "' convolutes '" + _pHist->name() + "' with kernel." +
450  "'. Condition on Processor '" + _condition->name() + "'");
451 }
452 
453 void pp313::process(const CASSEvent& evt, result_t &result)
454 {
455  const result_t& src(_pHist->result(evt.id()));
456  QReadLocker lock(&src.lock);
457 
458  typedef vigra::StandardAccessor<float> FAccessor;
459  typedef vigra::StandardAccessor<float> KernelAccessor;
460 
461  vigra::convolveLine(src.begin(),src.end(),FAccessor(),
462  result.begin(),FAccessor(),
463  _kernelCenter,KernelAccessor(),_kleft,_kright,
464  vigra::BORDER_TREATMENT_REFLECT);
465 }
466 
467 
468 
CachedList::item_type result_t
define the results
Definition: processor.h:52
void readTrainingMatrices()
Definition: hitrate.cpp:48
int _xend
xend - ROI for calculations
Definition: hitrate.h:100
std::vector< float >::iterator _kernelCenter
iterator to the center of the kernel
Definition: hitrate.h:174
pp300(const name_t &)
constructor
Definition: hitrate.cpp:26
Event to store all LCLS Data.
Definition: cass_event.h:32
virtual void createHistList(result_t::shared_pointer result)
create result list.
Definition: processor.cpp:79
matrixType _mean
Definition: hitrate.h:124
const_iterator end() const
retrieve iterator to the end of storage
Definition: result.hpp:632
int _ystart
ystart - ROI for calculations
Definition: hitrate.h:97
std::vector< value_t > storage_t
the storage of this container
Definition: result.hpp:332
std::vector< float > _kernel
array containing the kernel
Definition: hitrate.h:171
virtual void loadSettings(size_t)
load the settings for this pp
Definition: hitrate.cpp:100
const name_t name() const
retrieve the name of this processor
Definition: processor.h:167
processors for single particle hitfinding
matrixType _cov
Definition: hitrate.h:126
shared_pointer _pHist
the histogram to work on
Definition: hitrate.h:106
Settings for CASS.
Definition: cass_settings.h:30
matrixType _covI
Definition: hitrate.h:127
std::tr1::shared_ptr< self_type > shared_pointer
a shared pointer of this class
Definition: result.hpp:323
pp313(const name_t &name)
constructor
Definition: hitrate.cpp:412
void printTrainingMatrices()
Definition: hitrate.cpp:77
int _nTrainingSetSize
Definition: hitrate.h:111
QMutex _mutex
mutex to lock the processing part since only one can be processed at a time
Definition: hitrate.h:132
virtual ~pp300()
virtual destructor
Definition: hitrate.cpp:32
STL namespace.
int _nFeatures
Definition: hitrate.h:112
result classes
int _yend
yend - ROI for calculations
Definition: hitrate.h:103
void startNewTraining()
Definition: hitrate.cpp:42
virtual void clearHistogramEvent()
Definition: hitrate.cpp:153
static void add(Level level, const std::string &line)
add a string to the log
Definition: log.cpp:31
shared_pointer _pHist
pp containing input histogram
Definition: hitrate.h:168
void setValue(const_reference value)
assign the result container to a value
Definition: result.hpp:543
const_iterator begin() const
retrieve a iterator for read access to beginning
Definition: result.hpp:608
fromStdString(const std::string &str)
virtual void process(const CASSEvent &, result_t &)
check if image specified in settings contains a single particle hit
Definition: hitrate.cpp:173
base class for processors.
Definition: processor.h:39
shared_pointer setupDependency(const std::string &depVarName, const name_t &name="")
setup the dependecy.
Definition: processor.cpp:114
int _trainingSetsInserted
Definition: hitrate.h:128
contains(const QString &key)
int _readTraining
if filename is given in settings, load instead of calculate
Definition: hitrate.h:118
int _kleft
element to the left of the kernel center
Definition: hitrate.h:177
file contains declaration of classes and functions that help other processors to do their job...
vigra::Matrix< double > matrixType
Definition: hitrate.h:109
matrixType _variationFeatures
Definition: hitrate.h:123
virtual void processCommand(std::string command)
process command in pp
Definition: hitrate.cpp:159
QReadWriteLock lock
lock for locking operations on the data of the container
Definition: result.hpp:954
int _kright
elements to the right of the kernel center
Definition: hitrate.h:180
virtual void loadSettings(size_t)
load the settings of the pp
Definition: hitrate.cpp:418
file contains global definitions for project cass
int _saveTraining
user setting: do (default) or don't save training matrices to file
Definition: hitrate.h:115
id_t & id()
setters
Definition: cass_event.h:64
void saveTrainingMatrices()
Definition: hitrate.cpp:56
virtual void process(const CASSEvent &, result_t &)
process event
Definition: hitrate.cpp:453
value(const QString &key, const QVariant &defaultValue=QVariant()
#define VERBOSEOUT(a)
Definition: cass.h:38
void setupGeneral()
general setup of the processor
Definition: processor.cpp:85
file contains specialized class that do the settings for cass
int _reTrain
Definition: hitrate.h:129
void trainingFinished()
Definition: hitrate.cpp:36
shared_pointer _condition
pointer to the processor that will contain the condition
Definition: processor.h:277
shape_t shape() const
return the shape of the result
Definition: result.hpp:811
bool setupCondition(bool defaultConditionType=true)
setup the condition.
Definition: processor.cpp:94
std::string name_t
define the name type
Definition: processor.h:46
contains a logger for cass
int _xstart
xstart - ROI for calculations
Definition: hitrate.h:94
beginGroup(const QString &prefix)
std::string _trainingFile
optional user-setting: filename to read training from
Definition: hitrate.h:121
virtual const result_t & result(const CASSEvent::id_t eventid=0)
retrieve a result for a given id.
Definition: processor.cpp:54