[DEV] 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]

Development topics only, please. For generic questions, how-to's, questions and inquiries about existing tools etc, please go to the appropriate other forums.
Midgardsormr
Fusionator
Posts: 1488
Joined: Wed Nov 26, 2014 8:04 pm
Location: Los Angeles, CA, USA
Been thanked: 34 times
Contact:

Re: [DEV] Vibrance Fuse

Shem Namo wrote:
Mon Feb 10, 2020 12:19 pm
How I can I calculate to change the color of the input image to some thing else, using the color picker?
There are a few different ways you could go about this. It depends on exactly what it is you hope to accomplish. Probably the simplest method, and the one I use in XGlow, is to just multiply the channels by the desired color. Suppose you have assigned the inputs of your colorControl to the variables `red`, `green`, `blue`, and `alpha`.

Code: Select all

``````p.R = p.R * red
p.G = p.G * green
p.B = p.B * blue
p.A = p.A * alpha
``````
If your pixel is pure white, it will be changed to the color you chose with the picker. This is identical to multiplying the image by a Background using a ChannelBooleans. The trouble is that it can be difficult to predict exactly what hue you'll wind up with if you're recoloring something that isn't mono-chromatic. Also, it will typically reduce the luminance of your image.
How can I turn black pixels transparent?
Probably the best bet would be to luma key the image before sending it to the fuse. If you want to do it in the fuse itself, you'll need to decide what you mean—do you want just black pixels to be transparent? Or a luminance key of some kind? I mean, if all you want is to turn (0,0,0,1) into (0,0,0,0), then that's as simple as:

Code: Select all

``````if p.R == 0 and p.G == 0 and p.B == 0 then
p.A = 0
end
``````
But you'll wind up with some terrible aliasing. You could multiply the alpha by an average of the channels, perhaps:

Code: Select all

``````channelAverage = (p.R + p.G + p.B) / 3
p.A = p.A * channelAverage
``````
That's a very quick-and-dirty luma key. For a better one, you'd have to do a weighted average of the three channels. Coefficients vary depending on your color space, but for Rec.709, I think this is correct:

Code: Select all

``````luma = p.R * 0.299 + p.G * 0.587 + p.B * 0.114
p.A = p.A * luma
``````
What is the math for a saturation control(that can also increase saturation)?
Complicated. Fortunately, we don't really need to always do that math, as there is a `Saturate()` method built in to the API. Assuming we have `img` as our source and want to apply the saturation change to `out`, and we have a control with its value held in the `saturation` variable:

Code: Select all

``````out = img:Copy() -- Copy the image directly to the output image.
out:Saturate(saturation, saturation, saturation)   -- Change the output image's saturation.
``````
Saturate() takes three arguments, one for each channel. Usually these will be identical, as above. There may, however, be circumstances where you'd want to only do the operation on one channel, or adjust the weighting to support different color spaces. In any case, a value of 1.0 represents no change in saturation.

Shem Namo
Fusioneer
Posts: 224
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 3 times

Re: [DEV] Vibrance Fuse

Thanks A million Bryan,
I can't believe how many things are actually working
the only thing I can't get to work, is the saturation control.
Other than that everything else works.
Where was I supposed to put the

Code: Select all

``````out = img:Copy() -- Copy the image directly to the output image.
out:Saturate(saturation, saturation, saturation)   -- Change the output image's saturation.``````

block?

