Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAutoLevels Namespace Reference

This namespace contains functions to compute the levels adjustment parameters automatically from a histogram. More...

Classes

struct  ChannelHistogram
 Convenience class that associates a KisHistogram and a channel index. This is useful because setting a channel on the histogram mutates it. This way the functions can change the histogram channel by looking at the "channel" field. More...
 

Enumerations

enum  MidtonesAdjustmentMethod { MidtonesAdjustmentMethod_None , MidtonesAdjustmentMethod_UseMedian , MidtonesAdjustmentMethod_UseMean }
 The different methods to enhance the mid tones. More...
 
enum  ShadowsAndHighlightsAdjustmentMethod { ShadowsAndHighlightsAdjustmentMethod_MonochromaticContrast , ShadowsAndHighlightsAdjustmentMethod_PerChannelContrast }
 The different methods to enhance the contrast. More...
 

Functions

QVector< KisLevelsCurveadjustMonochromaticContrast (ChannelHistogram lightnessHistogram, QVector< ChannelHistogram > &channelsHistograms, qreal shadowsClipping, qreal highlightsClipping, qreal maximumInputBlackAndWhiteOffset, MidtonesAdjustmentMethod midtonesAdjustmentMethod, qreal midtonesAdjustmentAmount, const QVector< qreal > &outputBlackPoints, const QVector< qreal > &outputWhitePoints, const QVector< qreal > &outputMidtones)
 Creates a KisLevelsCurve for every channel in "channelsHistograms". Computes the input black and white points from the intensity histogram. Computes the gamma from each channels histogram and "outputMidtones" if the method is not "None". The output black and white points are computed from "outputBlackPoints" and "outputWhitePoints".
 
QVector< KisLevelsCurveadjustPerChannelContrast (QVector< ChannelHistogram > &channelsHistograms, qreal shadowsClipping, qreal highlightsClipping, qreal maximumInputBlackAndWhiteOffset, MidtonesAdjustmentMethod midtonesAdjustmentMethod, qreal midtonesAdjustmentAmount, const QVector< qreal > &outputBlackPoints, const QVector< qreal > &outputWhitePoints, const QVector< qreal > &outputMidtones)
 Creates a KisLevelsCurve for every channel in "channelsHistograms". Computes the input black and white points from each channels histogram separately. Computes the gamma from each channels histogram and "outputMidtones" if the method is not "None". The output black and white points are computed from "outputBlackPoints" and "outputWhitePoints".
 
QPair< KoColor, KoColorgetDarkestAndWhitestColors (const KisPaintDeviceSP device, qreal shadowsClipping, qreal highlightsClipping)
 Finds the darkest and whitest colors in the device having into account the clipping.
 
qreal getGamma (qreal blackPoint, qreal whitePoint, qreal inputIntensity, qreal outputIntensity)
 Computes a gamma value that "moves" the input midpoint towards the output midpoint.
 
QPair< qreal, qreal > getInputBlackAndWhitePoints (ChannelHistogram histogram, qreal shadowsClipping, qreal highlightsClipping)
 Takes a reference histogram (luma, lightness) and computes the black and white points to maximize the contrast having into account the clipping.
 
QPair< qreal, qreal > getMeanAndMedian (ChannelHistogram histogram, qreal begin, qreal end)
 

Detailed Description

This namespace contains functions to compute the levels adjustment parameters automatically from a histogram.

Enumeration Type Documentation

◆ MidtonesAdjustmentMethod

The different methods to enhance the mid tones.

Enumerator
MidtonesAdjustmentMethod_None 
MidtonesAdjustmentMethod_UseMedian 
MidtonesAdjustmentMethod_UseMean 

Definition at line 39 of file KisAutoLevels.h.

◆ ShadowsAndHighlightsAdjustmentMethod

The different methods to enhance the contrast.

Enumerator
ShadowsAndHighlightsAdjustmentMethod_MonochromaticContrast 
ShadowsAndHighlightsAdjustmentMethod_PerChannelContrast 

Definition at line 31 of file KisAutoLevels.h.

Function Documentation

◆ adjustMonochromaticContrast()

QVector< KisLevelsCurve > KRITAIMAGE_EXPORT KisAutoLevels::adjustMonochromaticContrast ( ChannelHistogram lightnessHistogram,
QVector< ChannelHistogram > & channelsHistograms,
qreal shadowsClipping,
qreal highlightsClipping,
qreal maximumInputBlackAndWhiteOffset,
MidtonesAdjustmentMethod midtonesAdjustmentMethod,
qreal midtonesAdjustmentAmount,
const QVector< qreal > & outputBlackPoints,
const QVector< qreal > & outputWhitePoints,
const QVector< qreal > & outputMidtones )

