复制 Photoshop sRGB 到 LAB 转换

2024-04-16

我想要实现的任务是复制 Photoshop RGB 到 LAB 的转换。
为简单起见,我将描述我如何仅提取 L 通道。

提取 Photoshop 的 L 通道

这是 RGB 图像,其中包括所有 RGB 颜色(请点击下载):

为了提取 Photoshop 的 LAB,我做了以下操作:

  1. 将图像加载到 Photoshop 中。
  2. 将模式设置为 LAB。
  3. 在通道面板中选择 L 通道。
  4. 将模式设置为灰度。
  5. 将模式设置为 RGB。
  6. 另存为 PNG。

这是 Photoshop 的 L 通道(这正是在 LAB 模式下选择 L 通道时在屏幕上看到的内容):

sRGB 到 LAB 转换

我的主要参考是布鲁斯·林德布鲁姆很棒的网站 http://www.brucelindbloom.com/.
还知道的是Photoshop 在其 LAB 模式下使用 D50 白点 http://www.color-image.com/2011/10/the-reference-white-in-adobe-photoshop-lab-mode/(也可以看看维基百科的 LAB 色彩空间页面 https://en.wikipedia.org/wiki/Lab_color_space).

假设 RGB 图像采用 sRGB 格式,则转换如下:

sRGB -> XYZ (White Point D65) -> XYZ (White Point D50) -> LAB

假设数据处于 [0, 1] 范围内的 Float 中,则阶段由下式给出:

  1. 将 sRGB 转换为 XYZ http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html.
    转换矩阵由下式给出RGB -> XYZ 矩阵 http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html(参见 sRGB D65)。
  2. 从 XYZ D65 转换为 XYZ D50
    转换是使用完成的色彩适应矩阵 http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html。由于上一步和这是矩阵乘法,因此它们可以组合成一个矩阵,该矩阵从 sRGB -> XYZ D50 (请参见底部RGB 到 XYZ 矩阵 http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html)。请注意,Photoshop 使用 Bradford 适应方法。
  3. 从 XYZ D50 转换为 LAB
    转换是使用XYZ 到 LAB 步骤 http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html.

MATLAB 代码

因为,首先,我只是在 L 通道的事情变得简单一点之后。图像被加载到 MATLAB 中并转换为 Float [0, 1] 范围。

这是代码:

%% Setting Enviorment Parameters

INPUT_IMAGE_RGB             = 'RgbColors.png';
INPUT_IMAGE_L_PHOTOSHOP     = 'RgbColorsL.png';


%% Loading Data

mImageRgb   = im2double(imread(INPUT_IMAGE_RGB));
mImageLPhotoshop     = im2double(imread(INPUT_IMAGE_L_PHOTOSHOP));
mImageLPhotoshop     = mImageLPhotoshop(:, :, 1); %<! All channels are identical


%% Convert to L Channel

mImageLMatlab = ConvertRgbToL(mImageRgb, 1);


%% Display Results
figure();
imshow(mImageLPhotoshop);
title('L Channel - Photoshop');

figure();
imshow(mImageLMatlab);
title('L Channel - MATLAB');

函数在哪里ConvertRgbToL()是(谁)给的:

function [ mLChannel ] = ConvertRgbToL( mRgbImage, sRgbMode )

OFF = 0;
ON  = 1;

RED_CHANNEL_IDX     = 1;
GREEN_CHANNEL_IDX   = 2;
BLUE_CHANNEL_IDX    = 3;

RGB_TO_Y_MAT = [0.2225045, 0.7168786, 0.0606169]; %<! D50

Y_CHANNEL_THR = 0.008856;

% sRGB Compensation
if(sRgbMode == ON)
    vLinIdx = mRgbImage < 0.04045;

    mRgbImage(vLinIdx)  = mRgbImage(vLinIdx) ./ 12.92;
    mRgbImage(~vLinIdx) = ((mRgbImage(~vLinIdx) + 0.055) ./ 1.055) .^ 2.4;
end

% RGB to XYZ (D50)
mY = (RGB_TO_Y_MAT(1) .* mRgbImage(:, :, RED_CHANNEL_IDX)) + (RGB_TO_Y_MAT(2) .* mRgbImage(:, :, GREEN_CHANNEL_IDX)) + (RGB_TO_Y_MAT(3) .* mRgbImage(:, :, BLUE_CHANNEL_IDX));

