Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1d1551a

Browse files
committed
Reading and writing JSON/binary JSON based surface data (.jmsh and .bmsh)
1 parent b7bbf0e commit 1d1551a

File tree

4 files changed

+248
-1
lines changed

4 files changed

+248
-1
lines changed

‎nibabel/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
from .cifti2 import Cifti2Header, Cifti2Image
5757
from .gifti import GiftiImage
5858
from .freesurfer import MGHImage
59+
from .jmesh import JMesh
5960
from .funcs import (squeeze_image, concat_images, four_to_three,
6061
as_closest_canonical)
6162
from .orientations import (io_orientation, orientation_affine,

‎nibabel/imageclasses.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from .spm99analyze import Spm99AnalyzeImage
2222
from .spm2analyze import Spm2AnalyzeImage
2323
from .volumeutils import Recoder
24+
from .jmesh import JMesh
2425
from .deprecated import deprecate_with_version
2526

2627
from .optpkg import optional_package
@@ -32,7 +33,7 @@
3233
Cifti2Image, Nifti2Image, # Cifti2 before Nifti2
3334
Spm2AnalyzeImage, Spm99AnalyzeImage, AnalyzeImage,
3435
Minc1Image, Minc2Image, MGHImage,
35-
PARRECImage, GiftiImage, AFNIImage]
36+
PARRECImage, GiftiImage, AFNIImage, JMesh]
3637

3738

3839
# DEPRECATED: mapping of names to classes and class functionality

‎nibabel/jmesh/__init__.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
4+
#
5+
# See COPYING file distributed along with the NiBabel package for the
6+
# copyright and license terms.
7+
#
8+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9+
"""JSON and BJData based JMesh format IO
10+
11+
.. currentmodule:: nibabel.jmesh
12+
13+
.. autosummary::
14+
:toctree: ../generated
15+
16+
jmesh
17+
"""
18+
19+
from .jmesh import load, save, JMesh, default_header

