![]() |
| |||||||
| Software Photoshop, Paintshop Pro, Painter, etc., and all their various plugins. Of course, you can also discuss all other programs, as well. |
| | Thread Tools |
|
#61
| ||||
| ||||
| 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
(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. |
|
#62
| ||||
| ||||
| 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. |
|
#63
| ||||
| ||||
| 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. |
|
#64
| ||||
| ||||
| 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'. |
|
#65
| ||||
| ||||
| 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
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. |
|
#66
| ||||
| ||||
| Here's one to ponder. |
|
#67
| ||||
| ||||
| 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) |
|
#68
| ||||
| ||||
| 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
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.) |
|
#69
| ||||
| ||||
| 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. |
|
#70
| ||||
| ||||
| 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. |
|
#71
| ||||
| ||||
| 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. |
|
#72
| ||||
| ||||
| 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 |
|
#73
| ||||
| ||||
| 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 |
|
#74
| ||||
| ||||
| red = grey + grey*(255-grey)/128 ??? |
|
#75
| ||||
| ||||
| 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 |
|
#76
| ||||
| ||||
| Here it is. Ken’s colourizing skin Filter. It may be small but it works great. The graph in post 145 shows how real skintones look in b&w. And the graph below shows how this code puts the colour back. It adds tonal variations to the skin that would be difficult to achieve with a brush My sample points come from skin charts and real pictures. Here is how to use it for a base to colour skin. Run the code on a b&w or colour photo. In PS add a hide all mask and paint back the skin (and hair) Add a Hue/Sat Adjustment and adjust the saturation to taste. (Sat -20 to -50 usually looks good) This is by no means a finished colorize, but it is a good (and quick) start. Please let me know what you think. If you find it useful I will package it up for our friends. NB. All the swatches I used were for European skin. I may add other Nationalities. Thanks for your help Stroker. I changed 128 to 255 as I found it a better fit overall. Ken. Code: %ffp
Category :"Ken"
Title :"Ken's Skin Colorizer"
Copyright :"Ken ©2006"
Author :"CameraKen"
Filename :"Ken's Skin Colorizer.8bf"
Description :"European Skin Colorizer"
Version :"1.0"
Dialog :"CameraKen's European Skin Colorizer"
supportedmodes:RGBMode
ForEveryTile:{
int r,g,b,Grey, rv,gv,bv;
for (y=y_start; y<y_end; y++){
if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
//(Grey=src(x,y,0)+src(x,y,1)+src(x,y,2))/3;
Grey=(src(x,y,0)*0.30+src(x,y,1)*0.59+src(x,y,2)*0.11);
rv=Grey+Grey*(255-Grey)/255;
gv=Grey -15;
bv=Grey-Grey*(255-Grey)/255;
pset(x,y,0, rv );
pset(x,y,1, gv );
pset(x,y,2, bv );
}}
return true;
}
|
|
#77
| ||||
| ||||
| Rock on, Ken. Take the idea a bit further and have different data sets for different sets of skin tones. Snapper might be a good idea. Great. Now I'm going to be messing with this idea. Back when I started messing with vectors in Lab mode, I started using a technique that I call Spherify. Basically take a point from the center and then shoot it onto the outside of the Lab sphere. p1 = center of the sphere p2 = given pixel values get the vector convert vector to unit vector final values = unit vector * 127 Interesting technique. Can show you a few things about an image with a glance. But it does take a bit of getting used to. As I started looking at the final Spherify images more and more often, I started to see another data set that I wanted. I eventually took Spherify and ChOpped it into Event Horizon. This is kind of a weird way of dividing a photograph into mids, highs, and lows. A bizarre mix of Lightness and saturation/rho. When you run the code, you will be left with a mess. You can ignore the mess because the good stuff is in the individual channels. L = mids a = highs b = lows These channels are an interesting place to work with contrast, saturation, and things. Code: %ffp
Category:"Tech Slop Lab"
Title:"Event Horizon"
Author:"JLHalmich"
Copyright: "2006 by JLHalmich"
Organization:"Tech Slop Lab"
Version:"beta 1"
Filename: "TS_eventhorizon.8bf"
Description: "Lightness and Rho for\nHigh/Mid/Low"
About: "!t Plug-in !V\n!C\n!c\n!D"
supportedmodes: labmode
dialog: size=(240,185)
ctl[CTL_OK]: MODIFY, pos=(160,165)
ctl[CTL_CANCEL]: MODIFY, pos=(200,165)
ForEveryTile:{
int x,y,ll,aa,bb,rho;
float p1dist,p1nx,p1ny,p1nz;
for (y=y_start; y<y_end; y++){
//if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
// grab values
ll=src(x,y,0)-128;
aa=src(x,y,1)-128;
bb=src(x,y,2)-128;
// get distance
p1dist=sqrt((float)ll*ll + aa*aa + bb*bb);
// unit vector
p1nx=(float)ll/p1dist;
p1ny=(float)aa/p1dist;
p1nz=(float)bb/p1dist;
// spherify a and b, then get rho
// rho*2 for range of 0 to 255
aa=p1ny*127;
bb=p1nz*127;
rho=c2m(aa,bb)*2;
// reset and recalc a and b for high/low
aa=0; bb=0;
if(ll>=0){aa=255-rho;}
if(ll<0){bb=255-rho;}
// output
pset(x,y,0,rho);
pset(x,y,1,aa);
pset(x,y,2,bb);
}}// x, y
return true;
} // for every tile
|
|
#78
| ||||
| ||||
| Thanks Stroker. Glad you like it. The graphs are the important bit. Once we have these the rest could be done with curves. But FM is easier. I’ve tried it on a lot of b&w pictures and it’s worked OK with all of them. The more tones in the b&w the better the colour. It’s interesting that there is this pattern to the way skintones desaturate. If all colours desaturate slightly differently it may be possible to make good guesses at what the colours were before the picture was made b&w (just a thought) I may try grass, skies, wood, hair etc as well. I intend to add other skintones and a sat slider and a couple of adjustment to this. I’ll keep playing with it. Snapper(Gen-A) – I don’t understand? This seems to add grain and alter transparency. Would I not be better altering saturation? Or are you suggesting I use the BMP method of loading a file? There is another bitmap loader here. http://groups.yahoo.com/group/FMML/message/3622 Event Horizon Interesting Stroker. But what can I do with it? Its messed up the picture so adjusting Mids, Highs and Lows would be of no use. Or can you ‘see’ something in the channels that I can’t? Ken |
|
#79
| ||||
| ||||
| Yes, I was refering to the bitmap loader code. Each horizontal line in the bitmap can be a different data set of values. It would be like having a bunch of gradient maps or Curves presets in one little bitmap. The problem you are going to run into is one of variance. How would you handle a peach forehead and rosie cheeks that have the same brightness values? Delve into segmentation? Some post filter painting? While there is a general pattern, variance is key and may not be easy. Event Horizon - the secrets are held in the individual channels. I'll see about putting something together. However, I'm leaving tomorrow and won't be back for a week or so. |
|
#80
| ||||
| ||||
| I improved the code slightly by changing gv=Grey-15 to gv-gv*(255-gv)/1000 This keeps the lows and mids the same but adds a little more green to the highlights. I now understand why most use CMYK for skin. RGB values contain the colour and the luminosity. And that is why my graphs were not uniform. In CMYK the Black is split out. So it is easier to talk percentages. SupportedModes: CMYKMODE test=(c+m+y); This works OK. But SupportedModes: CMYKMODE test=(c+m+y+k); This does not work. How do I access the Black channel? There seems to be nothing at FM about this but there is are FM command rgb2cmyk and cmyk2rgb??? But there is nothing in the Wiki. I have also got some code to convert RGB to CMYK but they talk about ‘Normalising’ the data. Is this just a case of changing 0-255 to 0-1 ? RGB -> CMYK Black=minimum(1-Red,1-Green,1-Blue) Cyan=(1-Red-Black)/(1-Black) Magenta=(1-Green-Black)/(1-Black) Yellow=(1-Blue-Black)/(1-Black) CMYK -> RGB Red=1-minimum(1,Cyan*(1-Black)+Black) Green=1-minimum(1,Magenta*(1-Black)+Black) Blue=1-minimum(1,Yellow*(1-Black)+Black) C, M, Y, K, R, G, and B have a range of [0;1]. http://www.scarse.org/docs/color_faq.html#rgb http://www.scarse.org/docs/color_faq.html Ken |
|
#81
| ||||
| ||||
| ken and stroker, et al, just to show my ignorance on all this, psp doesnt allow working in cmyk, so, i'm guessing from your line "SupportedModes: CMYKMODE" that this code wouldnt work in psp. true? craig |
|
#82
| ||||
| ||||
| I'm mostly back but not quite. When dealing with CMYK, things are a bit different. For one, CMYK is subtractive for print as oppossed to additive for monitors. Then there is the CMY <> K paradigm. Even though K is a channel, it's not a true channel. Then there is the gamut and ICC thing, which I'm not very fluent with. I'm not sure how well PS and FM will get along when doing CMYK with FM. Right now I can't say that I recommend using FM for CMYK if print is your thing. I honestly don't know. Code: %ffp
supportedmodes: cmykmode
ForEveryTile:{
int x,y,k;
for (y=y_start; y<y_end; y++){
//if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
// grab k
k=src(x,y,3);
pset(x,y,0,255);
pset(x,y,1,255);
pset(x,y,2,255);
pset(x,y,3,k);
}}// x, y
return true;
} // for every tile
|
|
#83
| ||||
| ||||
| I was thinking more of converting RGB to CMYK to adjust colours (skin is easier in percentage terms) and the convert back to RGB. Or am I wasting my time? I wrote this but I can’t get it working. It does something but the channels are very dark? I’m not sure what the problem is? If its my code then I can’t see my error. I’ve found the same formulas at several different sources so they should be OK. I also tried float but got ‘not yet implemented errors’ Ken. Code: %ffp
Title :"RGB to CMYK"
Author :"CameraKen"
Dialog :"RGB to CMYK"
supportedmodes:RGBMode
ForEveryTile:{
int Red,Green,Blue,Cyan,Magenta,Yellow,Black,rv,gv,bv;
for (y=y_start; y<y_end; y++){
if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
//Get to a range of 0 to 1 (Normalise)
rv=(src(x,y,0)/255*100);
gv=(src(x,y,1)/255*100);
bv=(src(x,y,2)/255*100);
// convert to CMY
Black=min(1-rv,1-gv,1-bv);
Cyan=(1-rv-Black)/(1-Black);
Magenta=(1-gv-Black)/(1-Black);
Yellow=(1-bv-Black)/(1-Black);
//Convert CMYK back to RGB
//Red=1-min(1,Cyan*(1-Black)+Black);
//Green=1-min(1,Magenta*(1-Black)+Black);
//Blue=1-min(1,Yellow*(1-Black)+Black);
//CMY have values 0 to 1 so multiply by 255
pset(x,y,0,Cyan*255 );
pset(x,y,1,Magenta*255 );
pset(x,y,2,Yellow*255 );
}}
return true;
}
|
|
#84
| ||||
| ||||
| You got your ranges messed up a little bit. Might as well stick with range 0 to 255. Code: %ffp
Title :"RGB to CMYK"
Author :"CameraKen"
Dialog :"RGB to CMYK"
ctl(0):standard,"Cyan",range=(-255,255),val=0,track
ctl(1):standard,"Magenta",range=(-255,255),val=0,track
ctl(2):standard,"Yellow",range=(-255,255),val=0,track
supportedmodes:RGBMode
ForEveryTile:{
int Red,Green,Blue,Cyan,Magenta,Yellow,Black;
for (y=y_start; y<y_end; y++){
//if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
//Get to a range of 0 to 1 (Normalise)
Red=src(x,y,0);
Green=src(x,y,1);
Blue=src(x,y,2);
// convert to CMY
Cyan=255-Red;
Magenta=255-Green;
Yellow=255-Blue;
// not going to bother with Black
// might be +=Black ???
/**
Black=min(Cyan,min(Magenta,Yellow));
Cyan-=Black;
Magenta-=Black;
Yellow-=Black;
**/
// modify
Cyan+=ctl(0);
Magenta+=ctl(1);
Yellow+=ctl(2);
// back to RGB
Red=255-Cyan;
Green=255-Magenta;
Blue=255-Yellow;
//Convert CMYK back to RGB
//Red=1-min(1,Cyan*(1-Black)+Black);
//Green=1-min(1,Magenta*(1-Black)+Black);
//Blue=1-min(1,Yellow*(1-Black)+Black);
//CMY have values 0 to 1 so multiply by 255
pset(x,y,0,Red);
pset(x,y,1,Green);
pset(x,y,2,Blue);
}}
return true;
}
|
|
#85
| ||||
| ||||
| Thanks Stroker. I searched for ages to get a RGB to CMYK conversion. I tried + and – black and +=Black seemed closest although it’s not the same as PS. Adjusting skin tones in CMYK is easier than RGB because you can work in percentages. An average Caucasian should be around C-15%-17%; M-32%-38%; Y-53%-55%; K-0% (ignore the black as it doesn't contribute to colour) In RGB the Black content is included in the RGB values and makes the correction more difficult (as my graphs showed) Maybe there is an easy way by using Hue/Sat (with Lum removed) as well. But the only way I know is CMY. I understand that many now use LAB .”normal" skin B would usually be 5 to 10 points higher than A. Which is another method I will look at. Ken. |
|
#86
| ||||
| ||||
| I did some messing around and I'm pretty sure I understand the process. Here is the code to translate one percentage to another percentage. F in the controls means From, and T means To. Cyan F = 20 Cyan T = 25 That means take cyan of 20% and make it 25%. The rest will follow. (This is actually very similiar to my Colour2Colour filter.) * You will have to pardon the percentages being in the range of 0 to 100 instead of 0 to 1. Going to 100 is a simple way of hacking out some of the calculations. One thing to watch out for is K/black. The formula used is very crude compared to what Photoshop does. I don't know how much of a difference this will make to some of you folks, but seems okay to me because I don't do this kind of thing. If you understand what's going on, you may want to eventually do it straight-up RGB. After all, the simple formulas used in the code are nothing more than simple inversions. Or not depending on what you are comfortable with. Clean up it, add features, package it, make lots of money, and sent me 10%. Code: %ffp
ctl(0):standard,"Cyan F",range=(0,100),val=15,track
ctl(1):standard,"Magenta F",range=(0,100),val=30,track
ctl(2):standard,"Yellow F",range=(0,100),val=50,track
ctl(3):standard,"Cyan T",range=(0,100),val=15,track
ctl(4):standard,"Magenta T",range=(0,100),val=30,track
ctl(5):standard,"Yellow T",range=(0,100),val=50,track
supportedmodes: rgbmode
ForEveryTile:{
int x,y,red,green,blue,range;
int black,cyan,magenta,yellow;
for (y=y_start; y<y_end; y++){
//if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
// grab values
cyan=255-src(x,y,0);
magenta=255-src(x,y,1);
yellow=255-src(x,y,2);
// get k or black
black=min(cyan,min(magenta,yellow));
// subtract black
// range 0 to 255 ---> range 0 to 100
cyan=(cyan-black)/2.55;
magenta=(magenta-black)/2.55;
yellow=(yellow-black)/2.55;
// no need for this
//range=255-black;
// scale cyan
if(cyan<=ctl(0)){
cyan=scl(cyan,0,ctl(0),0,ctl(3));
} else {
cyan=scl(cyan,ctl(0),100,ctl(3),100);
}
// scale magenta
if(magenta<=ctl(1)){
magenta=scl(magenta,0,ctl(1),0,ctl(4));
} else {
magenta=scl(magenta,ctl(1),100,ctl(4),100);
}
// scale yellow
if(yellow<=ctl(2)){
yellow=scl(yellow,0,ctl(2),0,ctl(5));
} else {
yellow=scl(yellow,ctl(2),100,ctl(5),100);
}
// back to rgb
// range 0 to 100 ---> range 0 to 255
// order of operations, so it's fine
red=255-cyan*2.55-black;
green=255-magenta*2.55-black;
blue=255-yellow*2.55-black;
// output
pset(x,y,0,red);
pset(x,y,1,green);
pset(x,y,2,blue);
}}// x, y
return true;
} // for every tile
|
|
#87
| ||||
| ||||
| Thanks for the code Stroker. However I am beginning to question this conversion code. I just loaded a picture with skin of C18%, M56% Y72%. Yellow and Magenta work fine but Cyan did not. I am beginning to suspect the conversion. FM has two built in functions rgb2cmyk cmyk2rgb Harald Heim has given us the internal code of these half way down this page http://groups.yahoo.com/group/FMML/message/3220 Code: static int fm_rgb2cmyk(int r, int g, int b, int z)
{
int k;
int bitMultiply= (bitDepthMode == 16 ? 128 : 1);
k=255*bitMultiply-max(r,max(g,b));
if (z == 0)
return 255*bitMultiply-r-k;
else if (z == 1)
return 255*bitMultiply-g-k;
else if (z == 2)
return 255*bitMultiply-b-k;
else if (z == 3)
return k;
else
return 0;
}
static int fm_cmyk2rgb(int c, int m, int y, int k, int z)
{
int bitMultiply= (bitDepthMode == 16 ? 128 : 1);
if (z == 0)
return 255*bitMultiply-c-k;
else if (z == 1)
return 255*bitMultiply-m-k;
else if (z == 2)
return 255*bitMultiply-y-k;
else
return 0;
If we get RGB to CMYK working then my next project will be a RGBCMYK mono channel mixer. This would be useful for PS users and especially PSP users who do not have access to CMYK. Quote:
Ken |
|
#88
| ||||
| ||||
| Harald's rgb2cmyk and cmyk2rgb internal code use the exact same method that we've been using. Written differently, but still the same. I messed with the functions and had no problem with k. Hmmm. One interesting difference: Code: int bitMultiply= (bitDepthMode == 16 ? 128 : 1); The cyan problem you are running into is a logic bomb related to the conversion (you are right). Our code is no where near as sophisticated as Photoshop. Our simpleness has spawned ugly. Can you find it? Can you hack it? * There is another logic bomb in there. Consider: CMY are percentages, but a percentages of what? Logic bombs are the good stuffs of programming. Last edited by Stroker; 04-16-2006 at 01:06 PM. |
|
#89
| ||||
| ||||
| I started my Channel Mixer which demonstrates the problem better. The Cyan slider does virtually nothing. The Magenta and Yellows sliders do something but because CMYK is subtractive it seems to be taking away rather than adding. However invert these and we are back to RGB. So the whole point of doing this is lost. The main problem seems to be that some RGB has to be present to see the effect of the CMY sliders. I read in the usergroup that RGB>LAB>CMYK may be better that RGB>CMYK There is something very wrong here? We need a new formula. I very often drag a CMYK layer to a RGB layer. So it is possible to display CMYK in RGB space. Percentages of What? Good question. Never thought about that. R128, G128, B128 = C52%, M43%, Y43%, K8% So adding black back would make them all 50%ish. That sounds OK. R255, G255, B255 = C0%, M0%, Y0%, K0% R0, G0, B0 = C75%, M68%, Y67%, K90%. Hmmm I see what you mean. The above figures are taken with the eyedropper in PS. The figures from this online conversion tool make more sense http://www.forret.com/tools/color.asp?R=128&G=128&B=128 Ken Code: %ffp
Title :"RGB and CMYK Channel Mixer"
ctl(0):standard,"Red",range=(0,100),val=30,track
ctl(1):standard,"Green",range=(0,100),val=59,track
ctl(2):standard,"Blue",range=(0,100),val=11,track
ctl(3):standard,"Cyan",range=(0,100),val=0,track
ctl(4):standard,"Magenta",range=(0,100),val=0,track
ctl(5):standard,"Yellow",range=(0,100),val=0,track
ctl(6):standard,"Black",range=(0,100),val=0,track
supportedmodes: rgbmode
ForEveryTile:{
int x,y,red,green,blue,range;
int black,cyan,magenta,yellow;
int out;
for (y=y_start; y<y_end; y++){
if(updateProgress(y,y_end)) abort();
for (x=x_start; x<x_end; x++){
// grab values
red=src(x,y,0);
green=src(x,y,1);
blue=src(x,y,2);
cyan=255-src(x,y,0);
magenta=255-src(x,y,1);
yellow=255-src(x,y,2);
// get k or black
black=min(cyan,min(magenta,yellow));
// subtract black
// range 0 to 255 ---> range 0 to 100
cyan=(cyan-black)/2.55;
magenta=(magenta-black)/2.55;
yellow=(yellow-black)/2.55;
// Scale values
out=(red*ctl(0)/100)+(green*ctl(1)/100)+(blue*ctl(2)/100);
out=out+(cyan*ctl(3)/100)+(magenta*ctl(4)/100)+(yellow*ctl(5)/100);
out=out+(black*ctl(6)/100);
// output
pset(x,y,0,out);
pset(x,y,1,out);
pset(x,y,2,out);
}}// x, y
return true;
} // for every tile
|
|
#90
| ||||
| ||||
| The conversion code we are using IS wrong. Its intended to show how RGB may look when Printed. It is not intended for any sort of manipulation purposes. http://en.wikipedia.org/wiki/CMYK In the code increasing Red reduces the Cyan. This is correct when printing but what we would need to do is increase Magenta and Yellow for an Additive screen view. Ken |
| Thread Tools | |
| |
| | ||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Color Space conversions | Reimar | Photoshop Elements Help | 4 | 01-16-2004 06:58 AM |
| Batch Raw conversions in PS CS | okplayer | Software | 0 | 12-19-2003 01:03 PM |