vYThrIdx = mY > Y_CHANNEL_THR;

mY3 = mY .^ (1 / 3);

mLChannel = ((vYThrIdx .* (116 * mY3 - 16.0)) + ((~vYThrIdx) .* (903.3 * mY))) ./ 100;


end

正如人们所看到的,结果是不同的。
Photoshop 对于大多数颜色来说要暗得多。

有人知道如何复制 Photoshop 的 LAB 转换吗?
任何人都可以发现这段代码中的问题吗?

谢谢。


最新答案(现在知道是错误的,等待正确答案)

Photoshop 是一个非常古老且混乱的软件。没有明确的文档说明当您执行从一种模式到另一种模式的转换时,为什么像素值会发生这样或那样的情况。

出现问题的原因是,当您在 Adob​​e Photoshop 中将选定的 L* 通道转换为灰度时,伽玛发生了变化。本质上,转换使用 1.74 的伽玛值进行单通道到灰度转换。不要问我为什么,我猜这与旧激光打印机有关(?)。

无论如何,这是我发现的最好的方法:

打开文件,将其转为 LAB 模式,仅选择 L 通道

然后前往:

编辑 > 转换为配置文件

您将选择“自定义伽玛”并输入值2.0(不要问我为什么2.0效果更好,我不知道Adobe的软件制造商在想什么......) 此操作会将您的图片变成只有一个通道的灰度图片

然后你可以将其转换为RGB模式。

如果将结果与您的结果进行比较,您将看到高达 4 点的差异 - 全部位于最暗的区域。

我怀疑这是因为伽玛曲线应用程序不适用于暗值中的 LAB 模式(参见,如您所知,所有低于 0.008856 的 XYZ 值在 LAB 中都是线性的)

结论:

据我所知,Adobe Photoshop中没有正确的实现方法来将L通道从LAB模式提取到灰度模式!

之前的回答

这是我用自己的方法得到的结果:

看起来和Adobe Photoshop 的结果一模一样。

我不确定您这边出了什么问题,因为您描述的步骤与我遵循的步骤完全相同,并且我会建议您遵循。我没有 Matlab,所以我使用了 python:

import cv2, Syn

# your file
fn = "EASA2.png"

#reading the file
im = cv2.imread(fn,-1)

#openCV works in BGR, i'm switching to RGB
im = im[:,:,::-1]

#conversion to XYZ
XYZ = Syn.sRGB2XYZ(im)

#white points D65 and D50
WP_D65 = Syn.Yxy2XYZ((100,0.31271, 0.32902))
WP_D50 = Syn.Yxy2XYZ((100,0.34567, 0.35850))

#bradford
XYZ2 = Syn.bradford_adaptation(XYZ, WP_D65, WP_D50) 

#conversion to L*a*b*
LAB = Syn.XYZ2Lab(XYZ2, WP_D50)

#picking the L channel only
L = LAB[:,:,0] /100. * 255.

#image output
cv2.imwrite("result.png", L)

Syn 库是我自己的东西,以下是函数(抱歉搞得一团糟):

