Pass NumPy Arrays to the Model
| Field | Value |
|---|---|
| Difficulty | Intermediate |
| Estimated Read Time | 10-15 minutes |
| Labels | numpy, pytorch, tensor, io |
Concept
Move data between Neat tensors and the data structures Python users already have — NumPy arrays and PyTorch tensors — without rewriting your existing preprocessing or postprocessing stacks. Layout, dtype, and copy semantics handled in two calls.
If you are integrating Neat into an existing Python inference stack, this is the interop surface you need. It prevents the common integration mistakes (wrong layout, silent dtype coercion, unexpected aliasing) before you build larger pipelines.
APIs introduced
pyneat.Tensor.from_numpy(array, copy=..., image_format=...)— wrap a NumPy array as a Neat tensor.tensor.to_numpy(copy=...)— materialize a NumPy view (or copy) of a Neat tensor.pyneat.Tensor.from_torch(tensor, copy=..., image_format=...)— same surface for PyTorch.tensor.to_torch(copy=...)— round-trip back to PyTorch.pyneat.PixelFormat.RGB(and siblings) — explicit image-layout tag that controls interpretation.
When to use this
- Existing preprocessing in NumPy: keep that path, then hand off tensors to Neat.
- Existing model/postprocessing in PyTorch: convert in/out cleanly without rewriting your whole stack.
- Interop debugging: use deterministic round-trip checks to confirm data integrity.
Prerequisites Chapter 001.
References
Learning Process
- Build deterministic tensor inputs in NumPy/PyTorch and C++ tensor storage.
- Convert across boundaries (NumPy/PyTorch <-> Neat tensor).
- Verify round-trip shape/bytes/checksum behavior.
Run
Python:
python3 share/sima-neat/tutorials/008_pass_numpy_to_model/pass_numpy_to_model.py \
--width 128 --height 96
C++ (prebuilt):
./lib/sima-neat/tutorials/tutorial_008_pass_numpy_to_model \
--width 128 --height 96
C++ (build from source):
./build.sh --target tutorial_008_pass_numpy_to_model
./build/tutorials-standalone/tutorial_008_pass_numpy_to_model \
--width 128 --height 96
To integrate this chapter's C++ source into your own project with a custom CMakeLists.txt (no extras folder required), see How to Run Tutorials on the landing page.
Code
// Convert a cv::Mat into a Neat Tensor, map it read-only, and clone it.
//
// Usage:
// tutorial_008_pass_numpy_to_model [--width 128] [--height 96]
#include "neat.h"
#include <opencv2/core.hpp>
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <string>
namespace {
bool get_arg(int argc, char** argv, const std::string& key, std::string& out) {
for (int i = 1; i + 1 < argc; ++i) {
if (key == argv[i]) {
out = argv[i + 1];
return true;
}
}
return false;
}
int parse_int_arg(int argc, char** argv, const std::string& key, int def) {
std::string value;
if (!get_arg(argc, argv, key, value))
return def;
return std::stoi(value);
}
} // namespace
int main(int argc, char** argv) {
try {
const int width = parse_int_arg(argc, argv, "--width", 128);
const int height = parse_int_arg(argc, argv, "--height", 96);
cv::Mat rgb(height, width, CV_8UC3, cv::Scalar(7, 17, 27));
if (!rgb.isContinuous())
rgb = rgb.clone();
// CORE LOGIC
// from_cv_mat converts a cv::Mat into a CPU-backed Neat Tensor.
simaai::neat::Tensor tensor = simaai::neat::from_cv_mat(
rgb, simaai::neat::ImageSpec::PixelFormat::RGB, simaai::neat::TensorMemory::CPU);
// map_read yields a Mapping with a raw pointer and size in bytes.
simaai::neat::Mapping mapped = tensor.map_read();
std::uint64_t checksum = 0;
const auto* bytes = static_cast<const std::uint8_t*>(mapped.data);
const std::size_t n = std::min<std::size_t>(mapped.size_bytes, 256);
for (std::size_t i = 0; i < n; ++i)
checksum += bytes[i];
// CORE LOGIC
// clone() copies into CPU-owned storage, detached from the cv::Mat buffer.
simaai::neat::Tensor owned = tensor.clone();
std::cout << "tensor_rank=" << tensor.shape.size() << "\n";
std::cout << "tensor_bytes=" << mapped.size_bytes << "\n";
std::cout << "head_checksum=" << checksum << "\n";
std::cout << "clone_bytes=" << owned.dense_bytes_tight() << "\n";
std::cout << "[OK] 008_pass_numpy_to_model\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}