Creates a KisLevelsCurve for every channel in "channelsHistograms". Computes the input black and white points from the intensity histogram. Computes the gamma from each channels histogram and "outputMidtones" if the method is not "None". The output black and white points are computed from "outputBlackPoints" and "outputWhitePoints".

Parameters
lightnessHistogramhistogram to compute the input black and white points from
channelsHistogramslist of histograms to compute the gammas from. This is also used to know the number of output levels infos
shadowsClippingA normalized percentage that is used to know how many samples should be clipped on the shadows side of the histogram by the input black point
highlightsClippingA normalized percentage that is used to know how many samples should be clipped on the highlights side of the histogram by the input white point
maximumInputBlackAndWhiteOffsetA maximum value for the input black and white points. The black point won't be greater than this, and the white point won't be lesser than 1 - this
midtonesAdjustmentMethodThe method used to get the gamma
midtonesAdjustmentAmountThe strength of the midtone adjustment
outputBlackPointsThe output black points used in each levels info
outputWhitePointsThe output white points used in each levels info
outputMidtonesThe desired output midtone values for the gamma adjustment
Returns
A list of levels infos containing parameters for the levels adjustment

Definition at line 138 of file KisAutoLevels.cpp.

148{
149 Q_ASSERT(lightnessHistogram.histogram);
150 Q_ASSERT(lightnessHistogram.channel >= 0);
151 Q_ASSERT(lightnessHistogram.channel < lightnessHistogram.histogram->producer()->channels().size());
152 Q_ASSERT(outputBlackPoints.size() == channelsHistograms.size());
153 Q_ASSERT(outputWhitePoints.size() == channelsHistograms.size());
154 Q_ASSERT(outputMidtones.size() == channelsHistograms.size());
155
156 QVector<KisLevelsCurve> levelsCurves;
157
158 const QPair<qreal, qreal> inputBlackAndWhitePoints =
159 getInputBlackAndWhitePoints(lightnessHistogram, shadowsClipping, highlightsClipping);
160 const qreal inputBlackPoint = qMin(maximumInputBlackAndWhiteOffset, inputBlackAndWhitePoints.first);
161 const qreal inputWhitePoint = qMax(1.0 - maximumInputBlackAndWhiteOffset, inputBlackAndWhitePoints.second);
162 const qreal linearMappingMidPoint = (inputBlackPoint + inputWhitePoint) / 2.0;
163
164 for (int i = 0; i < channelsHistograms.size(); ++i) {
165 ChannelHistogram &channelHistogram = channelsHistograms[i];
166
167 qreal gamma = 1.0;
168 if (midtonesAdjustmentMethod != MidtonesAdjustmentMethod_None &&
169 channelHistogram.histogram &&
170 channelHistogram.channel >= 0 &&
171 channelHistogram.channel < channelHistogram.histogram->producer()->channels().size()) {
172
173 qreal inputIntensity;
174 QPair<qreal, qreal> meanAndMedian = getMeanAndMedian(channelHistogram, inputBlackPoint, inputWhitePoint);
175
176 if (midtonesAdjustmentMethod == MidtonesAdjustmentMethod_UseMean) {
177 inputIntensity = meanAndMedian.first;
178 } else {
179 inputIntensity = meanAndMedian.second;
180 }
181
182 inputIntensity = linearMappingMidPoint + (inputIntensity - linearMappingMidPoint) * midtonesAdjustmentAmount;
183 gamma = getGamma(inputBlackPoint, inputWhitePoint, inputIntensity, outputMidtones[i]);
184 }
185
186 levelsCurves.append(
188 inputBlackPoint,
189 inputWhitePoint,
190 gamma,
191 outputBlackPoints[i],
192 outputWhitePoints[i]
193 )
194 );
195 }
196
197 return levelsCurves;
198}
KoHistogramProducer * producer()
This class holds the parameters for a levels adjustment. It is modeled after KisCubicCurve and has si...
virtual QList< KoChannelInfo * > channels()=0
QPair< qreal, qreal > getMeanAndMedian(ChannelHistogram histogram, qreal begin, qreal end)
qreal getGamma(qreal blackPoint, qreal whitePoint, qreal inputIntensity, qreal outputIntensity)
Computes a gamma value that "moves" the input midpoint towards the output midpoint.
QPair< qreal, qreal > getInputBlackAndWhitePoints(ChannelHistogram histogram, qreal shadowsClipping, qreal highlightsClipping)
Takes a reference histogram (luma, lightness) and computes the black and white points to maximize the...
Convenience class that associates a KisHistogram and a channel index. This is useful because setting ...

References KisAutoLevels::ChannelHistogram::channel, KoHistogramProducer::channels(), getGamma(), getInputBlackAndWhitePoints(), getMeanAndMedian(), KisAutoLevels::ChannelHistogram::histogram, MidtonesAdjustmentMethod_None, MidtonesAdjustmentMethod_UseMean, and KisHistogram::producer().

