Browse Source

Annotation: replaced attribute `vertex_{color_codes->label_index}`; changed type of attribute `labels` to dict

Fabian Peter Hammerle 5 years ago
parent
commit
253ca9024e

+ 29 - 23
examples/annotation_stats.ipynb

@@ -22,7 +22,7 @@
     "SUBJECTS_DIR = '../tests/subjects'\n",
     "surface = Surface.read_triangular(SUBJECTS_DIR + '/fabian/surf/lh.pial')\n",
     "surface.load_annotation_file(SUBJECTS_DIR + '/fabian/label/lh.aparc.annot')\n",
-    "len(surface.annotation.vertex_color_codes)"
+    "len(surface.annotation.vertex_label_index)"
    ]
   },
   {
@@ -162,7 +162,7 @@
     "label_frame = pandas.DataFrame((dict(color_code=label.color_code,\n",
     "                                     hex_color_code=label.hex_color_code,\n",
     "                                     **vars(label))\n",
-    "                                for label in surface.annotation.labels),\n",
+    "                                for label in surface.annotation.labels.values()),\n",
     "                               columns=['index', 'name', \n",
     "                                        'red', 'green', 'blue', 'transparency', \n",
     "                                        'hex_color_code', 'color_code'])\n",
@@ -195,7 +195,7 @@
        "  <thead>\n",
        "    <tr style=\"text-align: right;\">\n",
        "      <th></th>\n",
-       "      <th>color_code</th>\n",
+       "      <th>label_index</th>\n",
        "      <th>vertex_index</th>\n",
        "      <th>index</th>\n",
        "      <th>name</th>\n",
@@ -204,12 +204,13 @@
        "      <th>blue</th>\n",
        "      <th>transparency</th>\n",
        "      <th>hex_color_code</th>\n",
+       "      <th>color_code</th>\n",
        "    </tr>\n",
        "  </thead>\n",
        "  <tbody>\n",
        "    <tr>\n",
        "      <th>0</th>\n",
-       "      <td>6558940</td>\n",
+       "      <td>5</td>\n",
        "      <td>0</td>\n",
        "      <td>5</td>\n",
        "      <td>cuneus</td>\n",
@@ -218,10 +219,11 @@
        "      <td>100</td>\n",
        "      <td>0</td>\n",
        "      <td>#dc1464</td>\n",
+       "      <td>6558940</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>1</th>\n",
-       "      <td>6558940</td>\n",
+       "      <td>5</td>\n",
        "      <td>1</td>\n",
        "      <td>5</td>\n",
        "      <td>cuneus</td>\n",
@@ -230,10 +232,11 @@
        "      <td>100</td>\n",
        "      <td>0</td>\n",
        "      <td>#dc1464</td>\n",
+       "      <td>6558940</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>2</th>\n",
-       "      <td>6558940</td>\n",
+       "      <td>5</td>\n",
        "      <td>2</td>\n",
        "      <td>5</td>\n",
        "      <td>cuneus</td>\n",
@@ -242,10 +245,11 @@
        "      <td>100</td>\n",
        "      <td>0</td>\n",
        "      <td>#dc1464</td>\n",
+       "      <td>6558940</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>3</th>\n",
-       "      <td>6558940</td>\n",
+       "      <td>5</td>\n",
        "      <td>3</td>\n",
        "      <td>5</td>\n",
        "      <td>cuneus</td>\n",
@@ -254,10 +258,11 @@
        "      <td>100</td>\n",
        "      <td>0</td>\n",
        "      <td>#dc1464</td>\n",
+       "      <td>6558940</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>4</th>\n",
-       "      <td>6558940</td>\n",
+       "      <td>5</td>\n",
        "      <td>4</td>\n",
        "      <td>5</td>\n",
        "      <td>cuneus</td>\n",
@@ -266,25 +271,26 @@
        "      <td>100</td>\n",
        "      <td>0</td>\n",
        "      <td>#dc1464</td>\n",
+       "      <td>6558940</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n",
        "</div>"
       ],
       "text/plain": [
-       "   color_code  vertex_index  index    name  red  green  blue  transparency  \\\n",
-       "0     6558940             0      5  cuneus  220     20   100             0   \n",
-       "1     6558940             1      5  cuneus  220     20   100             0   \n",
-       "2     6558940             2      5  cuneus  220     20   100             0   \n",
-       "3     6558940             3      5  cuneus  220     20   100             0   \n",
-       "4     6558940             4      5  cuneus  220     20   100             0   \n",
+       "   label_index  vertex_index  index    name  red  green  blue  transparency  \\\n",
+       "0            5             0      5  cuneus  220     20   100             0   \n",
+       "1            5             1      5  cuneus  220     20   100             0   \n",
+       "2            5             2      5  cuneus  220     20   100             0   \n",
+       "3            5             3      5  cuneus  220     20   100             0   \n",
+       "4            5             4      5  cuneus  220     20   100             0   \n",
        "\n",
-       "  hex_color_code  \n",
-       "0        #dc1464  \n",
-       "1        #dc1464  \n",
-       "2        #dc1464  \n",
-       "3        #dc1464  \n",
-       "4        #dc1464  "
+       "  hex_color_code  color_code  \n",
+       "0        #dc1464     6558940  \n",
+       "1        #dc1464     6558940  \n",
+       "2        #dc1464     6558940  \n",
+       "3        #dc1464     6558940  \n",
+       "4        #dc1464     6558940  "
       ]
      },
      "execution_count": 4,
