| 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. | 
04-07-2006, 03:02 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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;
}
| 
04-09-2006, 07:53 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 314
| | 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
| 
04-09-2006, 06:59 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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 | 
04-10-2006, 01:45 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 314
| | | 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. | 
04-14-2006, 05:27 AM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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 | 
04-14-2006, 01:25 PM
|  | Moderator | | Join Date: Apr 2005 Location: somewhere over there
Posts: 6,730
| | 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 | 
04-14-2006, 03:39 PM
|  | Senior Member | | Join Date: Jan 2005
Posts: 314
| | 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
| 
04-14-2006, 04:03 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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;
}
| 
04-14-2006, 04:26 PM
|  | Senior Member | | Join Date: Jan 2005
Posts: 314
| | 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;
}
| 
04-14-2006, 08:28 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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. | 
04-15-2006, 02:17 AM
|  | Senior Member | | Join Date: Jan 2005
Posts: 314
| | 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
| 
04-15-2006, 03:05 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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;
I would really like to get these working for the sake of comparison. Reading the internal code it appears that fm_cmyk2rgb is expecting c,m,y,k but I can’t get it to accept a value for k. Also fm_rgb2cmyk seems to give dark results like I was getting originally.
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: |
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.
| Yes. I agree. But skin and channel mixer are two exceptions I think. When restoring an old B&W photo I always look at All the channels. Very often more detail can be obtained starting with a CMY mix.
Ken | 
04-16-2006, 12:50 PM
|  | Senior Member | | Join Date: Jan 2005
Posts: 314
| | 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);
Do you know what that is? That is 16-bit compatability. If you look closely, you'll find that it means something rather sinister. The plot sickens. dun dun duuunnnnn
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.
| 
04-16-2006, 03:59 PM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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
| 
04-17-2006, 09:37 AM
|  | Senior Member | | Join Date: Feb 2005 Location: Lancashire (UK)
Posts: 1,112
| | 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 |
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 | | |