Class TensorFlightClient
- All Implemented Interfaces:
AutoCloseable
A tensor is identified by its globally-unique array_id (the tensor
identity policy; see the top of proto/biopb/tensor/descriptor.proto):
either source_id for a single-tensor source or source_id/field
for a multi-tensor one. The array_id-first methods (getTensor(String),
getDescriptor(String), getPhysicalScale(String)) take that one
identifier; there is no (sourceId, tensorId) form.
Usage:
TensorFlightClient client = new TensorFlightClient("localhost:8815");
// Browse the catalog (SQL over the server's DuckDB)
VectorSchemaRoot rows = client.query("SELECT * FROM sources");
// Access a tensor by its array_id ("source_id" or "source_id/field")
RandomAccessibleInterval<UnsignedByteType> arr = client.getTensor("my-source/tensor-0");
long[] pos = { 10, 20, 30 };
UnsignedByteType pixel = arr.getAt(pos);
client.close();
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classPer-dimension physical pixel size + unit for a tensor, as returned bygetPhysicalScale(java.lang.String). -
Constructor Summary
ConstructorsConstructorDescriptionTensorFlightClient(String host, int port) Create a new TensorFlightClient.TensorFlightClient(String host, int port, long cacheBytes) Create a new TensorFlightClient with custom cache size.TensorFlightClient(String host, int port, long cacheBytes, String token) Create a new TensorFlightClient with authentication token.TensorFlightClient(org.apache.arrow.flight.Location location) Create a new TensorFlightClient for an Arrow Flight location.TensorFlightClient(org.apache.arrow.flight.Location location, long cacheBytes) Create a new TensorFlightClient for an Arrow Flight location.TensorFlightClient(org.apache.arrow.flight.Location location, long cacheBytes, String token) Create a new TensorFlightClient for an Arrow Flight location with authentication. -
Method Summary
Modifier and TypeMethodDescriptionvoidclose()deleteRois(String arrayId, List<String> roiIds, String setName) Delete ROI annotations.deregisterLocalPath(String rootUrl) Deregister a drag-dropped source branch on the SERVER at runtime.static TensorDescriptorThe resolved descriptor a SerializedTensor's plan names.static org.apache.arrow.flight.FlightInfoThe plan a SerializedTensor carries: its serialized Arrow FlightInfo.longGet the cache size in bytes.getDescriptor(String arrayId) Fetch one tensor'sTensorDescriptorby its globally-unique array_id.getLabelSets(String imageArrayId) Thearray_ids of the label sets served under an image.org.apache.arrow.flight.LocationGet the Flight server location.getPhysicalScale(String arrayId) Per-dimension physical pixel size + unit for a tensor.getSourceMetadata(String sourceId) Get source-level OME/vendor metadata as a map.<T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>>
net.imglib2.RandomAccessibleInterval<T>Get a RandomAccessibleInterval for a tensor by its globally-unique array_id.<T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>>
net.imglib2.RandomAccessibleInterval<T>Get a RandomAccessibleInterval for a tensor (by array_id) with scaled reads.<T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>>
net.imglib2.RandomAccessibleInterval<T>Get a RandomAccessibleInterval for a tensor (by array_id) with a slice hint.<T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>>
net.imglib2.RandomAccessibleInterval<T>Get a RandomAccessibleInterval for a tensor (by array_id) with all options.getTensorAsPb(String arrayId) Get a SerializedTensor protobuf for a whole tensor.getTensorAsPb(String arrayId, SliceHint sliceHint, long[] scaleHint, String reductionMethod) Get a SerializedTensor protobuf for cross-process transfer.getToken()Get the authentication token.getUploadStatus(String arrayId) Get upload status for a writable tensor.Check server health status via Flight action.Fetch a tensor's ROI annotations.Fetch one layer of a tensor's ROI annotations.pruneRois(int unseenDays, boolean apply) Report, and withapplydelete, annotations whose image is gone.putRois(String arrayId, List<RoiAnnotation> rois) Create or update ROI annotations on a tensor, as one batch.putRois(String arrayId, List<RoiAnnotation> rois, boolean checkRev) Create or update ROI annotations, optionally conditional onrev.org.apache.arrow.vector.VectorSchemaRootExecute SQL query against server's source metadata database.registerLocalPath(String url) Register a local path on the SERVER as a served source at runtime.registerLocalPath(String url, String sourceType, Consumer<AddSourceProgress> onProgress, BooleanSupplier shouldCancel) Register a path on the server, with progress and cancellation hooks.org.apache.arrow.vector.VectorSchemaRootresolveSource(String sourceId) Resolve an unresolved source and return itssourcescatalog row.org.apache.arrow.vector.VectorSchemaRootresolveSource(String sourceId, Consumer<ResolveProgress> onProgress, BooleanSupplier shouldCancel) Resolve an unresolved source with optional progress and cancellation hooks.setupArrayUpload(String arrayId, long[] shape, String dtype, long[] chunkShape, List<String> dimLabels, String omeMetadataJson) Declare a tensor to fill: the first half of an upload.setupArrayUpload(String arrayId, long[] shape, String dtype, long[] chunkShape, List<String> dimLabels, String omeMetadataJson, Integer ttlSeconds) setupArrayUpload(String, long[], String, long[], List, String)with a lifetime.<T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>>
TensorDescriptorsetupArrayUpload(String arrayId, net.imglib2.RandomAccessibleInterval<T> template, long[] chunkShape, List<String> dimLabels, String omeMetadataJson) Declare a tensor shaped like an array you already hold.setUploadStatus(String arrayId, UploadStatus.State state, String reason) Move an upload along its lifecycle; the only thing that moves one.static <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>>
net.imglib2.RandomAccessibleInterval<T>tensorFromPb(SerializedTensor pb, long cacheBytes) The lazy imglib2 array a SerializedTensor describes.uploadArray(TensorDescriptor descriptor, net.imglib2.RandomAccessibleInterval<T> array) Fill a declared tensor with an array, and seal it.<T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>>
voiduploadChunk(TensorDescriptor descriptor, ChunkBounds bounds, net.imglib2.RandomAccessibleInterval<T> source) Upload one chunk of a declared tensor.warmSource(String sourceId) Hydrate-ahead: ask the server to recall all of a resolved multi-file source's member files, so later reads are warm and never stall.warmSource(String sourceId, Consumer<WarmProgress> onProgress, BooleanSupplier shouldCancel) Warm a source with optional progress and cancellation hooks.
-
Constructor Details
-
TensorFlightClient
Create a new TensorFlightClient.- Parameters:
host- Server hostport- Server port
-
TensorFlightClient
Create a new TensorFlightClient with custom cache size.- Parameters:
host- Server hostport- Server portcacheBytes- Maximum cache size in bytes
-
TensorFlightClient
Create a new TensorFlightClient with authentication token.- Parameters:
host- Server hostport- Server portcacheBytes- Maximum cache size in bytestoken- Bearer token for authentication (null disables auth)
-
TensorFlightClient
public TensorFlightClient(org.apache.arrow.flight.Location location) Create a new TensorFlightClient for an Arrow Flight location.- Parameters:
location- Flight server location
-
TensorFlightClient
public TensorFlightClient(org.apache.arrow.flight.Location location, long cacheBytes) Create a new TensorFlightClient for an Arrow Flight location.- Parameters:
location- Flight server locationcacheBytes- Maximum cache size in bytes
-
TensorFlightClient
Create a new TensorFlightClient for an Arrow Flight location with authentication.- Parameters:
location- Flight server locationcacheBytes- Maximum cache size in bytestoken- Bearer token for authentication (null disables auth)
-
-
Method Details
-
getLocation
public org.apache.arrow.flight.Location getLocation()Get the Flight server location.- Returns:
- Location used for this client
-
getToken
Get the authentication token.- Returns:
- Bearer token (null if no authentication)
-
getCacheBytes
public long getCacheBytes()Get the cache size in bytes.- Returns:
- Maximum cache size for cell images
-
query
Execute SQL query against server's source metadata database. Returns Arrow VectorSchemaRoot with query results. Schema metadata may contain "total_sources" key if result was truncated. The server-side metadata database is mandatory (biopb/biopb#225), so any standard tensor-server supports this. Only an embedded server explicitly constructed without a metadata database rejects the query.- Parameters:
sql- SQL query (e.g., "SELECT source_id FROM sources WHERE source_url LIKE '%plate%'")- Returns:
- VectorSchemaRoot with query results (caller must close)
- Throws:
IOException- If query fails or the server has no metadata database attached Example:VectorSchemaRoot result = client.query("SELECT source_id, source_type FROM sources"); System.out.println("Found " + result.getRowCount() + " sources"); result.close();
-
getSourceMetadata
Get source-level OME/vendor metadata as a map.Source-scoped, and read from the source's own catalog row: this is the metadata the format carries for the whole container. A field's own extras (an OME-Zarr HCS field's OME block, an EMD signal's
original_metadata) are per-tensor and are not merged in here.- Parameters:
sourceId- Source identifier- Returns:
- The source's metadata map, or an empty map if it carries none
- Throws:
IllegalArgumentException- if the source is unknownIllegalStateException- if the source is unresolved (cloud / synced-folder) -- callresolveSource(java.lang.String)firstIOException
-
resolveSource
Resolve an unresolved source and return itssourcescatalog row.An unresolved source is catalogued by URL only -- its shape/dtype/field list are unknown until first access (it lists with
is_resolvedfalse and an emptytensors). The canonical case is a cloud / synced-folder ("Files-On-Demand") source.Resolving asks the server to hydrate it. For a dehydrated placeholder this downloads the whole file -- a recall that can take minutes, consume local disk, and fail when offline -- then reads its real shape, dtype, and field list. This is the heavyweight, consenting operation that catalog browsing (
query(java.lang.String)) deliberately avoids; call it only when you intend to read the data. AfterwardsgetTensor(java.lang.String)and friends work normally. Idempotent.- Parameters:
sourceId- The source to resolve (e.g."onedrive_a3f2")- Returns:
- The source's
sourcesrow, with every tensor enumerated -- oneVectorSchemaRoot, the same typequery(java.lang.String)returns, which the caller must close. A row, not a type this SDK picked: every client can already decode one, and what you decode it into stays yours (biopb/biopb#1032).Unlike
warmSource(java.lang.String), which returns a status because residency is not a durable catalog fact and its file counts exist nowhere else, this returns the result: resolving is defined by what it writes to the row. - Throws:
IOException- If the action fails or the server returns no row
-
resolveSource
public org.apache.arrow.vector.VectorSchemaRoot resolveSource(String sourceId, Consumer<ResolveProgress> onProgress, BooleanSupplier shouldCancel) throws IOException Resolve an unresolved source with optional progress and cancellation hooks.Both callbacks run on the calling thread after each streamed action message. Returning true from
shouldCancelstops consuming the action stream; the server may finish its recall independently and cache the result for a later call.- Parameters:
sourceId- source to resolveonProgress- receives server progress heartbeats, or nullshouldCancel- polled once per action message, or null- Returns:
- the terminal catalog row; caller must close it
- Throws:
IOException
-
warmSource
Hydrate-ahead: ask the server to recall all of a resolved multi-file source's member files, so later reads are warm and never stall.resolveSource(java.lang.String)populates a source's metadata but, for a multi-file cloud source (zarr / ome-zarr / ndtiff / tiff-sequence / micromanager), leaves the bulk pixel data dehydrated -- each member file then recalls one-at-a-time, slowly, the first time a read touches it. This walks the source directory server-side and reads every file to force the sync engine's recall; no pixels cross the wire, only progress. It is idempotent (already-resident files are cheap local reads) and a no-op for a single-file source (resolveSource already recalled it). A remote-url source -- an object store, or agrpc://mirror -- fails instead: nothing on the serving machine can be made resident (biopb/biopb#1035).- Parameters:
sourceId- The (already-resolved) source to warm.- Returns:
- The terminal
WarmProgresssnapshot (files/bytes made resident).filesTotal == 0means the source was local and had nothing to warm, i.e. single-file; "not applicable" raises. - Throws:
IOException- If the action fails or it returns no terminal statusUnsupportedOperationException- If the server predates thewarmaction
-
warmSource
public WarmProgress warmSource(String sourceId, Consumer<WarmProgress> onProgress, BooleanSupplier shouldCancel) throws IOException Warm a source with optional progress and cancellation hooks.- Parameters:
sourceId- source to warmonProgress- receives non-terminal warm progress, or nullshouldCancel- polled once per action message, or null- Returns:
- the terminal progress snapshot
- Throws:
IOException
-
registerLocalPath
Register a local path on the SERVER as a served source at runtime.Hands the server a filesystem path (or directory) that it interprets on its own filesystem, and the server routes it through the same claim -> adapter -> catalog pipeline the directory watcher uses. A directory that is not itself a dataset is walked recursively and may register several sources, so this reports a tally rather than one source.
- Parameters:
url- Absolute path (or directory) on the server's filesystem- Returns:
- the terminal
AddSourceResult - Throws:
IOException- If the action fails, the server is too old to support theadd_sourceaction, or it returns no terminal result
-
registerLocalPath
public AddSourceResult registerLocalPath(String url, String sourceType, Consumer<AddSourceProgress> onProgress, BooleanSupplier shouldCancel) throws IOException Register a path on the server, with progress and cancellation hooks.Because a dropped directory's walk has no known size up front, there is no percentage -- progress is a running count of sources registered so far.
Cancelling cancels the RPC, which the server observes and stops discovery on; sources already registered stay registered, and this returns an empty tally rather than raising -- the cancel was intentional.
- Parameters:
url- Absolute path (or directory) on the server's filesystemsourceType- Explicit adapter type ("zarr","ome-zarr", ...); empty means auto-detect via the adapters' claim protocolonProgress- receives oneAddSourceProgressper source as it registers, or nullshouldCancel- polled once per action message, or null- Returns:
- the terminal
AddSourceResult:added/alreadyPresent/refreshed/removedsource_ids andfailed(path, reason) pairs. Re-adding a registered path REBUILDS it against the file as it is now -- that is whatrefreshedreports, and it is how a source picks up an in-place edit. Registration wrote each source's catalog row, so anything beyond the ids is onequery(java.lang.String)away. - Throws:
IOException
-
deregisterLocalPath
Deregister a drag-dropped source branch on the SERVER at runtime.The narrow counterpart to
registerLocalPath(java.lang.String): it removes ONLY drag-dropped sources, which the server identifies by thednd://origin scheme on their catalogsource_url. Every source at or underrootUrlgoes as a unit; a non-dnd://root is refused by the server.- Parameters:
rootUrl- thednd://branch root to remove- Returns:
removedsource_ids, andfailedentries whosepathcarries the source_id- Throws:
IOException
-
getLabelSets
Thearray_ids of the label sets served under an image.A label set is an ordinary tensor of its image, named
<image array_id>/@labels/<name>, so this is a catalog query over the path and nothing more --getTensor(java.lang.String)/getDescriptor(java.lang.String)read one like any other tensor.- Parameters:
imageArrayId- the image's array_id- Returns:
- the sets' array_ids, sorted; empty when the image has none
- Throws:
IOException
-
listRois
Fetch a tensor's ROI annotations.There is no plane or bbox filter: a client hit-tests and re-renders from the resident set. Annotations are private data, gated by the tensor's source like its pixels, so they are not on the SQL surface.
- Parameters:
arrayId- unversioned array_id of the tensor- Returns:
- the annotations, a
truncatedflag, andsets-- every set on the tensor with its stored row count, whateverroiscovers - Throws:
IOException
-
listRois
Fetch one layer of a tensor's ROI annotations.- Parameters:
arrayId- unversioned array_id of the tensorsetName- restrict to one layer, and the only way to read a reserved (@) set; empty means the client-owned sets- Returns:
- the annotations, a
truncatedflag, and the tensor's sets - Throws:
IOException
-
putRois
Create or update ROI annotations on a tensor, as one batch.Geometry is
biopb.image.ROIin LEVEL-0 pixel coordinates -- a shape drawn on a downsampled level must be scaled up by the caller. Only the 2-D vector arms are accepted (point / rectangle / ellipse / polygon / polyline); a mask or mesh is refused, because instance segmentation belongs in a label tensor.An annotation with an empty
roi_idis created (the server mints a uuid4); one that names an existing id is updated. The batch is applied in a single transaction, last writer wins.- Parameters:
arrayId- unversioned array_id every annotation belongs torois- the annotations to store- Returns:
stored(with server-assigned roi_id / rev / timestamps) andconflicts- Throws:
IOException
-
putRois
public RoiPutResult putRois(String arrayId, List<RoiAnnotation> rois, boolean checkRev) throws IOException Create or update ROI annotations, optionally conditional onrev.- Parameters:
arrayId- unversioned array_id every annotation belongs torois- the annotations to storecheckRev- make each write conditional onrevmatching what is stored; mismatches come back inconflictsand are not applied, and the rest of the batch still lands- Returns:
storedandconflicts- Throws:
IOException
-
deleteRois
public RoiDeleteResult deleteRois(String arrayId, List<String> roiIds, String setName) throws IOException Delete ROI annotations.With
roiIds, deletes exactly those. Without, deletes every annotation on the tensor -- narrowed tosetNamewhen given, which is how a whole layer is dropped.- Parameters:
arrayId- unversioned array_id of the tensorroiIds- the ids to delete, or empty for allsetName- narrow a delete-all to one layer, or empty- Returns:
- the ids actually removed
- Throws:
IOException
-
pruneRois
Report, and withapplydelete, annotations whose image is gone.An annotation is unseen when the catalog has not held its source for
unseenDays(a row whose source never appeared counts from its creation). Reserved, server-owned sets are never pruned. Requires the server-wide token: orphans have no source to authorize against.- Parameters:
unseenDays- how long a source must have been absent to countapply- false reports only; true deletes- Returns:
- the unseen annotations grouped per tensor, and the row count deleted (0 on a report)
- Throws:
IOException
-
getDescriptor
Fetch one tensor'sTensorDescriptorby its globally-unique array_id.A tensor is identified by its
array_idalone (see the tensor identity policy at the top ofproto/biopb/tensor/descriptor.proto), so this takes that one identifier. Works even when the source is beyond the server's query row cap. OneGetFlightInfoper call -- nothing is cached, because a descriptor is what the server says now. A baresource_id(single-tensor source, or to anchor on a multi-tensor source's default/first tensor) is accepted. To enumerate ALL tensors/scenes of a source, read its catalog row'stensorscolumn -- NOT this method.This is a cheap probe -- it does NOT resolve. On an unresolved (cloud / synced-folder) source it raises an error pointing at
resolveSource(java.lang.String).- Parameters:
arrayId- Globally-unique tensor id, e.g."zarr_a3f2"or"aics_7f3/Image:0"- Returns:
- The TensorDescriptor for that tensor
-
getPhysicalScale
Per-dimension physical pixel size + unit for a tensor.Returns a
TensorFlightClient.PhysicalScalewhosescaleandunitarrays are aligned with the tensor'sdim_labels(source axis order), ornullwhen no physical sizes are known (an older server, or a format that carries none).physical_scale/physical_unitareTensorDescriptorfields the server fills on everyGetFlightInfo(issue #31), so this is one describe -- the cheap projection, which never requests the opt-inmetadata_jsonfield or the O(chunks) endpoint plan. (ContrastgetSourceMetadata(java.lang.String), which ships the whole OME tree; do not dig physical sizes out of that -- this is the compact projection meant for display scale.)- Parameters:
arrayId- Globally-unique tensor id (source_idorsource_id/field). A bare source id anchors on the source's default (first) tensor.- Returns:
- A PhysicalScale, or
nullif no physical scale is known
-
getTensor
public <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> net.imglib2.RandomAccessibleInterval<T> getTensor(String arrayId) Get a RandomAccessibleInterval for a tensor by its globally-unique array_id.- Type Parameters:
T- The pixel type- Parameters:
arrayId- Globally-unique tensor id (source_idorsource_id/field)- Returns:
- RandomAccessibleInterval containing the requested tensor
-
getTensor
public <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> net.imglib2.RandomAccessibleInterval<T> getTensor(String arrayId, SliceHint sliceHint) Get a RandomAccessibleInterval for a tensor (by array_id) with a slice hint.- Type Parameters:
T- The pixel type- Parameters:
arrayId- Globally-unique tensor id (source_idorsource_id/field)sliceHint- Optional slice hint- Returns:
- RandomAccessibleInterval containing the requested tensor
-
getTensor
public <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> net.imglib2.RandomAccessibleInterval<T> getTensor(String arrayId, long[] scaleHint, String reductionMethod) Get a RandomAccessibleInterval for a tensor (by array_id) with scaled reads.- Type Parameters:
T- The pixel type- Parameters:
arrayId- Globally-unique tensor id (source_idorsource_id/field)scaleHint- Per-dimension scale factorsreductionMethod- Requested reduction method- Returns:
- RandomAccessibleInterval containing the requested tensor
-
getTensor
public <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> net.imglib2.RandomAccessibleInterval<T> getTensor(String arrayId, SliceHint sliceHint, long[] scaleHint, String reductionMethod) Get a RandomAccessibleInterval for a tensor (by array_id) with all options.- Type Parameters:
T- The pixel type- Parameters:
arrayId- Globally-unique tensor id (source_idorsource_id/field)sliceHint- Optional slice hintscaleHint- Per-dimension scale factorsreductionMethod- Requested reduction method- Returns:
- lazy RandomAccessibleInterval containing the requested tensor
-
getTensorAsPb
Get a SerializedTensor protobuf for a whole tensor.- Parameters:
arrayId- Globally-unique tensor id (source_idorsource_id/field)- Returns:
- SerializedTensor protobuf object
-
getTensorAsPb
public SerializedTensor getTensorAsPb(String arrayId, SliceHint sliceHint, long[] scaleHint, String reductionMethod) Get a SerializedTensor protobuf for cross-process transfer. Returns a protobuf containing connection info and chunk tickets for lazy reconstruction. The protobuf can be serialized to bytes and broadcast to worker processes (e.g., Spark), where each worker can call tensorFromPb() to reconstruct a lazy imglib2 array.- Parameters:
arrayId- Globally-unique tensor id (source_idorsource_id/field)sliceHint- Optional slice hintscaleHint- Per-dimension scale factorsreductionMethod- Requested reduction method- Returns:
- SerializedTensor protobuf object
-
flightInfoOf
The plan a SerializedTensor carries: its serialized Arrow FlightInfo. -
descriptorOf
The resolved descriptor a SerializedTensor's plan names. -
tensorFromPb
public static <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> net.imglib2.RandomAccessibleInterval<T> tensorFromPb(SerializedTensor pb, long cacheBytes) The lazy imglib2 array a SerializedTensor describes. The one consumer-side helper. The handle is a FlightInfo plus where and as whom to read it. Its plan is consumed directly on first access; only the documented endpoint-less progressive-discovery plan is refreshed.- Type Parameters:
T- The pixel type- Parameters:
pb- SerializedTensor protobuf objectcacheBytes- Maximum cache size in bytes- Returns:
- RandomAccessibleInterval with lazy chunk loading
-
close
public void close()- Specified by:
closein interfaceAutoCloseable
-
healthCheck
Check server health status via Flight action. Returns a map with health status information including: - status: "SERVING" or other status string - source_count: number of registered sources - metadata_db_enabled: whether the server offers a catalog (false means it serves sources by id alone and every catalog surface refuses) - writable: whether server accepts uploads - uptime_seconds: server uptime in seconds- Returns:
- Map containing health status
- Throws:
IOException- If action fails
-
getUploadStatus
Get upload status for a writable tensor.- Parameters:
arrayId- the array_id setupArrayUpload() returned- Returns:
- Map containing source_id (the array_id passed in -- the key name mirrors the server's own status map shape), state, expected_chunks, uploaded_chunks and reason
- Throws:
IOException- If the action fails
-
setupArrayUpload
public TensorDescriptor setupArrayUpload(String arrayId, long[] shape, String dtype, long[] chunkShape, List<String> dimLabels, String omeMetadataJson) Declare a tensor to fill: the first half of an upload.Experimental. The upload API (tensor creation, chunk upload, and upload-status polling) may change.
An upload adds a tensor to a source that already exists and never creates one. A result that belongs to no source of yours goes on the server's scratch source, at the fixed id
"scratch"-- every writable server serves one, so there is nothing to ask for first. Declare, then fill: the returned descriptor is the server's echo -- array_id, shape, dtype, chunk_shape, dim_labels -- and is whatuploadArray(biopb.tensor.TensorDescriptor, net.imglib2.RandomAccessibleInterval<T>),uploadChunk(biopb.tensor.TensorDescriptor, biopb.tensor.ChunkBounds, net.imglib2.RandomAccessibleInterval<T>)andsetUploadStatus(java.lang.String, biopb.tensor.UploadStatus.State, java.lang.String)take.setUploadStatus(java.lang.String, biopb.tensor.UploadStatus.State, java.lang.String)is what publishes the tensor and marks it complete.A field is taken while its tensor is served: a second add under it -- pending, published or discarded -- is refused. Only the server's reclaim sweep frees one, after a discarded upload's
upload_ttl.- Parameters:
arrayId-"<scheme>://<source_id>/@fields/<name>", where scheme is the store format --zarrfor an OME-Zarr image group,cachefor the chunks as uploaded -- and source_id is a source the server already serves -- one it discovered, or"scratch". The@fieldssegment is not optional: an uploaded tensor keeps its own store beside its source, and the mark is what stops its id colliding with one of the file's own tensors, so a bare"<source_id>/<field>"is a native tensor id and is refused here. The one other form is"zarr://<image array_id>/@labels/<name>", a label set of an image the server already serves. A set is unsigned-integer, spans its image's non-channel axes at full length, and its all-zero chunks are skipped byuploadArray(biopb.tensor.TensorDescriptor, net.imglib2.RandomAccessibleInterval<T>). The scheme names the store format and nothing else: the answered id carries noneshape- the tensor's shapedtype- the numpy dtype string to store it as (e.g."<u2")chunkShape- the upload grid; null or empty means one chunk. A request, not a promise: the server plans on its own grid and answers with itdimLabels- optional dimension labelsomeMetadataJson- ignored except for a label set'simage-labelblock. Metadata is source-scoped: a tensor inherits its source's, and the scratch source has none- Returns:
- the new tensor's descriptor, under the id it keeps
-
setupArrayUpload
public TensorDescriptor setupArrayUpload(String arrayId, long[] shape, String dtype, long[] chunkShape, List<String> dimLabels, String omeMetadataJson, Integer ttlSeconds) setupArrayUpload(String, long[], String, long[], List, String)with a lifetime.How long the result is worth keeping is the producer's to say, and
nullasks for no deadline. A source may cap it -- a scratch source caps every upload on it, an unset request included -- so the answer's ownttl_secondsis what was granted, which may be shorter. Past it the tensor is discarded as if you had discarded it: the store goes and the id readsDISCARDED.- Parameters:
ttlSeconds- seconds to keep the tensor, or null for no deadline; must be positive, since zero is not a lifetime- Returns:
- the new tensor's descriptor, its
ttl_secondsthe lifetime granted
-
setupArrayUpload
public <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> TensorDescriptor setupArrayUpload(String arrayId, net.imglib2.RandomAccessibleInterval<T> template, long[] chunkShape, List<String> dimLabels, String omeMetadataJson) Declare a tensor shaped like an array you already hold.Experimental, with the rest of the upload API. The template's shape and pixel type stand in for the explicit
shape/dtypeofsetupArrayUpload(String, long[], String, long[], List, String); it is the array about to be uploaded, or one shaped like it.- Type Parameters:
T- the pixel type- Parameters:
arrayId- as insetupArrayUpload(String, long[], String, long[], List, String)template- the array to be uploaded, or one shaped like itchunkShape- the upload grid; null or empty means one chunkdimLabels- optional dimension labelsomeMetadataJson- as insetupArrayUpload(String, long[], String, long[], List, String)- Returns:
- the new tensor's descriptor
-
uploadArray
public <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> Map<String,Object> uploadArray(TensorDescriptor descriptor, net.imglib2.RandomAccessibleInterval<T> array) Fill a declared tensor with an array, and seal it.Experimental, with the rest of the upload API.
arraymust match the descriptor's shape; it is walked on the descriptor's chunk grid, every block is sent as one chunk, and the source is finished. An all-zero block of a label set is not sent at all: the store's fill value already reads as background, so one labelled frame of a thousand costs one frame (biopb/biopb#1059).- Type Parameters:
T- the pixel type- Parameters:
descriptor- the descriptorsetupArrayUpload(java.lang.String, long[], java.lang.String, long[], java.util.List<java.lang.String>, java.lang.String)returnedarray- the array to upload- Returns:
- the sealed upload status, as
getUploadStatus(java.lang.String)reports it - Throws:
IllegalArgumentException- ifarraydoes not match the declared shapeUploadRefusedException- if the upload is over -- sealed or discarded
-
uploadChunk
public <T extends net.imglib2.type.NativeType<T> & net.imglib2.type.numeric.RealType<T>> void uploadChunk(TensorDescriptor descriptor, ChunkBounds bounds, net.imglib2.RandomAccessibleInterval<T> source) Upload one chunk of a declared tensor.Experimental, with the rest of the upload API.
The manual half of
uploadArray(biopb.tensor.TensorDescriptor, net.imglib2.RandomAccessibleInterval<T>): a caller writing chunks itself calls this per chunk andsetUploadStatus(java.lang.String, biopb.tensor.UploadStatus.State, java.lang.String)when done. The chunk's elements are read out ofsourceatbounds-- in that interval's own global coordinates, so the whole array can be passed for every chunk.- Type Parameters:
T- the pixel type- Parameters:
descriptor- the descriptorsetupArrayUpload(java.lang.String, long[], java.lang.String, long[], java.util.List<java.lang.String>, java.lang.String)returnedbounds- chunk start/stop coordinatessource- the array to read the chunk out of- Throws:
UploadRefusedException- if the upload is over -- sealed or discarded
-
setUploadStatus
Move an upload along its lifecycle; the only thing that moves one.Experimental, with the rest of the upload API.
The states form a ladder, and a call climbs it or stands still:
READY-- publish and seal. The source becomes readable, a chunk that was never uploaded reads back as zeros, and no further chunk is accepted, so what is there is final. This is the state a consumer waiting on a result polls for, and whatuploadArray(biopb.tensor.TensorDescriptor, net.imglib2.RandomAccessibleInterval<T>)sets for you.DISCARDED-- give up, from any of the above. Whatever the server minted goes with it: azarr://member's store, a label set's sidecar, and the tensor's place in its source's listing. This is how an uploaded tensor is deleted; the field frees after the server's reclaim sweep, like any other discarded upload's.
Setting the state the upload is already in is a no-op; moving back down the ladder is refused.
- Parameters:
arrayId- whatsetupArrayUpload(java.lang.String, long[], java.lang.String, long[], java.util.List<java.lang.String>, java.lang.String)answered with, or a label set's array_id asgetLabelSets(java.lang.String)reports itstate-READYorDISCARDEDreason- why, forDISCARDED; it is what a poller waiting on this result reads back, so write it for them- Returns:
- the resulting upload status, as
getUploadStatus(java.lang.String)reports it.DISCARDEDis total -- an id tracking no upload answersUNKNOWNrather than throwing - Throws:
UploadRefusedException- if the upload was discarded, so it cannot be moved
-