如何使表面颜色对比更强烈
在Matlab中,我正在尝试用以下代码在二维空间中绘制一个函数。
s=.05;
x=[-2:s:2+s];
y=[-1:s:3+s];
[X,Y]=meshgrid(x,y);
Z=(1.-X).^2 + 100.*(Y-X.*X).^2;
surf(X,Y,Z)
colormap jet
这是我绘制的图:
我希望能让表面的颜色对比更强,就像维基百科上展示的那样:

维基百科上的图是用Python代码绘制的:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.colors import LogNorm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = Axes3D(fig, azim = -128, elev = 43)
s = .05
X = np.arange(-2, 2.+s, s)
Y = np.arange(-1, 3.+s, s)
X, Y = np.meshgrid(X, Y)
Z = (1.-X)**2 + 100.*(Y-X*X)**2
ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1, norm = LogNorm(), cmap = cm.jet)
plt.xlabel("x")
plt.ylabel("y")
plt.show()
我的Matlab代码和维基百科的Python代码似乎都使用了“jet”这种颜色映射,但它们将高度值映射到颜色的方式却不同。所以我想知道在Matlab中如何才能得到类似的颜色效果?
谢谢!
2 个回答
直接来自于调色板帮助页面:
颜色文件夹里的文件可以生成多种调色板。每个文件都可以接受一个参数,来指定调色板的大小。例如,
colormap(hsv(128))
这个命令会创建一个有128种颜色的hsv调色板。如果你不指定大小,它会生成一个和当前调色板大小相同的调色板。
我用这个功能来改变颜色的范围,以更好地利用可用的色谱。
你可以通过以下方式实现类似的效果:
- 把表面对象的
'EdgeColor'
属性设置为'none'
,这样就可以去掉边缘的颜色。 - 修改表面的
'CData'
属性,让它模仿 Python 代码中的 对颜色数据的对数缩放。
下面是你可以修改的代码:
s = .05;
x = [-2:s:2+s];
y = [-1:s:3+s];
[X, Y] = meshgrid(x, y);
Z = (1.-X).^2 + 100.*(Y-X.*X).^2;
minZ = min(Z(:)); % Find minimum value of Z
maxZ = max(Z(:)); % Find maximum value of Z
C = minZ+(maxZ-minZ).*log(1+Z-minZ)./log(1+maxZ-minZ); % Create a log-scaled
% set of color data
surf(X, Y, Z, C, 'EdgeColor', 'none');
colormap jet
这是生成的图:
对数缩放是怎么工作的...
用来生成颜色数据 C
的对数缩放 Z
数据,使得红橙色范围的颜色在更多的表面点上使用,从而提高了这个特定表面的对比度。这个过程可以用一个简单的例子来说明:
x = 0:5:100; % Create a range of values from 0 to 100
plot(x, x, 'b-*'); % Plot the values as a straight line (y = x) in blue
hold on; % Add to the plot
plot(x, 100.*log(1+x)./log(101), 'r-*'); % Plot a log-scaled version of x in red
colorbar % Display the default jet color map, for comparison
最初的蓝色点在右侧的颜色条中均匀分布。当进行对数缩放时,这些点会向上移动到红色线附近。你会注意到,这样一来,蓝绿色范围的点密度减少,而红橙色范围的点密度增加。
如何提高整体对比度...
对于这里使用的特定表面,颜色数据的对数缩放有助于在表面的所有点上使用更广泛的颜色范围。因为在较低高度(即颜色索引)值的点很多,所以对数缩放会让这些低点分布得更开,从而在表面的低谷中使用更广泛的颜色。
不过,如果你想通过更好地利用颜色图的范围来改善任意表面的对比度,单纯的对数缩放可能不一定有效。一个更通用的解决方案是将表面的所有高度值按升序排列,然后将这些值映射到一个覆盖整个颜色图的线性范围。如果你对上面的表面这样做,结果会是:
C = Z;
[~, index] = sort(C(:));
C(index) = 1:numel(index);
h = surf(X, Y, Z, C, 'EdgeColor', 'none');
colormap jet
caxis([1 numel(index)]);
这样通常会比 C = Z
默认的表面着色效果更好。