1#include "StretchTool.h"
14#include <QStackedWidget>
18#include <QInputDialog>
20#include "AdvancedStretchDialog.h"
23#include "CubeViewport.h"
25#include "IException.h"
27#include "MainWindow.h"
28#include "MdiCubeViewport.h"
29#include "RubberBandTool.h"
30#include "Statistics.h"
33#include "ViewportBuffer.h"
34#include "ViewportMainWindow.h"
37#include "CubeStretch.h"
67 QPushButton *hiddenButton =
new QPushButton();
68 hiddenButton->setVisible(
false);
69 hiddenButton->setDefault(
true);
84 if (parentMainWindow) {
85 connect(
this, SIGNAL(
warningSignal(std::string &,
const std::string)),
86 parentMainWindow, SLOT(displayWarning(std::string &,
const std::string &)));
113 action->setIcon(QPixmap(
toolIconDir() +
"/stretch_global.png"));
114 action->setToolTip(
"Stretch (S)");
115 action->setShortcut(Qt::Key_S);
117 "<b>Function:</b> Change the stretch range of the cube.\
118 <p><b>Shortcut:</b> S</p> ";
119 action->setWhatsThis(text);
148 QToolButton *butt =
new QToolButton(hbox);
149 butt->setAutoRaise(
true);
150 butt->setIconSize(QSize(22, 22));
151 butt->setIcon(QPixmap(
toolIconDir() +
"/regional_stretch-2.png"));
152 butt->setToolTip(
"Stretch");
154 "<b>Function:</b> Automatically compute min/max stretch using viewed \
155 pixels in the band(s) of the active viewport. That is, only pixels \
156 that are visible in the viewport are used. \
157 If the viewport is in RGB color all three bands will be stretched. \
158 <p><b>Shortcut:</b> Ctrl+R</p> \
159 <p><b>Mouse:</b> Left click \
160 <p><b>Hint:</b> Left click and drag for a local stretch. Uses only \
161 pixels in the red marquee</p>";
162 butt->setWhatsThis(text);
174 "<b>Function:</b> Selecting the color will allow the appropriate \
175 min/max to be seen and/or edited in text fields to the right.";
190 "<b>Function:</b> Select the minimum & maximum value types to \
191 set the stretch to. The four options are: \
192 <p>- Default: Min and max values are set to the \
193 0.5 and 99.5 percentiles, respectively. \
194 <p>- Best: The better of the absolute min/max or the \
195 Chebyshev min/max. The better value is considered the value \
196 closest to the mean. \
197 <p>- Absolute: The absolute min/max value of all valid pixels. \
198 <p>- Chebyshev: The min/max value such that a certain percentage \
199 of data will fall within K standard deviations of the average \
200 (Chebyshev's Theorem). It can be used to obtain a value that \
201 does not include statistical outliers.";
212 QDoubleValidator *dval =
new QDoubleValidator(hbox);
217 "<b>Function:</b> Shows the current minimum pixel value. Pixel values \
218 below minimum are shown as black. Pixel values above the maximum \
219 are shown as white or the highest intensity of red/green/blue \
220 if in color. Pixel values between the minimum and maximum are stretched \
221 linearly between black and white (or color component). \
222 <p><b>Hint:</b> You can manually edit the minimum but it must be \
223 less than the maximum.";
233 "<b>Function:</b> Shows the current maximum pixel value. Pixel values \
234 below minimum are shown as black. Pixel values above the maximum \
235 are shown as white or the highest intensity of red/green/blue \
236 if in color. Pixel values between the minimum and maximum are stretched \
237 linearly between black and white (or color component). \
238 <p><b>Hint:</b> You can manually edit the maximum but it must be \
239 greater than the minimum";
253 copyAll->setIcon(QPixmap(
toolIconDir() +
"/copy_stretch.png"));
254 copyAll->setText(
"to All Viewports");
257 copyMenu->addAction(copyAll);
264 m_copyButton->setPopupMode(QToolButton::MenuButtonPopup);
269 "<b>Function:</b> Copy the current stretch to all the \
270 active viewports. Or use the drop down menu to copy the current stretch \
271 to all the bands in the active viewport. \
272 <p><b>Hint:</b> Can reset the stretch to an automaticaly computed \
273 stretch by using the 'Reset' stretch button option. </p>";
277 currentView->setText(
"Active Viewport");
278 currentView->setIcon(QPixmap(
toolIconDir() +
"/global_stretch.png"));
279 globalMenu->addAction(currentView);
280 connect(currentView, SIGNAL(triggered(
bool)),
this, SLOT(
stretchGlobal()));
283 globalAll->setText(
"All Viewports");
284 globalMenu->addAction(globalAll);
288 globalBands->setText(
"All Bands");
289 globalMenu->addAction(globalBands);
300 "<b>Function:</b> Reset the stretch to be automatically computed "
301 "using the statisics from the entire image. Use the drop down menu "
302 "to reset the stretch for all the bands in the active viewport or "
303 "to reset the stretch for all the viewports.";
306 QPushButton *advancedButton =
new QPushButton(
"Advanced");
311 "<b>Function:</b> While this button is pressed down, the visible stretch "
312 "will be the automatically computed stretch using the statisics from the "
313 "entire image. The original stretch is restored once you let up on this "
320 QPushButton *saveToCubeButton =
new QPushButton(
"Save");
321 connect(saveToCubeButton, SIGNAL(clicked(
bool)),
this, SLOT(
saveStretchToCube()));
323 QPushButton *deleteFromCubeButton =
new QPushButton(
"Delete");
324 connect(deleteFromCubeButton, SIGNAL(clicked(
bool)),
this, SLOT(
deleteFromCube()));
326 QPushButton *loadStretchButton =
new QPushButton(
"Restore");
329 QHBoxLayout *layout =
new QHBoxLayout(hbox);
330 layout->setMargin(0);
338 layout->addWidget(advancedButton);
343 layout->addWidget(saveToCubeButton);
344 layout->addWidget(deleteFromCubeButton);
345 layout->addWidget(loadStretchButton);
347 layout->addStretch();
348 hbox->setLayout(layout);
425 bluStretch, bluHist);
444 for (objIter=lab->
beginObject(); objIter<lab->endObject(); objIter++) {
445 if (objIter->name() ==
"Stretch") {
446 PvlKeyword tempKeyword = objIter->findKeyword(
"Name");
447 int bandNumber = int(objIter->findKeyword(
"BandNumber"));
448 if (cvp->
grayBand() == bandNumber) {
449 QString tempName = tempKeyword[0];
450 namelist.append(tempName);
456 int redBandNumber = cvp->
redBand();
458 int blueBandNumber = cvp->
blueBand();
462 for (objIter=lab->
beginObject(); objIter<lab->endObject(); objIter++) {
463 if (objIter->name() ==
"Stretch") {
464 PvlKeyword tempKeyword = objIter->findKeyword(
"Name");
465 int bandNumber = int(objIter->findKeyword(
"BandNumber"));
466 if (bandNumber == redBandNumber || bandNumber == greenBandNumber
467 || bandNumber == blueBandNumber) {
468 QString tempName = tempKeyword[0];
469 if (tempNameMap.contains(tempName)) {
470 tempNameMap[tempName].append(bandNumber);
474 tempNameMap[tempName] = {bandNumber};
480 while (i != tempNameMap.constEnd()) {
481 if (i.value().contains(redBandNumber) && i.value().contains(greenBandNumber) &&
482 i.value().contains(blueBandNumber) ){
483 namelist.append(i.key());
492 if (namelist.size() >=1) {
493 stretchName = QInputDialog::getItem((
QWidget *)parent(), tr(
"Load Stretch"),
494 tr(
"Name of Stretch to Load:"), namelist, 0,
498 QMessageBox::information((
QWidget *)parent(),
"Information",
499 "There are no saved stretches to restore.");
515 std::vector<PvlKeyword> keywordValueRed;
516 keywordValueRed.push_back(
PvlKeyword(
"BandNumber", QString::number(cvp->
redBand())));
518 std::vector<PvlKeyword> keywordValueGreen;
521 std::vector<PvlKeyword> keywordValueBlue;
522 keywordValueBlue.push_back(
PvlKeyword(
"BandNumber", QString::number(cvp->
blueBand())));
562 for (objIter=lab->
beginObject(); objIter<lab->endObject(); objIter++) {
563 if (objIter->name() ==
"Stretch") {
564 PvlKeyword tempKeyword = objIter->findKeyword(
"Name");
565 int bandNumber = int(objIter->findKeyword(
"BandNumber"));
566 if (cvp->
grayBand() == bandNumber) {
567 QString tempName = tempKeyword[0];
568 namelist.append(tempName);
576 if (namelist.size() >= 1) {
577 toDelete = QInputDialog::getItem((
QWidget *)parent(), tr(
"Delete Stretch"),
578 tr(
"Name of Stretch to Delete:"), namelist, 0,
582 QMessageBox::information((
QWidget *)parent(),
"Information",
583 "There are no saved stretches to delete.");
593 QMessageBox::information((
QWidget *)parent(),
"Error",
594 "Cannot open cube read/write to delete stretch");
599 bool cubeDeleted = icube->
deleteBlob(toDelete,
"Stretch");
603 msgBox.setText(
"Stretch Could Not Be Deleted!");
604 msgBox.setInformativeText(
"A stretch with name: \"" + toDelete +
605 "\" Could not be found, so there was nothing to delete from the Cube.");
606 msgBox.setStandardButtons(QMessageBox::Ok);
607 msgBox.setIcon(QMessageBox::Critical);
628 for (objIter=lab->
beginObject(); objIter<lab->endObject(); objIter++) {
629 if (objIter->name() ==
"Stretch") {
630 PvlKeyword tempKeyword = objIter->findKeyword(
"Name");
631 QString tempName = tempKeyword[0];
632 namelist.append(tempName);
666 if (((redBand == greenBand) && !(redStretch == greenStretch)) ||
667 ((redBand == blueBand) && !(redBand == blueBand)) ||
668 ((greenBand == blueBand) && !(greenBand == blueBand))) {
669 QMessageBox::information((
QWidget *)parent(),
"Error",
"Sorry, cannot save RGB stretches which include the same band multiple times, but have different stretches for each");
676 tr(
"Enter a name to save the stretch as:"), QLineEdit::Normal,
681 if (namelist.contains(text)) {
683 msgBox.setText(tr(
"Stretch Name Already Exists!"));
684 msgBox.setInformativeText(
"A stretch pair with name: \"" + text +
"\" already exists and "
685 "the existing saved data will be overwritten. Are you sure you "
687 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
688 msgBox.setIcon(QMessageBox::Warning);
689 msgBox.setDefaultButton(QMessageBox::Cancel);
690 int ret = msgBox.exec();
693 case QMessageBox::Save:
695 case QMessageBox::Cancel:
713 QMessageBox::information((
QWidget *)parent(),
"Error",
"Cannot open cube read/write to save stretch");
734 icube->
write(stretch);
752 icube->
write(stretchBlob,
false);
756 stretchBlob = greenStretch.
toBlob();
757 icube->
write(stretchBlob,
false);
761 stretchBlob = blueStretch.
toBlob();
762 icube->
write(stretchBlob,
false);
806 bluStretch, bluHist);
838 if(cvp && cvp->
isGray()) {
879 QRect rect(0, 0, cvp->viewport()->width(), cvp->viewport()->height());
881 if(bandId == (
int)
Gray) {
894 if(bandId == (
int)
Red || bandId == (
int)
All) {
905 if(bandId == (
int)
Green || bandId == (
int)
All) {
916 if(bandId == (
int)
Blue || bandId == (
int)
All) {
939 if(cvp == NULL)
return;
967 double min = 0, max = 0;
972 min = stretch.
Input(0);
984 min = rstretch.
Input(0);
988 min = gstretch.
Input(0);
992 min = bstretch.
Input(0);
1023 if(cvp == NULL)
return;
1063 if(cvp == NULL)
return;
1066 if (useMinMaxTypeSelection) {
1102 redStretch.
AddPair(max, 255.0);
1106 greenStretch.
AddPair(min, 0.0);
1107 greenStretch.
AddPair(max, 255.0);
1111 blueStretch.
AddPair(min, 0.0);
1112 blueStretch.
AddPair(max, 255.0);
1131 if(bandId == (
int)
Red) {
1134 else if(bandId == (
int)
Green) {
1137 else if(bandId == (
int)
Blue) {
1147 double selectedMin = 0;
1148 double selectedMax = 0;
1150 if (minMaxIndex == 0) {
1151 selectedMin = hist.
Percent(0.5);
1152 selectedMax = hist.
Percent(99.5);
1153 }
else if (minMaxIndex == 1) {
1157 }
else if (minMaxIndex == 2) {
1159 selectedMin = stats.
Minimum();
1160 selectedMax = stats.
Maximum();
1161 }
else if (minMaxIndex == 3) {
1167 QString qMin = QString::number(selectedMin);
1168 QString qMax = QString::number(selectedMax);
1197 if(cvp == NULL)
return;
1208 if(cvp == NULL)
return;
1244 if(cvp == NULL)
return;
1255 QRect rect(0, 0, cvp->viewport()->width(), cvp->viewport()->height());
1260 QString message =
"Cannot stretch while the cube is still loading";
1261 QMessageBox::warning((
QWidget *)parent(),
"Warning", message);
1275 if(cvp == NULL)
return;
1276 if(!rubberBandTool()->isValid())
return;
1278 QRect rubberBandRect = rubberBandTool()->rectangle();
1280 if(rubberBandRect.width() == 0 || rubberBandRect.height() == 0)
return;
1326 "Unknown stretch band",
1344 if(cvp == NULL)
return;
1350 if(s == Qt::RightButton) {
1367 rubberBandTool()->enable(RubberBandTool::RectangleMode);
1368 rubberBandTool()->setDrawActiveViewportOnly(
true);
1379 if(cvp == NULL)
return;
1431 if(thisViewport == NULL)
return;
1487 if(stretch.
Pairs() == 0) {
1488 stretch.
AddPair(-DBL_MAX, 0.0);
1489 stretch.
AddPair(DBL_MAX, 255.0);
1510 else if(band ==
Green) {
1514 else if(band ==
Blue) {
1529 stretch.
AddPair(-DBL_MAX, 0.0);
1530 stretch.
AddPair(DBL_MAX, 255.0);
1549 for(
int line = 0; line < cube->
lineCount(); line++) {
1572 "Cannot stretch while the cube is still loading",
1576 QRect dataArea = QRect(buffer->
bufferXYRect().intersected(rect));
1579 for(
int y = dataArea.top();
1580 !dataArea.isNull() && y <= dataArea.bottom();
1584 for(
int x = dataArea.left(); x < dataArea.right(); x++) {
1604 double min,
double max) {
1608 for(
int line = 0; line < cube->
lineCount(); line++) {
1645 QRect rect,
double min,
double max) {
1646 QRect dataArea = QRect(buffer->
bufferXYRect().intersected(rect));
1651 for(
int y = dataArea.top(); !dataArea.isNull() && y <= dataArea.bottom(); y++) {
1653 hist.
AddData(&line.front() + (dataArea.left() - buffer->
bufferXYRect().left()), dataArea.width());
1662 std::string msg =
"Insufficient data Min [" + sMin +
"], Max [" + sMax +
"]";
1663 msg +=
" in the stretch area.";
Buffer for containing a three dimensional section of an image.
void SetBasePosition(const int start_sample, const int start_line, const int start_band)
This method is used to set the base position of the shape buffer.
double * DoubleBuffer() const
Returns the value of the shape buffer.
IO Handler for Isis Cubes.
bool deleteBlob(QString BlobName, QString BlobType)
This method will delete a blob label object from the cube as specified by the Blob type and name.
CubeStretch readCubeStretch(QString name="CubeStretch", const std::vector< PvlKeyword > keywords=std::vector< PvlKeyword >()) const
Read a Stretch from a cube.
bool isReadOnly() const
Test if the opened cube is read-only, that is write operations will fail if this is true.
PixelType pixelType() const
void read(Blob &blob, const std::vector< PvlKeyword > keywords=std::vector< PvlKeyword >()) const
This method will read data from the specified Blob object.
void write(Blob &blob, bool overwrite=true)
This method will write a blob of data (e.g.
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
void reopen(QString access="r")
This method will reopen an isis sube for reading or reading/writing.
Stores stretch information for a cube.
void setBandNumber(int bandNumber)
Set the band number for the stretch.
void setName(QString name)
Set the Stretch name.
Isis::Blob toBlob() const
Serialize the CubeStretch to a Blob.
Widget to display Isis cubes for qt apps.
void stretchGray(const QString &string)
Apply stretch pairs to gray band.
void stretchBlue(const QString &string)
Apply stretch pairs to blue bands.
CubeStretch grayStretch() const
Return the gray band stretch.
CubeStretch redStretch() const
Return the red band stretch.
ViewportBuffer * grayBuffer()
Returns the gray viewport buffer (Will be NULL if in RGB mode.)
void stretchKnownGlobal()
List<Tool *> p This stretches to the global stretch.
void stretchGreen(const QString &string)
Apply stretch pairs to green bands.
void stretchRed(const QString &string)
Apply stretch pairs to red bands.
CubeStretch greenStretch() const
Return the green band stretch.
void forgetStretches()
Resets all remembered stretches.
ViewportBuffer * greenBuffer()
Returns the green viewport buffer (Will be NULL if in Gray mode.)
ViewportBuffer * redBuffer()
Returns the red viewport buffer (Will be NULL if in Gray mode.)
void setAllBandStretches(Stretch stretch)
Sets a stretch for all bands.
ViewportBuffer * blueBuffer()
Returns the blue viewport buffer (Will be NULL if in Gray mode.)
CubeStretch blueStretch() const
Return the blue band stretch.
Container of a cube histogram.
double Percent(const double percent) const
Computes and returns the value at X percent of the histogram.
virtual void AddData(const double *data, const unsigned int count)
Add an array of doubles to the histogram counters.
@ Unknown
A type of error that cannot be classified as any of the other error types.
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
@ Programmer
This error is for when a programmer made an API call that was illegal.
Adds specific functionality to C++ strings.
Cube display widget for certain Isis MDI applications.
Container for cube-like labels.
A single keyword-value pair.
PvlObjectIterator beginObject()
Returns the index of the beginning object.
QList< PvlObject >::iterator PvlObjectIterator
The counter for objects.
This class is used to accumulate statistics on double arrays.
double ChebyshevMinimum(const double percent=99.5) const
This method returns a minimum such that X percent of the data will fall with K standard deviations of...
double Minimum() const
Returns the absolute minimum double found in all data passed through the AddData method.
double BestMinimum(const double percent=99.5) const
This method returns the better of the absolute minimum or the Chebyshev minimum.
BigInt ValidPixels() const
Returns the total number of valid pixels processed.
double ChebyshevMaximum(const double percent=99.5) const
This method returns a maximum such that X percent of the data will fall with K standard deviations of...
void AddData(const double *data, const unsigned int count)
Add an array of doubles to the accumulators and counters.
double BestMaximum(const double percent=99.5) const
This method returns the better of the absolute maximum or the Chebyshev maximum.
double Maximum() const
Returns the absolute maximum double found in all data passed through the AddData method.
void CopyPairs(const Stretch &other)
Copies the stretch pairs from another Stretch object, but maintains special pixel values.
void AddPair(const double input, const double output)
Adds a stretch pair to the list of pairs.
double Input(const int index) const
Returns the value of the input side of the stretch pair at the specified index.
void ClearPairs()
Clears the stretch pairs.
int Pairs() const
Returns the number of stretch pairs.
Reads and stores visible DN values.
const std::vector< double > & getLine(int line)
Retrieves a line from the buffer.
QRect bufferXYRect()
Returns a rect, in screen pixels, of the area this buffer covers.
bool hasEntireCube()
Method to see if the entire cube is in the buffer.
bool working()
This tests if queued actions exist in the viewport buffer.
This was called the Qisis MainWindow.
This is free and unencumbered software released into the public domain.
This is free and unencumbered software released into the public domain.
Namespace for the standard library.