diff --git a/afidsvalidator/afids-templates/macaca/tpl-D99_afids.fcsv b/afidsvalidator/afids-templates/macaca/tpl-D99_afids.fcsv new file mode 100644 index 00000000..b5329ecb --- /dev/null +++ b/afidsvalidator/afids-templates/macaca/tpl-D99_afids.fcsv @@ -0,0 +1,35 @@ +# Markups fiducial file version = 4.6 +# CoordinateSystem = 0 +# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID +vtkMRMLMarkupsFiducialNode_1,-0.115733333333333,0.128533333333333,-0.1804,0,0,0,1,1,1,0,1,AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_2,-0.0898666666666667,-13.5647333333333,1.014,0,0,0,1,1,1,0,2,PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_3,-0.154949266666667,-20.8540666666667,-1.18409,0,0,0,1,1,1,0,3,infracollicular sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_4,-0.1954,-12.1107333333333,-8.9786,0,0,0,1,1,1,0,4,PMJ,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_5,-0.191,-8.80766666666667,-5.34613333333333,0,0,0,1,1,1,0,5,superior interpeduncular fossa,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_6,6.54978,-15.0369933333333,-3.80202133333333,0,0,0,1,1,1,0,6,R superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_7,-6.808302,-15.0460333333333,-3.73986933333333,0,0,0,1,1,1,0,7,L superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_8,6.30764666666667,-15.90516,-8.184836,0,0,0,1,1,1,0,8,R inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_9,-6.57371466666667,-15.81472,-8.08559733333333,0,0,0,1,1,1,0,9,L inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_10,-0.177700066666667,-27.56682,7.36913333333333,0,0,0,1,1,1,0,10,culmen,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_11,-0.1775278,-6.571888,-6.25063133333333,0,0,0,1,1,1,0,11,intermammillary sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_12,0.817333333333333,-6.85653333333333,-5.8828,0,0,0,1,1,1,0,12,R MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_13,-1.14173333333333,-6.88506666666667,-5.876,0,0,0,1,1,1,0,13,L MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_14,-0.115933333333333,-16.6617333333333,3.447,0,0,0,1,1,1,0,14,pineal gland,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_15,3.56786666666667,0.877066666666667,9.03266666666667,0,0,0,1,1,1,0,15,R LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_16,-3.81466666666667,0.892533333333333,9.0494,0,0,0,1,1,1,0,16,L LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_17,7.7982,-12.8142,10.2004,0,0,0,1,1,1,0,17,R LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_18,-7.97086666666667,-12.8361333333333,10.2534,0,0,0,1,1,1,0,18,L LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_19,-0.1542,13.2044666666667,5.20166666666667,0,0,0,1,1,1,0,19,genu of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_20,-0.1706,-16.6438666666667,4.73833333333333,0,0,0,1,1,1,0,20,splenium of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_21,13.1057333333333,-1.86335133333333,-14.57104,0,0,0,1,1,1,0,21,R AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_22,-13.4179866666667,-1.780824,-14.5191,0,0,0,1,1,1,0,22,L AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_23,7.60346666666667,-5.445,-8.81046666666667,0,0,0,1,1,1,0,23,R superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_24,-7.95793333333333,-5.40726666666667,-8.81153333333333,0,0,0,1,1,1,0,24,L superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_25,8.1622,-2.85706666666667,-14.1586,0,0,0,1,1,1,0,25,R inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_26,-8.4912,-2.82386666666667,-14.1978,0,0,0,1,1,1,0,26,L inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_27,7.41781466666667,-20.9339533333333,5.63792333333333,0,0,0,1,1,1,0,27,R indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_28,-7.702888,-20.88134,5.59702466666667,0,0,0,1,1,1,0,28,L indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_29,16.3349133333333,-24.98622,0.917984933333333,0,0,0,1,1,1,0,29,R ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_30,-16.6607266666667,-24.9668066666667,0.914438466666667,0,0,0,1,1,1,0,30,L ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_31,4.58283066666667,13.3663933333333,3.26909266666667,0,0,0,1,1,1,0,31,R olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_32,-5.05380266666667,13.2956866666667,3.23346066666667,0,0,0,1,1,1,0,32,L olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 diff --git a/afidsvalidator/afids-templates/macaca/tpl-INIA19_afids.fcsv b/afidsvalidator/afids-templates/macaca/tpl-INIA19_afids.fcsv new file mode 100644 index 00000000..c2531195 --- /dev/null +++ b/afidsvalidator/afids-templates/macaca/tpl-INIA19_afids.fcsv @@ -0,0 +1,35 @@ +# Markups fiducial file version = 4.6 +# CoordinateSystem = 0 +# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID +vtkMRMLMarkupsFiducialNode_1,-0.193375,0.088375,0.2631875,0,0,0,1,1,1,0,1,AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_2,-0.1525625,-12.9968125,0.0006875,0,0,0,1,1,1,0,2,PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_3,-0.216,-19.7260625,-3.4704375,0,0,0,1,1,1,0,3,infracollicular sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_4,-0.16075,-10.7433125,-9.3593125,0,0,0,1,1,1,0,4,PMJ,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_5,-0.2194375,-7.841125,-5.266125,0,0,0,1,1,1,0,5,superior interpeduncular fossa,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_6,6.76616125,-14.37709375,-4.7679675,0,0,0,1,1,1,0,6,R superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_7,-7.07919125,-14.32365,-4.91882375,0,0,0,1,1,1,0,7,L superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_8,6.416778125,-15.15824375,-8.782543125,0,0,0,1,1,1,0,8,R inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_9,-6.802196875,-14.99663125,-9.010619375,0,0,0,1,1,1,0,9,L inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_10,-0.1705976875,-29.76313125,4.388538125,0,0,0,1,1,1,0,10,culmen,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_11,-0.1375,-5.114125,-6.151,0,0,0,1,1,1,0,11,intermammillary sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_12,0.987844125,-5.26671625,-5.97335125,0,0,0,1,1,1,0,12,R MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_13,-1.44810625,-5.296490625,-5.947996875,0,0,0,1,1,1,0,13,L MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_14,-0.177394125,-16.913725,1.710676875,0,0,0,1,1,1,0,14,pineal gland,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_15,3.594125,-0.02425,9.1958125,0,0,0,1,1,1,0,15,R LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_16,-3.996625,-0.039625,9.208125,0,0,0,1,1,1,0,16,L LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_17,7.179,-13.176125,8.6773125,0,0,0,1,1,1,0,17,R LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_18,-7.46425,-13.1858125,8.51175,0,0,0,1,1,1,0,18,L LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_19,-0.2415625,11.6924375,7.926,0,0,0,1,1,1,0,19,genu of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_20,-0.1240625,-17.668375,2.9224375,0,0,0,1,1,1,0,20,splenium of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_21,14.0145,-1.5799375,-12.9796875,0,0,0,1,1,1,0,21,R AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_22,-14.3650125,-1.2189525,-13.1778625,0,0,0,1,1,1,0,22,L AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_23,8.0909375,-4.402,-8.93825,0,0,0,1,1,1,0,23,R superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_24,-9.0351875,-4.5783125,-8.91225,0,0,0,1,1,1,0,24,L superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_25,10.080375,-2.2085625,-12.406625,0,0,0,1,1,1,0,25,R inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_26,-10.2415625,-2.3674375,-12.8078125,0,0,0,1,1,1,0,26,L inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_27,8.040926875,-20.493525,2.26453125,0,0,0,1,1,1,0,27,R indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_28,-8.472299375,-20.51625625,2.381066875,0,0,0,1,1,1,0,28,L indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_29,16.016725,-23.318175,-1.614628125,0,0,0,1,1,1,0,29,R ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_30,-16.04579375,-23.4388625,-1.75378625,0,0,0,1,1,1,0,30,L ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_31,4.852285,12.4047625,5.47498,0,0,0,1,1,1,0,31,R olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_32,-5.90880125,12.2564125,5.5147125,0,0,0,1,1,1,0,32,L olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 diff --git a/afidsvalidator/afids-templates/macaca/tpl-MacaqueMNI_afids.fcsv b/afidsvalidator/afids-templates/macaca/tpl-MacaqueMNI_afids.fcsv new file mode 100644 index 00000000..31f5ca09 --- /dev/null +++ b/afidsvalidator/afids-templates/macaca/tpl-MacaqueMNI_afids.fcsv @@ -0,0 +1,35 @@ +# Markups fiducial file version = 4.6 +# CoordinateSystem = 0 +# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID +vtkMRMLMarkupsFiducialNode_1,-0.1525,0.4425625,-0.682,0,0,0,1,1,1,0,1,AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_2,-0.25540225,-12.445075,0.7061414375,0,0,0,1,1,1,0,2,PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_3,-0.28090875,-18.8221875,-2.358905625,0,0,0,1,1,1,0,3,infracollicular sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_4,-0.2053125,-10.808125,-9.1360625,0,0,0,1,1,1,0,4,PMJ,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_5,-0.1660625,-7.7131875,-5.22925,0,0,0,1,1,1,0,5,superior interpeduncular fossa,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_6,5.928510625,-13.92930625,-4.000454375,0,0,0,1,1,1,0,6,R superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_7,-6.560353125,-13.8463125,-4.19377,0,0,0,1,1,1,0,7,L superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_8,5.68814875,-14.6034625,-8.035674375,0,0,0,1,1,1,0,8,R inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_9,-5.92688625,-14.5166625,-8.193253125,0,0,0,1,1,1,0,9,L inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_10,-0.2503125,-25.9071875,6.0988125,0,0,0,1,1,1,0,10,culmen,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_11,-0.077375,-5.0741875,-6.4248125,0,0,0,1,1,1,0,11,intermammillary sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_12,1.0798125,-5.2316875,-6.2721875,0,0,0,1,1,1,0,12,R MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_13,-1.2868125,-5.2730625,-6.3146875,0,0,0,1,1,1,0,13,L MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_14,-0.268704125,-15.43580625,2.401966875,0,0,0,1,1,1,0,14,pineal gland,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_15,3.0006875,1.267875,8.3075,0,0,0,1,1,1,0,15,R LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_16,-3.49825,1.2855625,8.200625,0,0,0,1,1,1,0,16,L LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_17,6.3436875,-11.6543125,9.1398125,0,0,0,1,1,1,0,17,R LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_18,-6.6864375,-11.53625,8.9915625,0,0,0,1,1,1,0,18,L LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_19,-0.18125,11.1011875,6.292,0,0,0,1,1,1,0,19,genu of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_20,-0.2841875,-16.0635625,3.7816875,0,0,0,1,1,1,0,20,splenium of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_21,11.90005,-2.438523125,-13.0738625,0,0,0,1,1,1,0,21,R AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_22,-12.41914375,-2.47109875,-13.88066875,0,0,0,1,1,1,0,22,L AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_23,7.2395625,-4.1586875,-9.254375,0,0,0,1,1,1,0,23,R superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_24,-7.66971625,-4.36832625,-9.954780625,0,0,0,1,1,1,0,24,L superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_25,8.1785,-2.914375,-12.9280625,0,0,0,1,1,1,0,25,R inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_26,-8.57575,-3.09675,-13.5783125,0,0,0,1,1,1,0,26,L inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_27,6.385324375,-18.50754375,3.695403125,0,0,0,1,1,1,0,27,R indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_28,-6.658700625,-18.5387875,3.43432125,0,0,0,1,1,1,0,28,L indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_29,13.9107125,-22.3884,0.8325,0,0,0,1,1,1,0,29,R ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_30,-14.35241875,-22.70195,0.0955496875,0,0,0,1,1,1,0,30,L ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_31,4.69858375,12.4882625,3.502166875,0,0,0,1,1,1,0,31,R olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_32,-5.22115875,12.27171875,3.30565375,0,0,0,1,1,1,0,32,L olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 diff --git a/afidsvalidator/afids-templates/macaca/tpl-NMTv1.3_afids.fcsv b/afidsvalidator/afids-templates/macaca/tpl-NMTv1.3_afids.fcsv new file mode 100644 index 00000000..9852f7a2 --- /dev/null +++ b/afidsvalidator/afids-templates/macaca/tpl-NMTv1.3_afids.fcsv @@ -0,0 +1,35 @@ +# Markups fiducial file version = 4.6 +# CoordinateSystem = 0 +# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID +vtkMRMLMarkupsFiducialNode_1,-0.1445625,-0.3681875,-0.336,0,0,0,1,1,1,0,1,AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_2,-0.235375,-13.265,1.5816875,0,0,0,1,1,1,0,2,PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_3,-0.17275,-20.46925,-0.992125,0,0,0,1,1,1,0,3,infracollicular sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_4,-0.0499375,-12.5635625,-8.222625,0,0,0,1,1,1,0,4,PMJ,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_5,-0.0751875,-9.2508125,-4.4854375,0,0,0,1,1,1,0,5,superior interpeduncular fossa,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_6,6.66711875,-15.37825625,-3.071816875,0,0,0,1,1,1,0,6,R superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_7,-6.886910625,-15.37699375,-3.236519375,0,0,0,1,1,1,0,7,L superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_8,6.601545,-16.4498375,-6.962368125,0,0,0,1,1,1,0,8,R inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_9,-6.57792375,-16.36333125,-7.11506,0,0,0,1,1,1,0,9,L inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_10,-0.27807899375,-26.58195625,7.7371125,0,0,0,1,1,1,0,10,culmen,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_11,-0.0425,-6.6723125,-6.021125,0,0,0,1,1,1,0,11,intermammillary sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_12,1.032534375,-7.0002275,-5.478975625,0,0,0,1,1,1,0,12,R MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_13,-1.114105625,-7.044663125,-5.53065625,0,0,0,1,1,1,0,13,L MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_14,-0.2946875,-16.440875,3.681375,0,0,0,1,1,1,0,14,pineal gland,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_15,3.605125,0.962375,8.7104375,0,0,0,1,1,1,0,15,R LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_16,-4.136625,0.9684375,8.572875,0,0,0,1,1,1,0,16,L LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_17,7.105625,-11.9369375,10.0565625,0,0,0,1,1,1,0,17,R LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_18,-7.6420625,-11.863125,9.8369375,0,0,0,1,1,1,0,18,L LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_19,-0.2144375,11.63725,5.201875,0,0,0,1,1,1,0,19,genu of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_20,-0.3206875,-16.6750625,4.92025,0,0,0,1,1,1,0,20,splenium of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_21,13.6636875,-3.852625,-12.9245,0,0,0,1,1,1,0,21,R AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_22,-13.44820625,-3.75252625,-13.2946875,0,0,0,1,1,1,0,22,L AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_23,7.7138125,-6.0359375,-8.85475,0,0,0,1,1,1,0,23,R superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_24,-7.837173125,-6.116948125,-9.15375125,0,0,0,1,1,1,0,24,L superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_25,8.97875,-4.2975,-12.7649375,0,0,0,1,1,1,0,25,R inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_26,-8.748625,-4.2636875,-13.1769375,0,0,0,1,1,1,0,26,L inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_27,8.024435625,-20.0366625,5.476778125,0,0,0,1,1,1,0,27,R indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_28,-8.38731125,-19.96438125,5.280025,0,0,0,1,1,1,0,28,L indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_29,15.54165625,-26.1188,3.747598125,0,0,0,1,1,1,0,29,R ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_30,-15.87663125,-25.64653125,3.335800625,0,0,0,1,1,1,0,30,L ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_31,4.974789375,12.44625,2.46409125,0,0,0,1,1,1,0,31,R olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_32,-5.724045,12.52948125,2.37324875,0,0,0,1,1,1,0,32,L olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 diff --git a/afidsvalidator/afids-templates/macaca/tpl-NMTv2.0asym_afids.fcsv b/afidsvalidator/afids-templates/macaca/tpl-NMTv2.0asym_afids.fcsv new file mode 100644 index 00000000..67fca31d --- /dev/null +++ b/afidsvalidator/afids-templates/macaca/tpl-NMTv2.0asym_afids.fcsv @@ -0,0 +1,35 @@ +# Markups fiducial file version = 4.6 +# CoordinateSystem = 0 +# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID +vtkMRMLMarkupsFiducialNode_0,0.017712306194739003,19.487752704941716,15.314483484676307,0.0,0.0,0.0,1.0,1,1,0,1,AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_1,0.02507324150872536,6.5001968692204875,14.470492570396765,0.0,0.0,0.0,1.0,1,1,0,2,PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_8,0.06442593327277103,-0.15965139217043003,10.375316818009642,0.0,0.0,0.0,1.0,1,1,0,3,infracollicular sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_9,0.09695604666688457,9.265816164861734,4.983816818009642,0.0,0.0,0.0,1.0,1,1,0,4,PMJ,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_10,0.07145481432352847,11.758029209924914,9.373650151342977,0.0,0.0,0.0,1.0,1,1,0,5,superior interpeduncular fossa,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_11,6.977348200500579,5.378365350001412,9.44973348467631,0.0,0.0,0.0,1.0,1,1,0,6,R superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_12,-6.800657625097212,5.4475541657561415,9.42148348467631,0.0,0.0,0.0,1.0,1,1,0,7,L superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_13,6.74008527933924,5.298340724688141,5.314084929890324,0.0,0.0,0.0,1.0,1,1,0,8,R inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_14,-6.590634602624992,5.402942753646125,5.264310044425934,0.0,0.0,0.0,1.0,1,1,0,9,L inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_15,0.08141666666666672,-7.8352608496132605,17.54030945183531,0.0,0.0,0.0,1.0,1,1,0,10,culmen,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_16,0.07717123538604066,14.65491039017895,8.210161231445943,0.0,0.0,0.0,1.0,1,1,0,11,intermammillary sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_17,1.2688402122617857,14.13169514231175,8.943494564779277,0.0,0.0,0.0,1.0,1,1,0,12,R MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_18,-1.117837406615059,14.117983365304875,8.929161231445944,0.0,0.0,0.0,1.0,1,1,0,13,L MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_19,0.05658861845621968,3.069807485816424,15.839583333333332,0.0,0.0,0.0,1.0,1,1,0,14,PG,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_20,4.451996843451092,18.8782419601866,24.61933333333333,0.0,0.0,0.0,1.0,1,1,0,15,R LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_21,-4.11949706399852,18.831834104060285,24.669583333333332,0.0,0.0,0.0,1.0,1,1,0,16,L LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_22,7.547715764277959,5.965671638768311,22.936416666666663,0.0,0.0,0.0,1.0,1,1,0,17,R LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_23,-7.217390318508649,5.8615400434078255,22.933583333333335,0.0,0.0,0.0,1.0,1,1,0,18,L LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_24,0.051701710089667834,29.771102742966722,23.20975,0.0,0.0,0.0,1.0,1,1,0,19,genu of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_25,0.048807124904610644,2.3809993316120925,16.91342172091821,0.0,0.0,0.0,1.0,1,1,0,20,splenium of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_26,13.630206738735046,18.951922956490428,1.9276682056104553,0.0,0.0,0.0,1.0,1,1,0,21,R AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_27,-13.453007880555313,19.043484620942216,1.8324182056104552,0.0,0.0,0.0,1.0,1,1,0,22,L AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_28,7.7781795356474355,15.847431906184726,5.643083333333332,0.0,0.0,0.0,1.0,1,1,0,23,R superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_29,-7.621876358066017,15.76534380988375,5.649249999999999,0.0,0.0,0.0,1.0,1,1,0,24,L superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_30,8.864692140485749,18.495101897673837,2.1042732334634047,0.0,0.0,0.0,1.0,1,1,0,25,R inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_31,-8.559627961778657,18.4638541363551,2.0792962441972,0.0,0.0,0.0,1.0,1,1,0,26,L inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_32,8.991028224369595,-0.7428333418601726,16.278961914111196,0.0,0.0,0.0,1.0,1,1,0,27,R indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_33,-8.56842220766469,-0.8226484996903669,16.40748864754982,0.0,0.0,0.0,1.0,1,1,0,28,L indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_34,15.755055460105135,-5.730513345214814,13.43473864754982,0.0,0.0,0.0,1.0,1,1,0,29,R ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_35,-15.440053436467146,-5.509345111742168,13.630988647549819,0.0,0.0,0.0,1.0,1,1,0,30,L ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_36,5.245529717783376,31.16699076681327,20.75807198088315,0.0,0.0,0.0,1.0,1,1,0,31,R olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_37,-5.4082911727647485,31.158702247293167,20.80748864754982,0.0,0.0,0.0,1.0,1,1,0,32,L olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 diff --git a/afidsvalidator/afids-templates/macaca/tpl-Yerkes19_afids.fcsv b/afidsvalidator/afids-templates/macaca/tpl-Yerkes19_afids.fcsv new file mode 100644 index 00000000..1217839c --- /dev/null +++ b/afidsvalidator/afids-templates/macaca/tpl-Yerkes19_afids.fcsv @@ -0,0 +1,35 @@ +# Markups fiducial file version = 4.6 +# CoordinateSystem = 0 +# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID +vtkMRMLMarkupsFiducialNode_1,-0.44425,-0.3698125,0.74,0,0,0,1,1,1,0,1,AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_2,-0.4267771875,-14.1443125,0.6506348125,0,0,0,1,1,1,0,2,PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_3,-0.4103589375,-19.72289375,-3.428631875,0,0,0,1,1,1,0,3,infracollicular sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_4,-0.41825,-9.8081875,-9.2228125,0,0,0,1,1,1,0,4,PMJ,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_5,-0.4371716875,-7.882514375,-4.486480625,0,0,0,1,1,1,0,5,superior interpeduncular fossa,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_6,6.6565,-14.3133125,-4.7779375,0,0,0,1,1,1,0,6,R superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_7,-7.6025,-14.3045625,-4.8665625,0,0,0,1,1,1,0,7,L superior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_8,6.5045625,-14.35225,-8.9871875,0,0,0,1,1,1,0,8,R inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_9,-7.5005625,-14.4080625,-8.935125,0,0,0,1,1,1,0,9,L inferior LMS,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_10,-0.4625710625,-27.673775,2.92126,0,0,0,1,1,1,0,10,culmen,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_11,-0.5190650625,-5.25716625,-5.947925,0,0,0,1,1,1,0,11,intermammillary sulcus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_12,0.6485614375,-5.47547375,-5.70790625,0,0,0,1,1,1,0,12,R MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_13,-1.71992125,-5.5072525,-5.697660625,0,0,0,1,1,1,0,13,L MB,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_14,-0.4638439375,-18.2865625,1.763011875,0,0,0,1,1,1,0,14,pineal gland,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_15,2.6965,-0.329125,8.820625,0,0,0,1,1,1,0,15,R LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_16,-3.81675,-0.275625,8.870625,0,0,0,1,1,1,0,16,L LV at AC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_17,6.2923125,-14.04675,8.5334375,0,0,0,1,1,1,0,17,R LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_18,-7.172125,-14.0260625,8.579625,0,0,0,1,1,1,0,18,L LV at PC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_19,-0.432125,11.2950625,7.593,0,0,0,1,1,1,0,19,genu of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_20,-0.417125,-18.6766875,2.823375,0,0,0,1,1,1,0,20,splenium of CC,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_21,13.6163125,-1.793125,-13.138875,0,0,0,1,1,1,0,21,R AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_22,-14.70824375,-2.2890875,-12.69210625,0,0,0,1,1,1,0,22,L AL temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_23,7.392625,-4.0513125,-9.181875,0,0,0,1,1,1,0,23,R superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_24,-8.6914375,-4.4888125,-9.145375,0,0,0,1,1,1,0,24,L superior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_25,9.3619375,-2.6685625,-13.3648125,0,0,0,1,1,1,0,25,R inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_26,-10.905,-3.2800625,-13.1568125,0,0,0,1,1,1,0,26,L inferior AM temporal horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_27,6.880100625,-20.46729375,1.99231,0,0,0,1,1,1,0,27,R indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_28,-7.813145,-20.4593625,2.1077025,0,0,0,1,1,1,0,28,L indusium griseum origin,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_29,15.40296875,-23.8728125,-1.439950625,0,0,0,1,1,1,0,29,R ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_30,-15.9348125,-24.46655,-1.131631875,0,0,0,1,1,1,0,30,L ventral occipital horn,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_31,4.71014875,11.98429375,4.73296,0,0,0,1,1,1,0,31,R olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 +vtkMRMLMarkupsFiducialNode_32,-5.52772875,11.9167,4.754100625,0,0,0,1,1,1,0,32,L olfactory sulcal fundus,vtkMRMLScalarVolumeNode1 diff --git a/afidsvalidator/landmark_info.py b/afidsvalidator/landmark_info.py new file mode 100644 index 00000000..cfba38fb --- /dev/null +++ b/afidsvalidator/landmark_info.py @@ -0,0 +1,536 @@ +"""Anatomical descriptions and placement guidance for the 32 AFIDs landmarks.""" + +# Keys match the abbreviation used as the attribute name on the Afids object +# (i.e., EXPECTED_DESCS[i][-1]). +LANDMARK_INFO = { + "AC": { + "full_name": "Anterior Commissure", + "description": ( + "A white matter tract connecting the two temporal lobes, crossing " + "the midline at the base of the septum pellucidum." + ), + "key_features": ( + "On a midsagittal slice, look for a small hyperintense (bright) " + "bundle just anterior to the columns of the fornix and inferior " + "to the genu of the corpus callosum. The AC sits directly at the " + "midline where the lamina terminalis meets the anterior " + "commissure itself." + ), + "common_mistakes": ( + "Placing too posteriorly (confusing with the fornix columns) or " + "too superiorly (landing on the genu of CC). Stay at the " + "midline — any lateral offset is an error." + ), + }, + "PC": { + "full_name": "Posterior Commissure", + "description": ( + "A small white matter tract at the dorsal aspect of the cerebral " + "aqueduct, marking the posterior boundary of the third ventricle." + ), + "key_features": ( + "On midsagittal: find the aqueduct of Sylvius and trace it " + "superiorly to where it opens into the third ventricle. The PC " + "lies at this junction, just superior to the superior colliculi " + "and inferior to the habenular commissure." + ), + "common_mistakes": ( + "Placing on the habenular commissure (too superior) or on the " + "superior colliculi (too inferior and posterior)." + ), + }, + "ICS": { + "full_name": "Infracollicular Sulcus", + "description": ( + "The sulcus immediately inferior to the inferior colliculi on " + "the posterior surface of the midbrain." + ), + "key_features": ( + "On sagittal: identify the quadrigeminal plate (four bumps on " + "the dorsal midbrain). The ICS is the groove just below the " + "inferior colliculi, where the midbrain meets the superior " + "medullary velum." + ), + "common_mistakes": ( + "Confusing with the intercollicular sulcus (the groove between " + "superior and inferior colliculi). The ICS is inferior to both " + "pairs of colliculi." + ), + }, + "PMJ": { + "full_name": "Pontomedullary Junction", + "description": ( + "The anatomical boundary between the pons and the medulla " + "oblongata on the ventral brainstem." + ), + "key_features": ( + "On sagittal: the ventral pons has a characteristic bulge " + "(the basis pontis). The PMJ is where this bulge ends and " + "the more tapered medulla begins — look for the change in " + "ventral contour. On axial: the disappearance of the " + "transverse pontine fibres." + ), + "common_mistakes": ( + "Placing too superiorly (within the pons) or too inferiorly " + "(within the medulla). The junction is often subtler on " + "standard T1 — use multiple planes." + ), + }, + "SIPF": { + "full_name": "Superior Interpeduncular Fossa", + "description": ( + "The apex of the interpeduncular fossa, the CSF-filled space " + "between the cerebral peduncles on the ventral midbrain." + ), + "key_features": ( + "On coronal or axial: the interpeduncular fossa appears as a " + "dark (CSF) inverted V between the two cerebral peduncles. " + "Place at the most superior tip of this space, at the level " + "of the mammillary bodies." + ), + "common_mistakes": ( + "Placing at the midpoint of the fossa rather than its most " + "superior extent. Confirm on sagittal that placement is " + "superior to the pons." + ), + }, + "RSLMS": { + "full_name": "Right Superior Lateral Midbrain Sulcus", + "description": ( + "The sulcus at the superior lateral margin of the right " + "midbrain, at the midbrain–thalamus interface." + ), + "key_features": ( + "On axial at the level of the superior colliculi: identify " + "the lateral margin of the midbrain. The RSLMS is the groove " + "where the superior aspect of the right midbrain meets the " + "overlying thalamus/pulvinar." + ), + "common_mistakes": ( + "Placing on the midbrain surface rather than in the sulcal " + "fundus. Confirm the left–right placement by ensuring " + "symmetry with LSLMS." + ), + }, + "LSLMS": { + "full_name": "Left Superior Lateral Midbrain Sulcus", + "description": ( + "The sulcus at the superior lateral margin of the left " + "midbrain, at the midbrain–thalamus interface." + ), + "key_features": ( + "Mirror of RSLMS. On axial at the level of the superior " + "colliculi: the groove where the superior aspect of the left " + "midbrain meets the overlying thalamus/pulvinar." + ), + "common_mistakes": ( + "Placing on the midbrain surface rather than in the sulcal " + "fundus. Check bilateral symmetry with RSLMS." + ), + }, + "RILMS": { + "full_name": "Right Inferior Lateral Midbrain Sulcus", + "description": ( + "The sulcus at the inferior lateral margin of the right " + "midbrain, at the midbrain–pons interface." + ), + "key_features": ( + "On axial at the level of the inferior colliculi or superior " + "pons: the groove at the lateral inferior margin of the right " + "midbrain, where it blends with the superior pons." + ), + "common_mistakes": ( + "Confusing with RSLMS — RILMS is more inferior and anterior. " + "Use coronal plane to confirm the inferior extent." + ), + }, + "LILMS": { + "full_name": "Left Inferior Lateral Midbrain Sulcus", + "description": ( + "The sulcus at the inferior lateral margin of the left " + "midbrain, at the midbrain–pons interface." + ), + "key_features": ( + "Mirror of RILMS. On axial at the level of the inferior " + "colliculi: the groove at the lateral inferior margin of the " + "left midbrain." + ), + "common_mistakes": ( + "Confusing with LSLMS — LILMS is more inferior. Confirm " + "bilateral symmetry with RILMS." + ), + }, + "CUL": { + "full_name": "Culmen", + "description": ( + "The most superior point of the anterior lobe of the " + "cerebellar vermis (lobule IV/V)." + ), + "key_features": ( + "On midsagittal: follow the superior surface of the cerebellum " + "to its highest midline point. The culmen is the peak of the " + "anterior vermis, immediately posterior to the primary fissure." + ), + "common_mistakes": ( + "Placing on the declive (posterior to the primary fissure) " + "or on a lateral cerebellar hemisphere rather than the midline " + "vermis." + ), + }, + "IMS": { + "full_name": "Intermammillary Sulcus", + "description": ( + "The midpoint of the sulcus between the two mammillary bodies " + "on the inferior hypothalamus." + ), + "key_features": ( + "On coronal: identify the two round mammillary bodies just " + "posterior to the tuber cinereum. The IMS is placed at the " + "midpoint of the groove between them. On axial: two bright " + "dots separated by a narrow sulcus at the floor of the " + "diencephalon." + ), + "common_mistakes": ( + "Placing on one of the mammillary bodies rather than the " + "sulcus between them. The IMS must be at the midline." + ), + }, + "RMB": { + "full_name": "Right Mammillary Body", + "description": ( + "The right mammillary body, a posterior hypothalamic nucleus " + "involved in memory circuits." + ), + "key_features": ( + "On coronal: the right of the two round structures at the " + "floor of the diencephalon, just anterior to the midbrain. " + "On axial: appears as a bright dot just right of midline, " + "posterior to the tuber cinereum." + ), + "common_mistakes": ( + "Placing on the IMS midpoint instead of the centre of the " + "right body, or landing too superiorly in the hypothalamus." + ), + }, + "LMB": { + "full_name": "Left Mammillary Body", + "description": ( + "The left mammillary body, a posterior hypothalamic nucleus " + "involved in memory circuits." + ), + "key_features": ( + "Mirror of RMB. On coronal: the left of the two round " + "structures at the floor of the diencephalon." + ), + "common_mistakes": ( + "Placing on the IMS midpoint instead of the centre of the " + "left body, or landing too superiorly in the hypothalamus." + ), + }, + "PG": { + "full_name": "Pineal Gland", + "description": ( + "A small midline neuroendocrine gland at the posterior roof " + "of the third ventricle." + ), + "key_features": ( + "On midsagittal: posterior to the habenular commissure and " + "superior to the superior colliculi. Often appears as a " + "calcified (hyperdense on CT, variable on MRI) nodule. " + "Place at the centre of the gland." + ), + "common_mistakes": ( + "Confusing with the habenular commissure (too anterior/superior) " + "or placing on the superior colliculi (too inferior). The PG " + "sits in the pineal recess, not on the quadrigeminal plate." + ), + }, + "RLVAC": { + "full_name": "Right Lateral Ventricle at AC", + "description": ( + "The most lateral point of the right lateral ventricle body " + "at the coronal level of the anterior commissure." + ), + "key_features": ( + "On a coronal slice through the AC: find the body of the " + "right lateral ventricle and place at its most lateral " + "extent. The septum pellucidum separates left and right." + ), + "common_mistakes": ( + "Using the wrong coronal level — must be at the AC, not " + "anterior or posterior to it. Confirm AC location on sagittal " + "first, then switch to coronal." + ), + }, + "LLVAC": { + "full_name": "Left Lateral Ventricle at AC", + "description": ( + "The most lateral point of the left lateral ventricle body " + "at the coronal level of the anterior commissure." + ), + "key_features": ( + "Mirror of RLVAC. On coronal at AC level: most lateral " + "extent of the left lateral ventricle body." + ), + "common_mistakes": ( + "Same as RLVAC — confirm AC level on sagittal first. " + "Check bilateral symmetry." + ), + }, + "RLVPC": { + "full_name": "Right Lateral Ventricle at PC", + "description": ( + "The most lateral point of the right lateral ventricle body " + "at the coronal level of the posterior commissure." + ), + "key_features": ( + "On a coronal slice through the PC: the right lateral " + "ventricle body at its most lateral point. The ventricle " + "is typically narrower here than at the AC level." + ), + "common_mistakes": ( + "Confusing the PC level with the AC level. Identify PC on " + "sagittal first (dorsal aqueduct opening), then move to " + "coronal." + ), + }, + "LLVPC": { + "full_name": "Left Lateral Ventricle at PC", + "description": ( + "The most lateral point of the left lateral ventricle body " + "at the coronal level of the posterior commissure." + ), + "key_features": ( + "Mirror of RLVPC. Coronal at PC level: most lateral extent " + "of the left lateral ventricle." + ), + "common_mistakes": ( + "Same as RLVPC — confirm PC level before switching planes. " + "Check bilateral symmetry." + ), + }, + "GENU": { + "full_name": "Genu of the Corpus Callosum", + "description": ( + "The anteriormost bend of the corpus callosum where it " + "curves inferiorly toward the rostrum." + ), + "key_features": ( + "On midsagittal: the anterior end of the corpus callosum " + "where the fibres curve forward and downward. Place at the " + "most anterior and inferior point of this bend, not at the " + "superior surface." + ), + "common_mistakes": ( + "Placing on the superior surface of the CC rather than the " + "anterior-inferior curve, or confusing with the rostrum " + "(the thin portion below the genu)." + ), + }, + "SPLE": { + "full_name": "Splenium of the Corpus Callosum", + "description": ( + "The posterior thickened end of the corpus callosum, " + "connecting the occipital and parietal lobes." + ), + "key_features": ( + "On midsagittal: the bulbous posterior terminus of the " + "corpus callosum. Place at the most posterior point of " + "the splenium, at the midline." + ), + "common_mistakes": ( + "Placing too anteriorly (on the posterior body rather than " + "the splenium), or laterally off the midline." + ), + }, + "RALTH": { + "full_name": "Right Anterolateral Temporal Horn", + "description": ( + "The anterolateral tip of the right temporal horn of the " + "lateral ventricle." + ), + "key_features": ( + "On axial or coronal: follow the temporal horn anteriorly " + "until it terminates. Place at the most anterior and lateral " + "extent of the CSF-filled horn." + ), + "common_mistakes": ( + "Placing within the horn rather than at its tip, or " + "confusing the choroid fissure with the horn lumen." + ), + }, + "LALTH": { + "full_name": "Left Anterolateral Temporal Horn", + "description": ( + "The anterolateral tip of the left temporal horn of the " + "lateral ventricle." + ), + "key_features": ( + "Mirror of RALTH. Axial or coronal: most anterior and " + "lateral extent of the left temporal horn CSF space." + ), + "common_mistakes": ( + "Placing within the horn body rather than at its anterior " + "tip. Check bilateral symmetry with RALTH." + ), + }, + "RSAMTH": { + "full_name": "Right Superior Anteromedial Temporal Horn", + "description": ( + "The superior anteromedial extent of the right temporal horn, " + "near the amygdala." + ), + "key_features": ( + "On coronal anterior to the hippocampal head: find the " + "superomedial corner of the temporal horn where it abuts " + "the amygdala. This is a tight, CSF-filled angle." + ), + "common_mistakes": ( + "Placing too laterally (on the anterolateral horn) or " + "confusing with RIAMTH below. RSAMTH is the superior corner." + ), + }, + "LSAMTH": { + "full_name": "Left Superior Anteromedial Temporal Horn", + "description": ( + "The superior anteromedial extent of the left temporal horn, " + "near the amygdala." + ), + "key_features": ( + "Mirror of RSAMTH. Coronal anterior to hippocampal head: " + "superomedial corner of the left temporal horn." + ), + "common_mistakes": ( + "Confusing with LIAMTH (inferior corner) or LALTH (lateral). " + "Check bilateral symmetry." + ), + }, + "RIAMTH": { + "full_name": "Right Inferior Anteromedial Temporal Horn", + "description": ( + "The inferior anteromedial extent of the right temporal horn, " + "near the hippocampal head." + ), + "key_features": ( + "On coronal at the hippocampal head: the inferomedial corner " + "of the right temporal horn where the horn curves around the " + "hippocampus. Inferior to RSAMTH in the same coronal plane." + ), + "common_mistakes": ( + "Placing on the hippocampal surface rather than in the " + "CSF-filled corner of the horn, or confusing with RSAMTH " + "(which is superior)." + ), + }, + "LIAMTH": { + "full_name": "Left Inferior Anteromedial Temporal Horn", + "description": ( + "The inferior anteromedial extent of the left temporal horn, " + "near the hippocampal head." + ), + "key_features": ( + "Mirror of RIAMTH. Coronal at hippocampal head: inferomedial " + "corner of the left temporal horn." + ), + "common_mistakes": ( + "Confusing with LSAMTH (superior corner). Verify by " + "checking RIAMTH for bilateral symmetry." + ), + }, + "RIGO": { + "full_name": "Right Indusium Griseum Origin", + "description": ( + "The origin of the right indusium griseum — a thin grey " + "matter strip on the superior surface of the corpus callosum " + "continuous with the dentate gyrus." + ), + "key_features": ( + "On sagittal: trace the superior surface of the genu of the " + "CC posteriorly. The RIGO is at the point where the indusium " + "griseum first appears on the right side, just posterior to " + "the genu on the CC surface." + ), + "common_mistakes": ( + "Placing on the CC body surface rather than at the origin " + "point, or confusing with the cingulate gyrus above." + ), + }, + "LIGO": { + "full_name": "Left Indusium Griseum Origin", + "description": ( + "The origin of the left indusium griseum on the superior " + "surface of the corpus callosum." + ), + "key_features": ( + "Mirror of RIGO. Sagittal: posterior to the genu on the " + "left surface of the CC where the indusium griseum begins." + ), + "common_mistakes": ( + "Same as RIGO — avoid placing on the cingulate gyrus or " + "mid-body of the CC." + ), + }, + "RVOH": { + "full_name": "Right Ventral Occipital Horn", + "description": ( + "The most ventral and posterior tip of the right occipital " + "horn of the lateral ventricle." + ), + "key_features": ( + "On axial: follow the occipital horn posteriorly to its " + "terminal point. Place at the most posterior–inferior " + "tip of the right CSF-filled horn." + ), + "common_mistakes": ( + "Placing mid-horn rather than at the terminal tip, or " + "including the calcar avis (the white matter bulge into " + "the occipital horn) as part of the CSF space." + ), + }, + "LVOH": { + "full_name": "Left Ventral Occipital Horn", + "description": ( + "The most ventral and posterior tip of the left occipital " + "horn of the lateral ventricle." + ), + "key_features": ( + "Mirror of RVOH. Axial: most posterior–inferior tip of the " + "left occipital horn." + ), + "common_mistakes": ( + "Same as RVOH. Check bilateral symmetry with RVOH." + ), + }, + "ROSF": { + "full_name": "Right Olfactory Sulcal Fundus", + "description": ( + "The deepest point of the right olfactory sulcus on the " + "orbital (inferior) surface of the right frontal lobe." + ), + "key_features": ( + "On coronal through the frontal lobe: the olfactory sulcus " + "is a vertical groove on the orbital surface housing the " + "olfactory tract. Place at the deepest point (fundus) of " + "this sulcus on the right side." + ), + "common_mistakes": ( + "Placing on the olfactory tract itself rather than the " + "sulcal fundus, or confusing with the medial orbital sulcus." + ), + }, + "LOSF": { + "full_name": "Left Olfactory Sulcal Fundus", + "description": ( + "The deepest point of the left olfactory sulcus on the " + "orbital surface of the left frontal lobe." + ), + "key_features": ( + "Mirror of ROSF. Coronal: deepest point of the left " + "olfactory sulcus on the orbital frontal surface." + ), + "common_mistakes": ( + "Same as ROSF — check bilateral symmetry and use the " + "olfactory tract as a guide to find the sulcus." + ), + }, +} diff --git a/afidsvalidator/static/lib/afidsvalidator-react/styles/afids_custom.css b/afidsvalidator/static/lib/afidsvalidator-react/styles/afids_custom.css index 1e50f259..ddccf09c 100644 --- a/afidsvalidator/static/lib/afidsvalidator-react/styles/afids_custom.css +++ b/afidsvalidator/static/lib/afidsvalidator-react/styles/afids_custom.css @@ -1,26 +1,59 @@ -:root { - /* Global colours */ - --afids-green: #00ff01; - --dark: #000; - --light: #fff; +/* ===================================================================== + AFIDs Validator — Design System + ===================================================================== */ +@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Space+Mono:wght@400;700&family=Inter:wght@300;400;500;600&display=swap'); - /* Fonts */ - --nav-font: 'Bebas Neue', sans-serif; - --nav-fontsize: 36px; - --def-font: "Roboto", sans-serif; - --def-fontsize: 20px; - --foot-font: "Roboto", sans-serif; - --foot-fontsize: 16px; +:root { + --green: #00ff01; + --green-dim: rgba(0, 255, 1, 0.12); + --green-mid: rgba(0, 255, 1, 0.35); + --green-glow: 0 0 14px rgba(0, 255, 1, 0.45); + --green-glow-lg: 0 0 30px rgba(0, 255, 1, 0.3), 0 0 60px rgba(0, 255, 1, 0.1); + --dark: #080808; + --dark-card: #101010; + --dark-raised: #181818; + --dark-border: #222; + --light: #f0f0f0; + --muted: #888; + --warning: #ffaa00; + --danger: #ff4040; + --nav-font: 'Bebas Neue', sans-serif; + --mono-font: 'Space Mono', monospace; + --body-font: 'Inter', sans-serif; + --radius: 6px; + --radius-lg: 12px; + --transition: all 0.22s ease; } +/* ── Reset & Base ──────────────────────────────────────────────────── */ +*, *::before, *::after { box-sizing: border-box; } + html, body { max-width: 100vw; margin: 0; padding: 0; background-color: var(--dark); color: var(--light); - font-family: var(--def-font); - font-size: var(--def-fontsize); + font-family: var(--body-font); + font-size: 16px; + line-height: 1.6; + -webkit-font-smoothing: antialiased; +} + +/* subtle scanline overlay — gives scientific terminal feel */ +body::after { + content: ''; + position: fixed; + inset: 0; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.03) 2px, + rgba(0, 0, 0, 0.03) 4px + ); + pointer-events: none; + z-index: 9998; } .container { @@ -28,74 +61,829 @@ html, body { flex-direction: column; min-height: 100vh; background-color: var(--dark); - color: var(--light); - font-size: var(--def-fontsize); } main { flex: 1; + padding: 2rem 0; } -a { - color: var(--afids-green); +a { color: var(--green); transition: var(--transition); } +a:hover { color: var(--light); text-decoration: none; } + +/* ── Keyframe animations ───────────────────────────────────────────── */ +@keyframes glow-pulse { + 0%, 100% { box-shadow: 0 0 8px rgba(0,255,1,0.3); } + 50% { box-shadow: 0 0 24px rgba(0,255,1,0.7); } } -a:hover { - color: var(--light); +@keyframes fade-in-up { + from { opacity: 0; transform: translateY(24px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes slide-in-left { + from { opacity: 0; transform: translateX(-20px); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes border-spin { + to { --angle: 360deg; } +} + +@keyframes flicker { + 0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% { opacity: 1; } + 20%, 24%, 55% { opacity: 0.92; } +} + +@keyframes count-up { + from { opacity: 0; transform: scale(0.85); } + to { opacity: 1; transform: scale(1); } +} + +/* ── Navbar ────────────────────────────────────────────────────────── */ +#react-navbar { + position: sticky; + top: 0; + z-index: 1000; + background: rgba(8, 8, 8, 0.97); + backdrop-filter: blur(8px); + border-bottom: 1px solid var(--dark-border); +} + +#afids-banner { + width: clamp(360px, 55vw, 620px); + margin: 1.4rem auto 0.6rem; + display: block; + filter: drop-shadow(var(--green-glow)); } -/* Navbar */ a.nav-link { - padding: 0 50px !important; + padding: 0 2rem !important; text-transform: uppercase; text-align: center; - color: var(--light) !important; + color: var(--muted) !important; font-family: var(--nav-font); - font-size: var(--nav-fontsize); + font-size: 1.5rem; + letter-spacing: 0.05em; + transition: var(--transition); + position: relative; +} + +a.nav-link::after { + content: ''; + position: absolute; + bottom: -2px; + left: 50%; + right: 50%; + height: 2px; + background: var(--green); + transition: var(--transition); } +a.nav-link:hover, +a.nav-link.active, a.active { - color: var(--afids-green) !important; + color: var(--green) !important; + text-shadow: 0 0 8px rgba(0,255,1,0.5); } -a.nav-link:hover { - color: var(--afids-green) !important; +a.nav-link:hover::after, +a.active::after { + left: 10%; + right: 10%; } .nav-hr { - color: var(--afids-green); - margin-top: 0; - border-top: 0.2vw solid; + color: var(--green); + margin: 0; + border: none; + border-top: 1px solid var(--green); + box-shadow: var(--green-glow); } -/* Footer */ +/* ── Footer ────────────────────────────────────────────────────────── */ footer { display: flex; flex-direction: column; align-items: center; justify-content: center; - background-color: var(--afids-green); - color: var(--dark); - font-size: var(--foot-fontsize); + background-color: var(--dark-card); + border-top: 1px solid var(--dark-border); + color: var(--muted); + font-size: 0.85rem; + padding: 1.2rem; + font-family: var(--mono-font); +} + +footer a { color: var(--green); } +footer a:hover { color: var(--light); } + +.footIcon { + width: 20px; + height: 20px; + margin: 6px; + transition: var(--transition); } -footer a { +.footIcon:hover { filter: drop-shadow(0 0 6px var(--green)); } + +/* ── Typography ────────────────────────────────────────────────────── */ +h1, h2, h3, h4 { + font-family: var(--nav-font); + letter-spacing: 0.04em; +} + +.text-green { color: var(--green); } +.text-muted { color: var(--muted) !important; } +.text-warning { color: var(--warning); } +.text-danger { color: var(--danger); } +.text-mono { font-family: var(--mono-font); } + +.section-label { + font-family: var(--mono-font); + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--green); + margin-bottom: 0.3rem; +} + +/* ── Cards ─────────────────────────────────────────────────────────── */ +.afids-card { + background: var(--dark-card); + border: 1px solid var(--dark-border); + border-radius: var(--radius-lg); + padding: 1.5rem; + transition: var(--transition); +} + +.afids-card:hover { + border-color: rgba(0,255,1,0.4); + box-shadow: var(--green-glow); +} + +.afids-card-accent { + border-left: 3px solid var(--green); + border-radius: 0 var(--radius-lg) var(--radius-lg) 0; +} + +/* ── Form controls ─────────────────────────────────────────────────── */ +.form-section-title { + font-family: var(--mono-font); + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--green); + margin-bottom: 0.5rem; +} + +.afids-select { + width: 100%; + background: var(--dark-raised); + color: var(--light); + border: 1px solid var(--dark-border); + border-radius: var(--radius); + padding: 0.6rem 1rem; + font-family: var(--body-font); + font-size: 0.95rem; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2300ff01' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0.8rem center; + transition: var(--transition); + cursor: pointer; +} + +.afids-select:focus { + outline: none; + border-color: var(--green); + box-shadow: 0 0 0 2px var(--green-dim), var(--green-glow); +} + +.afids-select option { background: var(--dark-raised); } + +/* File upload zone */ +.file-drop-zone { + border: 2px dashed var(--dark-border); + border-radius: var(--radius-lg); + padding: 2.5rem 1rem; + text-align: center; + cursor: pointer; + transition: var(--transition); + position: relative; + background: var(--dark-card); +} + +.file-drop-zone:hover, +.file-drop-zone.dragover { + border-color: var(--green); + background: var(--green-dim); + box-shadow: var(--green-glow); +} + +.file-drop-zone input[type="file"] { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + opacity: 0; + cursor: pointer; +} + +.file-drop-icon { + font-size: 2.5rem; + margin-bottom: 0.5rem; + display: block; + color: var(--green); + opacity: 0.7; +} + +.file-drop-text { + font-family: var(--mono-font); + font-size: 0.85rem; + color: var(--muted); + margin: 0; +} + +.file-drop-text strong { + color: var(--light); +} + +#filename-display { + margin-top: 0.5rem; + font-family: var(--mono-font); + font-size: 0.8rem; + color: var(--green); + min-height: 1.2em; +} + +/* Checkbox */ +.afids-checkbox-row { + display: flex; + align-items: center; + gap: 0.6rem; + margin: 0.8rem 0; +} + +.afids-checkbox-row input[type="checkbox"] { + accent-color: var(--green); + width: 16px; + height: 16px; + cursor: pointer; +} + +.afids-checkbox-row label { + font-size: 0.88rem; + color: var(--muted); + cursor: pointer; + margin: 0; +} + +/* Submit button */ +.btn-afids { + display: inline-block; + background: transparent; + color: var(--green); + border: 2px solid var(--green); + border-radius: var(--radius); + padding: 0.65rem 2.5rem; + font-family: var(--nav-font); + font-size: 1.4rem; + letter-spacing: 0.12em; + cursor: pointer; + transition: var(--transition); + position: relative; + overflow: hidden; +} + +.btn-afids::before { + content: ''; + position: absolute; + inset: 0; + background: var(--green); + transform: translateX(-101%); + transition: transform 0.28s ease; + z-index: 0; +} + +.btn-afids span { position: relative; z-index: 1; } + +.btn-afids:hover { color: var(--dark); + box-shadow: var(--green-glow); + animation: glow-pulse 1.4s ease-in-out infinite; +} + +.btn-afids:hover::before { transform: translateX(0); } + +/* ── Result alert ───────────────────────────────────────────────────── */ +.afids-alert { + border-radius: var(--radius); + padding: 0.9rem 1.2rem; + font-family: var(--mono-font); + font-size: 0.9rem; + border: 1px solid transparent; + animation: fade-in-up 0.4s ease both; +} + +.afids-alert-success { + background: rgba(0, 255, 1, 0.08); + border-color: rgba(0, 255, 1, 0.4); + color: var(--green); +} + +.afids-alert-danger { + background: rgba(255, 64, 64, 0.08); + border-color: rgba(255, 64, 64, 0.4); + color: var(--danger); +} + +/* ── Session summary dashboard ─────────────────────────────────────── */ +.summary-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 1rem; + margin: 1.5rem 0; + animation: fade-in-up 0.5s ease both; +} + +.summary-stat { + background: var(--dark-card); + border: 1px solid var(--dark-border); + border-radius: var(--radius-lg); + padding: 1rem 1.2rem; + text-align: center; + transition: var(--transition); +} + +.summary-stat:hover { + border-color: rgba(0,255,1,0.3); + box-shadow: var(--green-glow); + transform: translateY(-2px); } -footer a:hover { +.summary-stat-value { + font-family: var(--mono-font); + font-size: 1.8rem; + font-weight: 700; + line-height: 1.1; color: var(--light); + animation: count-up 0.5s ease both; } -.footIcon { - width: var(--def-fontsize); - height: var(--def-fontsize); - margin: 8px; +.summary-stat-sub { + font-family: var(--mono-font); + font-size: 0.75rem; + color: var(--muted); + margin-top: 0.1rem; +} + +.summary-stat-label { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--muted); + margin-top: 0.4rem; + font-family: var(--mono-font); +} + +.stat-green { color: var(--green); } +.stat-red { color: var(--danger); } +.stat-yellow { color: var(--warning); } + +/* ── Tabs ───────────────────────────────────────────────────────────── */ +.afids-tabs { + display: flex; + gap: 0; + border-bottom: 1px solid var(--dark-border); + margin-bottom: 1.5rem; +} + +.afids-tabs .nav-link { + font-family: var(--mono-font) !important; + font-size: 0.8rem !important; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--muted) !important; + padding: 0.6rem 1.4rem !important; + border: 1px solid transparent; + border-bottom: none; + border-radius: var(--radius) var(--radius) 0 0; + transition: var(--transition); +} + +.afids-tabs .nav-link:hover { + color: var(--light) !important; + background: var(--dark-raised); + text-shadow: none; +} + +.afids-tabs .nav-link.active, +.afids-tabs .nav-link[aria-selected="true"] { + color: var(--green) !important; + background: var(--dark-card); + border-color: var(--dark-border); + border-bottom-color: var(--dark-card); + text-shadow: 0 0 8px rgba(0,255,1,0.4); +} + +.afids-tabs .nav-link::after { display: none; } + +/* ── Distance table ─────────────────────────────────────────────────── */ +.afids-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + font-family: var(--mono-font); + font-size: 0.82rem; +} + +.afids-table thead th { + padding: 0.6rem 0.9rem; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.7rem; + color: var(--muted); + border-bottom: 1px solid var(--dark-border); + background: var(--dark-card); + white-space: nowrap; +} + +.afids-table tbody tr { + border-bottom: 1px solid var(--dark-border); + transition: var(--transition); +} + +.afids-table tbody tr:hover { + background: var(--dark-raised); +} + +.afids-table td { + padding: 0.5rem 0.9rem; + vertical-align: middle; +} + +.afids-table .label-cell { + font-weight: 700; + color: var(--light); + white-space: nowrap; +} + +.afids-table .dist-excellent { color: var(--green); } +.afids-table .dist-good { color: #aaff00; } +.afids-table .dist-warning { color: var(--warning); } +.afids-table .dist-danger { color: var(--danger); } +.afids-table .dir-cell { color: var(--muted); font-size: 0.78rem; } + +/* ── Teaching cards ─────────────────────────────────────────────────── */ +.teaching-section-header { + display: flex; + align-items: center; + gap: 1rem; + margin: 2.5rem 0 1rem; +} + +.teaching-section-header h3 { + font-family: var(--mono-font); + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--green); + margin: 0; +} + +.teaching-section-header hr { + flex: 1; + border: none; + border-top: 1px solid var(--dark-border); + margin: 0; +} + +.teaching-card { + background: var(--dark-card); + border: 1px solid var(--dark-border); + border-left: 3px solid var(--green); + border-radius: 0 var(--radius-lg) var(--radius-lg) 0; + padding: 1.2rem 1.4rem; + margin-bottom: 1rem; + animation: slide-in-left 0.4s ease both; + transition: var(--transition); +} + +.teaching-card:hover { + border-color: rgba(0,255,1,0.5); + border-left-color: var(--green); + box-shadow: var(--green-glow); +} + +.teaching-card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.8rem; +} + +.teaching-card-title { + font-family: var(--mono-font); + font-size: 0.95rem; + font-weight: 700; + color: var(--green); + margin: 0; +} + +.teaching-card-abbr { + background: var(--green-dim); + color: var(--green); + border: 1px solid rgba(0,255,1,0.3); + border-radius: 3px; + padding: 0 0.4rem; + font-size: 0.8rem; + font-family: var(--mono-font); + margin-right: 0.5rem; +} + +.teaching-card-error { + font-family: var(--mono-font); + font-size: 0.9rem; + white-space: nowrap; +} + +.teaching-card-body p { + font-size: 0.88rem; + color: #ccc; + margin: 0 0 0.6rem; + line-height: 1.55; +} + +.teaching-card-sub-label { + font-family: var(--mono-font); + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.1em; + margin-bottom: 0.25rem; + display: block; +} + +.sub-label-green { color: var(--green); } +.sub-label-yellow { color: var(--warning); } + +/* ── Hero (index page) ──────────────────────────────────────────────── */ +.hero { + padding: 4rem 0 3rem; + text-align: center; +} + +.hero-kicker { + font-family: var(--mono-font); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.2em; + color: var(--green); + margin-bottom: 1rem; + display: block; +} + +.hero-title { + font-family: var(--nav-font); + font-size: clamp(2.5rem, 6vw, 4.5rem); + letter-spacing: 0.06em; + line-height: 1.05; + margin-bottom: 1.2rem; + color: var(--light); + text-shadow: 0 0 40px rgba(0,255,1,0.15); +} + +.hero-title .accent { + color: var(--green); + text-shadow: 0 0 20px rgba(0,255,1,0.6); + animation: flicker 6s linear infinite; +} + +.hero-subtitle { + font-size: 1.05rem; + color: var(--muted); + max-width: 580px; + margin: 0 auto 2.5rem; + line-height: 1.7; +} + +.hero-stats { + display: flex; + justify-content: center; + gap: 3rem; + margin: 2.5rem 0; + flex-wrap: wrap; +} + +.hero-stat-value { + font-family: var(--mono-font); + font-size: 2.5rem; + font-weight: 700; + color: var(--green); + display: block; + line-height: 1; + text-shadow: var(--green-glow); +} + +.hero-stat-label { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--muted); + margin-top: 0.3rem; + font-family: var(--mono-font); +} + +.hero-divider { + width: 60px; + height: 2px; + background: linear-gradient(90deg, transparent, var(--green), transparent); + margin: 2rem auto; + box-shadow: var(--green-glow); +} + +/* Feature grid */ +.feature-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + margin: 2rem 0; +} + +.feature-card { + background: var(--dark-card); + border: 1px solid var(--dark-border); + border-radius: var(--radius-lg); + padding: 1.4rem 1.2rem; + text-align: left; + transition: var(--transition); +} + +.feature-card:hover { + border-color: rgba(0,255,1,0.35); + transform: translateY(-3px); + box-shadow: var(--green-glow); +} + +.feature-icon { + font-size: 1.5rem; + margin-bottom: 0.6rem; + display: block; +} + +.feature-title { + font-family: var(--mono-font); + font-size: 0.82rem; + font-weight: 700; + color: var(--light); + text-transform: uppercase; + letter-spacing: 0.06em; + margin-bottom: 0.4rem; +} + +.feature-desc { + font-size: 0.82rem; + color: var(--muted); + line-height: 1.5; + margin: 0; +} + +/* Video panel */ +.video-panel { + border: 1px solid var(--dark-border); + border-radius: var(--radius-lg); + overflow: hidden; + box-shadow: 0 0 40px rgba(0,0,0,0.6); + transition: var(--transition); +} + +.video-panel:hover { + border-color: rgba(0,255,1,0.3); + box-shadow: var(--green-glow); } #afidsVideo { - width: 90%; - height: width / (16 / 9); - border: var(--light) solid 1px; - margin-bottom: 16px; -} \ No newline at end of file + width: 100%; + display: block; + margin: 0; + border: none; +} + +/* ── Validator form page ────────────────────────────────────────────── */ +.page-header { + padding: 1.5rem 0 1rem; + border-bottom: 1px solid var(--dark-border); + margin-bottom: 2rem; +} + +.page-header h1 { + font-size: clamp(1.8rem, 4vw, 2.5rem); + margin: 0; + color: var(--light); +} + +.page-header p { + color: var(--muted); + margin: 0.4rem 0 0; + font-size: 0.9rem; +} + +/* Two-column form layout */ +.form-row-cols { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 1.2rem; +} + +@media (max-width: 600px) { + .form-row-cols { grid-template-columns: 1fr; } + .hero-stats { gap: 2rem; } +} + +/* ── Misc utility ───────────────────────────────────────────────────── */ +.green-rule { + border: none; + border-top: 1px solid var(--green); + box-shadow: var(--green-glow); + margin: 1.5rem 0; + opacity: 0.6; +} + +.fade-in { animation: fade-in-up 0.5s ease both; } +.fade-in-2 { animation: fade-in-up 0.5s ease 0.1s both; } +.fade-in-3 { animation: fade-in-up 0.5s ease 0.2s both; } + +/* ── Login page ─────────────────────────────────────────────────────── */ +.login-wrap { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 40vh; + gap: 1.5rem; +} + +.login-heading { + font-family: var(--mono-font); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.15em; + color: var(--muted); +} + +.btn-orcid { + display: flex; + align-items: center; + gap: 0.9rem; + background: var(--dark-card); + border: 1px solid var(--dark-border); + border-radius: var(--radius-lg); + padding: 0.9rem 1.8rem; + color: var(--light); + font-family: var(--body-font); + font-size: 1rem; + font-weight: 500; + text-decoration: none; + transition: var(--transition); + cursor: pointer; +} + +.btn-orcid:hover { + border-color: var(--green); + box-shadow: var(--green-glow); + color: var(--light); + transform: translateY(-2px); +} + +.btn-orcid img { width: 32px; height: 32px; } + +/* ── Contact page ───────────────────────────────────────────────────── */ +.contact-card { + background: var(--dark-card); + border: 1px solid var(--dark-border); + border-radius: var(--radius-lg); + padding: 2rem; + max-width: 640px; +} + +.contact-card p { + color: #bbb; + font-size: 0.95rem; + line-height: 1.7; +} + +.contact-card a { + color: var(--green); + text-decoration: underline; + text-underline-offset: 3px; + text-decoration-color: rgba(0,255,1,0.4); +} + +.contact-card a:hover { + color: var(--light); + text-decoration-color: var(--light); +} diff --git a/afidsvalidator/templates/app.html b/afidsvalidator/templates/app.html index d01756c7..9d3040b7 100644 --- a/afidsvalidator/templates/app.html +++ b/afidsvalidator/templates/app.html @@ -1,144 +1,233 @@ {% extends "base.html" %} -{% block title %} | VALIDATOR {% endblock %} +{% block title %} | Validator{% endblock %} {% block content %} -
-
-
-
-