@@ -295,9 +301,9 @@
    "source": [
     "import pandas\n",
     "\n",
-    "vertex_frame = pandas.DataFrame({'vertex_index': vertex_index, 'color_code': annotation_value}\n",
-    "                                for vertex_index, annotation_value in surface.annotation.vertex_color_codes.items())\n",
-    "vertex_label_frame = vertex_frame.merge(label_frame, on='color_code')\n",
+    "vertex_frame = pandas.DataFrame({'vertex_index': vertex_index, 'label_index': annotation_value}\n",
+    "                                for vertex_index, annotation_value in surface.annotation.vertex_label_index.items())\n",
+    "vertex_label_frame = vertex_frame.merge(label_frame, left_on='label_index', right_on='index')\n",
     "vertex_label_frame.head()"
    ]
   },

+ 20 - 15
freesurfer_surface/__init__.py

@@ -19,6 +19,14 @@ https://surfer.nmr.mgh.harvard.edu/
 >>>
 >>> surface.load_annotation_file('bert/label/lh.aparc.annot')
 >>> print([label.name for label in surface.annotation.labels])
+>>>
+>>> precentral, = filter(lambda l: l.name == 'precentral', annotation.labels.values())
+>>> print(precentral.hex_color_code)
+>>>
+>>> precentral_vertix_indices = [vertex_index for vertex_index, label_index
+>>>                              in surface.annotation.vertex_label_index.items()
+>>>                              if label_index == precentral.index]
+>>> print(len(precentral_vertix_indices))
 """
 
 import collections
@@ -84,11 +92,9 @@ class Annotation:
 
     _TAG_OLD_COLORTABLE = b'\0\0\0\x01'
 
-    # TODO replace with vertex_label_index
-    vertex_color_codes: typing.Dict[int, int] = {}
+    vertex_label_index: typing.Dict[int, int] = {}
     colortable_path: typing.Optional[bytes] = None
-    # TODO dict
-    labels: typing.List[Label] = None
+    labels: typing.Dict[int, Label] = None
 
     @staticmethod
     def _read_label(stream: typing.BinaryIO) -> Label:
@@ -103,20 +109,20 @@ class Annotation:
     def _read(self, stream: typing.BinaryIO) -> None:
         # https://surfer.nmr.mgh.harvard.edu/fswiki/LabelsClutsAnnotationFiles
         annotations_num, = struct.unpack('>I', stream.read(4))
-        annotations = (struct.unpack('>II', stream.read(4 * 2))
-                       for _ in range(annotations_num))
-        self.vertex_color_codes = {vertex_index: color_code
-                                   for vertex_index, color_code in annotations}
+        annotations = [struct.unpack('>II', stream.read(4 * 2))
+                       for _ in range(annotations_num)]
         assert stream.read(4) == self._TAG_OLD_COLORTABLE
         colortable_version, _, filename_length = struct.unpack('>III', stream.read(4 * 3))
         assert colortable_version > 0  # new version
         self.colortable_path = stream.read(filename_length - 1)
         assert stream.read(1) == b'\0'
         labels_num, = struct.unpack('>I', stream.read(4))
-        self.labels = [self._read_label(stream) for _ in range(labels_num)]
-        label_color_codes = set(l.color_code for l in self.labels)
-        assert all(vertex_color_code in label_color_codes
-                   for vertex_color_code in self.vertex_color_codes.values())
+        self.labels = {label.index: label for label
+                       in (self._read_label(stream) for _ in range(labels_num))}
+        label_index_by_color_code = {label.color_code: label.index
+                                     for label in self.labels.values()}
+        self.vertex_label_index = {vertex_index: label_index_by_color_code[color_code]
+                                   for vertex_index, color_code in annotations}
         assert not stream.read(1)
 
     @classmethod
@@ -231,9 +237,8 @@ class Surface:
 
     def load_annotation_file(self, annotation_file_path: str) -> None:
         annotation = Annotation.read(annotation_file_path)
-        assert len(annotation.vertex_color_codes) <= len(self.vertices)
-        assert all(0 <= vertex_index < len(self.vertices)
-                   for vertex_index in annotation.vertex_color_codes.keys())
+        assert len(annotation.vertex_label_index) <= len(self.vertices)
+        assert max(annotation.vertex_label_index.keys()) < len(self.vertices)
         self.annotation = annotation
 
     def add_vertex(self, vertex: Vertex) -> int:

+ 2 - 1
freesurfer_surface/__main__.py

@@ -17,4 +17,5 @@ def annotation_labels():
     annotation = Annotation.read(args.annotation_file_path)
     csv_writer = csv.writer(sys.stdout, delimiter=args.delimiter)
     csv_writer.writerow(('index', 'color', 'name'))
-    csv_writer.writerows((l.index, l.hex_color_code, l.name,) for l in annotation.labels)
+    labels = sorted(annotation.labels.values(), key=lambda l: l.index)
+    csv_writer.writerows((l.index, l.hex_color_code, l.name,) for l in labels)

+ 15 - 13
tests/test_annotation.py

@@ -7,26 +7,28 @@ from conftest import SUBJECTS_DIR
 
 def test_load_annotation():
     annotation = Annotation.read(os.path.join(SUBJECTS_DIR, 'fabian', 'label', 'lh.aparc.annot'))
-    assert len(annotation.vertex_color_codes) == 155622
-    assert annotation.vertex_color_codes[64290] == 1316060
-    assert annotation.vertex_color_codes[72160] == 1316060
-    assert annotation.vertex_color_codes[84028] == 14423100
-    assert annotation.vertex_color_codes[97356] == 14423100
-    assert annotation.vertex_color_codes[123173] == 8204875
-    assert annotation.vertex_color_codes[140727] == 8204875
-    assert annotation.vertex_color_codes[93859] == 10542100
-    assert annotation.vertex_color_codes[78572] == 0
-    assert annotation.vertex_color_codes[120377] == 0
+    assert len(annotation.vertex_label_index) == 155622
+    assert annotation.vertex_label_index[64290] == 22
+    assert annotation.vertex_label_index[72160] == 22
+    assert annotation.vertex_label_index[84028] == 24
+    assert annotation.vertex_label_index[97356] == 24
+    assert annotation.vertex_label_index[123173] == 27
+    assert annotation.vertex_label_index[140727] == 27
+    assert annotation.vertex_label_index[93859] == 28
+    assert annotation.vertex_label_index[78572] == 0
+    assert annotation.vertex_label_index[120377] == 0
     assert annotation.colortable_path == b'/autofs/space/tanha_002/users/greve' \
                                          b'/fsdev.build/average/colortable_desikan_killiany.txt'
     assert len(annotation.labels) == 36
     assert vars(annotation.labels[0]) == {'index': 0, 'name': 'unknown',
                                           'red': 25, 'green': 5, 'blue': 25, 'transparency': 0}
-    precentral, = filter(lambda l: l.name == 'precentral', annotation.labels)
-    postcentral, = filter(lambda l: l.name == 'postcentral', annotation.labels)
+    assert vars(annotation.labels[28]) == {'index': 28, 'name': 'superiorfrontal',
+                                          'red': 20, 'green': 220, 'blue': 160, 'transparency': 0}
+    precentral, = filter(lambda l: l.name == 'precentral', annotation.labels.values())
+    postcentral, = filter(lambda l: l.name == 'postcentral', annotation.labels.values())
     assert vars(precentral) == {'index': 24, 'name': 'precentral',
                                 'red': 60, 'green': 20, 'blue': 220, 'transparency': 0}
     assert vars(postcentral) == {'index': 22, 'name': 'postcentral',
                                  'red': 220, 'green': 20, 'blue': 20, 'transparency': 0}
-    superiorfrontal, = filter(lambda l: l.color_code == 10542100, annotation.labels)
+    superiorfrontal, = filter(lambda l: l.color_code == 10542100, annotation.labels.values())
     assert superiorfrontal.name == 'superiorfrontal'

+ 2 - 2
tests/test_surface.py

@@ -122,8 +122,8 @@ def test_load_annotation():
     surface.load_annotation_file(os.path.join(SUBJECTS_DIR, 'fabian',
                                               'label', 'lh.aparc.annot'))
     assert isinstance(surface.annotation, Annotation)
-    assert len(surface.annotation.vertex_color_codes) == 155622
-    assert surface.annotation.vertex_color_codes[0] == (((100 << 8) + 20) << 8) + 220
+    assert len(surface.annotation.vertex_label_index) == 155622
+    assert surface.annotation.vertex_label_index[0] == 5
 
 
 def test_add_vertex():