201 lines
8.1 KiB
C++
201 lines
8.1 KiB
C++
#include "stretchtoolbar.h"
|
|
#include <QVBoxLayout>
|
|
#include <QDebug>
|
|
#include <QToolButton>
|
|
#include "imageringlist.h"
|
|
|
|
const float BLACK_POINT_SIGMA = -2.8f;
|
|
const float MAD_TO_SIGMA = 1.4826f;
|
|
const float TARGET_BACKGROUND = 0.25f;
|
|
|
|
float MTF(float x, float m)
|
|
{
|
|
if(x < 0)return 0;
|
|
if(x > 1)return 1;
|
|
return ((m - 1) * x) / ((2 * m - 1) * x - m);
|
|
}
|
|
|
|
StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar"), parent)
|
|
{
|
|
setObjectName("stretchtoolbar");
|
|
QWidget *lum = new QWidget(this);
|
|
QVBoxLayout *vbox1 = new QVBoxLayout(lum);
|
|
m_stfSlider = new STFSlider(Qt::white, this);
|
|
vbox1->addWidget(m_stfSlider);
|
|
|
|
m_stfSliderR = new STFSlider(Qt::red, this);
|
|
m_stfSliderG = new STFSlider(Qt::green, this);
|
|
m_stfSliderB = new STFSlider(Qt::blue, this);
|
|
QWidget *rgb = new QWidget(this);
|
|
QVBoxLayout *vbox2 = new QVBoxLayout(rgb);
|
|
vbox2->setSpacing(0);
|
|
vbox2->addWidget(m_stfSliderR);
|
|
vbox2->addWidget(m_stfSliderG);
|
|
vbox2->addWidget(m_stfSliderB);
|
|
|
|
m_stack = new QStackedWidget(this);
|
|
m_stack->addWidget(lum);
|
|
m_stack->addWidget(rgb);
|
|
m_stack->setCurrentIndex(0);
|
|
addWidget(m_stack);
|
|
|
|
connect(m_stfSlider, &STFSlider::paramChanged, [this](float blackPoint, float midPoint, float whitePoint){
|
|
m_mtfParam.blackPoint[0] = m_mtfParam.blackPoint[1] = m_mtfParam.blackPoint[2] = blackPoint;
|
|
m_mtfParam.midPoint[0] = m_mtfParam.midPoint[1] = m_mtfParam.midPoint[2] = midPoint;
|
|
m_mtfParam.whitePoint[0] = m_mtfParam.whitePoint[1] = m_mtfParam.whitePoint[2] = whitePoint;
|
|
emit paramChanged(m_mtfParam);
|
|
});
|
|
connect(m_stfSliderR, &STFSlider::paramChanged, [this](float blackPoint, float midPoint, float whitePoint){
|
|
m_mtfParam.blackPoint[0] = blackPoint;
|
|
m_mtfParam.midPoint[0] = midPoint;
|
|
m_mtfParam.whitePoint[0] = whitePoint;
|
|
emit paramChanged(m_mtfParam);
|
|
});
|
|
connect(m_stfSliderG, &STFSlider::paramChanged, [this](float blackPoint, float midPoint, float whitePoint){
|
|
m_mtfParam.blackPoint[1] = blackPoint;
|
|
m_mtfParam.midPoint[1] = midPoint;
|
|
m_mtfParam.whitePoint[1] = whitePoint;
|
|
emit paramChanged(m_mtfParam);
|
|
});
|
|
connect(m_stfSliderB, &STFSlider::paramChanged, [this](float blackPoint, float midPoint, float whitePoint){
|
|
m_mtfParam.blackPoint[2] = blackPoint;
|
|
m_mtfParam.midPoint[2] = midPoint;
|
|
m_mtfParam.whitePoint[2] = whitePoint;
|
|
emit paramChanged(m_mtfParam);
|
|
});
|
|
|
|
QAction *rgbStretch = addAction(QIcon(":/link.png"), tr("Linked channels"));
|
|
rgbStretch->setCheckable(true);
|
|
rgbStretch->setChecked(true);
|
|
connect(rgbStretch, &QAction::toggled, this, &StretchToolbar::unlinkStretch);
|
|
|
|
QAction *autoStretchButton = addAction(QIcon(":/nuke.png"), tr("Auto Stretch F12"));
|
|
autoStretchButton->setShortcut(Qt::Key_F12);
|
|
connect(autoStretchButton, &QAction::triggered, this, &StretchToolbar::autoStretch);
|
|
|
|
QAction *resetButton = addAction(style()->standardIcon(QStyle::SP_DialogResetButton), tr("Reset Screen Transfer Function F11"));
|
|
resetButton->setShortcut(Qt::Key_F11);
|
|
connect(resetButton, &QAction::triggered, this, &StretchToolbar::resetMTF);
|
|
|
|
QAction *invertButton = addAction(QIcon(":/invert.png"), tr("Invert colors"));
|
|
invertButton->setCheckable(true);
|
|
connect(invertButton, &QAction::toggled, this, &StretchToolbar::invert);
|
|
|
|
QAction *falseColorButton = addAction(QIcon(":/falsecolor.png"), tr("False colors"));
|
|
falseColorButton->setCheckable(true);
|
|
connect(falseColorButton, &QAction::toggled, this, &StretchToolbar::falseColor);
|
|
|
|
m_debayer = addAction(QIcon(":/bayer.png"), tr("Debayer CFA"));
|
|
m_debayer->setCheckable(true);
|
|
connect(m_debayer, &QAction::toggled, this, &StretchToolbar::superPixel);
|
|
|
|
m_autoStretchOnLoad = addAction(QIcon(":/nuke_a.png"), tr("Apply auto stretch on load"));
|
|
m_autoStretchOnLoad->setCheckable(true);
|
|
}
|
|
|
|
void StretchToolbar::stretchImage(Image *img)
|
|
{
|
|
if(img && img->rawImage())
|
|
{
|
|
const RawImage::Stats &stats = img->rawImage()->imageStats();
|
|
int i = 0;
|
|
int ch = 1;
|
|
int o = 0;
|
|
if(m_stack->currentIndex() == 1 && img->rawImage()->channels() == 1 && m_debayer->isChecked())
|
|
{
|
|
i = 1;
|
|
ch = 4;
|
|
o = 1;
|
|
}
|
|
if(img->rawImage()->channels() >= 3)
|
|
ch = 3;
|
|
|
|
float bp2 = 0;
|
|
float mid2 = 0;
|
|
float max2 = 0;
|
|
for(; i < ch; i++)
|
|
{
|
|
double median, mad, max;
|
|
median = stats.m_median[i];
|
|
mad = stats.m_mad[i];
|
|
max = stats.m_max[i];
|
|
median /= img->rawImage()->norm();
|
|
bool a = median > 0.5 ? true : false;
|
|
mad /= img->rawImage()->norm();
|
|
max = 1.0f;// /= img->rawImage()->norm();
|
|
float bp = a || mad == 0.0f ? 0.0f : std::clamp(median + mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA, 0.0, 1.0);
|
|
if(a && mad != 0.0f)
|
|
max = std::clamp(median - mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA, 0.0, 1.0);
|
|
float mid = !a ? MTF(median - bp, TARGET_BACKGROUND) : MTF(TARGET_BACKGROUND, max - median);
|
|
m_mtfParam.blackPoint[i-o] = bp;
|
|
m_mtfParam.midPoint[i-o] = mid;// / max;
|
|
m_mtfParam.whitePoint[i-o] = max;
|
|
bp2 += bp;
|
|
mid2 += mid;
|
|
max2 = max > max2 ? max : max2;
|
|
}
|
|
if(ch == 1)
|
|
{
|
|
m_mtfParam.blackPoint[1] = m_mtfParam.blackPoint[2] = m_mtfParam.blackPoint[0];
|
|
m_mtfParam.midPoint[1] = m_mtfParam.midPoint[2] = m_mtfParam.midPoint[0];
|
|
m_mtfParam.whitePoint[1] = m_mtfParam.whitePoint[2] = m_mtfParam.whitePoint[0];
|
|
}
|
|
if(m_stack->currentIndex() == 0)
|
|
{
|
|
m_mtfParam.blackPoint[0] = m_mtfParam.blackPoint[1] = m_mtfParam.blackPoint[2] = bp2 / ch;
|
|
m_mtfParam.midPoint[0] = m_mtfParam.midPoint[1] = m_mtfParam.midPoint[2] = mid2 / ch;
|
|
m_mtfParam.whitePoint[0] = m_mtfParam.whitePoint[1] = m_mtfParam.whitePoint[2] = max2;
|
|
m_stfSlider->setMTFParams(m_mtfParam.blackPoint[0], m_mtfParam.midPoint[0], m_mtfParam.whitePoint[0]);
|
|
}
|
|
else
|
|
{
|
|
m_stfSliderR->setMTFParams(m_mtfParam.blackPoint[0], m_mtfParam.midPoint[0], m_mtfParam.whitePoint[0]);
|
|
m_stfSliderG->setMTFParams(m_mtfParam.blackPoint[1], m_mtfParam.midPoint[1], m_mtfParam.whitePoint[1]);
|
|
m_stfSliderB->setMTFParams(m_mtfParam.blackPoint[2], m_mtfParam.midPoint[2], m_mtfParam.whitePoint[2]);
|
|
}
|
|
emit paramChanged(m_mtfParam);
|
|
}
|
|
}
|
|
|
|
void StretchToolbar::resetMTF()
|
|
{
|
|
MTFParam params;
|
|
m_mtfParam = params;
|
|
if(m_stack->currentIndex() == 0)
|
|
{
|
|
m_stfSlider->setMTFParams(0, 0.5, 1);
|
|
}
|
|
else
|
|
{
|
|
m_stfSliderR->setMTFParams(0, 0.5, 1);
|
|
m_stfSliderG->setMTFParams(0, 0.5, 1);
|
|
m_stfSliderB->setMTFParams(0, 0.5, 1);
|
|
}
|
|
emit paramChanged(params);
|
|
}
|
|
|
|
void StretchToolbar::imageLoaded(Image *img)
|
|
{
|
|
if(m_autoStretchOnLoad->isChecked())
|
|
stretchImage(img);
|
|
}
|
|
|
|
void StretchToolbar::unlinkStretch(bool enable)
|
|
{
|
|
if(!enable)
|
|
{
|
|
m_stack->setCurrentIndex(1);
|
|
m_mtfParam.blackPoint[0] = m_stfSliderR->blackPoint(); m_mtfParam.midPoint[0] = m_stfSliderR->midPoint(); m_mtfParam.whitePoint[0] = m_stfSliderR->whitePoint();
|
|
m_mtfParam.blackPoint[1] = m_stfSliderG->blackPoint(); m_mtfParam.midPoint[1] = m_stfSliderG->midPoint(); m_mtfParam.whitePoint[1] = m_stfSliderG->whitePoint();
|
|
m_mtfParam.blackPoint[2] = m_stfSliderB->blackPoint(); m_mtfParam.midPoint[2] = m_stfSliderB->midPoint(); m_mtfParam.whitePoint[2] = m_stfSliderB->whitePoint();
|
|
}
|
|
else
|
|
{
|
|
m_stack->setCurrentIndex(0);
|
|
m_mtfParam.blackPoint[0] = m_mtfParam.blackPoint[1] = m_mtfParam.blackPoint[2] = m_stfSlider->blackPoint();
|
|
m_mtfParam.midPoint[0] = m_mtfParam.midPoint[1] = m_mtfParam.midPoint[2] = m_stfSlider->midPoint();
|
|
m_mtfParam.whitePoint[0] = m_mtfParam.whitePoint[1] = m_mtfParam.whitePoint[2] = m_mtfParam.whitePoint[2] = m_stfSlider->whitePoint();
|
|
}
|
|
emit paramChanged(m_mtfParam);
|
|
}
|