photoshop style levels

jersmi's picture

looking to build or find the best way to adjust tonal range (for ex., midtones) ala photoshop levels. advice?

cybero's picture
Re: photoshop style levels

On a quick look into this, I find that RGB [ Shadows, Midtones, Highlights ] is not directly available via standard patches although we do have Color Controls [Hue, Saturation, Brightness, Contrast], Color Controls [Saturation, Brightness, Contrast], Color Transformation [ Hue, Saturation, Luminosity & Alpha].

We've also got Color Channels [affects all sub patches] & also Color Transformation - to change the base sprite or billboard colour.

However I cannot reproduce precisely, the sort of effect that shuffling the values would have on a Photoshop layer, I can get pretty close.

I think that interpolating the hue and other values can get you pretty close.

Probably requires a specific Core Image routine to accurately reproduce.

There's also the option of using CoGe Layers & also exporting layers as separate images.

cybero's picture
Now a Core Image Shadows, Midtones & Highlights routine :-)

Contains Core Image code ported over from the tutorial @ http://losingfight.com/blog/category/core-image/.

Uses Color Channels, which the CI Kernel doesn't specifically need.

Requisite image file included [embedded, hence 'hefty' file size].

PreviewAttachmentSize
ShadowHighlightsMidtones.qtz398.81 KB

cybero's picture
& GLSL [with Core Image]

This one is more a bit of fun, although it does have an option to run the Core Image SMH and Color Channels macro from the previous post.

Uses a vertex noise shader with ambient, diffuse & specular colour settings.

[updated attached example - previous upload sent before save]

PreviewAttachmentSize
SMH_GLSL_psd.qtz408.02 KB

gtoledo3's picture
Re: photoshop style levels

You should take a look at the CIHighlightShadow filter... just remember to crop the output ;-) It's not really the same kind of interface as Photoshop, but you should be able to use it to good effect.

@cybero - this patch definitely does allow for control of shadow/highlight/midtone, so there is some kind of built in patch for it... though it's a hidden CI filter.

cybero's picture
Re: photoshop style levels

@gtoledo3 cheers for the pointer.

Come to think of it, there really are several ways in which one can work with highlights, midtones & shadows color range / value wise in Photoshop, especially by layers.

PreviewAttachmentSize
SMH_CI_Private_psd.qtz395.16 KB

jersmi's picture
Re: photoshop style levels

Thanks so much for all the examples. I appreciate seeing the range of options.

jersmi's picture
Re: photoshop style levels

Thanks again, cybero and gt, for the leads. Sent me down a fruitful path into some Core Image filter efforts. I'll post the little I have when I polish it up.

I also stumbled upon this, which I had not seen: http://belightcommunity.free.fr/index.php

And one more question: what would an equation be for black / white clipping?

jersmi's picture
Re: photoshop style levels

Here's something, with some questions:

Here the Levels macro is essentially a chain of CI Filters. re: optimization, how should I think about this? Is it better to cram as much as possible into one kernel routine? (If so, how would I combine the CILevels with the Grayscale and CIMix filters?)

Also, still wondering about black and white clipping. I'm sure it's some easy math and I have not tried super hard to research it yet.

And I have not given credit where credit is due with the CIFilters here yet -- grayscale and levels both derived from cybero's post and link to the programmer in SF.

PreviewAttachmentSize
Levels_ala_psd.qtz1.03 MB

psonice's picture
Re: photoshop style levels

Sorry if this has been covered, can't open the .qtzs here just now.

Clipping is easy, just use: pixel.rgb = clamp(pixel.rgb, blacklevel, whitelevel); // pixel is your vec4 containing the current pixel, black/white level are floats. I'm using .rgb to avoid clamping alpha..

Levels can be done in a single CI filter pretty easily, using splines works well and gives you good control.

Brightness and contrast are the same thing really, and can be handled in a single filter. You just scale the pixel values by your brightness value (I call this 'scale', 1.0 = normal), but scale them about a fixed point (I call this 'bias', 0.0 gives you brightness control, 0.5 gives you contrast control, leaving it to the user gives you flexibility). Off the top of my head the filter looks something like this: pixel.rgb = ((pixel.rgb - bias) * scale) + bias; pixel.rgb = clamp(pixel.rgb, 0.0, 1.0);

jersmi's picture
Re: photoshop style levels

Hey, thanks. I'm just now starting to wrap my head around CI filter stuff. Or the pieces are starting to come together, something like that.

How do you use splines in a CI filter?

psonice's picture
Re: photoshop style levels

Splines are quite a bit harder. You don't draw the spline on screen (well, you can if you want a preview of the curve). You just use the spline curve to remap the brightness.

