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 }