[C++]实现Photoshop可选颜色算法之优化,与PS调节结果基本一致
【记录】
最近需要用到PS的可选颜色功能进行图像成批量处理,由于需要处理的图像数量巨大,使用PS的批处理功能效率太慢,平均每秒也就几张图像,通过查找资源发现有C++版本的可选颜色算法实现。
经过与PS对比测试发现有如下问题:
白色,黑色、中性色调整与PS结果不一致,其他颜色调整相对模式且当调整值为正值时明显可见有超调问题。
每调整通道内黑色与其他颜色调整值混合系数计算方式是错误的,在同时调整黑色和其他颜色时输出结果错误。
经过研究算法原理和测试优化,现调整结果已与PS结果基本一致,只在某些情况可能存在一个色值的误差。
1、白色、黑色计算初始参考值分别应参考原像素RGB的最小和最大分量,中性色应同时参考原像素RGB的最小和最大分量。
2、解决相对模式超调问题,同绝对模式一样需要限制调节结果范围。
3、修正颜色调整值混合系数计算方式的错误,在调整黑色时是相对全局的,同时作用于其他颜色,黑色调整值优先,系数计算公式:value = color * (1.0 + black) + black,黑色值为负时弱化其他颜色调整系数,黑色值为正数时则强化其他颜色调整系数,该系数绝对模式与相对模式计算方式相同。
在SelectiveColorAdjust类里声明变量,此处计算保存与黑色与其他调整值的混合系数结果或在设置值时计算,避免在处理图像循环内重复计算:
//calculate color adjustment is defined
for (int i = 0; i <= SELECT_BLACK; i++) {
colors[i].calcDefined();
if (colors[i].defined) {
if (colors[i].black != 0.0f) {
colors[i].cyan_black = colors[i].cyan * (1.0f + colors[i].black) + colors[i].black;
colors[i].magenta_black = colors[i].magenta * (1.0f + colors[i].black) + colors[i].black;
colors[i].yellow_black = colors[i].yellow * (1.0f + colors[i].black) + colors[i].black;
}
else {
colors[i].cyan_black = colors[i].cyan;
colors[i].magenta_black = colors[i].magenta;
colors[i].yellow_black = colors[i].yellow;
}
}
}
白色、中性色、黑色初始参考值计算(注释包含公式):
//calculate WHITE (range = 2 * min - 255)
c[SELECT_WHITE] = sorted[2] <= 127 ? 0 : 2 * sorted[2];
//calculate MIDDLE (range = 255 - (|max - 128| + |min - 128|))
c[SELECT_MIDDLE] = COLOR_RANGE(255 - (abs(sorted[0] - 128) + abs(sorted[2] - 128)));
//calculate BLACK (range = 255 - 2 * max)
c[SELECT_BLACK] = sorted[0] <= 127 ? 255 - 2 * sorted[0] : 0;
相对模式调整结果同样需要限制计算结果范围:
//calculate each selective color
if (isAbsolute) {
for (int j = 0; j <= SELECT_BLACK; j++) {
if (colors[j].defined && (c[j] > 0)) {
delta[BLUE] += c[j] * CLIP_RANGE(colors[j].yellow_black, ratio_negative[BLUE], ratio_positive[BLUE]);
delta[GREEN] += c[j] * CLIP_RANGE(colors[j].magenta_black, ratio_negative[GREEN], ratio_positive[GREEN]);
delta[RED] += c[j] * CLIP_RANGE(colors[j].cyan_black, ratio_negative[RED], ratio_positive[RED]);
}
}
}
else {
for (int j = 0; j <= SELECT_BLACK; j++) {
if (colors[j].defined && (c[j] > 0)) {
delta[BLUE] += c[j] * CLIP_RANGE(colors[j].yellow_black * -ratio_negative[BLUE], ratio_negative[BLUE], ratio_positive[BLUE]);
delta[GREEN] += c[j] * CLIP_RANGE(colors[j].magenta_black * -ratio_negative[GREEN], ratio_negative[GREEN], ratio_positive[GREEN]);
delta[RED] += c[j] * CLIP_RANGE(colors[j].cyan_black * -ratio_negative[RED], ratio_negative[RED], ratio_positive[RED]);
}
}
}
//save to output
out[BLUE] = (uchar)COLOR_RANGE(in[BLUE] - (int)round(delta[BLUE]));
out[GREEN] = (uchar)COLOR_RANGE(in[GREEN] - (int)round(delta[GREEN]));
out[RED] = (uchar)COLOR_RANGE(in[RED] - (int)round(delta[RED]));
另外,参考的原始代码中有一个bug,修正如下:
void SelectiveColorAdjust::calcDefined()
{
//判断调整值为非0时有效
if (cyan != 0.0f || magenta != 0.0f || yellow != 0.0f || black != 0.0f) {
defined = true;
return;
}
defined = false;
}
测试结果(2025-3-10):
经过大幅度参数调整测试,实际输出并不完全等同于PS的结果,某些情况下可能会存在一个色值的肉眼不可察觉的误差,本代码算法是在RGB模式处理,可能是由于PS转换为其他颜色空间来处理,所以导致两者存在差异,使用双精度浮点数测试结果一样,使用整形累计存储分量调整结果也没多大区别,所以后续优化为全整形运算也是没有问题的。
测试参数:
SELECT-RED cyan: -61, magenta: 28, yellow: -49, black: 32
SELECT-YELLOW cyan: 0, magenta: 72, yellow: -77, black: 0
SELECT-GREEN cyan: -58, magenta: -60, yellow: 0, black: 96
SELECT-CYAN cyan: 0, magenta: 0, yellow: 0, black: 0
SELECT-BLUE cyan: 0, magenta: -54, yellow: 78, black: 0
SELECT-MAGENTA cyan: -72, magenta: 0, yellow: 0, black: 0
SELECT-WHITE cyan: 26, magenta: -7, yellow: -41, black: 0
SELECT-MIDDLE cyan: 65, magenta: 0, yellow: 38, black: -27
SELECT-BLACK cyan: -19, magenta: 14, yellow: 8, black: 21
METHOD relative
原始图像(PSv20.0.5自带调色板,测试颜色足够标准丰富):
应用专业显示器颜色管理校正后,浏览器默认情况可能无法显示正确的RGB色彩,截图颜色也不正确,通常看起来颜色饱和度不足,可以在浏览器的地址栏中输入特定的URL(如chrome://flags或edge://flags),搜索“Force color profile”并选择正确的颜色配置文件(如sRGB)。
PS处理结果:
本代码算法处理结果:
红色文字标示的色值为差异色值。