def sRGB2XYZ(sRGB):

    sRGB = np.array(sRGB)
    aShape = np.array([1,1,1]).shape
    anotherShape = np.array([[1,1,1],[1,1,1]]).shape
    origShape = sRGB.shape

    if sRGB.shape == aShape:
        sRGB = np.reshape(sRGB, (1,1,3))

    elif len(sRGB.shape) == len(anotherShape):
        h,d = sRGB.shape
        sRGB = np.reshape(sRGB, (1,h,d))

    w,h,d = sRGB.shape

    sRGB = np.reshape(sRGB, (w*h,d)).astype("float") / 255.

    m1 = sRGB[:,0] > 0.04045
    m1b = sRGB[:,0] <= 0.04045
    m2 = sRGB[:,1] > 0.04045
    m2b = sRGB[:,1] <= 0.04045
    m3 = sRGB[:,2] > 0.04045
    m3b = sRGB[:,2] <= 0.04045

    sRGB[:,0][m1] = ((sRGB[:,0][m1] + 0.055 ) / 1.055 ) ** 2.4
    sRGB[:,0][m1b] = sRGB[:,0][m1b] / 12.92

    sRGB[:,1][m2] = ((sRGB[:,1][m2] + 0.055 ) / 1.055 ) ** 2.4
    sRGB[:,1][m2b] = sRGB[:,1][m2b] / 12.92

    sRGB[:,2][m3] = ((sRGB[:,2][m3] + 0.055 ) / 1.055 ) ** 2.4
    sRGB[:,2][m3b] = sRGB[:,2][m3b] / 12.92

    sRGB *= 100. 

    X = sRGB[:,0] * 0.4124 + sRGB[:,1] * 0.3576 + sRGB[:,2] * 0.1805
    Y = sRGB[:,0] * 0.2126 + sRGB[:,1] * 0.7152 + sRGB[:,2] * 0.0722
    Z = sRGB[:,0] * 0.0193 + sRGB[:,1] * 0.1192 + sRGB[:,2] * 0.9505

    XYZ = np.zeros_like(sRGB)

    XYZ[:,0] = X
    XYZ[:,1] = Y
    XYZ[:,2] = Z

    XYZ = np.reshape(XYZ, origShape)

    return XYZ

def Yxy2XYZ(Yxy):

    Yxy = np.array(Yxy)
    aShape = np.array([1,1,1]).shape
    anotherShape = np.array([[1,1,1],[1,1,1]]).shape
    origShape = Yxy.shape

    if Yxy.shape == aShape:
        Yxy = np.reshape(Yxy, (1,1,3))

    elif len(Yxy.shape) == len(anotherShape):
        h,d = Yxy.shape
        Yxy = np.reshape(Yxy, (1,h,d))

    w,h,d = Yxy.shape

    Yxy = np.reshape(Yxy, (w*h,d)).astype("float")

    XYZ = np.zeros_like(Yxy)

    XYZ[:,0] = Yxy[:,1] * ( Yxy[:,0] / Yxy[:,2] )
    XYZ[:,1] = Yxy[:,0]
    XYZ[:,2] = ( 1 - Yxy[:,1] - Yxy[:,2] ) * ( Yxy[:,0] / Yxy[:,2] )

    return np.reshape(XYZ, origShape)

def bradford_adaptation(XYZ, Neutral_source, Neutral_destination):
    """should be checked if it works properly, but it seems OK"""

    XYZ = np.array(XYZ)
    ashape = np.array([1,1,1]).shape
    siVal = False

    if XYZ.shape == ashape:


        XYZ = np.reshape(XYZ, (1,1,3))
        siVal = True


    bradford = np.array(((0.8951000, 0.2664000, -0.1614000),
                          (-0.750200, 1.7135000,  0.0367000),
                          (0.0389000, -0.068500,  1.0296000)))

    inv_bradford = np.array(((0.9869929, -0.1470543, 0.1599627),
                              (0.4323053,  0.5183603, 0.0492912),
                              (-.0085287,  0.0400428, 0.9684867)))

    Xs,Ys,Zs = Neutral_source
    s = np.array(((Xs),
                   (Ys),
                   (Zs)))

    Xd,Yd,Zd = Neutral_destination
    d = np.array(((Xd),
                   (Yd),
                   (Zd)))


    source = np.dot(bradford, s)
    Us,Vs,Ws = source[0], source[1], source[2]

    destination = np.dot(bradford, d)
    Ud,Vd,Wd = destination[0], destination[1], destination[2]

    transformation = np.array(((Ud/Us, 0, 0),
                                (0, Vd/Vs, 0),
                                (0, 0, Wd/Ws)))

    M = np.mat(inv_bradford)*np.mat(transformation)*np.mat(bradford)

    w,h,d = XYZ.shape
    result = np.dot(M,np.rot90(np.reshape(XYZ, (w*h,d)),-1))
    result = np.rot90(result, 1)
    result = np.reshape(np.array(result), (w,h,d))

    if siVal == False:
        return result
    else:
        return result[0,0]

