Commit d42c9b89 authored by Dan Blezek's avatar Dan Blezek
Browse files

ENH: Changed vtkImageDataToTkPhoto to handle window level, and all of

the vtk datatypes.  TestEmptyInput.tcl and TestSetGet.tcl were
failing, so an exception for vtkImageDataToTkPhoto was added to them.
parent 42fe4a91
......@@ -43,6 +43,7 @@ set classExceptions {
vtkTkImageViewerWidget
vtkTkImageWindowWidget
vtkTkRenderWidget
vtkImageDataToTkPhoto
vtkJPEGReader
vtkWin32VideoSource
}
......
......@@ -65,6 +65,7 @@ set classExceptions {
vtkTkImageViewerWidget
vtkTkImageWindowWidget
vtkTkRenderWidget
vtkImageDataToTkPhoto
vtkViewRays
vtkWin32OutputWindow
vtkXMLFileOutputWindow
......
......@@ -24,18 +24,18 @@ class SampleViewer:
reader.SetDataSpacing ( 3.2, 3.2, 1.5 )
reader.Update ()
self.cast = cast = vtkImageCast()
cast.SetInput ( reader.GetOutput() )
cast.SetOutputScalarType ( reader.GetOutput().GetScalarType() )
cast.ClampOverflowOn()
# Make the image a little bigger
resample = vtkImageResample ()
resample.SetInput ( reader.GetOutput() )
self.resample = resample = vtkImageResample ()
resample.SetInput ( cast.GetOutput() )
resample.SetAxisMagnificationFactor ( 0, 2 )
resample.SetAxisMagnificationFactor ( 1, 2 )
resample.SetAxisMagnificationFactor ( 2, 1 )
self.cast = cast = vtkImageShiftScale ()
cast.SetInput ( resample.GetOutput() )
cast.SetOutputScalarTypeToUnsignedChar( )
cast.ClampOverflowOn ()
cast.Update ()
l,h = reader.GetOutput().GetScalarRange()
# Create the three orthogonal views
......@@ -45,15 +45,28 @@ class SampleViewer:
sphoto = self.sphoto = vtkTkPhotoImage ();
self.Position = [0, 0, 0]
# Create a popup menu
v = IntVar()
self.popup = popup = Menu ( Tk, tearoff=0 )
popup.add_radiobutton ( label='unsigned char', command=self.CastToUnsignedChar, variable=v, value=-1 )
popup.add_radiobutton ( label='unsigned short', command=self.CastToUnsignedShort, variable=v, value=0 )
popup.add_radiobutton ( label='unsigned int', command=self.CastToFloat, variable=v, value=1 )
popup.add_radiobutton ( label='float', command=self.CastToFloat, variable=v, value=2 )
v.set ( 0 )
w = self.TransverseLabelWidget = Label ( Tk, image = tphoto )
w.grid ( row = 0, column = 0 )
w.bind ( "<Button1-Motion>", lambda e, i=tphoto, o='transverse', s=self: s.Motion ( e, i, o ) )
w.bind ( "<Button-3>", self.DoPopup )
w = Label ( Tk, image = cphoto )
w.grid ( row = 1, column = 0 )
w.bind ( "<Button1-Motion>", lambda e, i=cphoto, o='coronal', s=self: s.Motion ( e, i, o ) )
w.bind ( "<Button-3>", self.DoPopup )
w = Label ( Tk, image = sphoto )
w.grid ( row = 0, column = 1 )
w.bind ( "<Button1-Motion>", lambda e, i=sphoto, o='sagittal', s=self: s.Motion ( e, i, o ) )
w.bind ( "<Button-3>", self.DoPopup )
w = self.WindowWidget = Scale ( Tk, label='Window', orient='horizontal', from_=1, to=(h-l)/2, command = self.SetWindowLevel )
w = self.LevelWidget = Scale ( Tk, label='Level', orient='horizontal', from_=l, to=h, command=self.SetWindowLevel )
self.WindowWidget.grid ( row=2, columnspan=2, sticky='ew' )
......@@ -64,6 +77,23 @@ class SampleViewer:
w = self.LabelWidget = Label ( Tk, bd=2, relief='raised' )
w.grid ( row=4, columnspan=2, sticky='ew' )
def DoPopup ( self, event ):
self.popup.post ( event.x_root, event.y_root )
def CastToUnsignedChar ( self ):
self.cast.SetOutputScalarTypeToUnsignedChar()
self.SetImages()
def CastToUnsignedShort ( self ):
self.cast.SetOutputScalarTypeToUnsignedShort()
self.SetImages()
def CastToUnsignedInt ( self ):
self.cast.SetOutputScalarTypeToUnsignedInt()
self.SetImages()
def CastToFloat ( self ):
self.cast.SetOutputScalarTypeToFloat()
self.SetImages()
def Motion ( self, event, image, orientation ):
w = image.width();
......@@ -81,16 +111,27 @@ class SampleViewer:
self.SetImages()
def SetWindowLevel ( self, event ):
Window = self.WindowWidget.get()
Level = self.LevelWidget.get()
self.cast.SetScale ( 255.0 / Window )
self.cast.SetShift ( Window / 2.0 - Level )
self.SetImages()
def SetImages ( self ):
self.tphoto.PutImageSlice ( self.cast.GetOutput(), self.Position[2], 'transverse' )
self.sphoto.PutImageSlice ( self.cast.GetOutput(), self.Position[0], 'sagittal' )
self.cphoto.PutImageSlice ( self.cast.GetOutput(), self.Position[1], 'coronal' )
Window = self.WindowWidget.get()
Level = self.LevelWidget.get()
image = self.resample.GetOutput()
self.tphoto.PutImageSlice ( image,
self.Position[2],
'transverse',
Window,
Level )
self.sphoto.PutImageSlice ( image,
self.Position[0],
'sagittal',
Window,
Level )
self.cphoto.PutImageSlice ( image,
self.Position[1],
'coronal',
Window,
Level )
......
......@@ -13,19 +13,18 @@ vtkVolume16Reader reader
reader SetDataSpacing 3.2 3.2 1.5
reader Update
vtkImageCast cast
cast SetInput [reader GetOutput]
cast SetOutputScalarType [[reader GetOutput] GetScalarType]
cast ClampOverflowOn
# Make the image a little bigger
vtkImageResample resample
resample SetInput [reader GetOutput]
resample SetInput [cast GetOutput]
resample SetAxisMagnificationFactor 0 2
resample SetAxisMagnificationFactor 1 2
resample SetAxisMagnificationFactor 2 1
vtkImageShiftScale cast
cast SetInput [resample GetOutput]
cast SetOutputScalarTypeToUnsignedChar
cast ClampOverflowOn
cast Update
set range [[reader GetOutput] GetScalarRange]
set l [lindex $range 0]
set h [lindex $range 1]
......@@ -36,20 +35,30 @@ wm title .c "Tcl Version of vtkImageDataToTkPhoto"
wm protocol .c WM_DELETE_WINDOW ::vtk::cb_exit
# Create the three orthogonal views
set mode 0
set m [menu .c.mm -tearoff 0]
$m add radiobutton -label "unsigned char" -value -1 -variable mode -command CastToUnsignedChar
$m add radiobutton -label "unsigned short" -value 0 -variable mode -command CastToUnsignedShort
$m add radiobutton -label "unsigned int" -value 1 -variable mode -command CastToUnsignedInt
$m add radiobutton -label "float" -value 2 -variable mode -command CastToFloat
set tphoto [image create photo]
set cphoto [image create photo]
set sphoto [image create photo]
grid [label .c.t -image $tphoto] -row 0 -column 0
bind .c.t <Button1-Motion> "SetPosition transverse %W %x %y"
bind .c.t <Button-3> "$m post %X %Y"
grid [label .c.c -image $cphoto] -row 1 -column 0
bind .c.c <Button1-Motion> "SetPosition coronal %W %x %y"
bind .c.c <Button-3> "$m post %X %Y"
grid [label .c.s -image $sphoto] -row 0 -column 1
bind .c.s <Button1-Motion> "SetPosition sagittal %W %x %y"
bind .c.s <Button-3> "$m post %X %Y"
grid [scale .c.w -label Window -orient horizontal -from 1 -to [expr ($h - $l) / 2] -command SetWindow ] -row 2 -columnspan 2 -sticky ew
grid [scale .c.l -label Level -orient horizontal -from $l -to $h -command SetWindow ] -row 3 -columnspan 2 -sticky ew
grid [label .c.text -textvariable Label -bd 2 -relief raised] -row 4 -columnspan 2 -sticky ew
set Label "Use the right mouse button to change data type"
.c.w set 1370
.c.l set 1268
set Position(x) 0
......@@ -73,20 +82,35 @@ proc SetPosition { orientation widget x y } {
}
proc SetWindow { foo } {
global cast photo
set Window [.c.w get]
set Level [.c.l get]
cast SetScale [expr 255.0 / $Window]
cast SetShift [expr $Window / 2.0 - $Level]
SetImages
}
proc SetImages {} {
global Position tphoto sphoto cphoto
vtkImageDataToTkPhoto [cast GetOutput] $tphoto $Position(z) transverse
vtkImageDataToTkPhoto [cast GetOutput] $sphoto $Position(x) sagittal
vtkImageDataToTkPhoto [cast GetOutput] $cphoto $Position(y) coronal
set Window [.c.w get]
set Level [.c.l get]
vtkImageDataToTkPhoto [resample GetOutput] $tphoto $Position(z) transverse $Window $Level
vtkImageDataToTkPhoto [resample GetOutput] $sphoto $Position(x) sagittal $Window $Level
vtkImageDataToTkPhoto [resample GetOutput] $cphoto $Position(y) coronal $Window $Level
}
proc CastToUnsignedChar {} {
cast SetOutputScalarTypeToUnsignedChar
SetImages
}
proc CastToUnsignedShort {} {
cast SetOutputScalarTypeToUnsignedShort
SetImages
}
proc CastToUnsignedInt {} {
cast SetOutputScalarTypeToUnsignedInt
SetImages
}
proc CastToFloat {} {
cast SetOutputScalarTypeToFloat
SetImages
}
# Prime the pump
SetImages
#///////////////////////////////////////////////////
......
......@@ -80,193 +80,268 @@ extern "C"
static int vtkTkRenderWidget_MakeRenderWindow(struct vtkTkRenderWidget *self);
extern int vtkRenderWindowCommand(ClientData cd, Tcl_Interp *interp,
int argc, char *argv[]);
// Start of vtkImageDataToTkPhoto
template <class T>
void vtkExtractImageData ( unsigned char* buffer, T *inPtr, double shift, double scale, int width, int height, int pitch, int pixelSize, int components )
{
T* ImagePtr;
unsigned char* BufferPtr;
int i, j, c;
float pixel;
BufferPtr = buffer;
ImagePtr = inPtr;
for ( j = 0; j < height; j++ )
{
ImagePtr = j * pitch + inPtr;
for ( i = 0; i < width; i++ )
{
for ( c = 0; c < components; c++ )
{
// Clamp
pixel = (*ImagePtr + shift) * scale;
if ( pixel < 0 )
{
pixel = 0;
}
else
{
if ( pixel > 255 )
{
pixel = 255;
}
}
*BufferPtr = (unsigned char) pixel;
ImagePtr++;
BufferPtr++;
}
ImagePtr += pixelSize - components;
}
}
return;
}
extern "C" {
#define VTKIMAGEDATATOTKPHOTO_CORONAL 0
#define VTKIMAGEDATATOTKPHOTO_SAGITTAL 1
#define VTKIMAGEDATATOTKPHOTO_TRANSVERSE 2
int vtkImageDataToTkPhoto_Cmd (ClientData clientData, Tcl_Interp *interp,
int argc, char **argv)
{
int status = 0;
vtkImageData* image = NULL;
Tk_PhotoHandle photo;
int slice = 0;
int orientation = VTKIMAGEDATATOTKPHOTO_TRANSVERSE;
int vtkImageDataToTkPhoto_Cmd (ClientData clientData, Tcl_Interp *interp,
int argc, char **argv)
{
int status = 0;
vtkImageData* image = NULL;
Tk_PhotoHandle photo;
int slice = 0;
double window = 256.0;
double level = window / 2.0;
int orientation = VTKIMAGEDATATOTKPHOTO_TRANSVERSE;
// Usage: vtkImageDataToTkPhoto vtkImageData photo slice
if ( argc < 4 || argc > 5 )
{
Tcl_SetResult ( interp, "wrong # args: should be \"vtkImageDataToTkPhoto vtkImageData photo slice [orientation]\"", NULL );
return TCL_ERROR;
}
// Usage: vtkImageDataToTkPhoto vtkImageData photo slice
if ( argc < 4 || argc > 7 )
{
Tcl_SetResult ( interp, "wrong # args: should be \"vtkImageDataToTkPhoto vtkImageData photo slice [orientation] [window] [level]\"", NULL );
return TCL_ERROR;
}
// Start with slice, it's fast, etc...
status = Tcl_GetInt ( interp, argv[3], &slice );
if ( status != TCL_OK )
{
return status;
}
// Start with slice, it's fast, etc...
status = Tcl_GetInt ( interp, argv[3], &slice );
if ( status != TCL_OK )
{
return status;
}
// Find the image
// Find the image
#ifdef VTK_PYTHON_BUILD
void *ptr;
char typeCheck[128];
int i = sscanf ( argv[1], "_%lx_%s", (long *)&ptr, typeCheck);
if ( strcmp ( "vtkImageData", typeCheck ) != 0 )
{
// bad type
ptr = NULL;
}
image = (vtkImageData*) ptr;
void *ptr;
char typeCheck[128];
int i = sscanf ( argv[1], "_%lx_%s", (long *)&ptr, typeCheck);
if ( strcmp ( "vtkImageData", typeCheck ) != 0 )
{
// bad type
ptr = NULL;
}
image = (vtkImageData*) ptr;
#else
image = (vtkImageData*) vtkTclGetPointerFromObject ( argv[1], "vtkImageData", interp, status );
image = (vtkImageData*) vtkTclGetPointerFromObject ( argv[1], "vtkImageData", interp, status );
#endif
if ( !image )
{
Tcl_AppendResult ( interp, "could not find vtkImageData: ", argv[1], NULL );
return TCL_ERROR;
}
// Find the photo widget
photo = Tk_FindPhoto ( interp, argv[2] );
if ( !photo )
{
Tcl_AppendResult ( interp, "could not find photo: ", argv[2], NULL );
return TCL_ERROR;
}
// Determine if we have the correct datatype
if ( image->GetScalarType() != VTK_UNSIGNED_CHAR )
{
Tcl_SetResult ( interp, "image data type must be of type unsigned char", NULL );
return TCL_ERROR;
}
int components = image->GetNumberOfScalarComponents();
if ( components != 1 && components != 3 )
{
Tcl_SetResult ( interp, "number of scalar components must be 1, 3, 4", TCL_VOLATILE );
return TCL_ERROR;
}
if ( !image )
{
Tcl_AppendResult ( interp, "could not find vtkImageData: ", argv[1], NULL );
return TCL_ERROR;
}
// Determine the orientation
if ( argc == 5 )
{
if ( strcmp ( argv[4], "transverse" ) == 0 )
// Find the photo widget
photo = Tk_FindPhoto ( interp, argv[2] );
if ( !photo )
{
orientation = VTKIMAGEDATATOTKPHOTO_TRANSVERSE;
Tcl_AppendResult ( interp, "could not find photo: ", argv[2], NULL );
return TCL_ERROR;
}
if ( strcmp ( argv[4], "coronal" ) == 0 )
int components = image->GetNumberOfScalarComponents();
if ( components != 1 && components != 3 )
{
orientation = VTKIMAGEDATATOTKPHOTO_CORONAL;
Tcl_SetResult ( interp, "number of scalar components must be 1, 3, 4", TCL_VOLATILE );
return TCL_ERROR;
}
if ( strcmp ( argv[4], "sagittal" ) == 0 )
// Determine the orientation
if ( argc >= 5 )
{
orientation = VTKIMAGEDATATOTKPHOTO_SAGITTAL;
if ( strcmp ( argv[4], "transverse" ) == 0 )
{
orientation = VTKIMAGEDATATOTKPHOTO_TRANSVERSE;
}
if ( strcmp ( argv[4], "coronal" ) == 0 )
{
orientation = VTKIMAGEDATATOTKPHOTO_CORONAL;
}
if ( strcmp ( argv[4], "sagittal" ) == 0 )
{
orientation = VTKIMAGEDATATOTKPHOTO_SAGITTAL;
}
}
}
// Change to do sagittal Y-Z plane
int extent[6];
// Pass the check?
int valid = 1;
image->Update();
image->GetWholeExtent ( extent );
// Setup the photo data block
// For reference:
// pitch - address difference between two vertically adjacent pixels
// pixelSize - address difference between two
// horizontally adjacent pixels
Tk_PhotoImageBlock block;
switch ( orientation )
{
case VTKIMAGEDATATOTKPHOTO_TRANSVERSE:
// Get Window/Level
if ( argc >= 6 )
{
valid = ( slice >= extent[4] && slice <= extent[5] );
if ( valid )
if ( ( status = Tcl_GetDouble ( interp, argv[5], &window ) ) != TCL_OK )
{
block.pixelPtr = (unsigned char*) image->GetScalarPointer ( 0, extent[3], slice );
block.width = extent[1] - extent[0] + 1;
block.height = extent[3] - extent[2] + 1;
block.pixelSize = components;
block.pitch = -components * ( block.width );
}
break;
return status;
}
}
case VTKIMAGEDATATOTKPHOTO_SAGITTAL:
if ( argc >= 7 )
{
valid = ( slice >= extent[0] && slice <= extent[1] );
if ( valid )
if ( ( status = Tcl_GetDouble ( interp, argv[6], &level ) ) != TCL_OK )
{
block.pixelPtr = (unsigned char*) image->GetScalarPointer ( slice, extent[3], 0 );
block.width = extent[3] - extent[2] + 1;
block.height = extent[5] - extent[4] + 1;
block.pixelSize = -components * ( extent[1] - extent[0] + 1 );
block.pitch = components
* ( extent[1] - extent[0] + 1 )
* ( extent[3] - extent[2] + 1 );
return status;
}
break;
}
case VTKIMAGEDATATOTKPHOTO_CORONAL:
int extent[6];
// Pass the check?
int valid = 1;
image->Update();
image->GetWholeExtent ( extent );
// Setup the photo data block, this info will be used later to
// handle the vtk data types and window/level
// For reference:
// pitch - address difference between two vertically adjacent pixels
// pixelSize - address difference between two
// horizontally adjacent pixels
Tk_PhotoImageBlock block;
void *TempPointer;
switch ( orientation )
{
valid = ( slice >= extent[2] && slice <= extent[3] );
if ( valid )
case VTKIMAGEDATATOTKPHOTO_TRANSVERSE:
{
block.pixelPtr = (unsigned char*) image->GetScalarPointer ( 0, slice, 0 );
block.width = extent[1] - extent[0] + 1;
block.height = extent[5] - extent[4] + 1;
block.pixelSize = components;
block.pitch = components
* ( extent[1] - extent[0] + 1 )
* ( extent[3] - extent[2] + 1 );
valid = ( slice >= extent[4] && slice <= extent[5] );
if ( valid )
{
TempPointer = image->GetScalarPointer ( 0, extent[3], slice );
block.width = extent[1] - extent[0] + 1;
block.height = extent[3] - extent[2] + 1;
block.pixelSize = components;
block.pitch = -components * ( block.width );
}
break;
}
case VTKIMAGEDATATOTKPHOTO_SAGITTAL:
{
valid = ( slice >= extent[0] && slice <= extent[1] );
if ( valid )
{
TempPointer = image->GetScalarPointer ( slice, extent[3], 0 );
block.width = extent[3] - extent[2] + 1;
block.height = extent[5] - extent[4] + 1;
block.pixelSize = -components * ( extent[1] - extent[0] + 1 );
block.pitch = components
* ( extent[1] - extent[0] + 1 )
* ( extent[3] - extent[2] + 1 );
}
break;
}
case VTKIMAGEDATATOTKPHOTO_CORONAL:
{
valid = ( slice >= extent[2] && slice <= extent[3] );
if ( valid )
{
TempPointer = image->GetScalarPointer ( 0, slice, 0 );
block.width = extent[1] - extent[0] + 1;
block.height = extent[5] - extent[4] + 1;
block.pixelSize = components;
block.pitch = components
* ( extent[1] - extent[0] + 1 )
* ( extent[3] - extent[2] + 1 );
}
break;
}
break;
}
}
if ( !valid )
{
Tcl_SetResult ( interp, "slice is outside the image extent", NULL );
return TCL_ERROR;
}
char mybuffer[1024];
sprintf ( mybuffer, "Width: %d Height: %d Pitch: %d PixelSize: %d", block.width, block.height, block.pitch, block.pixelSize );
Tcl_AppendResult ( interp, mybuffer, NULL );
block.offset[0] = 0;
block.offset[1] = 1;
block.offset[2] = 2;
block.offset[3] = 0;
switch ( components )
{
case 1:
block.offset[0] = 0;
block.offset[1] = 0;
block.offset[2] = 0;
block.offset[3] = 0;
break;
case 3:
block.offset[3] = 0;
break;
case 4:
block.offset[3] = 3;
break;
}
Tk_PhotoSetSize ( photo, block.width, block.height );
if ( !valid )
{
Tcl_SetResult ( interp, "slice is outside the image extent", NULL );
return TCL_ERROR;
}
// Extract the data, and reset the block
unsigned char* photobuffer = new unsigned char[block.width * block.height * components];
double shift, scale;
shift = window / 2.0 - level;
scale = 255.0 / window;
switch ( image->GetScalarType() )
{
vtkTemplateMacro9 ( vtkExtractImageData,
photobuffer,
static_cast<VTK_TT*> (TempPointer),
shift,