Thanks again,
David.
1. FuRegisterClass("Vibrance", CT_Tool, {
2.     REGS_Category = "Fuses\\Color",
3.     REGS_OpIconString = "vib",
4.     REGS_OpDescription = "Vibrance",
6.     REG_NoBlendCtrls = true,
7.     REG_NoObjMatCtrls = true,
8.     REG_NoMotionBlurCtrls = true,
9.     })
10.
11.     function Create()
12.
14.         ICS_Name            = "Color",
16.         INPID_InputControl  = "ColorControl",
17.         INP_Default         = 0,
18.         INP_MaxScale        = 1.0,
19.         ICD_Center          = 1.0,
20.         INP_DoNotifyChanged = true,
21.         CLRC_ShowWheel      = False,
22.         IC_ControlGroup     = 1,
23.         IC_ControlID        = 0,
24.         IC_Visible          = true,
25.         })
26.
27.     InColorG = self:AddInput("Green", "Green", {
29.         INPID_InputControl  = "ColorControl",
30.         INP_Default         = 0.5,
31.         INP_DoNotifyChanged = true,
32.         IC_ControlGroup     = 1,
33.         IC_ControlID        = 1,
34.         })
35.
36.     InColorB = self:AddInput("Blue", "Blue", {
38.         INPID_InputControl  = "ColorControl",
39.         INP_Default         = 1,
40.         INP_DoNotifyChanged = true,
41.         IC_ControlGroup     = 1,
42.         IC_ControlID        = 2,
43.         })
44.
45.         InMatte = self:AddInput("Matte", "UnMultiply", {
47.             INPID_InputControl = "CheckboxControl",
48.             INP_Integer = true,
49.             INP_Default = 1,
50.             })
51.
52.    InVibrance = self:AddInput("Vibrance", "Vibrance", {
54.         INPID_InputControl = "SliderControl",
55.         INP_MinScale = -0,
56.         INP_MaxScale = 20,
57.         INP_Default = 3,
58.         })
59.
60.      InSaturation = self:AddInput("Saturation", "Saturation", {
62.         INPID_InputControl = "SliderControl",
63.         INP_MinScale = -0,
64.         INP_MaxScale = 2,
65.         INP_Default = 1,
66.         })
67.
68.      InGain = self:AddInput("Gain", "Gain", {
70.            INPID_InputControl = "SliderControl",
71.          INP_MinScale = -0,
72.        INP_MaxScale = 5,
73.            INP_Default = 2,
74.            })
75.
76.
77.     InImage = self:AddInput("Input", "Input", {
80.         })
81.
82.     OutImage = self:AddOutput("Output", "Output", {
85.         })
86.     end
87.
88.       function colorFunc(p,red,green,blue, alpha)
89.         p.R = p.R * red
90.     p.G = p.G * green
91.     p.B = p.B * blue
92.      return p
93.         end
94.
95.      function matteFunc(p, matte)
96.       p.A =p.A * matte
97.                 return p
98.         end
99.
100.         function vibranceFunc(p, vibrance)
101.     local channelAverage = (p.R + p.G + p.B) / 3                  -- From Inter, i1
102.     local channelMaximum = math.max(p.R, math.max(p.G, p.B))      -- From Inter, i2
103.     local amount = (channelMaximum - channelAverage) * (-vibrance * 3)
104.           p.R = p.R * (1-amount) + channelMaximum * amount        -- From channel expressions
105.           p.G = p.G * (1-amount) + channelMaximum * amount
106.           p.B = p.B * (1-amount) + channelMaximum * amount
107.     return p
108.     end
109.
110.         function satFunc(saturation,saturation, saturation)
111.         out = img:Copy()
112.     out:Saturate(saturation, saturation, saturation)
113.     Saturate = Saturate *   saturation
114.         return p
115.         end
116.
117.      function gainFunc(p, gain)
118.           p.R = p.R * gain
119.                 p.G = p.G * gain
120.                 p.B = p.B * gain
121.                 return p
122.                 end
123.
124.
125.
126. --It appears that I had the functions in the wrong order before.
127.         function Process(req)
128.       local img = InImage:GetValue(req)
129.             local red = InColorR:GetValue(req).Value
130.             local green = InColorG:GetValue(req).Value
131.             local blue = InColorB:GetValue(req).Value
132.             local matte = InMatte:GetValue(req).Value
133.       local vibrance = InVibrance:GetValue(req).Value
134.           local saturation = InSaturation:GetValue(req).Value
135.             local gain = InGain:GetValue(req).Value
136.             local out = Image({ IMG_Like = img })
137.
138.
139.
140.       local p = Pixel()         -- Create the pixel object only once
141.       for x=0, img.Width - 1 do
142.       for y = 0, img.Height - 1 do
143.         img:GetPixel(x,y, p)
144.                 p = colorFunc(p, red, green, blue)
145.                 p = matteFunc(p, matte)
146.         p = vibranceFunc(p, vibrance)
147.                 p = saturationFunc(saturation,saturation, saturation)
148.                 p = gainFunc(p, gain)
149.                 out:SetPixel(x,y, p)
150.     end
151. end
152.
153.
154.
155.
156.     OutImage:Set(req, out)
157. end

thibaud
Fusioneer
Posts: 160
Joined: Thu Sep 04, 2014 1:23 am
Been thanked: 2 times
Contact:

Re: [DEV] Vibrance Fuse

