// ComputeTransform determines the block coordinate and beginning + ending voxel points // for the data corresponding to the given Block. func (v *Voxels) ComputeTransform(block *storage.TKeyValue, blockSize dvid.Point) (blockBeg, dataBeg, dataEnd dvid.Point, err error) { var ptIndex *dvid.IndexZYX ptIndex, err = DecodeTKey(block.K) if err != nil { return } // Get the bounding voxel coordinates for this block. minBlockVoxel := ptIndex.MinPoint(blockSize) maxBlockVoxel := ptIndex.MaxPoint(blockSize) // Compute the boundary voxel coordinates for the ExtData and adjust // to our block bounds. minDataVoxel := v.StartPoint() maxDataVoxel := v.EndPoint() begVolCoord, _ := minDataVoxel.Max(minBlockVoxel) endVolCoord, _ := maxDataVoxel.Min(maxBlockVoxel) // Adjust the DVID volume voxel coordinates for the data so that (0,0,0) // is where we expect this slice/subvolume's data to begin. dataBeg = begVolCoord.Sub(v.StartPoint()) dataEnd = endVolCoord.Sub(v.StartPoint()) // Compute block coord matching dataBeg blockBeg = begVolCoord.Sub(minBlockVoxel) return }
// DecodeTKey returns a spatial index from a image block key. // TODO: Extend this when necessary to allow any form of spatial indexing like CZYX. func DecodeTKey(tk storage.TKey) (*dvid.IndexZYX, error) { ibytes, err := tk.ClassBytes(keyImageBlock) if err != nil { return nil, err } var zyx dvid.IndexZYX if err = zyx.IndexFromBytes(ibytes); err != nil { return nil, fmt.Errorf("Cannot recover ZYX index from image block key %v: %v\n", tk, err) } return &zyx, nil }
func DecodeBlockTKey(tk storage.TKey) (pt dvid.ChunkPoint3d, err error) { ibytes, err := tk.ClassBytes(keyBlock) if err != nil { return } var idx dvid.IndexZYX if err = idx.IndexFromBytes(ibytes); err != nil { return } pt = dvid.ChunkPoint3d(idx) return }
// DecodeTKey returns the components of a tile request based on an imagetile-specific key component. func DecodeTKey(tk storage.TKey) (tile dvid.ChunkPoint3d, plane dvid.DataShape, scale Scaling, err error) { if len(tk) < 21 { err = fmt.Errorf("imagetile-specific key component has too few bytes (%d)", len(tk)) return } plane, err = dvid.BytesToDataShape(tk[0:dvid.DataShapeBytes]) if err != nil { return } scale = Scaling(tk[dvid.DataShapeBytes]) var idx dvid.IndexZYX if err = idx.IndexFromBytes(tk[dvid.DataShapeBytes+2:]); err != nil { return } tile = dvid.ChunkPoint3d(idx) return }
// SplitCoarseLabels splits a portion of a label's voxels into a given split label or, if the given split // label is 0, a new label, which is returned. The input is a binary sparse volume defined by block // coordinates and should be the smaller portion of a labeled region-to-be-split. // // EVENTS // // labels.SplitStartEvent occurs at very start of split and transmits labels.DeltaSplitStart struct. // // labels.SplitBlockEvent occurs for every block of a split label and transmits labels.DeltaSplit struct. // // labels.SplitEndEvent occurs at end of split and transmits labels.DeltaSplitEnd struct. // func (d *Data) SplitCoarseLabels(v dvid.VersionID, fromLabel, splitLabel uint64, r io.ReadCloser) (toLabel uint64, err error) { store, err := d.GetOrderedKeyValueDB() if err != nil { err = fmt.Errorf("Data type labelvol had error initializing store: %v\n", err) return } batcher, ok := store.(storage.KeyValueBatcher) if !ok { err = fmt.Errorf("Data type labelvol requires batch-enabled store, which %q is not\n", store) return } // Create a new label id for this version that will persist to store if splitLabel != 0 { toLabel = splitLabel dvid.Debugf("Splitting coarse subset of label %d into given label %d ...\n", fromLabel, splitLabel) } else { toLabel, err = d.NewLabel(v) if err != nil { return } dvid.Debugf("Splitting coarse subset of label %d into new label %d ...\n", fromLabel, toLabel) } evt := datastore.SyncEvent{d.DataUUID(), labels.SplitStartEvent} splitOpStart := labels.DeltaSplitStart{fromLabel, toLabel} splitOpEnd := labels.DeltaSplitEnd{fromLabel, toLabel} // Make sure we can split given current merges in progress if err := labels.SplitStart(d.getMergeIV(v), splitOpStart); err != nil { return toLabel, err } defer labels.SplitStop(d.getMergeIV(v), splitOpEnd) // Signal that we are starting a split. msg := datastore.SyncMessage{labels.SplitStartEvent, v, splitOpStart} if err := datastore.NotifySubscribers(evt, msg); err != nil { return 0, err } // Read the sparse volume from reader. var splits dvid.RLEs splits, err = dvid.ReadRLEs(r) if err != nil { return } numBlocks, _ := splits.Stats() // Order the split blocks splitblks := make(dvid.IZYXSlice, numBlocks) n := 0 for _, rle := range splits { p := rle.StartPt() run := rle.Length() for i := int32(0); i < run; i++ { izyx := dvid.IndexZYX{p[0] + i, p[1], p[2]} splitblks[n] = izyx.ToIZYXString() n++ } } sort.Sort(splitblks) // Iterate through the split blocks, read the original block and change labels. // TODO: Modifications should be transactional since it's GET-PUT, therefore use // hash on block coord to direct it to block-specific goroutine; we serialize // requests to handle concurrency. ctx := datastore.NewVersionedCtx(d, v) batch := batcher.NewBatch(ctx) var toLabelSize uint64 for _, splitblk := range splitblks { // Get original block tk := NewTKey(fromLabel, splitblk) val, err := store.Get(ctx, tk) if err != nil { return toLabel, err } if val == nil { return toLabel, fmt.Errorf("Split block %s is not part of original label %d", splitblk, fromLabel) } var rles dvid.RLEs if err := rles.UnmarshalBinary(val); err != nil { return toLabel, fmt.Errorf("Unable to unmarshal RLE for original labels in block %s", splitblk) } numVoxels, _ := rles.Stats() toLabelSize += numVoxels // Delete the old block and save the sparse volume but under a new label. batch.Delete(tk) tk2 := NewTKey(toLabel, splitblk) batch.Put(tk2, val) } if err := batch.Commit(); err != nil { dvid.Errorf("Batch PUT during split of %q label %d: %v\n", d.DataName(), fromLabel, err) } // Publish split event evt = datastore.SyncEvent{d.DataUUID(), labels.SplitLabelEvent} msg = datastore.SyncMessage{labels.SplitLabelEvent, v, labels.DeltaSplit{fromLabel, toLabel, nil, splitblks}} if err := datastore.NotifySubscribers(evt, msg); err != nil { return 0, err } // Publish change in label sizes. delta := labels.DeltaNewSize{ Label: toLabel, Size: toLabelSize, } evt = datastore.SyncEvent{d.DataUUID(), labels.ChangeSizeEvent} msg = datastore.SyncMessage{labels.ChangeSizeEvent, v, delta} if err := datastore.NotifySubscribers(evt, msg); err != nil { return 0, err } delta2 := labels.DeltaModSize{ Label: fromLabel, SizeChange: int64(-toLabelSize), } evt = datastore.SyncEvent{d.DataUUID(), labels.ChangeSizeEvent} msg = datastore.SyncMessage{labels.ChangeSizeEvent, v, delta2} if err := datastore.NotifySubscribers(evt, msg); err != nil { return 0, err } // Publish split end evt = datastore.SyncEvent{d.DataUUID(), labels.SplitEndEvent} msg = datastore.SyncMessage{labels.SplitEndEvent, v, splitOpEnd} if err := datastore.NotifySubscribers(evt, msg); err != nil { return 0, err } dvid.Infof("Split %d voxels from label %d to label %d\n", toLabelSize, fromLabel, toLabel) return toLabel, nil }