Hello all,
Considering a segmented model that includes different parts (bones, ligaments, etc.). In FEM, we may need to have a fine mesh for some parts of the model and a coarse mesh for other parts (for example fine mesh for ligaments and coarse mesh for bones). Is it possible to do this with segment mesher?
I know that cleaver tool of segment mesher has an adaptive mesh option but I think it refines mesh at the regions where two part are in contact with each other and it is not the same thing as I want. Any helps on this is greatly appreciated.
Thanks in advance.
Cleaver can take an additional sizing field image, which can be used to specify mesh resolution. This option is not exposed on the graphical user interface but you need to create this field image and pass it to the mesher via additional command-line option. If you find that it works well then we can make it more conveniently available.
Thank you very much for your response @lassoan. I tried to add a field nrrd file. I apply the following command in the section “cleaver mesh options” of segment mesher:
--sizing_field /user/fields.seg.nrrd
However, i get the following error:
Traceback (most recent call last):
File "/user/.config/NA-MIC/Extensions-27931/SegmentMesher/lib/Slicer-4.10/qt-scripted-modules/SegmentMesher.py", line 280, in onApplyButton
self.logic.createMeshFromSegmentationCleaver(self.inputModelSelector.currentNode(), self.outputModelSelector.currentNode(), self.cleaverAdditionalParametersWidget.text)
File "/user/.config/NA-MIC/Extensions-27931/SegmentMesher/lib/Slicer-4.10/qt-scripted-modules/SegmentMesher.py", line 520, in createMeshFromSegmentationCleaver
self.logProcessOutput(ep, self.cleaverFilename)
File "/user/.config/NA-MIC/Extensions-27931/SegmentMesher/lib/Slicer-4.10/qt-scripted-modules/SegmentMesher.py", line 455, in logProcessOutput
raise subprocess.CalledProcessError(return_code, processName)
CalledProcessError: Command 'cleaver-cli' returned non-zero exit status -6
Do you have any ideas about the reason of this error?
Also for creating the size field nrrd file, I did this:
- Convert my main segmentation (for which I want to create mesh) to model in slicer.
- Use surface toolbox and decimation tool to make the model coarse in specific regions.
- Convert the model to segmentation node and use the new segmentation as the sizing field.
Do you also think this procedure is the correct way of doing this?
Thank you very much and I really appreciate your help.
Can you upload all your inputs somewhere and post theink here so I can easily reproduce what you did?
Sure @lassoan. I created some test files and uploaded them to the link below:
https://github.com/A-ep93/SlicerTest/blob/main/test.zip?raw=true
The file with the name “Segmentation.seg.nrrd” is the main file I want to mesh and the file with the name “sizing-field.seg.nrrd” is the sizing filed I created (In the sizing field, one of the parts is coarser than the other one).
I am not still sure, whether the sizing field I created is the thing I need for cleaver and I searched the web but I was not able to find any good examples for this. Your help is greatly appreciated.
Hello @lassoan. I wanted to kindly ask you whether you were able to check my files and if you did, whether you have any suggestions for me.
Thanks for your help in advance.
Yes, I’ve completed an investigation of how to use custom sizing field with Cleaver:
If you specify a custom sizing field then the segment’s geometry and scaling, multiplier, etc. parameters are not taken into account for determining element size. You need to then compute the entire field yourself. A reasonable approach would be to let Cleaver compute an initial mesh and then you could alter the values in selected regions (make them larger/smaller, depending on the resolution you want).
I’ve tried to changing cleaver-cli to write the computed sizing field and then using that field in the next execution. Unfortunately, this did not work (generated mesh was empty). It may be due to Cleaver making changes to the volume size (adds padding) and discards spacing, origin, and axis direction information when importing/exporting sizing fields, so it is hard to know what image geometry cleaver expects.
Before continuing the investigation, Cleaver should be fixed so that it uses image geometry information (origin, spacing, axis directions) consistently in all inputs and outputs. There are a number of open issues related to this already, so probably Cleaver developers are aware of this, but I’m not sure when they plan to address them. You can submit a bug report to their repository and see what they say.
Thank you so much for working on this @lassoan. I really appreciate your help. I will submit a bug report on the cleaver github page regarding the consistency of the geometry information for the inputs and outputs. But I have two questions now:
- You said that a reasonable approach for creating the sizing field is to use the cleaver to generate an initial mesh and then modify it as we want and use it as sizing field. I also think it is a good idea. But how is it possible? I mean when we generate meshes with segmentmesher, we have some volumetric models in slicer. As far as I know, most of the slicer modules are for working with surface models and not volumetric ones. Is this correct? If yes, then how should we modifiy those volumetric meshes?
- Aren’t we able to do this (modifying meshes in areas we want) by using tetgen instead of cleaver? Do you think it is possible in tetgen?
Thanks in advance
Hello @lassoan. I saw that you updated the cleaver version of segmentmesher. Thank you very much for this.
Now, how should we create the sizing field? You mentioned previously that we can generate an initial model with the cleaver itself and modify it as we want. But how should we obtain the initial sizing field from cleaver? and how should we modify that? Can you please send me the modifications that are needed to be applied on cleaver-cli for this?
One effective way of creating a sizing field is to paint segments where you want higher resolution, export that segmentation to labelmap node, replace label values by sizing values (using standard numpy array operations), then apply Gaussian blurring to make the sizing field smooth. There are of course many other ways and we can also easily automate these steps, once it is confirmed that it all works.
Thank you very much @lassoan for your explanation.
I did what you mentioned on a test file and attached the files here as well.
The file with name “Segmentation.seg.nrrd” is the main file I want to mesh. I created a segment for one part (for which I want finer mesh), converted that to label map and increased the value of the sizing field from 1 to 2000 with the following code:
volumeNode = getNode('field')
a = arrayFromVolume(volumeNode)
# Increase image contrast
a[:] = a * 2000.0
arrayFromVolumeModified(volumeNode)
I did not applied the Gaussian blurring because I muliplied the whole domain by a constant value and I think everything is still smooth for this simple example (is this correct?).
I exported the field with the name “field.nrrd” and used it for the sizing field parameter in segment mesher by writing the following in the “cleaver mesh options”:
--sizing_field /path/field.nrrd
However, I still get an error when I run segmentmesher. Can you please let me know what is wrong in this procedure?
Thanks in advance.
-
You need to replace the 0 label by setting to a constant and/or applying Gaussian blurring.
-
Extensions are rebuilt every night. You need to wait until tomorrow and then reinstall SegmentMesher to get latest Cleaver2 version.
Thanks, @lassoan. I will replace the zero label with a constant and check again tomorrow and update you with my results. Thank you so much.
Hello @lassoan. I removed the zero label and used Gaussian blurring and it seems to work! Here is my final mesh which is fine for one part and coarse for another part:
Also, I uploaded my field here so that others can check it in the future if they needed a sample case. I used a code similar to the following one and applied Gaussian blur after that:
volumeNode = getNode('fields')
a = arrayFromVolume(volumeNode)
a[a == 0] = 3
a[a == 1] = 5
a[a == 2] = 900
arrayFromVolumeModified(volumeNode)
I think one may need to play with the sizing values and maybe the sigma (for gaussian blur filter) to obtain the desired mesh. I should mention that I just applied this on a simple model and not my main model and I may need further considerations for main model.
Thank you so much @lassoan for all your help for this.
I have two questions now:
-
I checked some older slicer versions (such as 4.10.1) and to me, it seems that the segmentmesher is not updated for them. Is this right?
-
I still could not find the relation between the generated meshes and sizing values I give. So maybe it is helpful if I can generate some meshes with segmentmesher and check their sizing field. Can you please let me know how should I export the sizing field from segmentmesher?
Thanks in advance.
It is always recommended to use the latest stable or preview release (currently, Slicer-4.11.20200930 and Slicer-4.13.1). Nightly extensions updates are only available for these releases.
The sizing field defines the desired element size. I’m just not sure in what unit. You submit an issue to Cleaver2 repository asking for clarification.