Task 453: .NMF File Format
Task 453: .NMF File Format
1. List of all the properties of this file format intrinsic to its file system
The .NMF file format is the Neuromorphological File Format developed by MBF Bioscience, which is an XML-based format for representing digital reconstructions and models of microscopic anatomies from microscopy images. It uses XML 1.0 with ISO-8859-1 encoding. The format includes header metadata for provenance and sample details, and tracing data for structures like neurons, vessels, and annotations. All coordinates and measurements are in micrometers (µm) in a 3D space with origin at (0, 0, 0). The intrinsic properties (elements, attributes, and structures) are as follows:
XML Declaration Properties:
- version: "1.0"
- encoding: "ISO-8859-1"
Root <mbf> Element Properties:
- version: "4.0" (or similar version number)
- appname: String (software name that generated the file)
- appversion: String (format: YYYY.V.M)
- apprrid: String (Research Resource Identifier for the application, e.g., SCR_xxxxx)
- insrrid: String (Institution RRID, e.g., SCR_xxxxx)
Header Elements:
<description>: CDATA string (user-defined description of file contents)<filefacts>:<section>(multiple possible): sid (string), name (string), top (float), cutthickness (float), mountedthickness (float)<sectionmanager>: currentsection (string), sectioninterval (integer), startingsection (string/integer)<sparcdata>:<subject>: species (URL string to NCBI Taxon ID), subjectid (string), sex (string: "Male", "Female", "blind to condition"), age (string with units or "blind to condition")<atlas>: organ (string), label (string), rootid (URL string)<property name="TimePointManager">: Empty (unused, but always present)<images>(contains one or more<image>):<filename>: String (file path)<channels>: merge ("yes"/"no")<channel>(multiple, typically 3 for red/green/blue): id (string: "red", "green", "blue"), source (string: channel number or "none")<scale>: x (float), y (float)<coord>: x (float), y (float), z (float)<zspacing>: z (float), slices (integer)<thumbnail>: cols ("64"), rows ("64")<thumbnail-line>(64 instances): String of 192 hexadecimal characters (64 pixels × 3 bytes RRGGBB)
Tracing Data Elements (multiple instances possible for each type, with common sub-properties like <point>: x (float), y (float), z (float), d (float for diameter); and <property name="Channel">: version (integer), channel_number (integer), color (hex #RRGGBB); <property name="Set">: set_name (string)):
<marker>: type (string, e.g., "Plus", "OpenCircle"), color (hex RRGGBB), name (string), varicosity ("true"/"false")- For puncta (name="Punctum"): Additional
<property name="Punctum">with 11 values (version, spread, mean luminance, surface area, voxel count, 2D flag, volume, type, location, colocalized fraction, proximal fraction);<property name="VolumeRLE">:string (scaling x y z, total voxels VT, voxel dimensions Vx Vy Vz, origin x y z, run-length pairs B1 F1 ...) <contour>: name (string), color (hex RRGGBB), closed ("true"/"false"), shape (string: "Contour", "Circle", "Box")<property name="GUID">:unique ID (string)<property name="FillDensity">: 0-255 (integer)<property name="TraceAssociation">:URL (string)<resolution>: float- Child markers possible
<tree>(for neurons): color (hex RRGGBB), type (string: "Axon", "Dendrite", "Apical Dendrite"), leaf (string: "Normal", etc.), zsmear alpha (float), beta (float)<spine>(child): version (integer), classification (string, e.g., "stubby")<property name="Class">:class (string)<property name="Color">: hex (RRGGBB)<property name="Volume">: volume (float)<property name="Generated">: 0/1 (integer)<property name="GeneratedMetrics">: 21 values (floats/ints for extent, diameters, positions, surface area, voxel count, etc.)<property name="Backbone">:point list string<property name="VolumeRLE">: Same as punctum<varicosity>(child): version (integer), color (hex RRGGBB), generated ("true"/"false"), length (float), maximumdiameter (float), thicknessratio (float), is2d (integer), anchoroffset (float), attachment (float)- Five for volume definition
<vessel>: version ("2"), color (hex RRGGBB), type ("undirected"), name (string)<nodes>: <node id="N" (integer)>:<edges>: <edge id="N" (integer), type ("origin")>: list<edgelists>: <edgelist id="N" (integer), edge (integer), sourcenode (integer or -1), targetnode (integer or -1)><arrow>: name ("Arrow"), color (hex RRGGBB), tail ("true"/"false")<text>: color (hex RRGGBB)<font>: name (string), size (integer)<value>: string (text content)<scalebar>: color (hex RRGGBB)<value>: float (length in µm)<showlabel>: "true"/"false"<showunits>: "true"/"false"
These properties define the file's structure, metadata, and data elements.
2. Two direct download links for files of format .NMF
- https://data.mendeley.com/datasets/wpzd2wxtgn/1 (Zip archive containing protocols and example neuromorphological data, including .nmf files for neuron tracing examples)
- https://github.com/MBFBioscience/nmf-schema/raw/main/examples/example.nmf (Example .nmf file from the schema repository for testing the format; note: actual file name may vary, but repository contains samples)
3. Ghost blog embedded HTML JavaScript for drag and drop .NMF file to dump properties
4. Python class for .NMF file
import xml.etree.ElementTree as ET
import xml.dom.minidom as minidom
class NMFHandler:
def __init__(self):
self.tree = None
self.root = None
def open(self, filename):
self.tree = ET.parse(filename)
self.root = self.tree.getroot()
def decode_read(self):
if not self.root:
raise ValueError("No file opened")
return self.root
def print_properties(self):
if not self.root:
raise ValueError("No file opened")
print(self._dump_element(self.root))
def _dump_element(self, element, indent=''):
result = f"{indent}<{element.tag}"
for key, value in element.attrib.items():
result += f' {key}="{value}"'
result += ">\n"
if element.text and element.text.strip():
result += f"{indent} {element.text.strip()}\n"
for child in element:
result += self._dump_element(child, indent + ' ')
result += f"{indent}</{element.tag}>\n"
return result
def write(self, filename, new_data=None):
if new_data:
self.root = ET.Element('mbf', new_data.get('mbf_attrib', {}))
# Add example header for write (simplified)
ET.SubElement(self.root, 'description').text = new_data.get('description', 'Example')
# Add more as needed
self.tree = ET.ElementTree(self.root)
if self.tree:
xml_str = minidom.parseString(ET.tostring(self.root)).toprettyxml(indent=" ")
with open(filename, 'w', encoding='iso-8859-1') as f:
f.write(xml_str)
else:
raise ValueError("No data to write")
# Example usage:
# handler = NMFHandler()
# handler.open('example.nmf')
# handler.print_properties()
# handler.write('new.nmf', {'mbf_attrib': {'version': '4.0', 'appname': 'Test'}})
5. Java class for .NMF file
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
public class NMFHandler {
private Document doc;
public void open(String filename) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(new File(filename));
}
public Document decodeRead() {
if (doc == null) throw new IllegalStateException("No file opened");
return doc;
}
public void printProperties() {
if (doc == null) throw new IllegalStateException("No file opened");
System.out.println(dumpNode(doc.getDocumentElement(), ""));
}
private String dumpNode(Node node, String indent) {
StringBuilder result = new StringBuilder();
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element elem = (Element) node;
result.append(indent).append("<").append(elem.getTagName());
for (int i = 0; i < elem.getAttributes().getLength(); i++) {
Node attr = elem.getAttributes().item(i);
result.append(" ").append(attr.getNodeName()).append("=\"").append(attr.getNodeValue()).append("\"");
}
result.append(">\n");
if (node.getTextContent() != null && !node.getTextContent().trim().isEmpty()) {
result.append(indent).append(" ").append(node.getTextContent().trim()).append("\n");
}
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
result.append(dumpNode(children.item(i), indent + " "));
}
result.append(indent).append("</").append(elem.getTagName()).append(">\n");
}
return result.toString();
}
public void write(String filename, String newDescription) throws Exception {
if (doc == null) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.newDocument();
Element root = doc.createElement("mbf");
root.setAttribute("version", "4.0");
root.setAttribute("appname", "Test");
doc.appendChild(root);
Element desc = doc.createElement("description");
desc.setTextContent(newDescription != null ? newDescription : "Example");
root.appendChild(desc);
// Add more elements as needed
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filename));
transformer.transform(source, result);
}
// Example usage:
// NMFHandler handler = new NMFHandler();
// handler.open("example.nmf");
// handler.printProperties();
// handler.write("new.nmf", "New description");
}
6. JavaScript class for .NMF file
class NMFHandler {
constructor() {
this.xmlDoc = null;
}
open(filePath) {
// For node.js, use fs to read (assume browser or adapt)
// This example assumes async read for browser, but for console, use sync if node
return new Promise((resolve, reject) => {
const fs = require('fs'); // For node.js
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) reject(err);
const parser = new DOMParser();
this.xmlDoc = parser.parseFromString(data, 'application/xml');
if (this.xmlDoc.getElementsByTagName('parsererror').length > 0) reject('Parse error');
resolve();
});
});
}
decodeRead() {
if (!this.xmlDoc) throw new Error('No file opened');
return this.xmlDoc;
}
printProperties() {
if (!this.xmlDoc) throw new Error('No file opened');
console.log(this.dumpNode(this.xmlDoc.documentElement, ''));
}
dumpNode(node, indent = '') {
let result = '';
if (node.nodeType === 1) {
result += `${indent}<${node.tagName}`;
for (let i = 0; i < node.attributes.length; i++) {
const attr = node.attributes[i];
result += ` ${attr.name}="${attr.value}"`;
}
result += '>\n';
if (node.textContent && node.textContent.trim()) {
result += `${indent} ${node.textContent.trim()}\n`;
}
for (let child of node.childNodes) {
result += this.dumpNode(child, indent + ' ');
}
result += `${indent}</${node.tagName}>\n`;
}
return result;
}
write(filePath, newData = {}) {
if (!this.xmlDoc) {
const parser = new DOMParser();
this.xmlDoc = parser.parseFromString('<mbf version="4.0" appname="Test"></mbf>', 'application/xml');
}
// Modify or add, example add description
const desc = this.xmlDoc.createElement('description');
desc.textContent = newData.description || 'Example';
this.xmlDoc.documentElement.appendChild(desc);
const serializer = new XMLSerializer();
const xmlStr = serializer.serializeToString(this.xmlDoc);
const fs = require('fs'); // For node.js
fs.writeFileSync(filePath, xmlStr, 'iso-8859-1');
}
}
// Example usage:
// const handler = new NMFHandler();
// await handler.open('example.nmf');
// handler.printProperties();
// handler.write('new.nmf', {description: 'New'});
7. C class for .NMF file
(Note: C does not have built-in XML parsing, so this uses pseudo-code assuming libxml2 library for parsing. Compile with libxml2.)
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
typedef struct {
xmlDocPtr doc;
} NMFHandler;
void nmf_open(NMFHandler *handler, const char *filename) {
handler->doc = xmlReadFile(filename, NULL, 0);
if (handler->doc == NULL) {
fprintf(stderr, "Failed to parse %s\n", filename);
}
}
xmlDocPtr nmf_decode_read(NMFHandler *handler) {
if (handler->doc == NULL) {
fprintf(stderr, "No file opened\n");
return NULL;
}
return handler->doc;
}
void nmf_print_properties(NMFHandler *handler) {
if (handler->doc == NULL) {
fprintf(stderr, "No file opened\n");
return;
}
xmlNode *root = xmlDocGetRootElement(handler->doc);
dump_node(root, 0);
}
void dump_node(xmlNode *node, int level) {
if (node == NULL) return;
for (int i = 0; i < level; i++) printf(" ");
printf("<%s", node->name);
for (xmlAttr *attr = node->properties; attr; attr = attr->next) {
printf(" %s=\"%s\"", attr->name, attr->children->content);
}
printf(">\n");
if (node->content && xmlStrlen(node->content) > 0) {
for (int i = 0; i < level; i++) printf(" ");
printf(" %s\n", node->content);
}
for (xmlNode *child = node->children; child; child = child->next) {
dump_node(child, level + 1);
}
for (int i = 0; i < level; i++) printf(" ");
printf("</%s>\n", node->name);
}
void nmf_write(NMFHandler *handler, const char *filename, const char *new_description) {
if (handler->doc == NULL) {
handler->doc = xmlNewDoc(BAD_CAST "1.0");
xmlNode *root = xmlNewNode(NULL, BAD_CAST "mbf");
xmlNewProp(root, BAD_CAST "version", BAD_CAST "4.0");
xmlNewProp(root, BAD_CAST "appname", BAD_CAST "Test");
xmlDocSetRootElement(handler->doc, root);
}
// Add example description
xmlNode *desc = xmlNewChild(xmlDocGetRootElement(handler->doc), NULL, BAD_CAST "description", BAD_CAST (new_description ? new_description : "Example"));
xmlSaveFormatFileEnc(filename, handler->doc, "ISO-8859-1", 1);
}
void nmf_close(NMFHandler *handler) {
xmlFreeDoc(handler->doc);
}
// Example usage:
// NMFHandler handler = {NULL};
// nmf_open(&handler, "example.nmf");
// nmf_print_properties(&handler);
// nmf_write(&handler, "new.nmf", "New description");
// nmf_close(&handler);