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

     1  package labelarray
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/janelia-flyem/dvid/datastore"
     7  	"github.com/janelia-flyem/dvid/datatype/common/downres"
     8  	"github.com/janelia-flyem/dvid/datatype/common/labels"
     9  	"github.com/janelia-flyem/dvid/dvid"
    10  )
    11  
    12  // For any lores block, divide it into octants and see if we have mutated the corresponding higher-res blocks.
    13  type octantMap map[dvid.IZYXString][8]*labels.Block
    14  
    15  // Group hires blocks by octants so we see when we actually need to GET a lower-res block.
    16  func (d *Data) getHiresChanges(hires downres.BlockMap) (octantMap, error) {
    17  	octants := make(octantMap)
    18  
    19  	for hiresZYX, value := range hires {
    20  		block, ok := value.(*labels.Block)
    21  		if !ok {
    22  			return nil, fmt.Errorf("bad changing block %s: expected *labels.Block got %v", hiresZYX, value)
    23  		}
    24  		hresCoord, err := hiresZYX.ToChunkPoint3d()
    25  		if err != nil {
    26  			return nil, err
    27  		}
    28  		downresX := hresCoord[0] >> 1
    29  		downresY := hresCoord[1] >> 1
    30  		downresZ := hresCoord[2] >> 1
    31  		loresZYX := dvid.ChunkPoint3d{downresX, downresY, downresZ}.ToIZYXString()
    32  		octidx := ((hresCoord[2] % 2) << 2) + ((hresCoord[1] % 2) << 1) + (hresCoord[0] % 2)
    33  		oct, found := octants[loresZYX]
    34  		if !found {
    35  			oct = [8]*labels.Block{}
    36  		}
    37  		oct[octidx] = block
    38  		octants[loresZYX] = oct
    39  	}
    40  
    41  	return octants, nil
    42  }
    43  
    44  func (d *Data) StoreDownres(v dvid.VersionID, hiresScale uint8, hires downres.BlockMap) (downres.BlockMap, error) {
    45  	if hiresScale >= d.MaxDownresLevel {
    46  		return nil, fmt.Errorf("can't downres %q scale %d since max downres scale is %d", d.DataName(), hiresScale, d.MaxDownresLevel)
    47  	}
    48  	octants, err := d.getHiresChanges(hires)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	blockSize, ok := d.BlockSize().(dvid.Point3d)
    53  	if !ok {
    54  		return nil, fmt.Errorf("block size for data %q is not 3d: %v\n", d.DataName(), d.BlockSize())
    55  	}
    56  
    57  	batcher, err := datastore.GetKeyValueBatcher(d)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	ctx := datastore.NewVersionedCtx(d, v)
    62  	batch := batcher.NewBatch(ctx)
    63  
    64  	downresBMap := make(downres.BlockMap)
    65  	for loresZYX, octant := range octants {
    66  		var numBlocks int
    67  		for _, block := range octant {
    68  			if block != nil {
    69  				numBlocks++
    70  			}
    71  		}
    72  
    73  		var loresBlock *labels.Block
    74  		if numBlocks < 8 {
    75  			chunkPt, err := loresZYX.ToChunkPoint3d()
    76  			if err != nil {
    77  				return nil, err
    78  			}
    79  			loresBlock, err = d.GetLabelBlock(v, chunkPt, hiresScale+1)
    80  			if err != nil {
    81  				return nil, err
    82  			}
    83  		} else {
    84  			loresBlock = labels.MakeSolidBlock(0, blockSize)
    85  		}
    86  		if err := loresBlock.Downres(octant); err != nil {
    87  			return nil, err
    88  		}
    89  		downresBMap[loresZYX] = loresBlock
    90  
    91  		compressed, _ := loresBlock.MarshalBinary()
    92  		serialization, err := dvid.SerializeData(compressed, d.Compression(), d.Checksum())
    93  		if err != nil {
    94  			return nil, fmt.Errorf("Unable to serialize downres block in %q: %v\n", d.DataName(), err)
    95  		}
    96  		tk := NewBlockTKeyByCoord(hiresScale+1, loresZYX)
    97  		batch.Put(tk, serialization)
    98  	}
    99  	if err := batch.Commit(); err != nil {
   100  		return nil, fmt.Errorf("Error on trying to write downres batch of scale %d->%d: %v\n", hiresScale, hiresScale+1, err)
   101  	}
   102  	return downresBMap, nil
   103  }