-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from MrLixm/feat-whitebalance
Feat: add whitebalance tool
- Loading branch information
Showing
9 changed files
with
376 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[tool.poetry] | ||
name = "foundry-nuke" | ||
version = "0.4.0" | ||
version = "0.5.0" | ||
description = "Collection of script & resources for Foundry's Nuke software." | ||
authors = ["Liam Collod <[email protected]>"] | ||
readme = "README.md" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# White Balance | ||
|
||
Change the "white balance" of your source imagery. | ||
|
||
This is not a technical transform and was more designed as creative transformation tool. | ||
Usually whitebalancing is applied on undemosaiced raw camera data while | ||
this node will operate on already debayered data. | ||
|
||
 | ||
|
||
# Instructions | ||
|
||
## Install | ||
|
||
- Copy/paste the content of [WhiteBalance.nk](WhiteBalance.nk) in any nuke | ||
scene. | ||
- That's it | ||
|
||
## Requirements | ||
|
||
The tool use the following features : | ||
|
||
- blink script but works on non-commercial versions >= 14.0 | ||
|
||
## Reference | ||
|
||
Default value for temperature and tint try to match the value of the illuminant E | ||
which should produce a "no-operation" result. | ||
|
||
### temperature | ||
|
||
In Kelvin, lower produce a warmer look, higher a colder one. | ||
|
||
### tint | ||
|
||
Deviation from the planckian locus, referred usually as "D*uv*". Scaled x3000 | ||
for conveniency. Negatives values produce a pink shift while positive value a green tint. | ||
|
||
### intensity | ||
|
||
Strength of the effect applied as a simple linear interpolation. 0.0 means no | ||
effect. | ||
|
||
### show RGB coefficients | ||
|
||
Fill the image with the R-G-B coefficient used to whitebalance the image. Useful | ||
for debugging or alternative workflow: | ||
- provide a 1x1 pixels input in the node | ||
- check `show RGB coefficients` | ||
- reformat output to size of your image | ||
- merge previous step with the actual image using a `multiply` blend mode | ||
|
||
|
||
# Developer | ||
|
||
See the [./src/](./src) folder for development instructions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
Group { | ||
name WhiteBalance | ||
tile_color 0xca7c4200 | ||
addUserKnob {20 User} | ||
addUserKnob {26 txt_title l "" T "<h1>WhiteBalance</h1>"} | ||
addUserKnob {7 temperature l "temperature" t "CCT expressed in Kelvin" R 1000 15000} | ||
temperature 5495 | ||
addUserKnob {7 tint l "tint" t "Deviation from planckian locus. Negatives are pinkish, positives are greener." R -150 150} | ||
tint -13 | ||
addUserKnob {7 intensity l "intensity" t "Intensity of the balancing using linear interpolation." R 0.0 5.0} | ||
intensity 1.0 | ||
addUserKnob {6 show_coefficients l "show RGB coefficients" t "Fill the input with RGB coefficients instead of white-balancing." +STARTLINE} | ||
addUserKnob {20 About} | ||
addUserKnob {26 toolName l name T WhiteBalance} | ||
addUserKnob {26 toolVersion l version T 0.1.0} | ||
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 "Creative white-balancing with temperature and tint control."} | ||
addUserKnob {26 toolUrl l url T "<a style=\"color: rgb(200,200,200);\" href=\"https://github.com/MrLixm/Foundry_Nuke\">https://github.com/MrLixm/Foundry_Nuke</a>"} | ||
} | ||
Input { | ||
inputs 0 | ||
name Input1 | ||
xpos 0 | ||
} | ||
BlinkScript { | ||
inputs 1 | ||
recompileCount 2 | ||
ProgramGroup 1 | ||
KernelDescription "3 \"WhiteBalance\" iterate pixelWise 18eb8371345f90c6c9786195225c12c35bf1c5b11eeec618d305318349545608 2 \"src\" Read Point \"dst\" Write Point 4 \"u_show_coeffs\" Bool 1 AA== \"u_temperature\" Float 1 AACvRQ== \"u_tint\" Float 1 AAB4wQ== \"u_intensity\" Float 1 AACAPw== 4 \"u_show_coeffs\" 1 1 Default \"u_temperature\" 1 1 Default \"u_tint\" 1 1 Default \"u_intensity\" 1 1 Default 0" | ||
kernelSource "// version 2\n//\n// References :\n// - \[2] Ohno, Yoshi (2014). Practical Use and Calculation of CCT and Duv. LEUKOS, 10(1), 47-55. doi:10.1080/15502724.2014.839020\n// - \[3] https://en.wikipedia.org/wiki/Planckian_locus#Approximation\n// - \[4] SMPTE Recommended Practice - Derivation of Basic Television Color Equations https://ieeexplore.ieee.org/document/7291155\n\n#define ohno_deltaT float(0.01)\n\n\nfloat powsafe(float color, float power)\{\n // pow() but safe for NaNs/negatives\n return pow(fabs(color), power) * sign(color);\n\}\n\n\nfloat2 convert_CCT_to_uv_Krystek1985(float CCT)\{\n // Convert the given CCT to CIE 1960 u,v colorspace values using Krystek\\'s method.\n //\n // Krystek\\'s method is an approximation and not intended for accuracy.\n //\n // :param CCT: in kelvin, ~\[1000-15000] range\n // --\[3]\n float CCT_2 = pow(CCT,2.0f);\n float u = 0.860117757f + 1.54118254f * pow(10.0f,-4.0f) * CCT + 1.28641212f * pow(10.0f,-7.0f) * CCT_2;\n u = u / (1.0f + 8.42420235f * pow(10.0f,-4.0f) * CCT + 7.08145163f * pow(10.0f,-7.0f) * CCT_2);\n float v = 0.317398726f + 4.22806245f * pow(10.0f,-5.0f) * CCT + 4.20481691f * pow(10.0f,-8.0f) * CCT_2;\n v = v / (1.0f - 2.89741816f * pow(10.0f,-5.0f) * CCT + 1.61456053f * pow(10.0f,-7.0f) * CCT_2);\n return float2(u, v);\n\}\n\n\nfloat2 convert_CCT_Duv_to_xy(float CCT, float Duv)\{\n // :param CCT: correlated color temperature in kelvin, ~\[1000-15000] range\n // :param Duv: also called \"tint\" \[-0.05-+0.05] range\n // -- \[2]\n float2 uv0 = convert_CCT_to_uv_Krystek1985(CCT);\n float2 uv1 = convert_CCT_to_uv_Krystek1985(CCT + ohno_deltaT);\n\n float du = uv0.x - uv1.x;\n float dv = uv0.y - uv1.y;\n\n float hypothenus = sqrt(powsafe(du,2.0f) + powsafe(dv,2.0f));\n float sinTheta = dv / hypothenus;\n float cosTheta = du / hypothenus;\n\n float u = uv0.x - Duv * sinTheta;\n float v = uv0.y + Duv * cosTheta;\n\n float u_p = u;\n float v_p = 1.5f * v;\n\n float x = 9.0f * u_p / (6.0f * u_p - 16.0f * v_p + 12.0f);\n float y = 2.0f * v_p / (3.0f * u_p - 8.0f * v_p + 6.0f);\n return float2(x, y);\n\}\n\n\nkernel WhiteBalance : ImageComputationKernel<ePixelWise>\n\{\n Image<eRead, eAccessPoint, eEdgeClamped> src;\n Image<eWrite> dst;\n\n param:\n bool u_show_coeffs;\n float u_temperature;\n float u_tint;\n float u_intensity;\n\n void define()\{\n // default values try to match illuminant E\n defineParam(u_temperature, \"u_temperature\", 5600.0f);\n defineParam(u_tint, \"u_tint\", -15.5f);\n defineParam(u_intensity, \"u_intensity\", 1.0f);\n \}\n\n float lerp(float a1, float a2, float amount)\{\n // linear interpolation between 2 values\n return (1.0f - amount) * a1 + amount * a2;\n \}\n\n void process() \{\n\n // 3000 is an arbitrary scale for the tint parameter to have a more UI friendly range.\n // (actually same as Adobe)\n float2 new_white_xy = convert_CCT_Duv_to_xy(u_temperature, u_tint/3000.0f);\n\n // --\[4] normalise primary matrix algorithm but only with whitepoint\n float Wz = 1.0f - new_white_xy.x - new_white_xy.y;\n float3 W = float3(new_white_xy.x / new_white_xy.y, 1.0f, Wz / new_white_xy.y);\n\n float4 rgba = src();\n float3 new_rgb(rgba.x, rgba.y, rgba.z);\n\n if (u_show_coeffs)\{\n new_rgb.x = W.x;\n new_rgb.y = W.y;\n new_rgb.z = W.z;\n \} else \{\n new_rgb.x = lerp(rgba.x, new_rgb.x * W.x, u_intensity);\n new_rgb.y = lerp(rgba.y, new_rgb.y * W.y, u_intensity);\n new_rgb.z = lerp(rgba.z, new_rgb.z * W.z, u_intensity);\n \}\n\n dst() = float4(\n new_rgb.x,\n new_rgb.y,\n new_rgb.z,\n rgba.w\n );\n \}\n\};" | ||
rebuild "" | ||
WhiteBalance_u_temperature {{parent.temperature}} | ||
WhiteBalance_u_tint {{parent.tint}} | ||
WhiteBalance_u_intensity {{parent.intensity}} | ||
WhiteBalance_u_show_coeffs {{parent.show_coefficients}} | ||
format "2048 2048 0 0 2048 2048 1 square_2K" | ||
rebuild_finalise "" | ||
name WhiteBalanceBlink | ||
xpos 0 | ||
ypos 150 | ||
} | ||
Output { | ||
name Output1 | ||
xpos 0 | ||
ypos 300 | ||
} | ||
end_group |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# src | ||
|
||
code here need to be "compiled" to be usable. This is achieved by executing | ||
the `build.py` file. | ||
|
||
# build instructions | ||
|
||
## build-requires | ||
|
||
- python-3 | ||
- any nuke version (including non-commercial) | ||
|
||
## build-usage | ||
|
||
- take the blink script at root and import them into a nuke scene | ||
- make sure to compile the blink script | ||
- add a new user `python button` knob | ||
- use the following code inside : | ||
```python | ||
node = nuke.thisNode() | ||
print(repr(node["kernelSource"].getValue())) | ||
print() | ||
print(repr(node["KernelDescription"].getValue())) | ||
``` | ||
- execute the button and check the result in the Script Editor | ||
- copy the first line (kernelSource) and paste into a new file named `WhiteBalance.blink.src` | ||
- think to remove the first and trailling quote `'` | ||
- do the same for the second line (KernelDescription) : | ||
- new `WhiteBalance.blink.desc` file | ||
- think to remove the first and trailling quote `'` | ||
- run `build.py` | ||
- check result which is `../WhiteBalance.nk` defined by `BuildPaths.build_gizmo` variable (in build.py) | ||
You need to perform the manipulation again **everytime** the blink script | ||
is modified. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
Group { | ||
name WhiteBalance | ||
tile_color 0xca7c4200 | ||
addUserKnob {20 User} | ||
addUserKnob {26 txt_title l "" T "<h1>WhiteBalance</h1>"} | ||
addUserKnob {7 temperature l "temperature" t "CCT expressed in Kelvin" R 1000 15000} | ||
temperature 5495 | ||
addUserKnob {7 tint l "tint" t "Deviation from planckian locus. Negatives are pinkish, positives are greener." R -150 150} | ||
tint -13 | ||
addUserKnob {7 intensity l "intensity" t "Intensity of the balancing using linear interpolation." R 0.0 5.0} | ||
intensity 1.0 | ||
addUserKnob {6 show_coefficients l "show RGB coefficients" t "Fill the input with RGB coefficients instead of white-balancing." +STARTLINE} | ||
addUserKnob {20 About} | ||
addUserKnob {26 toolName l name T WhiteBalance} | ||
addUserKnob {26 toolVersion l version T 0.1.0} | ||
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 "Creative white-balancing with temperature and tint control."} | ||
addUserKnob {26 toolUrl l url T "<a style=\"color: rgb(200,200,200);\" href=\"https://github.com/MrLixm/Foundry_Nuke\">https://github.com/MrLixm/Foundry_Nuke</a>"} | ||
} | ||
Input { | ||
inputs 0 | ||
name Input1 | ||
xpos 0 | ||
} | ||
BlinkScript { | ||
inputs 1 | ||
recompileCount 2 | ||
ProgramGroup 1 | ||
KernelDescription "%BLINK_DESC%" | ||
kernelSource "%BLINK_SRC%" | ||
rebuild "" | ||
WhiteBalance_u_temperature {{parent.temperature}} | ||
WhiteBalance_u_tint {{parent.tint}} | ||
WhiteBalance_u_intensity {{parent.intensity}} | ||
WhiteBalance_u_show_coeffs {{parent.show_coefficients}} | ||
format "2048 2048 0 0 2048 2048 1 square_2K" | ||
rebuild_finalise "" | ||
name WhiteBalanceBlink | ||
xpos 0 | ||
ypos 150 | ||
} | ||
Output { | ||
name Output1 | ||
xpos 0 | ||
ypos 300 | ||
} | ||
end_group |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// version 2 | ||
// | ||
// References : | ||
// - [2] Ohno, Yoshi (2014). Practical Use and Calculation of CCT and Duv. LEUKOS, 10(1), 47-55. doi:10.1080/15502724.2014.839020 | ||
// - [3] https://en.wikipedia.org/wiki/Planckian_locus#Approximation | ||
// - [4] SMPTE Recommended Practice - Derivation of Basic Television Color Equations https://ieeexplore.ieee.org/document/7291155 | ||
|
||
#define ohno_deltaT float(0.01) | ||
|
||
|
||
float powsafe(float color, float power){ | ||
// pow() but safe for NaNs/negatives | ||
return pow(fabs(color), power) * sign(color); | ||
} | ||
|
||
|
||
float2 convert_CCT_to_uv_Krystek1985(float CCT){ | ||
// Convert the given CCT to CIE 1960 u,v colorspace values using Krystek's method. | ||
// | ||
// Krystek's method is an approximation and not intended for accuracy. | ||
// | ||
// :param CCT: in kelvin, ~[1000-15000] range | ||
// --[3] | ||
float CCT_2 = pow(CCT,2.0f); | ||
float u = 0.860117757f + 1.54118254f * pow(10.0f,-4.0f) * CCT + 1.28641212f * pow(10.0f,-7.0f) * CCT_2; | ||
u = u / (1.0f + 8.42420235f * pow(10.0f,-4.0f) * CCT + 7.08145163f * pow(10.0f,-7.0f) * CCT_2); | ||
float v = 0.317398726f + 4.22806245f * pow(10.0f,-5.0f) * CCT + 4.20481691f * pow(10.0f,-8.0f) * CCT_2; | ||
v = v / (1.0f - 2.89741816f * pow(10.0f,-5.0f) * CCT + 1.61456053f * pow(10.0f,-7.0f) * CCT_2); | ||
return float2(u, v); | ||
} | ||
|
||
|
||
float2 convert_CCT_Duv_to_xy(float CCT, float Duv){ | ||
// :param CCT: correlated color temperature in kelvin, ~[1000-15000] range | ||
// :param Duv: also called "tint" [-0.05-+0.05] range | ||
// -- [2] | ||
float2 uv0 = convert_CCT_to_uv_Krystek1985(CCT); | ||
float2 uv1 = convert_CCT_to_uv_Krystek1985(CCT + ohno_deltaT); | ||
|
||
float du = uv0.x - uv1.x; | ||
float dv = uv0.y - uv1.y; | ||
|
||
float hypothenus = sqrt(powsafe(du,2.0f) + powsafe(dv,2.0f)); | ||
float sinTheta = dv / hypothenus; | ||
float cosTheta = du / hypothenus; | ||
|
||
float u = uv0.x - Duv * sinTheta; | ||
float v = uv0.y + Duv * cosTheta; | ||
|
||
float u_p = u; | ||
float v_p = 1.5f * v; | ||
|
||
float x = 9.0f * u_p / (6.0f * u_p - 16.0f * v_p + 12.0f); | ||
float y = 2.0f * v_p / (3.0f * u_p - 8.0f * v_p + 6.0f); | ||
return float2(x, y); | ||
} | ||
|
||
|
||
kernel WhiteBalance : ImageComputationKernel<ePixelWise> | ||
{ | ||
Image<eRead, eAccessPoint, eEdgeClamped> src; | ||
Image<eWrite> dst; | ||
|
||
param: | ||
bool u_show_coeffs; | ||
float u_temperature; | ||
float u_tint; | ||
float u_intensity; | ||
|
||
void define(){ | ||
// default values try to match illuminant E | ||
defineParam(u_temperature, "u_temperature", 5600.0f); | ||
defineParam(u_tint, "u_tint", -15.5f); | ||
defineParam(u_intensity, "u_intensity", 1.0f); | ||
} | ||
|
||
float lerp(float a1, float a2, float amount){ | ||
// linear interpolation between 2 values | ||
return (1.0f - amount) * a1 + amount * a2; | ||
} | ||
|
||
void process() { | ||
|
||
// 3000 is an arbitrary scale for the tint parameter to have a more UI friendly range. | ||
// (actually same as Adobe) | ||
float2 new_white_xy = convert_CCT_Duv_to_xy(u_temperature, u_tint/3000.0f); | ||
|
||
// --[4] normalise primary matrix algorithm but only with whitepoint | ||
float Wz = 1.0f - new_white_xy.x - new_white_xy.y; | ||
float3 W = float3(new_white_xy.x / new_white_xy.y, 1.0f, Wz / new_white_xy.y); | ||
|
||
float4 rgba = src(); | ||
float3 new_rgb(rgba.x, rgba.y, rgba.z); | ||
|
||
if (u_show_coeffs){ | ||
new_rgb.x = W.x; | ||
new_rgb.y = W.y; | ||
new_rgb.z = W.z; | ||
} else { | ||
new_rgb.x = lerp(rgba.x, new_rgb.x * W.x, u_intensity); | ||
new_rgb.y = lerp(rgba.y, new_rgb.y * W.y, u_intensity); | ||
new_rgb.z = lerp(rgba.z, new_rgb.z * W.z, u_intensity); | ||
} | ||
|
||
dst() = float4( | ||
new_rgb.x, | ||
new_rgb.y, | ||
new_rgb.z, | ||
rgba.w | ||
); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# python 3 | ||
import logging | ||
import sys | ||
from pathlib import Path | ||
|
||
LOGGER = logging.getLogger(__name__) | ||
THIS_DIR = Path(__file__).parent | ||
|
||
|
||
class BuildPaths: | ||
src_blink_script = THIS_DIR / "WhiteBalance.blink" | ||
assert src_blink_script.exists() | ||
|
||
src_gizmo = THIS_DIR / "WhiteBalance-template.nk" | ||
assert src_gizmo.exists() | ||
|
||
build_dir = THIS_DIR.parent | ||
build_gizmo = build_dir / "WhiteBalance.nk" | ||
|
||
|
||
def sanitize_nuke_script(script: str, convert_new_lines=True) -> str: | ||
if convert_new_lines: | ||
newscript = script.replace("\\", r"\\") | ||
newscript = newscript.split("\n") | ||
newscript = r"\n".join(newscript) | ||
else: | ||
newscript = script.split(r"\n") | ||
newscript = [line.replace("\\", r"\\") for line in newscript] | ||
newscript = r"\n".join(newscript) | ||
|
||
newscript = newscript.replace('"', r"\"") | ||
newscript = newscript.replace("{", r"\{") | ||
newscript = newscript.replace("}", r"\}") | ||
newscript = newscript.replace("[", r"\[") | ||
return newscript | ||
|
||
|
||
def build(): | ||
LOGGER.info(f"build started") | ||
base_gizmo = BuildPaths.src_gizmo.read_text("utf-8") | ||
|
||
blink_source = BuildPaths.src_blink_script.with_suffix(".blink.src") | ||
assert blink_source.exists() | ||
blink_source = blink_source.read_text() | ||
blink_source = sanitize_nuke_script(blink_source, False) | ||
|
||
blink_desc = BuildPaths.src_blink_script.with_suffix(".blink.desc") | ||
assert blink_desc.exists() | ||
blink_desc = blink_desc.read_text() | ||
blink_desc = sanitize_nuke_script(blink_desc, False) | ||
|
||
new_gizmo = [] | ||
|
||
for line_index, line in enumerate(base_gizmo.split("\n")): | ||
if "%BLINK_SRC%" in line: | ||
line = line.replace("%BLINK_SRC%", blink_source) | ||
LOGGER.debug(f"replaced BLINK_SRC") | ||
elif "%BLINK_DESC%" in line: | ||
line = line.replace("%BLINK_DESC%", blink_desc) | ||
LOGGER.debug(f"replaced BLINK_DESC") | ||
|
||
new_gizmo.append(line) | ||
|
||
new_gizmo = "\n".join(new_gizmo) | ||
LOGGER.info(f"writting {BuildPaths.build_gizmo}") | ||
BuildPaths.build_gizmo.write_text(new_gizmo, "utf-8") | ||
LOGGER.info("build finished") | ||
|
||
|
||
if __name__ == "__main__": | ||
logging.basicConfig( | ||
level=logging.DEBUG, | ||
format="{levelname: <7} | {asctime} [{name}] {message}", | ||
style="{", | ||
stream=sys.stdout, | ||
) | ||
build() |