I am doing a VTK program in that when I enter a 2D DICOM patient image position (kindly refer the given image for better understanding), I need to get that particular slice in the 3D surface rendered output.
For volume rendered 3D image this can be achieved by using these functions namely vtkImageData, vtkImageMapToColors,vtkImage Actor.
My question is how to do it in the surface rendered output.do anyone know the concept for that. If anybody knows please answer. If my question is incorrect or not understandable kindly share your opinion.
For clear understanding I am showing a sample picture
consider my 3d output will be like the picture below
and when I enter the image position(patient) in a text box and click ok button that corresponding slice should be shown in the 3d image like the below picture
please inform if my question is not understandable
I got stuck here i don't even know whether my code is right.Here is my code
ExtractVOI->SetInput(reader1->GetOutput());//VOI extractor
ExtractVOI->SetVOI(1,0,0,0,1,0);//i have given the Image Orientation(patient) as the SetVOI value
////====CREATE LookUpTable
tableax1->SetTableRange(0.0, 4096.0);
tableax1->SetValueRange(0.0, 1.0);
tableax1->SetSaturationRange(0.0, 0.0);
tableax1->SetRampToSCurve();
tableax1->SetAlphaRange(0.0, 0.08);
tableax1->Build();
//====CREATE TEXTURE
planesourceax1->SetXResolution(1);
planesourceax1->SetYResolution(1);
planesourceax1->SetOrigin(0,0,0);
planesourceax1->SetPoint1(xg , yg,zg);//i have given the value of Image Position(patient) that is taken from a textbox ,as the points
planesourceax1->Update();
vtkSmartPointer<vtkPolyDataMapper> mapax1 = vtkSmartPointer<vtkPolyDataMapper>::New();
mapax1->SetInputConnection(planesourceax1->GetOutputPort());
mapax1->UpdateWholeExtent();
textureax1->SetInputConnection(ExtractVOI->GetOutputPort());
textureax1->InterpolateOn();
textureax1->SetLookupTable(tableax1);
textureax1->UpdateWholeExtent();
//===PASS TO ACTOR
actorax1->SetMapper(mapax1);
actorax1->GetMapper()->SetResolveCoincidentTopologyToPolygonOffset();
actorax1->GetMapper()->SetResolveCoincidentTopologyPolygonOffsetParameters(0.1, -1.0);
actorax1->SetTexture(textureax1);
renderer->AddActor(actorax1);
renderWindow->Render();
but i am not getting output
I also tried:
static double axialElements[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
resliceax1->SetInputConnection(reader->GetOutputPort());
resliceax1->SetOutputDimensionality(2);
vtkSmartPointer<vtkMatrix4x4> reslicematrixax1 = vtkSmartPointer<vtkMatrix4x4>::New();
reslicematrixax1->DeepCopy(axialElements);
resliceax1->SetResliceAxes(reslicematrixax1);
resliceax1->SetResliceAxesOrigin(0.0, 0.0, 0.0);
resliceax1->Update();
extractaxpos1->RemoveAllInputs();
extractaxpos1->SetInputConnection(resliceax1->GetOutputPort());
////====CREATE LUT
tableax1->SetTableRange(0.0, 4096.0);
tableax1->SetValueRange(0.0, 1.0);
tableax1->SetSaturationRange(0.0, 0.0);
tableax1->SetRampToSCurve();
tableax1->SetAlphaRange(0.0, 0.08);
tableax1->Build();
//====CREATE TEXTURE
planesourceax1->SetXResolution(1);
planesourceax1->SetYResolution(1);
planesourceax1->SetOrigin(0,0,0);
planesourceax1->SetPoint1((xval/20 + xval/32),(yval/20 + yval/32),(zval/20 + zval/32));//this is where i put the values ad divided by its tag id(0020,0032)
//planesourceax1->SetPoint2(fBounds[0] , fBounds[3], fBounds[4]);
planesourceax1->Update();
vtkSmartPointer<vtkPolyDataMapper> mapax1 = vtkSmartPointer<vtkPolyDataMapper>::New();
mapax1->SetInputConnection(planesourceax1->GetOutputPort());
mapax1->UpdateWholeExtent();
textureax1->SetInputConnection(extractaxpos1->GetOutputPort());
textureax1->InterpolateOn();
textureax1->SetLookupTable(tableax1);
textureax1->UpdateWholeExtent();
//===PASS TO ACTOR
actorax1->SetMapper(mapax1);
actorax1->GetMapper()->SetResolveCoincidentTopologyToPolygonOffset();
actorax1->GetMapper()->SetResolveCoincidentTopologyPolygonOffsetParameters(0.1, -1.0);
actorax1->SetTexture(textureax1);
resliceax1->SetResliceAxesOrigin(0.0, 0.0, 0.0);
actorax1->SetPosition((xval/20 + xval/32),(yval/20 + yval/32),(zval/20 + zval/32));//I made the same changes here also
planesourceax1->SetOrigin(fBoundsUpdated[0], fBoundsUpdated[2], pDoc->fBounds[4]);
planesourceax1->SetPoint1(fBoundsUpdated[1] , fBoundsUpdated[2], pDoc->fBounds[4]);
planesourceax1->SetPoint2(fBoundsUpdated[0] , fBoundsUpdated[3], pDoc->fBounds[4]);
planesourceax1->Update();
but it is not cutting the position where the slice is.It is cutting a different position
If you want to know what element decide cutting direction
https://github.com/Kitware/VTK/blob/master/Examples/ImageProcessing/Cxx/ImageSlicing.cxx
please look this link
static double axialElements[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
...
vtkSmartPointer<vtkMatrix4x4> resliceAxes = vtkSmartPointer<vtkMatrix4x4>::New();
resliceAxes->DeepCopy(axialElements);
// Set the point through which to slice
resliceAxes->SetElement(0, 3, 0);
resliceAxes->SetElement(1, 3, 0);
resliceAxes->SetElement(2, 3, 70);
Matrix 4x4 is setting Reslice direction
and SetElement decide Reslice origin
this code setting option is Reslice XYimage start at Z = 70
vtkSmartPointer<vtkImageReslice> reslice = vtkSmartPointer<vtkImageReslice>::New();
reslice->SetInputData(m_vtkVolumeImageData);
reslice->SetOutputDimensionality(2);
reslice->SetResliceAxes(resliceAxes);
reslice->SetInterpolationModeToLinear();
reslice->Update();
this code make Reslice Data.
Result Dimesionality is made 2D(Plane)
and if you setting this sequence,
connect Reslice data to vtkImageMapToColors for paint Reslice image.
and last, Connect Mapper and Actor for show with Volume.
you need to setting Actor location because Reslice Data maybe dosen't have a location info
if Plane location is not changed, using vtkDataSetMapper and vtkActor, not vtkImageActor
Or you just use Widget,
I recommand vtkImagePlaneWidget.
It is easy and very Powerful.
I hope it will help you.
If you need full code, please tell me
Given a DICOM slice, position goes directly into the SetResliceAxesOrigin, whereas texture primitive is shifted a bit to make the VTK center of pixel -units to correspond with OpenGL bounds. Then the image dimensions and spacing along X and Y axis are used to calculate the extent of the primitive (track the prints of DICOM tags). Directions can be currently omitted since vtkImageData doesn't have them (https://discourse.vtk.org/t/proposal-to-add-orientation-to-vtkimagedata-feedback-wanted/120/2). But they are most likely (hopefully) baked into the image data during reading.
You can verify the rendering by comparing with volume or surface models.
For handling medical image data, I recommend ITK (https://itk.org/). If still not using ITK, just read the image and call the output as image in the code.
Sample print:
0020|0037 1\-2.58172e-10\3.63164e-11\-1.26711e-11\-0.187259\-0.982311
Warning, direction will be lost
0018|0050 1
0028|0030 1\1
0020|0032 -252.69\-118.273\305.807
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "vtkInteractorStyleTrackballCamera.h"
#include "vtkImplicitPlaneWidget2.h"
#include "vtkImplicitPlaneRepresentation.h"
#include "vtkCommand.h"
#include "vtkPlane.h"
#include "vtkImageMapToColors.h"
#include "vtkSmartPointer.h"
#include "vtkPolyData.h"
#include "vtkPoints.h"
#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include "vtkImageReslice.h"
#include "vtkPiecewiseFunction.h"
#include "vtkColorTransferFunction.h"
#include "vtkGPUVolumeRayCastMapper.h"
#include "vtkVolumeProperty.h"
#include "vtkTexture.h"
#include "vtkLookupTable.h"
#include "vtkImageMarchingCubes.h"
vtkSmartPointer<vtkPolyData> MyCreateSimplePlane( const double * corners )
{
vtkSmartPointer<vtkPolyData> ret=vtkSmartPointer<vtkPolyData>::New();
vtkSmartPointer< vtkFloatArray > tcoords = vtkSmartPointer< vtkFloatArray >::New();
tcoords->SetNumberOfComponents( 2 );
tcoords->InsertNextTuple2( 0, 0 );
tcoords->InsertNextTuple2( 1, 0 );
tcoords->InsertNextTuple2( 1, 1 );
tcoords->InsertNextTuple2( 0, 1 );
ret->GetPointData()->SetTCoords( tcoords );
vtkSmartPointer<vtkFloatArray> floatArray = vtkSmartPointer<vtkFloatArray>::New();
floatArray->SetNumberOfComponents( 3 );
for ( int i=0;i<4;++i )
{
floatArray->InsertNextTuple3( corners[3*i], corners[3*i+1], corners[3*i+2] );
}
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points->SetData( floatArray );
ret->SetPoints( points );
vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
cells->InsertNextCell( 4 );
cells->InsertCellPoint( 0 );
cells->InsertCellPoint( 1 );
cells->InsertCellPoint( 2 );
cells->InsertCellPoint( 3 );
ret->SetPolys( cells );
return ret;
}
void mainactual( void )
{
bool doVolumeRender = true;
bool doSurfaceRender = false;
typedef itk::Image<short,3> Image;
typedef itk::VTKImageExport< Image > Export;
typedef itk::ImageFileReader<Image> Reader;
// read image using ITK (this is essentially an nrrd-converted Visible Human CT "Male")
// https://mri.radiology.uiowa.edu/VHDicom/
// prefer using ITK for medical data
Reader::Pointer reader = Reader::New();
reader->SetFileName("D:/tmp/vhp_male.nrrd");
reader->Update();
// if input image orientation is not (1,0,0\0,1,0), user should handle
// the respective transform by using, e.g., vtkVolume::SetOrientation
Image::DirectionType dir = reader->GetOutput()->GetDirection();
std::cout << "0020|0037" << " ";
std::cout << dir(0,0) << "\\" << dir(1,0) << "\\" << dir(2,0) << "\\";
std::cout << dir(0,1) << "\\" << dir(1,1) << "\\" << dir(2,1);
std::cout << std::endl;
if ( !dir.GetVnlMatrix().is_identity( 0.0001 ) )
std::cout << "Warning, direction will be lost" << std::endl;
// import ITK image into VTK
vtkSmartPointer<vtkImageImport> imp = vtkSmartPointer<vtkImageImport>::New();
Export::Pointer exp = Export::New();
exp->SetInput( reader->GetOutput() );
imp->SetUpdateInformationCallback(exp->GetUpdateInformationCallback());
imp->SetPipelineModifiedCallback(exp->GetPipelineModifiedCallback());
imp->SetWholeExtentCallback(exp->GetWholeExtentCallback());
imp->SetSpacingCallback(exp->GetSpacingCallback());
imp->SetOriginCallback(exp->GetOriginCallback());
imp->SetScalarTypeCallback(exp->GetScalarTypeCallback());
imp->SetNumberOfComponentsCallback(exp->GetNumberOfComponentsCallback());
imp->SetPropagateUpdateExtentCallback(exp->GetPropagateUpdateExtentCallback());
imp->SetUpdateDataCallback(exp->GetUpdateDataCallback());
imp->SetDataExtentCallback(exp->GetDataExtentCallback());
imp->SetBufferPointerCallback(exp->GetBufferPointerCallback());
imp->SetCallbackUserData(exp->GetCallbackUserData());
imp->Update();
// this is our vtkImageData counterpart as it was read using VTK
vtkImageData * image = imp->GetOutput();
// create render window
vtkSmartPointer< vtkRenderer > ren = vtkSmartPointer< vtkRenderer >::New();
vtkSmartPointer< vtkRenderWindow > rw = vtkSmartPointer< vtkRenderWindow >::New();
rw->AddRenderer( ren );
rw->SetSize( 1024,1024 );
// create interactor used later
vtkSmartPointer< vtkRenderWindowInteractor > ia = vtkSmartPointer<vtkRenderWindowInteractor>::New();
ia->SetRenderWindow( rw );
ia->SetInteractorStyle( vtkSmartPointer< vtkInteractorStyleTrackballCamera >::New() );
ia->Initialize();
// define cutplane early on
vtkSmartPointer<vtkPlane > cutplane = vtkSmartPointer<vtkPlane>::New();
cutplane->SetNormal( 0,0,-1);
if ( doVolumeRender )
{
// set up some fancy volume rendering transfer function
vtkSmartPointer<vtkPiecewiseFunction> pw = vtkSmartPointer<vtkPiecewiseFunction>::New();
pw->AddPoint(-761.61130742049477, 0);
pw->AddPoint(-40.042402826855096, 0);
pw->AddPoint(353.54063604240287, 0.28817733990147787);
pw->AddPoint(1091.5088339222616, 0.69458128078817727);
pw->AddPoint(1763.8798586572439, 1);
vtkSmartPointer<vtkPiecewiseFunction> gf = vtkSmartPointer<vtkPiecewiseFunction>::New();
gf->AddPoint(-1024, 0);
gf->AddPoint(-1007.6007067137809, 1);
gf->AddPoint(-220.43462897526501, 0.78244274809160308);
gf->AddPoint(697.92579505300364, 0.9007633587786259);
gf->AddPoint(1157.1060070671379, 0.53435114503816794);
vtkSmartPointer<vtkColorTransferFunction> cf = vtkSmartPointer<vtkColorTransferFunction>::New();
cf->AddRGBPoint(-105.63957597173146, 0, 0, 0, 0.5, 0);
cf->AddRGBPoint(255.14487632508826, 0.93333299999999997, 0, 0, 0.5, 0);
cf->AddRGBPoint(353.54063604240287, 1, 0.90588199999999997, 0.66666700000000001, 0.5, 0);
cf->AddRGBPoint(632.32862190812716, 1, 0.66666666666666663, 0, 0.5, 0);
cf->AddRGBPoint(779.92226148409895, 1, 1, 1, 0.5, 0);
// and make GPUVolumeRayCastMapper to render them
typedef vtkGPUVolumeRayCastMapper Mapper;
vtkSmartPointer< Mapper > mapper = vtkSmartPointer< Mapper >::New();
mapper->SetInputData( image );
vtkSmartPointer< vtkVolumeProperty > prop = vtkSmartPointer< vtkVolumeProperty >::New();
prop->SetColor( cf );
prop->SetScalarOpacity( pw );
prop->SetGradientOpacity( gf );
prop->SetInterpolationTypeToLinear();
prop->SetDiffuse( 0 );
prop->SetSpecular( 0.0 );
prop->SetAmbient( 1 );
mapper->SetUseDepthPass( 1 );
mapper->SetUseJittering( 1 );
mapper->SetAutoAdjustSampleDistances( 0 );
vtkSmartPointer< vtkVolume > volume = vtkSmartPointer< vtkVolume >::New();
volume->SetMapper( mapper );
volume->SetProperty( prop );
// clip the volume
mapper->AddClippingPlane( cutplane );
ren->AddVolume( volume );
}
if ( doSurfaceRender )
{
// do marching cubes polygons
vtkSmartPointer<vtkImageMarchingCubes> mc = vtkSmartPointer<vtkImageMarchingCubes>::New();
mc->SetComputeGradients( 0 );
mc->SetComputeNormals( 0 );
mc->SetComputeScalars( 0 );
mc->SetInputData( image );
mc->SetNumberOfContours( 1 );
mc->SetValue( 0, 100.0 );
mc->Update();
vtkSmartPointer< vtkPolyDataMapper > surfmapper = vtkSmartPointer< vtkPolyDataMapper >::New();
vtkSmartPointer< vtkActor > surf = vtkSmartPointer< vtkActor >::New();
surf->SetMapper( surfmapper );
surfmapper->SetInputData( mc->GetOutput() );
surf->GetProperty()->SetAmbient(0);
surf->GetProperty()->SetDiffuse(1);
surf->GetProperty()->SetSpecular(0);
surf->GetProperty()->SetColor( 0.5, 0.9, 0.1 );
surfmapper->AddClippingPlane( cutplane );
ren->AddActor( surf );
}
// do the image slice plane
vtkSmartPointer< vtkImageReslice > slicer = vtkSmartPointer< vtkImageReslice >::New();
slicer->SetInputData( image );
vtkSmartPointer<vtkMatrix4x4> identity = vtkSmartPointer<vtkMatrix4x4>::New() ;
//identity->Identity(); // not needed as the orientation is identity anyways
slicer->SetResliceAxes( identity );
slicer->SetOutputDimensionality( 2 );
slicer->SetInterpolationModeToLinear();
vtkSmartPointer< vtkTexture > texture = vtkSmartPointer< vtkTexture > ::New();
vtkSmartPointer< vtkLookupTable > table = vtkSmartPointer< vtkLookupTable >::New();
table->SetNumberOfColors( 256 );
table->SetHueRange( 0.0, 0.0 );
table->SetSaturationRange( 0, 0 );
table->SetValueRange( 0.0, 1.0 );
table->SetAlphaRange( 1.0, 1.0 );
table->SetUseBelowRangeColor( 1 );
table->SetBelowRangeColor(0,0,0,0);
table->SetRange( -200, 200 );
table->Build();
texture->SetInputConnection( slicer->GetOutputPort() );
texture->SetLookupTable( table );
texture->SetColorModeToMapScalars();
vtkSmartPointer< vtkPolyDataMapper > polymapper = vtkSmartPointer< vtkPolyDataMapper >::New();
vtkSmartPointer< vtkActor > plane = vtkSmartPointer< vtkActor >::New();
plane->SetMapper( polymapper );
plane->GetProperty()->SetTexture(0, texture );
plane->GetProperty()->SetAmbient(1);
plane->GetProperty()->SetDiffuse(0);
plane->GetProperty()->SetSpecular(0);
plane->GetProperty()->SetAmbientColor(1,1,1);
plane->GetProperty()->SetEdgeColor(1,0,0);
plane->GetProperty()->SetEdgeVisibility(1);
ren->AddActor( plane );
int extent[6];
double origin[3];
double spacing[3];
image->GetOrigin( origin );
image->GetSpacing( spacing );
image->GetExtent( extent );
{
std::stringstream s;
std::cout << "0018|0050" << " ";
std::cout << spacing[2];
std::cout << std::endl;
}
{
std::cout << "0028|0030" << " ";
std::cout << spacing[0] << "\\" << spacing[1];
std::cout << std::endl;
}
ren->ResetCamera(); // before looping, hope there is either surface or volume
int z = (extent[5]-extent[4])/2;
// for ( ... )
{
// DICOM pixel corner
double corner[3]={ origin[0], origin[1], origin[2] + (double)z * spacing[2] };
std::cout << "0020|0032" << " ";
std::cout << corner[0] << "\\" << corner[1] << "\\" << corner[2];
std::cout << std::endl;
slicer->SetResliceAxesOrigin( corner );
double corners[12];
// remove the half spacing to make DICOM / ITK / VTK origin to OpenGL origin
corners[0]=corner[0]-0.5*spacing[0];
corners[1]=corner[1]-0.5*spacing[1];
corners[2]=corner[2]; // the slicing direction
// +1 are to convert from DICOM pixel coordinates to OpenGL bounds
const double vx[3]={ (double)(extent[1]-extent[0]+1)*spacing[0], 0.0, 0.0 };
const double vy[3]={ 0.0, (double)(extent[3]-extent[2]+1)*spacing[1], 0.0} ;
for ( unsigned int u=0;u<3;++u )
{
corners[ 3+ u ] = corners[u] + vx[u];
corners[ 6+ u ] = corners[u] + vx[u] + vy[u];
corners[ 9+ u ] = corners[u] + vy[u];
}
cutplane->SetOrigin( corner );
vtkSmartPointer< vtkPolyData > polys = MyCreateSimplePlane( corners );
polymapper->SetInputData( polys );
ia->Render();
}
ia->Start();
}
* UPDATE *
Note: the direction must be processed separately, e.g., via SetUserTransform for both the plane and the volume, or resampling of the imagedata. Two images shown below are sliced similarly along the outermost image dimension, and not along the worldspace z-axis:
void CMFCApplication1Dlg::setSliceImage()
{
int extent[6];
double spacing[3];
double origin[3];
//m_vtkVolumeImageData is vtkImageData(Save Slice Images)
//Same as dicomReader->GetOutput();
m_vtkVolumeImageData->GetExtent(extent);
m_vtkVolumeImageData->GetSpacing(spacing);
m_vtkVolumeImageData->GetOrigin(origin);
double center[3];
center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]);
center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]);
center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]);
// Matrices for axial, coronal, sagittal, oblique view orientations
static double axialElements[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
static double coronalElements[16] = {
1, 0, 0, 0,
0, 0, 1, 0,
0,-1, 0, 0,
0, 0, 0, 1 };
static double sagittalElements[16] = {
0, 0,-1, 0,
1, 0, 0, 0,
0,-1, 0, 0,
0, 0, 0, 1 };
static double obliqueElements[16] = {
1, 0, 0, 0,
0, 0.866025, -0.5, 0,
0, 0.5, 0.866025, 0,
0, 0, 0, 1 };
// Set the slice orientation
vtkSmartPointer<vtkMatrix4x4> resliceAxes = vtkSmartPointer<vtkMatrix4x4>::New();
resliceAxes->DeepCopy(axialElements);
// Set the point through which to slice
resliceAxes->SetElement(0, 3, 0);
resliceAxes->SetElement(1, 3, 0);
resliceAxes->SetElement(2, 3, 70);
// Extract a slice in the desired orientation
vtkSmartPointer<vtkImageReslice> reslice = vtkSmartPointer<vtkImageReslice>::New();
reslice->SetInputData(m_vtkVolumeImageData);
reslice->SetOutputDimensionality(2);
reslice->SetResliceAxes(resliceAxes);
reslice->SetInterpolationModeToLinear();
reslice->Update();
// Create a lookup table
table = vtkSmartPointer<vtkLookupTable>::New();
// image intensity range
// my image is USHORT TYPE, so I Set 0 ~ 65535
table->SetRange(0, 65535);
table->SetNumberOfTableValues(65536);
double red = 0, green = 0, blue = 0;
for (int i = 0; i < table->GetNumberOfTableValues(); i++)
{
// 0~19999 value have Black ~ Red
if (i < 20000)
red += 1.0 / 20000;
// 20000~39999 value have Red ~ Yellow
else if (i < 40000)
green += 1.0 / 20000;
// 40000~59999 value have Yellow ~ white
// and 60000~ have white
else if (i < 60000)
blue += 1.0 / 20000;
table->SetTableValue(i, red, green, blue, 1);
}
table->Build();
//// Map the image through the lookup table
vtkSmartPointer<vtkImageMapToColors> color = vtkSmartPointer<vtkImageMapToColors>::New();
color->SetLookupTable(table);
color->SetInputData(reslice->GetOutput());
color->Update();
vtkSmartPointer<vtkDataSetMapper> mapper = vtkSmartPointer<vtkDataSetMapper>::New();
mapper->SetInputData(color->GetOutput());
//Setting ResliceImagePlaneActor
//Location, Opacity, Connect Mapper and Actor
double position[3] = { 0, 0, 70 };
vtkSmartPointer<vtkActor> nomal_actor = vtkSmartPointer<vtkActor>::New();
nomal_actor->SetMapper(mapper);
nomal_actor->GetProperty()->SetOpacity(0.7);
nomal_actor->SetPosition(position);
m_vtkRenderer->AddActor(nomal_actor);
//Setting ReslicePlaneOutLineActor
vtkSmartPointer<vtkOutlineFilter> sliceOutlineFilter = vtkSmartPointer<vtkOutlineFilter>::New();
sliceOutlineFilter->SetInputData(color->GetOutput());
sliceOutlineFilter->Update();
vtkSmartPointer<vtkPolyDataMapper> sliceOutlineMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
sliceOutlineMapper->SetInputData(sliceOutlineFilter->GetOutput());
vtkSmartPointer<vtkActor> vtksliceOutlineActor = vtkSmartPointer<vtkActor>::New();
vtksliceOutlineActor->SetMapper(sliceOutlineMapper);
vtksliceOutlineActor->GetProperty()->SetColor(1, 0, 0);
vtksliceOutlineActor->SetPosition(position);
m_vtkRenderer->AddActor(vtksliceOutlineActor);
//Setting ScalarBarActor
//It is fine to Skip
vtkSmartPointer<vtkScalarBarActor> scalarBar = vtkSmartPointer<vtkScalarBarActor>::New();
scalarBar->SetLookupTable(table);
scalarBar->SetTitle("value");
scalarBar->SetNumberOfLabels(10);
scalarBar->SetLabelFormat("%10.2f");
scalarBar->SetHeight(.2);
scalarBar->SetWidth(.2);
m_vtkRenderer->AddActor2D(scalarBar);
}
This is My fullCode
Check it and if you don't understand some parts, Post a comment plz :)
I just implemented bicubic interpolation for resizing images.
I have a test image 6x6 pixels (grayscale), its columns are black and white (x3).
I am comparing the results of my code with the results from the tool ffmpeg and they are not correct. I can not understand why, I think I may be calculating the neighbourhood of pixels wrong or maybe the distance of the resized pixel to the original ones.
Can someone look into my code (I will simplify it for better reading) and tell me where the error is?
// Iterate through each line
for(int lin = 0; lin < dstHeight; lin++){
// Original coordinates
float linInOriginal = (lin - 0.5) / scaleHeightRatio;
// Calculate original pixels coordinates to interpolate
int linTopFurther = clamp(floor(linInOriginal) - 1, 0, srcHeight - 1);
int linTop = clamp(floor(linInOriginal), 0, srcHeight - 1);
int linBottom = clamp(ceil(linInOriginal), 0, srcHeight - 1);
int linBottomFurther = clamp(ceil(linInOriginal) + 1, 0, srcHeight - 1);
// Calculate distance to the top left pixel
float linDist = linInOriginal - floor(linInOriginal);
// Iterate through each column
for(int col = 0; col < dstWidth; col++){
// Original coordinates
float colInOriginal = (col - 0.5) / scaleWidthRatio;
// Calculate original pixels coordinates to interpolate
int colLeftFurther = clamp(floor(colInOriginal) - 1, 0, srcWidth - 1);
int colLeft = clamp(floor(colInOriginal), 0, srcWidth - 1);
int colRight = clamp(ceil(colInOriginal), 0, srcWidth - 1);
int colRightFurther = clamp(ceil(colInOriginal) + 1, 0, srcWidth - 1);
// Calculate distance to the top left pixel
float colDist = colInOriginal - floor(colInOriginal);
// Gets the original pixels values
// 1st row
uint8_t p00 = srcSlice[0][linTopFurther * srcWidth + colLeftFurther];
// ...
// 2nd row
uint8_t p01 = srcSlice[0][linTop * srcWidth + colLeftFurther];
// ...
// 3rd row
// ...
// 4th row
// ...
// Bilinear interpolation operation
// Y
float value = cubicInterpolate(
cubicInterpolate(static_cast<float>(p00), static_cast<float>(p10), static_cast<float>(p20), static_cast<float>(p30), colDist),
cubicInterpolate(static_cast<float>(p01), static_cast<float>(p11), static_cast<float>(p21), static_cast<float>(p31), colDist),
cubicInterpolate(static_cast<float>(p02), static_cast<float>(p12), static_cast<float>(p22), static_cast<float>(p32), colDist),
cubicInterpolate(static_cast<float>(p03), static_cast<float>(p13), static_cast<float>(p23), static_cast<float>(p33), colDist),
linDist);
dstSlice[0][lin * dstWidth + col] = double2uint8_t(clamp(value, 0.0f, 255.0f));
}
}
I was forgetting to set the values of the second degree variables of the interpolation matrix. They were set to 0, so the resulting interpolation would resemble the bilinear interpolation.
I am using the OpenCV function decomposeHomographyMat(). Because I want to decompose my H1 and H2 matrices I have got from the OpenCV function stereoRectifyUncalibrated().
Just for testing I wanted to multiply the rotation and translation back together to see if the result are the same and everything works correct.
decomposeHomographyMat(H1, Mat(Identity), result_rot, result_trans, result_normals);
Matx44f rotM(result_rot[id].at<float>(0,0), result_rot[id].at<float>(0,0), result_rot[id].at<float>(0,0), 0,
result_rot[id].at<float>(0,0), result_rot[id].at<float>(0,0), result_rot[id].at<float>(0,0), 0,
result_rot[id].at<float>(0,0), result_rot[id].at<float>(0,0), result_rot[id].at<float>(0,0), 0,
0, 0, 0, 1);
Matx44f transM(1, 0, 0, result_trans[id].at<float>(0,0),
0, 1, 0, result_trans[id].at<float>(0,1),
0, 0, 1, result_trans[id].at<float>(0,2),
0, 0, 0, 1);
H1 = Mat(transM) * Mat(rotM);
I know that for the rotM matrix I am referencing the same value all the time, this is jut temporary.
My problem is, that if I print out result_rot[id] and rotM I get weir result:
result_rot[id]: [0.9920521909211247, -0.1258265282306712, 0.0003678069998755973;
0.1258266147675855, 0.9920522232074044, -0.0002223635722913286;
-0.0003369043855492455, 0.0002668761040765017, 0.9999999076363849]
rotM: [-0.015474273, -0.015474273, -0.015474273, 0;
-0.015474273, -0.015474273, -0.015474273, 0;
-0.015474273, -0.015474273, -0.015474273, 0;
0, 0, 0, -0.99892187]
The value -0.015474273 is not even in the original rotationmatrix and the value 1 seems to be stored as -0.99892187.
I also tried to replace float with double, then the result changes again but still not correct.
How can I properly get the correct value that was stored in that matrix? and what is going wrong here?
I'm trying to export application data as xls chart.
I'm doing it using SimpleXlsxWriter.
But I have some problems with this lib.
Here's what I got using this lib
#include <Xlsx/Workbook.h>
int main()
{
using namespace SimpleXlsx;
CWorkbook book;
CWorksheet &data = book.AddSheet("Data");
std::vector<CellDataDbl> header = { 1, 3, 5, 6, 8 };
std::vector<CellDataDbl> data1 = { 2, 6, 4, 8, 5 };
std::vector<CellDataDbl> data2 = { 5, 3, 5, 2, 4 };
data.AddRow(header);
data.AddRow(data1);
data.AddRow(data2);
CChartsheet &chart = book.AddChart("Chart", CHART_BAR);
chart.SetBarDirection(CChartsheet::BAR_DIR_VERTICAL);
chart.SetBarGrouping(CChartsheet::BAR_GROUP_STACKED);
chart.SetTableDataState(CChartsheet::TBL_DATA);
chart.SetLegendPos(CChartsheet::EPosition::POS_TOP);
CChartsheet::Series ser1;
ser1.valAxisFrom = CellCoord(1, 0);
ser1.valAxisTo = CellCoord(1, data1.size() - 1);
ser1.valSheet = &data;
ser1.title = "Ser1";
CChartsheet::Series ser2;
ser2.valAxisFrom = CellCoord(2, 0);
ser2.valAxisTo = CellCoord(2, data2.size() - 1);
ser2.valSheet = &data;
ser2.title = "Ser2";
chart.AddSeries(ser1);
chart.AddSeries(ser2);
book.Save("test.xls");
}
I didn't find the ways how to show y-axes, show horizontal lines, set x-axes values, insert the chart into the data sheet, etc.
Here's what do I want to get as a result
What a library can help me or how can I get a required result? Maybe SimpleXlsxWriter can do that?
You can get what you want from the SimpleXlsxWriter, just with a few modifications of your code. Briefly:
save with SimpleXlsxWriter's format .xlsx not .xls
Add a category axis to one of your series and set it to first data row
use BAR_GROUP_PERCENT_STACKED since you want the y axis to be on % scale
You can control the order you add the series the the chart, to get the order you want in the bottom table
Consider this code, suggested modifications are indicated in comments:
int main()
{
using namespace SimpleXlsx;
CWorkbook book;
CWorksheet &data = book.AddSheet("Data");
std::vector<CellDataDbl> header = { 1, 3, 5, 6, 8 };
std::vector<CellDataDbl> data1 = { 2, 6, 4, 8, 5 };
std::vector<CellDataDbl> data2 = { 5, 3, 5, 2, 4 };
data.AddRow(header);
data.AddRow(data1);
data.AddRow(data2);
CChartsheet &chart = book.AddChart("Chart", CHART_BAR);
chart.SetBarDirection(CChartsheet::BAR_DIR_VERTICAL);
chart.SetBarGrouping(CChartsheet::BAR_GROUP_PERCENT_STACKED); // <-- seems you want this format?
chart.SetTableDataState(CChartsheet::TBL_DATA);
chart.SetLegendPos(CChartsheet::EPosition::POS_TOP);
chart.SetYAxisGrid(CChartsheet::EGridLines::GRID_MAJOR); // <-- to draw the horizontal lines
CChartsheet::Series ser1;
ser1.valSheet = &data;
ser1.valAxisFrom = CellCoord(1, 0);
ser1.valAxisTo = CellCoord(1, data1.size() - 1);
ser1.title = "Ser1";
// Now add a category axis from your the first row of the data sheet
ser1.catAxisFrom = CellCoord(0, 0);
ser1.catAxisTo = CellCoord(0, data1.size() - 1);
ser1.catSheet = &data;
CChartsheet::Series ser2;
ser2.valSheet = &data;
ser2.valAxisFrom = CellCoord(2, 0);
ser2.valAxisTo = CellCoord(2, data2.size() - 1);
ser2.title = "Ser2";
// insert the series in the order you want from the bottom up in the chart's table
chart.AddSeries(ser2);
chart.AddSeries(ser1);
book.Save("c:\\so\\test.xlsx"); // <-- Save as xlsx, not xls
}
There are two vectors "ImgCoordinatesList" & "ImgCoordinatesListCopy".
The first vector continuously receives values which are pushbacked.(x,y,Type - I have previously defined a struct which has int,int,string and its object is list_obj)
Once the function "ImgCreation" is called, the contents of the first vector should be copied into the "ImgCoordinatesListCopy" vector and the contents inside this copy vector should be used for further processing.
But the method what i did produced errors.
void SliceImageNew::BuildImgCoordinatesList(const long X, const long Y, string Type)
{
//Creates the Structure for one x,y,type coordinate
list_obj.x = X;
list_obj.y = Y;
list_obj.Coordinates_Type = Type;
//Pushes the structure into a vector which eventually forms a vector of structs
ImgCoordinatesList.push_back(list_obj);
}
void SliceImageNew::ImgCreation()
{
ImgCoordinatesListCopy.resize(ImgCoordinatesList.size());
copy(ImgCoordinatesList.begin(), ImgCoordinatesList.end(), ImgCoordinatesListCopy.begin());
//ImgCoordinatesListCopy = ImgCoordinatesList; //Copy the contents into another vector and create image with the copy vector
ImgCoordinatesList.erase(ImgCoordinatesList.begin(), ImgCoordinatesList.end());//Clear the vector after copying the contents into another vector
PlotImgCoordinates();
//SaveSliceImg();
//ClearImgCoordinatesList();
}
void SliceImageNew::PlotImgCoordinates()
{
static int SliceImgCount = 1;
Mat SliceImg(Size(1920, 1080), CV_16UC3); // Blank Image with White Background and 1920*1080 Dimensions
for (int i = 1; i!=ImgCoordinatesListCopy.size(); i++)
{
//Color differentiation between Mark and Jump Lines
if (ImgCoordinatesListCopy[i].Coordinates_Type == "Mark")
{
//cout << "This is a mark line" << endl;
line(SliceImg, Point(ImgCoordinatesListCopy[i - 1].x, ImgCoordinatesListCopy[i - 1].y), Point(ImgCoordinatesListCopy[i].x, ImgCoordinatesListCopy[i].y), Scalar(255, 255, 155), 4, 2, 0);
}
else
{
//cout << "This is a jump line" << endl;
line(SliceImg, Point(ImgCoordinatesListCopy[i - 1].x, ImgCoordinatesListCopy[i - 1].y), Point(ImgCoordinatesListCopy[i].x, ImgCoordinatesListCopy[i].y), Scalar(255, 100, 155), 4, 2, 0);
}
}
//Creating Legends for the Plot
putText(SliceImg, "Mark Line", cvPoint(1600, 40),
FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 2);
line(SliceImg, Point(1540, 35), Point(1590, 35), Scalar(255, 255, 155), 4, 2, 0);
putText(SliceImg, "Jump Line", cvPoint(1600, 80),
FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 2);
line(SliceImg, Point(1540, 75), Point(1590, 75), Scalar(255, 100, 155), 4, 2, 0);
//Providing unique names for every picture that is being saved
name << "Slice" << SliceImgCount << ".jpg";
// Saving the image
imwrite(name.str(), SliceImg);
SliceImgCount++; //Increment the count to provide unique names to the images
waitKey(0);
}
I have attached an image which shows the code and the error generated
While debugging, the highlighted line in the image produced that error!
Could someone help me?
Found the solution!
I had messed up with the object declaration of the "SliceImageNew" class. After correction it works fine and draws the image perfectly.