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

     1  // Functions that support point operations across DVID datatypes.
     2  
     3  package labelmap
     4  
     5  import (
     6  	"encoding/binary"
     7  	"fmt"
     8  	"sort"
     9  	"sync"
    10  
    11  	"github.com/janelia-flyem/dvid/datatype/common/labels"
    12  	"github.com/janelia-flyem/dvid/dvid"
    13  )
    14  
    15  // GetLabelAtScaledPoint returns the 64-bit unsigned int label for a given point.
    16  func (d *Data) GetLabelAtScaledPoint(v dvid.VersionID, pt dvid.Point, scale uint8, supervoxels bool) (uint64, error) {
    17  	coord, ok := pt.(dvid.Chunkable)
    18  	if !ok {
    19  		return 0, fmt.Errorf("Can't determine block of point %s", pt)
    20  	}
    21  	blockSize := d.BlockSize()
    22  	bcoord := coord.Chunk(blockSize).(dvid.ChunkPoint3d)
    23  
    24  	labelData, err := d.getBlockLabels(v, bcoord, scale, supervoxels)
    25  	if err != nil {
    26  		return 0, err
    27  	}
    28  	if len(labelData) == 0 {
    29  		return 0, nil
    30  	}
    31  
    32  	// Retrieve the particular label within the block.
    33  	ptInBlock := coord.PointInChunk(blockSize)
    34  	nx := int64(blockSize.Value(0))
    35  	nxy := nx * int64(blockSize.Value(1))
    36  	i := (int64(ptInBlock.Value(0)) + int64(ptInBlock.Value(1))*nx + int64(ptInBlock.Value(2))*nxy) * 8
    37  
    38  	return binary.LittleEndian.Uint64(labelData[i : i+8]), nil
    39  }
    40  
    41  // The following functions implement an interface to synced data types like annotation.
    42  
    43  // GetLabelBytes returns a block of hi-res (body) labels (scale 0) in packed little-endian uint64 format
    44  func (d *Data) GetLabelBytes(v dvid.VersionID, bcoord dvid.ChunkPoint3d) ([]byte, error) {
    45  	return d.getBlockLabels(v, bcoord, 0, false)
    46  }
    47  
    48  // GetLabelAtPoint returns the 64-bit unsigned int label for a given point.
    49  func (d *Data) GetLabelAtPoint(v dvid.VersionID, pt dvid.Point) (uint64, error) {
    50  	return d.GetLabelAtScaledPoint(v, pt, 0, false)
    51  }
    52  
    53  // GetSupervoxelAtPoint returns the 64-bit unsigned int supervoxel id for a given point.
    54  func (d *Data) GetSupervoxelAtPoint(v dvid.VersionID, pt dvid.Point) (uint64, error) {
    55  	return d.GetLabelAtScaledPoint(v, pt, 0, true)
    56  }
    57  
    58  type ptsIndex struct {
    59  	pts     []dvid.Point3d
    60  	indices []int
    61  }
    62  
    63  type blockPtsI struct {
    64  	*labels.PositionedBlock
    65  	ptsIndex
    66  }
    67  
    68  type blockPtsSlice []blockPtsI
    69  
    70  // --- Functions for sort.Sort interface on blockPtsSlice
    71  
    72  func (b blockPtsSlice) Len() int {
    73  	return len(b)
    74  }
    75  
    76  func (b blockPtsSlice) Less(i, j int) bool {
    77  	return b[i].BCoord < b[j].BCoord
    78  }
    79  
    80  func (b blockPtsSlice) Swap(i, j int) {
    81  	b[i], b[j] = b[j], b[i]
    82  }
    83  
    84  // partitions points int blocks and returns a list of blocks with points, sorted
    85  // by block coordinate in ZYX order.
    86  func (d *Data) sortByBlockCoord(pts []dvid.Point3d) blockPtsSlice {
    87  	// iterate through points, adding to the corresponding blockPtsI if it exists
    88  	// or creating a new one.
    89  	indexStartSize := len(pts) / 10
    90  	if indexStartSize < 10 {
    91  		indexStartSize = 10
    92  	}
    93  	blockIndex := make(map[dvid.IZYXString]int, indexStartSize)
    94  	blockPts := make(blockPtsSlice, 0, indexStartSize)
    95  	blockSize := d.BlockSize().(dvid.Point3d)
    96  	for origPos, pt := range pts {
    97  		// Autogenerated.
    98  		x := pt[0] / blockSize[0]
    99  		y := pt[1] / blockSize[1]
   100  		z := pt[2] / blockSize[2]
   101  		bx := pt[0] % blockSize[0]
   102  		by := pt[1] % blockSize[1]
   103  		bz := pt[2] % blockSize[2]
   104  		bcoord := dvid.ChunkPoint3d{x, y, z}.ToIZYXString()
   105  		i, found := blockIndex[bcoord]
   106  		if found {
   107  			blockPts[i].pts = append(blockPts[i].pts, dvid.Point3d{bx, by, bz})
   108  			blockPts[i].indices = append(blockPts[i].indices, origPos)
   109  		} else {
   110  			i = len(blockPts)
   111  			blockIndex[bcoord] = i
   112  			blockPts = append(blockPts, blockPtsI{
   113  				PositionedBlock: &labels.PositionedBlock{
   114  					BCoord: bcoord,
   115  				},
   116  				ptsIndex: ptsIndex{
   117  					pts:     []dvid.Point3d{dvid.Point3d{bx, by, bz}},
   118  					indices: []int{origPos},
   119  				},
   120  			})
   121  		}
   122  	}
   123  	sort.Sort(blockPts)
   124  	return blockPts
   125  }
   126  
   127  func (d *Data) partitionPoints(pts []dvid.Point3d) map[dvid.IZYXString]ptsIndex {
   128  	blockSize := d.BlockSize().(dvid.Point3d)
   129  	blockPts := make(map[dvid.IZYXString]ptsIndex)
   130  	for i, pt := range pts {
   131  		x := pt[0] / blockSize[0]
   132  		y := pt[1] / blockSize[1]
   133  		z := pt[2] / blockSize[2]
   134  		bx := pt[0] % blockSize[0]
   135  		by := pt[1] % blockSize[1]
   136  		bz := pt[2] % blockSize[2]
   137  		bpt := dvid.Point3d{bx, by, bz}
   138  		bcoord := dvid.ChunkPoint3d{x, y, z}.ToIZYXString()
   139  		ptsi, found := blockPts[bcoord]
   140  		if found {
   141  			ptsi.pts = append(ptsi.pts, bpt)
   142  			ptsi.indices = append(ptsi.indices, i)
   143  		} else {
   144  			ptsi.pts = []dvid.Point3d{bpt}
   145  			ptsi.indices = []int{i}
   146  		}
   147  		blockPts[bcoord] = ptsi
   148  	}
   149  	return blockPts
   150  }
   151  
   152  func (d *Data) getFewLabelPoints(mapped []uint64, v dvid.VersionID, bptsSlice blockPtsSlice, mapping *VCache, mappedVersions distFromRoot, scale uint8) error {
   153  	for _, bptsI := range bptsSlice {
   154  		chunkPt3d, err := bptsI.BCoord.ToChunkPoint3d()
   155  		if err != nil {
   156  			return err
   157  		}
   158  		block, err := d.getSupervoxelBlock(v, chunkPt3d, scale)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		if len(mappedVersions) > 0 {
   163  			mapping.ApplyMappingToBlock(mappedVersions, block)
   164  		}
   165  		blockLabels := block.GetPointLabels(bptsI.pts)
   166  		for i, index := range bptsI.indices {
   167  			mapped[index] = blockLabels[i]
   168  		}
   169  	}
   170  	return nil
   171  }
   172  
   173  func (d *Data) getManyLabelPoints(mapped []uint64, v dvid.VersionID, bptsSlice blockPtsSlice, mapping *VCache, mappedVersions distFromRoot, scale uint8) (err error) {
   174  	timedLog := dvid.NewTimeLog()
   175  
   176  	// Launch goroutines to receive blocks and extract labels directly from compact data structure.
   177  	var processorWG sync.WaitGroup
   178  	processors := len(bptsSlice) / 10
   179  	if processors < 1 {
   180  		processors = 1
   181  	}
   182  	processCh := make(chan *blockPtsI, processors)
   183  	for c := 0; c < processors; c++ {
   184  		processorWG.Add(1)
   185  		go func() {
   186  			for bptsI := range processCh {
   187  				if len(mappedVersions) > 0 {
   188  					mapping.ApplyMappingToBlock(mappedVersions, &(bptsI.Block))
   189  				}
   190  				blockLabels := bptsI.Block.GetPointLabels(bptsI.pts)
   191  				for i, index := range bptsI.indices {
   192  					mapped[index] = blockLabels[i]
   193  				}
   194  			}
   195  			processorWG.Done()
   196  		}()
   197  	}
   198  
   199  	// Launch small number of goroutines to get blocks concurrently since tests suggest
   200  	// we don't saturate SSDs with up to 16 goroutines. (Depends on SSD load)
   201  	var readerWG sync.WaitGroup
   202  	numReaders := 16
   203  	readerCh := make(chan *blockPtsI, numReaders)
   204  	errorCh := make(chan error)
   205  	for r := 0; r < numReaders; r++ {
   206  		readerWG.Add(1)
   207  		go func(reader int) {
   208  			for bptsI := range readerCh {
   209  				chunkPt3d, err := bptsI.BCoord.ToChunkPoint3d()
   210  				if err != nil {
   211  					errorCh <- err
   212  					readerWG.Done()
   213  					return
   214  				}
   215  				block, err := d.getSupervoxelBlock(v, chunkPt3d, scale)
   216  				if err != nil {
   217  					errorCh <- err
   218  					readerWG.Done()
   219  					return
   220  				}
   221  				bptsI.Block = *block
   222  				processCh <- bptsI
   223  			}
   224  			readerWG.Done()
   225  		}(r)
   226  	}
   227  
   228  	// Stream sorted blocks to readers.
   229  	for i := 0; i < len(bptsSlice); i++ {
   230  		readerCh <- &(bptsSlice[i])
   231  	}
   232  	close(readerCh)
   233  	readerWG.Wait()
   234  
   235  	close(processCh)
   236  	processorWG.Wait()
   237  
   238  	timedLog.Infof("Large-scale labelmap %q label lookup for %d pts -> %d blocks with %d processors / %d readers",
   239  		d.DataName(), len(mapped), len(bptsSlice), processors, numReaders)
   240  
   241  	select {
   242  	case err := <-errorCh:
   243  		dvid.Errorf("Large-scale labelmap %q label lookup had error: %v", d.DataName(), err)
   244  		return err
   245  	default:
   246  		return nil
   247  	}
   248  }
   249  
   250  // GetLabelPoints returns labels or supervoxels corresponding to given 3d points.
   251  func (d *Data) GetLabelPoints(v dvid.VersionID, pts []dvid.Point3d, scale uint8, useSupervoxels bool) (mapped []uint64, err error) {
   252  	if len(pts) == 0 {
   253  		return
   254  	}
   255  	mapped = make([]uint64, len(pts))
   256  	bptsSlice := d.sortByBlockCoord(pts)
   257  
   258  	// Get mapping.
   259  	var mapping *VCache
   260  	var mappedVersions distFromRoot
   261  	if !useSupervoxels {
   262  		if mapping, err = getMapping(d, v); err != nil {
   263  			return nil, err
   264  		}
   265  		if mapping != nil {
   266  			mappedVersions = mapping.getMappedVersionsDist(v)
   267  			if err != nil {
   268  				err = fmt.Errorf("unable to get ancestry for version %d: %v", v, err)
   269  				return
   270  			}
   271  		}
   272  	}
   273  
   274  	if len(pts) < 100 {
   275  		err = d.getFewLabelPoints(mapped, v, bptsSlice, mapping, mappedVersions, scale)
   276  	} else {
   277  		err = d.getManyLabelPoints(mapped, v, bptsSlice, mapping, mappedVersions, scale)
   278  	}
   279  	return
   280  }
   281  
   282  // GetPointsInSupervoxels returns the 3d points that fall within given supervoxels that are
   283  // assumed to be mapped to one label.  If supervoxels are assigned to more than one label,
   284  // or a mapping is not available, an error is returned.  The label index is not used so
   285  // this function will use immutable data underneath if there are no supervoxel splits.
   286  func (d *Data) GetPointsInSupervoxels(v dvid.VersionID, pts []dvid.Point3d, supervoxels []uint64) (inSupervoxels []bool, err error) {
   287  	inSupervoxels = make([]bool, len(pts))
   288  	if len(pts) == 0 || len(supervoxels) == 0 {
   289  		return
   290  	}
   291  	timedLog := dvid.NewTimeLog()
   292  
   293  	supervoxelSet := make(labels.Set)
   294  	for _, supervoxel := range supervoxels {
   295  		supervoxelSet[supervoxel] = struct{}{}
   296  	}
   297  	ptsByBlock := d.partitionPoints(pts)
   298  
   299  	// Launch the block processing goroutines
   300  	var wg sync.WaitGroup
   301  	concurrency := len(ptsByBlock) / 10
   302  	if concurrency < 1 {
   303  		concurrency = 1
   304  	}
   305  	ch := make(chan blockPtsI, len(ptsByBlock))
   306  	for c := 0; c < concurrency; c++ {
   307  		wg.Add(1)
   308  		go func() {
   309  			for bptsI := range ch {
   310  				blockLabels := bptsI.Block.GetPointLabels(bptsI.pts)
   311  				for i, index := range bptsI.indices {
   312  					supervoxel := blockLabels[i]
   313  					if _, found := supervoxelSet[supervoxel]; found {
   314  						inSupervoxels[index] = true
   315  					}
   316  				}
   317  			}
   318  			wg.Done()
   319  		}()
   320  	}
   321  
   322  	// Send the blocks spanning the given supervoxels.
   323  	var block *labels.Block
   324  	for bcoord, bptsI := range ptsByBlock {
   325  		var chunkPt3d dvid.ChunkPoint3d
   326  		if chunkPt3d, err = bcoord.ToChunkPoint3d(); err != nil {
   327  			close(ch)
   328  			return
   329  		}
   330  		if block, err = d.getSupervoxelBlock(v, chunkPt3d, 0); err != nil {
   331  			close(ch)
   332  			return nil, err
   333  		}
   334  		ch <- blockPtsI{&labels.PositionedBlock{Block: *block}, bptsI}
   335  	}
   336  	close(ch)
   337  	wg.Wait()
   338  
   339  	timedLog.Infof("Annotation %q point check for %d supervoxels, %d points -> %d blocks (%d goroutines)", d.DataName(), len(supervoxels), len(pts), len(ptsByBlock), concurrency)
   340  	return
   341  }