github.com/janelia-flyem/dvid@v1.0.0/datatype/labelarray/read.go (about)

     1  package labelarray
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"log"
     7  	"sync"
     8  
     9  	"github.com/janelia-flyem/dvid/datastore"
    10  	"github.com/janelia-flyem/dvid/datatype/common/labels"
    11  	"github.com/janelia-flyem/dvid/datatype/imageblk"
    12  	"github.com/janelia-flyem/dvid/dvid"
    13  	"github.com/janelia-flyem/dvid/server"
    14  	"github.com/janelia-flyem/dvid/storage"
    15  )
    16  
    17  // ComputeTransform determines the block coordinate and beginning + ending voxel points
    18  // for the data corresponding to the given Block.
    19  func (v *Labels) ComputeTransform(tkey storage.TKey, blockSize dvid.Point) (blockBeg, dataBeg, dataEnd dvid.Point, err error) {
    20  	var ptIndex *dvid.IndexZYX
    21  	_, ptIndex, err = DecodeBlockTKey(tkey)
    22  	if err != nil {
    23  		return
    24  	}
    25  
    26  	// Get the bounding voxel coordinates for this block.
    27  	minBlockVoxel := ptIndex.MinPoint(blockSize)
    28  	maxBlockVoxel := ptIndex.MaxPoint(blockSize)
    29  
    30  	// Compute the boundary voxel coordinates for the ExtData and adjust
    31  	// to our block bounds.
    32  	minDataVoxel := v.StartPoint()
    33  	maxDataVoxel := v.EndPoint()
    34  	begVolCoord, _ := minDataVoxel.Max(minBlockVoxel)
    35  	endVolCoord, _ := maxDataVoxel.Min(maxBlockVoxel)
    36  
    37  	// Adjust the DVID volume voxel coordinates for the data so that (0,0,0)
    38  	// is where we expect this slice/subvolume's data to begin.
    39  	dataBeg = begVolCoord.Sub(v.StartPoint())
    40  	dataEnd = endVolCoord.Sub(v.StartPoint())
    41  
    42  	// Compute block coord matching dataBeg
    43  	blockBeg = begVolCoord.Sub(minBlockVoxel)
    44  
    45  	return
    46  }
    47  
    48  // GetImage retrieves a 2d image from a version node given a geometry of labels.
    49  func (d *Data) GetImage(v dvid.VersionID, vox *Labels, scale uint8, roiname dvid.InstanceName) (*dvid.Image, error) {
    50  	r, err := imageblk.GetROI(v, roiname, vox)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	if err := d.GetLabels(v, scale, vox, r); err != nil {
    55  		return nil, err
    56  	}
    57  	return vox.GetImage2d()
    58  }
    59  
    60  // GetVolume retrieves a n-d volume from a version node given a geometry of labels.
    61  func (d *Data) GetVolume(v dvid.VersionID, vox *Labels, scale uint8, roiname dvid.InstanceName) ([]byte, error) {
    62  	r, err := imageblk.GetROI(v, roiname, vox)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	if err := d.GetLabels(v, scale, vox, r); err != nil {
    67  		return nil, err
    68  	}
    69  	return vox.Data(), nil
    70  }
    71  
    72  type getOperation struct {
    73  	voxels      *Labels
    74  	blocksInROI map[string]bool
    75  	mapping     *labels.Mapping
    76  }
    77  
    78  // GetLabels copies labels from the storage engine to Labels, a requested subvolume or 2d image.
    79  func (d *Data) GetLabels(v dvid.VersionID, scale uint8, vox *Labels, r *imageblk.ROI) error {
    80  	store, err := datastore.GetOrderedKeyValueDB(d)
    81  	if err != nil {
    82  		return fmt.Errorf("Data type imageblk had error initializing store: %v\n", err)
    83  	}
    84  	if r != nil && scale != 0 {
    85  		return fmt.Errorf("DVID does not currently support ROI masks on lower-scale GETs")
    86  	}
    87  
    88  	// Only do one request at a time, although each request can start many goroutines.
    89  	if vox.NumVoxels() > 256*256*256 {
    90  		server.LargeMutationMutex.Lock()
    91  		defer server.LargeMutationMutex.Unlock()
    92  	}
    93  
    94  	ctx := datastore.NewVersionedCtx(d, v)
    95  
    96  	iv := dvid.InstanceVersion{d.DataUUID(), v}
    97  	mapping := labels.LabelMap(iv)
    98  
    99  	wg := new(sync.WaitGroup)
   100  
   101  	okv := store.(storage.BufferableOps)
   102  	// extract buffer interface
   103  	req, hasbuffer := okv.(storage.KeyValueRequester)
   104  	if hasbuffer {
   105  		okv = req.NewBuffer(ctx)
   106  	}
   107  
   108  	for it, err := vox.NewIndexIterator(d.BlockSize()); err == nil && it.Valid(); it.NextSpan() {
   109  		indexBeg, indexEnd, err := it.IndexSpan()
   110  		if err != nil {
   111  			return err
   112  		}
   113  		begTKey := NewBlockTKey(scale, indexBeg)
   114  		endTKey := NewBlockTKey(scale, indexEnd)
   115  
   116  		// Get set of blocks in ROI if ROI provided
   117  		var chunkOp *storage.ChunkOp
   118  		if r != nil && r.Iter != nil {
   119  			ptBeg := indexBeg.Duplicate().(dvid.ChunkIndexer)
   120  			ptEnd := indexEnd.Duplicate().(dvid.ChunkIndexer)
   121  			begX := ptBeg.Value(0)
   122  			endX := ptEnd.Value(0)
   123  
   124  			blocksInROI := make(map[string]bool, (endX - begX + 1))
   125  			c := dvid.ChunkPoint3d{begX, ptBeg.Value(1), ptBeg.Value(2)}
   126  			for x := begX; x <= endX; x++ {
   127  				c[0] = x
   128  				curIndex := dvid.IndexZYX(c)
   129  				if r.Iter.InsideFast(curIndex) {
   130  					indexString := string(curIndex.Bytes())
   131  					blocksInROI[indexString] = true
   132  				}
   133  			}
   134  			chunkOp = &storage.ChunkOp{&getOperation{vox, blocksInROI, mapping}, wg}
   135  		} else {
   136  			chunkOp = &storage.ChunkOp{&getOperation{vox, nil, mapping}, wg}
   137  		}
   138  
   139  		if !hasbuffer {
   140  			// Send the entire range of key-value pairs to chunk processor
   141  			err = okv.ProcessRange(ctx, begTKey, endTKey, chunkOp, storage.ChunkFunc(d.ReadChunk))
   142  			if err != nil {
   143  				return fmt.Errorf("Unable to GET data %s: %v", ctx, err)
   144  			}
   145  		} else {
   146  			// Extract block list
   147  			var tkeys []storage.TKey
   148  			ptBeg := indexBeg.Duplicate().(dvid.ChunkIndexer)
   149  			ptEnd := indexEnd.Duplicate().(dvid.ChunkIndexer)
   150  			begX := ptBeg.Value(0)
   151  			endX := ptEnd.Value(0)
   152  
   153  			c := dvid.ChunkPoint3d{begX, ptBeg.Value(1), ptBeg.Value(2)}
   154  			for x := begX; x <= endX; x++ {
   155  				c[0] = x
   156  				tk := NewBlockTKeyByCoord(scale, c.ToIZYXString())
   157  				tkeys = append(tkeys, tk)
   158  			}
   159  
   160  			err = okv.(storage.RequestBuffer).ProcessList(ctx, tkeys, chunkOp, storage.ChunkFunc(d.ReadChunk))
   161  			if err != nil {
   162  				return fmt.Errorf("Unable to GET data %s: %v", ctx, err)
   163  			}
   164  		}
   165  	}
   166  
   167  	if hasbuffer {
   168  		// submit the entire buffer to the DB
   169  		err = okv.(storage.RequestBuffer).Flush()
   170  
   171  		if err != nil {
   172  			return fmt.Errorf("Unable to GET data %s: %v", ctx, err)
   173  		}
   174  	}
   175  
   176  	if err != nil {
   177  		return err
   178  	}
   179  	wg.Wait()
   180  	return nil
   181  }
   182  
   183  // ReadChunk reads a chunk of data as part of a mapped operation.
   184  // Only some multiple of the # of CPU cores can be used for chunk handling before
   185  // it waits for chunk processing to abate via the buffered server.HandlerToken channel.
   186  func (d *Data) ReadChunk(chunk *storage.Chunk) error {
   187  	server.CheckChunkThrottling()
   188  	go d.readChunk(chunk)
   189  	return nil
   190  }
   191  
   192  func (d *Data) readChunk(chunk *storage.Chunk) {
   193  	defer func() {
   194  		// After processing a chunk, return the token.
   195  		server.HandlerToken <- 1
   196  
   197  		// Notify the requestor that this chunk is done.
   198  		if chunk.Wg != nil {
   199  			chunk.Wg.Done()
   200  		}
   201  	}()
   202  
   203  	op, ok := chunk.Op.(*getOperation)
   204  	if !ok {
   205  		log.Fatalf("Illegal operation passed to readChunk() for data %s\n", d.DataName())
   206  	}
   207  
   208  	// Make sure our received chunk is valid.
   209  	if chunk == nil {
   210  		dvid.Errorf("Received nil chunk in readChunk.  Ignoring chunk.\n")
   211  		return
   212  	}
   213  	if chunk.K == nil {
   214  		dvid.Errorf("Received nil chunk key in readChunk.  Ignoring chunk.\n")
   215  		return
   216  	}
   217  
   218  	// If there's an ROI, if outside ROI, use blank buffer.
   219  	var zeroOut bool
   220  	_, indexZYX, err := DecodeBlockTKey(chunk.K)
   221  	if err != nil {
   222  		dvid.Errorf("Error processing voxel block: %s\n", err)
   223  		return
   224  	}
   225  	if op.blocksInROI != nil {
   226  		indexString := string(indexZYX.Bytes())
   227  		_, insideROI := op.blocksInROI[indexString]
   228  		if !insideROI {
   229  			zeroOut = true
   230  		}
   231  	}
   232  
   233  	// Initialize the block buffer using the chunk of data.  For voxels, this chunk of
   234  	// data needs to be uncompressed and deserialized.
   235  	var block labels.Block
   236  	if zeroOut || chunk.V == nil {
   237  		blockSize, ok := d.BlockSize().(dvid.Point3d)
   238  		if !ok {
   239  			dvid.Errorf("Block size for data %q is not 3d: %s\n", d.DataName(), d.BlockSize())
   240  			return
   241  		}
   242  		block = *labels.MakeSolidBlock(0, blockSize)
   243  	} else {
   244  		var data []byte
   245  		data, _, err = dvid.DeserializeData(chunk.V, true)
   246  		if err != nil {
   247  			dvid.Errorf("Unable to deserialize block in %q: %v\n", d.DataName(), err)
   248  			return
   249  		}
   250  		if err := block.UnmarshalBinary(data); err != nil {
   251  			dvid.Errorf("Unable to unmarshal labels Block compression in %q: %v\n", d.DataName(), err)
   252  			return
   253  		}
   254  	}
   255  
   256  	// Perform the operation.
   257  	if op.mapping != nil {
   258  		err := op.voxels.readMappedBlock(chunk.K, block, d.BlockSize(), op.mapping)
   259  		if err != nil {
   260  			dvid.Errorf("readMappedBlock, key %v in %q: %v\n", chunk.K, d.DataName(), err)
   261  		}
   262  	} else {
   263  		err := op.voxels.readBlock(chunk.K, block, d.BlockSize())
   264  		if err != nil {
   265  			dvid.Errorf("readBlock, key %v in %q: %v\n", chunk.K, d.DataName(), err)
   266  		}
   267  	}
   268  }
   269  
   270  func (v *Labels) readMappedBlock(tkey storage.TKey, block labels.Block, blockSize dvid.Point, m *labels.Mapping) error {
   271  	if blockSize.NumDims() > 3 {
   272  		return fmt.Errorf("DVID voxel blocks currently only supports up to 3d, not 4+ dimensions")
   273  	}
   274  	blockBeg, dataBeg, dataEnd, err := v.ComputeTransform(tkey, blockSize)
   275  	if err != nil {
   276  		return err
   277  	}
   278  
   279  	// TODO -- Refactor this function to make direct use of compressed label Block without
   280  	// conversion into full label array.
   281  	labels := v.Data()
   282  	blabels, _ := block.MakeLabelVolume()
   283  
   284  	// Compute the strides (in bytes)
   285  	bX := int64(blockSize.Value(0)) * 8
   286  	bY := int64(blockSize.Value(1)) * bX
   287  	dX := int64(v.Stride())
   288  
   289  	blockBegX := int64(blockBeg.Value(0))
   290  	blockBegY := int64(blockBeg.Value(1))
   291  	blockBegZ := int64(blockBeg.Value(2))
   292  
   293  	// Do the transfers depending on shape of the external voxels.
   294  	switch {
   295  	case v.DataShape().Equals(dvid.XY):
   296  		dataI := int64(dataBeg.Value(1))*dX + int64(dataBeg.Value(0))*8
   297  		blockI := blockBegZ*bY + blockBegY*bX + blockBegX*8
   298  		for y := int64(dataBeg.Value(1)); y <= int64(dataEnd.Value(1)); y++ {
   299  			bI := blockI
   300  			dI := dataI
   301  			for x := dataBeg.Value(0); x <= dataEnd.Value(0); x++ {
   302  				orig := binary.LittleEndian.Uint64(blabels[bI : bI+8])
   303  				mapped, found := m.FinalLabel(orig)
   304  				if found {
   305  					binary.LittleEndian.PutUint64(labels[dI:dI+8], mapped)
   306  				} else {
   307  					copy(labels[dI:dI+8], blabels[bI:bI+8])
   308  				}
   309  				bI += 8
   310  				dI += 8
   311  			}
   312  			blockI += bX
   313  			dataI += dX
   314  		}
   315  
   316  	case v.DataShape().Equals(dvid.XZ):
   317  		dataI := int64(dataBeg.Value(2))*dX + int64(dataBeg.Value(0))*8
   318  		blockI := blockBegZ*bY + blockBegY*bX + blockBegX*8
   319  		for y := int64(dataBeg.Value(2)); y <= int64(dataEnd.Value(2)); y++ {
   320  			bI := blockI
   321  			dI := dataI
   322  			for x := dataBeg.Value(0); x <= dataEnd.Value(0); x++ {
   323  				orig := binary.LittleEndian.Uint64(blabels[bI : bI+8])
   324  				mapped, found := m.FinalLabel(orig)
   325  				if found {
   326  					binary.LittleEndian.PutUint64(labels[dI:dI+8], mapped)
   327  				} else {
   328  					copy(labels[dI:dI+8], blabels[bI:bI+8])
   329  				}
   330  				bI += 8
   331  				dI += 8
   332  			}
   333  			blockI += bY
   334  			dataI += dX
   335  		}
   336  
   337  	case v.DataShape().Equals(dvid.YZ):
   338  		bz := blockBegZ
   339  		for y := int64(dataBeg.Value(2)); y <= int64(dataEnd.Value(2)); y++ {
   340  			dataI := y*dX + int64(dataBeg.Value(1))*8
   341  			blockI := bz*bY + blockBegY*bX + blockBegX*8
   342  			for x := dataBeg.Value(1); x <= dataEnd.Value(1); x++ {
   343  				orig := binary.LittleEndian.Uint64(blabels[blockI : blockI+8])
   344  				mapped, found := m.FinalLabel(orig)
   345  				if found {
   346  					binary.LittleEndian.PutUint64(labels[dataI:dataI+8], mapped)
   347  				} else {
   348  					copy(labels[dataI:dataI+8], blabels[blockI:blockI+8])
   349  				}
   350  				blockI += bX
   351  				dataI += 8
   352  			}
   353  			bz++
   354  		}
   355  
   356  	case v.DataShape().ShapeDimensions() == 2:
   357  		// TODO: General code for handling 2d ExtData in n-d space.
   358  		return fmt.Errorf("DVID currently does not support 2d in n-d space.")
   359  
   360  	case v.DataShape().Equals(dvid.Vol3d):
   361  		blockOffset := blockBegX * 8
   362  		dX := int64(v.Size().Value(0)) * 8
   363  		dY := int64(v.Size().Value(1)) * dX
   364  		dataOffset := int64(dataBeg.Value(0)) * 8
   365  		blockZ := blockBegZ
   366  
   367  		for dataZ := int64(dataBeg.Value(2)); dataZ <= int64(dataEnd.Value(2)); dataZ++ {
   368  			blockY := blockBegY
   369  			for dataY := int64(dataBeg.Value(1)); dataY <= int64(dataEnd.Value(1)); dataY++ {
   370  				bI := blockZ*bY + blockY*bX + blockOffset
   371  				dI := dataZ*dY + dataY*dX + dataOffset
   372  				for x := dataBeg.Value(0); x <= dataEnd.Value(0); x++ {
   373  					orig := binary.LittleEndian.Uint64(blabels[bI : bI+8])
   374  					mapped, found := m.FinalLabel(orig)
   375  					if found {
   376  						binary.LittleEndian.PutUint64(labels[dI:dI+8], mapped)
   377  					} else {
   378  						copy(labels[dI:dI+8], blabels[bI:bI+8])
   379  					}
   380  					bI += 8
   381  					dI += 8
   382  				}
   383  				blockY++
   384  			}
   385  			blockZ++
   386  		}
   387  
   388  	default:
   389  		return fmt.Errorf("Cannot readBlock() unsupported voxels data shape %s", v.DataShape())
   390  	}
   391  	return nil
   392  }
   393  
   394  func (v *Labels) readBlock(tkey storage.TKey, block labels.Block, blockSize dvid.Point) error {
   395  	if blockSize.NumDims() > 3 {
   396  		return fmt.Errorf("DVID voxel blocks currently only supports up to 3d, not 4+ dimensions")
   397  	}
   398  	blockBeg, dataBeg, dataEnd, err := v.ComputeTransform(tkey, blockSize)
   399  	if err != nil {
   400  		return err
   401  	}
   402  
   403  	// TODO -- Refactor this function to make direct use of compressed label Block without
   404  	// conversion into full label array.
   405  	labels := v.Data()
   406  	blabels, _ := block.MakeLabelVolume()
   407  
   408  	// Compute the strides (in bytes)
   409  	bX := int64(blockSize.Value(0)) * 8
   410  	bY := int64(blockSize.Value(1)) * bX
   411  	dX := int64(v.Stride())
   412  
   413  	blockBegX := int64(blockBeg.Value(0))
   414  	blockBegY := int64(blockBeg.Value(1))
   415  	blockBegZ := int64(blockBeg.Value(2))
   416  
   417  	// Do the transfers depending on shape of the external voxels.
   418  	switch {
   419  	case v.DataShape().Equals(dvid.XY):
   420  		dataI := int64(dataBeg.Value(1))*dX + int64(dataBeg.Value(0))*8
   421  		blockI := blockBegZ*bY + blockBegY*bX + blockBegX*8
   422  		bytes := int64(dataEnd.Value(0)-dataBeg.Value(0)+1) * 8
   423  		for y := dataBeg.Value(1); y <= dataEnd.Value(1); y++ {
   424  			copy(labels[dataI:dataI+bytes], blabels[blockI:blockI+bytes])
   425  			blockI += bX
   426  			dataI += dX
   427  		}
   428  
   429  	case v.DataShape().Equals(dvid.XZ):
   430  		dataI := int64(dataBeg.Value(2))*dX + int64(dataBeg.Value(0))*8
   431  		blockI := blockBegZ*bY + blockBegY*bX + blockBegX*8
   432  		bytes := int64(dataEnd.Value(0)-dataBeg.Value(0)+1) * 8
   433  		for y := dataBeg.Value(2); y <= dataEnd.Value(2); y++ {
   434  			copy(labels[dataI:dataI+bytes], blabels[blockI:blockI+bytes])
   435  			blockI += bY
   436  			dataI += dX
   437  		}
   438  
   439  	case v.DataShape().Equals(dvid.YZ):
   440  		bz := blockBegZ
   441  		for y := int64(dataBeg.Value(2)); y <= int64(dataEnd.Value(2)); y++ {
   442  			dataI := y*dX + int64(dataBeg.Value(1))*8
   443  			blockI := bz*bY + blockBegY*bX + blockBegX*8
   444  			for x := dataBeg.Value(1); x <= dataEnd.Value(1); x++ {
   445  				copy(labels[dataI:dataI+8], blabels[blockI:blockI+8])
   446  				blockI += bX
   447  				dataI += 8
   448  			}
   449  			bz++
   450  		}
   451  
   452  	case v.DataShape().ShapeDimensions() == 2:
   453  		// TODO: General code for handling 2d ExtData in n-d space.
   454  		return fmt.Errorf("DVID currently does not support 2d in n-d space.")
   455  
   456  	case v.DataShape().Equals(dvid.Vol3d):
   457  		blockOffset := blockBegX * 8
   458  		dX := int64(v.Size().Value(0)) * 8
   459  		dY := int64(v.Size().Value(1)) * dX
   460  		dataOffset := int64(dataBeg.Value(0)) * 8
   461  		bytes := int64(dataEnd.Value(0)-dataBeg.Value(0)+1) * 8
   462  		blockZ := blockBegZ
   463  
   464  		for dataZ := int64(dataBeg.Value(2)); dataZ <= int64(dataEnd.Value(2)); dataZ++ {
   465  			blockY := blockBegY
   466  			for dataY := int64(dataBeg.Value(1)); dataY <= int64(dataEnd.Value(1)); dataY++ {
   467  				blockI := blockZ*bY + blockY*bX + blockOffset
   468  				dataI := dataZ*dY + dataY*dX + dataOffset
   469  				copy(labels[dataI:dataI+bytes], blabels[blockI:blockI+bytes])
   470  				blockY++
   471  			}
   472  			blockZ++
   473  		}
   474  
   475  	default:
   476  		return fmt.Errorf("Cannot readBlock() unsupported voxels data shape %s", v.DataShape())
   477  	}
   478  	return nil
   479  }