◆ adjustPerChannelContrast()

QVector< KisLevelsCurve > KRITAIMAGE_EXPORT KisAutoLevels::adjustPerChannelContrast ( QVector< ChannelHistogram > & channelsHistograms,
qreal shadowsClipping,
qreal highlightsClipping,
qreal maximumInputBlackAndWhiteOffset,
MidtonesAdjustmentMethod midtonesAdjustmentMethod,
qreal midtonesAdjustmentAmount,
const QVector< qreal > & outputBlackPoints,
const QVector< qreal > & outputWhitePoints,
const QVector< qreal > & outputMidtones )

Creates a KisLevelsCurve for every channel in "channelsHistograms". Computes the input black and white points from each channels histogram separately. Computes the gamma from each channels histogram and "outputMidtones" if the method is not "None". The output black and white points are computed from "outputBlackPoints" and "outputWhitePoints".

Parameters
channelsHistogramslist of histograms to compute the gammas from. This is also used to know the number of output levels infos
shadowsClippingA normalized percentage that is used to know how many samples should be clipped on the shadows side of the histogram by the input black point
highlightsClippingA normalized percentage that is used to know how many samples should be clipped on the highlights side of the histogram by the input white point
maximumInputBlackAndWhiteOffsetA maximum value for the input black and white points. The black point won't be greater than this, and the white point won't be lesser than 1 - this
midtonesAdjustmentMethodThe method used to get the gamma
midtonesAdjustmentAmountThe strength of the midtone adjustment
outputBlackPointsThe output black points used in each levels info
outputWhitePointsThe output white points used in each levels info
outputMidtonesThe desired output midtone values for the gamma adjustment
Returns
A list of levels infos containing parameters for the levels adjustment

Definition at line 200 of file KisAutoLevels.cpp.

209{
210 QVector<KisLevelsCurve> levelsCurves;
211
212 for (int i = 0; i < channelsHistograms.size(); ++i) {
213 QVector<ChannelHistogram> channelHistogram{channelsHistograms[i]};
214 levelsCurves.append(
215 adjustMonochromaticContrast(
216 channelHistogram[0],
217 channelHistogram,
218 shadowsClipping,
219 highlightsClipping,
220 maximumInputBlackAndWhiteOffset,
221 midtonesAdjustmentMethod,
222 midtonesAdjustmentAmount,
223 {outputBlackPoints[i]},
224 {outputWhitePoints[i]},
225 {outputMidtones[i]}
226 )
227 );
228 }
229
230 return levelsCurves;
231}

References adjustMonochromaticContrast().

◆ getDarkestAndWhitestColors()

QPair< KoColor, KoColor > KRITAIMAGE_EXPORT KisAutoLevels::getDarkestAndWhitestColors ( const KisPaintDeviceSP device,
qreal shadowsClipping,
qreal highlightsClipping )

Finds the darkest and whitest colors in the device having into account the clipping.

Returns
A pair containing the "darkest" and "lighter" colors

◆ getGamma()

qreal KRITAIMAGE_EXPORT KisAutoLevels::getGamma ( qreal blackPoint,
qreal whitePoint,
qreal inputIntensity,
qreal outputIntensity )

Computes a gamma value that "moves" the input midpoint towards the output midpoint.

Parameters
blackPointIf this gamma value will be part of a more complex levels adjustment, set its black point here. Set it to 0 otherwise. Since the gamma correction is applied after the linear mapping given by the black and white points in a levels adjustment, you have to provide those here to get the correct gamma
whitePointsame as with blackPoint but for the white point
inputIntensityThis is the intensity value that we want to map to the output value. Sensible values are the mean and the median
outputIntensityThis is the intensity value to which the input value will be mapped to after the gamma correction. Use 0.5 to neutralize the midtones
Returns
the gamma value that, when applied after the linear mapping given by the black and white points, will map the input intensity to the output intensity

Definition at line 119 of file KisAutoLevels.cpp.

120{
121 Q_ASSERT(blackPoint < whitePoint);
122 Q_ASSERT(inputIntensity >= blackPoint && inputIntensity <= whitePoint);
123
124 if (qFuzzyIsNull(outputIntensity)) {
125 return 0.01;
126 }
127 if (qFuzzyCompare(outputIntensity, 1.0)) {
128 return 10.0;
129 }
130
131 const qreal inputIntensityAfterLinearMapping =
132 (inputIntensity - blackPoint) / (whitePoint - blackPoint);
133
134 return qBound(0.01, log(inputIntensityAfterLinearMapping) / log(outputIntensity), 10.0);
135}
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)

References qFuzzyCompare(), and qFuzzyIsNull().

◆ getInputBlackAndWhitePoints()

QPair< qreal, qreal > KRITAIMAGE_EXPORT KisAutoLevels::getInputBlackAndWhitePoints ( ChannelHistogram histogram,
qreal shadowsClipping,
qreal highlightsClipping )

