In light of the Coronavirus crisis, please help make sure nothing stands in the way of social distancing in the VFX industry.

This petition asks the MPAA to act accordingly:


Sign the petition on Change.org

Be safe, everyone.

[RC] Vibrance Fuse

Where the future is being made, today.

Welcome to the WSL development corner!

In this forum, please post your development projects. You get kudos and feedback here.
Topics ideally have preset prefixes, and this is what they (might) mean:

  • [DEV] - very much work in progress, don't build a business on this, could go anywhere
  • [BETA] - should kinda do what it's supposed to do, please test, give feedback
  • [RC] - this may end up in Reactor soon, polishing up, now's the time for last minute thoughts
  • [ABD] - died a premature death, sadness, will not see the light of day ever (unless someone picks up the scraps)

Once a development project has been released (hurray), topics can be marked as - you guessed it - [RELEASED] :cheer:

Development topics only, please. For generic questions, how-to's, questions and inquiries about existing tools etc, please go to the appropriate other forums.
User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#151

Post by Shem Namo » Tue Mar 24, 2020 8:25 am

@Midgardsormr, all of this trouble is so that it can add colorization when the invert is on.
What I have in there before was col.x *= params->red, this didn't work when the invert was on,
it would turn the white background red, and leave the thing I want to colorize black.
You can see an example in the video below.

For it to apply colorization right, I wanted to make the color algorithm work through gamma correction when the invert is on,
and with gain correction when it's off.
I this a good idea?

Thanks for pointing out the extra plus, I don't even know how that got there :oops:
I'll get rid of it ASAP.

Other than that, I would like to say that this is ready, it does everything I want for version 1
What do you guys think?

Thanks again!!


User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#152

Post by Shem Namo » Wed Mar 25, 2020 8:56 am

@intelligent machine Fixed it :)
The NaN pixels are gone!!
Thanks for telling me about this bug, better to fix it sooner than later, right?!
Did you happen to notice any other bugs while you were testing?

This one seems to work fine.
Feel free to test if you have the time.

Thanks again,
Code: [Select all] [Expand/Collapse] [Download] (Vibrance.fuse)
  1. FuRegisterClass("Tintensity", CT_Tool, {
  2.   REGS_Category = "Fuses\\Color",
  3.   REGS_OpIconString = "TNT",
  4.   REGS_OpDescription = "Tintensity",
  5.   REG_Version = 1.0,
  6.   REGS_Company = "Learn Now FX",
  7.   REGS_URL = "http://www.youtube.com/LearnNowFX",
  8. })
  9.  
  10.  
  11. TintensityParams = [[
  12.     float red;
  13.     float green;
  14.     float blue;
  15.     float vibrance;
  16.     float saturation;
  17.     float gain;
  18.     float gamma;
  19.     int unmult;
  20.     int invert;
  21.     float again;
  22.     int clip;
  23.     int srcCompOrder;
  24. ]]
  25.  
  26. TintensityKernel = [[
  27.     __KERNEL__ void TintensityKernel(__CONSTANTREF__ TintensityParams *params, __TEXTURE2D__ src, __TEXTURE2D_WRITE__ dst)
  28.     {
  29.         DEFINE_KERNEL_ITERATORS_XY(x, y);
  30.          float4 col = _tex2DVecN(src, x, y, params->srcCompOrder);
  31.                
  32.              // The Invert Control math goes here:
  33.               if (params->invert == 1) {
  34.                 col.x = 1 - col.x;
  35.                 col.y = 1 - col.y;
  36.                 col.z = 1 - col.z;                                            
  37.                 }
  38.                
  39.           // The Color Picker math goes here:
  40.          
  41.            if (params->invert == 0) {                        
  42.               col.x *=  params->red;
  43.               col.y *=  params->green;
  44.               col.z *=  params->blue;
  45.               }
  46.                          
  47.               if (params->invert == 1) {
  48.                  col.x = powr(col.x, 1/_fmaxf(params->red, 0.0001f));
  49.                  col.y = powr(col.y, 1/_fmaxf(params->green, 0.0001f));
  50.                  col.z = powr(col.z, 1/_fmaxf(params->blue, 0.0001f));  
  51.                 }                                                                              
  52.                                          
  53.                                  
  54.                  
  55.            // The Gain control math goes here:
  56.               col.x *=  params->gain;
  57.               col.y *=  params->gain;
  58.               col.z *=  params->gain;
  59.                
  60.                                  
  61.      // The Gamma control math goes here:
  62.         col.x = powr(col.x, 1/_fmaxf(params->gamma, 0.0001f));
  63.         col.y = powr(col.y, 1/_fmaxf(params->gamma, 0.0001f));
  64.         col.z = powr(col.z, 1/_fmaxf(params->gamma, 0.0001f));
  65.                
  66.                
  67.          // The UnMultiply math goes here:
  68.             float Luma = col.x * 0.299f + col.y * 0.587f + col.z * 0.114f;
  69.             if (params->unmult == 1)
  70.             {col.w *= (Luma + params->again);
  71.             }
  72.                        
  73.               //The Vibrance math goes here:
  74.                 float channelaverage = (col.x + col.y + col.z) / 3.0f;
  75.                 float channelm = _fmaxf(col.x, _fmaxf(col.y, col.z));
  76.                 float amount = (channelm - channelaverage) * (-params->vibrance * 3);
  77.                
  78.                
  79.                
  80.                  col.x *= (1-amount);
  81.                  col.y *= (1-amount);
  82.                  col.z *= (1-amount);
  83.                                  
  84.                                  col.x += (channelm * amount);
  85.                                  col.y += (channelm * amount);
  86.                                  col.z += (channelm * amount);
  87.                
  88.                  
  89.             // The Saturation math goes here:        
  90.                  
  91.            col.x = params->saturation * col.x + (1-params->saturation) * Luma;
  92.            col.y = params->saturation * col.y + (1-params->saturation) * Luma;
  93.            col.z = params->saturation * col.z + (1-params->saturation) * Luma;                     
  94.              
  95.            
  96.       //the Clip negative Pixels math goes here:        
  97.         if (params->clip == 1)
  98.         {  
  99.         if (col.x < 0) { col.x = 0;}
  100.            
  101.         if (col.y < 0) { col.y = 0;}
  102.    
  103.         if (col.z < 0) { col.z = 0;}          
  104.         }
  105.  
  106.  
  107.  
  108.         _tex2DVec4Write(dst, x, y, col);
  109.     }
  110. ]]
  111.  
  112. function Create()
  113.  self:BeginControlNest("Color", "Color", true, {})
  114.   InColorR= self:AddInput("Red", "Red", {
  115.     ICS_Name            = "Color",
  116.     LINKID_DataType     = "Number",
  117.     INPID_InputControl  = "ColorControl",
  118.     INP_Default         = 1,
  119.     INP_MaxScale        = 1.0,
  120.     ICD_Center          = 1.0,
  121.     INP_DoNotifyChanged = true,
  122.     CLRC_ShowWheel      = False,
  123.     IC_ControlGroup     = 1,
  124.     IC_ControlID        = 0,
  125.     IC_Visible          = true,
  126.   })
  127.  
  128.   InColorG = self:AddInput("Green", "Green", {
  129.     LINKID_DataType     = "Number",
  130.     INPID_InputControl  = "ColorControl",
  131.     INP_Default         = 1,
  132.     INP_DoNotifyChanged = true,
  133.     IC_ControlGroup     = 1,
  134.     IC_ControlID        = 1,
  135.   })
  136.  
  137.   InColorB = self:AddInput("Blue", "Blue", {
  138.     LINKID_DataType     = "Number",
  139.     INPID_InputControl  = "ColorControl",
  140.     INP_Default         = 1,
  141.     INP_DoNotifyChanged = true,
  142.     IC_ControlGroup     = 1,
  143.     IC_ControlID        = 2,
  144.   })
  145.  
  146.   InInvert = self:AddInput("Invert", "Invert", {
  147.     LINKID_DataType = "Number",
  148.     INPID_InputControl = "CheckboxControl",
  149.     INP_Integer = true,
  150.     ICD_Width = 0.5,
  151.     INP_Default = 0,
  152.   })  
  153.    
  154.    self:EndControlNest()
  155.    
  156.    self:BeginControlNest("Intensity", "Intensity", true, {})
  157.    InVibrance = self:AddInput("Vibrance", "Vibrance", {
  158.     LINKID_DataType = "Number",
  159.     INPID_InputControl = "SliderControl",
  160.     INP_MinScale = 0,
  161.     INP_MaxScale = 3,
  162.     INP_Default = 1,
  163.   })
  164.    
  165.   InSaturation = self:AddInput("Saturation", "Saturation", {
  166.     LINKID_DataType = "Number",
  167.     INPID_InputControl = "SliderControl",
  168.     INP_MinScale = 0,
  169.     INP_MaxScale = 2,
  170.     INP_Default = 1,
  171.   })
  172.  
  173.   InGain = self:AddInput("Gain", "Gain", {
  174.     LINKID_DataType = "Number",
  175.     INPID_InputControl = "SliderControl",
  176.     INP_MinScale = 0,
  177.     INP_MaxScale = 5,
  178.     INP_Default = 1,
  179.   })
  180.    
  181.  
  182.    InGamma = self:AddInput("Gamma", "Gamma", {
  183.     LINKID_DataType = "Number",
  184.     INPID_InputControl = "SliderControl",
  185.     INP_MinScale = 0,
  186.     INP_MaxScale = 2,
  187.     INP_Default = 1,
  188.   })
  189.     self:EndControlNest()
  190.    
  191.    self:BeginControlNest("Alpha", "Alpha", true, {})
  192.   InUnMultiply = self:AddInput("UnMultiply", "UnMultiply", {
  193.     LINKID_DataType = "Number",
  194.     INPID_InputControl = "CheckboxControl",
  195.     INP_Integer = true,
  196.     INP_Default = 0,
  197.   })
  198.      
  199.    
  200.   InAlphaGain = self:AddInput("Alpha Gain", "Alpha Gain", {
  201.     LINKID_DataType = "Number",
  202.     INPID_InputControl = "SliderControl",
  203.     INP_MinScale = 0,
  204.     INP_MaxScale = 1,
  205.     INP_Default = 0,
  206.   })
  207.     self:EndControlNest()
  208.    
  209.   self:BeginControlNest("Advanced", "Advanced", false, {})
  210.   InDepth = self:AddInput("Depth", "Depth", {
  211.     LINKID_DataType = "Number",
  212.     INPID_InputControl = "ComboControl",
  213.     INP_Default = 2.0,
  214.     INP_Integer = true,
  215.     ICD_Width = 1.0,
  216.     CC_LabelPosition = "Vertical",
  217.     INP_DoNotifyChanged = true,
  218.     { CCS_AddString = "int8", },
  219.     { CCS_AddString = "int16", },
  220.     { CCS_AddString = "float 16", },
  221.     { CCS_AddString = "float 32", },
  222.    })
  223.  
  224.   InClip = self:AddInput("Clip Negative Pixels", "Clip Negative Pixels", {
  225.     LINKID_DataType = "Number",
  226.     INPID_InputControl = "CheckboxControl",
  227.     INP_Integer = true,
  228.     ICD_Width = 1.0,
  229.     INP_Default = 1,
  230.   })
  231.    
  232.   DemoButton = self:AddInput("Demo", "Demo", {
  233.         LINKS_Name = "Demo",
  234.         LINKID_DataType = "Number",
  235.         INPID_InputControl = "ButtonControl",
  236.         BTNCS_Execute = [[
  237. ------------------------------------------------------------------------
  238. -- Open a webpage window up using your default web browser
  239.                     platform = (FuPLATFORM_WINDOWS and "Windows") or (FuPLATFORM_MAC and "Mac") or (FuPLATFORM_LINUX and "Linux")
  240.                     function OpenURL(siteName, path)
  241.                         if platform == "Windows" then
  242.                             -- Running on Windows
  243.                             command = "explorer \"" .. path .. "\""
  244.                         elseif platform == "Mac" then
  245.                             -- Running on Mac
  246.                             command = "open \"" .. path .. "\" &"
  247.                          elseif platform == "Linux" then
  248.                             -- Running on Linux
  249.                             command = "xdg-open \"" .. path .. "\" &"
  250.                         else
  251.                             print("[Error] There is an invalid Fusion platform detected")
  252.                             return
  253.                         end
  254.                         os.execute(command)
  255.                         -- print("[Launch Command] ", command)
  256.                         print("[Opening URL] [" .. siteName .. "] " .. path)
  257.                     end
  258.  
  259.                     OpenURL("Demo", "https://youtu.be/2XF8BsJ1J5o")
  260. ]],
  261.     })
  262.        
  263.     self:EndControlNest()
  264.    
  265.   InImage = self:AddInput("Image", "Image", {
  266.     LINKID_DataType = "Image",
  267.     LINK_Main = 1,
  268.     INP_AcceptsGPUImages = true,
  269.     })
  270.  
  271.   OutImage = self:AddOutput("Output", "Output", {
  272.     LINKID_DataType = "Image",
  273.     LINK_Main = 1,
  274.     })
  275. end
  276.  
  277.  
  278.  
  279. function Process(req)
  280.     local src = InImage:GetValue(req)
  281.     local depth = InDepth:GetValue(req).Value
  282.     local dst = Image{ IMG_Like = src, IMG_Depth = depth + 1, IMG_DeferAlloc = true }
  283.  
  284.     local node = DVIPComputeNode(req, "TintensityKernel", TintensityKernel, "TintensityParams", TintensityParams)
  285.     local params = node:GetParamBlock(TintensityParams)
  286.  
  287.     params.red = InColorR:GetValue(req).Value
  288.     params.green = InColorG:GetValue(req).Value
  289.     params.blue = InColorB:GetValue(req).Value
  290.     params.vibrance = InVibrance:GetValue(req).Value
  291.     params.saturation = InSaturation:GetValue(req).Value
  292.     params.gain = InGain:GetValue(req).Value
  293.     params.gamma = InGamma:GetValue(req).Value
  294.     params.unmult = InUnMultiply:GetValue(req).Value
  295.     params.again = InAlphaGain:GetValue(req).Value
  296.     params.invert = InInvert:GetValue(req).Value
  297.     params.clip = InClip:GetValue(req).Value
  298.     params.srcCompOrder = src:IsMask() and 1 or 15
  299.  
  300.     node:SetParamBlock(params)
  301.  
  302.     node:AddInput("src", src)
  303.     node:AddOutput("dst", dst)
  304.  
  305.     local ok = node:RunSession(req)
  306.  
  307.     if not ok then
  308.     dst = nil
  309.     end
  310.        
  311.  
  312.        
  313.        
  314.     OutImage:Set(req, dst)
  315. end

