projects links |
Tutorials / ImageFeatureCorrection
Protocol on making a CFD mesh using a sigmoid maskIn this protocol is explained how you can mask the bone with a sigmoid function to create a good mesh from for example the internal carotid artery or an aneurysm located near bone. The sigmoid function masks the high gradients in bone and air in order to decrease their influence on the levelset. If you can't understand the used code, I advise you to look at one of the other tutorials on the vmtk website. vmtk scripts Read and Display the imageThe first step is to read in the dicom files and to display them on the screen. This is done using vmtkimagereader -f dicom -d dicom_directory_path -ofile image_volume.vti –pipe vmtkimageviewer The next step is to select a volume of interest. If you look at the object using vmtkimagevoiselector -ifile image_volume.vti -ofile image_volume_voi.vti
Creating the sigmoid maskTo create the proper mask on the image it is necessary to locate regions of bone and air. Air was included later because it also has a steep gradient, but the values in the air level set are opposite to those of bone. It has the same effect as bone on the segmentation of a vessel. To properly locate these regions, Bone and air segmentationAn isosurface of bone and air have to be made seperately. The values from bone and vessels can overlap, but still it is pretty important to choose an isosurface with a value that is as low as possible. (I did this in steps of 100) For this image I chose level 500 HU (Hounsfield Units) for the isosurface. Before you make the actual isosurface with vmtkmarchingcubes -ifile image_volume_voi.vti -l 500.0 –-pipe vmtksurfaceviewer This will give you an idea of what will be included in the isosurface. If you can recognize vascular structures, then you will have to increase the value of the isosurface in order to exclude the vascular structures from the levelset The actual construction of the levelset of bone: vmtklevelsetsegmentation -ifile image_volume_voi.vti -ofeatureimagefile bone_feature_500.vti I used For air, the same thing can be done. The value for the isosurface I used was vmtklevelsetsegmentation -ifile image_volume_voi.vti -ofeatureimagefile air_feature_-50.vti If you then use vmtkimagecompose -ifile bone_lvlset_500.vti -i2file air_lvlset_-50.vti -negatei2 1 How to segment specific regionsWhen creating the levelsets of bone and air, it is wise to choose a relatively small volume of interest, because it may take a while before the process is completed. With VMTK it is also possible to make a smaller volume of interest in the one you already made, which contains the region of bone that you want to segment (I.e. the region where the aneurysm/vessel is very close to the bone)
vmtkimagevoiselector -ifile image_volume_voi.vti -ofile image_volume_voi_2.vti With this volume of interest, you can segment bone and air in the same way as mentioned previously. However, because you made a smaller volume of interest, you can not add the segmentations together and use the original volume of interest to do the segmentation of your vessel. The following steps have to be taken to be able to segment the vessel. After the segmentation of a piece of bone you were interested in, the levelset image has to be resliced with vmtkimagereslice -ifile piece_of_bone_voi_2.vti -rfile image_volume_voi.vti -background10.0 The option Adding the smaller pieces together: vmtkimagecompose -ifile piece_of_bone_voi.vti -i2file another_bone_piece.vti -ofile big_piece_of_bone.vti Correct the feature imageIn this composed image the positions of the gradients you want to mask can be seen in black.
vmtkimagefeaturecorrection -ifile bone_feature_500.vti -levelsetsfile bone_air_lvlset.vti
Figure 7 shows the original feature image, figure 8 shows the altered one. In the latter figure you can see that some areas are brighter compared to figure 7. This is caused by the application of the sigmoid function I recommend however to use a small standard deviation (for example one pixel) because then you will be able to use the curvature option more efficiently. If you will use a higher standard deviation , the curvature option has less freedom when creating the actual levelset. This is because they both erode the levelset a bit in order to create a better shape. The curvature is also capable of smoothing the surface of the aneurysm, which the sigmoid cannot. Short explanation of the problem.When looking at figure 6, the intensities of the different tissues are displayed by the blue line. The red dotted line represents the negative absolute derivative (for a more detailed explanation i refer to the PhD thesis of Luca Antiga). Normally when the bone is away from the aneurysm, a deformable model that would start at point A would evolve to point B. However, when bone is close, it will evolve to point C because this is the local minimum and not point B. A deformable model for bone starting at point D will always evolve to point C. If you look at the figure closely, you can see that the same can happen when air will come too close. The great difference in intensities between vessel and bone/air, cause the problem. The correction that is applied by the sigmoid function is the green line, which will cause the local minimum to shift to point B again. Constructing the AneurysmNow the actual model of the aneurysm will be constructed. vmtklevelsetsegmentation -ifile image_volume_voi.vti -featureimagefile sigmoid_feature.vti To see the difference with the levelset without using the sigmoid function, exclude the I started at the aneurysm, because this was the most important reason why the sigmoid was introduced. Aneurysm -> fast marching -> thresholds 250 – 500 -> 500 iterations Place one source seed at the center of the aneurysm and one target seed at the border. After running the iteration step, you get a good impression of what your aneurysm will look like. The sharp edges can be removed by increasing the curvature option. High values for curvature will cause the surface to collapse, so increase the curvature in small steps. My final values: Vessels -> colliding fronts -> thresholds 250 - 500 -> 500 iterations When the thresholds fail to make an initialization, the thresholds are adjusted until an initialization can be made. I kept the curvature at Remove levelset regionIf you want to remove a region afterwards because you are not satisfied with it, you can use vmtkrenderer -–pipe vmtkmarchingcubes -ifile aneurysm_model.vti -l 0.0 -–pipe vmtksurfaceviewer -i @.o The paintvalue has to be greater then the maximum value in the levelset. You could look this up by using After painting, you can continue again with the levelsetsegmentation using vmtklevelsetsegmentation -ifile image_volume_voi.vti -featureimagefile sigmoid_feature.vti Constructing the Internal CarotidThe first thing to do is to create an initial levelset of the internal carotid. vmtkimageinitialization -ifile image_volume_voi.vti -olevelsetsfile intcarotid_initial.vti Now you will have to construct the whole internal carotid. First it is wise to choose a small interval of thresholds in order to segment the carotid only and avoid the neighboring tissue. Second you should do the segmentation in small steps. This is also done to avoid the inclusion of neighboring tissue.
In the end it is still possible that you have some extra tissue in your initialization, but try to keep this to a minimum.
For the internal carotid artery I took As you may have noticed, Once you have created the initial levelset, you can make the actual model of the internal carotid. vmtklevelsetsegmentation -ifile image_volume_voi.vti -featureimagefile image_volume_voi_sigmoid_feature.vti Notice that the option My endvalues here were Why initial levelsetInitially I created a model of the internal carotid in the same way as described in the aneurysm section. However, this sometimes gave complications and took a long time. Afterwards I tried to make a model with an initial levelset which gave a result almost as good as the previous method, only taking two steps. Creating the meshWhen you have made a model of the aneurysm and/or the internal carotid, you can proceed to make a mesh from the model. In this part I will use the aneurysm model in the code, but you can easily apply the same to the internal carotid. The first step is to create a surface from the levelset. vmtkmarchingcubes -ifile aneurysm_model.vti -ofile aneurysm_model.vtp Then the surface will be smoothed vmtksurfacesmoothing -ifile aneurysm_model.vtp -passband 0.1 -iterations 30 -ofile aneurysm_model_sm.vtp In the rare occasion that in some regions on the surface strange peaks will appear, you can also use vmtksurfacereader -ifile aneurysm_model.vtp –-pipe vmtkcenterlines –pipe vmtkdistancetocenterlines Clip the surface with vmtksurfaceclipper -ifile aneurysm_model_sm.vtp -ofile aneurysm_model_sm_cl.vtp The next step is increasing the number of surface triangles. This is optional but I used this step every time I made the mesh. The used method is loop . vmtksurfacesubdivision -ifile aneurysm_model_sm_cl.vtp -method loop -ofile aneurysm_model_sm_cl_sb.vtp Then flowextensions will be added to the model. vmtksurfacereader -ifile aneurysm_model_sm_cl_sb.vtp –-pipe vmtkcenterlines -seedselector openprofiles The extensionratio Creating mesh with vmtkmeshgeneratorThere is a slight difference in steps you have to take when using vmtkmeshgenerator instead of NetGen. In this section, the use of vmtkmeshgenerator will be discussed.
vmtksurfacereader -ifile aneurysm_model_sm_cl_sb_ex.vtp –-pipe vmtkcenterlines -seedselector openprofiles The following code transforms the elements from linear to quadratic elements. vmtklineartoquadratic -ifile aneurysm_model_mesh.vtu -ofile aneurysm_model_meshq.vtu With vmtksurfacescaling -ifile aneurysm_model_sm_clip_sb_ex.vtp -scale 0.1 –-pipe This step scales the mesh from mm to cm vmtkmeshscaling -ifile aneurysm_model_meshq.vtu -scale 0.1 -ofile aneurysm_model_meshq_sc.vtu Finally the mesh is written to libmesh format. This is also dependant on the solver you are using. Here I used a solver that is currently in development. Generating an inputfile for a CFD solver vmtkmeshwriter -ifile aneurysm_model_meshq_sc.vtu -entityidsarray CellEntityIds -ofile aneurysm_model_meshq_sc.xda Creating mesh with NetGenIn this section, the use of creating a mesh with NetGen will be discussed The first thing to do is to cap the flowextensions so you have a model with closed extensions. Afterwards the file will be written to a .stl file which can be opened with NetGen vmtksurfacecapper -ifile aneurysm_model_sm_cl_sb_ex.vtp -ofile aneurysm_model_sm_cl_sb_ex_cp.vtp The option -interactive is set to NetGen is an open source mesh generation tool. Download NetGen or instal from the ubuntu repository. For a more detailed explanation see Meshing with NetGen Remove spurious edges with STL doctor
With meshscaling you have to convert the model from millimeters to centimeters. vmtkmeshscaling -ifile filename.neu -scale 0.1 -ofile filename.vtu After this step you are ready to generate the input for your solver. This greatly depends on the solver you are using. Generating an inputfile for a CFD solver If you have any questions, feel free to contact me at R.P.M.Jansen@student.tue.nl |