Takes a reference histogram (luma, lightness) and computes the black and white points to maximize the contrast having into account the clipping.

Returns
A pair containing the black and white points indices

Definition at line 54 of file KisAutoLevels.cpp.

57{
58 Q_ASSERT(histogram.histogram);
59 Q_ASSERT(histogram.channel >= 0 && histogram.channel < histogram.histogram->producer()->channels().size());
60
61 histogram.histogram->setChannel(histogram.channel);
62
63 int numberOfBins = histogram.histogram->producer()->numberOfBins();
64
65 Q_ASSERT(numberOfBins > 1);
66
67 const qreal totalNumberOfSamples = static_cast<qreal>(histogram.histogram->producer()->count());
68
69 // This basically integrates the probability mass function given by the
70 // histogram, from the left and the right, until the thresholds given by the
71 // clipping are reached, to obtain the black and white points
72
73 int blackPoint = 0;
74 qreal accumulator = 0.0;
75 for (int i = 0; i < numberOfBins; ++i) {
76 const qreal sampleCountForBin = static_cast<qreal>(histogram.histogram->getValue(i));
77 const qreal probability = sampleCountForBin / totalNumberOfSamples;
78
79 accumulator += probability;
80 if (accumulator > shadowsClipping) {
81 break;
82 }
83 blackPoint = i;
84 }
85
86 int whitePoint = numberOfBins - 1;
87 accumulator = 0.0;
88 for (int i = numberOfBins - 1; i >= 0; --i) {
89 const qreal sampleCountForBin = static_cast<qreal>(histogram.histogram->getValue(i));
90 const qreal probability = sampleCountForBin / totalNumberOfSamples;
91
92 accumulator += probability;
93 if (accumulator > highlightsClipping) {
94 break;
95 }
96 whitePoint = i;
97 }
98
99 if (whitePoint <= blackPoint) {
100 if (blackPoint + 1 == numberOfBins) {
101 whitePoint = blackPoint;
102 --blackPoint;
103 } else {
104 whitePoint = blackPoint + 1;
105 }
106 }
107
108 return
109 {
110 static_cast<qreal>(blackPoint) / static_cast<qreal>(numberOfBins),
111 static_cast<qreal>(whitePoint) / static_cast<qreal>(numberOfBins)
112 };
113}
void setChannel(qint32 channel)
quint32 getValue(quint8 i)
virtual qint32 numberOfBins()=0
virtual qint32 count()=0

References KisAutoLevels::ChannelHistogram::channel, KoHistogramProducer::channels(), KoHistogramProducer::count(), KisHistogram::getValue(), KisAutoLevels::ChannelHistogram::histogram, KoHistogramProducer::numberOfBins(), KisHistogram::producer(), and KisHistogram::setChannel().

◆ getMeanAndMedian()

QPair< qreal, qreal > KisAutoLevels::getMeanAndMedian ( ChannelHistogram histogram,
qreal begin,
qreal end )

Definition at line 18 of file KisAutoLevels.cpp.

19{
20 Q_ASSERT(histogram.histogram);
21 Q_ASSERT(histogram.channel >= 0 && histogram.channel < histogram.histogram->producer()->channels().size());
22
23 histogram.histogram->setChannel(histogram.channel);
24
25 const int numberOfBins = histogram.histogram->producer()->numberOfBins();
26 const int beginBin = static_cast<int>(begin * static_cast<qreal>(numberOfBins));
27 const int endBin = static_cast<int>(end * static_cast<qreal>(numberOfBins)) + 1;
28
29 qreal numberOfSamples = 0.0;
30 qreal meanSum = 0.0;
31
32 for (int i = beginBin; i < endBin; ++i) {
33 const qreal current = static_cast<qreal>(histogram.histogram->getValue(i));
34 numberOfSamples += current;
35 meanSum += current * static_cast<qreal>(i);
36 }
37
38 const qreal mean = meanSum / (numberOfSamples * static_cast<qreal>(numberOfBins));
39 qreal probabilityAccumulator = 0.0;
40 qreal median = 0.0;
41
42 for (int i = beginBin; i < endBin; ++i) {
43 const qreal probability = static_cast<qreal>(histogram.histogram->getValue(i)) / numberOfSamples;
44 probabilityAccumulator += probability;
45 if (probabilityAccumulator >= 0.5) {
46 median = static_cast<qreal>(i) / static_cast<qreal>(numberOfBins);
47 break;
48 }
49 }
50
51 return {mean, median};
52}

References KisAutoLevels::ChannelHistogram::channel, KoHistogramProducer::channels(), KisHistogram::getValue(), KisAutoLevels::ChannelHistogram::histogram, KoHistogramProducer::numberOfBins(), KisHistogram::producer(), and KisHistogram::setChannel().