github.com/janelia-flyem/dvid@v1.0.0/datatype/imageblk/arb_image.go (about) 1 package imageblk 2 3 import ( 4 "fmt" 5 "math" 6 "strconv" 7 "sync" 8 9 "github.com/janelia-flyem/dvid/datastore" 10 "github.com/janelia-flyem/dvid/dvid" 11 "github.com/janelia-flyem/dvid/server" 12 "github.com/janelia-flyem/dvid/storage" 13 ) 14 15 // ArbSlice is a 2d rectangle that can be positioned arbitrarily in 3D. 16 type ArbSlice struct { 17 topLeft dvid.Vector3d 18 topRight dvid.Vector3d 19 bottomLeft dvid.Vector3d 20 res float64 21 // Calculated from above 22 size dvid.Point2d 23 incrX dvid.Vector3d 24 incrY dvid.Vector3d 25 26 // The image buffer. We don't worry about strides and byte order for now because 27 // we only GET and don't PUT arbitrary images, where we have to worry about receiving 28 // external data. 29 bytesPerVoxel int32 30 data []byte 31 } 32 33 // NewArbSliceFromStrings returns an image with arbitrary 3D orientation given string parameters. 34 // The 3d points are in real world space definited by resolution, e.g., nanometer space. 35 func (d *Data) NewArbSliceFromStrings(tlStr, trStr, blStr, resStr, sep string) (*ArbSlice, error) { 36 topLeft, err := dvid.StringToVector3d(tlStr, sep) 37 if err != nil { 38 return nil, err 39 } 40 topRight, err := dvid.StringToVector3d(trStr, sep) 41 if err != nil { 42 return nil, err 43 } 44 bottomLeft, err := dvid.StringToVector3d(blStr, sep) 45 if err != nil { 46 return nil, err 47 } 48 res, err := strconv.ParseFloat(resStr, 64) 49 if err != nil { 50 return nil, err 51 } 52 return d.NewArbSlice(topLeft, topRight, bottomLeft, res) 53 } 54 55 // NewArbSlice returns an image with arbitrary 3D orientation. 56 // The 3d points are in real world space definited by resolution, e.g., nanometer space. 57 func (d *Data) NewArbSlice(topLeft, topRight, bottomLeft dvid.Vector3d, res float64) (*ArbSlice, error) { 58 // Compute the increments in x,y and number of pixes in each direction. 59 dx := topRight.Distance(topLeft) 60 dy := bottomLeft.Distance(topLeft) 61 nxFloat := math.Floor(dx / res) 62 nyFloat := math.Floor(dy / res) 63 incrX := topRight.Subtract(topLeft).DivideScalar(nxFloat) 64 incrY := bottomLeft.Subtract(topLeft).DivideScalar(nyFloat) 65 size := dvid.Point2d{int32(nxFloat) + 1, int32(nyFloat) + 1} 66 bytesPerVoxel := d.Properties.Values.BytesPerElement() 67 arb := &ArbSlice{topLeft, topRight, bottomLeft, res, size, incrX, incrY, bytesPerVoxel, nil} 68 69 // Allocate the image buffer 70 numVoxels := size[0] * size[1] 71 if numVoxels <= 0 { 72 return nil, fmt.Errorf("Bad arbitrary image size requested: %s", arb) 73 } 74 requestSize := int64(bytesPerVoxel) * int64(numVoxels) 75 if requestSize > server.MaxDataRequest { 76 return nil, fmt.Errorf("Requested payload (%d bytes) exceeds this DVID server's set limit (%d)", 77 requestSize, server.MaxDataRequest) 78 } 79 arb.data = make([]byte, requestSize) 80 return arb, nil 81 } 82 83 func (s ArbSlice) String() string { 84 return fmt.Sprintf("Arbitrary %d x %d image: top left %q, top right %q, bottom left %q, res %f", 85 s.size[0], s.size[1], s.topLeft, s.topRight, s.bottomLeft, s.res) 86 } 87 88 func (d *Data) GetArbitraryImage(ctx storage.Context, tlStr, trStr, blStr, resStr string) (*dvid.Image, error) { 89 // Setup the image buffer 90 arb, err := d.NewArbSliceFromStrings(tlStr, trStr, blStr, resStr, "_") 91 if err != nil { 92 return nil, err 93 } 94 95 // Iterate across arbitrary image using res increments, retrieving trilinear interpolation 96 // at each point. 97 cache := NewValueCache(100) 98 keyF := func(pt dvid.Point3d) []byte { 99 chunkPt := pt.Chunk(d.BlockSize()).(dvid.ChunkPoint3d) 100 idx := dvid.IndexZYX(chunkPt) 101 return NewTKey(&idx) 102 } 103 104 // TODO: Add concurrency. 105 leftPt := arb.topLeft 106 var i int32 107 var wg sync.WaitGroup 108 for y := int32(0); y < arb.size[1]; y++ { 109 server.CheckChunkThrottling() 110 wg.Add(1) 111 go func(curPt dvid.Vector3d, dstI int32) { 112 defer func() { 113 server.HandlerToken <- 1 114 wg.Done() 115 }() 116 for x := int32(0); x < arb.size[0]; x++ { 117 value, err := d.computeValue(curPt, ctx, KeyFunc(keyF), cache) 118 if err != nil { 119 dvid.Errorf("Error in concurrent arbitrary image calc: %v", err) 120 return 121 } 122 copy(arb.data[dstI:dstI+arb.bytesPerVoxel], value) 123 124 curPt.Increment(arb.incrX) 125 dstI += arb.bytesPerVoxel 126 } 127 }(leftPt, i) 128 leftPt.Increment(arb.incrY) 129 i += arb.size[0] * arb.bytesPerVoxel 130 } 131 wg.Wait() 132 133 return dvid.ImageFromData(arb.size[0], arb.size[1], arb.data, d.Properties.Values, d.Properties.Interpolable) 134 } 135 136 type neighbors struct { 137 xd, yd, zd float64 138 coords [8]dvid.Point3d 139 values []byte 140 } 141 142 func (d *Data) neighborhood(pt dvid.Vector3d) neighbors { 143 res32 := d.Properties.Resolution 144 res := dvid.Vector3d{float64(res32.VoxelSize[0]), float64(res32.VoxelSize[1]), float64(res32.VoxelSize[2])} 145 146 // Calculate voxel lattice points 147 voxelCoord := dvid.Vector3d{pt[0] / res[0], pt[1] / res[1], pt[2] / res[2]} 148 149 x0 := math.Floor(voxelCoord[0]) 150 x1 := math.Ceil(voxelCoord[0]) 151 y0 := math.Floor(voxelCoord[1]) 152 y1 := math.Ceil(voxelCoord[1]) 153 z0 := math.Floor(voxelCoord[2]) 154 z1 := math.Ceil(voxelCoord[2]) 155 156 ix0 := int32(x0) 157 ix1 := int32(x1) 158 iy0 := int32(y0) 159 iy1 := int32(y1) 160 iz0 := int32(z0) 161 iz1 := int32(z1) 162 163 // Calculate real-world lattice points in given resolution 164 rx0 := x0 * res[0] 165 rx1 := x1 * res[0] 166 ry0 := y0 * res[1] 167 ry1 := y1 * res[1] 168 rz0 := z0 * res[2] 169 rz1 := z1 * res[2] 170 171 var n neighbors 172 if ix0 != ix1 { 173 n.xd = (pt[0] - rx0) / (rx1 - rx0) 174 } 175 if iy0 != iy1 { 176 n.yd = (pt[1] - ry0) / (ry1 - ry0) 177 } 178 if iz0 != iz1 { 179 n.zd = (pt[2] - rz0) / (rz1 - rz0) 180 } 181 182 n.coords[0] = dvid.Point3d{ix0, iy0, iz0} 183 n.coords[1] = dvid.Point3d{ix1, iy0, iz0} 184 n.coords[2] = dvid.Point3d{ix0, iy1, iz0} 185 n.coords[3] = dvid.Point3d{ix1, iy1, iz0} 186 n.coords[4] = dvid.Point3d{ix0, iy0, iz1} 187 n.coords[5] = dvid.Point3d{ix1, iy0, iz1} 188 n.coords[6] = dvid.Point3d{ix0, iy1, iz1} 189 n.coords[7] = dvid.Point3d{ix1, iy1, iz1} 190 191 // Allocate the values slice buffer based on bytes/voxel. 192 bufSize := 8 * d.Properties.Values.BytesPerElement() 193 n.values = make([]byte, bufSize, bufSize) 194 195 return n 196 } 197 198 type KeyFunc func(dvid.Point3d) []byte 199 type PopulateFunc func([]byte) ([]byte, error) 200 201 // ValueCache is a concurrency-friendly cache 202 type ValueCache struct { 203 sync.Mutex 204 deserializedBlocks map[string]([]byte) 205 keyQueue []string 206 size int 207 } 208 209 func NewValueCache(size int) *ValueCache { 210 var vc ValueCache 211 vc.deserializedBlocks = make(map[string]([]byte), size) 212 vc.keyQueue = make([]string, size) 213 vc.size = size 214 return &vc 215 } 216 217 // Get returns the cached value of a key. On a miss, it uses the passed PopulateFunc 218 // to retrieve the key and stores it in the cache. If nil is passed for the PopulateFunc, 219 // the function just returns a "false" with no value. 220 func (vc *ValueCache) Get(key []byte, pf PopulateFunc) ([]byte, bool, error) { 221 vc.Lock() 222 data, found := vc.deserializedBlocks[string(key)] 223 if !found { 224 // If no populate function provided, just say it's not found. 225 if pf == nil { 226 vc.Unlock() 227 return nil, false, nil 228 } 229 // Populate the cache 230 var err error 231 data, err = pf(key) 232 if err != nil { 233 vc.Unlock() 234 return nil, false, err 235 } 236 vc.add(key, data) 237 } 238 vc.Unlock() 239 return data, found, nil 240 } 241 242 func (vc *ValueCache) add(key []byte, data []byte) { 243 stringKey := string(key) 244 if len(vc.keyQueue) >= vc.size { 245 delete(vc.deserializedBlocks, vc.keyQueue[0]) 246 vc.keyQueue = append(vc.keyQueue[1:], stringKey) 247 } else { 248 vc.keyQueue = append(vc.keyQueue, stringKey) 249 } 250 vc.deserializedBlocks[stringKey] = data 251 } 252 253 // Clear clears the cache. 254 func (vc *ValueCache) Clear() { 255 vc.Lock() 256 vc.deserializedBlocks = make(map[string]([]byte), vc.size) 257 vc.keyQueue = make([]string, vc.size) 258 vc.Unlock() 259 } 260 261 // Calculates value of a 3d real world point in space defined by underlying data resolution. 262 func (d *Data) computeValue(pt dvid.Vector3d, ctx storage.Context, keyF KeyFunc, cache *ValueCache) ([]byte, error) { 263 db, err := datastore.GetOrderedKeyValueDB(d) 264 if err != nil { 265 return nil, err 266 } 267 268 valuesPerElement := d.Properties.Values.ValuesPerElement() 269 bytesPerValue, err := d.Properties.Values.BytesPerValue() 270 if err != nil { 271 return nil, err 272 } 273 bytesPerVoxel := valuesPerElement * bytesPerValue 274 275 // Allocate an empty block. 276 blockSize, ok := d.BlockSize().(dvid.Point3d) 277 if !ok { 278 return nil, fmt.Errorf("Data %q does not have a 3d block size", d.DataName()) 279 } 280 nx := blockSize[0] 281 nxy := nx * blockSize[1] 282 emptyBlock := d.BackgroundBlock() 283 284 populateF := func(key []byte) ([]byte, error) { 285 serializedData, err := db.Get(ctx, key) 286 if err != nil { 287 return nil, err 288 } 289 var deserializedData []byte 290 if serializedData == nil || len(serializedData) == 0 { 291 deserializedData = emptyBlock 292 } else { 293 deserializedData, _, err = dvid.DeserializeData(serializedData, true) 294 if err != nil { 295 return nil, fmt.Errorf("Unable to deserialize block: %v", err) 296 } 297 } 298 return deserializedData, nil 299 } 300 301 // For the given point, compute surrounding lattice points and retrieve values. 302 neighbors := d.neighborhood(pt) 303 var valuesI int32 304 for _, voxelCoord := range neighbors.coords { 305 deserializedData, _, err := cache.Get(keyF(voxelCoord), populateF) 306 if err != nil { 307 return nil, err 308 } 309 blockPt := voxelCoord.PointInChunk(blockSize).(dvid.Point3d) 310 blockI := blockPt[2]*nxy + blockPt[1]*nx + blockPt[0] 311 //fmt.Printf("Block %s (%d) len %d -> Neighbor %s (buffer %d, len %d)\n", 312 // blockPt, blockI, len(blockData), voxelCoord, valuesI, len(neighbors.values)) 313 copy(neighbors.values[valuesI:valuesI+bytesPerVoxel], deserializedData[blockI:blockI+bytesPerVoxel]) 314 valuesI += bytesPerVoxel 315 } 316 317 // Perform trilinear interpolation on the underlying data values. 318 unsupported := func() error { 319 return fmt.Errorf("DVID cannot retrieve images with arbitrary orientation using %d channels and %d bytes/channel", 320 valuesPerElement, bytesPerValue) 321 } 322 var value []byte 323 switch valuesPerElement { 324 case 1: 325 switch bytesPerValue { 326 case 1: 327 if d.Interpolable { 328 interpValue := trilinearInterpUint8(neighbors.xd, neighbors.yd, neighbors.zd, []uint8(neighbors.values)) 329 value = []byte{byte(interpValue)} 330 } else { 331 value = []byte{nearestNeighborUint8(neighbors.xd, neighbors.yd, neighbors.zd, []uint8(neighbors.values))} 332 } 333 case 2: 334 fallthrough 335 case 4: 336 fallthrough 337 case 8: 338 fallthrough 339 default: 340 return nil, unsupported() 341 } 342 case 4: 343 switch bytesPerValue { 344 case 1: 345 value = make([]byte, 4, 4) 346 for c := 0; c < 4; c++ { 347 channelValues := make([]uint8, 8, 8) 348 for i := 0; i < 8; i++ { 349 channelValues[i] = uint8(neighbors.values[i*4+c]) 350 } 351 if d.Interpolable { 352 interpValue := trilinearInterpUint8(neighbors.xd, neighbors.yd, neighbors.zd, channelValues) 353 value[c] = byte(interpValue) 354 } else { 355 value[c] = byte(nearestNeighborUint8(neighbors.xd, neighbors.yd, neighbors.zd, channelValues)) 356 } 357 } 358 case 2: 359 fallthrough 360 default: 361 return nil, unsupported() 362 } 363 default: 364 } 365 366 return value, nil 367 } 368 369 // Returns value of nearest neighbor to point. 370 func nearestNeighborUint8(xd, yd, zd float64, values []uint8) uint8 { 371 var x, y, z int 372 if xd > 0.5 { 373 x = 1 374 } 375 if yd > 0.5 { 376 y = 1 377 } 378 if zd > 0.5 { 379 z = 1 380 } 381 return values[z*4+y*2+x] 382 } 383 384 // Returns the trilinear interpolation of a point 'pt' where 'pt0' is the lattice point below and 385 // 'pt1' is the lattice point above. The values c000...c111 are at lattice points surrounding 386 // the interpolated point. This can be used for interpolation of anisotropic space. Formulation 387 // follows Wikipedia trilinear interpolation page although direction of y axes is flipped, which 388 // shouldn't matter for formulae. 389 func trilinearInterpUint8(xd, yd, zd float64, values []uint8) uint8 { 390 c000 := float64(values[0]) 391 c100 := float64(values[1]) 392 c010 := float64(values[2]) 393 c110 := float64(values[3]) 394 c001 := float64(values[4]) 395 c101 := float64(values[5]) 396 c011 := float64(values[6]) 397 c111 := float64(values[7]) 398 c00 := c000*(1.0-xd) + c100*xd 399 c10 := c010*(1.0-xd) + c110*xd 400 c01 := c001*(1.0-xd) + c101*xd 401 c11 := c011*(1.0-xd) + c111*xd 402 403 c0 := c00*(1.0-yd) + c10*yd 404 c1 := c01*(1.0-yd) + c11*yd 405 406 c := math.Floor(c0*(1-zd) + c1*zd + 0.5) 407 if c > 255 { 408 return 255 409 } 410 return uint8(c) 411 }