User avatar
intelligent machine
Fusionista
Posts: 508
Joined: Fri May 13, 2016 10:01 pm
Answers: 3
Location: Austin, Texas, USA
Real name: Sam Treadway
Been thanked: 13 times
Contact:

Re: [RC] Vibrance Fuse

#153

Post by intelligent machine » Wed Mar 25, 2020 9:13 am

Shem Namo wrote:
Wed Mar 25, 2020 8:56 am
Did you happen to notice any other bugs while you were testing?
I still think this:

Code: Select all

              if (params->invert == 1) {
                 col.x = powr(col.x, 1/_fmaxf(params->red, 0.0001f));
                 col.y = powr(col.y, 1/_fmaxf(params->green, 0.0001f));
                 col.z = powr(col.z, 1/_fmaxf(params->blue, 0.0001f));  
                }                                                                              
is wrong.

Let's fill in some variables and see how this looks working with the col.x

col.x = 0.789
params->red = 0.9


col.x = powr(col.x, 1/_fmaxf(params->red, 0.0001f))
col.x = powr(0.789, 1/_fmaxf(0.9, 0.0001f)
col.x = 0.789^(1/0.9)
col.x = 0.789^1.111111111
col.x = 0.768495121

All Good!

col.x = 1.189
params->red = 0


col.x = powr(col.x, 1/_fmaxf(params->red, 0.0001f))
col.x = powr(1.189, 1/_fmaxf(0, 0.0001f)
col.x = 1.189^(1/0.0001)
col.x = 1.189^10000
col.x = to large to compute so go with largest allowed value

Not good and nowhere close to zero!

col.x = 0.189
params->red = 0


col.x = powr(col.x, 1/_fmaxf(params->red, 0.0001f))
col.x = powr(0.189, 1/_fmaxf(0, 0.0001f)
col.x = 0.189^(1/0.0001)
col.x = 0.189^10000
col.x = to large to compute so go with largest allowed value

May not compute!

col.x = 0.189
params->red = 0


col.x = powr(col.x, 1/_fmaxf(params->red, 0.0001f))
col.x = powr(0.189, 1/_fmaxf(0, 0.01f)
col.x = 0.189^(1/0.01)
col.x = 0.189^100
col.x = 0

OK

I'd suggested switching the above to col.x = powr(col.x, _fmaxf(1/params->red, 0.0f)) so that when params->red is Zero and we have "1/0", THEN 0 is used.

User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#154

Post by Shem Namo » Wed Mar 25, 2020 9:41 am

Thanks @intelligent machine ,
It's looks great, but if 1 of the color values is 0, it sort of fails.

How's this?
It seems to look nice, but I'm not sure if everything is the way it should be.

Thanks again,
David.
Code: [Select all] [Expand/Collapse] [Download] (Vibrance.fuse)
  1. FuRegisterClass("Tintensity", CT_Tool, {
  2.   REGS_Category = "Fuses\\Color",
  3.   REGS_OpIconString = "TNT",
  4.   REGS_OpDescription = "Tintensity",
  5.   REG_Version = 1.0,
  6.   REGS_Company = "Learn Now FX",
  7.   REGS_URL = "http://www.youtube.com/LearnNowFX",
  8. })
  9.  
  10.  
  11. TintensityParams = [[
  12.     float red;
  13.     float green;
  14.     float blue;
  15.     float vibrance;
  16.     float saturation;
  17.     float gain;
  18.     float gamma;
  19.     int unmult;
  20.     int invert;
  21.     float again;
  22.     int clip;
  23.     int srcCompOrder;
  24. ]]
  25.  
  26. TintensityKernel = [[
  27.     __KERNEL__ void TintensityKernel(__CONSTANTREF__ TintensityParams *params, __TEXTURE2D__ src, __TEXTURE2D_WRITE__ dst)
  28.     {
  29.         DEFINE_KERNEL_ITERATORS_XY(x, y);
  30.          float4 col = _tex2DVecN(src, x, y, params->srcCompOrder);
  31.                
  32.              // The Invert Control math goes here:
  33.               if (params->invert == 1) {
  34.                 col.x = 1 - col.x;
  35.                 col.y = 1 - col.y;
  36.                 col.z = 1 - col.z;                                            
  37.                 }
  38.                
  39.           // The Color Picker math goes here:
  40.          
  41.            if (params->invert == 0) {                        
  42.               col.x *=  params->red;
  43.               col.y *=  params->green;
  44.               col.z *=  params->blue;
  45.               }
  46.                          
  47.               if (params->invert == 1) {
  48.             float nRed = params->red + 0.0001f;
  49.             float nGreen = params->green + 0.0001f;
  50.             float nBlue = params->blue + 0.0001f;
  51.                  col.x = powr(col.x, 1/nRed);
  52.                  col.y = powr(col.y, 1/nGreen);
  53.                  col.z = powr(col.z, 1/nBlue);
  54.                 }                                                                              
  55.                                          
  56.                                  
  57.                  
  58.            // The Gain control math goes here:
  59.               col.x *=  params->gain;
  60.               col.y *=  params->gain;
  61.               col.z *=  params->gain;
  62.                
  63.                                  
  64.      // The Gamma control math goes here:
  65.         col.x = powr(col.x, 1/_fmaxf(params->gamma, 0.0001f));
  66.         col.y = powr(col.y, 1/_fmaxf(params->gamma, 0.0001f));
  67.         col.z = powr(col.z, 1/_fmaxf(params->gamma, 0.0001f));
  68.                
  69.                
  70.          // The UnMultiply math goes here:
  71.             float Luma = col.x * 0.299f + col.y * 0.587f + col.z * 0.114f;
  72.             if (params->unmult == 1)
  73.             {col.w *= (Luma + params->again);
  74.             }
  75.                        
  76.               //The Vibrance math goes here:
  77.                 float channelaverage = (col.x + col.y + col.z) / 3.0f;
  78.                 float channelm = _fmaxf(col.x, _fmaxf(col.y, col.z));
  79.                 float amount = (channelm - channelaverage) * (-params->vibrance * 3.0f);
  80.                
  81.                
  82.                
  83.                  col.x *= (1-amount);
  84.                  col.y *= (1-amount);
  85.                  col.z *= (1-amount);
  86.                                  
  87.                                  col.x += (channelm * amount);
  88.                                  col.y += (channelm * amount);
  89.                                  col.z += (channelm * amount);
  90.                
  91.                  
  92.             // The Saturation math goes here:        
  93.                  
  94.            col.x = params->saturation * col.x + (1-params->saturation) * Luma;
  95.            col.y = params->saturation * col.y + (1-params->saturation) * Luma;
  96.            col.z = params->saturation * col.z + (1-params->saturation) * Luma;                     
  97.              
  98.            
  99.       //the Clip negative Pixels math goes here:        
  100.         if (params->clip == 1)
  101.         {  
  102.         if (col.x < 0) { col.x = 0;}
  103.            
  104.         if (col.y < 0) { col.y = 0;}
  105.    
  106.         if (col.z < 0) { col.z = 0;}          
  107.         }
  108.  
  109.  
  110.  
  111.         _tex2DVec4Write(dst, x, y, col);
  112.     }
  113. ]]
  114.  
  115. function Create()
  116.  self:BeginControlNest("Color", "Color", true, {})
  117.   InColorR= self:AddInput("Red", "Red", {
  118.     ICS_Name            = "Color",
  119.     LINKID_DataType     = "Number",
  120.     INPID_InputControl  = "ColorControl",
  121.     INP_Default         = 1,
  122.     INP_MaxScale        = 1.0,
  123.     ICD_Center          = 1.0,
  124.     INP_DoNotifyChanged = true,
  125.     CLRC_ShowWheel      = False,
  126.     IC_ControlGroup     = 1,
  127.     IC_ControlID        = 0,
  128.     IC_Visible          = true,
  129.   })
  130.  
  131.   InColorG = self:AddInput("Green", "Green", {
  132.     LINKID_DataType     = "Number",
  133.     INPID_InputControl  = "ColorControl",
  134.     INP_Default         = 1,
  135.     INP_DoNotifyChanged = true,
  136.     IC_ControlGroup     = 1,
  137.     IC_ControlID        = 1,
  138.   })
  139.  
  140.   InColorB = self:AddInput("Blue", "Blue", {
  141.     LINKID_DataType     = "Number",
  142.     INPID_InputControl  = "ColorControl",
  143.     INP_Default         = 1,
  144.     INP_DoNotifyChanged = true,
  145.     IC_ControlGroup     = 1,
  146.     IC_ControlID        = 2,
  147.   })
  148.  
  149.   InInvert = self:AddInput("Invert", "Invert", {
  150.     LINKID_DataType = "Number",
  151.     INPID_InputControl = "CheckboxControl",
  152.     INP_Integer = true,
  153.     ICD_Width = 0.5,
  154.     INP_Default = 0,
  155.   })  
  156.    
  157.    self:EndControlNest()
  158.    
  159.    self:BeginControlNest("Intensity", "Intensity", true, {})
  160.    InVibrance = self:AddInput("Vibrance", "Vibrance", {
  161.     LINKID_DataType = "Number",
  162.     INPID_InputControl = "SliderControl",
  163.     INP_MinScale = 0,
  164.     INP_MaxScale = 3,
  165.     INP_Default = 1,
  166.   })
  167.    
  168.   InSaturation = self:AddInput("Saturation", "Saturation", {
  169.     LINKID_DataType = "Number",
  170.     INPID_InputControl = "SliderControl",
  171.     INP_MinScale = 0,
  172.     INP_MaxScale = 2,
  173.     INP_Default = 1,
  174.   })
  175.  
  176.   InGain = self:AddInput("Gain", "Gain", {
  177.     LINKID_DataType = "Number",
  178.     INPID_InputControl = "SliderControl",
  179.     INP_MinScale = 0,
  180.     INP_MaxScale = 5,
  181.     INP_Default = 1,
  182.   })
  183.    
  184.  
  185.    InGamma = self:AddInput("Gamma", "Gamma", {
  186.     LINKID_DataType = "Number",
  187.     INPID_InputControl = "SliderControl",
  188.     INP_MinScale = 0,
  189.     INP_MaxScale = 2,
  190.     INP_Default = 1,
  191.   })
  192.     self:EndControlNest()
  193.    
  194.    self:BeginControlNest("Alpha", "Alpha", true, {})
  195.   InUnMultiply = self:AddInput("UnMultiply", "UnMultiply", {
  196.     LINKID_DataType = "Number",
  197.     INPID_InputControl = "CheckboxControl",
  198.     INP_Integer = true,
  199.     INP_Default = 0,
  200.   })
  201.      
  202.    
  203.   InAlphaGain = self:AddInput("Alpha Gain", "Alpha Gain", {
  204.     LINKID_DataType = "Number",
  205.     INPID_InputControl = "SliderControl",
  206.     INP_MinScale = 0,
  207.     INP_MaxScale = 1,
  208.     INP_Default = 0,
  209.   })
  210.     self:EndControlNest()
  211.    
  212.   self:BeginControlNest("Advanced", "Advanced", false, {})
  213.   InDepth = self:AddInput("Depth", "Depth", {
  214.     LINKID_DataType = "Number",
  215.     INPID_InputControl = "ComboControl",
  216.     INP_Default = 2.0,
  217.     INP_Integer = true,
  218.     ICD_Width = 1.0,
  219.     CC_LabelPosition = "Vertical",
  220.     INP_DoNotifyChanged = true,
  221.     { CCS_AddString = "int8", },
  222.     { CCS_AddString = "int16", },
  223.     { CCS_AddString = "float 16", },
  224.     { CCS_AddString = "float 32", },
  225.    })
  226.  
  227.   InClip = self:AddInput("Clip Negative Pixels", "Clip Negative Pixels", {
  228.     LINKID_DataType = "Number",
  229.     INPID_InputControl = "CheckboxControl",
  230.     INP_Integer = true,
  231.     ICD_Width = 1.0,
  232.     INP_Default = 1,
  233.   })
  234.    
  235.   DemoButton = self:AddInput("Demo", "Demo", {
  236.         LINKS_Name = "Demo",
  237.         LINKID_DataType = "Number",
  238.         INPID_InputControl = "ButtonControl",
  239.         BTNCS_Execute = [[
  240. ------------------------------------------------------------------------
  241. -- Open a webpage window up using your default web browser
  242.                     platform = (FuPLATFORM_WINDOWS and "Windows") or (FuPLATFORM_MAC and "Mac") or (FuPLATFORM_LINUX and "Linux")
  243.                     function OpenURL(siteName, path)
  244.                         if platform == "Windows" then
  245.                             -- Running on Windows
  246.                             command = "explorer \"" .. path .. "\""
  247.                         elseif platform == "Mac" then
  248.                             -- Running on Mac
  249.                             command = "open \"" .. path .. "\" &"
  250.                          elseif platform == "Linux" then
  251.                             -- Running on Linux
  252.                             command = "xdg-open \"" .. path .. "\" &"
  253.                         else
  254.                             print("[Error] There is an invalid Fusion platform detected")
  255.                             return
  256.                         end
  257.                         os.execute(command)
  258.                         -- print("[Launch Command] ", command)
  259.                         print("[Opening URL] [" .. siteName .. "] " .. path)
  260.                     end
  261.  
  262.                     OpenURL("Demo", "https://youtu.be/2XF8BsJ1J5o")
  263. ]],
  264.     })
  265.        
  266.     self:EndControlNest()
  267.    
  268.   InImage = self:AddInput("Image", "Image", {
  269.     LINKID_DataType = "Image",
  270.     LINK_Main = 1,
  271.     INP_AcceptsGPUImages = true,
  272.     })
  273.  
  274.   OutImage = self:AddOutput("Output", "Output", {
  275.     LINKID_DataType = "Image",
  276.     LINK_Main = 1,
  277.     })
  278. end
  279.  
  280.  
  281.  
  282. function Process(req)
  283.     local src = InImage:GetValue(req)
  284.     local depth = InDepth:GetValue(req).Value
  285.     local dst = Image{ IMG_Like = src, IMG_Depth = depth + 1, IMG_DeferAlloc = true }
  286.  
  287.     local node = DVIPComputeNode(req, "TintensityKernel", TintensityKernel, "TintensityParams", TintensityParams)
  288.     local params = node:GetParamBlock(TintensityParams)
  289.  
  290.     params.red = InColorR:GetValue(req).Value
  291.     params.green = InColorG:GetValue(req).Value
  292.     params.blue = InColorB:GetValue(req).Value
  293.     params.vibrance = InVibrance:GetValue(req).Value
  294.     params.saturation = InSaturation:GetValue(req).Value
  295.     params.gain = InGain:GetValue(req).Value
  296.     params.gamma = InGamma:GetValue(req).Value
  297.     params.unmult = InUnMultiply:GetValue(req).Value
  298.     params.again = InAlphaGain:GetValue(req).Value
  299.     params.invert = InInvert:GetValue(req).Value
  300.     params.clip = InClip:GetValue(req).Value
  301.     params.srcCompOrder = src:IsMask() and 1 or 15
  302.  
  303.     node:SetParamBlock(params)
  304.  
  305.     node:AddInput("src", src)
  306.     node:AddOutput("dst", dst)
  307.  
  308.     local ok = node:RunSession(req)
  309.  
  310.     if not ok then
  311.     dst = nil
  312.     end
  313.        
  314.  
  315.        
  316.        
  317.     OutImage:Set(req, dst)
  318. end

User avatar
thibaud
Fusioneer
Posts: 175
Joined: Thu Sep 04, 2014 1:23 am
Been thanked: 4 times
Contact:

Re: [RC] Vibrance Fuse

#155

Post by thibaud » Wed Mar 25, 2020 9:57 am

when asking for people to help and look at "your" code, I would suggest to format the thing properly first (start by fixing the indentation)
  1. TintensityKernel = [[
  2.     __KERNEL__ void TintensityKernel(__CONSTANTREF__ TintensityParams *params, __TEXTURE2D__ src, __TEXTURE2D_WRITE__ dst) {
  3.         DEFINE_KERNEL_ITERATORS_XY(x, y);
  4.         float4 col = _tex2DVecN(src, x, y, params->srcCompOrder);
  5.  
  6.         // The Invert Control math goes here:
  7.         if (params->invert == 1) {
  8.             col.x = 1-col.x;
  9.             col.y = 1-col.y;
  10.             col.z = 1-col.z;
  11.         }
  12.  
  13.         // The Color Picker math goes here:
  14.         if (params->invert == 0) {
  15.             col.x *= params->red;
  16.             col.y *= params->green;
  17.             col.z *= params->blue;
  18.         }
  19.  
  20.         if (params->invert == 1) {
  21.             float nRed = params->red + 0.0001f;
  22.             float nGreen = params->green + 0.0001f;
  23.             float nBlue = params->blue + 0.0001f;
  24.             col.x = powr(col.x, 1/nRed);
  25.             col.y = powr(col.y, 1/nGreen);
  26.             col.z = powr(col.z, 1/nBlue);
  27.         }
  28.  
  29.         // The Gain control math goes here:
  30.         col.x *= params->gain;
  31.         col.y *= params->gain;
  32.         col.z *= params->gain;
  33.  
  34.         // The Gamma control math goes here:
  35.         col.x = powr(col.x, 1/_fmaxf(params->gamma, 0.0001f));
  36.         col.y = powr(col.y, 1/_fmaxf(params->gamma, 0.0001f));
  37.         col.z = powr(col.z, 1/_fmaxf(params->gamma, 0.0001f));
  38.  
  39.         // The UnMultiply math goes here:
  40.         float Luma = col.x * 0.299f + col.y * 0.587f + col.z * 0.114f;
  41.         if (params->unmult == 1){
  42.             col.w *= (Luma + params->again);
  43.         }
  44.  
  45.         //The Vibrance math goes here:
  46.         float channelaverage = (col.x + col.y + col.z) / 3.0f;
  47.         float channelm = _fmaxf(col.x, _fmaxf(col.y, col.z));
  48.         float amount = (channelm-channelaverage) * (-params->vibrance * 3.0f);
  49.         col.x *= (1 - amount);
  50.         col.y *= (1 - amount);
  51.         col.z *= (1 - amount);
  52.         col.x += (channelm * amount);
  53.         col.y += (channelm * amount);
  54.         col.z += (channelm * amount);
  55.  
  56.         // The Saturation math goes here:        
  57.         col.x = params->saturation * col.x + (1 - params->saturation) * Luma;
  58.         col.y = params->saturation * col.y + (1 - params->saturation) * Luma;
  59.         col.z = params->saturation * col.z + (1 - params->saturation) * Luma;
  60.  
  61.         //the Clip negative Pixels math goes here:        
  62.         if (params->clip == 1) {
  63.             if (col.x < 0) { col.x = 0;}
  64.             if (col.y < 0) { col.y = 0;}
  65.             if (col.z < 0) { col.z = 0;}
  66.         }
  67.  
  68.         _tex2DVec4Write(dst, x, y, col);
  69.     }
  70. ]]
Here's the kernel code formated.
seems to me that the first condition if (params->invert == 1) doesn't serve any purpose as another same condition is assigning other values to the same variables
Last edited by thibaud on Wed Mar 25, 2020 10:34 am, edited 1 time in total.

User avatar
intelligent machine
Fusionista
Posts: 508
Joined: Fri May 13, 2016 10:01 pm
Answers: 3
Location: Austin, Texas, USA
Real name: Sam Treadway
Been thanked: 13 times
Contact:

Re: [RC] Vibrance Fuse

#156

Post by intelligent machine » Wed Mar 25, 2020 10:09 am

This version seems to be stable, although, I'm still a bit confused at what certain things should actually do.

What is the actual intent of the "Invert" option?
When I click it without adjusting any other parameters I'm expecting the input image to simply be inverted, correct?.
Or do you wish to invert the luminance values?

Next, The color input is subtractive right now. Is this in the intent? Meaning: RGB are all set to 1 and sliding any one of those toward zero removes that color from the image. Should this happen before/after the invert is processed?

The Vibrance control: I get what this should do but the effect seems a bit subtle and also seems to sharply divide the saturated from the desaturated parts. I would think that the less saturated parts get a larger boost than the most saturated parts.

I agree with @thibaud...the indentations are all over the place and makes it very difficult to track down things like missing/extra brackets or which variables belong in which statements.

Added in 20 minutes 22 seconds:
...something I don't normally do but since my text editor has great hotkeys for fixing formatting quickly and I wanted to look closely at a few things...

I also changed the name to "Vibrance" in the FuRegisterClass to match the file name and move the demo url code to the top section:
Vibrance.fuse
Code: [Select all] [Expand/Collapse] [Download] (Vibrance.fuse)
  1. FuRegisterClass("Vibrance", CT_Tool, {
  2.     REGS_Category = "Fuses\\Color",
  3.     REGS_OpIconString = "TNT",
  4.     REGS_OpDescription = "Vibrance",
  5.     REG_Version = 1.0,
  6.     REGS_Company = "Learn Now FX",
  7.     REGS_URL = "http://www.youtube.com/LearnNowFX",
  8. })
  9.  
  10.  
  11. TintensityParams = [[
  12.     float red;
  13.     float green;
  14.     float blue;
  15.     float vibrance;
  16.     float saturation;
  17.     float gain;
  18.     float gamma;
  19.     int unmult;
  20.     int invert;
  21.     float again;
  22.     int clip;
  23.     int srcCompOrder;
  24. ]]
  25.  
  26. TintensityKernel = [[
  27.     __KERNEL__ void TintensityKernel(__CONSTANTREF__ TintensityParams *params, __TEXTURE2D__ src, __TEXTURE2D_WRITE__ dst) {
  28.         DEFINE_KERNEL_ITERATORS_XY(x, y);
  29.         float4 col = _tex2DVecN(src, x, y, params->srcCompOrder);
  30.  
  31.         // The Invert Control math goes here:
  32.         if (params->invert == 1) {
  33.             col.x = 1 - col.x;
  34.             col.y = 1 - col.y;
  35.             col.z = 1 - col.z;
  36.         }
  37.  
  38.         // The Color Picker math goes here:
  39.         if (params->invert == 0) {
  40.             col.x *=  params->red;
  41.             col.y *=  params->green;
  42.             col.z *=  params->blue;
  43.         }
  44.  
  45.         if (params->invert == 1) {
  46.             float nRed = params->red + 0.0001f;
  47.             float nGreen = params->green + 0.0001f;
  48.             float nBlue = params->blue + 0.0001f;
  49.             col.x = powr(col.x, 1/nRed);
  50.             col.y = powr(col.y, 1/nGreen);
  51.             col.z = powr(col.z, 1/nBlue);
  52.         }
  53.                  
  54.         // The Gain control math goes here:
  55.         col.x *=  params->gain;
  56.         col.y *=  params->gain;
  57.         col.z *=  params->gain;
  58.                                  
  59.         // The Gamma control math goes here:
  60.         col.x = powr(col.x, 1/_fmaxf(params->gamma, 0.01f));
  61.         col.y = powr(col.y, 1/_fmaxf(params->gamma, 0.01f));
  62.         col.z = powr(col.z, 1/_fmaxf(params->gamma, 0.01f));
  63.                
  64.         // The UnMultiply math goes here:
  65.         float Luma = col.x * 0.299f + col.y * 0.587f + col.z * 0.114f;
  66.         if (params->unmult == 1) {
  67.             col.w *= (Luma + params->again);
  68.         }
  69.                        
  70.         //The Vibrance math goes here:
  71.         float channelaverage = (col.x + col.y + col.z) / 3.0f;
  72.         float channelm = _fmaxf(col.x, _fmaxf(col.y, col.z));
  73.         float amount = (channelm - channelaverage) * (-params->vibrance * 3.0f);
  74.  
  75.         col.x *= (1-amount);
  76.         col.y *= (1-amount);
  77.         col.z *= (1-amount);
  78.  
  79.         col.x += (channelm * amount);
  80.         col.y += (channelm * amount);
  81.         col.z += (channelm * amount);
  82.                  
  83.         // The Saturation math goes here:
  84.         col.x = params->saturation * col.x + (1-params->saturation) * Luma;
  85.         col.y = params->saturation * col.y + (1-params->saturation) * Luma;
  86.         col.z = params->saturation * col.z + (1-params->saturation) * Luma;
  87.            
  88.         //the Clip negative Pixels math goes here:        
  89.         if (params->clip == 1) {
  90.             if (col.x < 0) { col.x = 0; }
  91.             if (col.y < 0) { col.y = 0; }
  92.             if (col.z < 0) { col.z = 0; }
  93.         }
  94.  
  95.         _tex2DVec4Write(dst, x, y, col);
  96.     }
  97. ]]
  98.  
  99. OpenDemoURL = [[
  100. -- Open a webpage window up using your default web browser
  101.     platform = (FuPLATFORM_WINDOWS and "Windows") or (FuPLATFORM_MAC and "Mac") or (FuPLATFORM_LINUX and "Linux")
  102.     function OpenURL(siteName, path)
  103.         if platform == "Windows" then
  104.             -- Running on Windows
  105.             command = "explorer \"" .. path .. "\""
  106.         elseif platform == "Mac" then
  107.             -- Running on Mac
  108.             command = "open \"" .. path .. "\" &"
  109.         elseif platform == "Linux" then
  110.             -- Running on Linux
  111.             command = "xdg-open \"" .. path .. "\" &"
  112.         else
  113.             print("[Error] There is an invalid Fusion platform detected")
  114.             return
  115.         end
  116.  
  117.         os.execute(command)
  118.         -- print("[Launch Command] ", command)
  119.         print("[Opening URL] [" .. siteName .. "] " .. path)
  120.     end
  121.  
  122.     OpenURL("Demo", "https://youtu.be/2XF8BsJ1J5o")
  123. ]]
  124.  
  125. function Create()
  126.     self:BeginControlNest("Color", "Color", true, {})
  127.         InColorR= self:AddInput("Red", "Red", {
  128.             ICS_Name            = "Color",
  129.             LINKID_DataType     = "Number",
  130.             INPID_InputControl  = "ColorControl",
  131.             INP_Default         = 1,
  132.             INP_MaxScale        = 1.0,
  133.             ICD_Center          = 1.0,
  134.             INP_DoNotifyChanged = true,
  135.             CLRC_ShowWheel      = False,
  136.             IC_ControlGroup     = 1,
  137.             IC_ControlID        = 0,
  138.             IC_Visible          = true,
  139.         })
  140.         InColorG = self:AddInput("Green", "Green", {
  141.             LINKID_DataType     = "Number",
  142.             INPID_InputControl  = "ColorControl",
  143.             INP_Default         = 1,
  144.             INP_DoNotifyChanged = true,
  145.             IC_ControlGroup     = 1,
  146.             IC_ControlID        = 1,
  147.         })
  148.         InColorB = self:AddInput("Blue", "Blue", {
  149.             LINKID_DataType     = "Number",
  150.             INPID_InputControl  = "ColorControl",
  151.             INP_Default         = 1,
  152.             INP_DoNotifyChanged = true,
  153.             IC_ControlGroup     = 1,
  154.             IC_ControlID        = 2,
  155.         })
  156.         InInvert = self:AddInput("Invert", "Invert", {
  157.             LINKID_DataType = "Number",
  158.             INPID_InputControl = "CheckboxControl",
  159.             INP_Integer = true,
  160.             ICD_Width = 0.5,
  161.             INP_Default = 0,
  162.         })  
  163.     self:EndControlNest()
  164.    
  165.     self:BeginControlNest("Intensity", "Intensity", true, {})
  166.         InVibrance = self:AddInput("Vibrance", "Vibrance", {
  167.             LINKID_DataType = "Number",
  168.             INPID_InputControl = "SliderControl",
  169.             INP_MinScale = 0,
  170.             INP_MaxScale = 3,
  171.             INP_Default = 1,
  172.         })
  173.         InSaturation = self:AddInput("Saturation", "Saturation", {
  174.             LINKID_DataType = "Number",
  175.             INPID_InputControl = "SliderControl",
  176.             INP_MinScale = 0,
  177.             INP_MaxScale = 2,
  178.             INP_Default = 1,
  179.         })
  180.         InGain = self:AddInput("Gain", "Gain", {
  181.             LINKID_DataType = "Number",
  182.             INPID_InputControl = "SliderControl",
  183.             INP_MinScale = 0,
  184.             INP_MaxScale = 5,
  185.             INP_Default = 1,
  186.         })
  187.         InGamma = self:AddInput("Gamma", "Gamma", {
  188.             LINKID_DataType = "Number",
  189.             INPID_InputControl = "SliderControl",
  190.             INP_MinScale = 0,
  191.             INP_MaxScale = 2,
  192.             INP_Default = 1,
  193.         })
  194.     self:EndControlNest()
  195.    
  196.     self:BeginControlNest("Alpha", "Alpha", true, {})
  197.         InUnMultiply = self:AddInput("UnMultiply", "UnMultiply", {
  198.             LINKID_DataType = "Number",
  199.             INPID_InputControl = "CheckboxControl",
  200.             INP_Integer = true,
  201.             INP_Default = 0,
  202.         })
  203.         InAlphaGain = self:AddInput("Alpha Gain", "Alpha Gain", {
  204.             LINKID_DataType = "Number",
  205.             INPID_InputControl = "SliderControl",
  206.             INP_MinScale = 0,
  207.             INP_MaxScale = 1,
  208.             INP_Default = 0,
  209.         })
  210.     self:EndControlNest()
  211.    
  212.     self:BeginControlNest("Advanced", "Advanced", false, {})
  213.         InDepth = self:AddInput("Depth", "Depth", {
  214.             LINKID_DataType = "Number",
  215.             INPID_InputControl = "ComboControl",
  216.             INP_Default = 2.0,
  217.             INP_Integer = true,
  218.             ICD_Width = 1.0,
  219.             CC_LabelPosition = "Vertical",
  220.             INP_DoNotifyChanged = true,
  221.             { CCS_AddString = "int8", },
  222.             { CCS_AddString = "int16", },
  223.             { CCS_AddString = "float 16", },
  224.             { CCS_AddString = "float 32", },
  225.         })
  226.         InClip = self:AddInput("Clip Negative Pixels", "Clip Negative Pixels", {
  227.             LINKID_DataType = "Number",
  228.             INPID_InputControl = "CheckboxControl",
  229.             INP_Integer = true,
  230.             ICD_Width = 1.0,
  231.             INP_Default = 1,
  232.         })
  233.         DemoButton = self:AddInput("Demo", "Demo", {
  234.             LINKS_Name = "Demo",
  235.             LINKID_DataType = "Number",
  236.             INPID_InputControl = "ButtonControl",
  237.             BTNCS_Execute = OpenDemoURL,
  238.         })
  239.     self:EndControlNest()
  240.    
  241.     InImage = self:AddInput("Image", "Image", {
  242.         LINKID_DataType = "Image",
  243.         LINK_Main = 1,
  244.         INP_AcceptsGPUImages = true,
  245.     })
  246.     OutImage = self:AddOutput("Output", "Output", {
  247.         LINKID_DataType = "Image",
  248.         LINK_Main = 1,
  249.     })
  250. end
  251.  
  252.  
  253. function Process(req)
  254.     local src = InImage:GetValue(req)
  255.     local depth = InDepth:GetValue(req).Value
  256.     local dst = Image{ IMG_Like = src, IMG_Depth = depth + 1, IMG_DeferAlloc = true }
  257.    
  258.     local node = DVIPComputeNode(req, "TintensityKernel", TintensityKernel, "TintensityParams", TintensityParams)
  259.     local params = node:GetParamBlock(TintensityParams)
  260.  
  261.     params.red = InColorR:GetValue(req).Value
  262.     params.green = InColorG:GetValue(req).Value
  263.     params.blue = InColorB:GetValue(req).Value
  264.     params.vibrance = InVibrance:GetValue(req).Value
  265.     params.saturation = InSaturation:GetValue(req).Value
  266.     params.gain = InGain:GetValue(req).Value
  267.     params.gamma = InGamma:GetValue(req).Value
  268.     params.unmult = InUnMultiply:GetValue(req).Value
  269.     params.again = InAlphaGain:GetValue(req).Value
  270.     params.invert = InInvert:GetValue(req).Value
  271.     params.clip = InClip:GetValue(req).Value
  272.     params.srcCompOrder = src:IsMask() and 1 or 15
  273.  
  274.     node:SetParamBlock(params)
  275.  
  276.     node:AddInput("src", src)
  277.     node:AddOutput("dst", dst)
  278.  
  279.     local ok = node:RunSession(req)
  280.  
  281.     if not ok then
  282.         dst = nil
  283.     end
  284.        
  285.     OutImage:Set(req, dst)
  286. end
You do not have the required permissions to view the files attached to this post.

User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#157

Post by Shem Namo » Wed Mar 25, 2020 10:40 am

@intelligent machine , sorry about the bad indentation.
I'm really new to programming.
I thought I had the indentation thing down, until I started with GPU code.
Is this indentation any better?
I'm still a bit confused at what certain things should actually do.
I don't know if you're familiar the Video Copilot's "Color Vibrance" plugin,
if you are, then it's easier to explain, if you aren't then I linked a video
that show's what there plug-in does.
I'm trying to recreate that plug-in as closely as possible as a fuse.
What is the actual intent of the "Invert" option?
When I click it without adjusting any other parameters I'm expecting the input image to simply be inverted, correct?.
Or do you wish to invert the luminance values?
Correct, I'm trying to invert the luminance, but not the color. Like in the original plug-in.
Next, The color input is subtractive right now. Is this in the intent? Meaning: RGB are all set to 1 and sliding any one of those toward zero removes that color from the image. Should this happen before/after the invert is processed?
I guess so, the point of that is just so change the color on the input. The only reason we're using col.x *= params->red is because I don't
know any other way.
Is there a another way to add colorization? Maybe the the same algorithm used in the "Color Correction" node?
The Vibrance control: I get what this should do but the effect seems a bit subtle and also seems to sharply divided the saturated from the desaturated parts. I would think that the less saturated parts get a larger boost than the most saturated parts.
Actually the Vibrance algorithm was found by @thibaud on this page: http://redqueengraphics.com/2018/08/24/ ... -vibrance/
And @Midgardsormr made that into a custom tool over in this topic: viewtopic.php?f=16&t=3783 . Thanks again for that Bryan.

And that got me started on writing this fuse with a LOT of help from @Midgardsormr , @SecondMan , @thibaud , and now also from you.

I hope this all makes sense.

Thanks again,
David


Code: [Select all] [Expand/Collapse] [Download] (Tintensity.fuse)
  1. FuRegisterClass("Tintensity", CT_Tool, {
  2.   REGS_Category = "Fuses\\Color",
  3.   REGS_OpIconString = "TNT",
  4.   REGS_OpDescription = "Tintensity",
  5.   REG_Version = 1.0,
  6.   REGS_Company = "Learn Now FX",
  7.   REGS_URL = "http://www.youtube.com/LearnNowFX",
  8. })
  9.  
  10.  
  11. TintensityParams = [[
  12.     float red;
  13.     float green;
  14.     float blue;
  15.     float vibrance;
  16.     float saturation;
  17.     float gain;
  18.     float gamma;
  19.     int unmult;
  20.     int invert;
  21.     float again;
  22.     int clip;
  23.     int srcCompOrder;
  24. ]]
  25.  
  26. TintensityKernel = [[
  27.     __KERNEL__ void TintensityKernel(__CONSTANTREF__ TintensityParams *params, __TEXTURE2D__ src, __TEXTURE2D_WRITE__ dst)
  28.     {
  29.         DEFINE_KERNEL_ITERATORS_XY(x, y);
  30.          float4 col = _tex2DVecN(src, x, y, params->srcCompOrder);
  31.          float Luma = col.x * 0.299f + col.y * 0.587f + col.z * 0.114f;
  32.                
  33.       // The Invert Control math goes here:
  34.        if (params->invert == 1) {
  35.                  
  36.           col.x = 1 - col.x;
  37.           col.y = 1 - col.y;
  38.           col.z = 1 - col.z;                                            
  39.            }
  40.                
  41.       // The Color Picker math goes here:        
  42.        if (params->invert == 0) {                        
  43.           col.x *=  params->red;
  44.           col.y *=  params->green;
  45.           col.z *=  params->blue;
  46.           }
  47.                          
  48.        if (params->invert == 1) {
  49.           float nRed = params->red + 0.0001f;
  50.           float nGreen = params->green + 0.0001f;
  51.           float nBlue = params->blue + 0.0001f;
  52.                  
  53.            col.x = powr(col.x, 1/nRed);
  54.            col.y = powr(col.y, 1/nGreen);
  55.            col.z = powr(col.z, 1/nBlue);
  56.            }                                                                              
  57.                                          
  58.                                  
  59.                  
  60.       // The Gain control math goes here:
  61.          col.x *=  params->gain;
  62.          col.y *=  params->gain;
  63.          col.z *=  params->gain;
  64.                
  65.                                  
  66.       // The Gamma control math goes here:
  67.          col.x = powr(col.x, 1/_fmaxf(params->gamma, 0.0001f));
  68.          col.y = powr(col.y, 1/_fmaxf(params->gamma, 0.0001f));
  69.          col.z = powr(col.z, 1/_fmaxf(params->gamma, 0.0001f));
  70.                
  71.                
  72.      // The UnMultiply math goes here:
  73.                        
  74.        if (params->unmult == 1) {              
  75.          col.w *= (Luma + params->again);
  76.        }
  77.                        
  78.     //The Vibrance math goes here:
  79.          float channelaverage = (col.x + col.y + col.z) / 3.0f;
  80.          float channelm = _fmaxf(col.x, _fmaxf(col.y, col.z));
  81.          float amount = (channelm - channelaverage) * (-params->vibrance * 3.0f);
  82.                
  83.          col.x *= (1-amount);
  84.          col.y *= (1-amount);
  85.          col.z *= (1-amount);
  86.                  
  87.          col.x += (channelm * amount);
  88.          col.y += (channelm * amount);
  89.          col.z += (channelm * amount);
  90.                
  91.                  
  92.     // The Saturation math goes here:                        
  93.          col.x = params->saturation * col.x + (1-params->saturation) * Luma;
  94.          col.y = params->saturation * col.y + (1-params->saturation) * Luma;
  95.          col.z = params->saturation * col.z + (1-params->saturation) * Luma;                       
  96.              
  97.            
  98.     //the Clip negative Pixels math goes here:
  99.    
  100.         if (params->clip == 1)
  101.         {  
  102.         if (col.x < 0) { col.x = 0;}
  103.            
  104.         if (col.y < 0) { col.y = 0;}
  105.    
  106.         if (col.z < 0) { col.z = 0;}          
  107.         }
  108.  
  109.  
  110.  
  111.         _tex2DVec4Write(dst, x, y, col);
  112.     }
  113. ]]
  114.  
  115. function Create()
  116.  self:BeginControlNest("Color", "Color", true, {})
  117.   InColorR= self:AddInput("Red", "Red", {
  118.     ICS_Name            = "Color",
  119.     LINKID_DataType     = "Number",
  120.     INPID_InputControl  = "ColorControl",
  121.     INP_Default         = 1,
  122.     INP_MaxScale        = 1.0,
  123.     ICD_Center          = 1.0,
  124.     INP_DoNotifyChanged = true,
  125.     CLRC_ShowWheel      = False,
  126.     IC_ControlGroup     = 1,
  127.     IC_ControlID        = 0,
  128.     IC_Visible          = true,
  129.   })
  130.  
  131.   InColorG = self:AddInput("Green", "Green", {
  132.     LINKID_DataType     = "Number",
  133.     INPID_InputControl  = "ColorControl",
  134.     INP_Default         = 1,
  135.     INP_DoNotifyChanged = true,
  136.     IC_ControlGroup     = 1,
  137.     IC_ControlID        = 1,
  138.   })
  139.  
  140.   InColorB = self:AddInput("Blue", "Blue", {
  141.     LINKID_DataType     = "Number",
  142.     INPID_InputControl  = "ColorControl",
  143.     INP_Default         = 1,
  144.     INP_DoNotifyChanged = true,
  145.     IC_ControlGroup     = 1,
  146.     IC_ControlID        = 2,
  147.   })
  148.  
  149.   InInvert = self:AddInput("Invert", "Invert", {
  150.     LINKID_DataType = "Number",
  151.     INPID_InputControl = "CheckboxControl",
  152.     INP_Integer = true,
  153.     ICD_Width = 0.5,
  154.     INP_Default = 0,
  155.   })  
  156.    
  157.    self:EndControlNest()
  158.    
  159.    self:BeginControlNest("Intensity", "Intensity", true, {})
  160.    InVibrance = self:AddInput("Vibrance", "Vibrance", {
  161.     LINKID_DataType = "Number",
  162.     INPID_InputControl = "SliderControl",
  163.     INP_MinScale = 0,
  164.     INP_MaxScale = 3,
  165.     INP_Default = 1,
  166.   })
  167.    
  168.   InSaturation = self:AddInput("Saturation", "Saturation", {
  169.     LINKID_DataType = "Number",
  170.     INPID_InputControl = "SliderControl",
  171.     INP_MinScale = 0,
  172.     INP_MaxScale = 2,
  173.     INP_Default = 1,
  174.   })
  175.  
  176.   InGain = self:AddInput("Gain", "Gain", {
  177.     LINKID_DataType = "Number",
  178.     INPID_InputControl = "SliderControl",
  179.     INP_MinScale = 0,
  180.     INP_MaxScale = 5,
  181.     INP_Default = 1,
  182.   })
  183.    
  184.  
  185.    InGamma = self:AddInput("Gamma", "Gamma", {
  186.     LINKID_DataType = "Number",
  187.     INPID_InputControl = "SliderControl",
  188.     INP_MinScale = 0,
  189.     INP_MaxScale = 2,
  190.     INP_Default = 1,
  191.   })
  192.     self:EndControlNest()
  193.    
  194.    self:BeginControlNest("Alpha", "Alpha", true, {})
  195.   InUnMultiply = self:AddInput("UnMultiply", "UnMultiply", {
  196.     LINKID_DataType = "Number",
  197.     INPID_InputControl = "CheckboxControl",
  198.     INP_Integer = true,
  199.     INP_Default = 0,
  200.   })
  201.      
  202.    
  203.   InAlphaGain = self:AddInput("Alpha Gain", "Alpha Gain", {
  204.     LINKID_DataType = "Number",
  205.     INPID_InputControl = "SliderControl",
  206.     INP_MinScale = 0,
  207.     INP_MaxScale = 1,
  208.     INP_Default = 0,
  209.   })
  210.     self:EndControlNest()
  211.    
  212.   self:BeginControlNest("Advanced", "Advanced", false, {})
  213.   InDepth = self:AddInput("Depth", "Depth", {
  214.     LINKID_DataType = "Number",
  215.     INPID_InputControl = "ComboControl",
  216.     INP_Default = 2.0,
  217.     INP_Integer = true,
  218.     ICD_Width = 1.0,
  219.     CC_LabelPosition = "Vertical",
  220.     INP_DoNotifyChanged = true,
  221.     { CCS_AddString = "int8", },
  222.     { CCS_AddString = "int16", },
  223.     { CCS_AddString = "float 16", },
  224.     { CCS_AddString = "float 32", },
  225.    })
  226.  
  227.   InClip = self:AddInput("Clip Negative Pixels", "Clip Negative Pixels", {
  228.     LINKID_DataType = "Number",
  229.     INPID_InputControl = "CheckboxControl",
  230.     INP_Integer = true,
  231.     ICD_Width = 1.0,
  232.     INP_Default = 1,
  233.   })
  234.    
  235.   DemoButton = self:AddInput("Demo", "Demo", {
  236.         LINKS_Name = "Demo",
  237.         LINKID_DataType = "Number",
  238.         INPID_InputControl = "ButtonControl",
  239.         BTNCS_Execute = [[
  240. ------------------------------------------------------------------------
  241. -- Open a webpage window up using your default web browser
  242.                     platform = (FuPLATFORM_WINDOWS and "Windows") or (FuPLATFORM_MAC and "Mac") or (FuPLATFORM_LINUX and "Linux")
  243.                     function OpenURL(siteName, path)
  244.                         if platform == "Windows" then
  245.                             -- Running on Windows
  246.                             command = "explorer \"" .. path .. "\""
  247.                         elseif platform == "Mac" then
  248.                             -- Running on Mac
  249.                             command = "open \"" .. path .. "\" &"
  250.                          elseif platform == "Linux" then
  251.                             -- Running on Linux
  252.                             command = "xdg-open \"" .. path .. "\" &"
  253.                         else
  254.                             print("[Error] There is an invalid Fusion platform detected")
  255.                             return
  256.                         end
  257.                         os.execute(command)
  258.                         -- print("[Launch Command] ", command)
  259.                         print("[Opening URL] [" .. siteName .. "] " .. path)
  260.                     end
  261.  
  262.                     OpenURL("Demo", "https://youtu.be/2XF8BsJ1J5o")
  263. ]],
  264.     })
  265.        
  266.     self:EndControlNest()
  267.    
  268.   InImage = self:AddInput("Image", "Image", {
  269.     LINKID_DataType = "Image",
  270.     LINK_Main = 1,
  271.     INP_AcceptsGPUImages = true,
  272.     })
  273.  
  274.   OutImage = self:AddOutput("Output", "Output", {
  275.     LINKID_DataType = "Image",
  276.     LINK_Main = 1,
  277.     })
  278. end
  279.  
  280.  
  281.  
  282. function Process(req)
  283.     local src = InImage:GetValue(req)
  284.     local depth = InDepth:GetValue(req).Value
  285.     local dst = Image{ IMG_Like = src, IMG_Depth = depth + 1, IMG_DeferAlloc = true }
  286.  
  287.     local node = DVIPComputeNode(req, "TintensityKernel", TintensityKernel, "TintensityParams", TintensityParams)
  288.     local params = node:GetParamBlock(TintensityParams)
  289.  
  290.     params.red = InColorR:GetValue(req).Value
  291.     params.green = InColorG:GetValue(req).Value
  292.     params.blue = InColorB:GetValue(req).Value
  293.     params.vibrance = InVibrance:GetValue(req).Value
  294.     params.saturation = InSaturation:GetValue(req).Value
  295.     params.gain = InGain:GetValue(req).Value
  296.     params.gamma = InGamma:GetValue(req).Value
  297.     params.unmult = InUnMultiply:GetValue(req).Value
  298.     params.again = InAlphaGain:GetValue(req).Value
  299.     params.invert = InInvert:GetValue(req).Value
  300.     params.clip = InClip:GetValue(req).Value
  301.     params.srcCompOrder = src:IsMask() and 1 or 15
  302.  
  303.     node:SetParamBlock(params)
  304.  
  305.     node:AddInput("src", src)
  306.     node:AddOutput("dst", dst)
  307.  
  308.     local ok = node:RunSession(req)
  309.  
  310.     if not ok then
  311.     dst = nil
  312.     end
  313.        
  314.  
  315.        
  316.        
  317.     OutImage:Set(req, dst)
  318. end

User avatar
intelligent machine
Fusionista
Posts: 508
Joined: Fri May 13, 2016 10:01 pm
Answers: 3
Location: Austin, Texas, USA
Real name: Sam Treadway
Been thanked: 13 times
Contact:

Re: [RC] Vibrance Fuse

#158

Post by intelligent machine » Wed Mar 25, 2020 11:11 am

Shem Namo wrote:
Wed Mar 25, 2020 10:40 am
Correct, I'm trying to invert the luminance, but not the color. Like in the original plug-in.
From that first page you linked to:
"Vibrance, on the other hand, is a slightly less blunt instrument. The equation takes into consideration how saturated the original value is. The higher the value, the less the equation impacts it."
This seems to be doing the opposite from my tests: More saturated areas of an the original image are impacted the most.

Here is a screenshot from a random image I found on pexels. On the left is the original image and the right has the vibrance value set to 2 and all other controls at default values. Look closely at the pixelation of the blues in the sky. The less saturated colors as the top of the building are barely touched while the pink and blue are oversaturated:
Image

User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#159

Post by Shem Namo » Wed Mar 25, 2020 11:13 am

I see, You absolutely right!!
Did we get something wrong with the algorithm?

Thanks

User avatar
Midgardsormr
Fusionator
Posts: 1591
Joined: Wed Nov 26, 2014 8:04 pm
Answers: 11
Location: Los Angeles, CA, USA
Been thanked: 51 times
Contact:

Re: [RC] Vibrance Fuse

#160

Post by Midgardsormr » Wed Mar 25, 2020 12:15 pm

Possibly the LERP is backward. That's the first thing I usually check whenever one is present. In these lines:

Code: Select all

         col.x *= (1-amount);
         col.y *= (1-amount);
         col.z *= (1-amount);
                 
         col.x += (channelm * amount);
         col.y += (channelm * amount);
         col.z += (channelm * amount);
Try swapping (1-amount) and amount to see if that makes it work as expected.

User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#161

Post by Shem Namo » Wed Mar 25, 2020 12:50 pm

It does look softer in terms of saturation, but it also turns a color into it's inverse.
I'll look online for more vibrance algorithms for now. maybe there's something interesting there.

Thanks,
David

User avatar
intelligent machine
Fusionista
Posts: 508
Joined: Fri May 13, 2016 10:01 pm
Answers: 3
Location: Austin, Texas, USA
Real name: Sam Treadway
Been thanked: 13 times
Contact:

Re: [RC] Vibrance Fuse

#162

Post by intelligent machine » Wed Mar 25, 2020 12:55 pm

...Not quite there yet but I think changing the amount variable to something like this will get a step closer:

Code: Select all

		//The Vibrance math goes here:
		float channelaverage = (col.x + col.y + col.z) / 3.0f;
		float channelmin = _fminf(col.x, _fminf(col.y, col.z));
		float channelmax = _fmaxf(col.x, _fmaxf(col.y, col.z));
		float amount = (((channelmax + channelmin) / 2) + (1-channelaverage)) * (-params->vibrance);
		
		col.x *= (1-amount);
		col.y *= (1-amount);
		col.z *= (1-amount);

		col.x += (channelmax * amount);
		col.y += (channelmax * amount);
		col.z += (channelmax * amount);

User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#163

Post by Shem Namo » Thu Mar 26, 2020 2:56 am

Thanks @intelligent machine !!
This looks interesting.

Here's what I found online I don't really understand most of it.
I think some of these are openGL. The syntax looks similar to DCTL, but I almost sure that there are a bunch of operations
that DCTL doesn't have in the API.

They all seem to work differently.
Maybe this can help us understand what's missing in our vibrance alogrithm.

Thanks again,
David.
  1. Filter.register "vibrance", (adjust) ->
  2.   adjust *= -1
  3.  
  4.   @process "vibrance", (rgba) ->
  5.     max = Math.max rgba.r, rgba.g, rgba.b
  6.     avg = (rgba.r + rgba.g + rgba.b) / 3
  7.     amt = ((Math.abs(max - avg) * 2 / 255) * adjust) / 100
  8.  
  9.     rgba.r += (max - rgba.r) * amt if rgba.r isnt max
  10.     rgba.g += (max - rgba.g) * amt if rgba.g isnt max
  11.     rgba.b += (max - rgba.b) * amt if rgba.b isnt max
  12.     rgba
that came from this page: http://camanjs.com/docs/filters.html
  1. /**
  2.  * @filter       Vibrance
  3.  * @description  Modifies the saturation of desaturated colors, leaving saturated colors unmodified.
  4.  * @param amount -1 to 1 (-1 is minimum vibrance, 0 is no change, and 1 is maximum vibrance)
  5.  */
  6. function vibrance(amount) {
  7.     gl.vibrance = gl.vibrance || new Shader(null, '\
  8.        uniform sampler2D texture;\
  9.        uniform float amount;\
  10.        varying vec2 texCoord;\
  11.        void main() {\
  12.            vec4 color = texture2D(texture, texCoord);\
  13.            float average = (color.r + color.g + color.b) / 3.0;\
  14.            float mx = max(color.r, max(color.g, color.b));\
  15.            float amt = (mx - average) * (-amount * 3.0);\
  16.            color.rgb = mix(color.rgb, vec3(mx), amt);\
  17.            gl_FragColor = color;\
  18.        }\
  19.    ');
  20.  
  21.     simpleShader.call(this, gl.vibrance, {
  22.         amount: clamp(-1, amount, 1)
  23.     });
  24.  
  25.     return this;
  26. }
That one came from his pagehttps://github.com/danielgtaylor/glfx.j ... ance.js#L1
  1.    /*-----------------------------------------------------------.  
  2.   /                          Vibrance                           /
  3.   '-----------------------------------------------------------*/
  4. /*
  5.   by Christian Cann Schuldt Jensen ~ CeeJay.dk
  6.  
  7.   Vibrance intelligently boosts the saturation of pixels
  8.   so pixels that had little color get a larger boost than pixels that had a lot.
  9.  
  10.   This avoids oversaturation of pixels that were already very saturated.
  11. */
  12.  
  13. float4 VibrancePass( float4 colorInput )
  14. {
  15.     float4 color = colorInput; //original input color
  16.     float3 lumCoeff = float3(0.2126, 0.7152, 0.0722);  //Values to calculate luma with
  17.  
  18.     float luma = dot(lumCoeff, color.rgb); //calculate luma (grey)
  19.    
  20.     float max_color = max(colorInput.r, max(colorInput.g,colorInput.b)); //Find the strongest color
  21.     float min_color = min(colorInput.r, min(colorInput.g,colorInput.b)); //Find the weakest color
  22.    
  23.     float color_saturation = max_color - min_color; //The difference between the two is the saturation
  24.  
  25.     //color.rgb = lerp(luma, color.rgb, (1.0 + (Vibrance * (1.0 - color_saturation)))); //extrapolate between luma and original by 1 + (1-saturation) - simple
  26.  
  27.     color.rgb = lerp(luma, color.rgb, (1.0 + (Vibrance * (1.0 - (sign(Vibrance) * color_saturation))))); //extrapolate between luma and original by 1 + (1-saturation) - current
  28.  
  29.     //color.rgb = lerp(luma, color.rgb, 1.0 + (1.0-pow(color_saturation, 1.0 - (1.0-Vibrance))) ); //pow version
  30.  
  31.     return color; //return the result
  32.     //return color_saturation.xxxx; //Visualize the saturation
  33. }
This one came from here: https://github.com/terrasque/sweetfxui/ ... Vibrance.h
  1. x = max(r, g, b)
  2. y = min(r, g, b)
  3. gray = toGray(unGamma(r, g, b))
  4. scale = input
  5. if x == r:
  6. t = min(1, abs((g - b) / (x - y)))
  7. scale = scale * (1 + t) * 0.5
  8. a = (x - y) / 255
  9. scale1 = scale * (2 - a)
  10. scale2 = 1 + scale1 * (1 - a)
  11. sub = y * scale1
  12. r = unGamma(r * scale2 - sub)
  13. g = unGamma(g * scale2 - sub)
  14. b = unGamma(b * scale2 - sub)
  15. gray2 = toGray(r, g, b)
  16. r *= gray / gray2
  17. g *= gray / gray2
  18. b *= gray / gray2
  19. m = max(r, g, b)
  20. if Gamma(m) > 255:
  21. scale = (unGamma(255) - gray2) / (m - gray2)
  22. r = (r - gray2) * scale + gray2
  23. g = (g - gray2) * scale + gray2
  24. b = (b - gray2) * scale + gray2
  25. (r, g, b) = Gamma(r, g, b)
and this last one came from here: https://android.developreference.com/ar ... filters%3F

This is just a bunch of code I found online, I'm not sure about how much of it is relevant.

Thanks again,

User avatar
intelligent machine
Fusionista
Posts: 508
Joined: Fri May 13, 2016 10:01 pm
Answers: 3
Location: Austin, Texas, USA
Real name: Sam Treadway
Been thanked: 13 times
Contact:

Re: [RC] Vibrance Fuse

#164

Post by intelligent machine » Thu Mar 26, 2020 10:57 am

Okay....so I tried a variety of these methods and ended up kinda rewriting all of them including something custom that I think "visually" works better. I haven't tested it against a lot of scenarios so let me know what you think.

In this version I've only been focused on the Vibrance function and tested against color photos.
The tinting feature needs to be worked on afterward and I think there may be an issue with the order in which these adjustments are stacked/combined.

I added a dropdown combo control to select from 4 different "vibrance" methods!! :D
The default, simply named "Vibrant" is the custom version I wrote as mentioned above. It is loosely based on a few other methods.
The other three are taken partly from different methods researched in the links you provided yesterday.
They still need some fine-tuning (how much the intensity slider multiplies over the algorithm, etc.).

The custom version I wrote, "Vibrant", gives preference to the less saturated pixels and also has a special treatment for when the dominant color is red (higher visual perception). I think it looks great but you be the judge(s). Each version has it's own strengths and weaknesses but having options rather than trying to perfect and force a single one is better, right? Right?

Also, I think the Saturation control should be renamed to maybe "Saturation Boost" unless we want to setup an either/or for the "Vibrance/Saturation" controls.
Code: [Select all] [Expand/Collapse] [Download] ( Vibrance.fuse)
  1. FuRegisterClass("Vibrance", CT_Tool, {
  2.     REGS_Category = "Fuses\\Color",
  3.     REGS_OpIconString = "TNT",
  4.     REGS_OpDescription = "Vibrance",
  5.     REG_Version = 1.0,
  6.     REGS_Company = "Learn Now FX",
  7.     REGS_URL = "http://www.youtube.com/LearnNowFX",
  8. })
  9.  
  10.  
  11. VibranceParams = [[
  12.     float red;
  13.     float green;
  14.     float blue;
  15.     float method;
  16.     float vibrance;
  17.     float saturation;
  18.     float gain;
  19.     float gamma;
  20.     int unmult;
  21.     int invert;
  22.     float again;
  23.     int clip;
  24.     int srcCompOrder;
  25. ]]
  26.  
  27. VibranceKernel = [[
  28.     __KERNEL__ void VibranceKernel(__CONSTANTREF__ VibranceParams *params, __TEXTURE2D__ src, __TEXTURE2D_WRITE__ dst) {
  29.         DEFINE_KERNEL_ITERATORS_XY(x, y);
  30.         float4 col = _tex2DVecN(src, x, y, params->srcCompOrder);
  31.  
  32.         // The Invert Control math goes here:
  33.         if (params->invert == 1) {
  34.             col.x = 1 - col.x;
  35.             col.y = 1 - col.y;
  36.             col.z = 1 - col.z;
  37.         }
  38.  
  39.  
  40.         // The Color Picker math goes here:
  41.         if (params->invert == 0) {
  42.             col.x *=  params->red;
  43.             col.y *=  params->green;
  44.             col.z *=  params->blue;
  45.         }
  46.  
  47.         if (params->invert == 1) {
  48.             float nRed = params->red + 0.0001f;
  49.             float nGreen = params->green + 0.0001f;
  50.             float nBlue = params->blue + 0.0001f;
  51.             col.x = powr(col.x, 1/nRed);
  52.             col.y = powr(col.y, 1/nGreen);
  53.             col.z = powr(col.z, 1/nBlue);
  54.         }
  55.  
  56.  
  57.         // The Gain control math goes here:
  58.         col.x *=  params->gain;
  59.         col.y *=  params->gain;
  60.         col.z *=  params->gain;
  61.  
  62.  
  63.         // The Gamma control math goes here:
  64.         col.x = powr(col.x, 1/_fmaxf(params->gamma, 0.01f));
  65.         col.y = powr(col.y, 1/_fmaxf(params->gamma, 0.01f));
  66.         col.z = powr(col.z, 1/_fmaxf(params->gamma, 0.01f));
  67.  
  68.  
  69.         // The UnMultiply math goes here:
  70.         float luma = col.x * 0.299f + col.y * 0.587f + col.z * 0.114f;
  71.         if (params->unmult == 1) {
  72.             col.w *= (luma + params->again);
  73.         }
  74.  
  75.  
  76.         //The Vibrance math goes here:
  77.         float cMax = _fmaxf(col.x, _fmaxf(col.y, col.z));  //find the strongest color
  78.         float cMin = _fminf(col.x, _fminf(col.y, col.z));  //find the weakest color
  79.         float cSat = cMax - cMin; //The diff of min and max color is the Saturation
  80.         float cAverage = (col.x + col.y + col.z) / 3.0f;
  81.         float vibrance = params->vibrance;
  82.         float scale = 1.0;
  83.                 //custom version - special treatemnt for reds
  84.         if (params->method == 0) {
  85.             scale = vibrance * (2 * (1 - cSat)); //Less Saturated given higher priority
  86.             if (cMax == col.x) { //special treatment when red is max
  87.                 scale = vibrance * (abs(col.y - col.z) / (cSat / cAverage)) * (1 - cSat);
  88.             }
  89.                     //Clamp
  90.             scale = _fminf(1, scale);
  91.                     //Lerp
  92.             col.x = (col.x - cMax) * scale + col.x;
  93.             col.y = (col.y - cMax) * scale + col.y;
  94.             col.z = (col.z - cMax) * scale + col.z;
  95.         }
  96.                 //Simple
  97.         if (params->method == 1) {
  98.             scale = vibrance * ( 0.25 * (1 - cAverage));
  99.                     //Clamp
  100.             scale = _fminf(1, scale);
  101.                     //Lerp
  102.             col.x = (col.x - cMin) * scale + col.x;
  103.             col.y = (col.y - cMin) * scale + col.y;
  104.             col.z = (col.z - cMin) * scale + col.z;
  105.         }
  106.                 //complex
  107.         if (params->method == 2) {
  108.             scale = vibrance * (1.0 - (sign(vibrance) * cAverage));
  109.                     //Clamp
  110.             scale = _fminf(1, scale);
  111.                     //Lerp
  112.             col.x = (col.x - cAverage) * scale + col.x;
  113.             col.y = (col.y - cAverage) * scale + col.y;
  114.             col.z = (col.z - cAverage) * scale + col.z;
  115.         }
  116.                 //pow version
  117.         if (params->method == 3) {
  118.             scale = 1.0 - powr(cSat, 1.0 - (1.0 - vibrance));
  119.                     //Clamp
  120.             scale = _fminf(1, scale);
  121.                     //Lerp
  122.             col.x = (col.x - cSat) * scale + col.x;
  123.             col.y = (col.y - cSat) * scale + col.y;
  124.             col.z = (col.z - cSat) * scale + col.z;
  125.         }
  126.  
  127.  
  128.         // The Saturation math goes here:
  129.         col.x = params->saturation * col.x + (1-params->saturation) * luma;
  130.         col.y = params->saturation * col.y + (1-params->saturation) * luma;
  131.         col.z = params->saturation * col.z + (1-params->saturation) * luma;
  132.  
  133.  
  134.         //the Clip negative Pixels math goes here:
  135.         if (params->clip == 1) {
  136.             if (col.x < 0) { col.x = 0; }
  137.             if (col.y < 0) { col.y = 0; }
  138.             if (col.z < 0) { col.z = 0; }
  139.         }
  140.  
  141.  
  142.         _tex2DVec4Write(dst, x, y, col);
  143.     }
  144. ]]
  145.  
  146. OpenDemoURL = [[
  147. -- Open a webpage window up using your default web browser
  148.     platform = (FuPLATFORM_WINDOWS and "Windows") or (FuPLATFORM_MAC and "Mac") or (FuPLATFORM_LINUX and "Linux")
  149.     function OpenURL(siteName, path)
  150.         if platform == "Windows" then
  151.             -- Running on Windows
  152.             command = "explorer \"" .. path .. "\""
  153.         elseif platform == "Mac" then
  154.             -- Running on Mac
  155.             command = "open \"" .. path .. "\" &"
  156.         elseif platform == "Linux" then
  157.             -- Running on Linux
  158.             command = "xdg-open \"" .. path .. "\" &"
  159.         else
  160.             print("[Error] There is an invalid Fusion platform detected")
  161.             return
  162.         end
  163.  
  164.         os.execute(command)
  165.         -- print("[Launch Command] ", command)
  166.         print("[Opening URL] [" .. siteName .. "] " .. path)
  167.     end
  168.  
  169.     OpenURL("Demo", "https://youtu.be/2XF8BsJ1J5o")
  170. ]]
  171.  
  172. function Create()
  173.     self:BeginControlNest("Color", "Color", true, {})
  174.         InColorR= self:AddInput("Red", "Red", {
  175.             ICS_Name            = "Color",
  176.             LINKID_DataType     = "Number",
  177.             INPID_InputControl  = "ColorControl",
  178.             INP_Default         = 1,
  179.             INP_MaxScale        = 1.0,
  180.             ICD_Center          = 1.0,
  181.             INP_DoNotifyChanged = true,
  182.             CLRC_ShowWheel      = False,
  183.             IC_ControlGroup     = 1,
  184.             IC_ControlID        = 0,
  185.             IC_Visible          = true,
  186.         })
  187.         InColorG = self:AddInput("Green", "Green", {
  188.             LINKID_DataType     = "Number",
  189.             INPID_InputControl  = "ColorControl",
  190.             INP_Default         = 1,
  191.             INP_DoNotifyChanged = true,
  192.             IC_ControlGroup     = 1,
  193.             IC_ControlID        = 1,
  194.         })
  195.         InColorB = self:AddInput("Blue", "Blue", {
  196.             LINKID_DataType     = "Number",
  197.             INPID_InputControl  = "ColorControl",
  198.             INP_Default         = 1,
  199.             INP_DoNotifyChanged = true,
  200.             IC_ControlGroup     = 1,
  201.             IC_ControlID        = 2,
  202.         })
  203.         InInvert = self:AddInput("Invert", "Invert", {
  204.             LINKID_DataType = "Number",
  205.             INPID_InputControl = "CheckboxControl",
  206.             INP_Integer = true,
  207.             ICD_Width = 0.5,
  208.             INP_Default = 0,
  209.         })  
  210.     self:EndControlNest()
  211.    
  212.     self:BeginControlNest("Intensity", "Intensity", true, {})
  213.         InVibranceMethod = self:AddInput("Vibrance Method", "VibranceMethod", {
  214.             LINKID_DataType = "Number",
  215.             INPID_InputControl = "ComboControl",
  216.             INP_MinScale = 0,
  217.             INP_MaxAllowed = 3,
  218.             INP_MinAllowed = 0,
  219.             INP_MaxAllowed = 3,
  220.             INP_Integer = true,
  221.             ICD_Width = 1.0,
  222.             CC_LabelPosition = "Horizontal",
  223.             INP_DoNotifyChanged = true,
  224.             { CCS_AddString = "Vibrant", },
  225.             { CCS_AddString = "Simple", },
  226.             { CCS_AddString = "Complex", },
  227.             { CCS_AddString = "Power", },
  228.         })
  229.         InVibrance = self:AddInput("Vibrance", "Vibrance", {
  230.             LINKID_DataType = "Number",
  231.             INPID_InputControl = "SliderControl",
  232.             INP_Default = 0,
  233.             INP_MinScale = 0,
  234.             INP_MaxScale = 3,
  235.             INP_Default = 1,
  236.         })
  237.         InSaturation = self:AddInput("Saturation", "Saturation", {
  238.             LINKID_DataType = "Number",
  239.             INPID_InputControl = "SliderControl",
  240.             INP_MinScale = 0,
  241.             INP_MaxScale = 2,
  242.             INP_Default = 1,
  243.         })
  244.         InGain = self:AddInput("Gain", "Gain", {
  245.             LINKID_DataType = "Number",
  246.             INPID_InputControl = "SliderControl",
  247.             INP_MinScale = 0,
  248.             INP_MaxScale = 5,
  249.             INP_Default = 1,
  250.         })
  251.         InGamma = self:AddInput("Gamma", "Gamma", {
  252.             LINKID_DataType = "Number",
  253.             INPID_InputControl = "SliderControl",
  254.             INP_MinScale = 0,
  255.             INP_MaxScale = 2,
  256.             INP_Default = 1,
  257.         })
  258.     self:EndControlNest()
  259.    
  260.     self:BeginControlNest("Alpha", "Alpha", true, {})
  261.         InUnMultiply = self:AddInput("UnMultiply", "UnMultiply", {
  262.             LINKID_DataType = "Number",
  263.             INPID_InputControl = "CheckboxControl",
  264.             INP_Integer = true,
  265.             INP_Default = 0,
  266.         })
  267.         InAlphaGain = self:AddInput("Alpha Gain", "Alpha Gain", {
  268.             LINKID_DataType = "Number",
  269.             INPID_InputControl = "SliderControl",
  270.             INP_MinScale = 0,
  271.             INP_MaxScale = 1,
  272.             INP_Default = 0,
  273.         })
  274.     self:EndControlNest()
  275.    
  276.     self:BeginControlNest("Advanced", "Advanced", false, {})
  277.         InDepth = self:AddInput("Depth", "Depth", {
  278.             LINKID_DataType = "Number",
  279.             INPID_InputControl = "ComboControl",
  280.             INP_Default = 2.0,
  281.             INP_Integer = true,
  282.             ICD_Width = 1.0,
  283.             CC_LabelPosition = "Vertical",
  284.             INP_DoNotifyChanged = true,
  285.             { CCS_AddString = "int8", },
  286.             { CCS_AddString = "int16", },
  287.             { CCS_AddString = "float 16", },
  288.             { CCS_AddString = "float 32", },
  289.         })
  290.         InClip = self:AddInput("Clip Negative Pixels", "Clip Negative Pixels", {
  291.             LINKID_DataType = "Number",
  292.             INPID_InputControl = "CheckboxControl",
  293.             INP_Integer = true,
  294.             ICD_Width = 1.0,
  295.             INP_Default = 1,
  296.         })
  297.         DemoButton = self:AddInput("Demo", "Demo", {
  298.             LINKS_Name = "Demo",
  299.             LINKID_DataType = "Number",
  300.             INPID_InputControl = "ButtonControl",
  301.             BTNCS_Execute = OpenDemoURL,
  302.         })
  303.     self:EndControlNest()
  304.    
  305.     InImage = self:AddInput("Image", "Image", {
  306.         LINKID_DataType = "Image",
  307.         LINK_Main = 1,
  308.         INP_AcceptsGPUImages = true,
  309.     })
  310.     OutImage = self:AddOutput("Output", "Output", {
  311.         LINKID_DataType = "Image",
  312.         LINK_Main = 1,
  313.     })
  314. end
  315.  
  316.  
  317. function Process(req)
  318.     local src = InImage:GetValue(req)
  319.     local depth = InDepth:GetValue(req).Value
  320.     local dst = Image{ IMG_Like = src, IMG_Depth = depth + 1, IMG_DeferAlloc = true }
  321.    
  322.     local node = DVIPComputeNode(req, "VibranceKernel", VibranceKernel, "VibranceParams", VibranceParams)
  323.     local params = node:GetParamBlock(VibranceParams)
  324.  
  325.     params.red = InColorR:GetValue(req).Value
  326.     params.green = InColorG:GetValue(req).Value
  327.     params.blue = InColorB:GetValue(req).Value
  328.     params.method = InVibranceMethod:GetValue(req).Value
  329.     params.vibrance = InVibrance:GetValue(req).Value
  330.     params.saturation = InSaturation:GetValue(req).Value
  331.     params.gain = InGain:GetValue(req).Value
  332.     params.gamma = InGamma:GetValue(req).Value
  333.     params.unmult = InUnMultiply:GetValue(req).Value
  334.     params.again = InAlphaGain:GetValue(req).Value
  335.     params.invert = InInvert:GetValue(req).Value
  336.     params.clip = InClip:GetValue(req).Value
  337.     params.srcCompOrder = src:IsMask() and 1 or 15
  338.  
  339.     node:SetParamBlock(params)
  340.  
  341.     node:AddInput("src", src)
  342.     node:AddOutput("dst", dst)
  343.  
  344.     local ok = node:RunSession(req)
  345.  
  346.     if not ok then
  347.         dst = nil
  348.     end
  349.        
  350.     OutImage:Set(req, dst)
  351. end
Added in 2 minutes 13 seconds:
Also, I changed (shortened) some of the variable names and cleaned up the formatting so please stick to this version.
I included comments for what each of the code sections do (i.e. "Clamp", "lerp", etc.)

User avatar
Shem Namo
Fusionista
Posts: 349
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 6 times

Re: [RC] Vibrance Fuse

#165

Post by Shem Namo » Thu Mar 26, 2020 11:55 am

WOW!!
You really put a lot of effort into this!
4 Methods, that's just amazing!

Between you and Bryan, you guys basically wrote this whole fuse.

It's not working on my end for some reason.
maybe there's a little syntax error or something.

I really appreciate your support,
This fuse wouldn't have been anything if it weren't for you guys.
Thanks for taking your time to do this.