Great to see you picking up on coding.
but before going any further please do yourself and others a favor by applying some basic formatting principles...
Some guidelines here or here

Shem Namo
Fusioneer
Posts: 224
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 3 times

Re: [DEV] Vibrance Fuse

Thanks, @thibaud !
All of the controls seem to be functioning the way I would want them to,
but the sat control is very fiddly.
Is there any method to normalize only this control?
Thanks,
David.

P.S, what do you guys think if we call this tool something else,
so it doesn't look like we're knocking of Video Copilot?
Thanks again
1. FuRegisterClass("Vibrance", CT_Tool, {
2.     REGS_Category = "Fuses\\Color",
3.     REGS_OpIconString = "vib",
4.     REGS_OpDescription = "Vibrance",
6.     REG_NoBlendCtrls = true,
7.     REG_NoObjMatCtrls = true,
8.     REG_NoMotionBlurCtrls = true,
9.     })
10.
11.     function Create()
12.
14.         ICS_Name            = "Color",
16.         INPID_InputControl  = "ColorControl",
17.         INP_Default         = 0,
18.         INP_MaxScale        = 1.0,
19.         ICD_Center          = 1.0,
20.         INP_DoNotifyChanged = true,
21.         CLRC_ShowWheel      = False,
22.         IC_ControlGroup     = 1,
23.         IC_ControlID        = 0,
24.         IC_Visible          = true,
25.         })
26.
27.     InColorG = self:AddInput("Green", "Green", {
29.         INPID_InputControl  = "ColorControl",
30.         INP_Default         = 0.5,
31.         INP_DoNotifyChanged = true,
32.         IC_ControlGroup     = 1,
33.         IC_ControlID        = 1,
34.         })
35.
36.     InColorB = self:AddInput("Blue", "Blue", {
38.         INPID_InputControl  = "ColorControl",
39.         INP_Default         = 1,
40.         INP_DoNotifyChanged = true,
41.         IC_ControlGroup     = 1,
42.         IC_ControlID        = 2,
43.         })
44.
45.         InMatte = self:AddInput("Matte", "UnMultiply", {
47.             INPID_InputControl = "CheckboxControl",
48.             INP_Integer = true,
49.             INP_Default = 1,
50.             })
51.
52.    InVibrance = self:AddInput("Vibrance", "Vibrance", {
54.         INPID_InputControl = "SliderControl",
55.         INP_MinScale = -0,
56.         INP_MaxScale = 20,
57.         INP_Default = 3,
58.         })
59.
60.      InSaturation = self:AddInput("Saturation", "Saturation", {
62.         INPID_InputControl = "SliderControl",
63.         INP_MinScale = -0,
64.         INP_MaxScale = 2,
65.         INP_Default = 1,
66.         })
67.
68.      InGain = self:AddInput("Gain", "Gain", {
70.            INPID_InputControl = "SliderControl",
71.          INP_MinScale = -0,
72.        INP_MaxScale = 5,
73.            INP_Default = 2,
74.            })
75.
76.
77.     InImage = self:AddInput("Input", "Input", {
80.         })
81.
82.     OutImage = self:AddOutput("Output", "Output", {
85.         })
86.     end
87.
88.       function colorFunc(p,red,green,blue, alpha)
89.         p.R = p.R * red
90.     p.G = p.G * green
91.     p.B = p.B * blue
92.      return p
93.         end
94.
95.      function matteFunc(p, matte)
96.       p.A =p.A * matte
97.                 return p
98.         end
99.
100.         function vibranceFunc(p, vibrance)
101.     local channelAverage = (p.R + p.G + p.B) / 3                  -- From Inter, i1
102.     local channelMaximum = math.max(p.R, math.max(p.G, p.B))      -- From Inter, i2
103.     local amount = (channelMaximum - channelAverage) * (-vibrance * 3)
104.           p.R = p.R * (1-amount) + channelMaximum * amount        -- From channel expressions
105.           p.G = p.G * (1-amount) + channelMaximum * amount
106.           p.B = p.B * (1-amount) + channelMaximum * amount
107.     return p
108.     end
109.
110.
111.      function gainFunc(p, gain)
112.           p.R = p.R * gain
113.                 p.G = p.G * gain
114.                 p.B = p.B * gain
115.                 return p
116.                 end
117.
118.
119.
120. --It appears that I had the functions in the wrong order before.
121.         function Process(req)
122.       local img = InImage:GetValue(req)
123.             local red = InColorR:GetValue(req).Value
124.             local green = InColorG:GetValue(req).Value
125.             local blue = InColorB:GetValue(req).Value
126.             local matte = InMatte:GetValue(req).Value
127.       local vibrance = InVibrance:GetValue(req).Value
128.           local saturation = InSaturation:GetValue(req).Value
129.             local gain = InGain:GetValue(req).Value
130.             local out = Image({ IMG_Like = img })
131.
132.
133.       local p = Pixel()         -- Create the pixel object only once
134.       for x=0, img.Width - 1 do
135.       for y = 0, img.Height - 1 do
136.         img:GetPixel(x,y, p)
137.                 p = colorFunc(p, red, green, blue)
138.                 p = matteFunc(p, matte)
139.         p = vibranceFunc(p, vibrance)
140.                 p = gainFunc(p, gain)
141.                 out:SetPixel(x,y, p)
142.     end
143. end
144.
145.
146.
147.
148.      out:Saturate(saturation, saturation, saturation)   -- Change the output image's saturation.
149.     OutImage:Set(req, out)
150. end
151.
152.

