# Reading TRX Files This page covers the common patterns for loading and inspecting TRX data. See {doc}`api_layers` for guidance on choosing between `AnyTrxFile` and `TrxFile
`. ## Load and inspect The simplest entry point is {func}`trx::load_any`, which detects the dtype from the file and returns an {class}`trx::AnyTrxFile`: ```cpp #include auto trx = trx::load_any("/path/to/tracks.trx"); std::cout << "dtype : " << trx.positions.dtype << "\n"; std::cout << "streamlines : " << trx.num_streamlines() << "\n"; std::cout << "vertices : " << trx.num_vertices() << "\n"; trx.close(); ``` ## Access positions and offsets Positions and offsets are exposed as {class}`trx::TypedArray` objects. Call `as_matrix()` to obtain an `Eigen::Map` view without copying data: ```cpp auto pos = trx.positions.as_matrix(); // (NB_VERTICES, 3) auto offs = trx.offsets.as_matrix(); // (NB_STREAMLINES + 1, 1) for (size_t i = 0; i < trx.num_streamlines(); ++i) { const size_t start = static_cast(offs(i, 0)); const size_t end = static_cast(offs(i + 1, 0)); // vertices for streamline i: pos.block(start, 0, end - start, 3) } ``` ## Access DPV and DPS Per-vertex (DPV) and per-streamline (DPS) metadata are stored in `std::map` containers: ```cpp // List all DPS fields for (const auto& [name, arr] : trx.data_per_streamline) { std::cout << "dps/" << name << " (" << arr.rows() << " x " << arr.cols() << ")\n"; } // Access a specific DPV field auto fa = trx.data_per_vertex.at("fa").as_matrix(); // (NB_VERTICES, 1) ``` ## Access groups ```cpp // AnyTrxFile: groups are TypedArray values. for (const auto& [name, arr] : trx.groups) { std::cout << "group " << name << ": " << arr.rows << " streamlines\n"; } auto ids = trx.groups.at("CST_L").as_matrix(); // (N, 1) ``` ## Typed access via TrxFile\
When the dtype is known ahead of time, use {func}`trx::load` for a typed view. Positions and DPV arrays are exposed as `Eigen::Matrix` directly, avoiding element-wise conversion: ```cpp auto reader = trx::load("tracks.trx"); auto& trx = *reader; // trx.streamlines->_data is Eigen::Matrix // trx.streamlines->_offsets is Eigen::Matrix // Groups are discovered at load time but loaded lazily on first access. for (const auto& kv : trx.groups) { const auto& name = kv.first; const auto* group = trx.get_group_members(name); // lazy load + cache if (group == nullptr) { continue; } std::cout << name << ": " << group->_matrix.rows() << " members\n"; } reader->close(); ``` ## Iterating streamlines without copying Because TRX positions are memory-mapped, the full positions array is never read into RAM at once — the OS pages in only the regions you touch. You do not need a separate "streaming reader" to process a 10 M-streamline file without exhausting memory. To iterate over each streamline with zero per-streamline allocation, use {func}`trx::TrxFile::for_each_streamline`. The callback receives the streamline index, the start row in `_data`, and the number of vertices: ```cpp auto reader = trx::load("tracks.trx"); auto& trx = *reader; trx.for_each_streamline([&](size_t idx, uint64_t start, uint64_t length) { // Zero-copy block view of this streamline's vertices. auto pts = trx.streamlines->_data.block( static_cast(start), 0, static_cast(length), 3); // pts is an Eigen expression — no heap allocation. // Example: compute the centroid. Eigen::Vector3f centroid = pts.colwise().mean(); }); reader->close(); ``` For random access to a single streamline, use {func}`trx::TrxFile::get_streamline`. This copies the vertices into a `std::vector` and is convenient for one-off lookups, but avoid it in tight loops over large tractograms: ```cpp auto pts = trx.get_streamline(42); // std::vector> ``` ## Chunk-based iteration (AnyTrxFile) {class}`trx::AnyTrxFile` provides `for_each_positions_chunk`, which iterates the positions buffer in fixed-size byte chunks. This is useful for transcoding or checksum passes that process all vertices but do not need the per-streamline boundary structure: ```cpp auto trx = trx::load_any("tracks.trx"); trx.for_each_positions_chunk( 4 * 1024 * 1024, // 4 MB chunks [](trx::TrxScalarType dtype, const void* data, size_t point_offset, size_t point_count) { // data points to point_count * 3 values of the given dtype, // starting at global vertex index point_offset. }); trx.close();; ```