#!/opt/local/bin/perl # # # PERLfMRI A simple functional fMRI viewer written in perl. # # Requires PDL and PerlTK perl modules. # Both can be found in CPAN - www.cpan.org # # Jonas T. Kaplan # Version 1.0 JAN 13 2005 # use Tk; use Tk::FileSelect; use Tk::Dialog; use PDL; use PDL::Graphics::TriD; use PDL::Graphics::TriD::Image; use PDL::Graphics::OpenGL; my $mw = new MainWindow; $homedirectory = "/Users/jonask/fMRI_workspace"; $xpoint = 30; $zpoint = 30; $ypoint = 30; $zshow=1; $xshow=0; $yshow=0; $maxbrightness=400; $maxcontrast=400; $centerbrightness=100; $centercontrast=100; $Zcontincrement=1; $Zbrincrement=0; $Xcontincrement=1; $Xbrincrement=0; $Ycontincrement=1; $Ybrincrement=0; $OVbrincrement=1; $kind = COLOR; $imageopen = 0; $overlayopen = 0; $overlay2open = 0; $twiddleon=0; #------MENUBAR $mw->configure(-menu => my $menubar =$mw->Menu); $menubar->configure(-activebackground=>"royalblue"); my $file = $menubar->cascade(-label => '~File'); #my $edit = $menubar->cascade(-label => '~Edit'); my $help = $menubar->cascade(-label => '~Help', -tearoff=>0); $file->command( -label => "Open background...", -accelerator => "Ctrl-o", -command => \&openFile ); $file->command( -label => "Open overlay...", -accelerator => "Ctrl-l", -command => \&openOverlay ); $file->command( -label => "Close overlay", -accelerator => "Ctrl-w", -command => \&doCloseOverlay ); $file->command( -label => "Close all", -accelerator => "Ctrl-W", -command => \&doClose ); $file->separator; $file->command( -label => "Quit", -accelerator => "Ctrl-q", -command => sub{exit} ); $help->command( -label => "Version", -command => \&doVersion ); $help->command( -label => "Get help", -accelerator => "Ctrl-h", -command => \&doHelp ); $mw->bind('' => \&openFile); $mw->bind('' => \&openOverlay); $mw->bind('' => \&doCloseOverlay); $mw->bind('' => \&doClose); $mw->bind('' => sub{exit}); $mw->bind('' => \&doHelp); #-------X FRAME $XFrame=$mw->Frame(-background=>"gray" ,-borderwidth=>"5", -relief => "raised"); $XFrame->Checkbutton(-text => "Show Sagittal", -background=>"gray", -command => \&updateALLplane, -variable => \$xshow,)->grid("-","-"); $xlabel=$XFrame->Entry(-text =>"$xpoint", -width=>3, )->grid("-","-"); $Xbuttonup=$XFrame->Button(-text =>"X+", -image => $mw->Getimage(plusarm),-command => \&Xup); $Xbuttondown=$XFrame->Button(-text =>"X-", -image => $mw->Getimage(minusarm),-command => \&Xdown); $xdoEntryButton=$XFrame->Button(-text => "°", -bitmap => gray50, -command=>\&doXEntry)->grid($Xbuttonup,$Xbuttondown); $XbrightnessScale=$XFrame->Scale(-label => " Brightness", -command => \&XupdateBrightnessContrast, -troughcolor=>"royalblue", -from => 0, -to =>"$maxbrightness", -orient => horizontal, -variable => \$Xbrightness, -takefocus => 0)->grid("-","-"); $XbrightnessScale->set("$centerbrightness"); $XcontrastScale=$XFrame->Scale(-label => " Contrast", -command => \&XupdateBrightnessContrast, -troughcolor=>"royalblue", -from => 0, -to =>"$maxcontrast", -orient => horizontal, -variable => \$Xcontrast)->grid("-","-"); $XcontrastScale->set("$centercontrast"); #-------Y FRAME $YFrame=$mw->Frame(-background=>"gray" ,-borderwidth=>"5", -relief => "raised"); $YFrame->Checkbutton(-text => "Show Coronal", -background=>"gray", -command => \&updateALLplane, -variable => \$yshow,)->grid("-","-"); $ylabel=$YFrame->Entry(-text =>"$ypoint", -width=>3, )->grid("-","-"); $Ybuttonup=$YFrame->Button(-text =>"Y+", -image => $mw->Getimage(plusarm),-command => \&Yup); $Ybuttondown=$YFrame->Button(-text =>"Y-", -image => $mw->Getimage(minusarm), -command => \&Ydown); $ydoEntryButton=$YFrame->Button(-text => "°", -bitmap => gray50, -command=>\&doYEntry)->grid($Ybuttonup,$Ybuttondown); $YbrightnessScale=$YFrame->Scale(-label => " Brightness", -command => \&YupdateBrightnessContrast, -troughcolor=>"royalblue", -from => 0, -to =>"$maxbrightness", -orient => horizontal, -variable => \$Ybrightness, -takefocus => 0)->grid("-","-"); $YbrightnessScale->set("$centerbrightness"); $YcontrastScale=$YFrame->Scale(-label => " Contrast", -command => \&YupdateBrightnessContrast, -troughcolor=>"royalblue", -from => 0, -to =>"$maxcontrast", -orient => horizontal, -variable => \$Ycontrast)->grid("-","-"); $YcontrastScale->set("$centercontrast"); #-------Z FRAME $ZFrame=$mw->Frame(-background=>"gray" ,-borderwidth=>"5", -relief => "raised"); $ZFrame->Checkbutton(-text => "Show Horizontal", -background=>"gray", -command => \&updateALLplane, -variable => \$zshow,)->grid("-","-"); $zlabel=$ZFrame->Entry(-text =>"$zpoint", -width=>3, )->grid("-","-"); $Zbuttonup=$ZFrame->Button(-text =>"Z+", -image => $mw->Getimage(plusarm),-command=> \&Zup); #$Zbuttonup->bind(''=>\&Zup); $Zbuttondown=$ZFrame->Button(-text =>"Z-", -image => $mw->Getimage(minusarm), -command => \&Zdown); $zdoEntryButton=$ZFrame->Button(-text => "°", -bitmap => gray50, -command=>\&doZEntry)->grid($Zbuttonup,$Zbuttondown); $ZbrightnessScale=$ZFrame->Scale(-label => " Brightness", -command => \&ZupdateBrightnessContrast, -troughcolor=>"royalblue", -from => 0, -to =>"$maxbrightness", -orient => horizontal, -variable => \$Zbrightness, -takefocus => 0)->grid("-","-"); $ZbrightnessScale->set("$centerbrightness"); $ZcontrastScale=$ZFrame->Scale(-label => " Contrast", -command => \&ZupdateBrightnessContrast, -troughcolor=>"royalblue", -from => 0, -to =>"$maxcontrast", -orient => horizontal, -variable => \$Zcontrast)->grid("-","-"); $ZcontrastScale->set("$centercontrast"); #-----OVERLAY CONTROLS $overlaymaincolor=gray; $OverlayFrame=$mw->Frame(-background=>"$overlaymaincolor", -borderwidth=>5,-relief=>raised); $OverlayFrame->Label(-text=>"Overlay Options", -background=>"$overlaymaincolor")->pack; $ColorOptions = $OverlayFrame->Optionmenu(-variable=>\$coloroption, -options=>["red","green","blue","yellow"], -command=>\&updateALLplane, -background=>"$overlaymaincolor")->pack(); $OverlayBrightnessScale=$OverlayFrame->Scale(-label => " Brightness", -command => \&overlayUpdateBrightnessContrast, -troughcolor=>"royalblue", -from => 0, -to =>"$maxcontrast", -background => "$overlaymaincolor", -orient => horizontal, -variable => \$OVbrightness, -takefocus => 0)->pack; $OverlayBrightnessScale->set("$centercontrast"); #----Other GUI Elements $maintitle = $mw->Label(-text => "Current image: none")->pack; $overlaytitle = $mw->Label(-text => "Current overlay: none")->pack; $twiddlebutton=$mw->Button(-text =>"3D Rotate", -activebackground=> "purple",-command => \&doTwiddle)->pack(-fill=> 'both'); $BgValue = $mw->Label(-text=>"Bg Value:")->pack(-side=>top, -fill=>x); $ZFrame->pack(-side => "left", -padx=>5); $XFrame->pack(-side => "left", -padx=>5); $YFrame->pack(-side => "left", -padx=>5); $OverlayFrame->pack(-side=>"left",-padx=>5); #only while debugging MainLoop; sub openFile { $fileentry = $mw->FileSelect(-directory => "$homedirectory", -filter=>"*.hdr", -title=>"Select file to open", ); $file = $fileentry->Show; if ($file) {&doOpenImage($file);} } sub openOverlay { if ($imageopen) { $fileentry = $mw->FileSelect(-directory => "$homedirectory", -filter=>"*.hdr", -title=>"Select file to open", ); $file = $fileentry->Show; if ($file) {&doOpenOverlayImage($file)}; } else { $mw->Dialog(-title=>"Error", -text=>"You must open a background image before adding an overlay.", -default_button=>"OK", -image => $mw->Getimage(warning), -buttons=>["OK"])->Show(); } } sub doOpenImage { $inputprefix = @_[0]; $inputprefix=~s/.hdr//; $_=$inputprefix; /\/([\w-]+)$/i; $shortprefix=$1; $headerfilename = "$inputprefix.hdr"; $maskheaderfilename = "$maskprefix.hdr"; $imagefilename = "$inputprefix.img"; $maskimagefilename = "$maskprefix.img"; $maintitle->configure(-text => "Current image: $shortprefix", -background=>lightblue); #Read in image header file open (HEADER,$headerfilename) or die "Couldn't open file $headerfilename\n"; read (HEADER,$header_key,40); read (HEADER,$image_dimension,108); read (HEADER,$data_history,200); close (HEADER); #Extract useful info from headers my @dim = unpack("x0 s8",$image_dimension); my $bitpix = unpack("x32 s",$image_dimension); my $datatype = unpack("x30 s",$image_dimension); my $datatypename; if ($datatype==2) {$datatype = 0;$datatypename="byte";} if ($datatype==4) {$datatype = 1;$datatypename="Signed short";} if ($datatype==8) {$datatype = 2;$datatypename="Signed integer";} if ($datatype==16) {$datatype = 5;$datatypename="Float";} if ($datatype==32) {$datatype = 3;$datatypename="Long";} my @pixdim = unpack("x36 f8",$image_dimension); my @origin = unpack("x105 s5",$data_history); my $bytepix = $bitpix/8; my $numpix = $dim[1] * $dim[2] * $dim[3] * $dim[4]; my $numbytes = $numpix * $bytepix; #Read image data into piddle $image = pdl->new; $image->set_datatype($datatype); $image->setdims([$dim[1],$dim[2],$dim[3],$dim[4]]); $zdim=$dim[3]; $xdim=$dim[1]; $ydim=$dim[2]; my $dref = $image->get_dataref(); open my $file,"<$imagefilename" or die "Couldn't open $imagefilename\n"; read ($file,$$dref,$numbytes); close $file; $image->upd_data(); $image=$image->float; $maximageval=max($image); $image=$image/$maximageval; $imageopen=1; $XbrightnessScale->set("$centerbrightness"); $XcontrastScale->set("$centercontrast"); $ZbrightnessScale->set("$centerbrightness"); $ZcontrastScale->set("$centercontrast"); $YbrightnessScale->set("$centerbrightness"); $YcontrastScale->set("$centercontrast"); &updateALLplane; } sub doOpenOverlayImage { $OVinputprefix = @_[0]; $OVinputprefix=~s/.hdr//; $_=$OVinputprefix; /\/([\w-]+)$/i; $OVshortprefix=$1; $OVheaderfilename = "$OVinputprefix.hdr"; $OVimagefilename = "$OVinputprefix.img"; #Read in image header file open (HEADER,$OVheaderfilename) or die "Couldn't open file $OVheaderfilename\n"; read (HEADER,$header_key,40); read (HEADER,$image_dimension,108); read (HEADER,$data_history,200); close (HEADER); #Extract useful info from headers my @dim = unpack("x0 s8",$image_dimension); my $bitpix = unpack("x32 s",$image_dimension); my $datatype = unpack("x30 s",$image_dimension); my $datatypename; if ($datatype==2) {$datatype = 0;$datatypename="byte";} if ($datatype==4) {$datatype = 1;$datatypename="Signed short";} if ($datatype==8) {$datatype = 2;$datatypename="Signed integer";} if ($datatype==16) {$datatype = 5;$datatypename="Float";} if ($datatype==32) {$datatype = 3;$datatypename="Long";} my @pixdim = unpack("x36 f8",$image_dimension); my @origin = unpack("x105 s5",$data_history); my $bytepix = $bitpix/8; my $numpix = $dim[1] * $dim[2] * $dim[3] * $dim[4]; my $numbytes = $numpix * $bytepix; $OVzdim=$dim[3]; $OVxdim=$dim[1]; $OVydim=$dim[2]; #Make sure overlay and background are compatible if (($OVzdim eq $zdim) && ($OVxdim eq $xdim) && ($OVzdim eq $zdim)) { $overlaytitle->configure(-text => "Current overlay: $OVshortprefix", -background=>lightblue); $OverlayFrame->pack(); #Read image data into piddle $OVimage = pdl->new; $OVimage->set_datatype($datatype); $OVimage->setdims([$dim[1],$dim[2],$dim[3],$dim[4]]); my $dref = $OVimage->get_dataref(); open my $file,"<$OVimagefilename" or die "Couldn't open $OVimagefilename\n"; read ($file,$$dref,$numbytes); close $file; $OVimage->upd_data(); $OVimage=$OVimage->float; $maxoverlayval=max($OVimage); $OVimage=$OVimage/$maxoverlayval; $overlayopen=1; &updateALLplane; } else { $mw->Dialog(-title=>"Error", -text=>"Overlay and background image are not the same dimensions.", -default_button=>"OK", -image => $mw->Getimage(warning), -buttons=>["OK"])->Show(); } } sub colorChoose { print $coloroption; } sub doClose { $imageopen=0; $overlayopen=0; $maintitle->configure(-text => "Current image: none",-background=>gainsboro); $overlaytitle->configure(-text => "Current overlay: none",-background=>gainsboro); $xpoint = 30; $zpoint = 30; $ypoint = 30; $zlabel->configure(-text =>"$zpoint"); $xlabel->configure(-text =>"$xpoint"); $ylabel->configure(-text =>"$ypoint"); } sub doCloseOverlay { $overlayopen=0; $overlaytitle->configure(-text => "Current overlay: none",-background=>gainsboro); $OverlayFrame->packForget(); &updateALLplane; } sub Zup { ++$zpoint; $zlabel->configure(-text =>"$zpoint"); &updateALLplane; } sub Zdown { --$zpoint; $zlabel->configure(-text=>"$zpoint"); &updateALLplane; } sub doZEntry { $zpoint=$zlabel->get(); &updateALLplane; } sub Xup { ++$xpoint; $xlabel->configure(-text =>"$xpoint"); &updateALLplane; } sub Xdown { --$xpoint; $xlabel->configure(-text=>"$xpoint"); &updateALLplane; } sub doXEntry { $xpoint=$xlabel->get(); &updateALLplane; } sub Yup { ++$ypoint; $ylabel->configure(-text =>"$ypoint"); &updateALLplane; } sub Ydown { --$ypoint; $ylabel->configure(-text=>"$ypoint"); &updateALLplane; } sub doYEntry { $ypoint=$ylabel->get(); &updateALLplane; } sub doTwiddle { $twiddlebutton->configure(-text=>"Press Q to exit 3D Rotate", -activebackground=> "red"); $twiddlebutton->update; $twiddleon=1; &updateALLplane; $twiddleon=0; $twiddlebutton->configure(-text=>"3D Rotate", -activebackground=> "purple",); } sub ZupdateBrightnessContrast { if ($imageopen==1) { $Zbrincrement=($Zbrightness-$centerbrightness)/100; $Zcontincrement=($Zcontrast/$centercontrast); &updateALLplane; } } sub XupdateBrightnessContrast { if ($imageopen==1) { $Xbrincrement=($Xbrightness-$centerbrightness)/100; $Xcontincrement=($Xcontrast/$centercontrast); &updateALLplane; } } sub YupdateBrightnessContrast { if ($imageopen==1) { $Ybrincrement=($Ybrightness-$centerbrightness)/100; $Ycontincrement=($Ycontrast/$centercontrast); &updateALLplane; } } sub overlayUpdateBrightnessContrast { if ($overlayopen==1) { $OVbrincrement=($OVbrightness/$centercontrast); &updateALLplane; } } sub updateALLplane { if ($imageopen) { $thepoint=$image->slice("($xpoint),($ypoint),($zpoint),(0)"); $thepoint=$thepoint * $maximageval; if ($overlayopen) { $theoverlaypoint=$OVimage->slice("($xpoint),($ypoint),($zpoint),(0)"); $theoverlaypoint=$theoverlaypoint * $maxoverlayval; $BgValue->configure(-text=>"Bg Value: $thepoint Ov Value: $theoverlaypoint"); } else { $BgValue->configure(-text=>"Bg Value: $thepoint"); } } nokeeptwiddling3d(); if ($zshow && !$xshow && !$yshow) {if ($twiddleon) {keeptwiddling3d();}} if ($zshow && $imageopen) { $theZslice=$image->slice(":,:,($zpoint),(0)"); if ($overlayopen) {$theOverlayZslice=$OVimage->slice(":,:,($zpoint),(0)");} else {$theOverlayZslice=$theZslice-$theZslice;} $zpct = $zpoint/$zdim; release3d(); if ($coloroption eq "red") { imagrgb3d[$theZslice*$Zcontincrement+$Zbrincrement+($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement-($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement-($theOverlayZslice*$OVbrincrement)],{Points=>[[0,0,$zpct],[1,0,$zpct],[1,1,$zpct],[0,1,$zpct]]}; } elsif ($coloroption eq "green") { imagrgb3d[$theZslice*$Zcontincrement+$Zbrincrement-($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement+($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement-($theOverlayZslice*$OVbrincrement)],{Points=>[[0,0,$zpct],[1,0,$zpct],[1,1,$zpct],[0,1,$zpct]]}; } elsif ($coloroption eq "blue") { imagrgb3d[$theZslice*$Zcontincrement+$Zbrincrement-($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement-($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement+($theOverlayZslice*$OVbrincrement)],{Points=>[[0,0,$zpct],[1,0,$zpct],[1,1,$zpct],[0,1,$zpct]]}; } elsif ($coloroption eq "yellow") { imagrgb3d[$theZslice*$Zcontincrement+$Zbrincrement+($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement+($theOverlayZslice*$OVbrincrement),$theZslice*$Zcontincrement+$Zbrincrement-($theOverlayZslice*$OVbrincrement)],{Points=>[[0,0,$zpct],[1,0,$zpct],[1,1,$zpct],[0,1,$zpct]]}; } } if ($xshow &&!$yshow) {if ($twiddleon) {keeptwiddling3d();}} if ($xshow && $imageopen) { $theXslice=$image->slice("($xpoint),:,:,(0)"); if ($overlayopen) {$theOverlayXslice=$OVimage->slice("($xpoint),:,:,(0)");} else {$theOverlayXslice=$theXslice-$theXslice;} $xpct = $xpoint/$xdim; hold3d(); if ($coloroption eq "red") { imagrgb3d[$theXslice*$Xcontincrement+$Xbrincrement+($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement-($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement-($theOverlayXslice*$OVbrincrement)],{Points=>[[$xpct,0,0],[$xpct,1,0],[$xpct,1,1],[$xpct,0,1]]}; } elsif ($coloroption eq "green") { imagrgb3d[$theXslice*$Xcontincrement+$Xbrincrement-($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement+($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement-($theOverlayXslice*$OVbrincrement)],{Points=>[[$xpct,0,0],[$xpct,1,0],[$xpct,1,1],[$xpct,0,1]]}; } elsif ($coloroption eq "blue") { imagrgb3d[$theXslice*$Xcontincrement+$Xbrincrement-($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement-($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement+($theOverlayXslice*$OVbrincrement)],{Points=>[[$xpct,0,0],[$xpct,1,0],[$xpct,1,1],[$xpct,0,1]]}; } elsif ($coloroption eq "yellow") { imagrgb3d[$theXslice*$Xcontincrement+$Xbrincrement+($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement+($theOverlayXslice*$OVbrincrement),$theXslice*$Xcontincrement+$Xbrincrement-($theOverlayXslice*$OVbrincrement)],{Points=>[[$xpct,0,0],[$xpct,1,0],[$xpct,1,1],[$xpct,0,1]]}; } } if ($yshow && $twiddleon) {keeptwiddling3d();} if ($yshow && $imageopen) { $theYslice=$image->slice(":,($ypoint),:,(0)"); if ($overlayopen) {$theOverlayYslice=$OVimage->slice(":,($ypoint),:,(0)");} else {$theOverlayYslice=$theYslice-$theYslice;} $ypct = $ypoint/$ydim; hold3d(); if ($coloroption eq "red") { imagrgb3d[$theYslice*$Ycontincrement+$Ybrincrement+($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement-($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement-($theOverlayYslice*$OVbrincrement)],{Points=>[[0,$ypct,0],[1,$ypct,0],[1,$ypct,1],[0,$ypct,1]]}; } elsif ($coloroption eq "green") { imagrgb3d[$theYslice*$Ycontincrement +$Ybrincrement-($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement+($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement-($theOverlayYslice*$OVbrincrement)],{Points=>[[0,$ypct,0],[1,$ypct,0],[1,$ypct,1],[0,$ypct,1]]}; } elsif ($coloroption eq "blue") { imagrgb3d[$theYslice*$Ycontincrement+$Ybrincrement-($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement-($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement+($theOverlayYslice*$OVbrincrement)],{Points=>[[0,$ypct,0],[1,$ypct,0],[1,$ypct,1],[0,$ypct,1]]}; }elsif ($coloroption eq "yellow") { imagrgb3d[$theYslice*$Ycontincrement+$Ybrincrement+($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement+($theOverlayYslice*$OVbrincrement),$theYslice*$Ycontincrement+$Ybrincrement-($theOverlayYslice*$OVbrincrement)],{Points=>[[0,$ypct,0],[1,$ypct,0],[1,$ypct,1],[0,$ypct,1]]}; } } nokeeptwiddling3d(); } sub doVersion { $mw->Dialog(-title=>"Version", -text=>"Version 1.0, Jan 2005", -default_button=>"OK", -image => $mw->Getimage(info), -buttons=>["OK"])->Show(); } sub doHelp { $mw->Dialog(-title=>"Help", -text=>"Sorry. There's no help currently available, nor is there any documentation.", -default_button=>"OK", -image => $mw->Getimage(info), -buttons=>["OK"])->Show(); }