Shem Namo
Fusioneer
Posts: 224
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 3 times

Re: [DEV] Vibrance Fuse

Almost done, at least in my opinion
What do you guys think?
@SecondMan Is it okay to mark this post as BETA?
@Midgardsormr Do you want to submit your MT_Vibrance to Reactor, or can I submit this one? Thanks Again, I couldn't have done it without you!!

SecondMan
Posts: 4095
Joined: Thu Jul 31, 2014 5:31 pm
Been thanked: 28 times
Contact:

Re: [DEV] Vibrance Fuse

Shem Namo wrote:
Wed Feb 12, 2020 8:44 pm
Is it okay to mark this post as BETA?
Not up to me - it's your dev topic...

But if you want to submit to Reactor at some point, filename extensions should be lower case, please and thank you.
Shem Namo wrote:
Wed Feb 12, 2020 8:44 pm
Do you want to submit your MT_Vibrance to Reactor, or can I submit this one?
One does not exclude the other. For whatever the reason, it may be preferable to have a Fuse over a Macro (speed) or a Macro over a Fuse (portability, learning). But currently your Fuse is many times slower than Bryan's Macro so you may want to look into that...

By the way, should you be wondering how you managed to insert an "empty" post in this topic - that's what happens when you try to insert more than seven hundred thousand characters into one. Your browser either crashed when you were trying to submit (mine was when I was trying to read/edit the thing) or some other hiccup happened before everything was sent - the post was incomplete.

About those characters. I have a bit of a "here we go again" feeling about this, to be totally frank. The Fuse isn't finished, yet your priority seems to be to slap your name and banner on it - as garishly as you can - and get it in Reactor. It's your Fuse and you should of course do whatever you feel is appropriate with it, but given how much help you've received so far to make it - it was practically done for you - I find that to be in somewhat poor taste.

You're also shooting yourself in the foot by making the code harder to share in your own dev topic.

Anyway, just some friendly advice - I'm sure you mean well.

thibaud
Fusioneer
Posts: 160
Joined: Thu Sep 04, 2014 1:23 am
Been thanked: 2 times
Contact:

Re: [DEV] Vibrance Fuse

As far as code formatting goes things only got worse since my last advice, some editor (like vscode) will format it for you.

Midgardsormr
Fusionator
Posts: 1488
Joined: Wed Nov 26, 2014 8:04 pm
Location: Los Angeles, CA, USA
Been thanked: 34 times
Contact:

Re: [DEV] Vibrance Fuse

No, I don't think it's ready for Reactor yet. You're past step one: You have an algorithm that works. As SecondMan says, it's quite slow right now because you're using that brute-force nested loops method. That approach is single-threaded—it can only work on one core of the CPU, leaving most of your system resources idle while it processes. The next thing to do is optimize!

Remember when I suggested you try to implement the other three methods seen in the Gain Fuse? That wasn't an idle thought—it's something you really ought to do because each of those methods is progressively faster than the previous, and each has cases where you might want to use it. Let's go through them one at a time and see how they work (I don't have time to analyze all of them this morning; this is going to take a couple of days of writing, probably):
1. self:DoMultiProcess(nil, { In = img, Out = out, Gain = gain }, img.Height, function (y)
2.     local p = Pixel()
3.     for x=0,In.Width-1 do
4.         In:GetPixel(x,y, p)
5.         p.R = p.R * Gain
6.         p.G = p.G * Gain
7.         p.B = p.B * Gain
8.         Out:SetPixel(x,y, p)
9.     end
10. end)
I'm sure this looks familiar. It replaces the outer for loop, the one that looks at each row (y coordinates) with this `DoMultiProcess()` function. The advantage to this approach is that it can perform the calculation on multiple scanlines at the same time. This might be useful if information in the vertical dimension is independent, but you need to know the results of one pixel before you know what to do with the one next to it.