‎nibabel/jmesh/jmesh.py‎

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
4+
#
5+
# See COPYING file distributed along with the NiBabel package for the
6+
# copyright and license terms.
7+
#
8+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9+
# General JMesh Input - Output to and from the filesystem
10+
# Qianqian Fang <q.fang at neu.edu>
11+
##############
12+
13+
__all__ = ['JMesh','read','write','default_header']
14+
15+
from jdata import (load as jdload, save as jdsave)
16+
import numpy as np
17+
from ..filebasedimages import FileBasedImage
18+
19+
default_header = {
20+
"JMeshVersion":"0.5",
21+
"Comment":"Created by NiPy with NeuroJSON JMesh specification",
22+
"AnnotationFormat":"https://neurojson.org/jmesh/draft2",
23+
"Parser":{
24+
"Python":[
25+
"https://pypi.org/project/jdata",
26+
"https://pypi.org/project/bjdata"
27+
],
28+
"MATLAB":[
29+
"https://github.com/NeuroJSON/jnifty",
30+
"https://github.com/NeuroJSON/jsonlab"
31+
],
32+
"JavaScript":"https://github.com/NeuroJSON/jsdata",
33+
"CPP":"https://github.com/NeuroJSON/json",
34+
"C":"https://github.com/NeuroJSON/ubj"
35+
}
36+
}
37+
38+
class JMesh(FileBasedImage):
39+
"""JMesh: a simple data structure representing a brain surface
40+
41+
* Description - JMesh defines a set of language-neutral JSON annotations for
42+
storage and exchange of mesh-related data. The details of the specification
43+
can be found in NeuroJSON's website at https://neurojson.org
44+
45+
* Child Elements: [NA]
46+
* Text Content: [NA]
47+
48+
Attributes
49+
----------
50+
info: a dict
51+
A dict object storing the metadata (`_DataInfo_`) section of the JMesh
52+
file
53+
node : 2-D list or numpy array
54+
A 2-D numpy.ndarray object to store the vertices of the mesh
55+
nodelabel : 1-D list or numpy array
56+
A 1-D numpy.ndarray object to store the label of each vertex
57+
face : 2-D list or numpy array
58+
A 2-D numpy.ndarray object to store the triangular elements of the
59+
mesh; indices start from 1
60+
facelabel : 1-D list or numpy array
61+
A 1-D numpy.ndarray object to store the label of each triangle
62+
raw : a dict
63+
The raw data loaded from the .jmsh or .bmsh file
64+
"""
65+
valid_exts = ('.jmsh', '.bmsh')
66+
files_types = (('image', '.jmsh'), ('image', '.bmsh'))
67+
makeable = False
68+
rw = True
69+
70+
def __init__(self, info=None, node=None, nodelabel=None, face=None,
71+
facelabel=None):
72+
73+
self.raw = {}
74+
if(not info is None):
75+
self.raw['_DataInfo_'] = info
76+
77+
if(not nodelabel is None):
78+
self.raw['MeshVertex3'] = {'Data': node, 'Properties': {'Tag': nodelabel} }
79+
self.node = self.raw['MeshVertex3']['Data']
80+
self.nodelabel = self.raw['MeshVertex3']['Properties']['Tag']
81+
else:
82+
self.raw['MeshVertex3'] = node
83+
self.node = self.raw['MeshVertex3']
84+
85+
if(not facelabel is None):
86+
self.raw['MeshTri3'] = {'Data': face, 'Properties': {'Tag': facelabel} }
87+
self.face = self.raw['MeshTri3']['Data']
88+
self.facelabel = self.raw['MeshTri3']['Properties']['Tag']
89+
else:
90+
self.raw['MeshTri3'] = face
91+
self.face = self.raw['MeshTri3']
92+
93+
@classmethod
94+
def from_filename(self, filename, opt={}, **kwargs):
95+
self = read(filename, opt, **kwargs)
96+
return self
97+
98+
@classmethod
99+
def to_filename(self, filename, opt={}, **kwargs):
100+
write(self, filename, opt, **kwargs)
101+
102+
def read(filename, opt={}, **kwargs):
103+
""" Load a JSON or binary JData (BJData) based JMesh file
104+
105+
Parameters
106+
----------
107+
filename : string
108+
The JMesh file to open, it has usually ending .gii
109+
opt: a dict that may contain below option keys
110+
ndarray: boolean, if True, node/face/nodelabel/facelabel are converted
111+
to numpy.ndarray, otherwise, leave those unchanged
112+
kwargs: additional keyword arguments for `json.load` when .jmsh file is being loaded
113+
114+
Returns
115+
-------
116+
mesh : a JMesh object
117+
Return a JMesh object containing mesh data fields such as node, face, nodelabel etc
118+
"""
119+
opt.setdefault('ndarray',True)
120+
121+
mesh = JMesh
122+
mesh.raw = jdload(filename, opt, **kwargs)
123+
124+
#--------------------------------------------------
125+
# read metadata as `info`
126+
#--------------------------------------------------
127+
if('_DataInfo_' in mesh.raw):
128+
mesh.info = mesh.raw['_DataInfo_']
129+
130+
#--------------------------------------------------
131+
# read vertices as `node` and `nodelabel`
132+
#--------------------------------------------------
133+
if('MeshVertex3' in mesh.raw):
134+
mesh.node = mesh.raw['MeshVertex3']
135+
elif('MeshNode' in mesh.raw):
136+
mesh.node = mesh.raw['MeshNode']
137+
else:
138+
raise Exception('JMesh', 'JMesh surface must contain node (MeshVertex3 or MeshNode)')
139+
140+
if(isinstance(mesh.node, dict)):
141+
if(('Properties' in mesh.node) and ('Tag' in mesh.node['Properties'])):
142+
mesh.nodelabel = mesh.node['Properties']['Tag']
143+
if('Data' in mesh.node):
144+
mesh.node = mesh.node['Data']
145+
if(isinstance(mesh.node, np.ndarray) and mesh.node.ndim == 2 and mesh.node.shape[1] > 3):
146+
mesh.nodelabel = mesh.node[:,3:]
147+
mesh.node = mesh.node[:, 0:3]
148+
149+
#--------------------------------------------------
150+
# read triangles as `face` and `facelabel`
151+
#--------------------------------------------------
152+
if('MeshTri3' in mesh.raw):
153+
mesh.face = mesh.raw['MeshTri3']
154+
elif('MeshSurf' in mesh.raw):
155+
mesh.face = mesh.raw['MeshSurf']
156+
157+
if(isinstance(mesh.face, dict)):
158+
if(('Properties' in mesh.face) and ('Tag' in mesh.face['Properties'])):
159+
mesh.facelabel = mesh.face['Properties']['Tag']
160+
if('Data' in mesh.face):
161+
mesh.face = mesh.face['Data']
162+
if(isinstance(mesh.face, np.ndarray) and mesh.face.ndim == 2 and mesh.face.shape[1] > 3):
163+
mesh.facelabel = mesh.face[:,3:]
164+
mesh.face = mesh.face[:, 0:3]
165+
166+
#--------------------------------------------------
167+
# convert to numpy ndarray
168+
#--------------------------------------------------
169+
if(opt['ndarray']):
170+
if hasattr(mesh, 'node') and (not mesh.node is None) and (not isinstance(mesh.node, np.ndarray)):
171+
mesh.node = np.array(mesh.node)
172+
173+
if hasattr(mesh, 'face') and (not mesh.face is None) and (not isinstance(mesh.face, np.ndarray)):
174+
mesh.face = np.array(mesh.face)
175+
176+
if hasattr(mesh, 'nodelabel') and (not mesh.nodelabel is None) and (not isinstance(mesh.nodelabel, np.ndarray)):
177+
mesh.nodelabel = np.array(mesh.nodelabel)
178+
179+
if hasattr(mesh, 'facelabel') and (not mesh.facelabel is None) and (not isinstance(mesh.facelabel, np.ndarray)):
180+
mesh.facelabel = np.array(mesh.facelabel)
181+
182+
return mesh
183+
184+
def write(mesh, filename, opt={}, **kwargs):
185+
""" Save the current mesh to a new file
186+
187+
Parameters
188+
----------
189+
mesh : a JMesh object
190+
filename : string
191+
Filename to store the JMesh file (.jmsh for JSON based JMesh and
192+
.bmsh for binary JMesh files)
193+
opt: a dict that may contain below option keys
194+
ndarray: boolean, if True, node/face/nodelabel/facelabel are converted
195+
to numpy.ndarray, otherwise, leave those unchanged
196+
kwargs: additional keyword arguments for `json.dump` when .jmsh file is being saved
197+
198+
Returns
199+
-------
200+
None
201+
202+
We update the mesh related data fields `MeshVetex3`, `MeshTri3` and metadata `_DataInfo_`
203+
from mesh.node, mesh.face and mesh.info, then save mesh.raw to JData files
204+
"""
205+
206+
if not hasattr(mesh, 'raw') or mesh.raw is None:
207+
mesh.raw = {}
208+
209+
if hasattr(mesh, 'info') and not mesh.info is None:
210+
mesh.raw['_DataInfo_']=mesh.info
211+
if hasattr(mesh, 'node') and not mesh.node is None:
212+
if(hasattr(mesh, 'facelabel') and not mesh.nodelabel is None):
213+
mesh.raw['MeshVertex3']={'Data': mesh.node, 'Properties': {'Tag': mesh.nodelabel}}
214+
else:
215+
mesh.raw['MeshVertex3']=mesh.node
216+
217+
if hasattr(mesh, 'info') and not mesh.face is None:
218+
if(hasattr(mesh, 'facelabel') and not mesh.facelabel is None):
219+
mesh.raw['MeshTri3']={'Data': mesh.face, 'Properties': {'Tag': mesh.facelabel}}
220+
else:
221+
mesh.raw['MeshTri3']=mesh.face
222+
223+
return jdsave(mesh.raw, filename, opt, **kwargs)
224+
225+
load = read
226+
save = write

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /