| Notices | Welcome to RetouchPRO . You are currently viewing our boards as a guest which gives you limited access to view most discussions and access our other features. By joining our free community you will have access to post topics, communicate privately with other members (PM), respond to polls, upload images and access many other special features. Registration is fast, simple and absolutely free so please, join our community today! If you have any problems with the registration process or your account login, please contact contact us. | | Software Photoshop, Paintshop Pro, Painter, etc., and all their various plugins. Of course, you can also discuss all other programs, as well. | 
03-17-2006, 04:42 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | Nice catch with that pesky character. Wonder how that snuck in?
The last code does work as it should. It just doesn't work as most folks are used to. Hard to explain. If I think about it, I might be able to come with a way of explaining it.
Here comes a subject I adore.
Contrast is finding the difference and amplifying it. It can be anything you desire. Can be difference in saturation between subjects. Can be shades of red and shades of blue. A common use of contrast is Levels. Basically contrast between high/low in each seperate channel.
Earlier I showed how to get 3d mid-tones in Lab. You know, the distance from the center thing. With vectors and normals, you can actually move values away from the mid-tone center in a 3d manner. This is a form of contrast.
Very basic example for Lab: Code: %ffp
ctl(0):standard,"Amount",range=(-100,100),val=10,track
ctl(1):checkbox,"Absolute Distance",val=0
supportedmodes: labmode
ForEveryTile:{
int ll,aa,bb;
float distance,nx,ny,nz;
for(x=x_start;x<x_end;x++){
for(y=y_start;y<y_end;y++){
// grab values
// bother with 128 to make things easier
ll=src(x,y,0)-128;
aa=src(x,y,1)-128;
bb=src(x,y,2)-128;
// Pythagoras to get distance
distance=sqrt( (float)ll*ll + aa*aa + bb*bb);
//distance hack, move straight up
if(distance==0){
nx=1;
ny=0;
nz=0;
}else{
// normalize the vector
nx=ll/distance;
ny=aa/distance;
nz=bb/distance;
} // end distance hack
// add with option
if(ctl(1)==0){
// relative
ll=ll+nx*ctl(0);
aa=aa+ny*ctl(0);
bb=bb+nz*ctl(0);
}else{
// absolute distance
ll=nx*ctl(0);
aa=ny*ctl(0);
bb=nz*ctl(0);
}
// output, don't forget 128
pset(x,y,0,ll+128);
pset(x,y,1,aa+128);
pset(x,y,2,bb+128);
}} // y x
return true;
} // for every tile
With positive values for Amount, you will move values away from the center of the Lab sphere. With negative values for Amount, you will move values through the center and out the other side for an invert effect.
(If you want to, you don't have to push values away from the center. With some controls and offsetting, you can actually push values away from any given point. To tickle your brain, imagine using gravity instead of vectors. With gravity, you can pull towards and push away from several points. How would you like a magnet effect for contrast and colour balance?)
As the code is, it works just like Levels - except you are doing all 3 channels at once in a simple manner. Since this can be done with Levels, it may seem a bit silly. Believe me, it's not. This is just a building block. | 
03-18-2006, 02:23 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | That works just fine Stroker, Thank you.
It sounds like there is better to come.
I found this great thread on HSB http://photoshoptechniques.com/forum...t=10495&page=2
Pity I didn’t find it sooner. I may have won more cookies.
Ken. | 
03-19-2006, 05:25 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | Dude, I haven't seen that thread in awhile. Now that's an old school rampage. If I remember correctly, I made some gnarly mistakes in that thread and had to do some follow-ups to ammend.
Let's talk about gradients some more.
With trigonometry you can do a lot of things with triangles, especially if they are right triangles. The Photoshop's Gradient tool is proof positive of this. How does the Gradient tool work? Vectors and trig.
When you drag a the Gradient tool, you are defining a 2d vector in screen space. Basically a position vector in x and y. As you go through the x and y loops, you use one point on the defined vector to define a second vector. Then you make it a right triangle. Using a simple formula, you can get another angle in the right triangle. After that, it should be cake to finish it up.
The forumula to get the angle is simply:
p1 * p2 = |p1| * |p2| * cos(angle)
cos(angle) = p1 * p2 / |p1| * |p2|
angle = acos( p1 * p2 / |p1| * |p2| )
Well, maybe not simply. Maybe if you read Understanding the Dot Product enough times will click. Or maybe the attached graphic will help.
The graphic attached shows distance along a vector. Just a simple percentage with the length of v1 = 100%. The length of p3 along v1 = 50%.
And that's how the Linear Gradient tool in Photoshop works.
Um, yeah. | 
03-19-2006, 05:39 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | | I mentioned that Levels is a common method for increasing contrast. Once in a great while I take issue with this. Why? Because each channel is done seperately and you end up with a square. If you do this in 3d space, you end up with a box.
While the two points and the line between maybe good and gradual, sometimes I don't like what happens to the rest of the space around the 'vector'. | 
03-19-2006, 05:50 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | Why not create a gradient based on colour values instead of pos x and y? Why not make it 3d? Why not in Lab which is good for our eyes instead of RGB which is good for monitors?
Here is an early version of one of my personal filters that does just that. Code: %ffp
Category:"Tech Slop Lab"
Title:"3D Linear Grad"
Author:"JLHalmich"
Copyright: "2005 by JLHalmich"
Organization:"Tech Slop"
Version:"beta 1"
Filename: "TS_3dlineargrad.8bf"
Description: "Linear gradient in 3d Lab space"
about: "!t Plug-in !V\n!C\n!c\n!D"
SupportedModes: LabMode
dialog: size=(395,175)
ctl[CTL_OK]: MODIFY, pos=(315,155)
ctl[CTL_CANCEL]: MODIFY, pos=(355,155)
ctl(0):standard,"L",pos=(260,15),range=(0,255),val=25,track
ctl(1):standard,"a",pos=(260,25),range=(-127,127),val=-25,track
ctl(2):standard,"b",pos=(260,35),range=(-128,128),val=-25,track
ctl(3):standard,"L",pos=(260,65),range=(0,255),val=240,track
ctl(4):standard,"a",pos=(260,75),range=(-127,127),val=25,track
ctl(5):standard,"b",pos=(260,85),range=(-127,127),val=25,track
ctl(6):checkbox,"LL against Lightness",pos=(260,105)
ctl(7):groupbox,"Black", pos=(240,5),size=(150,45)
ctl(8):groupbox,"White",pos=(240,55),size=(150,45)
ctl(9):standard,"Blend",pos=(260,120),val=255,track
OnFilterStart:{
setPreviewCursor (32515);
return false;
}
OnCtl(n):{
if (e==FME_RIGHTCLICKED_DOWN && n==CTL_PREVIEW){
int x,y,ll,aa,bb;
x = getPreviewCoordX();//*scaleFactor;
y = getPreviewCoordY();//*scaleFactor;
ll=src(x,y,0);
aa=src(x,y,1)-128;
bb=src(x,y,2)-128;
if(getAsyncKeyState(VK_CONTROL)>=0){
// white
setCtlVal(3,ll);
setCtlVal(4,aa);
setCtlVal(5,bb);
} else {
// black
setCtlVal(0,ll);
setCtlVal(1,aa);
setCtlVal(2,bb);
}
doAction(CA_PREVIEW);
} // end right click
return false;
} // on ctl
ForEveryTile:{
int x,y,ll,aa,bb,c,a;
int final;
//float pi = 3.14159;
// p1 with normalization
int p1x,p1y,p1z;
float p1dist;
float p1nx,p1ny,p1nz;
//p2 without normalization
int p2x,p2y,p2z;
float p2dist;
// theta junk
float pretheta,theta;
float adj;
// can pre-calculate some junk before the loop
// do p1 with offset
p1x=ctl(3)-ctl(0);
p1y=ctl(4)-ctl(1);
p1z=ctl(5)-ctl(2);
// get length of p1
p1dist = sqrt( (float)p1x*p1x + p1y*p1y + p1z*p1z);
//normalize p1
p1nx=(float)p1x/p1dist;
p1ny=(float)p1y/p1dist;
p1nz=(float)p1z/p1dist;
for (y=y_start; y<y_end; y++){
//if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
c = srcp (x,y);
//Explode it into the color values
ll = Rval(c); //c & 0xff;
aa = Gval(c)-128; //c >> 8 & 0xff;
bb = Bval(c)-128; //c >> 16 & 0xff;
//if (Z>3) a = Aval(c); //c >> 24 & 0xff;
// p2
p2x=ll-ctl(0);
p2y=aa-ctl(1);
p2z=bb-ctl(2);
p2dist = sqrt( (float)p2x*p2x + p2y*p2y + p2z*p2z);
// get angle, aka theta
// don't forget: float and radians
// p1 * p2 = |p1| * |p2| * cos(angle)
// cos(angle)= p1 * p2 / |p1| * |p2|
pretheta=(p1x*p2x + p1y*p2y + p1z*p2z) / (p1dist*p2dist);
theta=acos(pretheta);
if(theta==0){ // theta hack, seems around 99.99%
final=(p2dist/p1dist)*255;
}else{
// now solve to adj
adj=fcos(theta)*p2dist;
final=(adj/p1dist)*255;
}
// write the values back
if(doingProxy==false){
if(ctl(6)==1){final=(final-ll)/2+128;}
pset(x,y,0,final);
pset(x,y,1,128);
pset(x,y,2,128);
}else{
ll=blend(ll,final,0,0,ctl(9));
aa=blend(aa+128,128,0,0,ctl(9));
bb=blend(bb+128,128,0,0,ctl(9));
pset(x,y,0,ll);
pset(x,y,1,aa);
pset(x,y,2,bb);
}
}} // x y
return true;
} // for every pixel
With that code, I can create dichotomy between two colour values in 3d Lab space.
I originally wanted this for increasing contrast in fleshtones. As I got to playing with it, I was rather surprised at how well it works with a variety of photographs. For example, helping with desaturating photographs of sunsets with tricky hues.
Oh, I almost forgot. That code has some right-click functionality. Right click Preview to sample White. Ctrl + right click Preview to sample Black. Two other optional do-hickies which aren't too hard to figure out. | 
03-19-2006, 06:15 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | | Here's one to ponder. | 
03-19-2006, 07:10 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | The Rabbit hole gets deeper
Phew. You weren’t joking
There is some good stuff at the X-zone.
But I found this a little easier. Thanks to Philip Harrison http://www.netsoc.tcd.ie/~jgilbert/m...t_product.html
When LW < LB the image goes negative (that’s understandable)
When LW > 200(approx) the image starts to get darker???
Right Click on Preview to sample White
Ctrl + right click Preview to sample Black
Alt + right click Preview seems to give threshold??
Sorry I can’t find another, I was expecting Mid Tones. And I can’t read the code
Oh dear. I’ve so much to learn.
Re the HSB thread.
I did read the follow up Addendums. It was good of you to add that. I don’t think many would have noticed
Vectors made easy http://www.bbc.co.uk/schools/gcsebit...onshrev2.shtml http://www.euclideanspace.com/maths/algebra/vectors/
Ken. (AKA March Hare) | 
03-22-2006, 01:03 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | Vectors and things are cool, but can be hard to wrap around your head. I'm glad you found a link that makes more sense to you.
Visualize what's going on?
Imagine a sphere. This is the Lab sphere. In this sphere, pick two points. Between these two points in a vector and even a normal. If you can imagine it, visualize two planes in the sphere. Each of these planes are parallel to each other and will never touch each other. The two previously defined points are on these planes.
- between the planes, points closer to one plane will darker
- between the planes, points closer to the other plane will be brighter
- points not between the planes will be either black or white
It is a truly 3d gradient.
In the last code,
- right click sets the white point
- ctrl + right click sets the black point
However, alt + click will behave as right click and that probably explains the threshhold effect you are seeing. This is because the two points are rather close together. If you use Levels to set black and white really close together, what do you get? A threshhold effect.
Another little thing that I truly adore is Linear Light. Over 128 goes in one direction and less than 128 goes in the opposite direction. We can actually use the 3d gradient to push values away from each other in a linear 3d fashion. Use the vector/normal as a direction and the gradient as a magnitude. Using this method, it becomes almost trivial to boost contrast in subtle shades and things. Need a little more contrast between slightly red cheeks and slightly yellow jowel? Not a problem.
Early beta of just such a thing: Code: %ffp
SupportedModes: LabMode
dialog: size=(395,175)
ctl[CTL_OK]: MODIFY, pos=(315,155)
ctl[CTL_CANCEL]: MODIFY, pos=(355,155)
ctl(0):standard,"L",pos=(260,15),range=(0,255),val=25,track
ctl(1):standard,"a",pos=(260,25),range=(-127,127),val=-25,track
ctl(2):standard,"b",pos=(260,35),range=(-128,128),val=-25,track
ctl(3):standard,"L",pos=(260,65),range=(0,255),val=240,track
ctl(4):standard,"a",pos=(260,75),range=(-127,127),val=25,track
ctl(5):standard,"b",pos=(260,85),range=(-127,127),val=25,track
//ctl(6):checkbox,"LL against Lightness",pos=(260,105)
ctl(7):groupbox,"Black", pos=(240,5),size=(150,45)
ctl(8):groupbox,"White",pos=(240,55),size=(150,45)
//ctl(9):standard,"Offset",pos=(260,120),range=(-100,100),val=0,track
ctl(10):standard,"Expand",pos=(270,130),range=(-200,200),val=50,track
OnFilterStart:{
setPreviewCursor (32515);
return false;
}
OnCtl(n):{
if (e==FME_RIGHTCLICKED_DOWN && n==CTL_PREVIEW){
int x,y,ll,aa,bb;
x = getPreviewCoordX();//*scaleFactor;
y = getPreviewCoordY();//*scaleFactor;
ll=src(x,y,0);
aa=src(x,y,1)-128;
bb=src(x,y,2)-128;
if(getAsyncKeyState(VK_CONTROL)>=0){
// white
setCtlVal(3,ll);
setCtlVal(4,aa);
setCtlVal(5,bb);
} else {
// black
setCtlVal(0,ll);
setCtlVal(1,aa);
setCtlVal(2,bb);
}
doAction(CA_PREVIEW);
} // end right click
return false;
} // on ctl
ForEveryTile:{
int x,y,ll,aa,bb,c,a;
int final;
//float pi = 3.14159;
// p1 with normalization
int p1x,p1y,p1z;
float p1dist;
float p1nx,p1ny,p1nz;
//p2 without normalization
int p2x,p2y,p2z;
float p2dist;
// theta junk
float pretheta,theta;
float adj;
// can pre-calculate some junk before the loop
// do p1 with offset
p1x=ctl(3)-ctl(0);
p1y=ctl(4)-ctl(1);
p1z=ctl(5)-ctl(2);
// get length of p1
p1dist = sqrt( (float)p1x*p1x + p1y*p1y + p1z*p1z);
//normalize p1
p1nx=(float)p1x/p1dist;
p1ny=(float)p1y/p1dist;
p1nz=(float)p1z/p1dist;
for (y=y_start; y<y_end; y++){
//if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
c = srcp (x,y);
//Explode it into the color values
ll = Rval(c); //c & 0xff;
aa = Gval(c)-128; //c >> 8 & 0xff;
bb = Bval(c)-128; //c >> 16 & 0xff;
//if (Z>3) a = Aval(c); //c >> 24 & 0xff;
// p2
p2x=ll-ctl(0);
p2y=aa-ctl(1);
p2z=bb-ctl(2);
p2dist = sqrt( (float)p2x*p2x + p2y*p2y + p2z*p2z);
// get angle, aka theta
pretheta=(p1x*p2x + p1y*p2y + p1z*p2z) / (p1dist*p2dist);
// don't forget: float + radians
theta=acos(pretheta);
// solve for adj using theta
// conert to linear light form using *100-50
if(theta==0){ // theta hack, seems around 99.99%
final=(p2dist/p1dist)*100-50;
}else{
// now solve to adj
adj=fcos(theta)*p2dist;
final=(adj/p1dist)*100-50;
}
// add it all up
ll=ll+p1nx*final*ctl(10)/100;
aa=aa+p1ny*final*ctl(10)/100;
bb=bb+p1nz*final*ctl(10)/100;
pset(x,y,0,ll);
pset(x,y,1,aa+128);
pset(x,y,2,bb+128);
}} // x y
return true;
} // for every pixel
I've been playing with that code for quite some time and it never ceases to amaze me.
Now, masking using trig in the Lab sphere is a lot of fun - doesn't have to be a point-to-point black-to-white gradient. I'll touch on this later.
(Roland, I got your message finally. I'll get back to you soon.) | 
03-24-2006, 03:51 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | | Meh. Getting sucked into other things. Some quick touchies from Uncle Stroker.
- Used Pythagoras to get distance from a point. Even did some pushing around.
- Used vectors/normals to get distance along a vector. Again, some pushing around.
To get the distance along the vector, used trig one way. However, you can get the distance from a vector using trig the other way. Instead of solving for adj, just solve for opp and go from there. Using this method, you can actually bore holes in the Lab sphere from one point to another. Instead of dichotomy, you are actually connecting. This is a good way of masking some vector operations.
Another good way to push values around or mask is to use theta. Instead of solving for adj or opp, just stop at theta and use that.
Once you start getting the hang of it, you can do some uber ChOps in 3d space. Do you want shades of orange that fade into magenta that are between the two? Not a problem. Harsh coloured lights causing extremely tinted highlights? Not a problem. That funky over-saturated event horizon? Not a problem.
(The event horizon thing is something that I had been wrestling with for quite some time. It's this funny space that exists between light and dark where the change takes place. I call it event horizon, but terminator might be more appropriate. Using trig in Lab, I have finally found a way to isolate it. So far just an annoying curiosity.)
Been getting sucked into several things. Of note, Hue and Sat in Lab space as Photoshop uses them. Been using theta and rho, but Photoshop cubes the sphere and things get funky. Haven't decided on a rampage yet. Another thing I've been messing with is manipulating Hue sort of like Levels. I've done this in RGB, but it is so much easier in Lab space with FM. It is very nice being able to manipulate tonal range in such a manner.
I haven't forgotten about you, Roland. I'm getting there. | 
03-27-2006, 12:50 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | Thanks Stroker.
The effect of the Linear Light code can be quite subtle.
I am still having difficulty with this whole vector thing.
I am slowly working my way through this. http://www.netcomuk.co.uk/~jenolive/vecfind.html
Ken. | 
03-29-2006, 11:32 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | | Oh, my. It appears that I've done it again. I went to MTU for programming and math, and yet I still made a very bad mistake. I don't mind when I make a mistake and keep it to myself, but I have to beat my own butt when I pass my mistakes on to others.
I work in my own little world. I understand things in my world in my own special way. However, I can't always properly express those things to others. That is, I have my own nomenclature and it doesn't always match things outside of my world.
unit vector - vector with unit length of 1
normal - vector that is 90 degrees to another vector in a special way
Through this whole thing I've been using normal to mean unit vector. Shame on me.
I need a classroom with a chaulkboard. | 
03-29-2006, 06:00 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | Thanks Stroker.
Hey, Stroker No worries. I don’t think I’ll ever catch up to you.
Your input to this thread has been absolutely fantastic and I can’t thank you enough for taking the time to do all this for us.
Craig has requested another filter http://www.retouchpro.com/forums/software/13201-another-plugin-idea-desire.html
I will try to write this as it does not sound too difficult.
My main concern is that it will need a ‘feather’ to blend in.
I’m not sure of the code for that.
Ken | 
04-06-2006, 02:16 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | | Anyone good at maths?
I have plotted some input values (grey) and tabulated the required RGB output values.
As you will see from the graph I have taken 21 samples. There are a few anomalies in there but it’s showing some sort of pattern
The output values of Green are easy (Grey – 15 will give something approx)
The Red values are giving me problems
At low Grey values, Red needs to be double Grey (Red = Grey*2) but at higher values Red equals Grey (Red = Grey)
I could do this with a load of ‘If’ Statements but I’m sure there is an easier way.
Any Ideas?
Ken | 
04-06-2006, 02:52 PM
|  | Senior Member | | Join Date: Jan 2005
Posts: 313
| | | red = grey + grey*(255-grey)/128
??? | 
04-07-2006, 05:17 AM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | | Thanks Stroker.
That’s looking pretty good.
The only problem is that input values over 125 are sending the output reds over 255
I guess I could knock these down by a percentage to keep them in range?
I’ll post this code as soon as I get it working.
Ken |
Posting Rules
| You may not post new threads You may not post replies You may not post attachments You may not edit your posts HTML code is Off | | |
Similar Threads | | Thread | Thread Starter | Forum | Replies | Last Post | | | |