Source code for ahds.header
# -*- coding: utf-8 -*-
# amira_header.py
"""
Module to convert parsed data from an Amira (R) header into a set of nested objects. The key class is :py:class:`ahds.header.AmiraHeader`.
Usage:
::
>>> from ahds.header import AmiraHeader
>>> ah = AmiraHeader.from_file('file.am')
>>> print ah
Each nested object is constructed from the :py:class:`ahds.header.Block` class defined.
There are four top-level attributes that every :py:class:`ahds.header.AmiraHeader` will have:
* designation
* definitions
* parameters
* data_pointers
Each attribute can be queried using the ``attrs`` attribute.
::
>>> print ah.data_pointers.attrs
['data_pointer_1', 'data_pointer_2', 'data_pointer_3', 'data_pointer_4', 'data_pointer_5', 'data_pointer_6']
>>> print ah.data_pointers.data_pointer_1
data_pointer_1
pointer_name: VERTEX
data_format: None
data_dimension: 3
data_type: float
data_name: VertexCoordinates
data_index: 1
data_length: None
Data pointers are identified by the name ``data_pointer_<n>``.
"""
from __future__ import print_function
import sys
from .grammar import get_parsed_data
[docs]class Block(object):
"""Generic block to be loaded with attributes"""
def __init__(self, name):
self.name = name
self.attrs = list()
[docs] def add_attr(self, name, value):
"""Add an attribute to an :py:class:`ahds.header.Block` object"""
setattr(self, name, value)
self.attrs.append(name)
def __str__(self):
string = "{}\n".format(self.name)
for attr in self.attrs:
if isinstance(getattr(self, attr), Block):
string += "{}\n".format(getattr(self, attr))
else:
string += "{}: {}\n".format(attr, getattr(self, attr))
return string
@property
def ids(self):
"""Convenience method to get the IDs for Materials present"""
assert self.name == "Materials"
ids = list()
for attr in self.attrs:
attr_obj = getattr(self, attr)
if hasattr(attr_obj, 'Id'):
ids.append(getattr(attr_obj, 'Id'))
return ids
def __getitem__(self, index):
"""Convenience method to get an attribute with 'Id' for a Material"""
assert self.name == "Materials"
assert isinstance(index, int)
for attr in self.attrs:
attr_obj = getattr(self, attr)
if hasattr(attr_obj, 'Id'):
if getattr(attr_obj, 'Id') == index:
return attr_obj
else:
continue # next attr
else:
return None
[docs]class AmiraHeader(object):
"""Class to encapsulate Amira (R) metadata"""
def __init__(self, parsed_data):
self._parsed_data = parsed_data
self._load()
[docs] @classmethod
def from_file(cls, fn, *args, **kwargs):
"""Constructor to build an :py:class:`ahds.header.AmiraHeader` object from a file
:param str fn: Amira (R) file
:return ah: object of class :py:class:`ahds.header.AmiraHeader` containing header metadata
:rtype: ah: :py:class:`ahds.header.AmiraHeader`
"""
return cls(get_parsed_data(fn, *args, **kwargs))
@property
def raw_header(self):
"""Show the raw header data"""
return self._parsed_data
def __len__(self):
return len(self._parsed_data)
@staticmethod
def flatten_dict(in_dict):
block_data = dict()
for block in in_dict:
block_data[block.keys()[0]] = block[block.keys()[0]]
return block_data
def _load(self):
# first flatten the dict
block_data = self.flatten_dict(self._parsed_data)
# pprint(block_data, width=147)
self._load_designation(block_data['designation'])
self._load_definitions(block_data['definitions'])
self._load_data_pointers(block_data['data_pointers'])
self._load_parameters(block_data['parameters'])
# self._load_date()
@property
def designation(self):
"""Designation of the Amira (R) file defined in the first row
Designations consist of some or all of the following data:
* filetype e.g. ``AmiraMesh`` or ``HyperSurface``
* dimensions e.g. ``3D``
* format e.g. ``BINARY-LITTLE-ENDIAN``
* version e.g. ``2.1``
* extra format e.g. ``<hxsurface>``
"""
return self._designation
@property
def definitions(self):
"""Definitions consist of a key-value pair specified just after the
designation preceded by the key-word 'define'
"""
return self._definitions
@property
def parameters(self):
"""The set of parameters for each of the segments specified
e.g. colour, data pointer etc.
"""
return self._parameters
@property
def data_pointers(self):
"""The list of data pointers together with a name, data type, dimension,
index, format and length
"""
return self._data_pointers
def _load_designation(self, block_data):
self._designation = Block('designation')
if 'filetype' in block_data:
self._designation.add_attr('filetype', block_data['filetype'])
else:
self._designation.add_attr('filetype', None)
if 'dimension' in block_data:
self._designation.add_attr('dimension', block_data['dimension'])
else:
self._designation.add_attr('dimension', None)
if 'format' in block_data:
self._designation.add_attr('format', block_data['format'])
else:
self._designation.add_attr('format', None)
if 'version' in block_data:
self._designation.add_attr('version', block_data['version'])
else:
self._designation.add_attr('version', None)
if 'extra_format' in block_data:
self._designation.add_attr('extra_format', block_data['extra_format'])
else:
self._designation.add_attr('extra_format', None)
def _load_definitions(self, block_data):
self._definitions = Block('definitions')
for definition in block_data:
self._definitions.add_attr(definition['definition_name'], definition['definition_value'])
def _load_parameters(self, block_data):
self._parameters = Block('parameters')
for parameter in block_data:
if 'nested_parameter' in parameter:
nested_parameter = parameter['nested_parameter']
self._parameters.add_attr(nested_parameter['nested_parameter_name'],
Block(nested_parameter['nested_parameter_name']))
nested_parameter_obj = getattr(self._parameters, nested_parameter['nested_parameter_name'])
for nested_parameter_value in nested_parameter['nested_parameter_values']:
if 'attributes' in nested_parameter_value:
if nested_parameter_value['attributes']:
nested_parameter_obj.add_attr(nested_parameter_value['name'],
Block(nested_parameter_value['name']))
nested_parameter_value_obj = getattr(nested_parameter_obj, nested_parameter_value['name'])
for attribute in nested_parameter_value['attributes']:
nested_parameter_value_obj.add_attr(attribute['attribute_name'],
attribute['attribute_value'])
else:
nested_parameter_obj.add_attr(nested_parameter_value['name'], None)
elif 'nested_attributes' in nested_parameter_value:
nested_parameter_obj.add_attr(nested_parameter_value['name'],
Block(nested_parameter_value['name']))
for nested_attribute in nested_parameter_value['nested_attributes']:
nested_attribute_obj = getattr(nested_parameter_obj, nested_parameter_value['name'])
nested_attribute_obj.add_attr(nested_attribute['nested_attribute_name'],
Block(nested_attribute['nested_attribute_name']))
nested_attribute_value_obj = getattr(nested_attribute_obj,
nested_attribute['nested_attribute_name'])
for nested_attribute_value in nested_attribute['nested_attribute_values']:
nested_attribute_value_obj.add_attr(
nested_attribute_value['nested_attribute_value_name'],
nested_attribute_value['nested_attribute_value_value'])
else:
nested_parameter_obj.add_attr(nested_parameter_value['name'],
nested_parameter_value['inline_parameter_value'])
if 'inline_parameter' in parameter:
inline_parameter = parameter['inline_parameter']
self._parameters.add_attr(inline_parameter['inline_parameter_name'],
inline_parameter['inline_parameter_value'])
def _load_data_pointers(self, block_data):
self._data_pointers = Block('data_pointers')
for data_pointer in block_data:
data_pointer_name = "data_pointer_{}".format(data_pointer['data_index'])
self._data_pointers.add_attr(data_pointer_name, Block(data_pointer_name))
pointer_obj = getattr(self._data_pointers, data_pointer_name)
if 'pointer_name' in data_pointer:
pointer_obj.add_attr('pointer_name', data_pointer['pointer_name'])
else:
pointer_obj.add_attr('pointer_name', None)
if 'data_format' in data_pointer:
pointer_obj.add_attr('data_format', data_pointer['data_format'])
else:
pointer_obj.add_attr('data_format', None)
if 'data_dimension' in data_pointer:
pointer_obj.add_attr('data_dimension', data_pointer['data_dimension'])
else:
pointer_obj.add_attr('data_dimension', None)
if 'data_type' in data_pointer:
pointer_obj.add_attr('data_type', data_pointer['data_type'])
else:
pointer_obj.add_attr('data_type', None)
if 'data_name' in data_pointer:
pointer_obj.add_attr('data_name', data_pointer['data_name'])
else:
pointer_obj.add_attr('data_name', None)
if 'data_index' in data_pointer:
pointer_obj.add_attr('data_index', data_pointer['data_index'])
else:
pointer_obj.add_attr('data_index', None)
if 'data_length' in data_pointer:
pointer_obj.add_attr('data_length', data_pointer['data_length'])
else:
pointer_obj.add_attr('data_length', None)
def __repr__(self):
return "<AmiraHeader with {:,} bytes>".format(len(self))
def __str__(self):
string = "*" * 50 + "\n"
string += "AMIRA HEADER\n"
string += "-" * 50 + "\n"
string += "{}\n".format(self.designation)
string += "-" * 50 + "\n"
string += "{}\n".format(self.definitions)
string += "-" * 50 + "\n"
string += "{}\n".format(self.parameters)
string += "-" * 50 + "\n"
string += "{}\n".format(self.data_pointers)
string += "*" * 50
return string
def main():
try:
fn = sys.argv[1]
except IndexError:
print("usage: ./{} <amira-fn>".format(__file__), file=sys.stderr)
return 1
h = AmiraHeader.from_file(fn, verbose=False)
# print h.parameters
# print h.parameters.attrs
# print h.parameters.Materials, type(h.parameters.Materials)
# print h.parameters.Materials.attrs
# print h.parameters.Materials.Exterior
# print h.parameters.Materials.ids
# print h.parameters.Materials[1].attrs
for id_ in h.parameters.Materials.ids:
print
h.parameters.Materials[id_]
print
return 0
if __name__ == "__main__":
sys.exit(main())