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

     1  /*
     2  	Functinos that support compositing label and image blocks into a pseudo-color representation.
     3  */
     4  
     5  package labelarray
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"sync"
    12  
    13  	"github.com/janelia-flyem/dvid/datastore"
    14  	"github.com/janelia-flyem/dvid/datatype/common/labels"
    15  	"github.com/janelia-flyem/dvid/datatype/imageblk"
    16  	"github.com/janelia-flyem/dvid/dvid"
    17  	"github.com/janelia-flyem/dvid/server"
    18  	"github.com/janelia-flyem/dvid/storage"
    19  )
    20  
    21  type compositeOp struct {
    22  	grayscale *imageblk.Data
    23  	composite *imageblk.Data
    24  	versionID dvid.VersionID
    25  }
    26  
    27  // CreateComposite creates a new rgba8 image by combining hash of labels + the grayscale
    28  func (d *Data) CreateComposite(request datastore.Request, reply *datastore.Response) error {
    29  	timedLog := dvid.NewTimeLog()
    30  
    31  	// Parse the request
    32  	var uuidStr, dataName, cmdStr, grayscaleName, destName string
    33  	request.CommandArgs(1, &uuidStr, &dataName, &cmdStr, &grayscaleName, &destName)
    34  
    35  	// Get the version
    36  	uuid, v, err := datastore.MatchingUUID(uuidStr)
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	// Log request
    42  	if err = datastore.AddToNodeLog(uuid, []string{request.Command.String()}); err != nil {
    43  		return err
    44  	}
    45  
    46  	// Get the grayscale data.
    47  	dataservice, err := datastore.GetDataByUUIDName(uuid, dvid.InstanceName(grayscaleName))
    48  	if err != nil {
    49  		return err
    50  	}
    51  	grayscale, ok := dataservice.(*imageblk.Data)
    52  	if !ok {
    53  		return fmt.Errorf("%s is not the name of uint8 data", grayscaleName)
    54  	}
    55  
    56  	// Create a new rgba8blk data.
    57  	var compservice datastore.DataService
    58  	compservice, err = datastore.GetDataByUUIDName(uuid, dvid.InstanceName(destName))
    59  	if err == nil {
    60  		return fmt.Errorf("Data instance with name %q already exists", destName)
    61  	}
    62  	typeService, err := datastore.TypeServiceByName("rgba8blk")
    63  	if err != nil {
    64  		return fmt.Errorf("Could not get rgba8 type service from DVID")
    65  	}
    66  	config := dvid.NewConfig()
    67  	compservice, err = datastore.NewData(uuid, typeService, dvid.InstanceName(destName), config)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	composite, ok := compservice.(*imageblk.Data)
    72  	if !ok {
    73  		return fmt.Errorf("Error: %s was unable to be set to rgba8 data", destName)
    74  	}
    75  
    76  	// Iterate through all labels and grayscale chunks incrementally in Z, a layer at a time.
    77  	wg := new(sync.WaitGroup)
    78  	op := &compositeOp{grayscale, composite, v}
    79  	chunkOp := &storage.ChunkOp{op, wg}
    80  
    81  	store, err := datastore.GetOrderedKeyValueDB(d)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	ctx := datastore.NewVersionedCtx(d, v)
    86  	extents := d.Extents()
    87  	blockBeg := imageblk.NewTKey(extents.MinIndex)
    88  	blockEnd := imageblk.NewTKey(extents.MaxIndex)
    89  	err = store.ProcessRange(ctx, blockBeg, blockEnd, chunkOp, storage.ChunkFunc(d.CreateCompositeChunk))
    90  	wg.Wait()
    91  
    92  	// Set new mapped data to same extents.
    93  	composite.Properties.Extents = grayscale.Properties.Extents
    94  	if err := datastore.SaveDataByUUID(uuid, composite); err != nil {
    95  		dvid.Infof("Could not save new data '%s': %v\n", destName, err)
    96  	}
    97  
    98  	timedLog.Infof("Created composite of %s and %s", grayscaleName, destName)
    99  	return nil
   100  }
   101  
   102  // CreateCompositeChunk processes each chunk of labels and grayscale data,
   103  // saving the composited result into an rgba8.
   104  // Only some multiple of the # of CPU cores can be used for chunk handling before
   105  // it waits for chunk processing to abate via the buffered server.HandlerToken channel.
   106  func (d *Data) CreateCompositeChunk(chunk *storage.Chunk) error {
   107  	server.CheckChunkThrottling()
   108  	go d.createCompositeChunk(chunk)
   109  	return nil
   110  }
   111  
   112  var curZ int32
   113  var curZMutex sync.Mutex
   114  
   115  func (d *Data) createCompositeChunk(chunk *storage.Chunk) {
   116  	defer func() {
   117  		// After processing a chunk, return the token.
   118  		server.HandlerToken <- 1
   119  
   120  		// Notify the requestor that this chunk is done.
   121  		if chunk.Wg != nil {
   122  			chunk.Wg.Done()
   123  		}
   124  	}()
   125  
   126  	op := chunk.Op.(*compositeOp)
   127  
   128  	// Get the spatial index associated with this chunk.
   129  	zyx, err := imageblk.DecodeTKey(chunk.K)
   130  	if err != nil {
   131  		dvid.Errorf("Error in %s.ChunkApplyMap(): %v", d.Data.DataName(), err)
   132  		return
   133  	}
   134  
   135  	// Initialize the label buffers.  For voxels, this data needs to be uncompressed and deserialized.
   136  	curZMutex.Lock()
   137  	if zyx[2] > curZ {
   138  		curZ = zyx[2]
   139  		minZ := zyx.MinPoint(d.BlockSize()).Value(2)
   140  		maxZ := zyx.MaxPoint(d.BlockSize()).Value(2)
   141  		dvid.Debugf("Now creating composite blocks for Z %d to %d\n", minZ, maxZ)
   142  	}
   143  	curZMutex.Unlock()
   144  
   145  	deserialization, _, err := dvid.DeserializeData(chunk.V, true)
   146  	if err != nil {
   147  		dvid.Errorf("Unable to deserialize block in %q: %v\n", d.DataName(), err)
   148  		return
   149  	}
   150  	var block labels.Block
   151  	if err = block.UnmarshalBinary(deserialization); err != nil {
   152  		dvid.Errorf("Unable to unmarshal binary block: %v\n", err)
   153  		return
   154  	}
   155  	labelData, _ := block.MakeLabelVolume()
   156  	blockBytes := len(labelData)
   157  	if blockBytes%8 != 0 {
   158  		dvid.Infof("Retrieved, deserialized block is wrong size: %d bytes\n", blockBytes)
   159  		return
   160  	}
   161  
   162  	// Get the corresponding grayscale block.
   163  	store, err := datastore.GetOrderedKeyValueDB(op.grayscale)
   164  	if err != nil {
   165  		dvid.Errorf("Unable to retrieve store for %q: %v\n", op.grayscale.DataName(), err)
   166  		return
   167  	}
   168  	grayscaleCtx := datastore.NewVersionedCtx(op.grayscale, op.versionID)
   169  	blockData, err := store.Get(grayscaleCtx, chunk.K)
   170  	if err != nil {
   171  		dvid.Errorf("Error getting grayscale block for index %s\n", zyx)
   172  		return
   173  	}
   174  	grayscaleData, _, err := dvid.DeserializeData(blockData, true)
   175  	if err != nil {
   176  		dvid.Errorf("Unable to deserialize block in '%s': %v\n", op.grayscale.DataName(), err)
   177  		return
   178  	}
   179  
   180  	// Compute the composite block.
   181  	// TODO -- Exploit run lengths, use cache of hash?
   182  	compositeBytes := blockBytes / 2
   183  	compositeData := make([]byte, compositeBytes, compositeBytes)
   184  	compositeI := 0
   185  	labelI := 0
   186  	hashBuf := make([]byte, 4, 4)
   187  	for _, grayscale := range grayscaleData {
   188  		//murmurhash3(labelData[labelI:labelI+8], hashBuf)
   189  		//hashBuf[3] = grayscale
   190  		writePseudoColor(grayscale, labelData[labelI:labelI+8], hashBuf)
   191  		copy(compositeData[compositeI:compositeI+4], hashBuf)
   192  		compositeI += 4
   193  		labelI += 8
   194  	}
   195  
   196  	// Store the composite block into the rgba8 data.
   197  	serialization, err := dvid.SerializeData(compositeData, d.Compression(), d.Checksum())
   198  	if err != nil {
   199  		dvid.Errorf("Unable to serialize composite block %s: %v\n", zyx, err)
   200  		return
   201  	}
   202  	compositeCtx := datastore.NewVersionedCtx(op.composite, op.versionID)
   203  	err = store.Put(compositeCtx, chunk.K, serialization)
   204  	if err != nil {
   205  		dvid.Errorf("Unable to PUT composite block %s: %v\n", zyx, err)
   206  		return
   207  	}
   208  }
   209  
   210  func writePseudoColor(grayscale uint8, in64bits, out32bits []byte) {
   211  	murmurhash3(in64bits, out32bits)
   212  	var t uint64
   213  	t = uint64(out32bits[0]) * uint64(grayscale)
   214  	t >>= 8
   215  	out32bits[0] = uint8(t)
   216  	t = uint64(out32bits[1]) * uint64(grayscale)
   217  	t >>= 8
   218  	out32bits[1] = uint8(t)
   219  	t = uint64(out32bits[2]) * uint64(grayscale)
   220  	t >>= 8
   221  	out32bits[2] = uint8(t)
   222  	out32bits[3] = 255
   223  }
   224  
   225  func murmurhash3(in64bits, out32bits []byte) {
   226  	length := len(in64bits)
   227  	var c1, c2 uint32 = 0xcc9e2d51, 0x1b873593
   228  	nblocks := length / 4
   229  	var h, k uint32
   230  	buf := bytes.NewBuffer(in64bits)
   231  	for i := 0; i < nblocks; i++ {
   232  		binary.Read(buf, binary.LittleEndian, &k)
   233  		k *= c1
   234  		k = (k << 15) | (k >> (32 - 15))
   235  		k *= c2
   236  		h ^= k
   237  		h = (h << 13) | (h >> (32 - 13))
   238  		h = (h * 5) + 0xe6546b64
   239  	}
   240  	k = 0
   241  	tailIndex := nblocks * 4
   242  	switch length & 3 {
   243  	case 3:
   244  		k ^= uint32(in64bits[tailIndex+2]) << 16
   245  		fallthrough
   246  	case 2:
   247  		k ^= uint32(in64bits[tailIndex+1]) << 8
   248  		fallthrough
   249  	case 1:
   250  		k ^= uint32(in64bits[tailIndex])
   251  		k *= c1
   252  		k = (k << 15) | (k >> (32 - 15))
   253  		k *= c2
   254  		h ^= k
   255  	}
   256  	h ^= uint32(length)
   257  	h ^= h >> 16
   258  	h *= 0x85ebca6b
   259  	h ^= h >> 13
   260  	h *= 0xc2b2ae35
   261  	h ^= h >> 16
   262  	binary.BigEndian.PutUint32(out32bits, h)
   263  }