def XYZ2Lab(XYZ, neutral):
    """transforms XYZ to CIE Lab
    Neutral should be normalized to Y = 100"""

    XYZ = np.array(XYZ)
    aShape = np.array([1,1,1]).shape
    anotherShape = np.array([[1,1,1],[1,1,1]]).shape
    origShape = XYZ.shape

    if XYZ.shape == aShape:
        XYZ = np.reshape(XYZ, (1,1,3))

    elif len(XYZ.shape) == len(anotherShape):
        h,d = XYZ.shape
        XYZ = np.reshape(XYZ, (1,h,d))

    N_x, N_y, N_z = neutral
    w,h,d = XYZ.shape

    XYZ = np.reshape(XYZ, (w*h,d)).astype("float")

    XYZ[:,0] = XYZ[:,0]/N_x
    XYZ[:,1] = XYZ[:,1]/N_y
    XYZ[:,2] = XYZ[:,2]/N_z

    m1 = XYZ[:,0] > 0.008856
    m1b = XYZ[:,0] <= 0.008856
    m2 = XYZ[:,1] > 0.008856 
    m2b = XYZ[:,1] <= 0.008856
    m3 = XYZ[:,2] > 0.008856
    m3b = XYZ[:,2] <= 0.008856

    XYZ[:,0][m1] = XYZ[:,0][XYZ[:,0] > 0.008856] ** (1/3.0)
    XYZ[:,0][m1b] = ( 7.787 * XYZ[:,0][m1b] ) + ( 16 / 116.0 )

    XYZ[:,1][m2] = XYZ[:,1][XYZ[:,1] > 0.008856] ** (1/3.0)
    XYZ[:,1][m2b] = ( 7.787 * XYZ[:,1][m2b] ) + ( 16 / 116.0 )

    XYZ[:,2][m3] = XYZ[:,2][XYZ[:,2] > 0.008856] ** (1/3.0)
    XYZ[:,2][m3b] = ( 7.787 * XYZ[:,2][m3b] ) + ( 16 / 116.0 )

    Lab = np.zeros_like(XYZ)

    Lab[:,0] = (116. * XYZ[:,1] ) - 16.
    Lab[:,1] = 500. * ( XYZ[:,0] - XYZ[:,1] )
    Lab[:,2] = 200. * ( XYZ[:,1] - XYZ[:,2] )

    return np.reshape(Lab, origShape)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

