|
a |
|
b/vol3d.m |
|
|
1 |
function [model] = vol3d(varargin) |
|
|
2 |
|
|
|
3 |
%H = VOL3D Volume render 3-D data. |
|
|
4 |
% VOL3D uses the orthogonal plane 2-D texture mapping technique for |
|
|
5 |
% volume rending 3-D data in OpenGL. Use the 'texture' option to fine |
|
|
6 |
% tune the texture mapping technique. This function is best used with |
|
|
7 |
% fast OpenGL hardware. |
|
|
8 |
% |
|
|
9 |
% vol3d Provide a demo of functionality. |
|
|
10 |
% |
|
|
11 |
% H = vol3d('CData',data) Create volume render object from input |
|
|
12 |
% 3-D data. Use interp3 on data to increase volume |
|
|
13 |
% rendering resolution. Returns a struct |
|
|
14 |
% encapsulating the pseudo-volume rendering object. |
|
|
15 |
% XxYxZ array represents scaled colormap indices. |
|
|
16 |
% XxYxZx3 array represents truecolor RGB values for |
|
|
17 |
% each voxel (along the 4th dimension). |
|
|
18 |
% |
|
|
19 |
% vol3d(...,'Alpha',alpha) XxYxZ array of alpha values for each voxel, in |
|
|
20 |
% range [0,1]. Default: data (interpreted as |
|
|
21 |
% scaled alphamap indices). |
|
|
22 |
% |
|
|
23 |
% vol3d(...,'Parent',axH) Specify parent axes. Default: gca. |
|
|
24 |
% |
|
|
25 |
% vol3d(...,'XData',x) 1x2 x-axis bounds. Default: [0 size(data, 2)]. |
|
|
26 |
% vol3d(...,'YData',y) 1x2 y-axis bounds. Default: [0 size(data, 1)]. |
|
|
27 |
% vol3d(...,'ZData',z) 1x2 z-axis bounds. Default: [0 size(data, 3)]. |
|
|
28 |
% |
|
|
29 |
% vol3d(...,'texture','2D') Only render texture planes parallel to nearest |
|
|
30 |
% orthogonal viewing plane. Requires doing |
|
|
31 |
% vol3d(h) to refresh if the view is rotated |
|
|
32 |
% (i.e. using cameratoolbar). |
|
|
33 |
% |
|
|
34 |
% vol3d(...,'texture','3D') Default. Render x,y,z texture planes |
|
|
35 |
% simultaneously. This avoids the need to |
|
|
36 |
% refresh the view but requires faster OpenGL |
|
|
37 |
% hardware peformance. |
|
|
38 |
% |
|
|
39 |
% vol3d(H) Refresh view. Updates rendering of texture planes |
|
|
40 |
% to reduce visual aliasing when using the 'texture'='2D' |
|
|
41 |
% option. |
|
|
42 |
% |
|
|
43 |
% NOTES |
|
|
44 |
% Use vol3dtool (from the original vol3d FEX submission) for editing the |
|
|
45 |
% colormap and alphamap. Adjusting these maps will allow you to explore |
|
|
46 |
% your 3-D volume data at various intensity levels. See documentation on |
|
|
47 |
% alphamap and colormap for more information. |
|
|
48 |
% |
|
|
49 |
% Use interp3 on input date to increase/decrease resolution of data |
|
|
50 |
% |
|
|
51 |
% Examples: |
|
|
52 |
% |
|
|
53 |
% % Visualizing fluid flow |
|
|
54 |
% v = flow(50); |
|
|
55 |
% h = vol3d('cdata',v,'texture','2D'); |
|
|
56 |
% view(3); |
|
|
57 |
% % Update view since 'texture' = '2D' |
|
|
58 |
% vol3d(h); |
|
|
59 |
% alphamap('rampdown'), alphamap('decrease'), alphamap('decrease') |
|
|
60 |
% |
|
|
61 |
% % Visualizing MRI data |
|
|
62 |
% load mri.mat |
|
|
63 |
% D = squeeze(D); |
|
|
64 |
% h = vol3d('cdata',D,'texture','3D'); |
|
|
65 |
% view(3); |
|
|
66 |
% axis tight; daspect([1 1 .4]) |
|
|
67 |
% alphamap('rampup'); |
|
|
68 |
% alphamap(.06 .* alphamap); |
|
|
69 |
% |
|
|
70 |
% See also alphamap, colormap, opengl, isosurface |
|
|
71 |
|
|
|
72 |
% Copyright Joe Conti, 2004 |
|
|
73 |
% Improvements by Oliver Woodford, 2008-2011, with permission of the |
|
|
74 |
% copyright holder. |
|
|
75 |
|
|
|
76 |
if nargin == 0 |
|
|
77 |
demo_vol3d; |
|
|
78 |
return |
|
|
79 |
end |
|
|
80 |
|
|
|
81 |
if isstruct(varargin{1}) |
|
|
82 |
model = varargin{1}; |
|
|
83 |
if length(varargin) > 1 |
|
|
84 |
varargin = {varargin{2:end}}; |
|
|
85 |
end |
|
|
86 |
else |
|
|
87 |
model = localGetDefaultModel; |
|
|
88 |
end |
|
|
89 |
|
|
|
90 |
if length(varargin)>1 |
|
|
91 |
for n = 1:2:length(varargin) |
|
|
92 |
switch(lower(varargin{n})) |
|
|
93 |
case 'cdata' |
|
|
94 |
model.cdata = varargin{n+1}; |
|
|
95 |
case 'parent' |
|
|
96 |
model.parent = varargin{n+1}; |
|
|
97 |
case 'texture' |
|
|
98 |
model.texture = varargin{n+1}; |
|
|
99 |
case 'alpha' |
|
|
100 |
model.alpha = varargin{n+1}; |
|
|
101 |
case 'xdata' |
|
|
102 |
model.xdata = varargin{n+1}([1 end]); |
|
|
103 |
case 'ydata' |
|
|
104 |
model.ydata = varargin{n+1}([1 end]); |
|
|
105 |
case 'zdata' |
|
|
106 |
model.zdata = varargin{n+1}([1 end]); |
|
|
107 |
end |
|
|
108 |
|
|
|
109 |
end |
|
|
110 |
end |
|
|
111 |
|
|
|
112 |
if isempty(model.parent) |
|
|
113 |
model.parent = gca; |
|
|
114 |
end |
|
|
115 |
|
|
|
116 |
[model] = local_draw(model); |
|
|
117 |
|
|
|
118 |
%------------------------------------------% |
|
|
119 |
function [model] = localGetDefaultModel |
|
|
120 |
|
|
|
121 |
model.cdata = []; |
|
|
122 |
model.alpha = []; |
|
|
123 |
model.xdata = []; |
|
|
124 |
model.ydata = []; |
|
|
125 |
model.zdata = []; |
|
|
126 |
model.parent = []; |
|
|
127 |
model.handles = []; |
|
|
128 |
model.texture = '3D'; |
|
|
129 |
tag = tempname; |
|
|
130 |
model.tag = ['vol3d_' tag(end-11:end)]; |
|
|
131 |
|
|
|
132 |
%------------------------------------------% |
|
|
133 |
function [model,ax] = local_draw(model) |
|
|
134 |
|
|
|
135 |
cdata = model.cdata; |
|
|
136 |
siz = size(cdata); |
|
|
137 |
|
|
|
138 |
% Define [x,y,z]data |
|
|
139 |
if isempty(model.xdata) |
|
|
140 |
model.xdata = [0 siz(2)]; |
|
|
141 |
end |
|
|
142 |
if isempty(model.ydata) |
|
|
143 |
model.ydata = [0 siz(1)]; |
|
|
144 |
end |
|
|
145 |
if isempty(model.zdata) |
|
|
146 |
model.zdata = [0 siz(3)]; |
|
|
147 |
end |
|
|
148 |
|
|
|
149 |
try |
|
|
150 |
delete(model.handles); |
|
|
151 |
catch |
|
|
152 |
end |
|
|
153 |
|
|
|
154 |
ax = model.parent; |
|
|
155 |
cam_dir = camtarget(ax) - campos(ax); |
|
|
156 |
[m,ind] = max(abs(cam_dir)); |
|
|
157 |
|
|
|
158 |
opts = {'Parent',ax,'cdatamapping',[],'alphadatamapping',[],'facecolor','texturemap','edgealpha',0,'facealpha','texturemap','tag',model.tag}; |
|
|
159 |
|
|
|
160 |
if ndims(cdata) > 3 |
|
|
161 |
opts{4} = 'direct'; |
|
|
162 |
else |
|
|
163 |
cdata = double(cdata); |
|
|
164 |
opts{4} = 'scaled'; |
|
|
165 |
end |
|
|
166 |
|
|
|
167 |
if isempty(model.alpha) |
|
|
168 |
alpha = cdata; |
|
|
169 |
if ndims(model.cdata) > 3 |
|
|
170 |
alpha = sqrt(sum(double(alpha).^2, 4)); |
|
|
171 |
alpha = alpha - min(alpha(:)); |
|
|
172 |
alpha = 1 - alpha / max(alpha(:)); |
|
|
173 |
end |
|
|
174 |
opts{6} = 'scaled'; |
|
|
175 |
else |
|
|
176 |
alpha = model.alpha; |
|
|
177 |
if ~isequal(siz(1:3), size(alpha)) |
|
|
178 |
error('Incorrect size of alphamatte'); |
|
|
179 |
end |
|
|
180 |
opts{6} = 'none'; |
|
|
181 |
end |
|
|
182 |
|
|
|
183 |
h = findobj(ax,'type','surface','tag',model.tag); |
|
|
184 |
for n = 1:length(h) |
|
|
185 |
try |
|
|
186 |
delete(h(n)); |
|
|
187 |
catch |
|
|
188 |
end |
|
|
189 |
end |
|
|
190 |
|
|
|
191 |
is3DTexture = strcmpi(model.texture,'3D'); |
|
|
192 |
handle_ind = 1; |
|
|
193 |
|
|
|
194 |
% Create z-slice |
|
|
195 |
if(ind==3 || is3DTexture ) |
|
|
196 |
x = [model.xdata(1), model.xdata(2); model.xdata(1), model.xdata(2)]; |
|
|
197 |
y = [model.ydata(1), model.ydata(1); model.ydata(2), model.ydata(2)]; |
|
|
198 |
z = [model.zdata(1), model.zdata(1); model.zdata(1), model.zdata(1)]; |
|
|
199 |
diff = model.zdata(2)-model.zdata(1); |
|
|
200 |
delta = diff/size(cdata,3); |
|
|
201 |
for n = 1:size(cdata,3) |
|
|
202 |
|
|
|
203 |
cslice = squeeze(cdata(:,:,n,:)); |
|
|
204 |
aslice = double(squeeze(alpha(:,:,n))); |
|
|
205 |
h(handle_ind) = surface(x,y,z,cslice,'alphadata',aslice,opts{:}); |
|
|
206 |
z = z + delta; |
|
|
207 |
handle_ind = handle_ind + 1; |
|
|
208 |
end |
|
|
209 |
|
|
|
210 |
end |
|
|
211 |
|
|
|
212 |
% Create x-slice |
|
|
213 |
if (ind==1 || is3DTexture ) |
|
|
214 |
x = [model.xdata(1), model.xdata(1); model.xdata(1), model.xdata(1)]; |
|
|
215 |
y = [model.ydata(1), model.ydata(1); model.ydata(2), model.ydata(2)]; |
|
|
216 |
z = [model.zdata(1), model.zdata(2); model.zdata(1), model.zdata(2)]; |
|
|
217 |
diff = model.xdata(2)-model.xdata(1); |
|
|
218 |
delta = diff/size(cdata,2); |
|
|
219 |
for n = 1:size(cdata,2) |
|
|
220 |
|
|
|
221 |
cslice = squeeze(cdata(:,n,:,:)); |
|
|
222 |
aslice = double(squeeze(alpha(:,n,:))); |
|
|
223 |
h(handle_ind) = surface(x,y,z,cslice,'alphadata',aslice,opts{:}); |
|
|
224 |
x = x + delta; |
|
|
225 |
handle_ind = handle_ind + 1; |
|
|
226 |
end |
|
|
227 |
end |
|
|
228 |
|
|
|
229 |
% Create y-slice |
|
|
230 |
if (ind==2 || is3DTexture) |
|
|
231 |
x = [model.xdata(1), model.xdata(1); model.xdata(2), model.xdata(2)]; |
|
|
232 |
y = [model.ydata(1), model.ydata(1); model.ydata(1), model.ydata(1)]; |
|
|
233 |
z = [model.zdata(1), model.zdata(2); model.zdata(1), model.zdata(2)]; |
|
|
234 |
diff = model.ydata(2)-model.ydata(1); |
|
|
235 |
delta = diff/size(cdata,1); |
|
|
236 |
for n = 1:size(cdata,1) |
|
|
237 |
|
|
|
238 |
cslice = squeeze(cdata(n,:,:,:)); |
|
|
239 |
aslice = double(squeeze(alpha(n,:,:))); |
|
|
240 |
h(handle_ind) = surface(x,y,z,cslice,'alphadata',aslice,opts{:}); |
|
|
241 |
y = y + delta; |
|
|
242 |
handle_ind = handle_ind + 1; |
|
|
243 |
end |
|
|
244 |
end |
|
|
245 |
|
|
|
246 |
model.handles = h; |
|
|
247 |
|
|
|
248 |
function demo_vol3d |
|
|
249 |
figure; |
|
|
250 |
load mri.mat |
|
|
251 |
vol3d('cdata', squeeze(D), 'xdata', [0 1], 'ydata', [0 1], 'zdata', [0 0.7]); |
|
|
252 |
colormap(bone(256)); |
|
|
253 |
alphamap([0 linspace(0.1, 0, 255)]); |
|
|
254 |
axis equal off |
|
|
255 |
set(gcf, 'color', 'w'); |
|
|
256 |
view(3); |