hi Paullina,
sorry for my late reply to your iso2mesh ticket.
the iso2mesh mesh coordinates were adjusted so that the lower-bottom corner of the first voxel of a 3D volume is treated as [0,0,0].
the offset you observed was a result of using imagesc - which displays the center of the first voxel as [1,1] instead of [0.5,0.5] as in the iso2mesh mesh coordinates.
you can try these commands
>> [no,fc]=binsurface(box);
>> min(no)
ans =
1 1 1
>> max(no)
ans =
5 5 5
>> [no,el]=v2s(box,0.5,0.2);
extracting surfaces from a volume ...
region 1 centroid : 5.333333 5.666667 4.000000
processing threshold level 1...
Warning: You are meshing the surface with sub-pixel
size. If this is not your your intent, please check if you set
...
done.
Final number of points: 1622
surface mesh generation is complete
>> min(no)
ans =
0.9999 0.9999 0.9999
>> max(no)
ans =
5.0001 5.0001 5.0001
>>
[no,el,~,~]=vol2surf(box,1:1:size(box,1),1:1:size(box,2),1:1:size(box,3),1,1);
extracting surfaces from a volume ...
region 1 centroid : 5.333333 5.666667 4.000000
...
done.
Final number of points: 189
surface mesh generation is complete
>> min(no)
ans =
1.4998 1.4998 1.4998
>> max(no)
ans =
4.5002 4.5002 4.5002
you can see that in all 3 cases, the center of the output mesh is around [3,3,3], which is the center of a 6x6x6 cube of your input volume "box".
the reason that your vol2surf() created mesh shrinks by 0.5 voxel in each side is because you use a threshold of 1 instead of 0.5 - using 0.5, the interpolation between binary 0-1 value should split right at the middle.
regardless - this is rather an iso2mesh convention, and most iso2mesh functions have been somewhat adjusted to follow this assumption. This assumption not only is used when converting a volume to a mesh, but also the reverse - rasterizing a surface to a volume, as shown by the match of contour lines in this example
https://github.com/fangq/iso2mesh/blob/master/sample/demo_surf2vol_ex1.m
this is different from mcx's issrcfrom0 flag - mcx uses a
voxelated grid, has only two possible options - either
issrcfrom0=0 by treating the bottom-lower corner of the 1st voxel
as [1,1,1], or, when issrcfrom0=1 by setting that corner as
[0,0,0]. This setting is unrelated to iso2mesh mesh/volume
mapping. The first option was rather confusing, but we had
it in mcx from the beginning just to be compatible with the tMCimg
input file that David Boas developed.
over time, in many of my demo scripts, I have set issrcfrom0 to be 1. it is my intent to set it default to 1 instead of 0 in the future release to eliminate this confusion.
let me know if my comments above make sense to you, and if I did
not address any part of your questions.
Qianqian
--
You received this message because you are subscribed to the Google Groups "mcx-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mcx-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/mcx-users/AS8P192MB132096995D57EE74E791FAA3E92B2%40AS8P192MB1320.EURP192.PROD.OUTLOOK.COM.
hi Pauliina,
sorry that I did not read it carefully - see my comments below
Dear Dr. Fang,
Many thanks for your quick response and the very useful information on Iso2Mesh!
My questions 2–4 were actually more related to MCXLAB and matching the outputs with the MATLAB (image) origin. I try to repeat the questions here in a shorter form, if you could still provide some comments on them:
2) Do the photon exit positions assume that the extreme corner of voxel (1,1,1) is at (0,0,0), so you have to translate the positions by +[0.5,0.5,0.5] to present them with respect to the MATLAB origin (independent of cfg.issrcfrom0)? The originally attached figure shows "misplacement" in MATLAB image frame without this operation.
the photon exit positions are computed inside the GPU kernel - in the GPU, all positions assume the extreme corner of first voxel is [0,0,0].
also, there is no [0.5,0.5,0.5] - mcx either subtract 1 from srcpos/detpos, or subtract nothing. there is no half-voxel shift.
you can run `grep issrcfrom0 *` inside mcx/src, and you can see that the only place issrcfrom0 is used is at the handling of user input:
if issrcfrom0 is 0, srcpos.x/y/z and detpos.x/y/z subtract 1
from that point on, everything in the code assumes the origin is
[0,0,0], including everything in the GPU kernels (and outputs)
3) When giving the src/det positions to MCXLAB with "cfg.issrcfrom0=0", we assume that the extreme corner of voxel (1,1,1) is at (1,1,1), thus we have to translate these positions by [-0.5, -0.5, -0.5] to present them with respect to the MATALB origin?
if you set cfg.srcpos/cfg.detpos in matlab, and set cfg.issrcfrom0=0, all it does is to trigger the -1 offset as shown in the above links (by calling mcx_validatecfg() in mcx_utils.c).
there is no 0.5 offset.
4) Are the exit directions of photons the directions that they come towards the surface, or is refraction at the tissue-air interface accounted for with the Snell's law?
if you use MCX releases after 2017, the detected existing
directions considers refraction
however, before 2017, mcx did not perform that extra step, and
this was fixed in the below commit and carried on since then
https://github.com/fangq/mcx/commit/c7895a6c5a70b82973f6aa9739cf0c6f780a0a53
let me know if this is helpful.
Qianqian
To view this discussion on the web visit https://groups.google.com/d/msgid/mcx-users/AS8P192MB13205E8D19C1668C3E4F4916E92B2%40AS8P192MB1320.EURP192.PROD.OUTLOOK.COM.
Dear Dr. Fang,
Many thanks for the careful explanations! Now I know how to perform the half-voxel shifts correctly in MATLAB when visualizing MCX inputs and outputs in MATLAB image frame.
I'm also very grateful for the advice on how to get the Iso2Mesh surface meshes to follow the voxel boundaries exactly (no shrink). I guess you only need to specify the level sets/"isovalues" if you use "cgalsurf"?
correct, cgalsurf determines the boundary using linear interpolation based upon the specified isovalue.
When using "cgalmesh" with v2s.m, v2m.m or vol2mesh.m, the "isovalues" input is not used, since eventually the function will call "[node,elem,face]=cgalv2m(vol,opt,maxvol)". Thus, it seems that "cgalmesh" will always output meshes that follow exterior and interior regions' voxel boundaries exactly?
for cgalmesh option, this can be achieved by adjusting the
`opt.distbound` option in v2m - the exact definition of this
parameter is denoted as "facet_distance" parameter
my understanding is that this number limits how far away the
triangles can deviate from the label based boundary. a small
number means the boundary is more accurate, but requires lots of
refinement, thus denser near the boundary.
One final question, if you may: How should one interpret the node indices, i.e., the fourth column values from "cgalmesh"? Why are they 1.5 in volume type 1, instead of 1 or some range?
to be honest, I never used the nodal labels returned by cgalmesh, I also wasn't able to find a documentation on its meaning, but it appears that the decimal numbers suggests the node is at the boundary of two labels.
I usually just use the element labels
Qianqian