Skip to content

Commit 52faf74

Browse files
hodoulpdoug-walker
andauthored
Adsk Contrib - Fix RGB spline curve inversion when the slope is zero (#1539) (#1554)
* Fix RGBCurve inverse when slope=0 Signed-off-by: Doug Walker <[email protected]> * Add GPU fix and test Signed-off-by: Doug Walker <[email protected]> * Tweak var name Signed-off-by: Doug Walker <[email protected]> Co-authored-by: Patrick Hodoul <[email protected]> Signed-off-by: Patrick Hodoul <[email protected]> Co-authored-by: doug-walker <[email protected]>
1 parent e1b3ffe commit 52faf74

File tree

3 files changed

+102
-21
lines changed

3 files changed

+102
-21
lines changed

src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ void EstimateSlopes(const std::vector<GradingControlPoint> & ctrlPnts, std::vect
192192
{
193193
size_t j = i;
194194
float DL = secantLen[i];
195-
while ((j < numCtrlPnts - 2) && (fabs(secantSlope[j + 1] - secantSlope[j]) < 1e-6f))
195+
while ((j < numCtrlPnts - 2) && (std::fabs(secantSlope[j + 1] - secantSlope[j]) < 1e-6f))
196196
{
197197
DL += secantLen[j + 1];
198198
j++;
@@ -255,7 +255,7 @@ void FitSpline(const std::vector<GradingControlPoint> & ctrlPnts,
255255
}
256256
else
257257
{
258-
if (fabs(aa) > fabs(bb))
258+
if (std::fabs(aa) > std::fabs(bb))
259259
{
260260
ksi = xi_pl1 + aa * del_x / (slopes[i + 1] - slopes[i]);
261261
}
@@ -471,7 +471,7 @@ void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st,
471471
st.newLine() << "{";
472472
st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets];";
473473
st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 2];";
474-
st.newLine() << " return (x - C) / B + knStart;";
474+
st.newLine() << " return abs(B) < 1e-5 ? knStart : (x - C) / B + knStart;";
475475
st.newLine() << "}";
476476

477477
st.newLine() << "else if (x >= knEndY)";
@@ -483,7 +483,7 @@ void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st,
483483
st.newLine() << " float t = knEnd - kn;";
484484
st.newLine() << " float slope = 2. * A * t + B;";
485485
st.newLine() << " float offs = ( A * t + B ) * t + C;";
486-
st.newLine() << " return (x - offs) / slope + knEnd;";
486+
st.newLine() << " return abs(slope) < 1e-5 ? knEnd : (x - offs) / slope + knEnd;";
487487
st.newLine() << "}";
488488

489489
// else
@@ -580,7 +580,7 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const
580580
{
581581
const float B = m_coefsArray[coefsOffs + coefsSets];
582582
const float C = m_coefsArray[coefsOffs + coefsSets * 2];
583-
return (y - C) / B + knStart;
583+
return std::fabs(B) < 1e-5f ? knStart : (y - C) / B + knStart;
584584
}
585585
else if (y >= knEndY)
586586
{
@@ -591,7 +591,7 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const
591591
const float t = knEnd - kn;
592592
const float slope = 2.f * A * t + B;
593593
const float offs = (A * t + B) * t + C;
594-
return (y - offs) / slope + knEnd;
594+
return std::fabs(slope) < 1e-5f ? knEnd : (y - offs) / slope + knEnd;
595595
}
596596
else
597597
{

tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpCPU_tests.cpp

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,14 @@ OCIO_ADD_TEST(GradingRGBCurveOpCPU, log)
176176
OCIO_CHECK_NO_THROW(op = OCIO::GetGradingRGBCurveCPURenderer(gcc));
177177
OCIO_CHECK_ASSERT(op);
178178

179-
const long num_samples = 2;
179+
constexpr long num_samples = 2;
180180
float res[4 * num_samples]{ 0.f };
181181

182-
const float input_32f[] = {
182+
constexpr float input_32f[] = {
183183
-0.2f, 0.2f, 0.5f, 0.0f,
184184
0.8f, 1.0f, 2.0f, 0.5f };
185185

186-
const float expected_32f[] = {
186+
constexpr float expected_32f[] = {
187187
0.25306581f, 0.35779659f, 0.98416632f, 0.0f,
188188
1.09451043f, 1.54596428f, 1.78067802f, 0.5f };
189189

@@ -219,14 +219,14 @@ OCIO_ADD_TEST(GradingRGBCurveOpCPU, log_partial_identity)
219219
OCIO_CHECK_NO_THROW(op = OCIO::GetGradingRGBCurveCPURenderer(gcc));
220220
OCIO_CHECK_ASSERT(op);
221221

222-
const long num_samples = 2;
222+
constexpr long num_samples = 2;
223223
float res[4 * num_samples]{ 0.f };
224224

225225
float input_32f[] = {
226226
-0.2f, 0.2f, 0.5f, 0.0f,
227227
0.8f, 1.0f, 2.0f, 0.5f };
228228

229-
const float expected_32f[] = {
229+
constexpr float expected_32f[] = {
230230
-0.2f, 0.15779659f, 0.5f, 0.0f,
231231
0.8f, 1.34596419f, 2.0f, 0.5f };
232232

@@ -264,14 +264,14 @@ OCIO_ADD_TEST(GradingRGBCurveOpCPU, monotonic)
264264
OCIO_CHECK_NO_THROW(op = OCIO::GetGradingRGBCurveCPURenderer(gcc));
265265
OCIO_CHECK_ASSERT(op);
266266

267-
const long num_samples = 2;
267+
constexpr long num_samples = 2;
268268
float res[4 * num_samples]{ 0.f };
269269

270270
float input_32f[] = {
271271
0.8f, 0.2f, 0.5f, 0.0f,
272272
0.9f, 1.0f, 2.0f, 0.5f };
273273

274-
const float expected_32f[] = {
274+
constexpr float expected_32f[] = {
275275
0.52230538f, 0.2f, 0.5f, 0.0f,
276276
0.68079938f, 1.0f, 2.0f, 0.5f };
277277

@@ -311,14 +311,14 @@ OCIO_ADD_TEST(GradingRGBCurveOpCPU, lin_bypass)
311311
OCIO_CHECK_NO_THROW(op = OCIO::GetGradingRGBCurveCPURenderer(gcc));
312312
OCIO_CHECK_ASSERT(op);
313313

314-
const long num_samples = 2;
314+
constexpr long num_samples = 2;
315315
float res[4 * num_samples]{ 0.f };
316316

317317
float input_32f[] = {
318318
-8.f, -3.f, -1.f, 0.0f,
319319
1.f, 2.5f, 4.0f, 0.5f };
320320

321-
const float expected_32f[] = {
321+
constexpr float expected_32f[] = {
322322
-8.50508935f, -6.37181915f, -3.01264257f, 0.0f,
323323
1.95205522f, 4.76796850f, 5.76796850f, 0.5f };
324324

@@ -357,14 +357,14 @@ OCIO_ADD_TEST(GradingRGBCurveOpCPU, lin)
357357
OCIO_CHECK_NO_THROW(op = OCIO::GetGradingRGBCurveCPURenderer(gcc));
358358
OCIO_CHECK_ASSERT(op);
359359

360-
const long num_samples = 2;
360+
constexpr long num_samples = 2;
361361
float res[4 * num_samples]{ 0.f };
362362

363363
float input_32f[] = {
364364
-0.003f, 0.02f, 0.09f, 0.0f,
365365
0.360f, 1.00f, 3.00f, 0.5f };
366366

367-
const float expected_32f[] = {
367+
constexpr float expected_32f[] = {
368368
-4.20784139e-03f, 1.26825221e-03f, 2.23983977e-02f, 0.0f,
369369
6.96706128e-01f, 4.79411018e+00f, 9.95152432e+00f, 0.5f };
370370

@@ -410,14 +410,33 @@ OCIO_ADD_TEST(GradingRGBCurveOpCPU, slopes)
410410
OCIO_CHECK_NO_THROW(op = OCIO::GetGradingRGBCurveCPURenderer(gcc));
411411
OCIO_CHECK_ASSERT(op);
412412

413-
const long num_samples = 1;
413+
constexpr long num_samples = 2;
414414
float input_32f[] = {
415-
-3.f, -1.f, 1.f, 0.5f };
415+
-3.f, -1.f, 1.f, 0.5f,
416+
-7.f, 0.f, 7.f, 1.0f };
416417

417418
// Test that the slopes were used (the values are significantly different without slopes).
418-
const float expected_32f[] = {
419-
-2.92582282f, 0.28069129f, 2.81987724f, 0.5f };
419+
constexpr float expected_32f[] = {
420+
-2.92582282f, 0.28069129f, 2.81987724f, 0.5f,
421+
-4.0f, 1.73250193f, 4.0f, 1.0f };
420422

421423
OCIO_CHECK_NO_THROW(op->apply(input_32f, input_32f, num_samples));
422424
ValidateImage(expected_32f, input_32f, num_samples, __LINE__);
425+
426+
// Test in inverse direction.
427+
428+
float rev_input_32f[] = {
429+
-2.92582282f, 0.28069129f, 2.81987724f, 0.5f,
430+
-7.0f, 1.73250193f, 7.0f, 1.0f };
431+
432+
constexpr float rev_expected_32f[] = {
433+
-3.f, -1.f, 1.f, 0.5f,
434+
-5.26017743f, 0.f, 4.67381243f, 1.0f };
435+
436+
gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE);
437+
438+
OCIO_CHECK_NO_THROW(op = OCIO::GetGradingRGBCurveCPURenderer(gcc));
439+
OCIO_CHECK_ASSERT(op);
440+
OCIO_CHECK_NO_THROW(op->apply(rev_input_32f, rev_input_32f, num_samples));
441+
ValidateImage(rev_expected_32f, rev_input_32f, num_samples, __LINE__);
423442
}

tests/gpu/GradingRGBCurveOp_test.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,68 @@ OCIO_ADD_GPU_TEST(GradingRGBCurve, style_lin_rev_dynamic)
105105
GradingRGBCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true);
106106
}
107107

108+
void GradingRGBSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic)
109+
{
110+
// Create an S-curve with 0 slope at each end.
111+
auto curve = OCIO::GradingBSplineCurve::Create({
112+
{-5.26017743f, -4.f},
113+
{-3.75502745f, -3.57868829f},
114+
{-2.24987747f, -1.82131329f},
115+
{-0.74472749f, 0.68124124f},
116+
{ 1.06145248f, 2.87457742f},
117+
{ 2.86763245f, 3.83406206f},
118+
{ 4.67381243f, 4.f}
119+
});
120+
float slopes[] = { 0.f, 0.55982688f, 1.77532247f, 1.55f, 0.8787017f, 0.18374463f, 0.f };
121+
for (size_t i = 0; i < 7; ++i)
122+
{
123+
curve->setSlope( i, slopes[i] );
124+
}
125+
126+
OCIO::ConstGradingBSplineCurveRcPtr m = curve;
127+
// Adjust scaling to ensure the test vector for the inverse hits the flat areas.
128+
auto scaling = OCIO::GradingBSplineCurve::Create({ { -5.f, 0.f }, { 5.f, 1.f } });
129+
OCIO::ConstGradingBSplineCurveRcPtr z = scaling;
130+
OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(m, m, m, z);
131+
132+
auto gc = OCIO::GradingRGBCurveTransform::Create(OCIO::GRADING_LOG);
133+
gc->setValue(curves);
134+
gc->setDirection(dir);
135+
if (dynamic)
136+
{
137+
gc->makeDynamic();
138+
}
139+
140+
test.setProcessor(gc);
141+
142+
test.setErrorThreshold(1.5e-4f);
143+
test.setExpectedMinimalValue(1.0f);
144+
test.setRelativeComparison(true);
145+
test.setTestWideRange(true);
146+
test.setTestInfinity(false);
147+
test.setTestNaN(true);
148+
}
149+
150+
OCIO_ADD_GPU_TEST(GradingRGBCurve, scurve_fwd)
151+
{
152+
GradingRGBSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false);
153+
}
154+
155+
OCIO_ADD_GPU_TEST(GradingRGBCurve, scurve_fwd_dynamic)
156+
{
157+
GradingRGBSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true);
158+
}
159+
160+
OCIO_ADD_GPU_TEST(GradingRGBCurve, scurve_rev)
161+
{
162+
GradingRGBSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false);
163+
}
164+
165+
OCIO_ADD_GPU_TEST(GradingRGBCurve, scurve_rev_dynamic)
166+
{
167+
GradingRGBSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true);
168+
}
169+
108170
namespace
109171
{
110172

0 commit comments

Comments
 (0)