It's using the method notation I've alluded to earlier: `object:method(arguments)`. In this case, the object the method is a member of is `self`, which means the `Process()` function. It therefore doesn't have an innate reference to the image it's supposed to be building—you could run this function to do general multi-threaded processing on something other than an image if you need to.

https://www.steakunderwater.com/VFXPedi ... ltiProcess

Let's take a look at those arguments: The first can hold a function used to set up any initial environment you might need for every thread. It will be processed only once, making it analogous to the Setup tab of the CustomTool. Most of the time, coders just do this kind of processing in the main part of `Process()` and pass the information in as variables in the second argument.

arg2 is a table containing a list of global variables to be imported into the multi-threaded function. These variables must be global in scope so that all of the threads can see them. Traditionally, global variables are capitalized, as we can see here. Since `DoMultiProcess()` doesn't get the images to work on by default, they have to be sent in through the variable list with all of the other input values.

arg3 is an integer. It determines how many times the function will run. In this case, we want to do it once for every line in the image, so we give it `img.Height`. It will start at 0 and run as many times are required (the `-1` is implicit in this case because the compiler understand that 0 - 3 is four numbers).

arg4 is a function, which gets a single argument of its own. This argument is always the value provided by arg3. Since in this case we know that's the y coordinate, we call it `y`. If you have declared the function you want to use elsewhere, you can instead give it the name of that function as arg4 instead of declaring the whole thing here, as illustrated in the documentation.

The lines that follow are actually still a part of the fourth argument. We create the `Pixel()` object outside the loop, so it only gets done once, then run the `for x` loop, just like you did in the single-threaded version. It is very important that `p` is a local variable in this case because you'll be processing multiple threads simultaneously. If `p` were global, then when each thread created it, they'd overwrite one another, and you'd get a scrambled output image.

And just like every function, this one requires an `end` statement, and the method call itself closes with that final parenthesis.

There's a subtle trick in this one that isn't obvious if you're not really familiar with Lua. When we use the assignment operator, `=`, to copy the out image to Out, we don't actually make a copy, but instead a pointer. `Out` is actually the same image as `out`. If we change something about one, we change it about both. That's why we don't need to copy the results back into `out` before we assign it to `OutImage`. It's confusing, but this doesn't happen with simple values, like numbers and text, but it does happen with tables. So if you ever need an independent copy of a table, you generally have to use a loop to copy the values one at a time instead of just saying `table1 = table2`.

Okay, I've got other stuff to do before I go to work today, so today's lesson is to see if you can get your fuse to work using `DoMultiProcess()`. And yes, I really am going to make you experience all three additional techniques!

Also, test the speed of the current Fuse, then compare it to the new one when you're done to see how much improvement you have made.
SecondMan wrote:
Thu Feb 13, 2020 1:00 am
should you be wondering how you managed to insert an "empty" post in this topic
I know I was wondering about that!

Shem Namo
Fusioneer
Posts: 224
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 3 times

Re: [DEV] Vibrance Fuse

@Midgardsormr You are the best coding teacher ever!!!
I really have too read this a few times and then a few more times to really learn from it.
I've been on the road all day and need to look at this with a fresh mind, so I'll try this method in the morning.
just to get this straight, this has nothing to do with OpenCL or DCTL right?
Thank you so much for explaining this to me, I can't thank you enough!!!

I hope the 700,000 character text didn't cause any damage.
By the way it looked fine in the preview window

Thanks Again,
David.

Midgardsormr
Fusionator
Posts: 1488
Joined: Wed Nov 26, 2014 8:04 pm
Location: Los Angeles, CA, USA
Been thanked: 34 times
Contact:

Re: [DEV] Vibrance Fuse

Correct. The techniques are similar to OpenCL—we're essentially writing a fragment shader—but we haven't touched GPU programming at all. Yet.

JUNE
Fusionista
Posts: 269
Joined: Wed Aug 06, 2014 5:45 am
Been thanked: 1 time
Contact:

