Skip to content

Commit

Permalink
Merge pull request #27 from MrLixm/feat-nuke-drt
Browse files Browse the repository at this point in the history
Feat: Add AgXc Nuke DRT implementation
  • Loading branch information
MrLixm authored Jan 1, 2024
2 parents 603dd97 + d39e826 commit 8ad18f3
Show file tree
Hide file tree
Showing 13 changed files with 2,360 additions and 11 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ poetry.lock
# log files
*.log

# personal stuff
# project specific
_uses/
_outputs/
_outputs/
*.blink.src
*.blink.desc
1,421 changes: 1,421 additions & 0 deletions nuke/AgXcDRT.nk

Large diffs are not rendered by default.

64 changes: 64 additions & 0 deletions nuke/AgXcTonescale.nk
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
Group {
name AgXcTonescale
tile_color 0x363d5700
note_font_color 0xefefefff
addUserKnob {20 User l AgXcTonescale}
addUserKnob {26 txt_header l "" +STARTLINE T "<h1> AgXcTonescale</h1>"}
addUserKnob {26 txt_header_d l "" +STARTLINE T "<i>Input is expected to be log encoded.</i>"}
addUserKnob {26 spacer1 l "" +STARTLINE T "|"}
addUserKnob {6 use_gpu l "Use GPU when available" -STARTLINE}
use_gpu true
addUserKnob {26 "" +STARTLINE}
addUserKnob {7 min_exposure l "min Exposure (EV)" R -15 0}
min_exposure -10.5
addUserKnob {7 max_exposure l "max Exposure (EV)" R 0 15}
max_exposure 6.5
addUserKnob {26 "" +STARTLINE}
addUserKnob {18 x_pivot l "x pivot" R 0.1 0.9}
x_pivot {{"fabs(min_exposure / (max_exposure - (min_exposure)))"}}
addUserKnob {18 y_pivot l "y pivot" R 0.1 0.9}
y_pivot 0.5
addUserKnob {18 general_contrast l "general contrast" R 1.01 5}
general_contrast 2.0
addUserKnob {18 toe_power l "toe power" R 0 5}
toe_power 3
addUserKnob {18 shoulder_power l "shoulder power" R 0 5}
shoulder_power 3.25
addUserKnob {20 About}
addUserKnob {26 toolName l name T AgXcTonescale}
addUserKnob {26 toolVersion l version T 0.7.1}
addUserKnob {26 toolAuthor l author T "<a style=\"color: rgb(200,200,200);\" href=\"https://mrlixm.github.io/\">Liam Collod</a>"}
addUserKnob {26 toolDescription l description T "AgX tonescale curve algorithm."}
addUserKnob {26 toolUrl l url T "<a style=\"color: rgb(200,200,200);\" href=\"https://github.com/MrLixm/AgXc\">https://github.com/MrLixm/AgXc</a>"}
}
Input {
inputs 0
name image
xpos 0
ypos 0
}
BlinkScript {
inputs 1
recompileCount 2
ProgramGroup 1
KernelDescription "3 \"AgXTonescale\" iterate pixelWise 62d49b8fffe0e146cd680fd19017775b5375d9bfc4300747f537c54139d68f98 2 \"src\" Read Point \"dst\" Write Point 5 \"u_x_pivot\" Float 3 AAAAPwAAAD8AAAA/AAAAAA== \"u_y_pivot\" Float 3 AAAAPwAAAD8AAAA/AAAAAA== \"u_general_contrast\" Float 3 AAAAQAAAAEAAAABAAAAAAA== \"u_toe_power\" Float 3 AABAQAAAQEAAAEBAAAAAAA== \"u_shoulder_power\" Float 3 AABQQAAAUEAAAFBAAAAAAA== 5 \"u_x_pivot\" 3 1 Default \"u_y_pivot\" 3 1 Default \"u_general_contrast\" 3 1 Default \"u_toe_power\" 3 1 Default \"u_shoulder_power\" 3 1 Default 0"
kernelSource "// version 7\n// The tonescale curve for AgX\n// to apply on log-encoded imagery (unless u_log_convert is true)\n//\n// All equations are from Troy Sobotka and Jed Smith work :\n// https://github.com/sobotka/AgX-S2O3/blob/main/AgX.py\n\nkernel AgXTonescale : ImageComputationKernel<ePixelWise>\n\{\n Image<eRead, eAccessPoint, eEdgeClamped> src;\n Image<eWrite> dst;\n\n param:\n float3 u_x_pivot;\n float3 u_y_pivot;\n float3 u_general_contrast;\n float3 u_toe_power;\n float3 u_shoulder_power;\n\n void define() \{\n defineParam(u_x_pivot, \"u_x_pivot\", float3(0.5, 0.5, 0.5));\n defineParam(u_y_pivot, \"u_y_pivot\", float3(0.5, 0.5, 0.5));\n defineParam(u_general_contrast, \"u_general_contrast\", float3(2.0, 2.0, 2.0));\n defineParam(u_toe_power, \"u_toe_power\", float3(3.0, 3.0, 3.0));\n defineParam(u_shoulder_power, \"u_shoulder_power\", float3(3.25, 3.25, 3.25));\n \}\n\n void init() \{\}\n\n float equation_scale(\n float x_pivot, float y_pivot, float slope_pivot, float power\n )\{\n float a = pow(slope_pivot * x_pivot, -1.0 * power);\n float b = pow(slope_pivot * (x_pivot / y_pivot), power) - 1.0;\n return pow(a * b, -1.0 / power);\n \}\n\n float equation_hyperbolic(float x, float power)\{\n return x / pow(1.0 + pow(x, power), 1.0 / power);\n \}\n\n float equation_term(float x, float x_pivot, float slope_pivot, float scale)\{\n return (slope_pivot * (x - x_pivot)) / scale;\n \}\n\n float equation_curve(\n float value,\n float scale,\n float x_pivot,\n float y_pivot,\n float contrast,\n float toe_power,\n float shoulder_power\n )\{\n float a = equation_hyperbolic(\n equation_term(value, x_pivot, contrast, scale),\n toe_power\n );\n a = a * scale + y_pivot;\n\n float b = equation_hyperbolic(\n equation_term(value, x_pivot, contrast, scale),\n shoulder_power\n );\n b = b * scale + y_pivot;\n\n return scale < 0.0? a: b;\n\n \}\n\n float equation_full_curve(\n float value,\n float x_pivot,\n float y_pivot,\n float contrast,\n float toe_power,\n float shoulder_power\n )\{\n float scale_x_pivot = value >= x_pivot? 1.0 - x_pivot: x_pivot;\n float scale_y_pivot = value >= x_pivot? 1.0 - y_pivot: y_pivot;\n\n float toe_scale = equation_scale(\n scale_x_pivot,\n scale_y_pivot,\n contrast,\n toe_power\n );\n\n float shoulder_scale = equation_scale(\n scale_x_pivot,\n scale_y_pivot,\n contrast,\n shoulder_power\n );\n\n float scale = value >= x_pivot? shoulder_scale: -1.0 * toe_scale;\n\n return equation_curve(\n value,\n scale,\n x_pivot,\n y_pivot,\n contrast,\n toe_power,\n shoulder_power\n );\n \}\n\n void process(int2 pos) \{\n\n float4 rgba = src();\n\n float3 converted_rgb(rgba.x, rgba.y, rgba.z);\n\n // apply per-channel tonescale curve\n converted_rgb.x = equation_full_curve(\n converted_rgb.x,\n u_x_pivot.x,\n u_y_pivot.x,\n u_general_contrast.x,\n u_toe_power.x,\n u_shoulder_power.x\n );\n converted_rgb.y = equation_full_curve(\n converted_rgb.y,\n u_x_pivot.y,\n u_y_pivot.y,\n u_general_contrast.y,\n u_toe_power.y,\n u_shoulder_power.y\n );\n converted_rgb.z = equation_full_curve(\n converted_rgb.z,\n u_x_pivot.z,\n u_y_pivot.z,\n u_general_contrast.z,\n u_toe_power.z,\n u_shoulder_power.z\n );\n\n dst() = float4(\n converted_rgb.x,\n converted_rgb.y,\n converted_rgb.z,\n rgba.w\n );\n \}\n\};"
rebuild ""
AgXTonescale_u_x_pivot {{parent.x_pivot} {parent.x_pivot} {parent.x_pivot}}
AgXTonescale_u_y_pivot {{parent.y_pivot} {parent.y_pivot} {parent.y_pivot}}
AgXTonescale_u_general_contrast {{parent.general_contrast} {parent.general_contrast} {parent.general_contrast}}
AgXTonescale_u_toe_power {{parent.toe_power} {parent.toe_power} {parent.toe_power}}
AgXTonescale_u_shoulder_power {{parent.shoulder_power} {parent.shoulder_power} {parent.shoulder_power}}
format "2048 2048 0 0 2048 2048 1 square_2K"
rebuild_finalise ""
useGPUIfAvailable {{parent.use_gpu}}
name AgXTonescaleBlink
xpos 0
ypos 150
}
Output {
name Output1
xpos 0
ypos 300
}
end_group
130 changes: 122 additions & 8 deletions nuke/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,137 @@ Nuke native implementations of AgX.

