《Color Transfer between Images》提出了一个非常经典的色彩迁移算法,用来将目标图像的色彩空间迁移到原图像上。
Python的实现代码见:https://github.com/ReBoRn8888/colorTransfer-python
出发点
- RGB三通道具有很强的相关性,改一个通道就得跟着改另外两个通道,否则图像会失真。
- 在RGB空间改变色彩有困难,因此考虑寻找一个各通道互不相关的色彩空间,lαβ空间就是个不错的选择。
- 在lαβ空间下,改变任何一个通道都不会影响其他通道,从而避免了像改变RGB通道时会导致的失真情况。
- l:亮度,α:黄蓝通道,β:红绿通道。
算法流程
将RGB空间转换为lαβ空间需要经过一个中间转换过程,即LMS空间,算法的整体流程如下:
RGB⇒LMS⇒log10(LMS)⇒lαβ⇒ 在 lαβ 空间下进行色彩变换 ⇒LMS⇒10LMS⇒RGB
1、RGB ⇒ LMS
通过以下矩阵运算将RGB色彩空间转换为LMS色彩空间
1 2 3 4 5 6 7 8
| def RGB2LMS(RGB): RGB2LMSMatrix = np.array([[0.3811, 0.5783, 0.0402], [0.1967, 0.7244, 0.0782], [0.0241, 0.1288, 0.8444]]).astype('float32') L = (RGB2LMSMatrix[0]*RGB).sum(2) M = (RGB2LMSMatrix[1]*RGB).sum(2) S = (RGB2LMSMatrix[2]*RGB).sum(2) LMS = cv2.merge([L, M, S]) LMS[np.where(LMS == 0)] = 1 return LMS
|
2、对LMS空间取以10为底的对数
3、LMS ⇒ lαβ
通过以下矩阵运算将LMS色彩空间转换为lαβ色彩空间
1 2 3 4 5 6 7 8 9
| def LMS2lab(LMS): a = [[1/np.sqrt(3), 0, 0], [0, 1/np.sqrt(6), 0], [0, 0, 1/np.sqrt(2)]] b = [[1, 1, 1], [1, 1, -2], [1, -1, 0]] c = np.matmul(a, b) ll = (c[0]*LMS).sum(2) aa = (c[1]*LMS).sum(2) bb = (c[2]*LMS).sum(2) lab = cv2.merge([ll, aa, bb]) return lab
|
4、在lαβ空间下进行色彩变换
- 求出原图像src在lαβ空间下三通道各自的均值srcMean和标准差srcStd
- 求出目标图像tar在lαβ空间下三通道各自的均值tarMean和标准差tarStd
- 将原图像减去它本身的均值,再除以它本身的标准差:res=srcStdsrc−srcMean
- 乘以目标图像的标准差,再加上目标图像的均值即可:res=res∗tarStd+tarMean
1 2 3 4 5 6 7 8 9 10 11
| def transfer(src, tar): srcMean = np.mean(src, axis=(0, 1)) srcStd = np.std(src, axis=(0, 1)) tarMean = np.mean(tar, axis=(0, 1)) tarStd = np.std(tar, axis=(0, 1))
res = src - srcMean res /= srcStd res *= tarStd res += tarMean return res
|
5、将经过色彩变换后的图像转回到LMS空间
通过以下矩阵运算将 lαβ 色彩空间转换为 LMS 色彩空间
1 2 3 4 5 6 7 8 9
| def lab2LMS(lab): a = [[1, 1, 1], [1, 1, -1], [1, -2, 0]] b = [[1/np.sqrt(3), 0, 0], [0, 1/np.sqrt(6), 0], [0, 0, 1/np.sqrt(2)]] c = np.matmul(a, b) L = (c[0]*lab).sum(2) M = (c[1]*lab).sum(2) S = (c[2]*lab).sum(2) LMS = cv2.merge([L, M, S]) return LMS
|
6、对LMS空间计算10的指数(即还原第二步的操作)
7、LMS ⇒ RGB
通过以下矩阵运算将 LMS 色彩空间转换为 RGB 色彩空间
1 2 3 4 5 6 7 8
| def LMS2RGB(LMS): LMS2RGBMatrix = np.array([[4.4679, -3.5873, 0.1193], [-1.2186, 2.3809, -0.1624], [0.0497, -0.2439, 1.2045]]).astype('float32') R = (LMS2RGBMatrix[0]*LMS).sum(2) G = (LMS2RGBMatrix[1]*LMS).sum(2) B = (LMS2RGBMatrix[2]*LMS).sum(2) RGB = cv2.merge([R, G, B]) RGB = np.clip(RGB, 0, 1) return RGB
|
算法结果演示