Use the following form to compare user placed fiducials to one of the available templates!

-

If you choose to "Upload to Database", fiducials from the file will be uploaded and stored. These - placements may be used in the future to identify fiducials users are having difficulty placing. -

-
-
- -
- -
- Select a template to compare against. - - -
- - -
- Select the AFIDs file to validate. - {% for field in form %} - {{ field }} - {% if field.errors %} -
    - {% for error in field.errors %} -
  • {{ error }}
  • - {% endfor %} -
- {% endif %} + + + + +
+ + + +
+
+
Species
+ - - - - - {% if result != "" %} -
-
-

Results

-
+
-
-
- {% if result[0:5] == "Valid" %} -
- {% else %} -
- {% endif %} - {{ result|safe }} -
-
+
+
Template
+
+
+ + +
AFIDs File
+
+ + +

+ Drop your file here or click to browse
+ Accepts .fcsv, .csv, .json +

+
+
+ + +
+ + +
+ + +
+ +
+ + +
+ + +{% if result != "" %} +
+ {% if result[:5] == "Valid" %} +
✔ {{ result|safe }}
+ {% else %} +
✖ {{ result|safe }}
+ {% endif %} +
+ +{% if scatter_html %} + + +{% if summary %} +
+
+
+ {{ summary.mean }} + mm +
+
± {{ summary.sd }} mm SD
+
Mean Error
+
+
+
{{ summary.best_label }}
+
{{ summary.best_dist }} mm
+
Best Landmark
+
+
+
{{ summary.worst_label }}
+
{{ summary.worst_dist }} mm
+
Worst Landmark
+
+
+
+ {{ summary.n_within_2mm }} + /{{ summary.total }} +
+
{{ summary.n_within_1mm }} within 1mm
+
Within 2mm
+
+
+{% endif %} + + + + +
+ + +
+ {{ scatter_html|safe }} + {{ histogram_html|safe }} +
- - {% if scatter_html %} -
-
- -
-
-
-
-
-
- {{scatter_html|safe}} -
-
-
-
- {{histogram_html|safe}} -
-
-
-
-
-
- - - - - {% for label, distance in table_zip %} - - {% endfor %} -
{{"Fiducial Name"}}{{"Distance [mm]"}}
{{label}}{{distance}}
-
-
-
-
- {% endif %} - {% endif %} -
+ +
+

