在 MATLAB 中导出矢量图形期间位图渲染部分绘图
Bitmap-render part of plot during vector-graphics export in MATLAB
我有一个相当复杂的函数,我使用 fsurf
和相当高的 MeshDensity
绘制(我不能比默认值低很多,即 35)。导出此图 (saveas(gcf, 'file.pdf', 'pdf');
) 会生成一个质量非常好的 20 MB 左右的 pdf 文件,但渲染速度非常慢。我想减小文件大小,最重要的是,减小此 pdf 文件的复杂性,而不将整个图(我指的是整个 MATLAB 图)导出为位图。我该怎么做?
完美的答案将解释如何将曲面图(我的意思是,只是白色背景上的彩色函数曲面)转换为位图,同时保持轴和标签的矢量化性质。
更新:这是一个这样的情节的例子。
这是我的 BitmapRender 函数,它对图形的一部分进行位图渲染:
%% Test Code
clc;clf;
Objects = surf(-4-2*peaks);
hold('on');
Objects(2 : 50) = plot(peaks);
Objects(51) = imagesc([20 40], [0, 5], magic(100));
hold('off');
ylim([0 10]);
zlim([-10 15]);
Objects(1).Parent.GridLineStyle = 'none';
view(45, 45);
set(gcf, 'Color', 'white');
rotate3d on
saveas(gcf, 'pre.pdf');
BitmapRender(gca, Objects(2 : 3 : end));
% BitmapRender(gca, Objects(2 : 3 : end), [0.25 0.25 0.5 0.5], false);
saveas(gcf, 'post.pdf');
函数本身非常简单,除了(重新)处理可见性,因为按下 space 键(在旋转、缩放等之后)会重新渲染图形。
function BitmapRender(Axes, KeepObjects, RelativePosition, Draft, Key)
if nargin < 2
KeepObjects = [];
end
if nargin < 3
RelativePosition = [0 0 1 1];
end
if nargin < 4
Draft = false;
end
if nargin < 5
Key = '';
end
Figure = Axes.Parent;
FigureInnerWH = Figure.InnerPosition([3 4 3 4]);
PixelPosition = round(RelativePosition .* FigureInnerWH);
if isempty(Key)
OverlayAxes = axes(Figure, 'Units', 'Normalized', 'Position', PixelPosition ./ FigureInnerWH);
if Draft
OverlayAxes.Box = 'on';
OverlayAxes.Color = 'none';
OverlayAxes.XTick = [];
OverlayAxes.YTick = [];
OverlayAxes.HitTest = 'off';
else
uistack(OverlayAxes, 'bottom');
OverlayAxes.Visible = 'off';
end
setappdata(Figure, 'BitmapRenderOriginalVisibility', get(Axes.Children, 'Visible'));
Axes.CLimMode = 'manual';
Axes.XLimMode = 'manual';
Axes.YLimMode = 'manual';
Axes.ZLimMode = 'manual';
hManager = uigetmodemanager(Figure);
[hManager.WindowListenerHandles.Enabled] = deal(false);
set(Figure, 'KeyPressFcn', @(f, e) BitmapRender(gca, KeepObjects, RelativePosition, Draft, e.Key));
elseif strcmpi(Key, 'space')
OverlayAxes = findobj(Figure, 'Tag', 'BitmapRenderOverlayAxes');
delete(get(OverlayAxes, 'Children'));
OriginalVisibility = getappdata(Figure, 'BitmapRenderOriginalVisibility');
[Axes.Children.Visible] = deal(OriginalVisibility{:});
else
return;
end
if Draft
return;
end
Axes.Visible = 'off';
KeepObjectsVisibility = get(KeepObjects, 'Visible');
[KeepObjects.Visible] = deal('off');
drawnow;
Frame = getframe(Figure, PixelPosition);
[Axes.Children.Visible] = deal('off');
Axes.Visible = 'on';
Axes.Color = 'none';
if numel(KeepObjects) == 1
KeepObjects.Visible = KeepObjectsVisibility;
else
[KeepObjects.Visible] = deal(KeepObjectsVisibility{:});
end
Image = imagesc(OverlayAxes, Frame.cdata);
uistack(Image, 'bottom');
OverlayAxes.Tag = 'BitmapRenderOverlayAxes';
OverlayAxes.Visible = 'off';
end
显然,该解决方案在屏幕像素方面是像素完美的。两个 pdf 文件(pre
和 post
)如下所示。请注意,表面、图像和一些绘图线是位图渲染的,但其他一些绘图线以及轴和标签仍然是矢量化的。
我有一个相当复杂的函数,我使用 fsurf
和相当高的 MeshDensity
绘制(我不能比默认值低很多,即 35)。导出此图 (saveas(gcf, 'file.pdf', 'pdf');
) 会生成一个质量非常好的 20 MB 左右的 pdf 文件,但渲染速度非常慢。我想减小文件大小,最重要的是,减小此 pdf 文件的复杂性,而不将整个图(我指的是整个 MATLAB 图)导出为位图。我该怎么做?
完美的答案将解释如何将曲面图(我的意思是,只是白色背景上的彩色函数曲面)转换为位图,同时保持轴和标签的矢量化性质。
更新:这是一个这样的情节的例子。
这是我的 BitmapRender 函数,它对图形的一部分进行位图渲染:
%% Test Code
clc;clf;
Objects = surf(-4-2*peaks);
hold('on');
Objects(2 : 50) = plot(peaks);
Objects(51) = imagesc([20 40], [0, 5], magic(100));
hold('off');
ylim([0 10]);
zlim([-10 15]);
Objects(1).Parent.GridLineStyle = 'none';
view(45, 45);
set(gcf, 'Color', 'white');
rotate3d on
saveas(gcf, 'pre.pdf');
BitmapRender(gca, Objects(2 : 3 : end));
% BitmapRender(gca, Objects(2 : 3 : end), [0.25 0.25 0.5 0.5], false);
saveas(gcf, 'post.pdf');
函数本身非常简单,除了(重新)处理可见性,因为按下 space 键(在旋转、缩放等之后)会重新渲染图形。
function BitmapRender(Axes, KeepObjects, RelativePosition, Draft, Key)
if nargin < 2
KeepObjects = [];
end
if nargin < 3
RelativePosition = [0 0 1 1];
end
if nargin < 4
Draft = false;
end
if nargin < 5
Key = '';
end
Figure = Axes.Parent;
FigureInnerWH = Figure.InnerPosition([3 4 3 4]);
PixelPosition = round(RelativePosition .* FigureInnerWH);
if isempty(Key)
OverlayAxes = axes(Figure, 'Units', 'Normalized', 'Position', PixelPosition ./ FigureInnerWH);
if Draft
OverlayAxes.Box = 'on';
OverlayAxes.Color = 'none';
OverlayAxes.XTick = [];
OverlayAxes.YTick = [];
OverlayAxes.HitTest = 'off';
else
uistack(OverlayAxes, 'bottom');
OverlayAxes.Visible = 'off';
end
setappdata(Figure, 'BitmapRenderOriginalVisibility', get(Axes.Children, 'Visible'));
Axes.CLimMode = 'manual';
Axes.XLimMode = 'manual';
Axes.YLimMode = 'manual';
Axes.ZLimMode = 'manual';
hManager = uigetmodemanager(Figure);
[hManager.WindowListenerHandles.Enabled] = deal(false);
set(Figure, 'KeyPressFcn', @(f, e) BitmapRender(gca, KeepObjects, RelativePosition, Draft, e.Key));
elseif strcmpi(Key, 'space')
OverlayAxes = findobj(Figure, 'Tag', 'BitmapRenderOverlayAxes');
delete(get(OverlayAxes, 'Children'));
OriginalVisibility = getappdata(Figure, 'BitmapRenderOriginalVisibility');
[Axes.Children.Visible] = deal(OriginalVisibility{:});
else
return;
end
if Draft
return;
end
Axes.Visible = 'off';
KeepObjectsVisibility = get(KeepObjects, 'Visible');
[KeepObjects.Visible] = deal('off');
drawnow;
Frame = getframe(Figure, PixelPosition);
[Axes.Children.Visible] = deal('off');
Axes.Visible = 'on';
Axes.Color = 'none';
if numel(KeepObjects) == 1
KeepObjects.Visible = KeepObjectsVisibility;
else
[KeepObjects.Visible] = deal(KeepObjectsVisibility{:});
end
Image = imagesc(OverlayAxes, Frame.cdata);
uistack(Image, 'bottom');
OverlayAxes.Tag = 'BitmapRenderOverlayAxes';
OverlayAxes.Visible = 'off';
end
显然,该解决方案在屏幕像素方面是像素完美的。两个 pdf 文件(pre
和 post
)如下所示。请注意,表面、图像和一些绘图线是位图渲染的,但其他一些绘图线以及轴和标签仍然是矢量化的。