Task 458: .NRX File Format
Task 458: .NRX File Format
1. List of all the properties of this file format intrinsic to its file system
The .NRX file format is a proprietary compressed binary format used by Neurolucida Explorer (part of the MBF Bioscience suite) for storing digital reconstructions of neural morphologies. It is closely related to the Neurolucida .DAT format, which is the binary format for Neurolucida software. No complete public specification is available, as it is proprietary and has been reverse-engineered for converters like xyz2swc. Based on available reverse-engineered information, the format consists of a header, a hierarchy of data blocks, and a footer. The intrinsic properties (structural components) are:
- Header: A 70-byte fixed header starting with bytes 0x04, 'V', '3', ' ', 'D', 'A', 'T', ' ', 'f', 'i', 'l', 'e', followed by zeros, and ending with 0x2B, 0xAD.
- Sections: Blocks containing section data for the reconstruction (e.g., Z-depth layers).
- Image Data: Blocks for embedded image information associated with the reconstruction.
- Global Markers: Blocks for markers that are not tied to specific trees or contours (e.g., global annotations).
- Contours: Blocks for contour data, representing outlines or boundaries (e.g., soma contours).
- Trees: Blocks for tree structures, including sample points with coordinates (X, Y, Z), radius, type (e.g., soma, axon, dendrite), index, and parent connections to form the neural tree.
- Text: Blocks for textual annotations or labels.
- Footer: A 4-byte footer with 0xAABBCCDD. Some .NRX files may have additional data after the footer, possibly garbage or compression artifacts.
The format is little-endian binary, and blocks have 6-byte headers for identification. The properties represent a hierarchical tree structure for 3D neural reconstructions, with additional metadata like scaling factors, shrinkage corrections, and lens calibrations implied in the data blocks.
2. Two direct download links for files of format .NRX
- https://neuromorpho.org/dableFiles/hamad/Source-Version/Culture-151-6.nrx
- https://neuromorpho.org/dableFiles/somogyi/Source-Version/070824-3cs.nrx
These are original .NRX files from NeuroMorpho.Org, representing neural reconstructions.
3. Ghost blog embedded html javascript that allows a user to drag n drop a file of format .NRX and it will dump to screen all these properties
Note: Since the full byte-level parsing spec is not publicly available, this script checks the header, footer, and identifies block types based on reverse-engineered info. It reads the file as an ArrayBuffer, validates the format, and dumps the properties in a high-level summary (e.g., presence of blocks). For full decoding, proprietary tools like Neurolucida Explorer are required.
Drag and Drop .NRX File
4. Python class that can open any file of format .NRX and decode read and write and print to console all the properties from the above list
Note: Due to the proprietary nature and incomplete public spec, this class validates the header/footer and lists high-level properties. Full decode/write would require proprietary knowledge; it assumes compressed binary and prints inferred properties.
import struct
import os
class NRXFile:
def __init__(self, filepath):
self.filepath = filepath
self.data = None
self.properties = {}
def read(self):
with open(self.filepath, 'rb') as f:
self.data = f.read()
# Decode header
header = self.data[0:70]
unpacked_header = struct.unpack('<Bcccccccccccc' + '34x' + 'BB', header) # Simplified unpack
if unpacked_header[0] == 0x04 and ''.join(unpacked_header[1:12]).startswith('V3 DAT file') and unpacked_header[-2] == 0x2B and unpacked_header[-1] == 0xAD:
self.properties['Header'] = 'Valid'
else:
raise ValueError('Invalid NRX header')
# Decode footer
footer = self.data[-4:]
if struct.unpack('<I', footer)[0] == 0xAABBCCDD:
self.properties['Footer'] = 'Valid'
else:
self.properties['Footer'] = 'Invalid or extra data'
# High-level blocks (placeholder; full parse not possible without spec)
block_types = ['Sections', 'Image Data', 'Global Markers', 'Contours', 'Trees', 'Text']
for bt in block_types:
self.properties[bt] = 'Present (inferred)'
# Inferred from trees/contours
self.properties['Additional'] = ['Coordinates (X, Y, Z)', 'Radius', 'Type (soma, axon, dendrite)', 'Index and Parent', 'Scaling factors', 'Shrinkage corrections']
def print_properties(self):
print('NRX File Properties:')
for key, value in self.properties.items():
print(f'- {key}: {value}')
def write(self, new_filepath):
# Write (simplified; recreates basic structure)
with open(new_filepath, 'wb') as f:
# Write header
header = b'\x04V3 DAT file' + b'\x00' * 52 + b'\x2B\xAD'
f.write(header)
# Placeholder blocks data
f.write(b'\x00' * 100) # Dummy blocks
# Footer
f.write(struct.pack('<I', 0xAABBCCDD))
print(f'Wrote basic NRX file to {new_filepath}')
# Example usage
# nrx = NRXFile('example.nrx')
# nrx.read()
# nrx.print_properties()
# nrx.write('new.nrx')
5. Java class that can open any file of format .NRX and decode read and write and print to console all the properties from the above list
Note: Similar limitations as above; uses byte array for decoding.
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class NRXFile {
private String filepath;
private byte[] data;
private String[] properties;
public NRXFile(String filepath) {
this.filepath = filepath;
}
public void read() throws IOException {
File file = new File(filepath);
data = new byte[(int) file.length()];
try (FileInputStream fis = new FileInputStream(file)) {
fis.read(data);
}
// Decode header
ByteBuffer bb = ByteBuffer.wrap(data, 0, 70).order(ByteOrder.LITTLE_ENDIAN);
byte first = bb.get();
String headerStr = new String(data, 1, 11);
byte last1 = data[68];
byte last2 = data[69];
if (first == 0x04 && headerStr.startsWith("V3 DAT file") && last1 == 0x2B && last2 == (byte)0xAD) {
System.out.println("Header: Valid");
} else {
throw new IOException("Invalid NRX header");
}
// Decode footer
bb = ByteBuffer.wrap(data, data.length - 4, 4).order(ByteOrder.LITTLE_ENDIAN);
if (bb.getInt() == 0xAABBCCDD) {
System.out.println("Footer: Valid");
} else {
System.out.println("Footer: Invalid or extra data");
}
// High-level blocks
String[] blockTypes = {"Sections", "Image Data", "Global Markers", "Contours", "Trees", "Text"};
properties = new String[blockTypes.length + 1];
for (int i = 0; i < blockTypes.length; i++) {
properties[i] = blockTypes[i] + ": Present (inferred)";
}
properties[properties.length - 1] = "Additional: Coordinates (X, Y, Z), Radius, Type, Index and Parent, Scaling factors, Shrinkage corrections";
}
public void printProperties() {
System.out.println("NRX File Properties:");
for (String prop : properties) {
System.out.println("- " + prop);
}
}
public void write(String newFilepath) throws IOException {
try (FileOutputStream fos = new FileOutputStream(newFilepath)) {
// Write header
fos.write(0x04);
fos.write("V3 DAT file".getBytes());
byte[] zeros = new byte[52];
fos.write(zeros);
fos.write(0x2B);
fos.write(0xAD);
// Placeholder blocks
byte[] dummy = new byte[100];
fos.write(dummy);
// Footer
ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
bb.putInt(0xAABBCCDD);
fos.write(bb.array());
}
System.out.println("Wrote basic NRX file to " + newFilepath);
}
// Example usage
// public static void main(String[] args) throws IOException {
// NRXFile nrx = new NRXFile("example.nrx");
// nrx.read();
// nrx.printProperties();
// nrx.write("new.nrx");
// }
}
6. Javascript class that can open any file of format .NRX and decode read and write and print to console all the properties from the above list
Note: Uses FileReader for read; write uses Blob and URL for download simulation. Console.log for print.
class NRXFile {
constructor(filepath) {
this.filepath = filepath;
this.data = null;
this.properties = {};
}
async read(file) { // Pass File object (e.g., from input)
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
this.data = new Uint8Array(e.target.result);
// Decode header
const header = this.data.subarray(0, 70);
const headerStr = String.fromCharCode(...header.subarray(1, 12));
if (header[0] === 0x04 && headerStr.startsWith('V3 DAT file') && header[68] === 0x2B && header[69] === 0xAD) {
this.properties.Header = 'Valid';
} else {
reject('Invalid NRX header');
}
// Decode footer
const footer = this.data.subarray(-4);
const footerVal = (footer[3] << 24) | (footer[2] << 16) | (footer[1] << 8) | footer[0]; // Little-endian
this.properties.Footer = (footerVal === 0xAABBCCDD) ? 'Valid' : 'Invalid or extra data';
// Blocks
const blockTypes = ['Sections', 'Image Data', 'Global Markers', 'Contours', 'Trees', 'Text'];
blockTypes.forEach(bt => this.properties[bt] = 'Present (inferred)');
this.properties.Additional = ['Coordinates (X, Y, Z)', 'Radius', 'Type', 'Index and Parent', 'Scaling factors', 'Shrinkage corrections'];
resolve();
};
reader.readAsArrayBuffer(file);
});
}
printProperties() {
console.log('NRX File Properties:');
for (const [key, value] of Object.entries(this.properties)) {
console.log(`- ${key}: ${Array.isArray(value) ? value.join(', ') : value}`);
}
}
write(filename) {
const buffer = new Uint8Array(174); // Header + dummy + footer
buffer[0] = 0x04;
const headerText = 'V3 DAT file'.split('').map(c => c.charCodeAt(0));
headerText.forEach((code, i) => buffer[i + 1] = code);
buffer[68] = 0x2B;
buffer[69] = 0xAD;
// Dummy blocks
// Footer
buffer[170] = 0xAA;
buffer[171] = 0xBB;
buffer[172] = 0xCC;
buffer[173] = 0xDD;
const blob = new Blob([buffer], {type: 'application/octet-stream'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename || 'new.nrx';
a.click();
URL.revokeObjectURL(url);
console.log('Wrote basic NRX file');
}
}
// Example usage
// const input = document.createElement('input');
// input.type = 'file';
// input.onchange = async (e) => {
// const nrx = new NRXFile('example.nrx');
// await nrx.read(e.target.files[0]);
// nrx.printProperties();
// nrx.write('new.nrx');
// };
// document.body.appendChild(input);
7. C class that can open any file of format .NRX and decode read and write and print to console all the properties from the above list
Note: Uses struct for header; simplified.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct {
char *filepath;
uint8_t *data;
size_t size;
// Properties as array of strings for simplicity
char *properties[10];
int prop_count;
} NRXFile;
NRXFile* nrx_create(const char *filepath) {
NRXFile *nrx = malloc(sizeof(NRXFile));
nrx->filepath = strdup(filepath);
nrx->data = NULL;
nrx->size = 0;
nrx->prop_count = 0;
return nrx;
}
void nrx_read(NRXFile *nrx) {
FILE *f = fopen(nrx->filepath, "rb");
if (!f) {
printf("Error opening file\n");
return;
}
fseek(f, 0, SEEK_END);
nrx->size = ftell(f);
fseek(f, 0, SEEK_SET);
nrx->data = malloc(nrx->size);
fread(nrx->data, 1, nrx->size, f);
fclose(f);
// Decode header
if (nrx->size < 74 || nrx->data[0] != 0x04 || strncmp((char*)&nrx->data[1], "V3 DAT file", 11) || nrx->data[68] != 0x2B || nrx->data[69] != 0xAD) {
printf("Invalid NRX header\n");
return;
}
nrx->properties[nrx->prop_count++] = "Header: Valid";
// Decode footer
uint32_t footer = *(uint32_t*)(&nrx->data[nrx->size - 4]);
if (footer == 0xAABBCCDD) { // Assumes little-endian host
nrx->properties[nrx->prop_count++] = "Footer: Valid";
} else {
nrx->properties[nrx->prop_count++] = "Footer: Invalid or extra data";
}
// Blocks
const char *blockTypes[] = {"Sections", "Image Data", "Global Markers", "Contours", "Trees", "Text"};
for (int i = 0; i < 6; i++) {
char buf[50];
sprintf(buf, "%s: Present (inferred)", blockTypes[i]);
nrx->properties[nrx->prop_count++] = strdup(buf);
}
nrx->properties[nrx->prop_count++] = "Additional: Coordinates (X, Y, Z), Radius, Type, Index and Parent, Scaling factors, Shrinkage corrections";
}
void nrx_print_properties(NRXFile *nrx) {
printf("NRX File Properties:\n");
for (int i = 0; i < nrx->prop_count; i++) {
printf("- %s\n", nrx->properties[i]);
}
}
void nrx_write(NRXFile *nrx, const char *new_filepath) {
FILE *f = fopen(new_filepath, "wb");
if (!f) {
printf("Error writing file\n");
return;
}
// Header
uint8_t header[70] = {0x04};
memcpy(&header[1], "V3 DAT file", 11);
header[68] = 0x2B;
header[69] = 0xAD;
fwrite(header, 1, 70, f);
// Dummy blocks
uint8_t dummy[100] = {0};
fwrite(dummy, 1, 100, f);
// Footer
uint32_t footer = 0xAABBCCDD;
fwrite(&footer, 4, 1, f);
fclose(f);
printf("Wrote basic NRX file to %s\n", new_filepath);
}
void nrx_destroy(NRXFile *nrx) {
free(nrx->data);
for (int i = 0; i < nrx->prop_count; i++) {
if (i >= 2) free(nrx->properties[i]); // Free duplicated strings
}
free(nrx->filepath);
free(nrx);
}
// Example usage
// int main() {
// NRXFile *nrx = nrx_create("example.nrx");
// nrx_read(nrx);
// nrx_print_properties(nrx);
// nrx_write(nrx, "new.nrx");
// nrx_destroy(nrx);
// return 0;
// }