+ Each axis = mean error (mm) for landmarks in that neuroanatomical region. + Smaller polygon = better overall accuracy. Hover vertices for exact values. +

+ {{ radar_html|safe }} +
+ + +
+

+ Directional error in anatomical LPS space. Components < 0.1mm suppressed. + <1mm + 1–3mm + >3mm +

+
+ + + + + + + + + + {% for label, distance, direction in table_rows %} + + + {% set d = distance|float %} + {% if d < 1.0 %} + + {% elif d < 2.0 %} + + {% elif d < 3.0 %} + + {% else %} + + {% endif %} + + + {% endfor %} + +
FiducialDistance (mm)Directional Error
{{ label }}{{ distance }}{{ distance }}{{ distance }}{{ distance }}{{ direction }}
- - // Populate dropdown choices if species is changed - species_select.onchange = update_dropdown - {% endblock content %} diff --git a/afidsvalidator/templates/base.html b/afidsvalidator/templates/base.html index 7c9c8ab4..ebc05dc7 100644 --- a/afidsvalidator/templates/base.html +++ b/afidsvalidator/templates/base.html @@ -3,21 +3,16 @@ - - + + + - Anatomical Fiducials{% block title %}{% endblock %} + AFIDs Validator{% block title %}{% endblock %} - + - + - + + + -
-
-
-
- -
{% block content %}{% endblock content %}
- - - - - - - +
+
+
+
+
+ +
{% block content %}{% endblock content %}
+ + +
+ + + + + diff --git a/afidsvalidator/templates/contact.html b/afidsvalidator/templates/contact.html index dddc8fcd..205ef000 100644 --- a/afidsvalidator/templates/contact.html +++ b/afidsvalidator/templates/contact.html @@ -1,28 +1,54 @@ -{% extends "base.html" %} {% block title %} | CONTACT{% endblock%} {%block -content %} -
-