Re: [DEV] Vibrance Fuse

Work together!

Shem Namo
Fusioneer
Posts: 224
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 3 times

Re: [DEV] Vibrance Fuse

Hi Bryan, I thought that I could figure it out with only the information that you already gave me,
but apparently I can't
Do you happen to know of a Fuse that uses this method(besides the gain fuse), that I can learn from?
I can't figure out how to make this work with multiple functions, like vibranceFunc, gainFunc and the rest.
I already when through all of the fuses of my computer, but I can't find one that uses this method.

Thanks,
David.

Midgardsormr
Fusionator
Posts: 1488
Joined: Wed Nov 26, 2014 8:04 pm
Location: Los Angeles, CA, USA
Been thanked: 34 times
Contact:

Re: [DEV] Vibrance Fuse

I'll give you a hint: The `DoMultiProcess()` function replaces `for y=0, img.Height-1 do...end`.

If you still can't figure it out, post your best guess for what should work, and I'll give you some more pointers.

I don't know of a fuse that uses the method off the top of my head—it's not one that sees a lot of use. But it's a good conceptual stepping-stone toward `ProcessPixels()`.

Shem Namo
Fusioneer
Posts: 224
Joined: Sun Oct 06, 2019 9:15 pm
Location: North Israel
Real name: David Kohen
Been thanked: 3 times

Re: [DEV] Vibrance Fuse

Thanks Bryan!!
Here is what I think it should look like, but the only thing is that
I don't know how to declare which functions should be in the loop.
In the simple loop method they were declared

Code: Select all

``p = vibranceFunc(p, vibrance)``
and like this for all of the other functions(except "Saturate"),
but I guess that type of declaration doesn't work with "DoMultiProcess"
I wrote a few notes inside the fuse that describe what I think is going on.

Thanks again,
David

