ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvHistogramWindow.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "ecvHistogramWindow.h"
9 
10 #include "ecvPersistentSettings.h"
11 #include "ecvQCustomPlot.h"
12 
13 // CV_DB_LIB
14 #include <ecvColorScalesManager.h>
15 #include <ecvFileUtils.h>
16 #include <ecvGuiParameters.h>
17 
18 // CV_IO_LIB
19 #include <ImageFileFilter.h>
20 
21 // Qt
22 #include <QtCompat.h>
23 
24 #include <QCloseEvent>
25 #include <QFile>
26 #include <QFileDialog>
27 #include <QSettings>
28 
29 // System
30 #include <assert.h>
31 
32 // Gui
33 #include "ui_histogramDlg.h"
34 
35 ccHistogramWindow::ccHistogramWindow(QWidget* parent /*=0*/)
36  : QCustomPlot(parent),
37  m_titlePlot(nullptr),
38  m_colorScheme(USE_SOLID_COLOR),
39  m_solidColor(Qt::blue),
40  m_colorScale(ccColorScalesManager::GetDefaultScale()),
41  m_associatedSF(nullptr),
42  m_numberOfClassesCanBeChanged(false),
43  m_histogram(nullptr),
44  m_minVal(0),
45  m_maxVal(0),
46  m_maxHistoVal(0),
47  m_overlayCurve(nullptr),
48  m_vertBar(nullptr),
49  m_drawVerticalIndicator(false),
50  m_verticalIndicatorPositionPercent(0),
51  m_sfInteractionModes(SFInteractionMode::None),
52  m_axisDisplayOptions(AxisDisplayOption::All),
53  m_selectedItem(NONE),
54  m_areaLeft(nullptr),
55  m_areaLeftlastValue(std::numeric_limits<double>::quiet_NaN()),
56  m_areaRight(nullptr),
57  m_areaRightlastValue(std::numeric_limits<double>::quiet_NaN()),
58  m_arrowLeft(nullptr),
59  m_arrowLeftlastValue(std::numeric_limits<double>::quiet_NaN()),
60  m_arrowRight(nullptr),
61  m_arrowRightlastValue(std::numeric_limits<double>::quiet_NaN()),
62  m_lastMouseClick(0, 0),
63  m_refreshAfterResize(true) {
64  setWindowTitle("Histogram");
65  setFocusPolicy(Qt::StrongFocus);
66 
67  setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
68 
69  setAutoAddPlottableToLegend(false);
70  setAntialiasedElements(QCP::AntialiasedElement::aeAll);
71 
72  // default font for text rendering
73  m_renderingFont.setFamily(QString::fromUtf8("Arial"));
74  m_renderingFont.setBold(false);
75  // m_renderingFont.setWeight(75);
76 
77  // make ticks on bottom axis go outward
78  assert(xAxis && yAxis);
79  xAxis->setTickLength(0, 5);
80  xAxis->setSubTickLength(0, 3);
81  yAxis->setTickLength(0, 5);
82  yAxis->setSubTickLength(0, 3);
83 }
84 
86 
88  clearInternal();
89  refresh();
90 }
91 
92 void ccHistogramWindow::clearInternal() {
93  if (m_associatedSF) {
95  m_associatedSF = nullptr;
96  }
97 
98  m_histoValues.resize(0);
99  m_maxHistoVal = 0;
100 
101  m_curveValues.resize(0);
102 
104 }
105 
106 void ccHistogramWindow::setTitle(const QString& str) { m_titleStr = str; }
107 
108 void ccHistogramWindow::setAxisLabels(const QString& xLabel,
109  const QString& yLabel) {
110  if (xLabel.isNull()) {
111  xAxis->setVisible(false);
112  } else {
113  // set labels
114  xAxis->setLabel(xLabel);
115  xAxis->setVisible(true);
116  }
117 
118  if (xLabel.isNull()) {
119  yAxis->setVisible(false);
120  } else {
121  // set labels
122  yAxis->setLabel(yLabel);
123  yAxis->setVisible(true);
124  }
125 }
126 
128  unsigned initialNumberOfClasses /*=0*/,
129  bool numberOfClassesCanBeChanged /*=true*/,
130  bool showNaNValuesInGrey /*=true*/) {
131  if (sf && m_associatedSF != sf) {
133  m_associatedSF = sf;
135  }
136 
137  if (m_associatedSF) {
138  m_minVal = showNaNValuesInGrey ? m_associatedSF->getMin()
140  m_maxVal = showNaNValuesInGrey ? m_associatedSF->getMax()
142  m_numberOfClassesCanBeChanged = numberOfClassesCanBeChanged;
143  } else {
144  assert(false);
145  m_minVal = m_maxVal = 0;
147  }
148 
150  setNumberOfClasses(initialNumberOfClasses);
151 };
152 
153 void ccHistogramWindow::fromBinArray(const std::vector<unsigned>& histoValues,
154  double minVal,
155  double maxVal) {
156  try {
158  } catch (const std::bad_alloc&) {
159  CVLog::Warning("[ccHistogramWindow::fromBinArray] Not enough memory!");
160  return;
161  }
162  m_minVal = minVal;
163  m_maxVal = maxVal;
165 
166  // update max histogram value
168 }
169 
170 void ccHistogramWindow::setCurveValues(const std::vector<double>& curveValues) {
171  try {
172  m_curveValues = curveValues;
173  } catch (const std::bad_alloc&) {
175  "[ccHistogramWindow::setCurveValues] Not enough memory!");
176  }
177 }
178 
180  // clear any existing histogram
181  m_histoValues.resize(0);
182 
183  if (!m_associatedSF) {
184  assert(false);
185  CVLog::Error(
186  "[ccHistogramWindow::computeBinArrayFromSF] Need an associated "
187  "SF!");
188  return false;
189  }
190 
191  if (binCount == 0) {
192  assert(false);
193  CVLog::Error(
194  "[ccHistogramWindow::computeBinArrayFromSF] Invalid number of "
195  "classes!");
196  return false;
197  }
198 
199  // shortcut: same number of classes than the SF own histogram!
200  if (binCount == m_associatedSF->getHistogram().size()) {
201  try {
203  } catch (const std::bad_alloc&) {
205  "[ccHistogramWindow::computeBinArrayFromSF] Not enough "
206  "memory!");
207  return false;
208  }
209  return true;
210  }
211 
212  //(try to) create new array
213  try {
214  m_histoValues.resize(binCount, 0);
215  } catch (const std::bad_alloc&) {
217  "[ccHistogramWindow::computeBinArrayFromSF] Not enough "
218  "memory!");
219  return false;
220  }
221 
222  double range = m_maxVal - m_minVal;
223  if (range > 0.0) {
224  unsigned count = m_associatedSF->currentSize();
225  double step = range / static_cast<double>(binCount);
226  for (unsigned i = 0; i < count; ++i) {
227  double val = static_cast<double>(m_associatedSF->getValue(i));
228 
229  // we ignore values outside of [m_minVal,m_maxVal] (works fro NaN
230  // values as well)
231  if (/*ccScalarField::ValidValue(val) &&*/ val >= m_minVal &&
232  val <= m_maxVal) {
233  size_t bin =
234  static_cast<size_t>(floor((val - m_minVal) / step));
235  ++m_histoValues[std::min(bin, binCount - 1)];
236  }
237  }
238  } else {
240  }
241 
242  return true;
243 }
244 
246  unsigned m_maxHistoVal = 0;
247 
248  for (size_t i = 0; i < m_histoValues.size(); ++i) {
250  }
251 
252  return m_maxHistoVal;
253 }
254 
256  if (n == 0) {
257  // invalid parameter
258  assert(false);
259  return;
260  }
261 
262  if (n == m_histoValues.size()) {
263  // nothing to do
264  return;
265  }
266 
267  if (m_associatedSF) {
268  // dynamically recompute histogram values
270  }
271 
272  // update max histogram value
274 }
275 
279  int histoSize = static_cast<int>(m_histoValues.size());
280 
281  // DGM: the bars will be redrawn only if we delete and recreate the
282  // graph?!
284 
285  QVector<double> keyData(histoSize);
286  QVector<double> valueData(histoSize);
287  QVector<QColor> colors(histoSize);
288 
289  for (int i = 0; i < histoSize; ++i) {
290  // we take the 'normalized' value at the middle of the class
291  double normVal = (static_cast<double>(i) + 0.5) / histoSize;
292 
293  keyData[i] = m_minVal + normVal * (m_maxVal - m_minVal);
294  valueData[i] = m_histoValues[i];
295 
296  const ecvColor::Rgb* col = m_associatedSF->getColor(
297  static_cast<ScalarType>(keyData[i]));
298  if (!col) // hidden values may have no associated color!
299  col = &ecvColor::lightGrey;
300  colors[i] = QColor(col->r, col->g, col->b);
301  }
302 
303  m_histogram->setData(keyData, valueData, colors);
304 
305  // rescaleAxes();
306  }
307 
308  replot(QCustomPlot::rpImmediateRefresh);
309 }
310 
311 void ccHistogramWindow::setSFInteractionMode(SFInteractionModes modes) {
312  m_sfInteractionModes = modes;
313 }
314 
315 void ccHistogramWindow::setAxisDisplayOption(AxisDisplayOptions axisOptions) {
316  m_axisDisplayOptions = axisOptions;
317 }
318 
319 void ccHistogramWindow::setRefreshAfterResize(bool refreshAfterResize) {
320  m_refreshAfterResize = refreshAfterResize;
321 }
322 
324  // set ranges appropriate to show data
325  double minVal = m_minVal;
326  double maxVal = m_maxVal;
328  double minSat = m_associatedSF->saturationRange().min();
329  double maxSat = m_associatedSF->saturationRange().max();
330  minVal = std::min(minVal, minSat);
331  maxVal = std::max(maxVal, maxSat);
332  }
333  xAxis->setRange(
334  minVal,
335  std::max(minVal + std::numeric_limits<ScalarType>::epsilon(),
336  maxVal));
337  yAxis->setRange(0, m_maxHistoVal);
338 
339  xAxis->setVisible(m_axisDisplayOptions.testFlag(AxisDisplayOption::XAxis));
340  yAxis->setVisible(m_axisDisplayOptions.testFlag(AxisDisplayOption::YAxis));
341 
342  if (!m_titleStr.isEmpty()) {
343  // add title layout element
344  if (!m_titlePlot) {
345  // add a row for the title
346  plotLayout()->insertRow(0);
347  } else {
348  // remove previous title
349  plotLayout()->remove(m_titlePlot);
350  m_titlePlot = nullptr;
351  }
352  m_titlePlot = new QCPTextElement(
353  this, QStringLiteral("%0 [%1 classes]")
354  .arg(m_titleStr,
355  QString::number(m_histoValues.size())));
356  // title font
357  m_renderingFont.setPointSize(ecvGui::Parameters().defaultFontSize);
358  m_titlePlot->setFont(m_renderingFont);
359  plotLayout()->addElement(0, 0, m_titlePlot);
360  }
361 
362  // clear previous display
363  m_histogram = nullptr;
364  m_vertBar = nullptr;
365  m_overlayCurve = nullptr;
366  m_areaLeft = nullptr;
367  m_areaRight = nullptr;
368  m_arrowLeft = nullptr;
369  m_arrowRight = nullptr;
370  this->clearGraphs();
371  this->clearPlottables();
372 
373  if (m_histoValues.empty()) return;
374 
375  // default color scale to be used for display
376  ccColorScale::Shared colorScale =
379 
380  // histogram
381  int histoSize = static_cast<int>(m_histoValues.size());
382  double totalSum = 0;
383  double partialSum = 0;
384  if (histoSize > 0) {
385  m_histogram = new QCPColoredBars(xAxis, yAxis);
386 
387  m_histogram->setWidth((m_maxVal - m_minVal) / histoSize);
388  m_histogram->setAntialiased(false);
389  m_histogram->setAntialiasedFill(false);
390 
391  QVector<double> keyData(histoSize);
392  QVector<double> valueData(histoSize);
393 
395  switch (colorScheme) {
396  case USE_SOLID_COLOR:
397  m_histogram->setBrush(QBrush(m_solidColor, Qt::SolidPattern));
398  m_histogram->setPen(QPen(m_solidColor));
399  break;
401  // nothing to do
402  break;
403  case USE_SF_SCALE:
405  // we use the SF's color scale
406  colorScale = m_associatedSF->getColorScale();
407  } else {
408  // we'll use the default one...
409  assert(false);
410  colorScheme = USE_CUSTOM_COLOR_SCALE;
411  }
412  break;
413  default:
414  assert(false);
415  colorScheme = USE_CUSTOM_COLOR_SCALE;
416  break;
417  }
418 
419  QVector<QColor> colors;
420  if (colorScheme != USE_SOLID_COLOR) {
421  colors.resize(histoSize);
422  }
423 
424  for (int i = 0; i < histoSize; ++i) {
425  // we take the 'normalized' value at the middle of the class
426  double normVal = (static_cast<double>(i) + 0.5) / histoSize;
427 
428  totalSum += m_histoValues[i];
429  if (normVal < m_verticalIndicatorPositionPercent) {
430  partialSum += m_histoValues[i];
431  }
432 
433  keyData[i] = m_minVal + normVal * (m_maxVal - m_minVal);
434  valueData[i] = m_histoValues[i];
435 
436  // import color for the current bin
437  if (colorScheme != USE_SOLID_COLOR) {
438  const ecvColor::Rgb* col = nullptr;
439  if (colorScheme == USE_SF_SCALE) {
440  // equivalent SF value
441  assert(m_associatedSF);
442  col = m_associatedSF->getColor(
443  static_cast<ScalarType>(keyData[i]));
444  } else if (colorScheme == USE_CUSTOM_COLOR_SCALE) {
445  // use default gradient
446  assert(colorScale);
447  col = colorScale->getColorByRelativePos(normVal);
448  }
449  if (!col) // hidden values may have no associated color!
450  {
451  col = &ecvColor::lightGrey;
452  }
453  colors[i] = QColor(col->r, col->g, col->b);
454  }
455  }
456 
457  if (!colors.isEmpty()) {
458  m_histogram->setData(keyData, valueData, colors);
459  } else {
460  m_histogram->setData(keyData, valueData);
461  }
462  }
463 
464  // overlay curve?
465  int curveSize = static_cast<int>(m_curveValues.size());
466  if (curveSize > 1) {
467  QVector<double> x(curveSize);
468  QVector<double> y(curveSize);
469 
470  double step = (m_maxVal - m_minVal) / (curveSize - 1);
471  for (int i = 0; i < curveSize; ++i) {
472  x[i] = m_minVal + (static_cast<double>(i) /*+0.5*/) * step;
473  y[i] = m_curveValues[i];
474  }
475 
476  // create graph and assign data to it:
477  m_overlayCurve = addGraph();
478  m_overlayCurve->setData(x, y);
479  m_overlayCurve->setName("OverlayCurve");
480 
481  // set pen color
482  const ecvColor::Rgb& col = ecvColor::darkGrey;
483  QPen pen(QColor(col.r, col.g, col.b));
484  m_overlayCurve->setPen(pen);
485 
486  // set width
488  }
489 
490  // sf interaction mode
493  const ccScalarField::Range& dispRange =
495 
496  m_areaLeft = new QCPHiddenArea(true, xAxis, yAxis);
497  m_areaLeft->setRange(dispRange.min(), dispRange.max());
500  : dispRange.start());
501 
502  m_areaRight = new QCPHiddenArea(false, xAxis, yAxis);
503  m_areaRight->setRange(dispRange.min(), dispRange.max());
506  : dispRange.stop());
507  }
508 
510  const ccScalarField::Range& satRange =
512 
513  m_arrowLeft = new QCPArrow(xAxis, yAxis);
514  m_arrowLeft->setRange(satRange.min(), satRange.max());
517  : satRange.start());
518  if (colorScale) {
519  const ecvColor::Rgb* col = colorScale->getColorByRelativePos(
520  m_associatedSF->symmetricalScale() ? 0.5 : 0,
522  if (col) {
523  m_arrowLeft->setColor(col->r, col->g, col->b);
524  }
525  }
526 
527  m_arrowRight = new QCPArrow(xAxis, yAxis);
528  m_arrowRight->setRange(satRange.min(), satRange.max());
531  : satRange.stop());
532  if (colorScale) {
533  const ecvColor::Rgb* col = colorScale->getColorByRelativePos(
535  if (col) {
536  m_arrowRight->setColor(col->r, col->g, col->b);
537  }
538  }
539  }
540  } else if (m_drawVerticalIndicator) // vertical hint
541  {
542  m_vertBar = new QCPBarsWithText(xAxis, yAxis);
543 
544  // now we can modify properties of vertBar
545  m_vertBar->setName("VertLine");
546  m_vertBar->setWidth(0 /*(m_maxVal - m_minVal) / histoSize*/);
547  m_vertBar->setBrush(QBrush(Qt::red));
548  m_vertBar->setPen(QPen(Qt::red));
549  m_vertBar->setAntialiasedFill(false);
550  QVector<double> keyData(1);
551  QVector<double> valueData(1);
552 
553  // horizontal position
554  keyData[0] = m_minVal +
556  valueData[0] = m_maxHistoVal;
557 
558  m_vertBar->setData(keyData, valueData);
559 
560  // precision (same as color scale)
561  int precision =
562  static_cast<int>(ecvGui::Parameters().displayedNumPrecision);
563  unsigned bin = static_cast<unsigned>(
565  QString valueStr = QString("bin %0").arg(bin);
566  m_vertBar->setText(valueStr);
567  valueStr =
568  QString("< %0 %").arg(100.0 * static_cast<double>(partialSum) /
569  static_cast<double>(totalSum),
570  0, 'f', 3);
571  m_vertBar->appendText(valueStr);
572  valueStr =
573  QString("val = %0")
574  .arg(m_minVal +
575  (m_maxVal - m_minVal) *
577  0, 'f', precision);
578  m_vertBar->appendText(valueStr);
580  }
581 
582  // rescaleAxes();
583 
584  // redraw
585  replot();
586 }
587 
589  m_areaLeftlastValue = val;
590  if (m_areaLeft && m_areaLeft->currentVal() != val) {
592 
593  if (m_associatedSF) {
594  // auto-update
595  m_associatedSF->setMinDisplayed(static_cast<ScalarType>(val));
596  refreshBars();
597  } else {
598  replot();
599  }
600 
601  emit sfMinDispValChanged(val);
602  }
603 }
604 
606  m_areaRightlastValue = val;
607  if (m_areaRight && m_areaRight->currentVal() != val) {
609 
610  if (m_associatedSF) {
611  // auto-update
612  m_associatedSF->setMaxDisplayed(static_cast<ScalarType>(val));
613  refreshBars();
614  } else {
615  replot();
616  }
617 
618  emit sfMaxDispValChanged(val);
619  }
620 }
621 
623  m_arrowLeftlastValue = val;
624  if (m_arrowLeft && m_arrowLeft->currentVal() != val) {
626 
627  if (m_associatedSF) {
628  // auto-update
629  m_associatedSF->setSaturationStart(static_cast<ScalarType>(val));
630  refreshBars();
631  } else {
632  replot();
633  }
634 
635  emit sfMinSatValChanged(val);
636  }
637 }
638 
640  m_arrowRightlastValue = val;
641  if (m_arrowRight && m_arrowRight->currentVal() != val) {
643 
644  if (m_associatedSF) {
645  // auto-update
646  m_associatedSF->setSaturationStop(static_cast<ScalarType>(val));
647  refreshBars();
648  } else {
649  replot();
650  }
651 
652  emit sfMaxSatValChanged(val);
653  }
654 }
655 
657  if (m_overlayCurve) {
658  int penWidth = std::max(w, h) / 200;
659  if (m_overlayCurve->pen().width() != penWidth) {
660  QPen pen = m_overlayCurve->pen();
661  pen.setWidth(penWidth);
662  m_overlayCurve->setPen(pen);
663  }
664  }
665 }
666 
668  QCustomPlot::resizeEvent(event);
669 
670  updateOverlayCurveWidth(event->size().width(), event->size().height());
671  if (m_refreshAfterResize) {
672  refresh();
673  }
674 }
675 
677  m_lastMouseClick = event->pos();
678 
679  if (m_sfInteractionModes) {
681  // check greyed areas (circles)
686  if (m_selectedItem == NONE)
688  else
690  }
691  }
692 
693  // check yellow triangles
695  (m_selectedItem == NONE)) {
699  if (m_selectedItem == NONE)
701  else
703  }
704  }
705  } else {
707  }
708 }
709 
711  if (event->buttons() & Qt::LeftButton) {
712  if (m_sfInteractionModes) {
713  QPoint mousePos = event->pos();
714  if (m_histogram) {
715  QRect rect = m_histogram->rect();
716  mousePos.setX(std::min(rect.x() + rect.width(),
717  std::max(rect.x(), mousePos.x())));
718  }
719 
720  switch (m_selectedItem) {
721  case NONE:
722  // nothing to do
723  break;
724  case LEFT_AREA:
725  if (m_areaLeft) {
726  double newValue = m_areaLeft->pixelToKey(mousePos.x());
727  if (m_areaRight)
728  newValue = std::min(newValue,
730  setMinDispValue(newValue);
731  }
732  break;
733  case RIGHT_AREA:
734  if (m_areaRight) {
735  double newValue = m_areaRight->pixelToKey(mousePos.x());
736  if (m_areaLeft)
737  newValue = std::max(newValue,
739  setMaxDispValue(newValue);
740  }
741  break;
742  case BOTH_AREAS: {
743  int dx = m_lastMouseClick.x() - mousePos.x();
744  if (dx < -2) {
745  // going to the right
747  // call the same method again
749  return;
750  } else if (dx > 2) {
751  // going to the left
753  // call the same method again
755  return;
756  }
757  // else: nothing we can do right now!
758  } break;
759  case LEFT_ARROW:
760  if (m_arrowLeft) {
761  double newValue = m_arrowLeft->pixelToKey(mousePos.x());
762  if (m_arrowRight)
763  newValue = std::min(newValue,
765  setMinSatValue(newValue);
766  }
767  break;
768  case RIGHT_ARROW:
769  if (m_arrowRight) {
770  double newValue =
771  m_arrowRight->pixelToKey(mousePos.x());
772  if (m_arrowLeft)
773  newValue = std::max(newValue,
775  setMaxSatValue(newValue);
776  }
777  break;
778  case BOTH_ARROWS: {
779  int dx = m_lastMouseClick.x() - mousePos.x();
780  if (dx < -2) {
781  // going to the right
783  // call the same method again
785  return;
786  } else if (dx > 2) {
787  // going to the left
789  // call the same method again
791  return;
792  }
793  // else: nothing we can do right now!
794  } break;
795  default:
796  assert(false);
797  break;
798  }
799  } else {
800  if (m_histogram && !m_histoValues.empty()) {
801  QRect roi = m_histogram->rect();
802  if (roi.contains(event->pos(), false)) {
804 
805  int verticalIndicatorPosition =
806  (static_cast<int>(m_histoValues.size()) *
807  (event->x() - roi.x())) /
808  roi.width();
810  static_cast<double>(verticalIndicatorPosition) /
811  m_histoValues.size();
812 
813  refresh();
814  }
815  }
816  }
817  } else {
818  event->ignore();
819  }
820 }
821 
822 void ccHistogramWindow::wheelEvent(QWheelEvent* e) {
824  e->ignore();
825  return;
826  }
827 
828  if (qtCompatWheelEventDelta(e) < 0) {
829  if (m_histoValues.size() > 4) {
830  setNumberOfClasses(std::max<size_t>(4, m_histoValues.size() - 4));
831  refresh();
832  }
833  } else // if (qtCompatWheelEventDelta(e) > 0)
834  {
835  setNumberOfClasses(m_histoValues.size() + 4);
836  refresh();
837  }
838 
839  e->accept();
840 }
841 
843  : QDialog(parent, Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
844  m_win(new ccHistogramWindow(this)),
845  m_gui(new Ui_HistogramDialog) {
846  m_gui->setupUi(this);
847 
848  auto hboxLayout = new QHBoxLayout;
849 
850  hboxLayout->setContentsMargins(0, 0, 0, 0);
851  hboxLayout->addWidget(m_win);
852 
853  m_gui->histoFrame->setLayout(hboxLayout);
854 
855  connect(m_gui->exportCSVToolButton, &QAbstractButton::clicked, this,
857  connect(m_gui->exportImageToolButton, &QAbstractButton::clicked, this,
859 }
860 
862 
863 // CSV file default separator
864 static const QChar s_csvSep(';');
865 
867  if (!m_win || m_win->histoValues().empty()) {
869  "[Histogram] Histogram has no associated values (can't save "
870  "file)");
871  return false;
872  }
873 
874  QFile file(filename);
875  if (!file.open(QFile::WriteOnly | QFile::Text)) {
877  QString("[Histogram] Failed to save histogram to file '%1'")
878  .arg(filename));
879  return false;
880  }
881 
882  QTextStream stream(&file);
883  stream.setRealNumberPrecision(12);
884  stream.setRealNumberNotation(QTextStream::FixedNotation);
885 
886  // header
887  stream << "Class; Value; Class start; Class end;" << QtCompat::endl;
888 
889  // data
890  {
891  const std::vector<unsigned>& histoValues = m_win->histoValues();
892  int histoSize = static_cast<int>(histoValues.size());
893  double step = (m_win->maxVal() - m_win->minVal()) / histoSize;
894  for (int i = 0; i < histoSize; ++i) {
895  double minVal = m_win->minVal() + i * step;
896  stream << i + 1; // class index
897  stream << s_csvSep;
898  stream << histoValues[i]; // class value
899  stream << s_csvSep;
900  stream << minVal; // min value
901  stream << s_csvSep;
902  stream << minVal + step; // max value
903  stream << s_csvSep;
904  stream << QtCompat::endl;
905  }
906  }
907 
908  file.close();
909 
910  CVLog::Print(QString("[Histogram] File '%1' saved").arg(filename));
911 
912  return true;
913 }
914 
916  if (!m_win) {
917  assert(false);
918  return;
919  }
920 
921  // persistent settings
922  QSettings settings;
923  settings.beginGroup(ecvPS::SaveFile());
924  QString currentPath =
926  .toString();
927 
928  currentPath += QString("/") + m_win->windowTitle() + ".csv";
929 
930  // ask for a filename
931  QString filename = QFileDialog::getSaveFileName(this, "Select output file",
932  currentPath, "*.csv");
933  if (filename.isEmpty()) {
934  // process cancelled by user
935  return;
936  }
937 
938  // save last saving location
939  settings.setValue(ecvPS::CurrentPath(), QFileInfo(filename).absolutePath());
940  settings.endGroup();
941 
942  // save file
944 }
945 
947  if (!m_win) {
948  assert(false);
949  return;
950  }
951 
952  // persistent settings
953  QSettings settings;
954  settings.beginGroup(ecvPS::SaveFile());
955  QString currentPath =
957  .toString();
958 
959  QString outputFilename = ImageFileFilter::GetSaveFilename(
960  "Select output file", m_win->windowTitle(), currentPath, this);
961 
962  if (outputFilename.isEmpty()) {
963  // process cancelled by user (or error)
964  return;
965  }
966 
967  // save current export path to persistent settings
968  settings.setValue(ecvPS::CurrentPath(),
969  QFileInfo(outputFilename).absolutePath());
970  settings.endGroup();
971 
972  // save the widget as an image file
973  QPixmap image = m_win->grab();
974  if (image.save(outputFilename)) {
975  CVLog::Print(QString("[Histogram] Image '%1' successfully saved")
976  .arg(outputFilename));
977  } else {
978  CVLog::Error(QString("Failed to save file '%1'").arg(outputFilename));
979  }
980 }
MouseEvent event
std::string filename
std::shared_ptr< core::Tensor > image
int width
int height
int count
double qtCompatWheelEventDelta(const QWheelEvent *event) noexcept
Definition: QtCompat.h:751
virtual void link()
Increase counter.
Definition: CVShareable.cpp:33
virtual void release()
Decrease counter and deletes object when 0.
Definition: CVShareable.cpp:35
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
static QString GetSaveFilename(const QString &dialogTitle, const QString &baseName, const QString &imageSavePath, QWidget *parentWidget=nullptr)
Helper: select an output image filename.
QCustomPlot: small arrows at the bottom.
void setColor(int r, int g, int b)
Sets triangle 'inside' color.
QCustomPlot: vertical bar with text along side.
void setText(QString text)
void setTextAlignment(bool left)
void appendText(QString text)
QCustomPlot: colored histogram.
QRect rect() const
void setData(const QVector< double > &key, const QVector< double > &value)
virtual void clearData()
QCustomPlot: greyed areas.
double pixelToKey(int pixX) const
Converts a pixel value (X) to the equivalent key.
void setRange(double minVal, double maxVal)
virtual bool isSelectable(QPoint click) const
void setCurrentVal(double val)
double currentVal() const
QSharedPointer< ccColorScale > Shared
Shared pointer type.
Definition: ecvColorScale.h:74
Color scales manager/container.
static ccColorScale::Shared GetDefaultScale(DEFAULT_SCALES scale=BGYR)
Returns a pre-defined color scale (static shortcut)
virtual ~ccHistogramWindowDlg()
Destructor.
void onExportToImage()
When the export to Image file button is pressed.
ccHistogramWindowDlg(QWidget *parent=0)
Default constructor.
ccHistogramWindow * m_win
void onExportToCSV()
When the export to CSV file button is pressed.
bool exportToCSV(QString filename) const
Exports histogram to a CSV file.
Ui_HistogramDialog * m_gui
Associated widgets.
Histogram widget.
HISTOGRAM_COLOR_SCHEME m_colorScheme
Color scheme.
void refreshBars()
Updates the histogram bars only.
void setMinDispValue(double)
void setTitle(const QString &str)
Sets title.
void setCurveValues(const std::vector< double > &curveValues)
Sets overlay curve values.
void sfMaxSatValChanged(double)
const std::vector< unsigned > & histoValues() const
Returns the current histogram bins.
virtual ~ccHistogramWindow()
Destructor.
double minVal() const
Returns the current histogram min value.
void resizeEvent(QResizeEvent *event)
QCPHiddenArea * m_areaRight
Right greyed area.
unsigned getMaxHistoVal()
Returns current maximum bin size.
void sfMinSatValChanged(double)
void wheelEvent(QWheelEvent *event)
QPoint m_lastMouseClick
Last mouse click.
QCPBarsWithText * m_vertBar
ccHistogramWindow(QWidget *parent=0)
Default constructor.
void refresh()
Updates the display.
void setColorScheme(HISTOGRAM_COLOR_SCHEME scheme)
Sets how the gradient bars should be colored.
void fromSF(ccScalarField *sf, unsigned initialNumberOfClasses=0, bool numberOfClassesCanBeChanged=true, bool showNaNValuesInGrey=true)
Computes histogram from a scalar field.
ccScalarField * m_associatedSF
Associated scalar field.
ccColorScale::Shared m_colorScale
Gradient color scale.
void sfMinDispValChanged(double)
AxisDisplayOptions m_axisDisplayOptions
void fromBinArray(const std::vector< unsigned > &histoValues, double minVal, double maxVal)
void setAxisDisplayOption(AxisDisplayOptions axisOptions)
void setRefreshAfterResize(bool refreshAfterResize)
SELECTABLE_ITEMS m_selectedItem
Currently selected item.
QCPHiddenArea * m_areaLeft
Left greyed area.
void setNumberOfClasses(size_t n)
Changes the current number of classes.
double m_verticalIndicatorPositionPercent
QCPGraph * m_overlayCurve
Overlay curve.
void mouseMoveEvent(QMouseEvent *event)
void setSFInteractionMode(SFInteractionModes modes)
Enables SF interaction mode.
QCPArrow * m_arrowRight
Right arrow.
void sfMaxDispValChanged(double)
bool computeBinArrayFromSF(size_t binCount)
Dynamically computes histogram bins from scalar field.
QCPColoredBars * m_histogram
std::vector< double > m_curveValues
void mousePressEvent(QMouseEvent *event)
double maxVal() const
Returns the current histogram max value.
QFont m_renderingFont
Rendering font.
QColor m_solidColor
Solid color.
void setMaxDispValue(double)
SFInteractionModes m_sfInteractionModes
Which SF interaction modes are enabled.
void clear()
Clears the display.
QCPArrow * m_arrowLeft
Left arrow.
std::vector< unsigned > m_histoValues
void setAxisLabels(const QString &xLabel, const QString &yLabel)
Sets axis labels.
QCPTextElement * m_titlePlot
void updateOverlayCurveWidth(int w, int h)
Updates overlay curve width depending on the widget display size.
Scalar field range structure.
ScalarType stop() const
ScalarType min() const
ScalarType max() const
ScalarType start() const
A scalar field associated to display-related parameters.
const ccColorScale::Shared & getColorScale() const
Returns associated color scale.
void setSaturationStop(ScalarType val)
Sets the value at which to stop color gradient.
void setMinDisplayed(ScalarType val)
Sets the minimum displayed value.
unsigned getColorRampSteps() const
Returns number of color ramp steps.
const Range & saturationRange() const
Access to the range of saturation values.
const Histogram & getHistogram() const
Returns associated histogram values (for display)
const Range & displayRange() const
Access to the range of displayed values.
const ecvColor::Rgb * getColor(ScalarType value) const
bool symmetricalScale() const
Returns whether the color scale s symmetrical or not.
void setSaturationStart(ScalarType val)
Sets the value at which to start color gradient.
void setMaxDisplayed(ScalarType val)
Sets the maximum displayed value.
ScalarType getMin() const
Returns the minimum value.
Definition: ScalarField.h:72
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
unsigned currentSize() const
Definition: ScalarField.h:100
ScalarType getMax() const
Returns the maximum value.
Definition: ScalarField.h:74
RGB color structure.
Definition: ecvColorTypes.h:49
static const ParamStruct & Parameters()
Returns the stored values of each parameter.
static const QString CurrentPath()
static const QString SaveFile()
double colors[3]
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
static const QChar s_csvSep(';')
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
constexpr utility::nullopt_t None
Definition: TensorKey.h:20
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb darkGrey(MAX/2, MAX/2, MAX/2)
constexpr Rgb blue(0, 0, MAX)
constexpr Rgb lightGrey(static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8))
QString defaultDocPath()
Shortcut for getting the documents location path.
Definition: ecvFileUtils.h:30
Definition: Eigen.h:85
unsigned displayedNumPrecision
Displayed numbers precision.
Definition: lsd.c:1170
double x
Definition: lsd.c:1173
double width
Definition: lsd.c:1172