- Please reach out if you would like to contribute to this open source - project, encounter any bugs with the validator, or have any questions! -

+{% extends "base.html" %} +{% block title %} | Contact{% endblock %} -

- For any issues related to the fiducial validator, please create a new issue - on our - Github repository. For all other inquiries, please reach out to us via - Mattermost - or - Twitter. -

+{% block content %} +
+ +

Contact

+ +
+

+ Please reach out if you would like to contribute to this open source + project, encounter any bugs with the validator, or have questions about + the AFIDs protocol. +

+

+ For validator bugs or feature requests, open an issue on our + GitHub repository. + For general inquiries, find us on + Mattermost + or Twitter. +

+

+ We will do our best to respond within a reasonable time. +

+
-

- We will do our best to respond to any messages within a reasonable time! -

+
{% endblock content %} diff --git a/afidsvalidator/templates/index.html b/afidsvalidator/templates/index.html index bb65224a..684a0d5a 100644 --- a/afidsvalidator/templates/index.html +++ b/afidsvalidator/templates/index.html @@ -1,30 +1,289 @@ -{% extends "base.html" %} {% block title %} | ABOUT{% endblock title %} {% block -content %} -
-
-

Anatomical fiducials

+{% extends "base.html" %} +{% block title %} | About{% endblock %} + +{% block content %} + + +
+ Open Neuroimaging Framework + +

