<div dir="ltr">Hi, <div><br></div><div>I have a 3D vascular free-hand ultrasound volume containing one vessel, and I am trying to reconstruct the surface of the vessel. The 3D volume is constructed from a stack of 2D images/B-scans, and the contour of the vessel in each B-scan has been segmented; that is, I have an ellipse representing the contour of the vessel in each B-scan in the volume. I have tried to reconstruct the contour of the vessel by following the VTK example of 'GenerateModelsFromLabels.cxx' (<a href="http://www.vtk.org/Wiki/VTK/Examples/Cxx/Medical/GenerateModelsFromLabels">http://www.vtk.org/Wiki/VTK/Examples/Cxx/Medical/GenerateModelsFromLabels</a>). However, the result is not a smooth surface from one frame to another as I would have hoped for it to be. It is discontinuous and irregular, and the surface doesn't connect the vessel contours between two adjacent frames in the volume if the displacement between the ellipses is large. In my approach, I basically used DiscreteMarchingCubes -> WindowedSincPolyDataFilter -> GeometryFilter. </div><div><br></div><div>I played around with the passband, smoothingIterations and featureAngle parameters, and I was able to obtain the best following result: </div><div><br></div><div><a href="https://snag.gy/yqK3Fd.jpg">https://snag.gy/yqK3Fd.jpg</a><br></div><div><a href="https://snag.gy/txu6Ur.jpg">https://snag.gy/txu6Ur.jpg</a><br></div><div><a href="https://snag.gy/ksJfUa.jpg">https://snag.gy/ksJfUa.jpg</a><br></div><div><br></div><div><br></div><div>As you can see, it is not a smooth continuous surface with a lot of uninterpolated "holes" between adjacent frames, but it is all right. Can it be made better? I also tried using a 3D Delaunay triangulation, but it only gave me the convex hull, which is not the output I expected. I would like to know if there is a better approach towards reconstructing a surface that closely follows the contour of the vessel from one B-scan to the next in a volume? <br></div><div><br></div><div>A minimal working example is shown below: </div><div><br></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>vtkSmartPointer<vtkImageData> vesselVolume =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">             </span>vtkSmartPointer<vtkImageData>::New();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>int totalImages = 210;<br></div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>for (int z = 0; z < totalImages; z++)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>{</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">              </span>std::string strFile = "E:/datasets/vasc/rendering/contour/" + std::to_string(z + 1) + ".png";</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">             </span>cv::Mat im = cv::imread(strFile, CV_LOAD_IMAGE_GRAYSCALE);</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span>if (z == 0)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span>{</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                      </span>vesselVolume->SetExtent(0, im.cols, 0, im.rows, 0, totalImages - 1);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>vesselVolume->SetSpacing(1, 1, 1);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                  </span>vesselVolume->SetOrigin(0, 0, 0);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                   </span>vesselVolume->AllocateScalars(VTK_UNSIGNED_CHAR, 0);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>}<span class="gmail-Apple-tab-span" style="white-space:pre">     </span></div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">              </span>std::vector<cv::Point2i> locations;   // output, locations of non-zero pixels </div><div><span class="gmail-Apple-tab-span" style="white-space:pre">         </span>cv::findNonZero(im, locations);</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">               </span>for (int nzi = 0; nzi < locations.size(); nzi++)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span>{</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                      </span>unsigned char* pixel = static_cast<unsigned char*>(vesselVolume->GetScalarPointer(locations[nzi].x, locations[nzi].y, z));</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                    </span>pixel[0] = 255;</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>}</div></div><div><br></div><div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>vtkSmartPointer<vtkDiscreteMarchingCubes> discreteCubes =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>vtkSmartPointer<vtkDiscreteMarchingCubes>::New();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>discreteCubes->SetInputData(vesselVolume);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>discreteCubes->GenerateValues(1, 255, 255);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>discreteCubes->ComputeNormalsOn();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>vtkSmartPointer<vtkWindowedSincPolyDataFilter> smoother =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>vtkSmartPointer<vtkWindowedSincPolyDataFilter>::New();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>unsigned int smoothingIterations = 10;</div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>double passBand = 2;</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>double featureAngle = 360.0;</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>smoother->SetInputConnection(discreteCubes->GetOutputPort());</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>smoother->SetNumberOfIterations(smoothingIterations);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>smoother->BoundarySmoothingOff();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>//smoother->FeatureEdgeSmoothingOff();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>smoother->FeatureEdgeSmoothingOn();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>smoother->SetFeatureAngle(featureAngle);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>smoother->SetPassBand(passBand);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>smoother->NonManifoldSmoothingOn();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>smoother->BoundarySmoothingOn();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>smoother->NormalizeCoordinatesOn();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>smoother->Update();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>vtkSmartPointer<vtkThreshold> selector =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">         </span>vtkSmartPointer<vtkThreshold>::New();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>selector->SetInputConnection(smoother->GetOutputPort());</div><div><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>selector->SetInputArrayToProcess(0, 0, 0,</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">           </span>vtkDataObject::FIELD_ASSOCIATION_CELLS,</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>vtkDataSetAttributes::SCALARS);</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>vtkSmartPointer<vtkMaskFields> scalarsOff =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">              </span>vtkSmartPointer<vtkMaskFields>::New();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>// Strip the scalars from the output</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>scalarsOff->SetInputConnection(selector->GetOutputPort());</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>scalarsOff->CopyAttributeOff(vtkMaskFields::POINT_DATA,</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">             </span>vtkDataSetAttributes::SCALARS);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>scalarsOff->CopyAttributeOff(vtkMaskFields::CELL_DATA,</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">              </span>vtkDataSetAttributes::SCALARS);</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>vtkSmartPointer<vtkGeometryFilter> geometry =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span>vtkSmartPointer<vtkGeometryFilter>::New();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>geometry->SetInputConnection(scalarsOff->GetOutputPort());</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>geometry->Update();</div></div></div><div><br></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">     </span>vtkSmartPointer<vtkPolyDataMapper> mapper =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">              </span>vtkSmartPointer<vtkPolyDataMapper>::New();<span style="white-space:pre">     </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>mapper->SetInputConnection(geometry->GetOutputPort());<span style="white-space:pre"> </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>mapper->ScalarVisibilityOff();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>mapper->Update();</div></div><div><div><br></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>vtkSmartPointer<vtkRenderWindow> renderWindow =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">          </span>vtkSmartPointer<vtkRenderWindow>::New();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">              </span>vtkSmartPointer<vtkRenderWindowInteractor>::New();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>renderWindowInteractor->SetRenderWindow(renderWindow);</div></div><div><br></div><div><div>        vtkSmartPointer<vtkRenderer> renderer =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">              </span>vtkSmartPointer<vtkRenderer>::New();</div></div><div><br></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">     </span>renderWindow->AddRenderer(renderer);</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>renderer->SetBackground(.2, .3, .4);<br></div></div><div><br></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>vtkSmartPointer<vtkActor> actor =</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>vtkSmartPointer<vtkActor>::New();</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>actor->SetMapper(mapper);</div></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>renderer->AddActor(actor);<br></div></div><div><br></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>renderer->ResetCamera();</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>renderWindow->Render();<br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>renderWindowInteractor->Start();<span class="gmail-Apple-tab-span" style="white-space:pre">                                   </span></div></div><div><br></div><div><br></div><div>Thanks, </div><div>Tejas</div><div><br></div><div><br></div></div>