复制 Photoshop sRGB 到 LAB 转换 的相关文章

  • 如何使ggplot2图形与R中的黑白打印(复印友好)兼容?

    有没有办法转换ggplot2绘制成黑白版本而不重写大部分代码 以便黑白版本保持可读性 例如 要替换scale fill gradient with scale fill grey 或者自动进行适合复印的转换 如网站http colorbre
  • matlab矩阵中求子矩阵的通用方法

    我正在寻找一种 好 方法来在更大的矩阵 任意维数 中找到矩阵 模式 Example total rand 3 4 5 sub total 2 3 1 3 3 4 现在我希望这样的事情发生 loc matrixFind total sub 在
  • 如何获取活动对象 MATLAB GUI 的句柄

    我正在尝试使用 MATLAB GUI 创建日历 我有两个Edit Text对象 edittext1 and edittext2 我想做这个 我把光标放在edittext1然后在日历中选择日期 它会进入文本字段edittext1 同样对于ed
  • 在 C/C++ 中调用 MATLAB API

    我刚刚从某处听说 对于数值计算 MATLAB 确实提供了一些用户友好的 API 如果你在 C C 代码中调用这些 API 你可以显着加快计算速度 但我在MATLAB文档中没有找到这样的信息 例如http www mathworks com
  • R 编程:创建堆叠条形图,每个堆叠条形具有可变颜色

    我正在尝试创建一个堆叠条形图 每个堆叠条形图中的颜色可变 也就是说 一个条形图在红色上方显示蓝色 下一个条形图在紫色上方显示红色 等等 我还想保留堆叠图表的功能 十分感谢大家 Adam 下图 使用上面的代码创建 显示了主要汽车制造商生产的汽
  • 在 MATLAB 中用两个值替换向量值

    我必须创建一个以向量作为输入的函数v和三个标量a b and c 该函数替换了的每个元素v等于a有一个二元素数组 b c 例如 给定v 1 2 3 4 and a 2 b 5 c 5 输出将是 out 1 5 5 3 4 我的第一次尝试是尝
  • CSS 文本装饰:反向

    我很惊讶 CSS 中没有 text decoration reverse 因为使用 JavaScript 来实现似乎非常尴尬 IE 将元素的前景色和背景色分别设置为父元素的背景色和前景色 我注意到了 JavaScript 技术here ht
  • 改变 RGB 颜色的色调

    我正在尝试编写一个函数来改变 RGB 颜色的色调 具体来说 我在 iOS 应用程序中使用它 但数学是通用的 下图显示了 R G 和 B 值如何随色调变化 看起来 编写一个函数来改变色调似乎应该是一个相对简单的事情 而不需要对不同的颜色格式进
  • 通过傅里叶空间填充进行插值

    我最近尝试在 matlab 上实现一个在傅立叶域中使用零填充的插值方法的简单示例 但我无法正常工作 我总是有一个小的频移 在傅里叶空间中几乎不可见 但它在时空上产生了巨大的误差 由于傅里叶空间中的零填充似乎是一种常见 且快速 的插值方法 因
  • 同时重新排序和旋转图像的高效方法

    为了快速加载 jpeg 我为turbojpeg 实现了一个 mex wrapper 以有效地将 大 jpeg 读入 MATLAB 对于 4000x3000px 的图像 实际解码只需要大约 120 毫秒 而不是 5 毫秒 然而 像素顺序是 R
  • CUDA、NPP 滤波器

    CUDA NPP 库支持使用 nppiFilter 8u C1R 命令过滤图像 但不断出现错误 我可以毫无问题地启动并运行 boxFilterNPP 示例代码 eStatusNPP nppiFilterBox 8u C1R oDeviceS
  • 对图像使用 Pixellib 自定义训练时出现 input_image 元形状错误

    我正在使用 Pixellib 来训练自定义图像实例分割 我创建了一个数据集 可以在下面的链接中看到 数据集 https drive google com drive folders 1MjpDNZtzGRNxEtCDcTmrjUuB1ics
  • 如何计算图像中的 RGB 或 HSV 通道组合?

    我使用 python opencv 加载形状为 30 100 3 的图像 现在想要按颜色计算所有颜色的频率 我不是指单个通道 而是指通道组合 含义 3 个频道列表 例如 255 0 0 表示红色 255 255 0 表示黄色 100 100
  • 频域和空间域的汉明滤波器

    我想通过在 MATLAB 中应用汉明滤波器来消除一维信号中的吉布斯伪影 我所拥有的是k1这是频域中的信号 我可以通过应用 DFT 来获取时域信号k1 s1 ifft ifftshift k1 该信号具有吉布斯伪影 现在 我想通过 A 乘以汉
  • 为什么 MATLAB 本机函数 cov(协方差矩阵计算)使用与我预期不同的除数?

    给定一个 M 维和 N 个样本的数据矩阵数据 例如 data randn N M 我可以计算协方差矩阵 data mu data ones N 1 mean data cov matrix data mu data mu N 如果我使用原生
  • 非模态 questdlg.m 提示

    我的代码绘制了一个图 然后提示用户是否想使用不同的参数绘制另一个图 问题是 当 questdlg m 打开时 用户无法查看绘图的详细信息 这是代码 while strcmp Cont Yes 1 Some code modifying da
  • 如何加载具有可变文件名的 .mat 文件?

    select all mat files oar dir oar mat n oar name loop through files for l 1 length oar load pat oar l lt this is the mat
  • 如何将任意颜色的色度键滤镜应用到实时摄像头源ios?

    基本上我想将色度键滤镜应用到 ios 实时摄像头源 但我希望用户选择将被另一种颜色替换的颜色 我找到了一些使用绿屏的示例 但我不知道如何动态替换颜色而不仅仅是绿色 知道如何以最佳性能实现这一目标吗 您之前曾询问过我的情况GPUImage h
  • 在Matlab中选择图像上的像素时,索引指的是什么?

    当在Matlab中查看图像的单个像素时 该索引指的是什么 X Y 指的是像素的坐标 RGB 指的是颜色 但是关于索引是什么有什么想法吗 为了澄清一下 当我在 Matlab 中查看图形并使用数据光标选择一个点时 显示的三行是 X Y 指数 R
  • 如何获取MATLAB句柄对象的ID?

    当我尝试使用时出现问题MATLAB 句柄对象 http www mathworks com help techdoc ref handle html作为关键值MATLAB 容器 Map http www mathworks com help

随机推荐