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 }