A Way to Generate A Colormap in MATLAB

Nov. 19, 2022

前两天看到了一个很漂亮、很有启发性的关于使用MATLAB生成色卡的工作。该工作来自slandarer,实现原理和代码都记录在他的CSDN博客中1

提取图片颜色及生成色卡的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
clc, clear, close all

% Read image
pic = imread('test.jpg');

% The number of colors in colormap
colorNum = 8;

Rchannel = pic(:, :, 1);
Gchannel = pic(:, :, 2);
Bchannel = pic(:, :, 3);
RGBarray = double([Rchannel(:), Gchannel(:), Bchannel(:)]);

% k-means cluster
[idx, Centers] = kmeans(RGBarray, colorNum, ...
    'Distance', 'sqeuclidean', 'MaxIter', 1000);
Centers = round(Centers);

figure("Units", "pixels", "Position", [492.33, 324.33, 1472, 674])
tiledlayout(1, 2)

nexttile
imshow(pic)

nexttile
view(3)
hold(gca, "on")
grid(gca, "on")
box(gca, "on")
for i = 1:colorNum
    scatter3(RGBarray(idx==i, 1), RGBarray(idx==i, 2), RGBarray(idx==i, 3), ...
        'filled', 'CData', Centers(i,:)./255);
    legends{i}=[num2str(Centers(i,1)), ' ', num2str(Centers(i,2)), ' ', num2str(Centers(i,3))];
end
legend(legends, 'Color', [0.9412    0.9412    0.9412], ...
    'FontName','Cambria', 'LineWidth', 0.8, 'FontSize', 11)
ax = gca;
ax.GridLineStyle = '--';
ax.LineWidth = 1.2;

ax.XLabel.String = 'R channel';
ax.XLabel.FontSize = 13;
ax.XLabel.FontName = 'Cambria';

ax.YLabel.String = 'G channel';
ax.YLabel.FontSize = 13;
ax.YLabel.FontName = 'Cambria';

ax.ZLabel.String = 'B channel';
ax.ZLabel.FontSize = 13;
ax.ZLabel.FontName = 'Cambria';

exportgraphics(gcf, "pic.png")

聚类中心对应的色卡,可以修改变量colorNum以改变色卡中颜色的数量。

图像中的三维散点就是图片中对应的像素点的值:

但是它们的颜色都是一样的,都是聚类中心点的颜色,从下面的代码片段可以看出这一点:

1
2
3
4
5
...
for i = 1:colorNum
 scatter3(RGBarray(idx==i, 1), RGBarray(idx==i, 2), RGBarray(idx==i, 3), ...
     'filled', 'CData', Centers(i,:)./255);
...

另一方面,我们可以将其修改为显示像素本身的颜色:

1
2
3
4
5
6
...
for i = 1:colorNum
 scatter3(RGBarray(idx==i, 1), RGBarray(idx==i, 2), RGBarray(idx==i, 3), ...
     'filled', 'CData', RGBarray(idx==i, :)./255);
...
end

pic_2

然而,这种生成色卡方式仍然具有一定的局限性,原因在于代码中使用了机器学习算法k-means寻找中心点,而机器算法是依赖于数据量的。例如,我简单画了一个图像,图像中只有纯白色、纯黑色、纯红色、纯蓝色、纯绿色:

test1

按照具有5个聚类中心点提取出的结果如下:

pic1

可以看到,色卡中黑色的RGB值没有误差,这是因为这张图片中黑色的像素点是最多的;而其他四种颜色的RGB值与理论值都有或多或少的误差。

但是,这样的RGB实际值与理论值之间的误差也可能是图片本身所造成的,并且很有可能是这个原因。

我们在使用绘图软件作图时会指定一些颜色,不同的绘图软件会采用不同的算法渲染图像并导出(考虑到分辨率设置 etc.)。我绘制上述图像时就仅仅使用了五种确定的颜色,因此理论上所有的像素点在RGB三维空间中应该只重合为5个点,但实际上我们可以看到它们并没有那么集中——使用上述展示单个像素点颜色的代码进行可视化,可以更明显地观察到这一点:

pic1_2

虽然有一些的局限性,但是瑕不掩瑜,这仍然是一个很好的色卡生成方法,上述的误差是可以接受的。只要我们看着右边的图像感觉到和谐美观,那么我们就可以将其作为一个色卡,可以不必理会人眼无法识别的差异。但是,假如我们设计一种色卡并且想要从科学或者量化的角度解释它为什么是美观的,那就是另外一回事了。


References