Good afternoon.
In my project i convert a vtkVolume by some given DICOM files.
Then i convert my vtkVolume on to a Surface by using vtkMarchingCubes and 6 steps more.
Code is the following (myVolume is a class that inherits from vtkVolume):
void VolumeToSurface::ConvertVolume(vtkSmartPointer vol, double r)
{
if (vol == nullptr)
return;
vol->setPicked(false);
double ratio = 1.0 - r;
bool finish = false;
vtkSmartPointer<vtkPlaneCollection> planes = vol->GetMapper()->GetClippingPlanes();
int min = -1000;
int max = -1000;
double R, G, B;
//convertToPoints(vol, 0.15, 200, 1800);
while(!finish){
finish = extraerRango(min, max, vol);
if (min < 10000) {
Consola::append("Creando superficie con rango ");
Consola::append(min);
Consola::append(" - ");
if(max < 10000)
Consola::append(max);
else
Consola::append("Max");
Consola::append("\n");
imageData = vtkSmartPointer<vtkImageData>::New();
surfaceExtractor = vtkSmartPointer<vtkMarchingCubes>::New();
imageData->DeepCopy(vol->getImageData());
surfaceExtractor->ComputeNormalsOn();
Consola::append("Etapa 1 de 7: Filtrando valores...");
if (abs(min) > abs(max)) {
surfaceExtractor->SetValue(max, min);
Consola::appendln("Salteada");
}
else {
aplicarRango(vol->getImageData(), min, max, imageData);
Consola::appendln("OK");
surfaceExtractor->SetValue(0, 128);
}
//marchingcubes
surfaceExtractor->SetInputData(imageData);
vtkSmartPointer<vtkCallbackCommand> callback = CallbacksClass::getProgressCallBack();
if (callback != nullptr)
{
CallbacksClass::setEncabezado("Generando superficie: ");
surfaceExtractor->AddObserver(vtkCommand::ProgressEvent, callback);
}
Consola::append("Etapa 2 de 7: Generando superficie...");
surfaceExtractor->Update();
Consola::appendln("OK");
//marchingcubes
vtkNew<vtkPolyData> mesh; //reemplazar por vtkSmartPointer
mesh->DeepCopy(surfaceExtractor->GetOutput());
//triangulate
vtkSmartPointer<vtkTriangleFilter> triangleFilter =
vtkSmartPointer<vtkTriangleFilter>::New();
triangleFilter->SetInputData(mesh);
if (callback != nullptr)
{
CallbacksClass::setEncabezado("Triangulando caras: ");
triangleFilter->AddObserver(vtkCommand::ProgressEvent, callback);
}
Consola::append("Etapa 3 de 7: Triangulando caras...");
triangleFilter->Update();
mesh->DeepCopy(triangleFilter->GetOutput());
Consola::appendln("OK");
//triangulate
//decimator
Consola::append("Etapa 4 de 7: Reduciendo caras...");
if (ratio > 0.01) {
vtkSmartPointer<vtkQuadricDecimation> decimator = vtkSmartPointer<vtkQuadricDecimation>::New();
decimator->SetInputData(mesh);
decimator->SetTargetReduction(ratio);
if (callback != nullptr)
{
CallbacksClass::setEncabezado("Reduciendo caras: ");
decimator->AddObserver(vtkCommand::ProgressEvent, callback);
}
decimator->Update();
mesh->DeepCopy(decimator->GetOutput());
Consola::appendln("OK");
}
else{
Consola::appendln("Salteado");
}
//decimator
//clean mesh
vtkSmartPointer<vtkCleanPolyData> cleanPolyData =
vtkSmartPointer<vtkCleanPolyData>::New();
cleanPolyData->SetInputData(mesh);
cleanPolyData->SetTolerance(0.001);
if (callback != nullptr)
{
CallbacksClass::setEncabezado("Limpiando puntos: ");
cleanPolyData->AddObserver(vtkCommand::ProgressEvent, callback);
}
Consola::append("Etapa 5 de 7: Limpiando puntos...");
cleanPolyData->Update();
Consola::appendln("OK");
mesh->DeepCopy(cleanPolyData->GetOutput());
//making slices
vtkSmartPointer<vtkBox> implicitCube =
vtkSmartPointer<vtkBox>::New();
double* bnds = vol->getCustomBounds();
double* origBounds = vol->getImageData()->GetBounds();
//printf("BOUNDS %f\n", bnds[0]);
double bounds[6];
bounds[0] = bnds[0] + origBounds[1] / 2.0;
bounds[1] = bnds[1] + origBounds[1] / 2.0;
bounds[2] = bnds[2] + origBounds[3] / 2.0;
bounds[3] = bnds[3] + origBounds[3] / 2.0;
bounds[4] = bnds[4] + origBounds[5] / 2.0;
bounds[5] = bnds[5] + origBounds[5] / 2.0;
implicitCube->SetBounds(bounds);
vtkSmartPointer<vtkClipPolyData> clipper =
vtkSmartPointer<vtkClipPolyData>::New();
clipper->SetClipFunction(implicitCube);
clipper->SetInputData(mesh);
clipper->InsideOutOn();
Consola::append("Etapa 6 de 7: Realizando cortes...");
if (callback != nullptr)
{
CallbacksClass::setEncabezado("Realizando cortes: ");
clipper->AddObserver(vtkCommand::ProgressEvent, callback);
}
clipper->Update();
Consola::appendln("OK");
mesh->DeepCopy(clipper->GetOutput());
MasterHandler::GetSurfaceInteractor()->GetMeshOperations()->moveMeshToCOSCenter(mesh);
//making slices
//remove small objects
Consola::appendln("Etapa 7 de 7: Removiendo objetos sueltos");
if (!vol->getIsSplitted()) {
vtkSmartPointer<vtkPolyDataConnectivityFilter> connectivityFilter = vtkSmartPointer<vtkPolyDataConnectivityFilter>::New();
connectivityFilter->SetInputData(mesh);
connectivityFilter->SetExtractionModeToAllRegions();
vtkSmartPointer<vtkCallbackCommand> m_progressCallback = CallbacksClass::getProgressCallBack();
CallbacksClass::setEncabezado("Analizando objeto: ");
connectivityFilter->AddObserver(vtkCommand::ProgressEvent, m_progressCallback);
Consola::appendln("...Etapa 1 de 2: Analizando objeto...");
connectivityFilter->Update();
// remove objects consisting of less than ratio vertexes of the biggest object
vtkIdTypeArray* regionSizes = connectivityFilter->GetRegionSizes();
Consola::appendln("OK");
// find object with most vertices
long maxSize = 0;
for (int regions = 0; regions < connectivityFilter->GetNumberOfExtractedRegions(); regions++)
if (regionSizes->GetValue(regions) > maxSize)
maxSize = regionSizes->GetValue(regions);
// append regions of sizes over the threshold
connectivityFilter->SetExtractionModeToSpecifiedRegions();
for (int regions = 0; regions < connectivityFilter->GetNumberOfExtractedRegions(); regions++)
if (regionSizes->GetValue(regions) > maxSize * 0.02)
connectivityFilter->AddSpecifiedRegion(regions);
Consola::appendln("...Etapa 2 de 2: Removiendo objetos...");
CallbacksClass::setEncabezado("Removiendo objetos: ");
connectivityFilter->Update();
//remove small objects
mesh->DeepCopy(connectivityFilter->GetOutput());
Consola::appendln("OK");
}
else {
Consola::appendln("Salteada");
}
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputData(mesh);
mapper->ScalarVisibilityOff();
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
if(max<10000)
extraerColor(R, G, B, (max - min) / 2, vol);
else
extraerColor(R, G, B, (1500 - min) / 2, vol);
actor->GetProperty()->SetColor(R, G, B);
renwinVTS->GetRenderers()->GetFirstRenderer()->AddActor(actor);
emit MasterHandler::GetSimpleView()->signal_renderizar();
Consola::append("Finalizado\n");
}
}
}
bool VolumeToSurface::extraerRango(int & min, int & max, vtkSmartPointer vol)
{
QVector X_function = *vol->GetOpacityFunction_X();
QVector Y_function = *vol->GetOpacityFunction_Y();
int cant = NUMERODEPUNTOS_SCALAR;
int i = 0;
while (i < cant && X_function[i] < max) {
i++;
}
while (i < cant && Y_function[i] < 0.9) {
i++;
}
if (i < cant)
min = X_function[i];
else {
min = 50000;
max = 50000;
return true;
}
while (i < cant && Y_function[i] >= 0.9) {
i++;
}
if (i < cant) {
max = X_function[i];
return false;
}
else {
max = 50000;
return true;
}
}
void VolumeToSurface::aplicarRango(vtkSmartPointer input, int min, int max, vtkSmartPointer output)
{
emit MasterHandler::GetSimpleView()->visibleProgressBar(true);
int* dims = output->GetDimensions();
int recorridas = 0;
int numCmp = dims[0] * dims[1] * dims[2];
short* input_ptr = static_cast<short*>(input->GetScalarPointer());
short* output_ptr = static_cast<short*>(output->GetScalarPointer());
int dim_x = dims[0];
int dim_y = dims[1];
int dim_z = dims[2];
int update = dims[2] / 20.0;
int iternum = 0;
for (int k = 0; k < dim_z; k++) {
for (int i = 0; i < dim_x; i++) {
for (int j = 0; j < dim_y; j++) {
//double val = input->GetScalarComponentAsDouble(i, j, k, 0);
double val = GetScalarValue(input_ptr, i, j, k, dim_x, dim_y);
if (val < min || val > max) {
/**
if (neighbourInRange(input, i, j, k, dims, min, max)) {
val = 255;
}
else/**/
val = 0;
}
else {
val = 255;
}
//output->SetScalarComponentFromDouble(i, j, k, 0, val);
SetScalarValue(output_ptr, i, j, k, dim_x, dim_y, val);
recorridas++;
}
//progress->setValue((int)(x*100.0f / (double)dim_x));
if (iternum == 0)
{
emit MasterHandler::GetSimpleView()->progressChanged(recorridas * 100.0f / (double)numCmp);
emit MasterHandler::GetSimpleView()->signal_WriteLabel(QString(
QString("Filtrando valores: ")
+ QString::number((int)(recorridas * 100.0f / (double)numCmp))
+ QString("%")
));
}
iternum = (iternum + 1) % update;
}
}
emit MasterHandler::GetSimpleView()->visibleProgressBar(false);
}
void VolumeToSurface::extraerColor(double & R, double & G, double & B, int index, vtkSmartPointer vol)
{
QVector<double> Color_function = *vol->GetColorFunction();
int i = (index + 1000) / STEP_COLOR;
R = Color_function[i * 3 + 0];
G = Color_function[i * 3 + 1];
B = Color_function[i * 3 + 2];
}
It generates the surface successfully. However on part 5 of cleanPolyData, it takes a little bit of a long time.
Next if i press smooth mesh button on my app, it runs the next code:
void MeshOperations::smoothMesh( vtkSmartPointer actor, unsigned int iterations )
{
vtkSmartPointer<vtkPolyData> mesh = (vtkPolyData*)actor->GetMapper()->GetInput();
//cout << "Mesh smoothing with " << nbrOfSmoothingIterations << " iterations." << endl;
Consola::appendln("Suavizando objeto");
vtkSmartPointer<vtkSmoothPolyDataFilter> smoother = vtkSmartPointer<vtkSmoothPolyDataFilter>::New();
smoother->SetInputData( mesh );
smoother->SetNumberOfIterations( int(iterations) );
smoother->SetFeatureAngle(45);
smoother->SetRelaxationFactor(0.05);
vtkSmartPointer<vtkCallbackCommand> m_progressCallback = CallbacksClass::getProgressCallBack();
{
CallbacksClass::setEncabezado("Suavizando objeto: ");
smoother->AddObserver(vtkCommand::ProgressEvent, m_progressCallback);
}
smoother->Update();
vtkSmartPointer<vtkPolyDataMapper> polyMapper = (vtkPolyDataMapper*)actor->GetMapper();
//lock mutex
MasterHandler::GetThread()->lock_Actor();
emit MasterHandler::GetSimpleView()->signal_RemoveActor(actor);
//lock mutex
MasterHandler::GetThread()->lock_Actor();
mesh->DeepCopy( smoother->GetOutput() );
polyMapper->SetInputData(mesh);
polyMapper->ScalarVisibilityOff();
actor->SetMapper(polyMapper);
MasterHandler::GetSurfaceInteractor()->AddActor(actor);
//unlock mutex
MasterHandler::GetThread()->unlock_Actor();
Consola::appendln("Suavizando objeto: Finalizado");
}
Then, when it comes to an end, it suddenly stops and throws an error:
In vtkAbstractArray.h in line 180 GetNumberOfValues() says:
Read access violation on this was 0xFFFFFFFFFFFFFFC7
I know it has to do that it did not find some value on the array, but i do not understand where it is corrupted. This used to work on version VTK 8.1 but now on VTK 8.2 or VTK 9.0.0 is not working anymore. Does anyone know if any of this used vtk classes is not working properly anymore?
I tried debugging and it passed all the lines, i suspect it has to do with the smoother->Update() line.
Best regards.