+ Anatomical
Fiducials Validator +

+ +

+ Train and validate the placement of 32 neuroanatomical landmarks + across human and non-human primate brain templates. + Built for reproducible science. +

+ + + Launch Validator + + + +
+
+ 32 +
Landmarks
+
+
+ {{ total_templates }} +
Templates
+
+
+ {{ n_species }} +
Species
+
+
+ +
Open access
+
-
-
-

- Anatomical fiducials (AFIDs) is an open framework for evaluating - correspondence in brain images and teaching neuroanatomy using anatomical - fiducial placement. Here you'll find a web application that allows you to - upload a fiducial markup file (.fcsv or .json) generated using the AFIDs - protocol, and validate that it conforms to the protocol. +

+ + +
+
+ +

+ Anatomical Fiducials (AFIDs) is an open framework for evaluating + correspondence in brain images and teaching neuroanatomy through + precisely defined landmark placement. Researchers and trainees + place 32 landmarks in 3D Slicer, export the markup file, and + upload here to receive instant, detailed placement feedback. +

+

+ The protocol spans + human and + non-human primate + brain templates, making it a cross-species tool for + image registration quality control and neuroanatomy training. +

+ +
+ +
+
+ +
+

+ AFIDs placement walkthrough in 3D Slicer

+
+ + +
+ + +
+ + +
+
+ + +
+
+ 01 +
+
+ Place +
+

