| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package PDL::IO::Dcm::Plugins::MRISiemens; | 
| 2 |  |  |  |  |  |  | #use base 'PDL::IO::Dcm'; | 
| 3 | 1 |  |  | 1 |  | 670 | use Exporter; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 32 |  | 
| 4 | 1 |  |  | 1 |  | 4 | use PDL; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 5 | 1 |  |  | 1 |  | 1963 | use PDL::NiceSlice; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 6 | 1 |  |  | 1 |  | 11735 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 20 |  | 
| 7 | 1 |  |  | 1 |  | 9 | use 5.10.0; | 
|  | 1 |  |  |  |  | 2 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | our @ISA=qw/Exporter/; | 
| 11 |  |  |  |  |  |  | our @EXPORT_OK=qw/init_dims populate_header setup_dcm/; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | sub setup_dcm { | 
| 14 | 0 |  |  | 0 | 1 |  | my $opt=shift; | 
| 15 | 0 | 0 |  |  |  |  | $opt={} unless (ref($opt) eq 'HASH'); # ensure hash context | 
| 16 |  |  |  |  |  |  | # split on series number by default | 
| 17 | 0 |  |  |  |  |  | $$opt{id}=\&PDL::IO::Dcm::sort_series; | 
| 18 | 0 |  |  |  |  |  | $$opt{sort}=\&populate_header; | 
| 19 | 0 |  |  |  |  |  | $$opt{duplicates}=\&handle_duplicates; | 
| 20 | 0 |  |  |  |  |  | $$opt{delete_raw}=1; # deletes the raw_dicom structure after parsing | 
| 21 | 0 |  |  |  |  |  | push @PDL::IO::Dcm::key_list,'0051,100f'; | 
| 22 |  |  |  |  |  |  | #say join ' ',%{$opt}; | 
| 23 | 0 | 0 |  |  |  |  | if ($$opt{c_phase_t} ) { | 
| 24 | 0 | 0 |  |  |  |  | if ($$opt{c_phase_set} ) { | 
| 25 | 0 |  |  |  |  |  | $$opt{Dimensions}=[qw/x y z=partitions*slices T*Set*Phases Echo Channel/]; | 
| 26 |  |  |  |  |  |  | } else { | 
| 27 | 0 |  |  |  |  |  | $$opt{Dimensions}=[qw/x y z=partitions*slices T*Phases Echo Channel Set/]; | 
| 28 |  |  |  |  |  |  | } | 
| 29 |  |  |  |  |  |  | }else { | 
| 30 | 0 | 0 |  |  |  |  | if ($$opt{c_phase_set} ) { | 
| 31 | 0 |  |  |  |  |  | $$opt{Dimensions}=[qw/x y z=partitions*slices T Echo Channel Set*Phase/]; | 
| 32 |  |  |  |  |  |  | } else { | 
| 33 | 0 |  |  |  |  |  | $$opt{Dimensions}=[qw/x y z=partitions*slices T Echo Channel Phase Set /]; | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  | # part,sl,t,echo,coil,phase,set | 
| 37 | 0 |  |  |  |  |  | $$opt{dim_order}=[6,10,4,1,0,2,3]; | 
| 38 |  |  |  |  |  |  | $$opt{internal_dims}=[ | 
| 39 |  |  |  |  |  |  | # | 
| 40 | 0 |  |  |  |  |  | qw/x y coil echo cphase set t ? partition? chron_slice? ? slice ? some_id/]; | 
| 41 |  |  |  |  |  |  | # note the order since dims change by clump! | 
| 42 |  |  |  |  |  |  | # partitions and slices | 
| 43 | 0 |  |  |  |  |  | $$opt{clump_dims}=[[0,1],]; | 
| 44 |  |  |  |  |  |  | # phases and set, phases*set and t | 
| 45 | 0 | 0 |  |  |  |  | push (@{$$opt{clump_dims}},[4,5]) if $$opt{c_phase_set}; | 
|  | 0 |  |  |  |  |  |  | 
| 46 | 0 | 0 |  |  |  |  | push (@{$$opt{clump_dims}},[1,4]) if $$opt{c_phase_t}; | 
|  | 0 |  |  |  |  |  |  | 
| 47 | 0 |  |  |  |  |  | $opt; | 
| 48 |  |  |  |  |  |  | } | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | sub read_text_hdr { | 
| 53 | 0 |  |  | 0 | 1 |  | my $f=shift; # File | 
| 54 | 0 |  |  |  |  |  | my $self=shift; | 
| 55 | 0 | 0 |  |  |  |  | open (HDR,'<',\$f) || die "no header !"; | 
| 56 | 0 |  |  |  |  |  | my $l; | 
| 57 |  |  |  |  |  |  | #say "file $f line $l"; | 
| 58 | 0 |  |  |  |  |  | do {$l=; } until ($l=~/ASCCONV BEGIN/); | 
|  | 0 |  |  |  |  |  |  | 
| 59 | 0 |  |  |  |  |  | while (($l=)!~/ASCCONV END/) { | 
| 60 | 0 |  |  |  |  |  | chomp $l; | 
| 61 | 0 | 0 |  |  |  |  | if ( $l) { | 
| 62 | 0 |  |  |  |  |  | chomp (my ($key,$val)=split /\s*=\s*/,$l); | 
| 63 | 0 |  |  |  |  |  | chomp($key); | 
| 64 | 0 |  |  |  |  |  | $key=~s/[\[\].]/_/g; | 
| 65 | 0 |  |  |  |  |  | $self->hdr->{ascconv}->{$key}=$val; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  | } | 
| 68 | 0 |  |  |  |  |  | close HDR; | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | sub sort_protid { | 
| 72 | 0 |  |  | 0 | 1 |  | $_[0]->hdr->{ascconv}->{"lProtID"}; | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub populate_header { | 
| 76 | 0 |  |  | 0 | 1 |  | my $dicom =shift; | 
| 77 | 0 |  |  |  |  |  | my $piddle=shift; | 
| 78 |  |  |  |  |  |  | # The protocol is in here: | 
| 79 |  |  |  |  |  |  | #say "populate_header ",$_[1]->info,$_[0]->getValue('0020,0032'); | 
| 80 | 0 |  |  |  |  |  | read_text_hdr($dicom->getValue ('0029,1020','native'),$piddle); | 
| 81 | 0 |  |  |  |  |  | delete $piddle->hdr->{raw_dicom}->{'0029,1020'}; # Protocol | 
| 82 | 0 |  |  |  |  |  | my @ret=$dicom->getValue('0029,1010','native')=~/ICE_Dims.{92}((_?(X|\d+)){13})/s; | 
| 83 | 0 |  |  |  |  |  | (my $str=$ret[0])=~s/X/1/e; | 
|  | 0 |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | # to make this unique | 
| 85 | 0 |  |  |  |  |  | say "Instance Number ",$dicom->getValue('InstanceNumber'); | 
| 86 | 0 |  | 0 |  |  |  | $piddle->hdr->{dcm_key}=$dicom->getValue('InstanceNumber').'_'.($dicom->getValue('0051,100f')||0); | 
| 87 | 0 |  |  |  |  |  | my @d=split ('_',$str); | 
| 88 | 0 |  |  |  |  |  | my $iced=pdl(short,@d); #badvalue(short)/er)]); | 
| 89 | 0 |  |  |  |  |  | $iced--; | 
| 90 | 0 |  |  |  |  |  | $piddle->hdr->{dim_idx}=$iced; | 
| 91 |  |  |  |  |  |  | #say $piddle->hdr->{dcm_key},": ",$iced,$dicom->getValue ('0020,0032','native'); | 
| 92 |  |  |  |  |  |  | #say "Dims $str pos $iced"; | 
| 93 | 0 |  |  |  |  |  | return $str; | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | sub handle_duplicates { | 
| 97 | 0 |  |  | 0 | 1 |  | my $stack=shift; | 
| 98 | 0 |  |  |  |  |  | my $dcm=shift; | 
| 99 | 0 |  |  |  |  |  | my $opt=shift; | 
| 100 |  |  |  |  |  |  | "This entry (". $dcm->hdr->{dim_idx}->($$opt{dim_order}). | 
| 101 | 0 |  |  |  |  |  | max ($stack->(,,list $dcm->hdr->{dim_idx}->($$opt{dim_order}))). | 
| 102 |  |  |  |  |  |  | ") is already set! This should not happen, please file a bug report!\n"; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | sub init_dims { | 
| 106 | 1 |  |  | 1 |  | 6 | use PDL::NiceSlice; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 6 |  | 
| 107 | 0 |  |  | 0 | 1 |  | my $self=shift; | 
| 108 | 0 |  |  |  |  |  | my $opt=shift; | 
| 109 |  |  |  |  |  |  | # we need these modules, return undef otherwise. | 
| 110 | 0 | 0 |  |  |  |  | require PDL::Dims || return; | 
| 111 | 0 | 0 |  |  |  |  | require PDL::Transform || return; | 
| 112 | 0 |  |  |  |  |  | PDL::Dims->import(qw/is_equidistant dmin dmax vals hpar dinc initdim dimsize drot idx diminfo /); | 
| 113 | 0 |  |  |  |  |  | say "init_dims: ",$self->hdr->{dicom}->{Rows} ; | 
| 114 | 0 |  |  |  |  |  | say "init_dims: hpar ",hpar($self,'dicom','Rows'); | 
| 115 | 0 |  |  |  |  |  | PDL::Transform->import(qw/t_linear/); | 
| 116 |  |  |  |  |  |  | #say diminfo ($self),$self->hdr->{ascconv}->{sGroupArray_asGroup_1__nSize}; | 
| 117 |  |  |  |  |  |  | # center=inner(rot*scale,dim/2)+Image Position (Patient) | 
| 118 |  |  |  |  |  |  | # xf=t_linear(matrix=>Pixel Scale*$rot->transpose,post=>Image Position (Patient)) | 
| 119 | 0 | 0 |  |  |  |  | if (hpar($self,'ascconv','sGroupArray_asGroup_1__nSize')){ | 
| 120 | 0 |  |  |  |  |  | warn "Multiple slice groups, support dubious!"; | 
| 121 |  |  |  |  |  |  | } | 
| 122 | 1 |  |  | 1 |  | 2778 | use PDL::NiceSlice; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 7 |  | 
| 123 | 0 |  |  |  |  |  | my $v=zeroes(3); | 
| 124 | 0 |  | 0 |  |  |  | $v(0,).=hpar($self,'ascconv','sSliceArray_asSlice_0__sPosition_dSag') ||0; #x | 
| 125 | 0 |  | 0 |  |  |  | $v(1,).=hpar($self,'ascconv','sSliceArray_asSlice_0__sPosition_dCor') ||0; #y | 
| 126 | 0 |  | 0 |  |  |  | $v(2,).=hpar($self,'ascconv','sSliceArray_asSlice_0__sPosition_dTra') ||0; #z | 
| 127 | 0 |  |  |  |  |  | say $v; | 
| 128 |  |  |  |  |  |  | say "hpar: pos ",hpar($self,'dicom','Image Position (Patient)'), | 
| 129 | 0 |  |  |  |  |  | $self->hdr->{dicom}->{'Image Position (Patient)'}; | 
| 130 | 0 |  |  |  |  |  | say "init_dims: ",hpar($self,'dicom','Rows'); | 
| 131 | 0 |  |  |  |  |  | my $pos_d=(hpar($self,'dicom','Image Position (Patient)'))->flat->(:5)->reshape(3,2); | 
| 132 | 0 |  | 0 |  |  |  | my $ir=hpar($self,'ascconv','sSliceArray_asSlice_0__dInPlaneRot') ||0; #radiant | 
| 133 | 0 |  |  |  |  |  | my $or=pdl(hpar($self,'dicom','Image Orientation (Patient)'))->flat->(:5) | 
| 134 |  |  |  |  |  |  | ->reshape(3,2)->transpose; # | 
| 135 | 0 |  |  |  |  |  | my $pe_dir=hpar($self,'dicom','In-plane Phase Encoding Direction'); | 
| 136 |  |  |  |  |  |  | # Scaling | 
| 137 |  |  |  |  |  |  | # Rotation | 
| 138 | 0 |  |  |  |  |  | my $srot=zeroes(3,3); | 
| 139 | 0 |  |  |  |  |  | $srot(:1,).=$or; | 
| 140 | 0 |  |  |  |  |  | $srot(2,;-).=norm($pos_d(,1;-)-$pos_d(,0;-)); | 
| 141 | 0 | 0 |  |  |  |  | $srot(2,;-).=pdl[0,0,1] unless any ($srot(2,;-)); | 
| 142 |  |  |  |  |  |  | #say "spatial rotation $srot"; | 
| 143 | 0 |  |  |  |  |  | $pos_d=$pos_d(,0;-); | 
| 144 |  |  |  |  |  |  | # Calculate and initialise the transformation | 
| 145 | 0 |  |  |  |  |  | my @ors=qw/Cor Sag Tra/; | 
| 146 | 0 |  |  |  |  |  | $self->hdr->{orientation}=$ors[maximum_ind($srot(2;-))];  # normal vector lookup | 
| 147 |  |  |  |  |  |  | my $pe=$self->hdr->{dicom}->{"In-plane Phase Encoding Direction"} | 
| 148 | 0 |  | 0 |  |  |  | ||$self->hdr->{dicom}->{"InPlanePhaseEncodingDirection"}; | 
| 149 | 0 | 0 |  |  |  |  | if ($self->hdr->{orientation} eq 'Tra'){ # transversal slice | 
| 150 | 0 |  |  |  |  |  | say $self->hdr->{orientation}. " Orientation"; | 
| 151 | 0 |  |  |  |  |  | $self->hdr->{sl}='z'; | 
| 152 | 0 | 0 |  |  |  |  | if ($pe eq 'COL'){ | 
| 153 | 0 |  |  |  |  |  | $self->hdr->{ro}='x'; | 
| 154 | 0 |  |  |  |  |  | $self->hdr->{pe}='y'; | 
| 155 |  |  |  |  |  |  | } else { | 
| 156 | 0 |  |  |  |  |  | $self->hdr->{ro}='y'; | 
| 157 | 0 |  |  |  |  |  | $self->hdr->{pe}='x'; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  | } | 
| 160 | 0 | 0 |  |  |  |  | if ($self->hdr->{orientation} eq 'Cor'){ # coronal slice | 
| 161 | 0 |  |  |  |  |  | $self->hdr->{sl}='y'; | 
| 162 | 0 | 0 |  |  |  |  | if ($pe eq 'COL') { | 
| 163 | 0 |  |  |  |  |  | $self->hdr->{ro}='x'; | 
| 164 | 0 |  |  |  |  |  | $self->hdr->{pe}='z'; | 
| 165 |  |  |  |  |  |  | } else { | 
| 166 | 0 |  |  |  |  |  | $self->hdr->{ro}='z'; | 
| 167 | 0 |  |  |  |  |  | $self->hdr->{pe}='x'; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  | } | 
| 170 | 0 | 0 |  |  |  |  | if ($self->hdr->{orientation} eq 'Sag'){ # sagittal slice | 
| 171 | 0 |  |  |  |  |  | $self->hdr->{sl}='x'; | 
| 172 | 0 | 0 |  |  |  |  | if ($pe eq 'COL') { | 
| 173 | 0 |  |  |  |  |  | $self->hdr->{ro}='y'; | 
| 174 | 0 |  |  |  |  |  | $self->hdr->{pe}='z'; | 
| 175 |  |  |  |  |  |  | } else { | 
| 176 | 0 |  |  |  |  |  | $self->hdr->{ro}='z'; | 
| 177 | 0 |  |  |  |  |  | $self->hdr->{pe}='y'; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  | } | 
| 180 | 0 |  |  |  |  |  | my $s=zeroes(3); # matrix size | 
| 181 | 0 |  |  |  |  |  | my $fov=zeroes(3); # FOV | 
| 182 | 0 |  |  |  |  |  | $fov(0).=$self->hdr->{ascconv}->{sSliceArray_asSlice_0__dReadoutFOV}; | 
| 183 | 0 |  |  |  |  |  | $fov(1).=$self->hdr->{ascconv}->{sSliceArray_asSlice_0__dPhaseFOV}; | 
| 184 | 0 | 0 |  |  |  |  | if ($pe =~ 'COL') { | 
| 185 | 0 |  | 0 |  |  |  | $s(0).=$self->hdr->{dicom}->{Width}||$self->hdr->{dicom}->{Columns}; | 
| 186 | 0 |  | 0 |  |  |  | $s(1).=$self->hdr->{dicom}->{Height}||$self->hdr->{dicom}->{Rows}; | 
| 187 | 0 |  |  |  |  |  | say "COL! $s"; | 
| 188 |  |  |  |  |  |  | } else { | 
| 189 | 0 |  | 0 |  |  |  | $s(1).=$self->hdr->{dicom}->{Width}||$self->hdr->{dicom}->{Columns}; | 
| 190 | 0 |  | 0 |  |  |  | $s(0).=$self->hdr->{dicom}->{Height}||$self->hdr->{dicom}->{Rows}; | 
| 191 |  |  |  |  |  |  | #$fov(1).=$self->hdr->{ascconv}->{sSliceArray_asSlice_0__dReadoutFOV}; | 
| 192 |  |  |  |  |  |  | #$fov(0).=$self->hdr->{ascconv}->{sSliceArray_asSlice_0__dPhaseFOV}; | 
| 193 |  |  |  |  |  |  | } | 
| 194 | 0 |  |  |  |  |  | say "PE $pe $s $fov " ; | 
| 195 |  |  |  |  |  |  | $self->hdr->{'3d'}=1 if (($self->hdr->{dicom}->{MRAcquisitionType}|| | 
| 196 | 0 | 0 | 0 |  |  |  | hpar($self,'dicom','MR Acquisition Type')) eq '3D'); # 3D | 
| 197 | 0 | 0 |  |  |  |  | if ($self->hdr->{'3d'}) { | 
| 198 | 0 |  |  |  |  |  | $s(2).=$self->hdr->{ascconv}->{'sKSpace_lImagesPerSlab'} ; | 
| 199 | 0 |  |  |  |  |  | $fov(2).=$self->hdr->{ascconv}->{sSliceArray_asSlice_0__dThickness}; | 
| 200 |  |  |  |  |  |  | } else { | 
| 201 | 0 |  |  |  |  |  | $s(2).=$self->hdr->{ascconv}->{'sSliceArray_lSize'}; | 
| 202 | 0 |  |  |  |  |  | $fov(2).=$self->hdr->{dicom}->{"Spacing Between Slices"}*$s(2); | 
| 203 | 0 |  |  |  |  |  | say "FOV $fov matrix $s"; | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 0 | 0 |  |  |  |  | $s(2).=1 if ($s(2)<1); | 
| 206 | 0 |  |  |  |  |  | my $rot=identity($self->ndims); | 
| 207 | 0 |  |  |  |  |  | my $inc_d=zeroes(3); | 
| 208 |  |  |  |  |  |  | #say "Pixel Spacing", hpar($self,'dicom','Pixel Spacing'); | 
| 209 | 0 |  |  |  |  |  | $inc_d(:1).=hpar($self,'dicom','Pixel Spacing')->(:1;-); | 
| 210 | 0 |  |  |  |  |  | $inc_d(2).=$fov(2,0)/$s(2,0); | 
| 211 |  |  |  |  |  |  | #say $srot; | 
| 212 | 0 |  |  |  |  |  | $rot(:2,:2).=$srot; | 
| 213 | 0 |  |  |  |  |  | say "FOV $fov matrix $s, pixels $inc_d"; | 
| 214 | 0 | 0 |  |  |  |  | barf  "dims don't fit! $s vs. ",$self->shape->(:2) if any($self->shape->(:2)-$s); | 
| 215 |  |  |  |  |  |  | #say "Rot: $rot"; | 
| 216 | 0 |  |  |  |  |  | initdim($self,'x',size=>$s(0),min=>sclr($pos_d(0)),inc=>sclr($inc_d(0)),unit=>'mm'); | 
| 217 | 0 |  |  |  |  |  | initdim($self,'y',size=>$s(1),min=>sclr($pos_d(1)),inc=>sclr($inc_d(1)),unit=>'mm'); | 
| 218 | 0 |  |  |  |  |  | initdim($self,'z',size=>$s(2),rot=>$rot,min=>sclr($pos_d(2)),inc=>sclr($inc_d(2)),unit=>'mm',); | 
| 219 | 0 |  |  |  |  |  | say "initdim for x,y,z done."; | 
| 220 | 0 |  |  |  |  |  | say "after init dim ",(diminfo ($self)); | 
| 221 | 0 |  |  |  |  |  | say "size $s min $pos_d inc $inc_d rot $rot"; | 
| 222 | 0 |  |  |  |  |  | idx($self,'x',dimsize($self,'x')/2); | 
| 223 | 0 |  |  |  |  |  | idx($self,'y',dimsize($self,'y')/2); | 
| 224 | 0 |  |  |  |  |  | idx($self,'z',dimsize($self,'z')/2); | 
| 225 | 0 |  |  |  |  |  | say "orientation : ",hpar($self,'orientation'),diminfo ($self); | 
| 226 |  |  |  |  |  |  | # other dimensions | 
| 227 | 0 |  |  |  |  |  | for my $n (3..$#{$$opt{Dimensions}}) { # x,y,z are handled above | 
|  | 0 |  |  |  |  |  |  | 
| 228 | 0 |  |  |  |  |  | my $dim=$$opt{Dimensions}->[$n]; | 
| 229 | 0 |  |  |  |  |  | print "Init Dim $dim - $n\n"; | 
| 230 | 0 |  |  |  |  |  | my $str=('(0),' x ($n-2)).','.('(0),' x ($#{$$opt{Dimensions}}-$n)); | 
|  | 0 |  |  |  |  |  |  | 
| 231 | 0 |  |  |  |  |  | say "$str "; | 
| 232 | 0 | 0 |  |  |  |  | if ($dim eq 'Echo') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | #	my $str=('(0),' x ($n-2)).','.('(0),' x ($#{$$opt{Dimensions}}-$n)); | 
| 234 | 0 |  |  |  |  |  | initdim ($self,'echo',unit=>'us', | 
| 235 |  |  |  |  |  |  | vals=>[list (hpar($self,'dicom','Echo Time')->($str))]); | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  | elsif ($dim eq 'T') { | 
| 238 |  |  |  |  |  |  | #	my $str=('(0),' x ($n-2)).','.('(0),' x ($#{$$opt{Dimensions}}-$n)); | 
| 239 | 0 |  |  |  |  |  | my $t=hpar($self,'dicom','Acquisition Time')->($str); | 
| 240 | 0 | 0 |  |  |  |  | if (is_equidistant($t,0.003)) { | 
| 241 | 0 |  |  |  |  |  | initdim ($self,'t',unit=>'s',min=>sclr($t(0)),max=>sclr($t(-1))); | 
| 242 | 0 |  |  |  |  |  | say "T min ",dmin($self,'t')," max ",dmax($self,'t')," inc ",dinc($self,'t'), $t; | 
| 243 |  |  |  |  |  |  | } else { | 
| 244 | 0 |  |  |  |  |  | initdim ($self,'t',unit=>'s',vals=>[list($t)]); | 
| 245 | 0 |  |  |  |  |  | say "T values :",vals ($self,'t'); | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  | } elsif ($dim =~ /Channel/) { | 
| 248 | 0 |  | 0 |  |  |  | my $coil=hpar($self,'dicom','0051,100f')||'combined'; | 
| 249 | 0 | 0 |  |  |  |  | if ($self->dim($n)>1) { | 
| 250 | 0 |  |  |  |  |  | initdim ($self,'channel',vals=>[hpar($self,'dicom','0051,100f')->flat->(:2)]); | 
| 251 |  |  |  |  |  |  | } else { | 
| 252 | 0 |  |  |  |  |  | initdim ($self,'channel',vals=>[$coil, size=>1]); | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  | } elsif ($dim =~ /Set/) { | 
| 255 | 0 |  |  |  |  |  | initdim ($self,'set'); # This can be anything, no further info easily available | 
| 256 |  |  |  |  |  |  | } elsif ($dim =~ /Phase/) { | 
| 257 | 0 |  |  |  |  |  | my $t=hpar($self,'dicom','Trigger Time'); | 
| 258 | 0 |  |  |  |  |  | say $t->info; | 
| 259 | 0 |  |  |  |  |  | $t=$t($str); | 
| 260 | 0 |  |  |  |  |  | say $t; | 
| 261 | 0 | 0 |  |  |  |  | if (is_equidistant($t)) { | 
| 262 | 0 |  |  |  |  |  | initdim ($self,'cphase',unit=>'ms',min=>sclr($t(0)),max=>sclr($t(-1))); | 
| 263 | 0 |  |  |  |  |  | say "Trigger min ",dmin($self,'cphase')," max ",dmax($self,'cphase')," inc ",dinc($self,'cphase'), $t; | 
| 264 | 0 |  |  |  |  |  | say "Trigger ",vals ($self,'cphase'); | 
| 265 |  |  |  |  |  |  | } else { | 
| 266 | 0 |  |  |  |  |  | initdim ($self,'cphase',unit=>'ms',vals=>[list($t)]); | 
| 267 | 0 |  |  |  |  |  | say "Trigger values :",vals ($self,'cphase'); | 
| 268 |  |  |  |  |  |  | } | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  | } | 
| 271 | 0 |  |  |  |  |  | my $mat=drot($self) x stretcher (pdl(dinc($self))); | 
| 272 | 0 |  |  |  |  |  | $mat=$mat->transpose ;#if ($pe_dir =~ /COL/); | 
| 273 |  |  |  |  |  |  | #say $mat; | 
| 274 | 0 |  |  |  |  |  | my $xf=t_linear(matrix=>$mat,post=>$pos_d(,0;-)); | 
| 275 | 0 |  |  |  |  |  | say "inc ",dinc ($self); | 
| 276 | 0 |  |  |  |  |  | say diminfo($self); | 
| 277 | 0 |  |  |  |  |  | hpar($self,'init_transform','matrix',$mat); | 
| 278 | 0 |  |  |  |  |  | hpar($self,'init_transform','post',$pos_d(,0;-)); | 
| 279 |  |  |  |  |  |  | #barf "initdim fails!" unless ($#{dimname($self)}>2); | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | =head1 Specific handling of Simenes MRI data | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | Key 0029,1010 is the Siemens specific field that contains the ICE | 
| 286 |  |  |  |  |  |  | miniheaders with dimension information - and position in matrix | 
| 287 |  |  |  |  |  |  | 0029,1020 is deleted from the header, it is big, containing the whole | 
| 288 |  |  |  |  |  |  | protocol. The important part, the Siemens protocol ASCCONV part, is stored in | 
| 289 |  |  |  |  |  |  | the ascconv key, see read_text_hdr. | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | =head1 FUNCTIONS | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | =head2 handle_duplicates | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | What to do if two images with the same position in the stack arrive. Throws an | 
| 297 |  |  |  |  |  |  | error, atm. Should handle duplicate exports | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | =head2 init_dims | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | provides support for PDL::Dims. Useful in combination with PDL::IO::Sereal to | 
| 302 |  |  |  |  |  |  | have a fully qualified data set. | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | =head2 populate_header | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | Here happens the vendor/modallity specific stuff like parsing private fields. | 
| 307 |  |  |  |  |  |  | It is required to return a position vector in the series' piddle. | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | =head2 read_text_hdr | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | parses the ASCCONV part of Siemens data header into the ascconv field of the | 
| 312 |  |  |  |  |  |  | piddle header. All special characters except [a-z0-9]i are converted to _ -- no | 
| 313 |  |  |  |  |  |  | quoting of hash keys required! You don't need to load this yourself. | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | =head2 setup_dcm | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | sets useful options for this modality. | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | =head2 sort_protid | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | alternative to split based on lProtID (matches raw data key). To activate, | 
| 322 |  |  |  |  |  |  | after running setup_dcm, point option id to \&sort_protid. | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | =head1 Specific options | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =over | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =item Nifti | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | Do we want Nifti output? May be used by your plugin to apply additional steps, | 
| 331 |  |  |  |  |  |  | eg. more clumps, reorders, setting header fields ... | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | =item c_phase_t | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | Serialize phase and t dimensions | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | =back | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | =cut | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | 1; |