I have a setup already built for this, but it's pretty complex and spans several .qtz files (it's part of an application i'm writing). I did it a more complex way: the spline is generated in javascript as a structure, I use that to draw an on-screen preview using lines, so I have a nice curves editor. I then use the control points (there are 4, it's a quadratic spline) to generate a lookup table that can be applied to my image to remap the colours (this part uses the spline formula in a CI filter). It's unfortunately totally not obvious how it works from looking at the .qtz...

Best bet is to look up the quadratic spline formula.. look for coding tutorials rather than the actual maths, you'll find the code required to get Y (your output brightness) for any X (your input brightness). Write that as a CI filter.. you might need to run it once per colour channel (this gives you per-colour levels like photoshop too).

Maybe splines are overkill for levels though, a simple s-curve with scaling would be easier.

toneburst's picture
Re: photoshop style levels

There are other kinds of interpolation you can use, too. They're all pretty complex to setup though, and involve lots of JavaScript coding, and passing around large numbers of coefficients. Once you have the magic numbers, you can do the rest in a relatively simple CIFilter.

What you're effectively doing is a 1D interpolation between a given number of points. You can either apply that directly to the values of the image's RGB channels, or generate a lookup table as 1-pixel strip, which you can then apply to the image's RGB channels, and also use to create some kind of graph or other graphic showing the curve.

a|x

usefuldesign.au's picture
Re: photoshop style levels

