35 #include <visp3/core/vpConfig.h>
36 #include <visp3/core/vpGaussRand.h>
37 #include <visp3/core/vpStatisticalTestEWMA.h>
38 #include <visp3/core/vpStatisticalTestHinkley.h>
39 #include <visp3/core/vpStatisticalTestMeanAdjustedCUSUM.h>
40 #include <visp3/core/vpStatisticalTestShewhart.h>
41 #include <visp3/core/vpStatisticalTestSigma.h>
42 #include <visp3/gui/vpPlot.h>
44 #ifdef ENABLE_VISP_NAMESPACE
48 #if defined(VISP_HAVE_DISPLAY)
49 namespace TutorialMeanDrift
58 HINLKEY_TYPE_TEST = 0,
60 MEAN_ADJUSTED_CUSUM_TYPE_TEST = 2,
61 SHEWHART_TYPE_TEST = 3,
64 UNKOWN_TYPE_TEST = COUNT_TYPE_TEST
74 std::string typeTestToString(
const TypeTest &type)
78 case HINLKEY_TYPE_TEST:
84 case MEAN_ADJUSTED_CUSUM_TYPE_TEST:
87 case SHEWHART_TYPE_TEST:
93 case UNKOWN_TYPE_TEST:
95 result =
"unknown-type-test";
108 TypeTest typeTestFromString(
const std::string &name)
110 TypeTest result = UNKOWN_TYPE_TEST;
111 unsigned int count =
static_cast<unsigned int>(COUNT_TYPE_TEST);
113 bool hasNotFound =
true;
114 while ((
id < count) && hasNotFound) {
115 TypeTest temp =
static_cast<TypeTest
>(id);
116 if (typeTestToString(temp) == name) {
133 std::string getAvailableTypeTest(
const std::string &prefix =
"<",
const std::string &sep =
" , ",
134 const std::string &suffix =
">")
136 std::string msg(prefix);
137 unsigned int count =
static_cast<unsigned int>(COUNT_TYPE_TEST);
138 unsigned int lastId = count - 1;
139 for (
unsigned int i = 0; i < lastId; i++) {
140 msg += typeTestToString(
static_cast<TypeTest
>(i)) + sep;
142 msg += typeTestToString(
static_cast<TypeTest
>(lastId)) + suffix;
153 template <
typename T>
154 std::string numberToString(
const T &number)
156 std::stringstream ss;
167 std::string boolToString(
const bool &
boolean)
186 std::string wecoRulesToString(
const bool rules[
vpStatisticalTestShewhart::COUNT_WECO - 1],
const std::string &prefix =
"[",
const std::string &suffix =
"]",
const std::string &sep =
" , ")
188 std::string rulesAsString = prefix;
191 rulesAsString +=
"ON";
194 rulesAsString +=
"OFF";
196 rulesAsString += sep;
199 rulesAsString +=
"ON";
202 rulesAsString +=
"OFF";
204 rulesAsString += suffix;
205 return rulesAsString;
228 size_t nbNames = names.size();
229 for (
size_t i = 0; i < nbNames; ++i) {
231 std::cout <<
"alarm[" << names[i] <<
"] (i.e. " <<
static_cast<unsigned int>(alarmToActivate) <<
") set to true" << std::endl;
232 array[
static_cast<unsigned int>(alarmToActivate)] =
true;
252 std::vector<std::string> listActivatedAlarms;
254 for (
unsigned int id = 0;
id < nbTypeTests; ++id) {
258 listActivatedAlarms.push_back(testName);
261 return listActivatedAlarms;
275 const std::string &prefix =
"[",
const std::string &sep =
" , ",
276 const std::string &suffix =
"]")
278 std::vector<std::string> listActivatedAlarms = meanDriftArrayToVectorOfString(array);
279 std::string result = prefix;
280 size_t nbTestActivated = listActivatedAlarms.size();
281 if (nbTestActivated == 0) {
282 return prefix +
" " + suffix;
284 for (
size_t i = 0; i < nbTestActivated - 1; ++i) {
285 result += listActivatedAlarms[i] + sep;
287 result += listActivatedAlarms[nbTestActivated - 1] + suffix;
299 unsigned int nbActivated = 0;
301 for (
unsigned int id = 0;
id < nbTypeAlarms; ++id) {
309 #ifndef DOXYGEN_SHOULD_SKIP_THIS
314 typedef struct ParametersForAlgo
316 unsigned int m_test_nbsamples;
318 unsigned int m_test_nbactivatedalarms;
322 float m_hinkley_alpha;
323 float m_hinkley_delta;
324 bool m_hinkley_computealphadelta;
327 bool m_shewhart_useWECO;
332 : m_test_nbsamples(30)
336 , m_hinkley_alpha(4.76f)
337 , m_hinkley_delta(1.f)
338 , m_hinkley_computealphadelta(false)
341 , m_shewhart_useWECO(false)
347 m_test_nbactivatedalarms = meanDriftArrayToNbActivated(m_test_activatedalarms);
354 int testOnSynthetic(
const TutorialMeanDrift::TypeTest &type,
const TutorialMeanDrift::ParametersForAlgo parameters,
355 const float &mean,
const float &mean_drift,
const float &stdev)
357 const float dt = 10.f;
361 plotter.initGraph(0, 1);
362 plotter.setTitle(0,
"Evolution of the signal");
363 plotter.setUnitX(0,
"Frame ID");
364 plotter.setUnitY(0,
"No units");
368 unsigned int idFrame = 0;
371 case TutorialMeanDrift::EWMA_TYPE_TEST:
374 case TutorialMeanDrift::HINLKEY_TYPE_TEST:
375 p_test =
new vpStatisticalTestHinkley(parameters.m_hinkley_alpha, parameters.m_hinkley_delta, parameters.m_test_nbsamples);
377 case TutorialMeanDrift::MEAN_ADJUSTED_CUSUM_TYPE_TEST:
380 case TutorialMeanDrift::SHEWHART_TYPE_TEST:
381 p_test =
new vpStatisticalTestShewhart(parameters.m_shewhart_useWECO, parameters.m_shewhart_rules, parameters.m_test_nbsamples);
383 case TutorialMeanDrift::SIGMA_TYPE_TEST:
391 if ((type == TutorialMeanDrift::HINLKEY_TYPE_TEST) && parameters.m_hinkley_computealphadelta) {
394 p_test =
new vpStatisticalTestHinkley(parameters.m_hinkley_h, parameters.m_hinkley_k,
true, parameters.m_test_nbsamples);
402 for (
unsigned int i = 0; i < parameters.m_test_nbsamples; ++i) {
403 vpGaussRand rndGen(stdev, mean,
static_cast<long>(idFrame * dt));
404 signal =
static_cast<float>(rndGen());
410 std::cout <<
"Estimated mean of the input signal: " << p_test->
getMean() << std::endl;
411 std::cout <<
"Estimated stdev of the input signal: " << p_test->
getStdev() << std::endl;
414 float mean_eff = mean;
415 bool hasToRun =
true;
418 vpGaussRand rndGen(stdev, mean_eff,
static_cast<long>(idFrame * dt));
419 signal =
static_cast<float>(rndGen());
420 plotter.plot(0, 0, idFrame - parameters.m_test_nbsamples, signal);
426 mean_eff += mean_drift;
433 std::cout <<
"Test failed at frame: " << idFrame - parameters.m_test_nbsamples << std::endl;
435 std::cout <<
"Last signal value: " << signal << std::endl;
436 if (type == TutorialMeanDrift::EWMA_TYPE_TEST) {
438 std::cout <<
"\tw(t) = " << p_testEwma->
getWt() << std::endl;
440 else if (type == TutorialMeanDrift::MEAN_ADJUSTED_CUSUM_TYPE_TEST) {
443 std::cout <<
"\tUpper cusum = " << p_testCusum->
getTestSignalPlus() << std::endl;
445 else if (type==TutorialMeanDrift::SHEWHART_TYPE_TEST) {
447 std::vector<float> signal = p_testShewhart->
getSignals();
448 size_t nbSignal = signal.size();
449 std::cout <<
"Signal history = [ ";
450 for (
size_t i = 0; i < nbSignal; ++i) {
451 std::cout << signal[i] <<
" ";
453 std::cout <<
"]" << std::endl;
456 else if (type == TutorialMeanDrift::HINLKEY_TYPE_TEST) {
458 float Mk = p_hinkley->
getMk();
459 float Nk = p_hinkley->
getNk();
460 float Sk = p_hinkley->
getSk();
461 float Tk = p_hinkley->
getTk();
462 std::cout <<
"S+(t) = " << Tk - Nk <<std::endl;
463 std::cout <<
"S-(t) = " << Mk - Sk <<std::endl;
465 float limitDown = 0.f, limitUp = 0.f;
467 std::cout <<
"\tLimit down = " << limitDown << std::endl;
468 std::cout <<
"\tLimit up = " << limitUp << std::endl;
470 std::cout <<
"End of test on synthetic data. Press enter to leave." << std::endl;
476 int main(
int argc,
char *argv[])
478 TutorialMeanDrift::TypeTest opt_typeTest = TutorialMeanDrift::MEAN_ADJUSTED_CUSUM_TYPE_TEST;
479 TutorialMeanDrift::ParametersForAlgo parameters;
480 float opt_mean = 6.f;
481 float opt_meandrift = 0.f;
482 float opt_stdev = 2.f;
486 if ((std::string(argv[i]) ==
"--test") && ((i + 1) < argc)) {
487 opt_typeTest = TutorialMeanDrift::typeTestFromString(argv[i + 1]);
490 else if ((std::string(argv[i]) ==
"--nb-samples") && ((i + 1) < argc)) {
491 parameters.m_test_nbsamples = std::atoi(argv[i + 1]);
494 else if ((std::string(argv[i]) ==
"--mean") && ((i + 1) < argc)) {
495 opt_mean =
static_cast<float>(std::atof(argv[i + 1]));
498 else if ((std::string(argv[i]) ==
"--mean-drift") && ((i + 1) < argc)) {
499 opt_meandrift =
static_cast<float>(std::atof(argv[i + 1]));
502 else if ((std::string(argv[i]) ==
"--stdev") && ((i + 1) < argc)) {
503 opt_stdev =
static_cast<float>(std::atof(argv[i + 1]));
506 else if ((std::string(argv[i]) ==
"--alarms")) {
507 unsigned int nbArguments = 0;
508 std::vector<std::string> alarmNames;
509 bool hasNotFoundNextParams =
true;
510 for (
int j = 1; ((i + j) < argc) && hasNotFoundNextParams; ++j) {
511 std::string candidate(argv[i+j]);
512 if (candidate.find(
"--") != std::string::npos) {
514 hasNotFoundNextParams =
false;
518 alarmNames.push_back(candidate);
522 TutorialMeanDrift::vectorOfStringToMeanDriftTypeArray(alarmNames, parameters.m_test_activatedalarms);
523 parameters.m_test_nbactivatedalarms = TutorialMeanDrift::meanDriftArrayToNbActivated(parameters.m_test_activatedalarms);
526 else if ((std::string(argv[i]) ==
"--cusum-h") && ((i + 1) < argc)) {
527 parameters.m_cusum_h =
static_cast<float>(std::atof(argv[i + 1]));
530 else if ((std::string(argv[i]) ==
"--cusum-k") && ((i + 1) < argc)) {
531 parameters.m_cusum_k =
static_cast<float>(std::atof(argv[i + 1]));
534 else if ((std::string(argv[i]) ==
"--ewma-alpha") && ((i + 1) < argc)) {
535 parameters.m_ewma_alpha =
static_cast<float>(std::atof(argv[i + 1]));
538 else if ((std::string(argv[i]) ==
"--hinkley-alpha") && ((i + 1) < argc)) {
539 parameters.m_hinkley_alpha =
static_cast<float>(std::atof(argv[i + 1]));
542 else if ((std::string(argv[i]) ==
"--hinkley-delta") && ((i + 1) < argc)) {
543 parameters.m_hinkley_delta =
static_cast<float>(std::atof(argv[i + 1]));
546 else if (std::string(argv[i]) ==
"--hinkley-compute") {
547 parameters.m_hinkley_computealphadelta =
true;
549 else if ((std::string(argv[i]) ==
"--hinkley-h") && ((i + 1) < argc)) {
550 parameters.m_hinkley_h =
static_cast<float>(std::atof(argv[i + 1]));
553 else if ((std::string(argv[i]) ==
"--hinkley-k") && ((i + 1) < argc)) {
554 parameters.m_hinkley_k =
static_cast<float>(std::atof(argv[i + 1]));
559 std::string argument = std::string(argv[i + 1 + j]);
560 if ((argument.find(
"on") != std::string::npos) || (argument.find(
"ON") != std::string::npos)) {
561 parameters.m_shewhart_rules[j] =
true;
564 parameters.m_shewhart_rules[j] =
false;
569 else if (std::string(argv[i]) ==
"--shewhart-weco") {
570 parameters.m_shewhart_useWECO =
true;
572 else if ((std::string(argv[i]) ==
"--sigma-h") && ((i + 1) < argc)) {
573 parameters.m_sigma_h =
static_cast<float>(std::atof(argv[i + 1]));
576 else if ((std::string(argv[i]) ==
"--help") || (std::string(argv[i]) ==
"-h")) {
577 std::cout <<
"\nSYNOPSIS " << std::endl
579 <<
" [--test <type>]"
580 <<
" [--nb-samples <value>]"
581 <<
" [--alarms <name_1 ... name_n>]"
582 <<
" [--mean <value>]"
583 <<
" [--mean-drift <value>]"
584 <<
" [--stdev <value>]"
585 <<
" [--cusum-h <value>]"
586 <<
" [--cusum-k <value>]"
587 <<
" [--ewma-alpha <value ]0; 1[>]"
588 <<
" [--hinkley-alpha <]0; inf[>]"
589 <<
" [--hinkley-delta <]0; inf[>]"
590 <<
" [--hinkley-compute]"
591 <<
" [--hinkley-h <]0; inf[>]"
592 <<
" [--hinkley-k <]0; inf[>]"
593 <<
" [--shewhart-rules <3-sigma:{on|off} 2-sigma:{on|off} 1-sigma:{on|off} same-side:{on|off}>"
594 <<
" [--shewhart-weco]"
595 <<
" [--sigma-h <value>]"
596 <<
" [--help,-h]" << std::endl;
597 std::cout <<
"\nOPTIONS " << std::endl
598 <<
" --test <type-name>" << std::endl
599 <<
" Type of test to perform on the data." << std::endl
600 <<
" Available values: " << TutorialMeanDrift::getAvailableTypeTest() << std::endl
602 <<
" --nb-samples <value>" << std::endl
603 <<
" Number of samples to compute the mean and standard deviation of the monitored signal." << std::endl
604 <<
" Default: " << parameters.m_test_nbsamples << std::endl
606 <<
" --alarms <name_1 .. name_n>" << std::endl
607 <<
" Set the mean drift alarms to monitor." << std::endl
608 <<
" Default: " << TutorialMeanDrift::meanDriftArrayToString(parameters.m_test_activatedalarms) << std::endl
611 <<
" --mean <value>" << std::endl
612 <<
" Mean of the signal." << std::endl
613 <<
" Default: " << opt_mean<< std::endl
615 <<
" --mean-drift <value>" << std::endl
616 <<
" Mean drift for the synthetic data." << std::endl
617 <<
" Default: " << opt_meandrift << std::endl
619 <<
" --stdev <value>" << std::endl
620 <<
" Standard deviation of the signal." << std::endl
621 <<
" Default: " << opt_stdev << std::endl
623 <<
" --cusum-h <value>" << std::endl
624 <<
" The alarm factor that permits to the CUSUM test to determine when the process is out of control" << std::endl
625 <<
" from the standard deviation of the signal." << std::endl
626 <<
" Default: " << parameters.m_cusum_h << std::endl
628 <<
" --cusum-k <value>" << std::endl
629 <<
" The factor that permits to determine the slack of the CUSUM test, " << std::endl
630 <<
" i.e. the minimum value of the jumps we want to detect, from the standard deviation of the signal." << std::endl
631 <<
" Default: " << parameters.m_cusum_k << std::endl
633 <<
" --ewma-alpha <value ]0; 1[>" << std::endl
634 <<
" Forgetting factor for the Exponential Weighted Moving Average (EWMA)." << std::endl
635 <<
" Default: " << parameters.m_ewma_alpha << std::endl
637 <<
" --hinkley-alpha <value ]0; inf[>" << std::endl
638 <<
" The alarm threshold indicating that a mean drift occurs for the Hinkley's test." << std::endl
639 <<
" Default: " << parameters.m_hinkley_alpha << std::endl
641 <<
" --hinkley-delta <value>" << std::endl
642 <<
" Detection threshold indicating minimal magnitude we want to detect for the Hinkley's test." << std::endl
643 <<
" Default: " << parameters.m_hinkley_delta << std::endl
645 <<
" --hinkley-compute" << std::endl
646 <<
" If set, the Hinkley's test will compute the alarm and detection thresholds" << std::endl
647 <<
" from the standard deviation of the input signal." << std::endl
648 <<
" Default: disabled" << std::endl
650 <<
" --hinkley-h <value>" << std::endl
651 <<
" Alarm factor permitting to compute the alarm threshold for the Hinkley's test." << std::endl
652 <<
" Default: " << parameters.m_hinkley_h << std::endl
654 <<
" --hinkley-k <value>" << std::endl
655 <<
" Detection factor permitting to compute the Detection threshold for the Hinkley's test." << std::endl
656 <<
" Default: " << parameters.m_hinkley_k << std::endl
658 <<
" --shewhart-rules <3-sigma:{on|off} 2-sigma:{on|off} 1-sigma:{on|off} same-side:{on|off}>" << std::endl
659 <<
" Choose the WECO additionnal tests for the Shewhart's test to use. To activate them, --shewart-weco must be used." << std::endl
660 <<
" Default: ON ON ON ON" << std::endl
662 <<
" --shewhart-weco" << std::endl
663 <<
" Activate the WECO additionnal tests for the Shewhart's test." << std::endl
664 <<
" Default: deactivated" << std::endl
666 <<
" --sigma-h <value>" << std::endl
667 <<
" The alarm factor of the sigma test." << std::endl
668 <<
" Default: " << parameters.m_sigma_h << std::endl
670 <<
" --help, -h" << std::endl
671 <<
" Display this helper message." << std::endl
676 std::cout <<
"\nERROR " << std::endl <<
" Unknown option " << argv[i] << std::endl;
682 if (parameters.m_test_nbactivatedalarms == 0) {
687 std::cout <<
" Activated statistical test : " << TutorialMeanDrift::typeTestToString(opt_typeTest) << std::endl;
688 std::cout <<
" Activated alarms : " << TutorialMeanDrift::meanDriftArrayToString(parameters.m_test_activatedalarms) << std::endl;
689 std::cout <<
" Nb samples for statistics computation: " << parameters.m_test_nbsamples << std::endl;
690 std::cout <<
" Alarm factor CUSUM test : " << (opt_typeTest == TutorialMeanDrift::MEAN_ADJUSTED_CUSUM_TYPE_TEST ? TutorialMeanDrift::numberToString(parameters.m_cusum_h) :
"N/A") << std::endl;
691 std::cout <<
" Detection factor CUSUM test : " << (opt_typeTest == TutorialMeanDrift::MEAN_ADJUSTED_CUSUM_TYPE_TEST ? TutorialMeanDrift::numberToString(parameters.m_cusum_k) :
"N/A") << std::endl;
692 std::cout <<
" Forgetting factor EWMA test : " << (opt_typeTest == TutorialMeanDrift::EWMA_TYPE_TEST ? TutorialMeanDrift::numberToString(parameters.m_ewma_alpha) :
"N/A") << std::endl;
693 std::cout <<
" Alarm threshold Hinkley's test : " << ((opt_typeTest == TutorialMeanDrift::HINLKEY_TYPE_TEST) && (!parameters.m_hinkley_computealphadelta) ? TutorialMeanDrift::numberToString(parameters.m_hinkley_alpha) :
"N/A") << std::endl;
694 std::cout <<
" Detection threshold Hinkley's test : " << ((opt_typeTest == TutorialMeanDrift::HINLKEY_TYPE_TEST) && (!parameters.m_hinkley_computealphadelta) ? TutorialMeanDrift::numberToString(parameters.m_hinkley_delta) :
"N/A") << std::endl;
695 std::cout <<
" Alarm factor Hinkley's test : " << ((opt_typeTest == TutorialMeanDrift::HINLKEY_TYPE_TEST) && parameters.m_hinkley_computealphadelta ? TutorialMeanDrift::numberToString(parameters.m_hinkley_h) :
"N/A") << std::endl;
696 std::cout <<
" Detection factor Hinkley's test : " << ((opt_typeTest == TutorialMeanDrift::HINLKEY_TYPE_TEST) && parameters.m_hinkley_computealphadelta ? TutorialMeanDrift::numberToString(parameters.m_hinkley_k) :
"N/A") << std::endl;
697 std::cout <<
" Shewhart's test set of WECO rules : " << (parameters.m_shewhart_useWECO && (opt_typeTest == TutorialMeanDrift::SHEWHART_TYPE_TEST) ? TutorialMeanDrift::wecoRulesToString(parameters.m_shewhart_rules) :
"N/A") << std::endl;
698 std::cout <<
" Shewhart's test use WECO rules : " << (opt_typeTest == TutorialMeanDrift::SHEWHART_TYPE_TEST ? TutorialMeanDrift::boolToString(parameters.m_shewhart_useWECO && (opt_typeTest == TutorialMeanDrift::SHEWHART_TYPE_TEST)) :
"N/A") << std::endl;
699 std::cout <<
" Alarm factor Sigma test : " << (opt_typeTest == TutorialMeanDrift::SIGMA_TYPE_TEST ? TutorialMeanDrift::numberToString(parameters.m_sigma_h) :
"N/A") << std::endl;
700 std::cout <<
" Actual mean of the input signal: " << opt_mean << std::endl;
701 std::cout <<
" Actual stdev of the input signal: " << opt_stdev << std::endl;
702 std::cout <<
" Mean drift of the input signal: " << opt_meandrift << std::endl;
704 return testOnSynthetic(opt_typeTest, parameters, opt_mean, opt_meandrift, opt_stdev);
709 std::cerr <<
"Recompile ViSP with display capabilities in order to use this tutorial." << std::endl;
error that can be emitted by ViSP classes.
@ badValue
Used to indicate that a value is not in the allowed range.
Class for generating random number with normal probability density.
This class enables real time drawing of 2D or 3D graphics. An instance of the class open a window whi...
Base class for methods detecting the drift of the mean of a process.
static std::string vpMeanDriftTypeToString(const vpMeanDriftType &type)
Cast a vpMeanDriftType into a string.
vpMeanDriftType
Enum that indicates if a drift of the mean occurred.
static vpMeanDriftType vpMeanDriftTypeFromString(const std::string &name)
Cast a string into a vpMeanDriftType.
void getLimits(float &limitDown, float &limitUp) const
Get the upper and lower limits of the test signal.
vpMeanDriftType testDownUpwardMeanDrift(const float &signal)
Test if a downward or an upward mean drift occurred according to the new value of the signal.
float getMean() const
Get the mean used as reference.
float getStdev() const
Get the standard deviation used as reference.
static std::string getAvailableMeanDriftType(const std::string &prefix="<", const std::string &sep=" , ", const std::string &suffix=">")
Get the list of available vpMeanDriftType objects that are handled.
Class that permits to perform Exponentially Weighted Moving Average mean drft tests.
float getWt() const
Get the current value of the test signal.
This class implements the Hinkley's cumulative sum test.
float getNk() const
Get the minimum of the test signal for upward mean drift .
float getTk() const
Get the test signal for upward mean drift..
float getSk() const
Get the test signal for downward mean drift.
float getMk() const
Get the maximum of the test signal for downward mean drift .
Class that permits to perform a mean adjusted Cumulative Sum test.
float getTestSignalMinus() const
Get the latest value of the test signal for downward jumps of the mean.
float getTestSignalPlus() const
Get the latest value of the test signal for upward jumps of the mean.
Class that permits a Shewhart's test.
vpWecoRulesAlarm getAlarm() const
Get the alarm raised by the last test due to the WECO's rules.
std::vector< float > getSignals() const
Get the NB_DATA_SIGNAL last signal values, sorted from the latest [0] to the newest [NB_DATA_SIGNAL -...
static const bool CONST_ALL_WECO_ACTIVATED[COUNT_WECO - 1]
static std::string vpWecoRulesAlarmToString(const vpWecoRulesAlarm &alarm)
Class that permits a simple test comparing the current value to the standard deviation of the signal.