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

3. Ghost blog embedded HTML JavaScript for drag and drop .NMF file to dump properties

NMF File Dumper
Drag and drop .NMF file here

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);