Are you meaning that you generate the 1-pixel strip inside the CIFilter code or importing it. Are you talking about a 1D matrix (can't remember if GLSL matrix types are allowed in CI code) or an image type variable?

If this isn't for an application, I don't see why then interpolation patch (set to custom graph) couldn't be used with a javascript patch (or two if you prefer) to parse the graph into a LUT for n number of values where n is whatever resolution you require.

I'm jumping in on this thread without having followed it so sorry if I'm out of place.

psonice's picture
Re: photoshop style levels

The 1-pixel strip is the lookup table, you generate it outside the CI kernel then use it within the kernel to apply the curve to the image (this is massively faster than calculating the curve for each pixel in the image..)

You could calculate the LUT within the CI filter actually, but you'd want to do it before processing the actual image (by having a second kernel and using the javascript/advanced filter function stuff). It would be the same as having 2 patches, just a bit tidier.

You don't need to use interpolator/javascript patches to do this btw - ideally, it should be just 2 CI filters, one doing the 'heavy' part of the work generating the LUT in a 256x1 px image, the second applying the LUT over the whole image. This way you can easily do levels at HD res while manipulating the levels curve in realtime..

toneburst's picture
Re: photoshop style levels

Much better explanation than my effort :D

Yes, that's exactly what I meant. You'd generate the curve LUT separately, then use that LUT to alter the colour of the input image in a 2nd CIFilter kernel program (though, as psonice says, you could do this all in the same patch using the Edit Filter Function option in the CIFilter patch).

The LUT doesn't have to be particularly large, because you can make use of the CIFilter's builtin bilinear filtering to make create values between the actual values of the pixels in the LUT.

a|x

usefuldesign.au's picture
Re: photoshop style levels

psonice wrote:
You don't need to use interpolator/javascript patches to do this btw - ideally, it should be just 2 CI filters, one doing the 'heavy' part of the work generating the LUT in a 256x1 px image,...

Well I tested my method just using a queue patch (no JS patch) and it is slugish, 0.42 seconds to generate a 128 element (x,y) graph. It matches the curve I drew in the interpolation patch (customise in settings) perfectly though. As tb pointed out, 128 elements is probably overkill in most situations with interpolation of the LUT inside CIFilter.

The reason for suggesting this was to avoid the maths of generating the graph (which for a complex function is seriously complex) and avoid the on screen interaction required to edit the graph. That's sounds like quite a bit of effort to me (yes/no?) so doing it in the QC editor instead might have it's advantages.

In analysis of the runtimes of this comp I came across some really weird runtime profiles. I have a macro collecting the number pairs into a queue, the queue is feed to a GLPoints Structure patch that draws the LUT as a graph. Normal runtime is 0.42sec as I said before. I disabled the GLPoints to see if it would speed up the execution. It slowed it down to around 0.68s!

Okay so maybe if I feed the queue to the enable of an iterator to force evaluation, that comes in at 0.4s, slightly faster than with GLPoints on. Then I fed the queue to both the iterator and the GLPoints, what now? Fastest result of all, 0.27s!!

Please advise on this counterintuitive profiling anomaly!

toneburst's picture
Re: photoshop style levels

M.U.C.H. faster to do this in a CIFilter than with JS and an Iterator. Bear in mind you need to do this three or four times is you want separate curves for the RGB/RGBA channels (as you would, for these type of effects https://machinesdontcare.wordpress.com/2010/03/16/garish-psychedelic-col... ).

I'd show an example of exactly how I'd do this, but I've used it for a commercial project, and promised not to share. I've probably said too much already.

a|x

usefuldesign.au's picture
Re: photoshop style levels

I believe you, tb. I'm not actually using iterator nor JS patches but you're point remains, CIFilters can crunch data fasta. My point is that this way you don't have to make an interactive graph editor in QC — just use the one that Interpolate patch provides.

"toneburst" wrote:
Bear in mind you need to do this three or four times is you want separate curves for the RGB/RGBA channels
That's what I meant by a "complex curve" ;)

psonice's picture
Re: photoshop style levels

That isn't a complex curve though, that's 3/4 separate but possibly simple curves.

Ideally you want a combination of the fast CI filters making the LUT, and a spline editor. Doing that with the interpolation patch is hard though, there's no access to the control points.

This is why I ended up with both the javascript + iterator AND the CI filters, I need the speed because I'm doing curves adjustment on live video, but I need a curve editor too. The curve editor runs at 20fps or so with 128 points, so I guess javascript + iterator beats the interpolate + queue somehow. Really I should have just bitten the bullet and written a spline editor in cocoa, seeing as it's for a cocoa app. Laziness :)

jersmi's picture
Re: photoshop style levels

I'd love to see the code for CIHighlightShadow.

And I do wish to process live video, so I appreciate the discussion on the fastest methods. I am using interpolation patches, "calibrated" by eye to get close to image source at midpoint. I'm not unhappy with the results, but that spline stuff sounds interesting.

usefuldesign.au's picture
Re: photoshop style levels

psonice wrote:
That isn't a complex curve though, that's 3/4 separate but possibly simple curves.

Well by complex curve I guess I meant a complex function, which in the maths I know is several curves (linear, quad, cubic, spline, whatever) defining a single function for different ranges of X.

psonice wrote:
Ideally you want a combination of the fast CI filters making the LUT, and a spline editor. Doing that with the interpolation patch is hard though, there's no access to the control points.
I've said this twice already but he goes again :) If this CIFilter isn't for an application and editing the graph defining the CIFilters levels in the QC editor is an option then it can be parsed from the interpolate customised graph.

This is a slack option for a non-programmer only. A rough way to get a LUT from a graph without building a whole Graph editing interface in QC. Would love to see your interface though, let us know when the app ships so we can work with it.

Obviously a spline editor is the way to go if one has the know how (big task I'm guessing).

psonice wrote:
The curve editor runs at 20fps or so with 128 points, so I guess javascript + iterator beats the interpolate + queue somehow. Really I should have just bitten the bullet and written a spline editor in cocoa, seeing as it's for a cocoa app. Laziness :)
That's much faster (I'm on an old dualG5 though). Any guesses why my comp would run twice as fast with the LUT patched to the enable input of an iterator and a GLPoints Structure patch than not patched to anything?! Maybe I should post the comp on a separate thread to ask that question.

psonice's picture
Re: photoshop style levels

usefuldesign.au wrote:
I've said this twice already but he goes again :) If this CIFilter isn't for an application and editing the graph defining the CIFilters levels in the QC editor is an option then it can be parsed from the interpolate customised graph.

Yeah, what I meant was that ideally we want the interpolation patch connected to a CI filter instead of an iterator or queue.. this way we get the nice curve editor and performance. I can't see how it can be done without modifying the plugin unfortunately :(

Quote:
This is a slack option for a non-programmer only. A rough way to get a LUT from a graph without building a whole Graph editing interface in QC. Would love to see your interface though, let us know when the app ships so we can work with it.

Obviously a spline editor is the way to go if one has the know how (big task I'm guessing).

It's not that big a task actually, it's more that I'd already built a spline editor in QC for testing and it was easier to reuse that :) It'll be a while before this app sees the light of day unfortunately, I've started a complete reworking of the UI (it'll be almost node-based like QC, but simplified a lot and designed for imaging (the app is for realtime astrophotography)). Here's a screenshot of an old build showing the curves editor though:

http://www.interealtime.com/Astrocam.jpg

Quote:
That's much faster (I'm on an old dualG5 though). Any guesses why my comp would run twice as fast with the LUT patched to the enable input of an iterator and a GLPoints Structure patch than not patched to anything?! Maybe I should post the comp on a separate thread to ask that question.

Yeah, I think we'd need to see the comp. Only thing that comes to mind is that the iterator might be forcing the comp to run, maybe without it it's actually running super fast but only updates occasionally? I've seen weird things like this, including a case I had once that looked like QC was nicely threaded - one part of the comp was running at 30fps, the other part at around 1fps.. straaaange!

usefuldesign.au's picture
Re: photoshop style levels

Yeah actually I think there was a editable spline thread going on these boards a while ago wasn't there? I remember two chunky white sprites for a handles on a black bkgd.

http://www.interealtime.com/Astrocam.jpg

I'm digging that Last Will and Testament, top shelf no less!!

