Not 8-bit, I just followed the formulas that were shared above. You’re saying the issue come from that division ?
The division tries to map 8-bit 0 to 255 int onto 0 to 1 float format, but code values in Resolve are already in 0 to 1 float format.Not 8-bit, I just followed the formulas that were shared above. You’re saying the issue come from that division ?
So how would you suggest i re-write the formulas provided by Tobias for converting to CMYK and back to RGB ? Or do you have an alternative formula that can be properly inverted ?The division tries to map 8-bit 0 to 255 int onto 0 to 1 float format, but code values in Resolve are already in 0 to 1 float format.
k = 1 - max(max(p.r, p.g), p.b);
if (k == 1) {
o.r = 0;
o.g = 0;
o.b = 0;
} else {
o.r = (1 - p.r - k) / (1 - k);
o.g = (1 - p.g - k) / (1 - k);
o.b = (1 - p.b - k) / (1 - k);
}
These are probably obvious questions, apologies if I missed this info somewhere:
I've had a recurring thought about building some custom tools to suit my workflow but don't know if these things are possible. If they are, I'd be very grateful for links to any reference materials. Maybe the DCTL library from Paul Dore contains examples of these? Looking at / playing with working code is a great learning tool.
- Can DCTLs operate spatially (i.e. can one pixel be influenced by the values in a different pixel in the frame)
This would be useful for doing things like custom blurs / glow / halation etc- Can DCTLs operate temporally (i.e. can one pixel in the current frame be influenced by the values in an adjacent frame)
Thanks!!
__DEVICE__ inline float sigmoid(float x, float s, float i) {
return 1.0f / (1 + _expf(_fabs(s)*(i - x)));
}
__DEVICE__ inline float sigmoid(float x, float s, float i) {
// Check for zero slope
if (s == 0)
return x;
float sigmin = sigmoid(0, s, i);
float sigmax = sigmoid(1, s, i);
// Handling positive s value (standard sigmoid)
if (s > 0) {
float sig = 1 / (1 + exp(-s * (x - i)));
return (sig - sigmin) / (sigmax - sigmin);
}
// Handling negative s value (inverted sigmoid)
else {
float sig = 1 / (1 + exp(s * (x - i)));
return (sig - sigmin) / (sigmax - sigmin);
}
}
__DEVICE__ inline float rolloff(float x, float t) {
const float threshold = 1.0f;
const float maxScale = 10.0f; // Adjust as needed for the range of your data
// Normalize x within the range [0, maxScale]
float normalizedX = x / maxScale;
// Apply a continuous curve adjustment
// The curve is more pronounced for x > threshold and more subtle for x <= threshold
float adjustedX = normalizedX <= threshold / maxScale ?
normalizedX * _powf((threshold / maxScale) / normalizedX, t) :
threshold / maxScale + (1 - _powf(1 - (normalizedX - threshold / maxScale) / (1 - threshold / maxScale), t)) * (1 - threshold / maxScale);
// Scale back to the original range
return adjustedX * maxScale;
}
1. YesThese are probably obvious questions, apologies if I missed this info somewhere:
I've had a recurring thought about building some custom tools to suit my workflow but don't know if these things are possible. If they are, I'd be very grateful for links to any reference materials. Maybe the DCTL library from Paul Dore contains examples of these? Looking at / playing with working code is a great learning tool.
- Can DCTLs operate spatially (i.e. can one pixel be influenced by the values in a different pixel in the frame)
This would be useful for doing things like custom blurs / glow / halation etc- Can DCTLs operate temporally (i.e. can one pixel in the current frame be influenced by the values in an adjacent frame)
Thanks!!
Thanks Fedor, that's incredibly useful and the tutorials from Thatcher look absolutely spectacular!1. Yes
2. Yes, but use with caution
Check examples of DCTL's and Readme file in "DaVinciCTL" folder (C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\DaVinciCTL on Windows). You should know basics of C syntaxis to write a program. Start with Sololearn "C beginners" course (you would not need much more that that), proceed with free tutorial series by Thatcher Freeman. And download Visual Studio Code, it is much better than trying to write something in notepad.
It is a shader language, so you can start with learning shader programming, but there are not that many simple and approachable courses out there.
Some functions are designed specifically for DaVinci Color Transform Language, so check a list in the readme file if you miss something.
Good luck!
I‘m using VScode and/or CodeRunner. Both work great. VScode has a bit more flexibility and options when you want to code Python tooCan anyone recommend a free and lightweight code editor for MAC?
I'm currently using TextEdit, which obviously isn't the best tool for the job!
Even if it just did things like colouring the text and showing the companion bracket to the one you're on would be really useful.
VS Code is a no-brainer and there are good plugins to help with syntax for most programming languages out there.Can anyone recommend a free and lightweight code editor for MAC?
I'm currently using TextEdit, which obviously isn't the best tool for the job!
Even if it just did things like colouring the text and showing the companion bracket to the one you're on would be really useful.
LAB.x=(_powf(LAB.x,1.0/LumaGamma)+LumaLift*(1.0-LAB.x))*LumaGain;
DEFINE_UI_PARAMS(HueCtrl, Hue, DCTLUI_SLIDER_FLOAT, 123, 0, 360, 1)
DEFINE_UI_PARAMS(HueRangeCtrl , Hue Range , DCTLUI_SLIDER_FLOAT , 11.0, 0.0, 45.0, 1.0)
DEFINE_UI_PARAMS(HueOffset , Hue Offset , DCTLUI_SLIDER_FLOAT , 0.0, 90.0, 180.0, 1.0)
DEFINE_UI_PARAMS(SatLow , Sat Low , DCTLUI_SLIDER_FLOAT, 0.125, 0.0, 1.0, 0.025)
DEFINE_UI_PARAMS(SatHigh , Sat High , DCTLUI_SLIDER_FLOAT, 0.250, 0.0, 1.0, 0.025)
DEFINE_UI_PARAMS(LumaShadow , Luma Shadow , DCTLUI_SLIDER_FLOAT, 0.05, 0.0, 1.0, 0.01)
DEFINE_UI_PARAMS(LumaHighlight, Luma Highlight, DCTLUI_SLIDER_FLOAT, 0.95, 0.0, 1.0, 0.01)
DEFINE_UI_PARAMS(BlackWhite , Black and White, DCTLUI_CHECK_BOX, 0)
DEFINE_UI_PARAMS(IndicatorLuma, Indicator Luma, DCTLUI_SLIDER_FLOAT, 0.5, 0.0, 1.0, 0.01)
DEFINE_UI_PARAMS(WheelType , Wheel Type , DCTLUI_COMBO_BOX , 0, {YCBCR, HSL, HSLCBCR},{YCbCr, HSL, HSL-120})
DEFINE_UI_PARAMS(WheelZoom , Wheel Zoom , DCTLUI_SLIDER_FLOAT, 1.0, 1.0, 4.0, 1.0)
DEFINE_UI_PARAMS(WheelSize , Wheel Size , DCTLUI_SLIDER_FLOAT, 0.5, 0.0, 1.0, 0.01)
__DEVICE__ float mod(float a, float b) {return a - b * _floor(a/b);} // same as a%b
__DEVICE__ float3 rgb2hsl(float3 RGB) {
float max = _fmaxf(RGB.x, _fmaxf(RGB.y, RGB.z));
float min = _fminf(RGB.x, _fminf(RGB.y, RGB.z));
float h, s, l;
l = (max + min) / 2.0f;
if (max == min) {
h = s = 0.0f; // achromatic
} else {
float d = max - min;
s = l > 0.5f ? d / (2.0f - max - min) : d / (max + min);
if (max == RGB.x) {
h = (RGB.y - RGB.z) / d + (RGB.y < RGB.z ? 6.0f : 0.0f);
} else if (max == RGB.y) {
h = (RGB.z - RGB.x) / d + 2.0f;
} else {
h = (RGB.x - RGB.y) / d + 4.0f;
}
h /= 6.0f;
}
float3 HSL = make_float3(h, s, l);
return HSL;
}
__DEVICE__ float3 hsl2rgb(float3 HSL) {
float3 RGB = {0.0f, 0.0f, 0.0f}; // Initialize RGB to zero
if (HSL.y == 0.0f) {
// Achromatic (grey)
RGB.x = RGB.y = RGB.z = HSL.z;
return RGB;
} else {
float q = HSL.z < 0.5f ? HSL.z * (1.0f + HSL.y) : HSL.z + HSL.y - HSL.z * HSL.y;
float p = 2.0f * HSL.z - q;
float hk = HSL.x;
float t[3];
t[0] = hk + 1.0f / 3.0f; // Red
t[1] = hk; // Green
t[2] = hk - 1.0f / 3.0f; // Blue
for (int i = 0; i < 3; i++) {
if (t[i] < 0.0f) t[i] += 1.0f;
if (t[i] > 1.0f) t[i] -= 1.0f;
if (t[i] < 1.0f / 6.0f) {
t[i] = p + ((q - p) * 6.0f * t[i]);
} else if (t[i] < 1.0f / 2.0f) {
t[i] = q;
} else if (t[i] < 2.0f / 3.0f) {
t[i] = p + ((q - p) * (2.0f / 3.0f - t[i]) * 6.0f);
} else {
t[i] = p;
}
}
RGB.x = t[0];
RGB.y = t[1];
RGB.z = t[2];
}
return RGB;
}
__DEVICE__ float3 RGBtoYCbCr_Rec709(float3 RGB) {
float3 YCbCr;
YCbCr.x = 0.2126f * RGB.x + 0.7152f * RGB.y + 0.0722f * RGB.z;
YCbCr.y = -0.114572f * RGB.x - 0.385428f * RGB.y + 0.5f * RGB.z;
YCbCr.z = 0.5f * RGB.x - 0.454153f * RGB.y - 0.045847f * RGB.z;
return YCbCr;
}
__DEVICE__ float3 YCbCrtoRGB_Rec709(float3 YCbCr) {
float3 RGB;
RGB.x = YCbCr.x + 1.5748f * (YCbCr.z);
RGB.y = YCbCr.x - 0.187324f * (YCbCr.y) - 0.468124f * (YCbCr.z);
RGB.z = YCbCr.x + 1.8556f * (YCbCr.y);
return RGB;
}
__DEVICE__ float Saturation(float3 YCbCr) {
float chroma = _sqrtf(((YCbCr.y) * (YCbCr.y) + (YCbCr.z) * (YCbCr.z))/0.5f);
return chroma;
}
// Function to detect the hue angle in degrees
__DEVICE__ float detectHue(float3 YCbCr) {
float PI = 3.14159f;
float hueAngle = _atan2f(YCbCr.z, YCbCr.y);
hueAngle = hueAngle * (180.0f / PI);
if (hueAngle < 0.0f) {
hueAngle += 360.0f;
}
return hueAngle;
}
// Function to achieve max saturated RGB colors based on given hue
__DEVICE__ float3 maxSaturatedRGBFromHue(float hueDeg, float Y, float chroma) {
float PI = 3.14159f;
float hueRad = hueDeg * (PI / 180.0f);
float Cb = chroma * _cosf(hueRad);
float Cr = chroma * _sinf(hueRad);
float3 RGB = make_float3(1.0f,1.0f,1.0f);
RGB = YCbCrtoRGB_Rec709(make_float3(Y,Cb,Cr));
return RGB;
}
__DEVICE__ float3 drawYCbCrCircle(float2 xy, float2 wh, float2 center, float radius,float boolHSV, float angleCtrl, float AngleDelta, float WheelZoom) {
float PI = 3.14159f;
// Calculate distance from the center
float2 dxy = xy/wh.y - make_float2(center.x*wh.x/wh.y,center.x);
float dist = _sqrtf((dxy.x * dxy.x + dxy.y * dxy.y));
// Check if the pixel is within the circle
if (dist < radius) {
if (_floor(xy.x/8.0f) == _floor(0.5f*wh.y/8.0f)) {return make_float3(0.1f,0.1f,0.1f);}
if (_floor(xy.y/8.0f) == _floor(0.5f*wh.y/8.0f)) {return make_float3(0.1f,0.1f,0.1f);}
float dist2 = 0.0f;
dist2 = dist*2.0f;
float angle = _atan2f(dxy.y, dxy.x);
float hue = angle / (2.0f * PI)*360.0f;
float3 rgb;
if (boolHSV==0.0f) {rgb = maxSaturatedRGBFromHue(hue+AngleDelta, 1.0f-dist, 1.0f/WheelZoom*dist*_sqrtf(2.0f) );}
if (boolHSV==1.0f) {rgb = hsl2rgb(make_float3(hue/360.0f+AngleDelta/360.0f,1.0f/WheelZoom*dist*2.65,0.0f+1.0f*(1.0f-dist)));} //Closest to Fusion ColorCorrector Wheel
if (boolHSV==2.0f) {rgb = hsl2rgb(make_float3(hue/360.0f+240.0f/360.0f,1.0f/WheelZoom*dist*2.65,0.0f+1.0f*(1.0f-dist)));}
return make_float3(rgb.x,rgb.y,rgb.z);
}
return make_float3(0.0f,0.0f,0.0f);
}
__DEVICE__ float3 drawLumaRamp(float2 xy, float2 wh, float2 center) {
float2 dxy = xy/wh;
float c = 0.05f;
float d = 0.05f;
if ((xy.x-c*wh.x > wh.x+d*xy.x) || ((xy.x+c*wh.x < wh.x+d*xy.x) || (xy.y > wh.y))) return make_float3(0.0f,0.0f,0.0f);
return make_float3(dxy.y,dxy.y,dxy.y);
}
__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B)
{
float3 iC0 = make_float3(p_R, p_G, p_B);
//float3 iC0 = make_float3(1.0f, 1.0f, 0.0f);
float WheelTypeF = 0.0f;
if (WheelType == HSL ) {WheelTypeF = 1.0f;}
if (WheelType == HSLCBCR) {WheelTypeF = 2.0f;}
float2 wh = make_float2(p_Height, p_Height);
float2 center = make_float2(0.5f,0.5f);
float radius = 0.5f;
float3 iC1 = make_float3(0.0f,0.0f,0.0f);
float3 iC2 = make_float3(0.0f,0.0f,0.0f);
if (WheelSize > 0.1f) {
iC1 = drawYCbCrCircle(make_float2(p_X,p_Height-p_Y)/WheelSize, wh, center, radius,WheelTypeF,HueCtrl,0.0f,WheelZoom);
iC2 = drawLumaRamp(make_float2(p_X,p_Height-p_Y)/WheelSize, wh, center);
}
if (dot(iC1,make_float3(1.0f,1.0f,1.0f))>0.0f) {iC0=iC1;}
if (dot(iC2,make_float3(1.0f,1.0f,1.0f))>0.0f) {iC0=iC2;}
// Example RGB values normalized to [0, 1]
float3 RGB = make_float3(iC0.x,iC0.y,iC0.z);
// Convert RGB to YCbCr using Rec. 709 coefficients
float3 YCbCr = RGBtoYCbCr_Rec709(RGB);
float Sat = Saturation(YCbCr);
float hueAngle = detectHue(YCbCr);
float Alternate = 1.0f;
float ViewType = 2.0f;
if ((BlackWhite == 1) && (dot(iC1,make_float3(1.0f,1.0f,1.0f))==0.0f) && Alternate == 1.0f) {iC0 = make_float3(YCbCr.x,YCbCr.x,YCbCr.x);}
if ((Alternate == 1.0f && ViewType == 0.0f) || (ViewType == 2.0f)) {
float hueAngleX = hueAngle;
float3 RGBLeft = maxSaturatedRGBFromHue(hueAngle-HueOffset,0.5f,0.5f);
float3 RGBMid = maxSaturatedRGBFromHue(hueAngle ,0.5f,0.5f);
float3 RGBMid2 = maxSaturatedRGBFromHue(hueAngle ,0.3f,0.3f);
float3 RGBRight= maxSaturatedRGBFromHue(hueAngle+HueOffset,0.5f,0.5f);
if (hueAngleX > (HueCtrl - 2.0f) && hueAngleX < (HueCtrl + 2.0f) && (dot(iC1,make_float3(1.0f,1.0f,1.0f))>0.0f)) {iC0 = RGBMid2;}
hueAngleX = hueAngle - 360.0f;
if (hueAngleX > (HueCtrl - 2.0f) && hueAngleX < (HueCtrl + 2.0f) && (dot(iC1,make_float3(1.0f,1.0f,1.0f))>0.0f)) {iC0 = RGBMid2;}
hueAngleX = hueAngle + 360.0f;
if (hueAngleX > (HueCtrl - 2.0f) && hueAngleX < (HueCtrl + 2.0f) && (dot(iC1,make_float3(1.0f,1.0f,1.0f))>0.0f)) {iC0 = RGBMid2;}
if (Sat > SatLow+0.0001 && Sat < SatHigh) {
hueAngleX = hueAngle;
if (hueAngleX > (HueCtrl - 3.0f*HueRangeCtrl) && hueAngleX < (HueCtrl - HueRangeCtrl)) {iC0 = RGBLeft;}
if (hueAngleX > (HueCtrl - HueRangeCtrl) && hueAngleX < (HueCtrl + HueRangeCtrl)) {iC0 = RGBMid;}
if (hueAngleX > (HueCtrl + HueRangeCtrl) && hueAngleX < (HueCtrl + 3.0f * HueRangeCtrl)) {iC0 = RGBRight;}
hueAngleX = hueAngle - 360.0f;
if (hueAngleX > (HueCtrl - 3.0f*HueRangeCtrl) && hueAngleX < (HueCtrl - HueRangeCtrl)) {iC0 = RGBLeft;}
if (hueAngleX > (HueCtrl - HueRangeCtrl) && hueAngleX < (HueCtrl + HueRangeCtrl)) {iC0 = RGBMid;}
if (hueAngleX > (HueCtrl + HueRangeCtrl) && hueAngleX < (HueCtrl + 3.0f * HueRangeCtrl)) {iC0 = RGBRight;}
hueAngleX = hueAngle + 360.0f;
if (hueAngleX > (HueCtrl - 3.0f*HueRangeCtrl) && hueAngleX < (HueCtrl - HueRangeCtrl)) {iC0 = RGBLeft;}
if (hueAngleX > (HueCtrl - HueRangeCtrl) && hueAngleX < (HueCtrl + HueRangeCtrl)) {iC0 = RGBMid;}
if (hueAngleX > (HueCtrl + HueRangeCtrl) && hueAngleX < (HueCtrl + 3.0f * HueRangeCtrl)) {iC0 = RGBRight;}
}
}
if (((Alternate ==1.0f && ViewType == 0.0f) || (ViewType == 2.0f) || (ViewType == 5.0f)) && (dot(iC1,make_float3(1.0f,1.0f,1.0f))==0.0f)) {
if (YCbCr.x < LumaShadow ) {iC0 = make_float3(1.0f,0.0f,0.0f);}
if (YCbCr.x < LumaShadow/2.0f) {iC0 = make_float3(1.0f,1.0f,0.0f);}
if (YCbCr.x > LumaHighlight ) {iC0 = make_float3(0.0f,0.0f,1.0f);}
if (YCbCr.x > LumaHighlight+(1.0f-LumaHighlight)/2.0f ) {iC0 = make_float3(0.0f,1.0f,1.0f);}
}
return iC0;
}
Follow along with the video below to see how to install our site as a web app on your home screen.
Note: this_feature_currently_requires_accessing_site_using_safari