1. FuRegisterClass("Vibrance", CT_Tool, {
2.     REGS_Category = "Fuses\\Color",
3.     REGS_OpIconString = "vib",
4.     REGS_OpDescription = "Vibrance",
6.     REG_NoBlendCtrls = true,
7.     REG_NoObjMatCtrls = true,
8.     REG_NoMotionBlurCtrls = true,
9.     })
10.
11.     function Create()
12.
14.         ICS_Name            = "Color",
16.         INPID_InputControl  = "ColorControl",
17.         INP_Default         = 0,
18.         INP_MaxScale        = 1.0,
19.         ICD_Center          = 1.0,
20.         INP_DoNotifyChanged = true,
21.         CLRC_ShowWheel      = False,
22.         IC_ControlGroup     = 1,
23.         IC_ControlID        = 0,
24.         IC_Visible          = true,
25.         })
26.
27.     InColorG = self:AddInput("Green", "Green", {
29.         INPID_InputControl  = "ColorControl",
30.         INP_Default         = 0.5,
31.         INP_DoNotifyChanged = true,
32.         IC_ControlGroup     = 1,
33.         IC_ControlID        = 1,
34.         })
35.
36.     InColorB = self:AddInput("Blue", "Blue", {
38.         INPID_InputControl  = "ColorControl",
39.         INP_Default         = 1,
40.         INP_DoNotifyChanged = true,
41.         IC_ControlGroup     = 1,
42.         IC_ControlID        = 2,
43.         })
44.
45.         InMatte = self:AddInput("Matte", "UnMultiply", {
47.             INPID_InputControl = "CheckboxControl",
48.             INP_Integer = true,
49.             INP_Default = 1,
50.             })
51.
52.    InVibrance = self:AddInput("Vibrance", "Vibrance", {
54.         INPID_InputControl = "SliderControl",
55.         INP_MinScale = -0,
56.         INP_MaxScale = 20,
57.         INP_Default = 3,
58.         })
59.
60.      InSaturation = self:AddInput("Saturation", "Saturation", {
62.         INPID_InputControl = "SliderControl",
63.         INP_MinScale = -0,
64.         INP_MaxScale = 1,
65.         INP_Default = 1,
66.         })
67.
68.      InGain = self:AddInput("Gain", "Gain", {
70.            INPID_InputControl = "SliderControl",
71.          INP_MinScale = -0,
72.        INP_MaxScale = 5,
73.            INP_Default = 2,
74.            })
75.
76.      InGamma = self:AddInput("Gamma", "Gamma", {
78.            INPID_InputControl = "SliderControl",
79.          INP_MinScale = -7,
80.        INP_MaxScale = 0,
81.            INP_Default = -1,
82.            })
83.
84.         InLift = self:AddInput("Lift", "Lift", {
86.            INPID_InputControl = "SliderControl",
87.          INP_MinScale = -1,
88.        INP_MaxScale = 1,
89.            INP_Default = 0,
90.            })
91.
92.
93.     InImage = self:AddInput("Input", "Input", {
96.         })
97.
98.     OutImage = self:AddOutput("Output", "Output", {
101.         })
102.     end
103.
104.       function colorFunc(p,red,green, blue)
105.         p.R = p.R * red
106.     p.G = p.G * green
107.     p.B = p.B * blue
108.      return p
109.         end
110.
111. function matteFunc(p, matte)
112.      p.A =p.A * matte
113.      return p
114. end
115.
116.         function vibranceFunc(p, vibrance)
117.     local channelAverage = (p.R + p.G + p.B) / 3                  -- From Inter, i1
118.     local channelMaximum = math.max(p.R, math.max(p.G, p.B))      -- From Inter, i2
119.     local amount = (channelMaximum - channelAverage) * (-vibrance * 3)
120.           p.R = p.R * (1-amount) + channelMaximum * amount        -- From channel expressions
121.           p.G = p.G * (1-amount) + channelMaximum * amount
122.           p.B = p.B * (1-amount) + channelMaximum * amount
123.     return p
124.     end
125.
126.
127.      function gainFunc(p, gain)
128.           p.R = p.R * gain
129.                 p.G = p.G * gain
130.                 p.B = p.B * gain
131.                 return p
132.                 end
133.
134.         function gammaFunc(p, gamma)
135.         local midtones = gamma*-1
136.           p.R = p.R ^ midtones
137.                 p.G = p.G ^ midtones
138.                 p.B = p.B ^ midtones
139.                 return p
140.                 end
141.
142.         function liftFunc(p, lift)
143.           p.R = p.R *(1-lift)+lift
144.                 p.G = p.G *(1-lift)+lift
145.                 p.B = p.B *(1-lift)+lift
146.                 return p
147.                 end
148.
149.
150.
151. --It appears that I had the functions in the wrong order before.
152.         function Process(req)
153.       local img = InImage:GetValue(req)
154.             local red = InColorR:GetValue(req).Value
155.             local green = InColorG:GetValue(req).Value
156.             local blue = InColorB:GetValue(req).Value
157.             local matte = InMatte:GetValue(req).Value
158.       local vibrance = InVibrance:GetValue(req).Value
159.           local saturation = InSaturation:GetValue(req).Value
160.             local gain = InGain:GetValue(req).Value
161.             local gamma = InGamma:GetValue(req).Value
162.             local lift = InLift:GetValue(req).Value
163.             local out = Image({ IMG_Like = img })
164.
165.         self:DoMultiProcess(nil, { In = img, Out = out, Red = red, Green = green, Blue = blue, Matte = matte, Vibrance = vibrance, Gain = gain, Gamma = gamma, Lift = lift  }, img.Height, function (y)
166.       local p = Pixel()
167.       for x=0,In.Width-1 do
168.             In:GetPixel(x,y, p)
169.             -- In this version, the fuse doesn't fail when I try to open it in Fusion, but none of the controls do anything,
170.             --which is something I would expect because:
171.             --I know that I need to declare which functions should be in the loop here, but I don't know how to declare them.
172.             -- In the simple loop method I would declare them as "p = vibranceFunc(p, vibrance)" and the same for the rest of the functions.
173.             -- I already tried using the same declarations here, but it doesn't work
174.         -- and I'm almost sure that is the reason the controls don't do anything.
175.             Out:SetPixel(x,y, p)
176.             end
177.          end)
178.
179.
180.
181.
182.
183.
184.      out:Saturate(saturation,saturation, saturation)   -- Change the output image's saturation.
185.     OutImage:Set(req, out)
186. end
187.
188.

tberakis
Posts: 31
Joined: Mon Dec 29, 2014 2:30 pm
Been thanked: 1 time

Re: [DEV] Vibrance Fuse

It's exactly the same as the last Fuse, as Brian was trying to tell you.

While I would never suggest learning is a bad thing, I'm curious what you're trying to do here, other than absorb. everyone's time to develop already established ideas?

You're very close, but then, you've been guided there every step of the way.