Strange indeed on the split comp-onality fps.

psonice's picture
Re: photoshop style levels

Haha, it's a 'write your own will' pack. My wife and I wrote one a while back, after realising that if we died most of our cash would go to relatives we don't know/like :) (It's worth thinking about this btw, you don't want to die and then all your cash goes to some rich bloke you've never met while some poor relative who looked after you your whole life gets nothing!)

toneburst's picture
Re: photoshop style levels

My partner and I are buying a house at the moment (hence, in part, me not updating my blog in months, or doing anything useful in QC), so we suddenly have to think about all that stuff we'd remained happily ignorant of previously.

a|x

vade's picture
Re: photoshop style levels

I have a basic plugin that outputs a LUT with the v002 Film effects. Levels is fairly easy on the GPU, you can see GLSL code for channel mixer and levels here, (include some older Max/MSP Jitter 4.0 style patches)

http://001.vade.info/?page_id=20

These may be helpful? I've not read the whole thread, but CI/GLSL is going to be much faster, as ToneBurst said. You could even put a custom NIB in the settings for a plugin to output curved Luts for RGB, or what not.

usefuldesign.au's picture
Re: photoshop style levels

Hey vade that's interesting, going slightly OT, custom NIBs in QC?! I periodically think of putting a feature request up on these boards for a plug-in that allows for a NIB style user palette to be created for use in QC editor, and I guess even better to be bundled with apps made in App-maker (which I haven't go into yet but I long too...) You seem to be saying this is already possible, yes?!

I have only played with NIB builder; never used it as such but it seems relatively easy to bind bools, text fields, colour wells and so on to a set of objects. I guess one would need additional Obj-C code to make a spline editor though :/

Another useful OSX element that would be great to integrate into a QC plugin is an Open/Save style modal dialogue window to be used in conjunction with kineme's structure save and structure read tools (and other XML, text ones, image writer etc).

I'd actually like to see Open/Save before the floating user inputs palette, can't be that hard a task for the genius's who can code Obj-C right? Maybe would be a good introduction to Obj-C coding of QC plugins for me but I don't have the concentration or time to begin that k2 climb at present :/. If anybody seconds this, I'l feature request it.

jersmi's picture
Re: photoshop style levels

Vade -- thanks, and the list of jitter patches looks well worth examining, but at present it reveals my newbness once again. I don't know how to extract useful GLSL or whatever from those patches.

Also, I see the LUT, but how would one apply the table to an image in a useful way? I can see the table processed as a gradient, but I don't know how to use this plugin as it was meant to be used.

jersmi's picture
Re: photoshop style levels

Re: v002 Film Effects LUT, I just found the example .qtz using the lomo plugin. I get it now.

vade's picture
Re: photoshop style levels

Ah, the GLSL code is in the JXS xml file. Jitter has a kind of an odd format for loading shaders. Open up the xxx.jxs and just copy the fragment shader and vertex shader out of there.

vade's picture
Re: photoshop style levels

You can make a QC plugin that has a custom view for the settings tab a-la Timeline patch, this has been an option for a while now. I don't particularly like it for various reasons (you cannot publish the view, for one), but it works.

jersmi's picture
Re: photoshop style levels

Ah, yes, there we go. I didn't see the shader download first time around. Thanks so much.

jersmi's picture
Re: photoshop style levels

Ah, no. I'm sure it's a little thing but I'm not experienced. Tried a couple things with the first on the list, looking at the glsl shader for blackandwhite. Produces an error: "Fragment reads varying 'texcoord0' which is not written."

Any help is appreciated. I stand to learn a good deal from these.

cybero's picture
Re: photoshop style levels

Fragment reads varying 'texcoord0' which is not written.

doesn't mean as much as you might at first think, jersmi.

Almost all the Developer code samples produce the same sort of result, although the unwritten or inactive item might well differ.

Just try opening them and having a look.

They all work, even though they provoke that sort of message.

I have found that message can occur because some text lint has been left in the code, which , once removed, allows the shader to work without an Editor panel error.

However, these shaders do work even when they are provoking an error message.

Nil output is the clearest proof of incorrectly coded shaders, at least for how QC handles GLSL, IMHO.

The attached GLSL example does not have any such errors in the GLSL editing panel.

PreviewAttachmentSize
LED_GLSL.qtz7.58 KB

jersmi's picture
Re: photoshop style levels

Ah, I see! Back on the 'yes' side. Thanks so much, cybero. I'm a babe in the woods over here.

jersmi's picture
Re: photoshop style levels

Vade, thanks again. For me that is a very useful collection of basic tools, and a great introduction to GLSL shaders.

Ironically, to stay on topic, the Levels shader has a tex0 input only with no variable parameters, so I don't know quite what to do with that... besides being curious what's up with that, it's nearly a moot point considering all the other color tools.