+ Open 3D Slicer and follow the AFIDs protocol to place 32 anatomical + landmarks on your brain image. +

+
+ + +
+
+ 02 +
+
+ Export +
+

+ Save your markup file from 3D Slicer as + .fcsv or + .json. +

+
+ + +
+
+ 03 +
+
+ Validate +
+

+ Upload here. Get per-landmark distance, directional error, + and a regional error profile — instantly. +

+
+ +
+
+ + +
+ +
+
+ 🧠 +
Regional Error Profile
+

+ Radar chart of mean placement error across 8 neuroanatomical + regions — see where your accuracy breaks down at a glance. +

+
+
+ 🧭 +
Directional Feedback
+

+ Each landmark error decomposed into anatomical axes — + "2.1mm posterior, 0.8mm superior" — immediately actionable. +

+
+
+ 📊 +
Session Dashboard
+

+ Mean ± SD error, best and worst landmark, and how many + placements fall within 1mm and 2mm of the template. +

+
+
+ 🐒 +
Multi-Species
+

+ Validate against human and macaque templates. + NMT, D99, INIA19, Yerkes19, MacaqueMNI all supported. +

+
+
+ 🗄️ +
Data Contribution
+

+ Log in with ORCID and contribute your placements to the + growing community dataset. +

+
+
+ 🔓 +
Open Framework
+

