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 :)
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.
I am playing around with the QCustomPlot libary for Qt. I created some plots succesfully. But I got still questions:
1: How can I set my y-axis range from 0% to 100%?
2: My tick labels are centered below the ticks. How can I change that to a left alignment?
Thanks for your help.
Peter
// generate some data:
QVector<double> x(101), y(101); // initialize with entries 0..100
for (int i=0; i<101; ++i)
{
x[i] = (i*960); // x goes from -1 to 1
y[i] = x[i]/96000.0; // let's plot a quadratic function
}
// create graph and assign data to it:
ui->customPlot->addGraph();
ui->customPlot->graph(0)->setData(x, y);
// set axes ranges, so we see all data:
ui->customPlot->xAxis->setTickLabelType(QCPAxis::ltDateTime);
ui->customPlot->xAxis->setDateTimeSpec(Qt::UTC);
ui->customPlot->xAxis->setDateTimeFormat("hh:mm");
ui->customPlot->xAxis->setAutoTickStep(false);
ui->customPlot->xAxis->setTickStep(3600);
ui->customPlot->xAxis->setRange(0, 86399);
ui->customPlot->yAxis->setRange(0, 1);
ui->customPlot->replot();
ui->customPlot->xAxis->setBasePen(QPen(Qt::white, 1));
ui->customPlot->yAxis->setBasePen(QPen(Qt::white, 1));
ui->customPlot->xAxis->setTickPen(QPen(Qt::white, 1));
ui->customPlot->yAxis->setTickPen(QPen(Qt::white, 1));
ui->customPlot->xAxis->setSubTickPen(QPen(Qt::white, 1));
ui->customPlot->yAxis->setSubTickPen(QPen(Qt::white, 1));
ui->customPlot->xAxis->setTickLabelColor(Qt::white);
ui->customPlot->yAxis->setTickLabelColor(Qt::white);
ui->customPlot->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
ui->customPlot->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
ui->customPlot->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
ui->customPlot->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
ui->customPlot->xAxis->grid()->setSubGridVisible(true);
ui->customPlot->yAxis->grid()->setSubGridVisible(true);
ui->customPlot->xAxis->grid()->setZeroLinePen(Qt::NoPen);
ui->customPlot->yAxis->grid()->setZeroLinePen(Qt::NoPen);
ui->customPlot->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
ui->customPlot->yAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
You should solve both questions with
ui->customPlot->yAxis->setRange(0, 100, Qt::AlignLeft);
EDIT: to show custom text for ticks, you should add this code:
QVector<double> TickValues;
QVector<QString> TickLabels;
// you can safely change the values according to the output
TickValues << 0 << 20 << 40 << 60 << 80 << 100;
TickLabels << "0" << "20%" << "40%" << "60%" << "80%" << "100%";
// disable default ticks and their labels
ui->customPlot->yAxis->setAutoTicks(false);
ui->customPlot->yAxis->setAutoTickLabels(false);
// add your custom values and labels
ui->customPlot->yAxis->setTickVector(TicksValues);
ui->customPlot->yAxis->setTickVectorLabels(TickLabels);
I am trying to obtain an increment that goes up from 0 to n, then decreases from n-1 to 0, and repeats the cycle over and over.
In this example written in Processing, I would like the background to go from black(i=0) to white(i=255) incrementally then white to black incrementally and so forth. Now I only get it to go from black to white, and then it comes back to black suddenly.
int i = 0;
void setup(){
size(640, 360);
frameRate(60);
}
void draw(){
background(i);
i++;
if(i==256){i=0;}
}
Try -
int change = 1;
void draw(){
background(i);
i = i + change;
if(i==256){change = -1;}
if(i==0){change = 1;}
}
Another way to look at this question would be: "How could I draw a triangle wave?".
I like this way cause it does not need "ifs". Some thing like this would do.
triangleWave = maxNumber - abs(incrementedVar % (2*maxNumber) - maxNumber);
Coll, isn't it?
I have this old code using this, it's not drawing the wave, but using it for size and fill color. Also there is a sine wave for comparision. Check it out:
float zigZag, toIncrement, speed =1, maxNumber = 255;
float sine, x = 270, speed2 = 1;
void setup() {
size(800, 400);
background(255);
}
void draw() {
background(255);
//triangle wave
toIncrement+=speed;
zigZag = maxNumber - abs(toIncrement % (2*maxNumber) - maxNumber);
fill(zigZag);
noStroke();
ellipse( 150, height/2+100, 50, 50);
strokeWeight(zigZag);
stroke(0);
line(100, height/2-100, 200, height/2-100);
text("triangle = " + int(zigZag), 100, height-30);
println("triangle wave value = " + zigZag);
//sine wave
x+=speed2;
sine = (1+sin(radians(x)))*(maxNumber/2);
fill(sine);
noStroke();
ellipse( 650, height/2+100, 50, 50);
strokeWeight(sine);
stroke(0);
line(600, height/2-100, 700, height/2-100);
fill(80);
text("sine = " + int(sine), 600, height-30);
}
I want to get the RGB value of the top/left pixel (0;0) of the whole x11 display.
what I've got so far:
XColor c;
Display *d = XOpenDisplay((char *) NULL);
XImage *image;
image = XGetImage (d, RootWindow (d, DefaultScreen (d)), x, y, 1, 1, AllPlanes, XYPixmap);
c->pixel = XGetPixel (image, 0, 0);
XFree (image);
XQueryColor (d, DefaultColormap(d, DefaultScreen (d)), c);
cout << c.red << " " << c.green << " " << c.blue << "\n";
but I need those values to be 0..255 or (0.00)..(1.00), while they look like 0..57825, which is no format I recognize.
also, copying the whole screen just to get one pixel is very slow. as this will be used in a speed-critical environment, I'd appreciate if someone knows a more performant way to do this. Maybe using XGetSubImage of a 1x1 size, but I'm very bad at x11 development and don't know how to implement that.
what shall I do?
I took your code and got it to compile. The values printed (scaled to 0-255) give me the same values as I set to the desktop background image.
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;
int main(int, char**)
{
XColor c;
Display *d = XOpenDisplay((char *) NULL);
int x=0; // Pixel x
int y=0; // Pixel y
XImage *image;
image = XGetImage (d, XRootWindow (d, XDefaultScreen (d)), x, y, 1, 1, AllPlanes, XYPixmap);
c.pixel = XGetPixel (image, 0, 0);
XFree (image);
XQueryColor (d, XDefaultColormap(d, XDefaultScreen (d)), &c);
cout << c.red/256 << " " << c.green/256 << " " << c.blue/256 << "\n";
return 0;
}
From the XColor(3) man page:
The red, green, and blue values are always in the range 0 to 65535 inclusive, independent of the number of bits actually used in the display hardware. The server scales these values down to the range used by the hardware. Black is represented by (0,0,0), and white is represented by (65535,65535,65535). In some functions, the flags member controls which of the red, green, and blue members is used and can be the inclusive OR of zero or more of DoRed, DoGreen, and DoBlue.
So you must scale these values to whatever range you want.