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  }