Merge pull request #5887 from Beep6581/waveform
Waveform, RGB parade, and vectorscopes
This commit is contained in:
@@ -17,16 +17,14 @@
|
||||
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include "improccoordinator.h"
|
||||
|
||||
#include "array2D.h"
|
||||
#include "cieimage.h"
|
||||
#include "color.h"
|
||||
#include "colortemp.h"
|
||||
#include "jaggedarray.h"
|
||||
#include "curves.h"
|
||||
#include "dcp.h"
|
||||
#include "iccstore.h"
|
||||
@@ -47,6 +45,9 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr int VECTORSCOPE_SIZE = 128;
|
||||
|
||||
using rtengine::Coord2D;
|
||||
Coord2D translateCoord(const rtengine::ImProcFunctions& ipf, int fw, int fh, int x, int y) {
|
||||
|
||||
@@ -129,6 +130,21 @@ ImProcCoordinator::ImProcCoordinator() :
|
||||
|
||||
histLRETI(256),
|
||||
|
||||
hist_lrgb_dirty(false),
|
||||
hist_raw_dirty(false),
|
||||
|
||||
vectorscopeScale(0),
|
||||
vectorscope_hc_dirty(false),
|
||||
vectorscope_hs_dirty(false),
|
||||
vectorscope_hc(VECTORSCOPE_SIZE, VECTORSCOPE_SIZE),
|
||||
vectorscope_hs(VECTORSCOPE_SIZE, VECTORSCOPE_SIZE),
|
||||
waveformScale(0),
|
||||
waveform_dirty(false),
|
||||
waveformRed(0, 0),
|
||||
waveformGreen(0, 0),
|
||||
waveformBlue(0, 0),
|
||||
waveformLuma(0, 0),
|
||||
|
||||
CAMBrightCurveJ(), CAMBrightCurveQ(),
|
||||
|
||||
rCurve(),
|
||||
@@ -348,6 +364,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
||||
}
|
||||
|
||||
imgsrc->getRAWHistogram(histRedRaw, histGreenRaw, histBlueRaw);
|
||||
hist_raw_dirty = !(hListener && hListener->updateHistogramRaw());
|
||||
|
||||
highDetailPreprocessComputed = highDetailNeeded;
|
||||
|
||||
@@ -1638,9 +1655,21 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
||||
imageListener->imageReady(params->crop);
|
||||
}
|
||||
|
||||
hist_lrgb_dirty = vectorscope_hc_dirty = vectorscope_hs_dirty = waveform_dirty = true;
|
||||
if (hListener) {
|
||||
updateLRGBHistograms();
|
||||
hListener->histogramChanged(histRed, histGreen, histBlue, histLuma, histToneCurve, histLCurve, histCCurve, /*histCLurve, histLLCurve,*/ histLCAM, histCCAM, histRedRaw, histGreenRaw, histBlueRaw, histChroma, histLRETI);
|
||||
if (hListener->updateHistogram()) {
|
||||
updateLRGBHistograms();
|
||||
}
|
||||
if (hListener->updateVectorscopeHC()) {
|
||||
updateVectorscopeHC();
|
||||
}
|
||||
if (hListener->updateVectorscopeHS()) {
|
||||
updateVectorscopeHS();
|
||||
}
|
||||
if (hListener->updateWaveform()) {
|
||||
updateWaveforms();
|
||||
}
|
||||
notifyHistogramChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1741,8 +1770,42 @@ void ImProcCoordinator::setScale(int prevscale)
|
||||
}
|
||||
|
||||
|
||||
void ImProcCoordinator::updateLRGBHistograms()
|
||||
void ImProcCoordinator::notifyHistogramChanged()
|
||||
{
|
||||
if (hListener) {
|
||||
hListener->histogramChanged(
|
||||
histRed,
|
||||
histGreen,
|
||||
histBlue,
|
||||
histLuma,
|
||||
histToneCurve,
|
||||
histLCurve,
|
||||
histCCurve,
|
||||
histLCAM,
|
||||
histCCAM,
|
||||
histRedRaw,
|
||||
histGreenRaw,
|
||||
histBlueRaw,
|
||||
histChroma,
|
||||
histLRETI,
|
||||
vectorscopeScale,
|
||||
vectorscope_hc,
|
||||
vectorscope_hs,
|
||||
waveformScale,
|
||||
waveformRed,
|
||||
waveformGreen,
|
||||
waveformBlue,
|
||||
waveformLuma
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImProcCoordinator::updateLRGBHistograms()
|
||||
{
|
||||
|
||||
if (!hist_lrgb_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int x1, y1, x2, y2;
|
||||
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
||||
@@ -1800,6 +1863,159 @@ void ImProcCoordinator::updateLRGBHistograms()
|
||||
}
|
||||
}
|
||||
|
||||
hist_lrgb_dirty = false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool ImProcCoordinator::updateVectorscopeHC()
|
||||
{
|
||||
if (!workimg || !vectorscope_hc_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int x1, y1, x2, y2;
|
||||
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
||||
|
||||
constexpr int size = VECTORSCOPE_SIZE;
|
||||
constexpr float norm_factor = size / (128.f * 655.36f);
|
||||
vectorscope_hc.fill(0);
|
||||
|
||||
vectorscopeScale = (x2 - x1) * (y2 - y1);
|
||||
|
||||
const std::unique_ptr<float[]> a(new float[vectorscopeScale]);
|
||||
const std::unique_ptr<float[]> b(new float[vectorscopeScale]);
|
||||
const std::unique_ptr<float[]> L(new float[vectorscopeScale]);
|
||||
ipf.rgb2lab(*workimg, x1, y1, x2 - x1, y2 - y1, L.get(), a.get(), b.get(), params->icm);
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel
|
||||
#endif
|
||||
{
|
||||
array2D<int> vectorscopeThr(size, size, ARRAY2D_CLEAR_DATA);
|
||||
#ifdef _OPENMP
|
||||
#pragma omp for nowait
|
||||
#endif
|
||||
for (int i = y1; i < y2; ++i) {
|
||||
for (int j = x1, ofs_lab = (i - y1) * (x2 - x1); j < x2; ++j, ++ofs_lab) {
|
||||
const int col = norm_factor * a[ofs_lab] + size / 2 + 0.5f;
|
||||
const int row = norm_factor * b[ofs_lab] + size / 2 + 0.5f;
|
||||
if (col >= 0 && col < size && row >= 0 && row < size) {
|
||||
vectorscopeThr[row][col]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef _OPENMP
|
||||
#pragma omp critical
|
||||
#endif
|
||||
{
|
||||
vectorscope_hc += vectorscopeThr;
|
||||
}
|
||||
}
|
||||
|
||||
vectorscope_hc_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImProcCoordinator::updateVectorscopeHS()
|
||||
{
|
||||
if (!workimg || !vectorscope_hs_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int x1, y1, x2, y2;
|
||||
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
||||
|
||||
constexpr int size = VECTORSCOPE_SIZE;
|
||||
vectorscope_hs.fill(0);
|
||||
|
||||
vectorscopeScale = (x2 - x1) * (y2 - y1);
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel
|
||||
#endif
|
||||
{
|
||||
array2D<int> vectorscopeThr(size, size, ARRAY2D_CLEAR_DATA);
|
||||
#ifdef _OPENMP
|
||||
#pragma omp for nowait
|
||||
#endif
|
||||
for (int i = y1; i < y2; ++i) {
|
||||
int ofs = (i * pW + x1) * 3;
|
||||
for (int j = x1; j < x2; ++j) {
|
||||
const float red = 257.f * workimg->data[ofs++];
|
||||
const float green = 257.f * workimg->data[ofs++];
|
||||
const float blue = 257.f * workimg->data[ofs++];
|
||||
float h, s, l;
|
||||
Color::rgb2hslfloat(red, green, blue, h, s, l);
|
||||
const auto sincosval = xsincosf(2.f * RT_PI_F * h);
|
||||
const int col = s * sincosval.y * (size / 2) + size / 2;
|
||||
const int row = s * sincosval.x * (size / 2) + size / 2;
|
||||
if (col >= 0 && col < size && row >= 0 && row < size) {
|
||||
vectorscopeThr[row][col]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef _OPENMP
|
||||
#pragma omp critical
|
||||
#endif
|
||||
{
|
||||
vectorscope_hs += vectorscopeThr;
|
||||
}
|
||||
}
|
||||
|
||||
vectorscope_hs_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImProcCoordinator::updateWaveforms()
|
||||
{
|
||||
if (!workimg) {
|
||||
// free memory
|
||||
waveformRed.free();
|
||||
waveformGreen.free();
|
||||
waveformBlue.free();
|
||||
waveformLuma.free();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!waveform_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int x1, y1, x2, y2;
|
||||
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
||||
int waveform_width = waveformRed.getWidth();
|
||||
|
||||
if (waveform_width != x2 - x1) {
|
||||
// Resize waveform arrays.
|
||||
waveform_width = x2 - x1;
|
||||
waveformRed(waveform_width, 256);
|
||||
waveformGreen(waveform_width, 256);
|
||||
waveformBlue(waveform_width, 256);
|
||||
waveformLuma(waveform_width, 256);
|
||||
}
|
||||
|
||||
// Start with zero.
|
||||
waveformRed.fill(0);
|
||||
waveformGreen.fill(0);
|
||||
waveformBlue.fill(0);
|
||||
waveformLuma.fill(0);
|
||||
|
||||
constexpr float luma_factor = 255.f / 32768.f;
|
||||
for (int i = y1; i < y2; i++) {
|
||||
int ofs = (i * pW + x1) * 3;
|
||||
float* L_row = nprevl->L[i] + x1;
|
||||
|
||||
for (int j = 0; j < waveform_width; j++) {
|
||||
waveformRed[workimg->data[ofs++]][j]++;
|
||||
waveformGreen[workimg->data[ofs++]][j]++;
|
||||
waveformBlue[workimg->data[ofs++]][j]++;
|
||||
waveformLuma[LIM<int>(L_row[j] * luma_factor, 0, 255)][j]++;
|
||||
}
|
||||
}
|
||||
|
||||
waveformScale = y2 - y1;
|
||||
waveform_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImProcCoordinator::getAutoWB(double& temp, double& green, double equal, double tempBias)
|
||||
@@ -2244,4 +2460,61 @@ void ImProcCoordinator::setHighQualComputed()
|
||||
highQualityComputed = true;
|
||||
}
|
||||
|
||||
void ImProcCoordinator::requestUpdateWaveform()
|
||||
{
|
||||
if (!hListener) {
|
||||
return;
|
||||
}
|
||||
bool updated = updateWaveforms();
|
||||
if (updated) {
|
||||
notifyHistogramChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ImProcCoordinator::requestUpdateHistogram()
|
||||
{
|
||||
if (!hListener) {
|
||||
return;
|
||||
}
|
||||
bool updated = updateLRGBHistograms();
|
||||
if (updated) {
|
||||
notifyHistogramChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ImProcCoordinator::requestUpdateHistogramRaw()
|
||||
{
|
||||
if (!hListener) {
|
||||
return;
|
||||
}
|
||||
// Don't need to actually update histogram because it is always
|
||||
// up-to-date.
|
||||
if (hist_raw_dirty) {
|
||||
hist_raw_dirty = false;
|
||||
notifyHistogramChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ImProcCoordinator::requestUpdateVectorscopeHC()
|
||||
{
|
||||
if (!hListener) {
|
||||
return;
|
||||
}
|
||||
bool updated = updateVectorscopeHC();
|
||||
if (updated) {
|
||||
notifyHistogramChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ImProcCoordinator::requestUpdateVectorscopeHS()
|
||||
{
|
||||
if (!hListener) {
|
||||
return;
|
||||
}
|
||||
bool updated = updateVectorscopeHS();
|
||||
if (updated) {
|
||||
notifyHistogramChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user