# What Is the Algorithm Behind Photoshop's "Black and White" Adjustment Layer?

Can someone explain the algorithm behind the "Black and White" adjustment layer in photoshop?

I have to reproduce it using C++ for an application that emphasizes non-red/magenta(ish) pixels from an image (with a percent-like configurable tolerance), and this resource showed the behavior I'm expecting.

Still couldn't reproduce it, but I found a clue:

Each pixel is defined by up to two controls, one additive (RGB) and one subtractive (CMY).

• Did you ever get any closer to figuring out the algorithm? I'm trying to figure this out as well. May 15 '13 at 16:26
• No, i didn't :/ May 16 '13 at 16:58

I replicated the algorithm perfectly in MATLAB (Based on @Ivan Kuckir answer):

function [ mO ] = ApplyBlackWhiteFilter( mI, vCoeffValues )

FALSE   = 0;
TRUE    = 1;

OFF = 0;
ON  = 1;

numRows = size(mI, 1);
numCols = size(mI, 2);
dataClass = class(mI);

numCoeff    = size(vCoeffValues, 1);
vHueVal     = [0:(numCoeff - 1)] * hueRadius;

mHsl = ConvertRgbToHsl(mI);
mO = zeros(numRows, numCols, dataClass);

vCoeffValues = numCoeff * vCoeffValues;

for jj = 1:numCols
for ii = 1:numRows
hueVal = mHsl(ii, jj, 1);
lumCoeff = 0;

% For kk = 1 we're dealing with circular distance
diffVal     = min(abs(vHueVal(1) - hueVal), abs(1 - hueVal));
lumCoeff    = lumCoeff + (vCoeffValues(1) * max(0, hueRadius - diffVal));
for kk = 2:numCoeff
lumCoeff = lumCoeff + (vCoeffValues(kk) * max(0, hueRadius - abs(vHueVal(kk) - hueVal)));
end

mO(ii, jj) = mHsl(ii, jj, 3) * (1 + lumCoeff);
end
end

end


Pay attention that the conversion from vPhotoshopValues to vCoeffValues should be done as vCoeffValues = (vPhotoshopValues - 50) ./ 50.
As Photoshop values are in [-200, 300] and should be linearly mapped into [-5, 5] with 50 -> 0.

Here is a comparison to Photoshop:

The maximum error is less than 1 in [0, 255] range.

The full code is available on my StackExchange Signal Processing Q688 GitHub Repository.

Each (color) image is composed of RGB components. when you add (or reduce) a constant value to all pixels only in RED components you will see the effect equivalent to moving the RED tab towards the right, and same way reducing the RED component by a constant will have the reverse effect.

Like wise you can increment/decrement each component by a fixed value as described. If you increase/decrease ALL RGB components by a same value this will be equivalent to change in brightness (basically you are adding/removing WHITE color).

The Cyan, Blue, Magenta - corresponds to such transformation in CMYK color space. (But i guess, Blue in this color space corresponds to mix of Cyan and Yellow. So that's a bit tricky. The transformation is essentially same for all.

The last element Tint: { Hue and Saturation } corresponds to same operations but here, the images is first transformed in HSV model and then HUE and Saturation is added/subtracted independently.

I don't know about the exact relationships of markers of the dial to the corresponding numbers but that you can figure out by trying out practical values.

• There is RGB and CMY, so all you need to do is transform to any of the two color spaces and you can adjust as much as you want. Just don't forget to update the other triplet once you're done adjusting. Nov 23 '11 at 17:13
• I believe this answer is incorrect and incomplete. 1. In the PS implementation, sliding the "blue" slider doesn't affect the brightness of cyan-ish pixels and sliding the "cyan" slider doesn't afffect the brightness of blue-ish pixels. This approach described in this answer wouldn't work like that. 2. you don't describe how, after RGBCMY components are manipulated, they're converted to grayscale (although it's probably a dotProduct(color, vec3(0.2989, 0.5870, 0.1140) operation). 3. Blue doesn't "correspond to a mix of cyan and yellow" in any color space. Jan 11 '17 at 21:09
• 4. You don't mention that the tint operation is performed after the conversion to grayscale and not before that. Jan 11 '17 at 21:09