# Content

- [Agx-tonescale.blink](Agx-tonescale.blink) : only the tonescale of AgX as a blink script.
| tool | description |
|-----------------------------------------------------------------------------------------------------------|--------------------------------------------------|
| [AgXcTonescale.nk](AgXcTonescale.nk) | tonescale algorithm of AgX (blink script based). |
| [AgXcDRT.nk](AgXcDRT.nk) | full image rendering pipeline for AgX |
| [PrimariesInset](https://github.com/MrLixm/Foundry_Nuke/blob/main/src/primaries_inset) (in external repo) | gamut remapping algorithm, referred as "inset" |

> [!NOTE]
> For the **inset** part of AgX you can check https://github.com/MrLixm/Foundry_Nuke/blob/main/src/primaries_inset

# Usage
# Instructions

## `Agx-tonescale.blink`
## installation

Common for all tools :

- Open the desired `.nk` file in GitHub or download it locally
- Copy to clipboard the whole content of the nk file
- Paste into any opened Nuke scene

## requirements

As of right now all tools are independent of each others and don't have external dependencies.

* Tools were developed for Nuke15 but MIGHT work on lower versions.
* Tools were developed on Windows but SHOULD work on other platforms.
* Tools use the following nuke features :
* blink script (compatible with non-commercial version >= 14.0)
* python for some internal components but static (i.e only triggered on user press)


## `AgXcTonescale.nk`

The tonescale is the per-channel "s-curve" algorithm that is remapping the luminance
range of its input data.

The algorithm is the same as per originally defined by Troy:
https://github.com/sobotka/AgX-S2O3/blob/daffcfa18edaa7172ce549cd25e80b7faadd8292/AgX.py#L192

![screenshot of a 2D plot of AgXcTonescale in Nuke](doc/img/AgXcTonescale-plot.png)
> PlotSlice node by Jed Smith
### input/output

The tonescale expect log-encoded data as input.

The initial formular to calcule the x and y pivot was :
### pivot

The initial formula to calcule the x and y pivot specified by Troy was:

```python
min_EV = -10
max_EV = +6.5
x_pivot = abs(min_EV / (max_EV - min_EV))
# x_pivot = 0.6060606
# >>> x_pivot = 0.6060606
y_pivot = 0.50
```
```

## `AgXcDRT.nk`

Encode "open-domain" data to display.

Algorithm is based on the original Troy implementation with various additions,
some personal, some from other AgX tinkerers (see credits).

For a full breakdown see my post on [Blender-Artist AgX thread.](https://blenderartists.org/t/feedback-development-filmic-baby-step-to-a-v2/1361663/2316)

![screenshot of AgXcDRT result in Nuke](doc/img/AgXcDRT-main.png)

### input/output

Expect "open domain" data as input, with a `linear BT.2020 D65` encoding.

Output a display-referred result bounds to the specified display that can be directly
previewed or written to disk without any more processing (_example: make sure
the nuke view-transform is disabled when viewing its output_).

### plot

It is possible to visualize a 2D "slice" plot of the process by checking "Show Plot".
A linear 0-1 ramp is being plotted and allow to visualize the effect of the tonescale
and other grading operations.

* The y axis is in [0-1] range and represent pixel "intensity".
* The x axis is a 2D slice of the ramp where lower values represent dark values
of the input and high values the white ones.

![screenshot of AgXcDRT plot option result in Nuke](doc/img/AgXcDRT-plot.png)

### tonescale

"Luminance mapping" of the image with additional grading possibilities to emulate
the color-shift of the analog film print process. Note the process MIGHT actually
have no similarity at all with the film print process but the name was found to be pertinent
and stayed, in lack of better term.

Original AgX implementation applied the tonescale a single time. This one can apply
it 2 times, producing an extra softness that can be comparable to the look
of analog film.

#### inset

Control the amount of chroma purity in bright values where higher values will
produce a less chroma-intense look.

The default value of 0.4 might be a bit strong.

#### contrast

Control the contrast of the 2 tonescale curves. It is interesting to see them
as working in tandem.

The `contrast second` only works when `Apply Double Tonescale` is of course checked .

#### print contrast

The tonescale section offer a creative control called "print contrast" that allow
to shift the tones of your image. The intended use is to use different R-G-B ratios
on the `print contrast` control, with the additional pivot control to refine further
the look (do not hesitate to check `Show Plot` to have an alternative representation
of your changes).


### display

Pick the target display the image should be intended to be displayed on.

# Developer

Check the [src/](src) directory.

# Credits

* Troy Sobotka: of course for the original AgX algorithm
* Jed Smith: [nuke-colortools](https://github.com/jedypod/nuke-colortools).
* flannelhead and other darktable developers: https://github.com/darktable-org/darktable/pull/15104
* EaryChow and anyone involved in the development of the Blender variant: https://blenderartists.org/t/feedback-development-filmic-baby-step-to-a-v2/1361663/
Binary file added nuke/doc/img/AgXcDRT-main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added nuke/doc/img/AgXcDRT-plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added nuke/doc/img/AgXcTonescale-plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8ad18f3

Please sign in to comment.