+ GPL-licensed, community driven, and built for reproducible + neuroimaging research. +

+
+
+
+ + +
+ + +
+ + {% for species, templates in species_templates.items() %} +
+ +
+ + {% if species == "Human" %}🧠{% else %}🐒{% endif %} + +
+
+ {{ species }} +
+
+ {{ templates|length }} template{% if templates|length != 1 %}s{% endif %} +
+
+
+ +
+ {% for tpl in templates %} + + {{ tpl }} + + {% endfor %} +
+
+ {% endfor %} -
-
+ + +
+
+

+ Ready to validate your fiducial placements? +

+ + Open Validator + +
+ {% endblock content %} diff --git a/afidsvalidator/templates/login.html b/afidsvalidator/templates/login.html index 5876d6d6..3ebbbc4a 100644 --- a/afidsvalidator/templates/login.html +++ b/afidsvalidator/templates/login.html @@ -1,19 +1,52 @@ {% extends "base.html" %} -{% block title %} | LOGIN{% endblock %} +{% block title %} | Login{% endblock %} {% block content %} -
- + {% endblock content %} diff --git a/afidsvalidator/views.py b/afidsvalidator/views.py index 6371dedc..23130f71 100644 --- a/afidsvalidator/views.py +++ b/afidsvalidator/views.py @@ -30,6 +30,7 @@ from afidsvalidator.visualizations import ( generate_3d_scatter, generate_histogram, + generate_radar_chart, ) validator = Blueprint("validator", __name__, template_folder="templates") @@ -48,7 +49,25 @@ class Average(wtf.Form): @validator.route("/") def index(): """Render the static index page.""" - return render_template("index.html", current_user=current_user) + template_root = Path(current_app.root_path) / "afids-templates" + species_templates = {} + for species_dir in sorted(template_root.iterdir()): + if not species_dir.is_dir(): + continue + templates = sorted( + f.name[4:].split("_")[0] + for f in species_dir.iterdir() + if f.name.startswith("tpl") + ) + species_templates[species_dir.name.capitalize()] = templates + total_templates = sum(len(t) for t in species_templates.values()) + return render_template( + "index.html", + current_user=current_user, + species_templates=species_templates, + total_templates=total_templates, + n_species=len(species_templates), + ) # Contact @@ -62,7 +81,17 @@ def contact(): @validator.route("/login.html") def login(): """Render the static login page.""" - return render_template("login.html", current_user=current_user) + client_id = current_app.config.get("ORCID_OAUTH_CLIENT_ID", "") + orcid_configured = bool(client_id) and client_id not in ( + "dev", + "dev-placeholder", + "from-orcid", + ) + return render_template( + "login.html", + current_user=current_user, + orcid_configured=orcid_configured, + ) @validator.route("/logout.html") @@ -72,34 +101,104 @@ def logout(): return redirect("/") +def _direction_label(dx, dy, dz, threshold=0.1): + """Convert LPS error components into anatomical direction language. + + In the tool's internal LPS representation: + dx > 0 → too far Left, dx < 0 → too far Right + dy > 0 → too far Posterior, dy < 0 → too far Anterior + dz > 0 → too far Superior, dz < 0 → too far Inferior + + Parameters + ---------- + dx, dy, dz : float + Error components (user − template) in LPS space. + threshold : float + Components smaller than this (mm) are suppressed. + + Returns + ------- + str + Human-readable directional error, e.g. "2.1mm posterior, 1.4mm left". + """ + axes = [ + (dx, "left", "right"), + (dy, "posterior", "anterior"), + (dz, "superior", "inferior"), + ] + parts = [] + for value, pos_label, neg_label in axes: + if abs(value) >= threshold: + direction = pos_label if value > 0 else neg_label + parts.append(f"{abs(value):.1f}mm {direction}") + return ", ".join(parts) if parts else "< 0.1mm" + + +def _session_summary(labels, float_distances): + """Compute summary statistics for a validation session.""" + mean = float(np.mean(float_distances)) + sd = float(np.std(float_distances)) + worst_idx = int(np.argmax(float_distances)) + best_idx = int(np.argmin(float_distances)) + return { + "mean": f"{mean:.2f}", + "sd": f"{sd:.2f}", + "worst_label": labels[worst_idx], + "worst_dist": f"{float_distances[worst_idx]:.2f}", + "best_label": labels[best_idx], + "best_dist": f"{float_distances[best_idx]:.2f}", + "n_within_1mm": sum(1 for d in float_distances if d < 1.0), + "n_within_2mm": sum(1 for d in float_distances if d < 2.0), + "total": len(float_distances), + } + + @dataclass class PlacementReport: """Dataclass for reporting fiducial placement error.""" labels: list[str] - distances: list[float] + distances: list[str] + directions: list[str] + summary: dict scatter_html: str histogram_html: str + radar_html: str @classmethod def from_afids(cls, user_afids, template_afids): """Calculate report variables from two sets of afids.""" distances = [] + directions = [] labels = [] + for desc in EXPECTED_DESCS: - distance = np.linalg.norm( - np.array( - [getattr(template_afids, desc[-1]).__composite_values__()] - ) - - np.array( - [getattr(user_afids, desc[-1]).__composite_values__()] - ) - ) + label = desc[-1] + u = getattr(user_afids, label) + t = getattr(template_afids, label) + dx, dy, dz = u.x - t.x, u.y - t.y, u.z - t.z + distance = np.linalg.norm([dx, dy, dz]) + direction = _direction_label(dx, dy, dz) + distances.append(f"{distance:.5f}") - labels.append(desc[-1]) + directions.append(direction) + labels.append(label) + + float_distances = [float(d) for d in distances] + summary = _session_summary(labels, float_distances) + scatter_html = generate_3d_scatter(template_afids, user_afids) histogram_html = generate_histogram(template_afids, user_afids) - return cls(labels, distances, scatter_html, histogram_html) + radar_html = generate_radar_chart(template_afids, user_afids) + return cls( + labels, + distances, + directions, + summary, + scatter_html, + histogram_html, + radar_html, + ) def render_validator(form, result="", placement_report=None): @@ -114,11 +213,17 @@ def render_validator(form, result="", placement_report=None): ) if placement_report: placement_dict = { - "table_zip": zip( - placement_report.labels, placement_report.distances + "table_rows": list( + zip( + placement_report.labels, + placement_report.distances, + placement_report.directions, + ) ), + "summary": placement_report.summary, "scatter_html": placement_report.scatter_html, "histogram_html": placement_report.histogram_html, + "radar_html": placement_report.radar_html, } else: placement_dict = {} diff --git a/afidsvalidator/visualizations.py b/afidsvalidator/visualizations.py index e269ff40..b5cfd475 100644 --- a/afidsvalidator/visualizations.py +++ b/afidsvalidator/visualizations.py @@ -4,6 +4,19 @@ from afidsvalidator.model import EXPECTED_DESCS +# Landmark abbreviations grouped by neuroanatomical region. +# Each abbreviation matches the attribute name on the Afids object (desc[-1]). +LANDMARK_REGIONS = { + "Commissural": ["AC", "PC"], + "Brainstem": ["ICS", "PMJ", "SIPF", "RSLMS", "LSLMS", "RILMS", "LILMS"], + "Cerebellar": ["CUL"], + "Diencephalic": ["PG", "IMS", "RMB", "LMB"], + "Ventricular": ["RLVAC", "LLVAC", "RLVPC", "LLVPC"], + "Callosal": ["GENU", "SPLE"], + "Temporal": ["RALTH", "LALTH", "RSAMTH", "LSAMTH", "RIAMTH", "LIAMTH"], + "Basal/Frontal": ["RIGO", "LIGO", "RVOH", "LVOH", "ROSF", "LOSF"], +} + def gen_connecting_lines(ref_afids, user_afids): """Assemble points from each fcsv into pairs. @@ -257,6 +270,85 @@ def generate_histogram(ref_afids, user_afids): return fig4.to_html(include_plotlyjs="cdn", full_html=False) +def generate_radar_chart(ref_afids, user_afids): + """Generate an HTML snippet containing a radar chart of regional errors. + + Parameters + ---------- + ref_afids : Afids + Reference AFIDs object. + user_afids : Afids + User-provided AFIDs object. + + Returns + ------- + str + HTML snippet containing a radar chart of mean Euclidean error + per neuroanatomical region. + """ + region_names = list(LANDMARK_REGIONS.keys()) + region_means = [] + + for landmarks in LANDMARK_REGIONS.values(): + distances = [] + for lm in landmarks: + ref_pos = getattr(ref_afids, lm) + user_pos = getattr(user_afids, lm) + dist = ( + (ref_pos.x - user_pos.x) ** 2 + + (ref_pos.y - user_pos.y) ** 2 + + (ref_pos.z - user_pos.z) ** 2 + ) ** 0.5 + distances.append(dist) + region_means.append(sum(distances) / len(distances)) + + # Close the polygon loop + theta = region_names + [region_names[0]] + r = region_means + [region_means[0]] + + fig = go.Figure() + fig.add_trace( + go.Scatterpolar( + r=r, + theta=theta, + fill="toself", + fillcolor="rgba(0, 255, 1, 0.15)", + line={"color": "rgba(0, 255, 1, 0.9)", "width": 2}, + hovertemplate=( + "%{theta}
Mean error: %{r:.2f} mm" + ), + name="Mean error (mm)", + ) + ) + + fig.update_layout( + title={ + "text": "Error Profile by Neuroanatomical Region", + "font": {"color": "white", "size": 16}, + }, + polar={ + "bgcolor": "rgba(0,0,0,0)", + "radialaxis": { + "visible": True, + "tickfont": {"color": "white", "size": 10}, + "ticksuffix": " mm", + "gridcolor": "rgba(255,255,255,0.2)", + "linecolor": "rgba(255,255,255,0.2)", + }, + "angularaxis": { + "tickfont": {"color": "white", "size": 12}, + "gridcolor": "rgba(255,255,255,0.15)", + "linecolor": "rgba(255,255,255,0.2)", + }, + }, + paper_bgcolor="rgba(0,0,0,0)", + showlegend=False, + margin={"t": 60, "b": 40, "l": 60, "r": 60}, + ) + + return fig.to_html(include_plotlyjs="cdn", full_html=False) + + def do_binning(in_data, nbins=6): """Manually bin a list of numbers.