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

     1  /*
     2  		Package imageblk implements DVID support for image blocks of various formats (uint8, uint16, rgba8).
     3  	    For label data, use labelblk.
     4  */
     5  package imageblk
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"encoding/gob"
    11  	"encoding/json"
    12  	"fmt"
    13  	"image"
    14  	"image/jpeg"
    15  	"io/ioutil"
    16  	"net/http"
    17  	"os"
    18  	"reflect"
    19  	"strconv"
    20  	"strings"
    21  	"sync"
    22  
    23  	"github.com/janelia-flyem/dvid/datastore"
    24  	"github.com/janelia-flyem/dvid/datatype/roi"
    25  	"github.com/janelia-flyem/dvid/dvid"
    26  	"github.com/janelia-flyem/dvid/server"
    27  	"github.com/janelia-flyem/dvid/storage"
    28  )
    29  
    30  const (
    31  	Version = "0.2"
    32  	RepoURL = "github.com/janelia-flyem/dvid/datatype/imageblk"
    33  )
    34  
    35  const helpMessage = `
    36  API for image block datatype (github.com/janelia-flyem/dvid/datatype/imageblk)
    37  ==============================================================================
    38  
    39  Note: UUIDs referenced below are strings that may either be a unique prefix of a
    40  hexadecimal UUID string (e.g., 3FA22) or a branch leaf specification that adds
    41  a colon (":") followed by the case-dependent branch name.  In the case of a
    42  branch leaf specification, the unique UUID prefix just identifies the repo of
    43  the branch, and the UUID referenced is really the leaf of the branch name.
    44  For example, if we have a DAG with root A -> B -> C where C is the current
    45  HEAD or leaf of the "master" (default) branch, then asking for "B:master" is
    46  the same as asking for "C".  If we add another version so A -> B -> C -> D, then
    47  references to "B:master" now return the data from "D".
    48  
    49  -----
    50  
    51  Different data types are available:
    52  
    53      uint8blk
    54      rgba8blk
    55  
    56  Command-line:
    57  
    58  $ dvid repo <UUID> new imageblk <data name> <settings...>
    59  
    60  	Adds newly named data of the 'type name' to repo with specified UUID.
    61  
    62  	Example (note anisotropic resolution specified instead of default 8 nm isotropic):
    63  
    64  	$ dvid repo 3f8c new uint8blk mygrayscale BlockSize=32,32,32 Res=3.2,3.2,40.0
    65  
    66      Arguments:
    67  
    68      UUID           Hexadecimal string with enough characters to uniquely identify a version node.
    69      type name      Data type name, e.g., "uint8"
    70      data name      Name of data to create, e.g., "mygrayscale"
    71      settings       Configuration settings in "key=value" format separated by spaces.
    72  
    73      Configuration Settings (case-insensitive keys)
    74  
    75      BlockSize      Size in pixels  (default: %d)
    76      VoxelSize      Resolution of voxels (default: %f)
    77      VoxelUnits     Resolution units (default: "nanometers")
    78  	Background     Integer value that signifies background in any element (default: 0)
    79  	GridStore      Store designation that gives neuroglancer precomputed specification
    80  	ScaleLevel     Used if GridStore set.  Specifies scale level (int) of resolution.
    81  
    82  $ dvid node <UUID> <data name> load <offset> <image glob>
    83  
    84      Initializes version node to a set of XY images described by glob of filenames.  The
    85      DVID server must have access to the named files.  Currently, XY images are required.
    86  
    87      Example: 
    88  
    89      $ dvid node 3f8c mygrayscale load 0,0,100 data/*.png
    90  
    91      Arguments:
    92  
    93      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
    94      data name     Name of data to add.
    95      offset        3d coordinate in the format "x,y,z".  Gives coordinate of top upper left voxel.
    96      image glob    Filenames of images, e.g., foo-xy-*.png
    97  
    98  $ dvid node <UUID> <data name> put local  <plane> <offset> <image glob>
    99  $ dvid node <UUID> <data name> put remote <plane> <offset> <image glob>
   100  
   101      Adds image data to a version node when the server can see the local files ("local")
   102      or when the server must be sent the files via rpc ("remote").  If possible, use the
   103      "load" command instead because it is much more efficient.
   104  
   105      Example: 
   106  
   107      $ dvid node 3f8c mygrayscale put local xy 0,0,100 data/*.png
   108  
   109      Arguments:
   110  
   111      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   112      data name     Name of data to add.
   113      dims          The axes of data extraction in form "i,j,k,..."  Example: "0,2" can be XZ.
   114                      Slice strings ("xy", "xz", or "yz") are also accepted.
   115      offset        3d coordinate in the format "x,y,z".  Gives coordinate of top upper left voxel.
   116      image glob    Filenames of images, e.g., foo-xy-*.png
   117  	
   118  
   119  $ dvid node <UUID> <data name> roi <new roi data name> <background values separated by comma> 
   120  
   121      Creates a ROI consisting of all voxel blocks that are non-background.
   122  
   123      Example:
   124  
   125      $ dvid node 3f8c mygrayscale roi grayscale_roi 0,255
   126  
   127      
   128      ------------------
   129  
   130  HTTP API (Level 2 REST):
   131  
   132  GET  <api URL>/node/<UUID>/<data name>/help
   133  
   134  	Returns data-specific help message.
   135  
   136  
   137  GET  <api URL>/node/<UUID>/<data name>/info
   138  
   139      Retrieves or DVID-specific data properties for these voxels.
   140  
   141      Example: 
   142  
   143      GET <api URL>/node/3f8c/grayscale/info
   144  
   145      Returns JSON with configuration settings that include location in DVID space and
   146      min/max block indices.
   147  
   148      Arguments:
   149  
   150      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   151      data name     Name of voxels data.
   152  
   153  
   154  GET  <api URL>/node/<UUID>/<data name>/metadata
   155  
   156  	Retrieves a JSON schema (application/vnd.dvid-nd-data+json) that describes the layout
   157  	of bytes returned for n-d images.
   158  
   159  POST  <api URL>/node/<UUID>/<data name>/extents
   160    
   161    	Sets the extents for the image volume.  This is primarily used when POSTing from multiple
   162  	DVID servers not sharing common metadata to a shared backend.
   163    
   164    	Extents should be in JSON in the following format:
   165    	{
   166    	    "MinPoint": [0,0,0],
   167    	    "MaxPoint": [300,400,500]
   168    	}
   169  
   170  POST  <api URL>/node/<UUID>/<data name>/resolution
   171    
   172    	Sets the resolution for the image volume. 
   173    
   174    	Extents should be in JSON in the following format:
   175    	[8,8,8]
   176  
   177  GET <api URL>/node/<UUID>/<data name>/rawkey?x=<block x>&y=<block y>&z=<block z>
   178  
   179      Returns JSON describing hex-encoded binary key used to store a block of data at the given block coordinate:
   180  
   181      {
   182          "Key": "FF3801AD78BBD4829A3"
   183      }
   184  
   185      The query options for block x, y, and z must be supplied or this request will return an error.
   186  
   187  GET  <api URL>/node/<UUID>/<data name>/isotropic/<dims>/<size>/<offset>[/<format>][?queryopts]
   188  
   189      Retrieves either 2d images (PNG by default) or 3d binary data, depending on the dims parameter. 
   190  	If the underlying data is float32, then the little-endian four byte format is written as RGBA.
   191      The 3d binary data response has "Content-type" set to "application/octet-stream" and is an array of 
   192      voxel values in ZYX order (X iterates most rapidly).
   193  
   194      Example: 
   195  
   196      GET <api URL>/node/3f8c/grayscale/isotropic/0_1/512_256/0_0_100/jpg:80
   197  
   198      Returns an isotropic XY slice (0th and 1st dimensions) with width (x) of 512 voxels and
   199      height (y) of 256 voxels with offset (0,0,100) in JPG format with quality 80.
   200      Additional processing is applied based on voxel resolutions to make sure the retrieved image 
   201      has isotropic pixels.  For example, if an XZ image is requested and the image volume has 
   202      X resolution 3 nm and Z resolution 40 nm, the returned image's height will be magnified 40/3
   203      relative to the raw data.
   204      The example offset assumes the "grayscale" data in version node "3f8c" is 3d.
   205      The "Content-type" of the HTTP response should agree with the requested format.
   206      For example, returned PNGs will have "Content-type" of "image/png", and returned
   207      nD data will be "application/octet-stream".
   208  
   209      Arguments:
   210  
   211      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   212      data name     Name of data to add.
   213      dims          The axes of data extraction in form "i_j_k,..."  Example: "0_2" can be XZ.
   214                      Slice strings ("xy", "xz", or "yz") are also accepted.
   215      size          Size in voxels along each dimension specified in <dims>.
   216      offset        Gives coordinate of first voxel using dimensionality of data.
   217      format        Valid formats depend on the dimensionality of the request and formats
   218                      available in server implementation.
   219                    2D: "png", "jpg" (default: "png")
   220                      jpg allows lossy quality setting, e.g., "jpg:80"
   221                    nD: uses default "octet-stream".
   222  
   223      Query-string Options:
   224  
   225      throttle      Only works for 3d data requests.  If "true", makes sure only N compute-intense operation 
   226                      (all API calls that can be throttled) are handled.  If the server can't initiate the API 
   227                      call right away, a 503 (Service Unavailable) status code is returned.
   228  
   229  GET  <api URL>/node/<UUID>/<data name>/specificblocks[?queryopts]
   230  
   231      Retrieves blocks corresponding to those specified in the query string.  This interface
   232      is useful if the blocks retrieved are not consecutive or if the backend in non ordered.
   233  
   234      TODO: enable arbitrary compression to be specified
   235  
   236      Example: 
   237  
   238      GET <api URL>/node/3f8c/grayscale/specificblocks?blocks=x1,y1,z2,x2,y2,z2,x3,y3,z3
   239  	
   240  	This will fetch blocks at position (x1,y1,z1), (x2,y2,z2), and (x3,y3,z3).
   241  	The returned byte stream has a list of blocks with a leading block 
   242  	coordinate (3 x int32) plus int32 giving the # of bytes in this block, and  then the 
   243  	bytes for the value.  If blocks are unset within the span, they will not appear in the stream,
   244  	so the returned data will be equal to or less than spanX blocks worth of data.  
   245  
   246      The returned data format has the following format where int32 is in little endian and the bytes of
   247      block data have been compressed in JPEG format.
   248  
   249          int32  Block 1 coordinate X (Note that this may not be starting block coordinate if it is unset.)
   250          int32  Block 1 coordinate Y
   251          int32  Block 1 coordinate Z
   252          int32  # bytes for first block (N1)
   253          byte0  Bytes of block data in jpeg-compressed format.
   254          byte1
   255          ...
   256          byteN1
   257  
   258          int32  Block 2 coordinate X
   259          int32  Block 2 coordinate Y
   260          int32  Block 2 coordinate Z
   261          int32  # bytes for second block (N2)
   262          byte0  Bytes of block data in jpeg-compressed format.
   263          byte1
   264          ...
   265          byteN2
   266  
   267          ...
   268  
   269      If no data is available for given block span, nothing is returned.
   270  
   271      Arguments:
   272  
   273      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   274      data name     Name of data to add.
   275  
   276      Query-string Options:
   277  
   278      compression   Allows retrieval of block data in default storage or as "uncompressed".
   279      blocks	  x,y,z... block string
   280      prefetch	  ("on" or "true") Do not actually send data, non-blocking (default "off")
   281  
   282  
   283  GET  <api URL>/node/<UUID>/<data name>/subvolblocks/<size>/<offset>[?queryopts]
   284  
   285      Retrieves blocks corresponding to the extents specified by the size and offset.  The
   286      subvolume request must be block aligned.  This is the most server-efficient way of
   287      retrieving imagelblk data, where data read from the underlying storage engine
   288      is written directly to the HTTP connection.
   289  
   290      Example: 
   291  
   292      GET <api URL>/node/3f8c/segmentation/subvolblocks/64_64_64/0_0_0
   293  
   294  	If block size is 32x32x32, this call retrieves up to 8 blocks where the first potential
   295  	block is at 0, 0, 0.  The returned byte stream has a list of blocks with a leading block 
   296  	coordinate (3 x int32) plus int32 giving the # of bytes in this block, and  then the 
   297  	bytes for the value.  If blocks are unset within the span, they will not appear in the stream,
   298  	so the returned data will be equal to or less than spanX blocks worth of data.  
   299  
   300      The returned data format has the following format where int32 is in little endian and the bytes of
   301      block data have been compressed in JPEG format.
   302  
   303          int32  Block 1 coordinate X (Note that this may not be starting block coordinate if it is unset.)
   304          int32  Block 1 coordinate Y
   305          int32  Block 1 coordinate Z
   306          int32  # bytes for first block (N1)
   307          byte0  Bytes of block data in jpeg-compressed format.
   308          byte1
   309          ...
   310          byteN1
   311  
   312          int32  Block 2 coordinate X
   313          int32  Block 2 coordinate Y
   314          int32  Block 2 coordinate Z
   315          int32  # bytes for second block (N2)
   316          byte0  Bytes of block data in jpeg-compressed format.
   317          byte1
   318          ...
   319          byteN2
   320  
   321          ...
   322  
   323      If no data is available for given block span, nothing is returned.
   324  
   325      Arguments:
   326  
   327      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   328      data name     Name of data to add.
   329      size          Size in voxels along each dimension specified in <dims>.
   330      offset        Gives coordinate of first voxel using dimensionality of data.
   331  
   332      Query-string Options:
   333  
   334  	compression   Allows retrieval of block data in "jpeg" (default) or "uncompressed". 
   335  					Note that if the data isn't stored as JPEG, it cannot be requested. 
   336      throttle      If "true", makes sure only N compute-intense operation (all API calls that can be throttled) 
   337                      are handled.  If the server can't initiate the API call right away, a 503 (Service Unavailable) 
   338                      status code is returned.
   339  
   340  
   341  
   342  
   343  
   344  GET  <api URL>/node/<UUID>/<data name>/raw/<dims>/<size>/<offset>[/<format>][?queryopts]
   345  
   346      Retrieves either 2d images (PNG by default) or 3d binary data, depending on the dims parameter.
   347  	If the underlying data is float32, then the little-endian four byte format is written as RGBA.
   348      The 3d binary data response has "Content-type" set to "application/octet-stream" and is an array of 
   349      voxel values in ZYX order (X iterates most rapidly).
   350  
   351      Example: 
   352  
   353      GET <api URL>/node/3f8c/grayscale/raw/0_1/512_256/0_0_100/jpg:80
   354  
   355      Returns a raw XY slice (0th and 1st dimensions) with width (x) of 512 voxels and
   356      height (y) of 256 voxels with offset (0,0,100) in JPG format with quality 80.
   357      By "raw", we mean that no additional processing is applied based on voxel
   358      resolutions to make sure the retrieved image has isotropic pixels.
   359      The example offset assumes the "grayscale" data in version node "3f8c" is 3d.
   360      The "Content-type" of the HTTP response should agree with the requested format.
   361      For example, returned PNGs will have "Content-type" of "image/png", and returned
   362      nD data will be "application/octet-stream". 
   363  
   364      Arguments:
   365  
   366      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   367      data name     Name of data to add.
   368      dims          The axes of data extraction in form "i_j_k,..."  
   369                      Slice strings ("xy", "xz", or "yz") are also accepted.
   370                      Example: "0_2" is XZ, and "0_1_2" is a 3d subvolume.
   371      size          Size in voxels along each dimension specified in <dims>.
   372      offset        Gives coordinate of first voxel using dimensionality of data.
   373      format        Valid formats depend on the dimensionality of the request and formats
   374                      available in server implementation.
   375                    2D: "png", "jpg" (default: "png")
   376                      jpg allows lossy quality setting, e.g., "jpg:80"
   377                    3D: uses default "octet-stream".
   378  
   379      Query-string Options:
   380  
   381      roi           Name of roi data instance used to mask the requested data.
   382      attenuation   For attenuation n, this reduces the intensity of voxels outside ROI by 2^n.
   383                    Valid range is n = 1 to n = 7.  Currently only implemented for 8-bit voxels.
   384                    Default is to zero out voxels outside ROI.
   385      throttle      Only works for 3d data requests.  If "true", makes sure only N compute-intense operation 
   386                      (all API calls that can be throttled) are handled.  If the server can't initiate the API 
   387                      call right away, a 503 (Service Unavailable) status code is returned.
   388  
   389  POST <api URL>/node/<UUID>/<data name>/raw/0_1_2/<size>/<offset>[?queryopts]
   390  
   391      Puts block-aligned voxel data using the block sizes defined for  this data instance.  
   392      For example, if the BlockSize = 32, offset and size must be multiples of 32.
   393  
   394      Example: 
   395  
   396      POST <api URL>/node/3f8c/grayscale/raw/0_1_2/512_256_128/0_0_32
   397  
   398      Throttling can be enabled by passing a "throttle=true" query string.  Throttling makes sure
   399      only one compute-intense operation (all API calls that can be throttled) is handled.
   400      If the server can't initiate the API call right away, a 503 (Service Unavailable) status
   401      code is returned.
   402  
   403      Arguments:
   404  
   405      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   406      data name     Name of data to add.
   407      size          Size in voxels along each dimension specified in <dims>.
   408      offset        Gives coordinate of first voxel using dimensionality of data.
   409  
   410      Query-string Options:
   411  
   412      roi           Name of roi data instance used to mask the requested data.
   413      mutate        Default "false" corresponds to ingestion, i.e., the first write of the given block.
   414                      Use "true" to indicate the POST is a mutation of prior data, which allows any
   415                      synced data instance to cleanup prior denormalizations.  If "mutate=true", the
   416                      POST operations will be slower due to a required GET to retrieve past data.
   417      throttle      If "true", makes sure only N compute-intense operation 
   418                      (all API calls that can be throttled) are handled.  If the server can't initiate the API 
   419                      call right away, a 503 (Service Unavailable) status code is returned.
   420  
   421  GET  <api URL>/node/<UUID>/<data name>/arb/<top left>/<top right>/<bottom left>/<res>[/<format>][?queryopts]
   422  
   423      Retrieves non-orthogonal (arbitrarily oriented planar) image data of named 3d data 
   424      within a version node.  Returns an image where the top left pixel corresponds to the
   425      real world coordinate (not in voxel space but in space defined by resolution, e.g.,
   426      nanometer space).  The real world coordinates are specified in  "x_y_z" format, e.g., "20.3_11.8_109.4".
   427      The resolution is used to determine the # pixels in the returned image.
   428  
   429      Example: 
   430  
   431      GET <api URL>/node/3f8c/grayscale/arb/100.2_90_80.7/200.2_90_80.7/100.2_190.0_80.7/10.0/jpg:80
   432  
   433      Arguments:
   434  
   435      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   436      data name     Name of data to add.
   437      top left      Real world coordinate (in nanometers) of top left pixel in returned image.
   438      top right     Real world coordinate of top right pixel.
   439      bottom left   Real world coordinate of bottom left pixel.
   440      res           The resolution/pixel that is used to calculate the returned image size in pixels.
   441      format        "png", "jpg" (default: "png")  
   442                      jpg allows lossy quality setting, e.g., "jpg:80"
   443  
   444      Query-string Options:
   445  
   446      throttle      If "true", makes sure only N compute-intense operation 
   447                      (all API calls that can be throttled) are handled.  If the server can't initiate the API 
   448                      call right away, a 503 (Service Unavailable) status code is returned.
   449  
   450   GET <api URL>/node/<UUID>/<data name>/blocks/<block coord>/<spanX>
   451  POST <api URL>/node/<UUID>/<data name>/blocks/<block coord>/<spanX>
   452  
   453      Retrieves or puts "spanX" blocks of uncompressed voxel data along X starting from given block coordinate.
   454  
   455      Example: 
   456  
   457      GET <api URL>/node/3f8c/grayscale/blocks/10_20_30/8
   458  
   459      Returns blocks where first block has given block coordinate and number
   460      of blocks returned along x axis is "spanX".  The data is sent in the following format:
   461  
   462      <block 0 byte array>
   463      <block 1 byte array>
   464      ... 
   465      <block N byte array>
   466  
   467      Each byte array iterates in X, then Y, then Z for that block.
   468  
   469      Arguments:
   470  
   471      UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   472      data name     Name of data to add.
   473      block coord   The block coordinate of the first block in X_Y_Z format.  Block coordinates
   474                    can be derived from voxel coordinates by dividing voxel coordinates by
   475                    the block size for a data type.
   476  `
   477  
   478  var (
   479  	// DefaultBlockSize specifies the default size for each block of this data type.
   480  	DefaultBlockSize int32 = 32
   481  
   482  	DefaultRes float32 = 8
   483  
   484  	DefaultUnits = "nanometers"
   485  )
   486  
   487  func init() {
   488  	// Need to register types that will be used to fulfill interfaces.
   489  	gob.Register(&Type{})
   490  	gob.Register(&Data{})
   491  	gob.Register(binary.LittleEndian)
   492  	gob.Register(binary.BigEndian)
   493  }
   494  
   495  // Type embeds the datastore's Type to create a unique type with voxel functions.
   496  // Refinements of general voxel types can be implemented by embedding this type,
   497  // choosing appropriate # of values and bytes/value, overriding functions as needed,
   498  // and calling datastore.Register().
   499  // Note that these fields are invariant for all instances of this type.  Fields
   500  // that can change depending on the type of data (e.g., resolution) should be
   501  // in the Data type.
   502  type Type struct {
   503  	datastore.Type
   504  
   505  	// values describes the data type/label for each value within a voxel.
   506  	values dvid.DataValues
   507  
   508  	// can these values be interpolated?
   509  	interpolable bool
   510  }
   511  
   512  // NewType returns a pointer to a new imageblk Type with default values set.
   513  func NewType(values dvid.DataValues, interpolable bool) Type {
   514  	dtype := Type{
   515  		Type: datastore.Type{
   516  			Requirements: &storage.Requirements{Batcher: true},
   517  		},
   518  		values:       values,
   519  		interpolable: interpolable,
   520  	}
   521  	return dtype
   522  }
   523  
   524  // NewData returns a pointer to a new Voxels with default values.
   525  func (dtype *Type) NewData(uuid dvid.UUID, id dvid.InstanceID, name dvid.InstanceName, c dvid.Config) (*Data, error) {
   526  	dvid.Infof("NewData on name %q\n", name)
   527  	basedata, err := datastore.NewDataService(dtype, uuid, id, name, c)
   528  	if err != nil {
   529  		return nil, err
   530  	}
   531  	var p Properties
   532  	p.setDefault(dtype.values, dtype.interpolable)
   533  	if err := p.setByConfig(c); err != nil {
   534  		return nil, err
   535  	}
   536  	if p.GridStore != "" {
   537  		gridProps, err := getGridProperties(p.GridStore, p.ScaleLevel)
   538  		if err != nil {
   539  			return nil, err
   540  		}
   541  		dvid.Infof("Got properties for scale %d of GridStore %q: %v\n", p.ScaleLevel, p.GridStore, gridProps)
   542  		p.MinPoint = dvid.Point3d{0, 0, 0}
   543  		p.MaxPoint = gridProps.VolumeSize
   544  		p.Resolution.Set3dNanometersFloat(gridProps.Resolution)
   545  		p.BlockSize = gridProps.ChunkSize
   546  	}
   547  	data := &Data{
   548  		Data:       basedata,
   549  		Properties: p,
   550  	}
   551  	dvid.Infof("Data props: %v\n", p)
   552  
   553  	return data, nil
   554  }
   555  
   556  // --- TypeService interface ---
   557  
   558  // NewDataService returns a pointer to a new Voxels with default values.
   559  func (dtype *Type) NewDataService(uuid dvid.UUID, id dvid.InstanceID, name dvid.InstanceName, c dvid.Config) (datastore.DataService, error) {
   560  	return dtype.NewData(uuid, id, name, c)
   561  }
   562  
   563  func (dtype *Type) Help() string {
   564  	return fmt.Sprintf(helpMessage, DefaultBlockSize, DefaultRes)
   565  }
   566  
   567  type bulkLoadInfo struct {
   568  	filenames     []string
   569  	versionID     dvid.VersionID
   570  	offset        dvid.Point
   571  	extentChanged dvid.Bool
   572  }
   573  
   574  // Voxels represents subvolumes or slices and implements the ExtData interface.
   575  type Voxels struct {
   576  	dvid.Geometry
   577  
   578  	values dvid.DataValues
   579  
   580  	// The data itself
   581  	data []byte
   582  
   583  	// The stride for 2d iteration in bytes between vertically adjacent pixels.
   584  	// For 3d subvolumes, we don't reuse standard Go images but maintain fully
   585  	// packed data slices, so stride isn't necessary.
   586  	stride int32
   587  }
   588  
   589  func NewVoxels(geom dvid.Geometry, values dvid.DataValues, data []byte, stride int32) *Voxels {
   590  	return &Voxels{geom, values, data, stride}
   591  }
   592  
   593  func (v *Voxels) String() string {
   594  	size := v.Size()
   595  	return fmt.Sprintf("%s of size %s @ %s", v.DataShape(), size, v.StartPoint())
   596  }
   597  
   598  func (v *Voxels) Values() dvid.DataValues {
   599  	return v.values
   600  }
   601  
   602  func (v *Voxels) Data() []byte {
   603  	return v.data
   604  }
   605  
   606  func (v *Voxels) Stride() int32 {
   607  	return v.stride
   608  }
   609  
   610  func (v *Voxels) BytesPerVoxel() int32 {
   611  	return v.values.BytesPerElement()
   612  }
   613  
   614  func (v *Voxels) SetGeometry(geom dvid.Geometry) {
   615  	v.Geometry = geom
   616  }
   617  
   618  func (v *Voxels) SetValues(values dvid.DataValues) {
   619  	v.values = values
   620  }
   621  
   622  func (v *Voxels) SetStride(stride int32) {
   623  	v.stride = stride
   624  }
   625  
   626  func (v *Voxels) SetData(data []byte) {
   627  	v.data = data
   628  }
   629  
   630  // -------  ExtData interface implementation -------------
   631  
   632  func (v *Voxels) NewChunkIndex() dvid.ChunkIndexer {
   633  	return &dvid.IndexZYX{}
   634  }
   635  
   636  func (v *Voxels) Interpolable() bool {
   637  	return true
   638  }
   639  
   640  // DownRes downsamples 2d Voxels data by averaging where the down-magnification are
   641  // integers.  If the source image size in Voxels is not an integral multiple of the
   642  // reduction factor, the edge voxels on the right and bottom side are truncated.
   643  // This function modifies the Voxels data.  An error is returned if a non-2d Voxels
   644  // receiver is used.
   645  func (v *Voxels) DownRes(magnification dvid.Point) error {
   646  	if v.DataShape().ShapeDimensions() != 2 {
   647  		return fmt.Errorf("ImageDownres() only supports 2d images at this time.")
   648  	}
   649  	// Calculate new dimensions and allocate.
   650  	srcW := v.Size().Value(0)
   651  	srcH := v.Size().Value(1)
   652  	reduceW, reduceH, err := v.DataShape().GetSize2D(magnification)
   653  	if err != nil {
   654  		return err
   655  	}
   656  	dstW := srcW / reduceW
   657  	dstH := srcH / reduceH
   658  
   659  	// Reduce the image.
   660  	img, err := v.GetImage2d()
   661  	if err != nil {
   662  		return err
   663  	}
   664  	img, err = img.ScaleImage(int(dstW), int(dstH))
   665  	if err != nil {
   666  		return err
   667  	}
   668  
   669  	// Set data and dimensions to downres data.
   670  	v.data = []byte(img.Data())
   671  	geom, err := dvid.NewOrthogSlice(v.DataShape(), v.StartPoint(), dvid.Point2d{dstW, dstH})
   672  	if err != nil {
   673  		return err
   674  	}
   675  	v.Geometry = geom
   676  	v.stride = dstW * v.values.BytesPerElement()
   677  	return nil
   678  }
   679  
   680  // NewIndexIterator returns an iterator that can move across the voxel geometry,
   681  // generating indices or index spans.
   682  func (v *Voxels) NewIndexIterator(chunkSize dvid.Point) (dvid.IndexIterator, error) {
   683  	// Setup traversal
   684  	begVoxel, ok := v.StartPoint().(dvid.Chunkable)
   685  	if !ok {
   686  		return nil, fmt.Errorf("ExtData StartPoint() cannot handle Chunkable points.")
   687  	}
   688  	endVoxel, ok := v.EndPoint().(dvid.Chunkable)
   689  	if !ok {
   690  		return nil, fmt.Errorf("ExtData EndPoint() cannot handle Chunkable points.")
   691  	}
   692  	begBlock := begVoxel.Chunk(chunkSize).(dvid.ChunkPoint3d)
   693  	endBlock := endVoxel.Chunk(chunkSize).(dvid.ChunkPoint3d)
   694  
   695  	return dvid.NewIndexZYXIterator(begBlock, endBlock), nil
   696  }
   697  
   698  // GetImage2d returns a 2d image suitable for use external to DVID.
   699  // TODO -- Create more comprehensive handling of endianness and encoding of
   700  // multibytes/voxel data into appropriate images.
   701  func (v *Voxels) GetImage2d() (*dvid.Image, error) {
   702  	// Make sure each value has same # of bytes or else we can't generate a go image.
   703  	// If so, we need to make another ExtData that knows how to convert the varying
   704  	// values into an appropriate go image.
   705  	valuesPerVoxel := int32(len(v.values))
   706  	if valuesPerVoxel < 1 || valuesPerVoxel > 4 {
   707  		return nil, fmt.Errorf("Standard voxels type can't convert %d values/voxel into image.",
   708  			valuesPerVoxel)
   709  	}
   710  	bytesPerValue := v.values.ValueBytes(0)
   711  	for _, dataValue := range v.values {
   712  		if dvid.DataTypeBytes(dataValue.T) != bytesPerValue {
   713  			return nil, fmt.Errorf("Standard voxels type can't handle varying sized values per voxel.")
   714  		}
   715  	}
   716  
   717  	unsupported := func() error {
   718  		return fmt.Errorf("DVID doesn't support images for %d channels and %d bytes/channel",
   719  			valuesPerVoxel, bytesPerValue)
   720  	}
   721  
   722  	var img image.Image
   723  	width := v.Size().Value(0)
   724  	height := v.Size().Value(1)
   725  	sliceBytes := width * height * valuesPerVoxel * bytesPerValue
   726  	beg := int32(0)
   727  	end := beg + sliceBytes
   728  	data := v.Data()
   729  	if int(end) > len(data) {
   730  		return nil, fmt.Errorf("Voxels %s has insufficient amount of data to return an image.", v)
   731  	}
   732  	r := image.Rect(0, 0, int(width), int(height))
   733  	switch valuesPerVoxel {
   734  	case 1:
   735  		switch bytesPerValue {
   736  		case 1:
   737  			img = &image.Gray{data[beg:end], 1 * r.Dx(), r}
   738  		case 2:
   739  			bigendian, err := v.littleToBigEndian(data[beg:end])
   740  			if err != nil {
   741  				return nil, err
   742  			}
   743  			img = &image.Gray16{bigendian, 2 * r.Dx(), r}
   744  		case 4:
   745  			img = &image.NRGBA{data[beg:end], 4 * r.Dx(), r}
   746  		case 8:
   747  			img = &image.NRGBA64{data[beg:end], 8 * r.Dx(), r}
   748  		default:
   749  			return nil, unsupported()
   750  		}
   751  	case 4:
   752  		switch bytesPerValue {
   753  		case 1:
   754  			img = &image.NRGBA{data[beg:end], 4 * r.Dx(), r}
   755  		case 2:
   756  			img = &image.NRGBA64{data[beg:end], 8 * r.Dx(), r}
   757  		default:
   758  			return nil, unsupported()
   759  		}
   760  	default:
   761  		return nil, unsupported()
   762  	}
   763  
   764  	return dvid.ImageFromGoImage(img, v.Values(), v.Interpolable())
   765  }
   766  
   767  // Properties are additional properties for image block data instances beyond those
   768  // in standard datastore.Data.   These will be persisted to metadata storage.
   769  type Properties struct {
   770  	// Values describes the data type/label for each value within a voxel.
   771  	Values dvid.DataValues
   772  
   773  	// Interpolable is true if voxels can be interpolated when resizing.
   774  	Interpolable bool
   775  
   776  	// Block size for this repo
   777  	BlockSize dvid.Point
   778  
   779  	dvid.Resolution
   780  
   781  	// leave field in metadata but no longer updated!!
   782  	dvid.Extents
   783  
   784  	// Background value for data
   785  	Background uint8
   786  
   787  	// GridStore designates store to be used for immutable data access
   788  	GridStore storage.Alias
   789  
   790  	// ScaleLevel designates resolution from 0 (high-res) to an int N with 2^N down-res
   791  	ScaleLevel int
   792  }
   793  
   794  // getGridProperties returns the properties of a GridStore
   795  func getGridProperties(storeName storage.Alias, scale int) (props storage.GridProps, err error) {
   796  	if storeName == "" {
   797  		err = fmt.Errorf("cannot get GridStore properties for a blank name")
   798  		return
   799  	}
   800  	var store dvid.Store
   801  	store, err = storage.GetStoreByAlias(storeName)
   802  	if err != nil {
   803  		return
   804  	}
   805  	gridStore, ok := store.(storage.GridStoreGetter)
   806  	if !ok {
   807  		err = fmt.Errorf("GridStore %q is not valid", storeName)
   808  		return
   809  	}
   810  	return gridStore.GridProperties(scale)
   811  }
   812  
   813  // gridStoreGetter either returns a gridStore or (okvDB, kvDB) as fallback, not both.
   814  func (d *Data) gridStoreGetter() (gridStore storage.GridStoreGetter, okvDB storage.OrderedKeyValueDB, kvDB storage.KeyValueDB, err error) {
   815  	if d.GridStore != "" {
   816  		var store dvid.Store
   817  		store, err = storage.GetStoreByAlias(d.GridStore)
   818  		if err == nil {
   819  			var ok bool
   820  			gridStore, ok = store.(storage.GridStoreGetter)
   821  			if ok {
   822  				return
   823  			}
   824  			gridStore = nil
   825  			dvid.Infof("Found gridstore %q but was not a GridStoreGetter, defaulting...\n", d.GridStore)
   826  		}
   827  	}
   828  	okvDB, err = datastore.GetOrderedKeyValueDB(d)
   829  	if err != nil {
   830  		return
   831  	}
   832  	kvDB, err = datastore.GetKeyValueDB(d)
   833  	if err != nil {
   834  		return
   835  	}
   836  	return
   837  }
   838  
   839  func (d *Data) PropertiesWithExtents(ctx *datastore.VersionedCtx) (props Properties, err error) {
   840  	var verExtents dvid.Extents
   841  	verExtents, err = d.GetExtents(ctx)
   842  	if err != nil {
   843  		return
   844  	}
   845  	props.Values = d.Properties.Values
   846  	props.Interpolable = d.Properties.Interpolable
   847  	props.BlockSize = d.Properties.BlockSize
   848  	props.Resolution = d.Properties.Resolution
   849  
   850  	props.Extents.MinPoint = verExtents.MinPoint
   851  	props.Extents.MaxPoint = verExtents.MaxPoint
   852  	props.Extents.MinIndex = verExtents.MinIndex
   853  	props.Extents.MaxIndex = verExtents.MaxIndex
   854  	props.Background = d.Properties.Background
   855  	props.GridStore = d.Properties.GridStore
   856  	props.ScaleLevel = d.Properties.ScaleLevel
   857  	return
   858  }
   859  
   860  // CopyPropertiesFrom copies the data instance-specific properties from a given
   861  // data instance into the receiver's properties. Fulfills the datastore.PropertyCopier interface.
   862  func (d *Data) CopyPropertiesFrom(src datastore.DataService, fs storage.FilterSpec) error {
   863  	d2, ok := src.(*Data)
   864  	if !ok {
   865  		return fmt.Errorf("unable to copy properties from non-imageblk data %q", src.DataName())
   866  	}
   867  	d.Properties.copyImmutable(&(d2.Properties))
   868  
   869  	// TODO -- Extents are no longer used so this should be refactored
   870  	d.Properties.Extents = d2.Properties.Extents.Duplicate()
   871  	return nil
   872  }
   873  
   874  func (p *Properties) copyImmutable(p2 *Properties) {
   875  	p.Values = make([]dvid.DataValue, len(p2.Values))
   876  	copy(p.Values, p2.Values)
   877  	p.Interpolable = p2.Interpolable
   878  
   879  	p.BlockSize = p2.BlockSize.Duplicate()
   880  
   881  	p.Resolution.VoxelSize = make(dvid.NdFloat32, 3)
   882  	copy(p.Resolution.VoxelSize, p2.Resolution.VoxelSize)
   883  	p.Resolution.VoxelUnits = make(dvid.NdString, 3)
   884  	copy(p.Resolution.VoxelUnits, p2.Resolution.VoxelUnits)
   885  
   886  	p.Background = p2.Background
   887  	p.GridStore = p2.GridStore
   888  	p.ScaleLevel = p2.ScaleLevel
   889  }
   890  
   891  // setDefault sets Voxels properties to default values.
   892  func (p *Properties) setDefault(values dvid.DataValues, interpolable bool) error {
   893  	p.Values = make([]dvid.DataValue, len(values))
   894  	copy(p.Values, values)
   895  	p.Interpolable = interpolable
   896  
   897  	dimensions := 3
   898  	size := make([]int32, dimensions)
   899  	for d := 0; d < dimensions; d++ {
   900  		size[d] = DefaultBlockSize
   901  	}
   902  	var err error
   903  	p.BlockSize, err = dvid.NewPoint(size)
   904  	if err != nil {
   905  		return err
   906  	}
   907  	p.Resolution.VoxelSize = make(dvid.NdFloat32, dimensions)
   908  	for d := 0; d < dimensions; d++ {
   909  		p.Resolution.VoxelSize[d] = DefaultRes
   910  	}
   911  	p.Resolution.VoxelUnits = make(dvid.NdString, dimensions)
   912  	for d := 0; d < dimensions; d++ {
   913  		p.Resolution.VoxelUnits[d] = DefaultUnits
   914  	}
   915  	return nil
   916  }
   917  
   918  // setByConfig sets Voxels properties based on type-specific keywords in the configuration.
   919  // Any property not described in the config is left as is.  See the Voxels help for listing
   920  // of configurations.
   921  func (p *Properties) setByConfig(config dvid.Config) error {
   922  	s, found, err := config.GetString("BlockSize")
   923  	if err != nil {
   924  		return err
   925  	}
   926  	if found {
   927  		p.BlockSize, err = dvid.StringToPoint(s, ",")
   928  		if err != nil {
   929  			return err
   930  		}
   931  	}
   932  	s, found, err = config.GetString("VoxelSize")
   933  	if err != nil {
   934  		return err
   935  	}
   936  	if found {
   937  		dvid.Infof("Changing resolution of voxels to %s\n", s)
   938  		p.Resolution.VoxelSize, err = dvid.StringToNdFloat32(s, ",")
   939  		if err != nil {
   940  			return err
   941  		}
   942  	}
   943  	s, found, err = config.GetString("VoxelUnits")
   944  	if err != nil {
   945  		return err
   946  	}
   947  	if found {
   948  		p.Resolution.VoxelUnits, err = dvid.StringToNdString(s, ",")
   949  		if err != nil {
   950  			return err
   951  		}
   952  	}
   953  	s, found, err = config.GetString("MinPoint")
   954  	if err != nil {
   955  		return err
   956  	}
   957  	if found {
   958  		p.MinPoint, err = dvid.StringToPoint(s, ",")
   959  		if err != nil {
   960  			return err
   961  		}
   962  	}
   963  	s, found, err = config.GetString("MaxPoint")
   964  	if err != nil {
   965  		return err
   966  	}
   967  	if found {
   968  		p.MaxPoint, err = dvid.StringToPoint(s, ",")
   969  		if err != nil {
   970  			return err
   971  		}
   972  	}
   973  	s, found, err = config.GetString("Background")
   974  	if err != nil {
   975  		return err
   976  	}
   977  	if found {
   978  		background, err := strconv.ParseUint(s, 10, 8)
   979  		if err != nil {
   980  			return err
   981  		}
   982  		p.Background = uint8(background)
   983  	}
   984  	s, found, err = config.GetString("GridStore")
   985  	if err != nil {
   986  		return err
   987  	}
   988  	if found {
   989  		gridStore := storage.Alias(s)
   990  		if _, err := storage.GetStoreByAlias(gridStore); err != nil {
   991  			return fmt.Errorf("bad store (%s) designated for GridStore: %v", s, err)
   992  		}
   993  		p.GridStore = gridStore
   994  	}
   995  	s, found, err = config.GetString("ScaleLevel")
   996  	if err != nil {
   997  		return err
   998  	}
   999  	if found {
  1000  		scale, err := strconv.Atoi(s)
  1001  		if err != nil {
  1002  			return err
  1003  		}
  1004  		p.ScaleLevel = scale
  1005  	}
  1006  	return nil
  1007  }
  1008  
  1009  type metadataT struct {
  1010  	Axes       []axisT
  1011  	Properties Properties
  1012  }
  1013  
  1014  type axisT struct {
  1015  	Label      string
  1016  	Resolution float32
  1017  	Units      string
  1018  	Size       int32
  1019  	Offset     int32
  1020  }
  1021  
  1022  // NdDataSchema returns the metadata in JSON for this Data
  1023  func (d *Data) NdDataMetadata(ctx *datastore.VersionedCtx) (string, error) {
  1024  	var err error
  1025  	var size, offset dvid.Point
  1026  
  1027  	dims := int(d.BlockSize().NumDims())
  1028  	extents, err := d.GetExtents(ctx)
  1029  	if err != nil {
  1030  		return "", err
  1031  	}
  1032  	if extents.MinPoint == nil || extents.MaxPoint == nil {
  1033  		zeroPt := make([]int32, dims)
  1034  		size, err = dvid.NewPoint(zeroPt)
  1035  		if err != nil {
  1036  			return "", err
  1037  		}
  1038  		offset = size
  1039  	} else {
  1040  		size = extents.MaxPoint.Sub(extents.MinPoint).AddScalar(1)
  1041  		offset = extents.MinPoint
  1042  	}
  1043  
  1044  	var axesName = []string{"X", "Y", "Z", "t", "c"}
  1045  	var metadata metadataT
  1046  	metadata.Axes = []axisT{}
  1047  	for dim := 0; dim < dims; dim++ {
  1048  		metadata.Axes = append(metadata.Axes, axisT{
  1049  			Label:      axesName[dim],
  1050  			Resolution: d.Properties.Resolution.VoxelSize[dim],
  1051  			Units:      d.Properties.Resolution.VoxelUnits[dim],
  1052  			Size:       size.Value(uint8(dim)),
  1053  			Offset:     offset.Value(uint8(dim)),
  1054  		})
  1055  	}
  1056  	metadata.Properties, err = d.PropertiesWithExtents(ctx)
  1057  	if err != nil {
  1058  		return "", err
  1059  	}
  1060  
  1061  	m, err := json.Marshal(metadata)
  1062  	if err != nil {
  1063  		return "", err
  1064  	}
  1065  	return string(m), nil
  1066  }
  1067  
  1068  // serializeExtents takes extents structure and serializes and compresses it
  1069  func (d *Data) serializeExtents(extents ExtentsJSON) ([]byte, error) {
  1070  
  1071  	jsonbytes, err := json.Marshal(extents)
  1072  	if err != nil {
  1073  		return nil, err
  1074  	}
  1075  
  1076  	compression, _ := dvid.NewCompression(dvid.Uncompressed, dvid.DefaultCompression)
  1077  	return dvid.SerializeData(jsonbytes, compression, dvid.NoChecksum)
  1078  
  1079  }
  1080  
  1081  // serializeExtents takes extents structure and serializes and compresses it
  1082  func (d *Data) deserializeExtents(serdata []byte) (ExtentsJSON, error) {
  1083  
  1084  	// return blank extents if nothing set
  1085  	var extents ExtentsJSON
  1086  	if serdata == nil || len(serdata) == 0 {
  1087  		return extents, nil
  1088  	}
  1089  
  1090  	// deserialize
  1091  	data, _, err := dvid.DeserializeData(serdata, true)
  1092  	if err != nil {
  1093  		return extents, err
  1094  	}
  1095  
  1096  	// unmarshal (hardcode to 3D for now!)
  1097  	var extentstemp extents3D
  1098  	if err = json.Unmarshal(data, &extentstemp); err != nil {
  1099  		return extents, err
  1100  	}
  1101  	extents.MinPoint = extentstemp.MinPoint
  1102  	extents.MaxPoint = extentstemp.MaxPoint
  1103  
  1104  	return extents, nil
  1105  }
  1106  
  1107  // SetExtents saves JSON data giving MinPoint and MaxPoint (put operation)
  1108  func (d *Data) SetExtents(ctx *datastore.VersionedCtx, uuid dvid.UUID, jsonBytes []byte) error {
  1109  	// unmarshal (hardcode to 3D for now!)
  1110  	var config extents3D
  1111  	var config2 ExtentsJSON
  1112  
  1113  	if err := json.Unmarshal(jsonBytes, &config); err != nil {
  1114  		return err
  1115  	}
  1116  
  1117  	//  call serialize
  1118  	config2.MinPoint = config.MinPoint
  1119  	config2.MaxPoint = config.MaxPoint
  1120  	data, err := d.serializeExtents(config2)
  1121  	if err != nil {
  1122  		return err
  1123  	}
  1124  
  1125  	// put (last one wins)
  1126  	store, err := datastore.GetKeyValueDB(d)
  1127  	if err != nil {
  1128  		return err
  1129  	}
  1130  	return store.Put(ctx, MetaTKey(), data)
  1131  }
  1132  
  1133  // SetResolution loads JSON data giving Resolution.
  1134  func (d *Data) SetResolution(uuid dvid.UUID, jsonBytes []byte) error {
  1135  	config := make(dvid.NdFloat32, 3)
  1136  	if err := json.Unmarshal(jsonBytes, &config); err != nil {
  1137  		return err
  1138  	}
  1139  	d.Properties.VoxelSize = config
  1140  	if err := datastore.SaveDataByUUID(uuid, d); err != nil {
  1141  		return err
  1142  	}
  1143  	return nil
  1144  }
  1145  
  1146  // ExtentsJSON is extents encoding for imageblk
  1147  type ExtentsJSON struct {
  1148  	MinPoint dvid.Point
  1149  	MaxPoint dvid.Point
  1150  }
  1151  
  1152  type extents3D struct {
  1153  	MinPoint dvid.Point3d
  1154  	MaxPoint dvid.Point3d
  1155  }
  1156  
  1157  // Data embeds the datastore's Data and extends it with voxel-specific properties.
  1158  type Data struct {
  1159  	*datastore.Data
  1160  	Properties
  1161  	sync.Mutex // to protect extent updates
  1162  }
  1163  
  1164  func (d *Data) Equals(d2 *Data) bool {
  1165  	if !d.Data.Equals(d2.Data) {
  1166  		return false
  1167  	}
  1168  	return reflect.DeepEqual(d.Properties, d2.Properties)
  1169  }
  1170  
  1171  // BlankImage initializes a blank image of appropriate size and depth for the
  1172  // current data values.  Returns an error if the geometry is not 2d.
  1173  func (d *Data) BlankImage(dstW, dstH int32) (*dvid.Image, error) {
  1174  	// Make sure values for this data can be converted into an image.
  1175  	valuesPerVoxel := int32(len(d.Properties.Values))
  1176  	if valuesPerVoxel < 1 || valuesPerVoxel > 4 {
  1177  		return nil, fmt.Errorf("Standard voxels type can't convert %d values/voxel into image.",
  1178  			valuesPerVoxel)
  1179  	}
  1180  	bytesPerValue, err := d.Properties.Values.BytesPerValue()
  1181  	if err != nil {
  1182  		return nil, err
  1183  	}
  1184  
  1185  	unsupported := func() error {
  1186  		return fmt.Errorf("DVID doesn't support images for %d channels and %d bytes/channel",
  1187  			valuesPerVoxel, bytesPerValue)
  1188  	}
  1189  
  1190  	var img image.Image
  1191  	stride := int(dstW * valuesPerVoxel * bytesPerValue)
  1192  	r := image.Rect(0, 0, int(dstW), int(dstH))
  1193  	imageBytes := int(dstH) * stride
  1194  	data := make([]uint8, imageBytes, imageBytes)
  1195  	switch valuesPerVoxel {
  1196  	case 1:
  1197  		switch bytesPerValue {
  1198  		case 1:
  1199  			img = &image.Gray{
  1200  				Stride: stride,
  1201  				Rect:   r,
  1202  				Pix:    data,
  1203  			}
  1204  		case 2:
  1205  			img = &image.Gray16{
  1206  				Stride: stride,
  1207  				Rect:   r,
  1208  				Pix:    data,
  1209  			}
  1210  		case 4:
  1211  			img = &image.NRGBA{
  1212  				Stride: stride,
  1213  				Rect:   r,
  1214  				Pix:    data,
  1215  			}
  1216  		case 8:
  1217  			img = &image.NRGBA64{
  1218  				Stride: stride,
  1219  				Rect:   r,
  1220  				Pix:    data,
  1221  			}
  1222  		default:
  1223  			return nil, unsupported()
  1224  		}
  1225  	case 4:
  1226  		switch bytesPerValue {
  1227  		case 1:
  1228  			img = &image.NRGBA{
  1229  				Stride: stride,
  1230  				Rect:   r,
  1231  				Pix:    data,
  1232  			}
  1233  		case 2:
  1234  			img = &image.NRGBA64{
  1235  				Stride: stride,
  1236  				Rect:   r,
  1237  				Pix:    data,
  1238  			}
  1239  		default:
  1240  			return nil, unsupported()
  1241  		}
  1242  	default:
  1243  		return nil, unsupported()
  1244  	}
  1245  
  1246  	return dvid.ImageFromGoImage(img, d.Properties.Values, d.Properties.Interpolable)
  1247  }
  1248  
  1249  // PutLocal adds image data to a version node, altering underlying blocks if the image
  1250  // intersects the block.
  1251  //
  1252  // The image filename glob MUST BE absolute file paths that are visible to the server.
  1253  // This function is meant for mass ingestion of large data files, and it is inappropriate
  1254  // to read gigabytes of data just to send it over the network to a local DVID.
  1255  func (d *Data) PutLocal(request datastore.Request, reply *datastore.Response) error {
  1256  	timedLog := dvid.NewTimeLog()
  1257  
  1258  	// Parse the request
  1259  	var uuidStr, dataName, cmdStr, sourceStr, planeStr, offsetStr string
  1260  	filenames := request.CommandArgs(1, &uuidStr, &dataName, &cmdStr, &sourceStr,
  1261  		&planeStr, &offsetStr)
  1262  	if len(filenames) == 0 {
  1263  		return fmt.Errorf("Need to include at least one file to add: %s", request)
  1264  	}
  1265  
  1266  	// Get offset
  1267  	offset, err := dvid.StringToPoint(offsetStr, ",")
  1268  	if err != nil {
  1269  		return fmt.Errorf("Illegal offset specification: %s: %v", offsetStr, err)
  1270  	}
  1271  
  1272  	// Get list of files to add
  1273  	var addedFiles string
  1274  	if len(filenames) == 1 {
  1275  		addedFiles = filenames[0]
  1276  	} else {
  1277  		addedFiles = fmt.Sprintf("filenames: %s [%d more]", filenames[0], len(filenames)-1)
  1278  	}
  1279  	dvid.Debugf(addedFiles + "\n")
  1280  
  1281  	// Get plane
  1282  	plane, err := dvid.DataShapeString(planeStr).DataShape()
  1283  	if err != nil {
  1284  		return err
  1285  	}
  1286  
  1287  	// Get Repo and IDs
  1288  	uuid, versionID, err := datastore.MatchingUUID(uuidStr)
  1289  	if err != nil {
  1290  		return err
  1291  	}
  1292  
  1293  	// Load and put each image.
  1294  	numSuccessful := 0
  1295  	for _, filename := range filenames {
  1296  		sliceLog := dvid.NewTimeLog()
  1297  		img, _, err := dvid.GoImageFromFile(filename)
  1298  		if err != nil {
  1299  			return fmt.Errorf("Error after %d images successfully added: %v", numSuccessful, err)
  1300  		}
  1301  		slice, err := dvid.NewOrthogSlice(plane, offset, dvid.RectSize(img.Bounds()))
  1302  		if err != nil {
  1303  			return fmt.Errorf("Unable to determine slice: %v", err)
  1304  		}
  1305  
  1306  		vox, err := d.NewVoxels(slice, img)
  1307  		if err != nil {
  1308  			return err
  1309  		}
  1310  		storage.FileBytesRead <- len(vox.Data())
  1311  		if err = d.IngestVoxels(versionID, d.NewMutationID(), vox, ""); err != nil {
  1312  			return err
  1313  		}
  1314  		sliceLog.Debugf("%s put local %s", d.DataName(), slice)
  1315  		numSuccessful++
  1316  		offset = offset.Add(dvid.Point3d{0, 0, 1})
  1317  	}
  1318  
  1319  	if err := datastore.AddToNodeLog(uuid, []string{request.Command.String()}); err != nil {
  1320  		return err
  1321  	}
  1322  	timedLog.Infof("RPC put local (%s) completed", addedFiles)
  1323  	return nil
  1324  }
  1325  
  1326  // NewVoxels returns Voxels with given geometry and optional image data.
  1327  // If img is passed in, the function will initialize the Voxels with data from the image.
  1328  // Otherwise, it will allocate a zero buffer of appropriate size.
  1329  func (d *Data) NewVoxels(geom dvid.Geometry, img interface{}) (*Voxels, error) {
  1330  	bytesPerVoxel := d.Properties.Values.BytesPerElement()
  1331  	stride := geom.Size().Value(0) * bytesPerVoxel
  1332  
  1333  	voxels := &Voxels{
  1334  		Geometry: geom,
  1335  		values:   d.Properties.Values,
  1336  		stride:   stride,
  1337  	}
  1338  
  1339  	if img == nil {
  1340  		numVoxels := geom.NumVoxels()
  1341  		if numVoxels <= 0 {
  1342  			return nil, fmt.Errorf("Illegal geometry requested: %s", geom)
  1343  		}
  1344  		requestSize := int64(bytesPerVoxel) * numVoxels
  1345  		if requestSize > server.MaxDataRequest {
  1346  			return nil, fmt.Errorf("Requested payload (%d bytes) exceeds this DVID server's set limit (%d)",
  1347  				requestSize, server.MaxDataRequest)
  1348  		}
  1349  		voxels.data = make([]uint8, requestSize)
  1350  	} else {
  1351  		switch t := img.(type) {
  1352  		case image.Image:
  1353  			var actualStride int32
  1354  			var err error
  1355  			voxels.data, _, actualStride, err = dvid.ImageData(t)
  1356  			if err != nil {
  1357  				return nil, err
  1358  			}
  1359  			if actualStride < stride {
  1360  				return nil, fmt.Errorf("Too little data in input image (expected stride %d)", stride)
  1361  			}
  1362  			voxels.stride = actualStride
  1363  		case []byte:
  1364  			voxels.data = t
  1365  			actualLen := int64(len(voxels.data))
  1366  			expectedLen := int64(bytesPerVoxel) * geom.NumVoxels()
  1367  			if actualLen != expectedLen {
  1368  				return nil, fmt.Errorf("voxels data was %d bytes, expected %d bytes for %s",
  1369  					actualLen, expectedLen, geom)
  1370  			}
  1371  		default:
  1372  			return nil, fmt.Errorf("Unexpected image type given to NewVoxels(): %T", t)
  1373  		}
  1374  	}
  1375  	return voxels, nil
  1376  }
  1377  
  1378  func (d *Data) BlockSize() dvid.Point {
  1379  	return d.Properties.BlockSize
  1380  }
  1381  
  1382  // GetExtents retrieves current extent (and updates extents cache)
  1383  // TODO -- refactor return since MinIndex / MaxIndex not used so should use extents3d.
  1384  func (d *Data) GetExtents(ctx *datastore.VersionedCtx) (dvidextents dvid.Extents, err error) {
  1385  	var extents ExtentsJSON
  1386  	// actually fetch extents from datatype storage
  1387  	var store storage.KeyValueDB
  1388  	if store, err = datastore.GetKeyValueDB(d); err != nil {
  1389  		return
  1390  	}
  1391  	var serialization []byte
  1392  	if serialization, err = store.Get(ctx, MetaTKey()); err != nil {
  1393  		return
  1394  	}
  1395  	if extents, err = d.deserializeExtents(serialization); err != nil {
  1396  		return
  1397  	}
  1398  	if extents.MinPoint == nil || extents.MaxPoint == nil {
  1399  		// assume this is old dataset and try to use values under Properties.
  1400  		if d.Properties.MinPoint == nil || d.Properties.MaxPoint == nil {
  1401  			return
  1402  		}
  1403  		dvidextents.MinPoint = d.Properties.MinPoint
  1404  		dvidextents.MaxPoint = d.Properties.MaxPoint
  1405  		extents.MinPoint = d.Properties.MinPoint
  1406  		extents.MaxPoint = d.Properties.MaxPoint
  1407  	} else {
  1408  		dvidextents.MinPoint = extents.MinPoint
  1409  		dvidextents.MaxPoint = extents.MaxPoint
  1410  	}
  1411  
  1412  	// derive corresponding block coordinate
  1413  	blockSize, ok := d.BlockSize().(dvid.Point3d)
  1414  	if !ok {
  1415  		err = fmt.Errorf("can't get extents for data instance %q when block size %s isn't 3d", d.DataName(), d.BlockSize())
  1416  		return
  1417  	}
  1418  	minPoint, ok := extents.MinPoint.(dvid.Point3d)
  1419  	if !ok {
  1420  		err = fmt.Errorf("can't get 3d point %s for data %q", extents.MinPoint, d.DataName())
  1421  		return
  1422  	}
  1423  	maxPoint, ok := extents.MaxPoint.(dvid.Point3d)
  1424  	if !ok {
  1425  		err = fmt.Errorf("can't get 3d point %s for data %q", extents.MaxPoint, d.DataName())
  1426  		return
  1427  	}
  1428  	dvidextents.MinIndex = minPoint.ChunkIndexer(blockSize)
  1429  	dvidextents.MaxIndex = maxPoint.ChunkIndexer(blockSize)
  1430  	return
  1431  }
  1432  
  1433  // PostExtents updates extents with the new points (always growing)
  1434  func (d *Data) PostExtents(ctx *datastore.VersionedCtx, start dvid.Point, end dvid.Point) error {
  1435  	store, err := datastore.GetOrderedKeyValueDB(d)
  1436  	if err != nil {
  1437  		return err
  1438  	}
  1439  	// lock datatype to protect get/put
  1440  	d.Lock()
  1441  	defer d.Unlock()
  1442  
  1443  	// retrieve extents
  1444  	data, err := store.Get(ctx, MetaTKey())
  1445  	if err != nil {
  1446  		return err
  1447  	}
  1448  
  1449  	extentsjson, err := d.deserializeExtents(data)
  1450  	if err != nil {
  1451  		return err
  1452  	}
  1453  
  1454  	// update extents if necessary
  1455  	var extents dvid.Extents
  1456  	extents.MinPoint = extentsjson.MinPoint
  1457  	extents.MaxPoint = extentsjson.MaxPoint
  1458  
  1459  	if mod := extents.AdjustPoints(start, end); mod {
  1460  		// serialize extents
  1461  		extentsjson.MinPoint = extents.MinPoint
  1462  		extentsjson.MaxPoint = extents.MaxPoint
  1463  		ser_extents, err := d.serializeExtents(extentsjson)
  1464  		if err != nil {
  1465  			return err
  1466  		}
  1467  
  1468  		// !! update extents only if a non-distributed dvid
  1469  		// TODO: remove this
  1470  		d.Extents = extents
  1471  		err = datastore.SaveDataByVersion(ctx.VersionID(), d)
  1472  		if err != nil {
  1473  			dvid.Infof("Error in trying to save repo on change: %v\n", err)
  1474  		}
  1475  
  1476  		// post actual extents
  1477  		return store.Put(ctx, MetaTKey(), ser_extents)
  1478  	}
  1479  	return nil
  1480  }
  1481  
  1482  func (d *Data) Resolution() dvid.Resolution {
  1483  	return d.Properties.Resolution
  1484  }
  1485  
  1486  func (d *Data) String() string {
  1487  	return string(d.DataName())
  1488  }
  1489  
  1490  // MarshalJSON returns a JSON representation of the data assuming that any
  1491  // extents are for the master branch leaf.
  1492  func (d *Data) MarshalJSON() ([]byte, error) {
  1493  	vctx, err := datastore.NewVersionedCtxMasterLeaf(d)
  1494  	if err != nil {
  1495  		return json.Marshal(struct {
  1496  			Base     *datastore.Data
  1497  			Extended Properties
  1498  		}{
  1499  			d.Data,
  1500  			d.Properties,
  1501  		})
  1502  	}
  1503  	return d.MarshalJSONExtents(vctx)
  1504  }
  1505  
  1506  func (d *Data) MarshalJSONExtents(ctx *datastore.VersionedCtx) ([]byte, error) {
  1507  	// grab extent property and load
  1508  	extents, err := d.GetExtents(ctx)
  1509  	if err != nil {
  1510  		return nil, err
  1511  	}
  1512  
  1513  	var extentsJSON ExtentsJSON
  1514  	extentsJSON.MinPoint = extents.MinPoint
  1515  	extentsJSON.MaxPoint = extents.MaxPoint
  1516  
  1517  	props, err := d.PropertiesWithExtents(ctx)
  1518  	if err != nil {
  1519  		return nil, err
  1520  	}
  1521  	return json.Marshal(struct {
  1522  		Base     *datastore.Data
  1523  		Extended Properties
  1524  		Extents  ExtentsJSON
  1525  	}{
  1526  		d.Data,
  1527  		props,
  1528  		extentsJSON,
  1529  	})
  1530  }
  1531  
  1532  func (d *Data) GobDecode(b []byte) error {
  1533  	buf := bytes.NewBuffer(b)
  1534  	dec := gob.NewDecoder(buf)
  1535  	if err := dec.Decode(&(d.Data)); err != nil {
  1536  		return err
  1537  	}
  1538  	if err := dec.Decode(&(d.Properties)); err != nil {
  1539  		return err
  1540  	}
  1541  	return nil
  1542  }
  1543  
  1544  func (d *Data) GobEncode() ([]byte, error) {
  1545  	var buf bytes.Buffer
  1546  	enc := gob.NewEncoder(&buf)
  1547  	if err := enc.Encode(d.Data); err != nil {
  1548  		return nil, err
  1549  	}
  1550  	if err := enc.Encode(d.Properties); err != nil {
  1551  		return nil, err
  1552  	}
  1553  	return buf.Bytes(), nil
  1554  }
  1555  
  1556  // --- DataService interface ---
  1557  
  1558  func (d *Data) Help() string {
  1559  	return fmt.Sprintf(helpMessage, DefaultBlockSize, DefaultRes)
  1560  }
  1561  
  1562  func (d *Data) ModifyConfig(config dvid.Config) error {
  1563  	p := &(d.Properties)
  1564  	if err := p.setByConfig(config); err != nil {
  1565  		return err
  1566  	}
  1567  	return nil
  1568  }
  1569  
  1570  // ForegroundROI creates a new ROI by determining all non-background blocks.
  1571  func (d *Data) ForegroundROI(req datastore.Request, reply *datastore.Response) error {
  1572  	if d.Values.BytesPerElement() != 1 {
  1573  		return fmt.Errorf("Foreground ROI command only implemented for 1 byte/voxel data!")
  1574  	}
  1575  
  1576  	// Parse the request
  1577  	var uuidStr, dataName, cmdStr, destName, backgroundStr string
  1578  	req.CommandArgs(1, &uuidStr, &dataName, &cmdStr, &destName, &backgroundStr)
  1579  
  1580  	// Get the version and repo
  1581  	uuid, versionID, err := datastore.MatchingUUID(uuidStr)
  1582  	if err != nil {
  1583  		return err
  1584  	}
  1585  	if err = datastore.AddToNodeLog(uuid, []string{req.Command.String()}); err != nil {
  1586  		return err
  1587  	}
  1588  
  1589  	// Use existing destination data or a new ROI data.
  1590  	var dest *roi.Data
  1591  	dest, err = roi.GetByUUIDName(uuid, dvid.InstanceName(destName))
  1592  	if err != nil {
  1593  		config := dvid.NewConfig()
  1594  		typeservice, err := datastore.TypeServiceByName("roi")
  1595  		if err != nil {
  1596  			return err
  1597  		}
  1598  		dataservice, err := datastore.NewData(uuid, typeservice, dvid.InstanceName(destName), config)
  1599  		if err != nil {
  1600  			return err
  1601  		}
  1602  		var ok bool
  1603  		dest, ok = dataservice.(*roi.Data)
  1604  		if !ok {
  1605  			return fmt.Errorf("Could not create ROI data instance")
  1606  		}
  1607  	}
  1608  
  1609  	// Asynchronously process the voxels.
  1610  	background, err := dvid.StringToPointNd(backgroundStr, ",")
  1611  	if err != nil {
  1612  		return err
  1613  	}
  1614  	go d.foregroundROI(versionID, dest, background)
  1615  
  1616  	return nil
  1617  }
  1618  
  1619  func (d *Data) foregroundROI(v dvid.VersionID, dest *roi.Data, background dvid.PointNd) {
  1620  	store, err := datastore.GetOrderedKeyValueDB(d)
  1621  	if err != nil {
  1622  		dvid.Criticalf("Data type imageblk had error initializing store: %v\n", err)
  1623  		return
  1624  	}
  1625  
  1626  	timedLog := dvid.NewTimeLog()
  1627  	timedLog.Infof("Starting foreground ROI %q for %s", dest.DataName(), d.DataName())
  1628  	dest.StartUpdate()
  1629  	defer dest.StopUpdate()
  1630  
  1631  	// Iterate through all voxel blocks, loading and then checking blocks
  1632  	// for any foreground voxels.
  1633  	ctx := datastore.NewVersionedCtx(d, v)
  1634  
  1635  	backgroundBytes := make([]byte, len(background))
  1636  	for i, b := range background {
  1637  		backgroundBytes[i] = byte(b)
  1638  	}
  1639  
  1640  	const BATCH_SIZE = 1000
  1641  	var numBatches int
  1642  	var span *dvid.Span
  1643  	spans := []dvid.Span{}
  1644  
  1645  	var f storage.ChunkFunc = func(chunk *storage.Chunk) error {
  1646  		if chunk == nil || chunk.V == nil {
  1647  			return nil
  1648  		}
  1649  		data, _, err := dvid.DeserializeData(chunk.V, true)
  1650  		if err != nil {
  1651  			return fmt.Errorf("Error decoding block: %v\n", err)
  1652  		}
  1653  		numVoxels := d.BlockSize().Prod()
  1654  		var foreground bool
  1655  		for i := int64(0); i < numVoxels; i++ {
  1656  			isBackground := false
  1657  			for _, b := range backgroundBytes {
  1658  				if data[i] == b {
  1659  					isBackground = true
  1660  					break
  1661  				}
  1662  			}
  1663  			if !isBackground {
  1664  				foreground = true
  1665  				break
  1666  			}
  1667  		}
  1668  		if foreground {
  1669  			indexZYX, err := DecodeTKey(chunk.K)
  1670  			if err != nil {
  1671  				return fmt.Errorf("Error decoding voxel block key: %v\n", err)
  1672  			}
  1673  			x, y, z := indexZYX.Unpack()
  1674  			if span == nil {
  1675  				span = &dvid.Span{z, y, x, x}
  1676  			} else if !span.Extends(x, y, z) {
  1677  				spans = append(spans, *span)
  1678  				if len(spans) >= BATCH_SIZE {
  1679  					init := (numBatches == 0)
  1680  					numBatches++
  1681  					go func(spans []dvid.Span) {
  1682  						if err := dest.PutSpans(v, spans, init); err != nil {
  1683  							dvid.Errorf("Error in storing ROI: %v\n", err)
  1684  						} else {
  1685  							timedLog.Debugf("-- Wrote batch %d of spans for foreground ROI %q", numBatches, dest.DataName())
  1686  						}
  1687  					}(spans)
  1688  					spans = []dvid.Span{}
  1689  				}
  1690  				span = &dvid.Span{z, y, x, x}
  1691  			}
  1692  		}
  1693  		server.BlockOnInteractiveRequests("voxels [compute foreground ROI]")
  1694  		return nil
  1695  	}
  1696  
  1697  	minTKey := storage.MinTKey(keyImageBlock)
  1698  	maxTKey := storage.MaxTKey(keyImageBlock)
  1699  
  1700  	err = store.ProcessRange(ctx, minTKey, maxTKey, &storage.ChunkOp{}, f)
  1701  	if err != nil {
  1702  		dvid.Errorf("Error in processing chunks in ROI: %v\n", err)
  1703  		return
  1704  	}
  1705  	if span != nil {
  1706  		spans = append(spans, *span)
  1707  	}
  1708  
  1709  	// Save new ROI
  1710  	if len(spans) > 0 {
  1711  		if err := dest.PutSpans(v, spans, numBatches == 0); err != nil {
  1712  			dvid.Errorf("Error in storing ROI: %v\n", err)
  1713  			return
  1714  		}
  1715  	}
  1716  	timedLog.Infof("Created foreground ROI %q for %s", dest.DataName(), d.DataName())
  1717  }
  1718  
  1719  // DoRPC acts as a switchboard for RPC commands.
  1720  func (d *Data) DoRPC(req datastore.Request, reply *datastore.Response) error {
  1721  	switch req.TypeCommand() {
  1722  	case "load":
  1723  		if len(req.Command) < 5 {
  1724  			return fmt.Errorf("Poorly formatted load command.  See command-line help.")
  1725  		}
  1726  		// Parse the request
  1727  		var uuidStr, dataName, cmdStr, offsetStr string
  1728  		filenames, err := req.FilenameArgs(1, &uuidStr, &dataName, &cmdStr, &offsetStr)
  1729  		if err != nil {
  1730  			return err
  1731  		}
  1732  		if len(filenames) == 0 {
  1733  			hostname, _ := os.Hostname()
  1734  			return fmt.Errorf("Couldn't find any files to add.  Are they visible to DVID server on %s?",
  1735  				hostname)
  1736  		}
  1737  
  1738  		// Get offset
  1739  		offset, err := dvid.StringToPoint(offsetStr, ",")
  1740  		if err != nil {
  1741  			return fmt.Errorf("Illegal offset specification: %s: %v", offsetStr, err)
  1742  		}
  1743  
  1744  		// Get list of files to add
  1745  		var addedFiles string
  1746  		if len(filenames) == 1 {
  1747  			addedFiles = filenames[0]
  1748  		} else {
  1749  			addedFiles = fmt.Sprintf("filenames: %s [%d more]", filenames[0], len(filenames)-1)
  1750  		}
  1751  		dvid.Debugf(addedFiles + "\n")
  1752  
  1753  		uuid, versionID, err := datastore.MatchingUUID(uuidStr)
  1754  		if err != nil {
  1755  			return err
  1756  		}
  1757  		if err = datastore.AddToNodeLog(uuid, []string{req.Command.String()}); err != nil {
  1758  			return err
  1759  		}
  1760  		reply.Text = fmt.Sprintf("Asynchronously loading %d files into data instance %q @ node %s (errors will be printed in server log) ...\n", len(filenames), dataName, uuidStr)
  1761  		go func() {
  1762  			err := d.LoadImages(versionID, offset, filenames)
  1763  			if err != nil {
  1764  				dvid.Errorf("Cannot load images into data instance %q @ node %s: %v\n", dataName, uuidStr, err)
  1765  			}
  1766  		}()
  1767  
  1768  	case "put":
  1769  		if len(req.Command) < 7 {
  1770  			return fmt.Errorf("Poorly formatted put command.  See command-line help.")
  1771  		}
  1772  		source := req.Command[4]
  1773  		switch source {
  1774  		case "local":
  1775  			return d.PutLocal(req, reply)
  1776  		case "remote":
  1777  			return fmt.Errorf("put remote not yet implemented")
  1778  		default:
  1779  			return fmt.Errorf("Unknown command.  Data instance '%s' [%s] does not support '%s' command.",
  1780  				d.DataName(), d.TypeName(), req.TypeCommand())
  1781  		}
  1782  
  1783  	case "roi":
  1784  		if len(req.Command) < 6 {
  1785  			return fmt.Errorf("Poorly formatted roi command. See command-line help.")
  1786  		}
  1787  		return d.ForegroundROI(req, reply)
  1788  
  1789  	default:
  1790  		return fmt.Errorf("Unknown command.  Data instance '%s' [%s] does not support '%s' command.",
  1791  			d.DataName(), d.TypeName(), req.TypeCommand())
  1792  	}
  1793  	return nil
  1794  }
  1795  
  1796  // Prints RGBA of first n x n pixels of image with header string.
  1797  func debugData(img image.Image, message string) {
  1798  	data, _, stride, _ := dvid.ImageData(img)
  1799  	var n = 3 // neighborhood to write
  1800  	fmt.Printf("%s>\n", message)
  1801  	for y := 0; y < n; y++ {
  1802  		for x := 0; x < n; x++ {
  1803  			i := y*int(stride) + x*4
  1804  			fmt.Printf("[%3d %3d %3d %3d]  ", data[i], data[i+1], data[i+2], data[i+3])
  1805  		}
  1806  		fmt.Printf("\n")
  1807  	}
  1808  }
  1809  
  1810  // SendUnserializedBlock writes a raw data block to the writer with given compression.
  1811  func (d *Data) SendUnserializedBlock(w http.ResponseWriter, x, y, z int32, v []byte, compression string) error {
  1812  	var data []byte
  1813  	switch compression {
  1814  	case "uncompressed":
  1815  		b := bytes.NewBuffer(v)
  1816  		imgdata, err := jpeg.Decode(b)
  1817  		if err != nil {
  1818  			return err
  1819  		}
  1820  
  1821  		data2 := imgdata.(*image.Gray)
  1822  		data = data2.Pix
  1823  	default: // JPEG by default
  1824  		data = v
  1825  	}
  1826  
  1827  	// Send block coordinate and size of data.
  1828  	if err := binary.Write(w, binary.LittleEndian, x); err != nil {
  1829  		return err
  1830  	}
  1831  	if err := binary.Write(w, binary.LittleEndian, y); err != nil {
  1832  		return err
  1833  	}
  1834  	if err := binary.Write(w, binary.LittleEndian, z); err != nil {
  1835  		return err
  1836  	}
  1837  
  1838  	n := len(data)
  1839  	if err := binary.Write(w, binary.LittleEndian, int32(n)); err != nil {
  1840  		return err
  1841  	}
  1842  	copydata := make([]byte, len(data))
  1843  	copy(copydata, data)
  1844  	if written, err := w.Write(data); err != nil || written != n {
  1845  		if err != nil {
  1846  			return err
  1847  		}
  1848  		return fmt.Errorf("could not write %d bytes of value: only %d bytes written", n, written)
  1849  	}
  1850  	return nil
  1851  }
  1852  
  1853  // SendSerializedBlock writes a serialized data block to the writer with given compression.
  1854  func (d *Data) SendSerializedBlock(w http.ResponseWriter, x, y, z int32, v []byte, compression string) error {
  1855  	// Check internal format and see if it's valid with compression choice.
  1856  	format, checksum := dvid.DecodeSerializationFormat(dvid.SerializationFormat(v[0]))
  1857  
  1858  	if (compression == "jpeg") && format != dvid.JPEG {
  1859  		return fmt.Errorf("can't encode JPEG: expected internal block data to be JPEG, was %s instead", format)
  1860  	}
  1861  
  1862  	// Send block coordinate and size of data.
  1863  	if err := binary.Write(w, binary.LittleEndian, x); err != nil {
  1864  		return err
  1865  	}
  1866  	if err := binary.Write(w, binary.LittleEndian, y); err != nil {
  1867  		return err
  1868  	}
  1869  	if err := binary.Write(w, binary.LittleEndian, z); err != nil {
  1870  		return err
  1871  	}
  1872  
  1873  	// ignore first byte
  1874  	start := 1
  1875  	if checksum == dvid.CRC32 {
  1876  		start += 4
  1877  	}
  1878  
  1879  	// Do any adjustment of sent data based on compression request
  1880  	var data []byte
  1881  	if compression == "uncompressed" {
  1882  		var err error
  1883  		data, _, err = dvid.DeserializeData(v, true)
  1884  		if err != nil {
  1885  			return err
  1886  		}
  1887  	} else {
  1888  		data = v[start:]
  1889  	}
  1890  	n := len(data)
  1891  	if err := binary.Write(w, binary.LittleEndian, int32(n)); err != nil {
  1892  		return err
  1893  	}
  1894  
  1895  	// Send data itself, skipping the first byte for internal format and next 4 for uncompressed length.
  1896  	if written, err := w.Write(data); err != nil || written != n {
  1897  		if err != nil {
  1898  			return err
  1899  		}
  1900  		return fmt.Errorf("could not write %d bytes of value: only %d bytes written", n, written)
  1901  	}
  1902  	return nil
  1903  }
  1904  
  1905  // SendBlocksSpecific writes data to the blocks specified -- best for non-ordered backend
  1906  func (d *Data) SendBlocksSpecific(ctx *datastore.VersionedCtx, w http.ResponseWriter, compression string, blockstring string, isprefetch bool) (numBlocks int, err error) {
  1907  	w.Header().Set("Content-type", "application/octet-stream")
  1908  
  1909  	if compression != "uncompressed" && compression != "jpeg" && compression != "" {
  1910  		err = fmt.Errorf("don't understand 'compression' query string value: %s", compression)
  1911  		return
  1912  	}
  1913  	timedLog := dvid.NewTimeLog()
  1914  	defer timedLog.Infof("SendBlocks Specific ")
  1915  
  1916  	// extract query string
  1917  	if blockstring == "" {
  1918  		return
  1919  	}
  1920  	coordarray := strings.Split(blockstring, ",")
  1921  	if len(coordarray)%3 != 0 {
  1922  		err = fmt.Errorf("block query string should be three coordinates per block")
  1923  		return
  1924  	}
  1925  	numBlocks = len(coordarray) / 3
  1926  
  1927  	// make a finished queue
  1928  	finishedRequests := make(chan error, len(coordarray)/3)
  1929  	var mutex sync.Mutex
  1930  
  1931  	// get store for data
  1932  	var gridStore storage.GridStoreGetter
  1933  	var kvDB storage.KeyValueDB
  1934  	gridStore, _, kvDB, err = d.gridStoreGetter()
  1935  	if err != nil {
  1936  		return
  1937  	}
  1938  
  1939  	// iterate through each block and query
  1940  	for i := 0; i < len(coordarray); i += 3 {
  1941  		var xloc, yloc, zloc int
  1942  		xloc, err = strconv.Atoi(coordarray[i])
  1943  		if err != nil {
  1944  			return
  1945  		}
  1946  		yloc, err = strconv.Atoi(coordarray[i+1])
  1947  		if err != nil {
  1948  			return
  1949  		}
  1950  		zloc, err = strconv.Atoi(coordarray[i+2])
  1951  		if err != nil {
  1952  			return
  1953  		}
  1954  
  1955  		go func(xloc, yloc, zloc int32, isprefetch bool, finishedRequests chan error) {
  1956  			var err error
  1957  			if !isprefetch {
  1958  				defer func() {
  1959  					finishedRequests <- err
  1960  				}()
  1961  			}
  1962  			chunkPt := dvid.ChunkPoint3d{xloc, yloc, zloc}
  1963  
  1964  			var value []byte
  1965  			if gridStore != nil {
  1966  				value, err = gridStore.GridGet(d.ScaleLevel, chunkPt)
  1967  				if err != nil {
  1968  					dvid.Infof("gridStore GET on scale %d, chunk %s had err: %v", d.ScaleLevel, chunkPt, err)
  1969  					err = nil
  1970  					return
  1971  				}
  1972  				if value == nil {
  1973  					dvid.Infof("gridStore GET on scale %d, chunk %s had nil value\n", d.ScaleLevel, chunkPt)
  1974  					return
  1975  				}
  1976  				mutex.Lock()
  1977  				defer mutex.Unlock()
  1978  				d.SendUnserializedBlock(w, xloc, yloc, zloc, value, compression)
  1979  				return
  1980  			}
  1981  			idx := dvid.IndexZYX(chunkPt)
  1982  			key := NewTKey(&idx)
  1983  			value, err = kvDB.Get(ctx, key)
  1984  			if err != nil {
  1985  				return
  1986  			}
  1987  			if len(value) > 0 {
  1988  				if !isprefetch {
  1989  					// lock shared resource
  1990  					mutex.Lock()
  1991  					defer mutex.Unlock()
  1992  					d.SendSerializedBlock(w, xloc, yloc, zloc, value, compression)
  1993  				}
  1994  			}
  1995  		}(int32(xloc), int32(yloc), int32(zloc), isprefetch, finishedRequests)
  1996  	}
  1997  
  1998  	if !isprefetch {
  1999  		// wait for everything to finish if not prefetching
  2000  		for i := 0; i < len(coordarray); i += 3 {
  2001  			errjob := <-finishedRequests
  2002  			if errjob != nil {
  2003  				err = errjob
  2004  			}
  2005  		}
  2006  	}
  2007  	return
  2008  }
  2009  
  2010  // SendBlocks returns a slice of bytes corresponding to all the blocks along a span in X
  2011  func (d *Data) SendBlocks(ctx *datastore.VersionedCtx, w http.ResponseWriter, subvol *dvid.Subvolume, compression string) error {
  2012  	w.Header().Set("Content-type", "application/octet-stream")
  2013  
  2014  	if compression != "uncompressed" && compression != "jpeg" && compression != "" {
  2015  		return fmt.Errorf("don't understand 'compression' query string value: %s", compression)
  2016  	}
  2017  
  2018  	// convert x,y,z coordinates to block coordinates
  2019  	blocksize := subvol.Size().Div(d.BlockSize())
  2020  	blockoffset := subvol.StartPoint().Div(d.BlockSize())
  2021  
  2022  	timedLog := dvid.NewTimeLog()
  2023  	defer timedLog.Infof("SendBlocks %s, span x %d, span y %d, span z %d", blockoffset, blocksize.Value(0), blocksize.Value(1), blocksize.Value(2))
  2024  
  2025  	// get store for data
  2026  	gridStore, okvDB, _, err := d.gridStoreGetter()
  2027  	if err != nil {
  2028  		return fmt.Errorf("cannot get suitable data store for imageblk %q: %v", d.DataName(), err)
  2029  	}
  2030  
  2031  	// if only one block is requested, avoid the range query
  2032  	if blocksize.Value(0) == int32(1) && blocksize.Value(1) == int32(1) && blocksize.Value(2) == int32(1) {
  2033  		blockCoord := dvid.ChunkPoint3d{blockoffset.Value(0), blockoffset.Value(1), blockoffset.Value(2)}
  2034  
  2035  		var value []byte
  2036  		switch {
  2037  		case gridStore != nil:
  2038  			if value, err = gridStore.GridGet(d.ScaleLevel, blockCoord); err != nil {
  2039  				return err
  2040  			}
  2041  			if len(value) > 0 {
  2042  				return d.SendUnserializedBlock(w, blockCoord[0], blockCoord[1], blockCoord[2], value, compression)
  2043  			}
  2044  		case okvDB != nil:
  2045  			indexBeg := dvid.IndexZYX(blockCoord)
  2046  			keyBeg := NewTKey(&indexBeg)
  2047  			if value, err = okvDB.Get(ctx, keyBeg); err != nil {
  2048  				return err
  2049  			}
  2050  			if len(value) > 0 {
  2051  				return d.SendSerializedBlock(w, blockCoord[0], blockCoord[1], blockCoord[2], value, compression)
  2052  			}
  2053  		default:
  2054  		}
  2055  		if err != nil {
  2056  			return err
  2057  		}
  2058  		return nil
  2059  	}
  2060  
  2061  	// only do one request at a time, although each request can start many goroutines.
  2062  	if subvol.NumVoxels() > 256*256*256 {
  2063  		server.LargeMutationMutex.Lock()
  2064  		defer server.LargeMutationMutex.Unlock()
  2065  	}
  2066  
  2067  	if gridStore != nil {
  2068  		ordered := false
  2069  		minBlock, maxBlock, err := subvol.BoundingChunks(d.BlockSize())
  2070  		if err != nil {
  2071  			return err
  2072  		}
  2073  		return gridStore.GridGetVolume(d.ScaleLevel, minBlock, maxBlock, ordered, &storage.BlockOp{}, func(b *storage.Block) error {
  2074  			if b.Value != nil {
  2075  				if err := d.SendUnserializedBlock(w, b.Coord[0], b.Coord[1], b.Coord[2], b.Value, compression); err != nil {
  2076  					return err
  2077  				}
  2078  			}
  2079  			return nil
  2080  		})
  2081  	}
  2082  
  2083  	okv := okvDB.(storage.BufferableOps)
  2084  	// extract buffer interface
  2085  	req, hasbuffer := okv.(storage.KeyValueRequester)
  2086  	if hasbuffer {
  2087  		okv = req.NewBuffer(ctx)
  2088  	}
  2089  
  2090  	for ziter := int32(0); ziter < blocksize.Value(2); ziter++ {
  2091  		for yiter := int32(0); yiter < blocksize.Value(1); yiter++ {
  2092  			if !hasbuffer {
  2093  				beginPoint := dvid.ChunkPoint3d{blockoffset.Value(0), blockoffset.Value(1) + yiter, blockoffset.Value(2) + ziter}
  2094  				endPoint := dvid.ChunkPoint3d{blockoffset.Value(0) + blocksize.Value(0) - 1, blockoffset.Value(1) + yiter, blockoffset.Value(2) + ziter}
  2095  				indexBeg := dvid.IndexZYX(beginPoint)
  2096  				sx, sy, sz := indexBeg.Unpack()
  2097  				begTKey := NewTKey(&indexBeg)
  2098  				indexEnd := dvid.IndexZYX(endPoint)
  2099  				endTKey := NewTKey(&indexEnd)
  2100  
  2101  				// Send the entire range of key-value pairs to chunk processor
  2102  				err = okv.ProcessRange(ctx, begTKey, endTKey, &storage.ChunkOp{}, func(c *storage.Chunk) error {
  2103  					if c == nil || c.TKeyValue == nil {
  2104  						return nil
  2105  					}
  2106  					kv := c.TKeyValue
  2107  					if kv.V == nil {
  2108  						return nil
  2109  					}
  2110  
  2111  					// Determine which block this is.
  2112  					indexZYX, err := DecodeTKey(kv.K)
  2113  					if err != nil {
  2114  						return err
  2115  					}
  2116  					x, y, z := indexZYX.Unpack()
  2117  					if z != sz || y != sy || x < sx || x >= sx+int32(blocksize.Value(0)) {
  2118  						return nil
  2119  					}
  2120  					if err := d.SendSerializedBlock(w, x, y, z, kv.V, compression); err != nil {
  2121  						return err
  2122  					}
  2123  					return nil
  2124  				})
  2125  
  2126  				if err != nil {
  2127  					return fmt.Errorf("Unable to GET data %s: %v", ctx, err)
  2128  				}
  2129  			} else {
  2130  				tkeys := make([]storage.TKey, 0)
  2131  				for xiter := int32(0); xiter < blocksize.Value(0); xiter++ {
  2132  					currPoint := dvid.ChunkPoint3d{blockoffset.Value(0) + xiter, blockoffset.Value(1) + yiter, blockoffset.Value(2) + ziter}
  2133  					currPoint2 := dvid.IndexZYX(currPoint)
  2134  					currTKey := NewTKey(&currPoint2)
  2135  					tkeys = append(tkeys, currTKey)
  2136  				}
  2137  				// Send the entire range of key-value pairs to chunk processor
  2138  				err = okv.(storage.RequestBuffer).ProcessList(ctx, tkeys, &storage.ChunkOp{}, func(c *storage.Chunk) error {
  2139  					if c == nil || c.TKeyValue == nil {
  2140  						return nil
  2141  					}
  2142  					kv := c.TKeyValue
  2143  					if kv.V == nil {
  2144  						return nil
  2145  					}
  2146  
  2147  					// Determine which block this is.
  2148  					indexZYX, err := DecodeTKey(kv.K)
  2149  					if err != nil {
  2150  						return err
  2151  					}
  2152  					x, y, z := indexZYX.Unpack()
  2153  
  2154  					if err := d.SendSerializedBlock(w, x, y, z, kv.V, compression); err != nil {
  2155  						return err
  2156  					}
  2157  					return nil
  2158  				})
  2159  
  2160  				if err != nil {
  2161  					return fmt.Errorf("Unable to GET data %s: %v", ctx, err)
  2162  				}
  2163  			}
  2164  		}
  2165  	}
  2166  
  2167  	if hasbuffer {
  2168  		// submit the entire buffer to the DB
  2169  		err = okv.(storage.RequestBuffer).Flush()
  2170  
  2171  		if err != nil {
  2172  			return fmt.Errorf("Unable to GET data %s: %v", ctx, err)
  2173  
  2174  		}
  2175  	}
  2176  
  2177  	return err
  2178  }
  2179  
  2180  // ServeHTTP handles all incoming HTTP requests for this data.
  2181  func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) (activity map[string]interface{}) {
  2182  	timedLog := dvid.NewTimeLog()
  2183  
  2184  	// Get the action (GET, POST)
  2185  	action := strings.ToLower(r.Method)
  2186  	switch action {
  2187  	case "get":
  2188  	case "post":
  2189  	default:
  2190  		server.BadRequest(w, r, "Data %q can only handle GET or POST HTTP verbs", d.DataName())
  2191  		return
  2192  	}
  2193  
  2194  	// Break URL request into arguments
  2195  	url := r.URL.Path[len(server.WebAPIPath):]
  2196  	parts := strings.Split(url, "/")
  2197  	if len(parts[len(parts)-1]) == 0 {
  2198  		parts = parts[:len(parts)-1]
  2199  	}
  2200  	if len(parts) < 4 {
  2201  		server.BadRequest(w, r, "Incomplete API request")
  2202  		return
  2203  	}
  2204  
  2205  	// Get query strings and possible roi
  2206  	var roiptr *ROI
  2207  	queryStrings := r.URL.Query()
  2208  	roiname := dvid.InstanceName(queryStrings.Get("roi"))
  2209  	if len(roiname) != 0 {
  2210  		roiptr = new(ROI)
  2211  		attenuationStr := queryStrings.Get("attenuation")
  2212  		if len(attenuationStr) != 0 {
  2213  			attenuation, err := strconv.Atoi(attenuationStr)
  2214  			if err != nil {
  2215  				server.BadRequest(w, r, err)
  2216  				return
  2217  			}
  2218  			if attenuation < 1 || attenuation > 7 {
  2219  				server.BadRequest(w, r, "Attenuation should be from 1 to 7 (divides by 2^n)")
  2220  				return
  2221  			}
  2222  			roiptr.attenuation = uint8(attenuation)
  2223  		}
  2224  	}
  2225  
  2226  	// Process help and info.
  2227  	switch parts[3] {
  2228  	case "help":
  2229  		w.Header().Set("Content-Type", "text/plain")
  2230  		fmt.Fprintln(w, d.Help())
  2231  		return
  2232  
  2233  	case "metadata":
  2234  		jsonStr, err := d.NdDataMetadata(ctx)
  2235  		if err != nil {
  2236  			server.BadRequest(w, r, err)
  2237  			return
  2238  		}
  2239  		w.Header().Set("Content-Type", "application/vnd.dvid-nd-data+json")
  2240  		fmt.Fprintln(w, jsonStr)
  2241  		return
  2242  
  2243  	case "extents":
  2244  		if action != "post" {
  2245  			server.BadRequest(w, r, "extents endpoint only supports POST HTTP verb")
  2246  			return
  2247  		}
  2248  		jsonBytes, err := ioutil.ReadAll(r.Body)
  2249  		if err != nil {
  2250  			server.BadRequest(w, r, err)
  2251  			return
  2252  		}
  2253  		if err := d.SetExtents(ctx, uuid, jsonBytes); err != nil {
  2254  			server.BadRequest(w, r, err)
  2255  			return
  2256  		}
  2257  
  2258  	case "resolution":
  2259  		if action != "post" {
  2260  			server.BadRequest(w, r, "resolution endpoint only supports POST HTTP verb")
  2261  			return
  2262  		}
  2263  		jsonBytes, err := ioutil.ReadAll(r.Body)
  2264  		if err != nil {
  2265  			server.BadRequest(w, r, err)
  2266  			return
  2267  		}
  2268  		if err := d.SetResolution(uuid, jsonBytes); err != nil {
  2269  			server.BadRequest(w, r, err)
  2270  			return
  2271  		}
  2272  
  2273  	case "info":
  2274  		if action != "get" {
  2275  			server.BadRequest(w, r, "info endpoint only supports GET HTTP verb")
  2276  		} else {
  2277  			jsonBytes, err := d.MarshalJSONExtents(ctx)
  2278  			if err != nil {
  2279  				server.BadRequest(w, r, err)
  2280  				return
  2281  			}
  2282  			w.Header().Set("Content-Type", "application/json")
  2283  			fmt.Fprint(w, string(jsonBytes))
  2284  		}
  2285  		return
  2286  
  2287  	case "rawkey":
  2288  		// GET <api URL>/node/<UUID>/<data name>/rawkey?x=<block x>&y=<block y>&z=<block z>
  2289  		if len(parts) != 4 {
  2290  			server.BadRequest(w, r, "rawkey endpoint should be followed by query strings (x, y, and z) giving block coord")
  2291  			return
  2292  		}
  2293  
  2294  	case "specificblocks":
  2295  		// GET <api URL>/node/<UUID>/<data name>/specificblocks?compression=gzip&prefetch=false&blocks=x,y,z,x,y,z...
  2296  		compression := queryStrings.Get("compression")
  2297  		blocklist := queryStrings.Get("blocks")
  2298  		isprefetch := false
  2299  		if prefetch := queryStrings.Get("prefetch"); prefetch == "on" || prefetch == "true" {
  2300  			isprefetch = true
  2301  		}
  2302  
  2303  		if action == "get" {
  2304  			numBlocks, err := d.SendBlocksSpecific(ctx, w, compression, blocklist, isprefetch)
  2305  			if err != nil {
  2306  				server.BadRequest(w, r, err)
  2307  				return
  2308  			}
  2309  			timedLog.Infof("HTTP %s: %s", r.Method, r.URL)
  2310  			activity = map[string]interface{}{
  2311  				"num_blocks": numBlocks,
  2312  			}
  2313  		} else {
  2314  			server.BadRequest(w, r, "DVID does not accept the %s action on the 'specificblocks' endpoint", action)
  2315  			return
  2316  		}
  2317  
  2318  	case "subvolblocks":
  2319  		// GET <api URL>/node/<UUID>/<data name>/subvolblocks/<size>/<offset>[?compression=...]
  2320  		sizeStr, offsetStr := parts[4], parts[5]
  2321  
  2322  		if throttle := queryStrings.Get("throttle"); throttle == "on" || throttle == "true" {
  2323  			if server.ThrottledHTTP(w) {
  2324  				return
  2325  			}
  2326  			defer server.ThrottledOpDone()
  2327  		}
  2328  		compression := queryStrings.Get("compression")
  2329  		subvol, err := dvid.NewSubvolumeFromStrings(offsetStr, sizeStr, "_")
  2330  		if err != nil {
  2331  			server.BadRequest(w, r, err)
  2332  			return
  2333  		}
  2334  
  2335  		if subvol.StartPoint().NumDims() != 3 || subvol.Size().NumDims() != 3 {
  2336  			server.BadRequest(w, r, "must specify 3D subvolumes", subvol.StartPoint(), subvol.EndPoint())
  2337  			return
  2338  		}
  2339  
  2340  		// Make sure subvolume gets align with blocks
  2341  		if !dvid.BlockAligned(subvol, d.BlockSize()) {
  2342  			server.BadRequest(w, r, "cannot use labels via 'block' endpoint in non-block aligned geometry %s -> %s", subvol.StartPoint(), subvol.EndPoint())
  2343  			return
  2344  		}
  2345  
  2346  		if action == "get" {
  2347  			if err := d.SendBlocks(ctx, w, subvol, compression); err != nil {
  2348  				server.BadRequest(w, r, err)
  2349  				return
  2350  			}
  2351  			timedLog.Infof("HTTP %s: %s (%s)", r.Method, subvol, r.URL)
  2352  		} else {
  2353  			server.BadRequest(w, r, "DVID does not accept the %s action on the 'blocks' endpoint", action)
  2354  			return
  2355  		}
  2356  
  2357  	case "blocks":
  2358  		// GET  <api URL>/node/<UUID>/<data name>/blocks/<block coord>/<spanX>
  2359  		// POST <api URL>/node/<UUID>/<data name>/blocks/<block coord>/<spanX>
  2360  		if len(parts) < 6 {
  2361  			server.BadRequest(w, r, "%q must be followed by block-coord/span-x", parts[3])
  2362  			return
  2363  		}
  2364  		bcoord, err := dvid.StringToChunkPoint3d(parts[4], "_")
  2365  		if err != nil {
  2366  			server.BadRequest(w, r, err)
  2367  			return
  2368  		}
  2369  		span, err := strconv.Atoi(parts[5])
  2370  		if err != nil {
  2371  			server.BadRequest(w, r, err)
  2372  			return
  2373  		}
  2374  		if action == "get" {
  2375  			data, err := d.GetBlocks(ctx.VersionID(), bcoord, int32(span))
  2376  			if err != nil {
  2377  				server.BadRequest(w, r, err)
  2378  				return
  2379  			}
  2380  			w.Header().Set("Content-type", "application/octet-stream")
  2381  			_, err = w.Write(data)
  2382  			if err != nil {
  2383  				server.BadRequest(w, r, err)
  2384  				return
  2385  			}
  2386  		} else {
  2387  			// TODO -- relax this once GridStore implementations allow mutation
  2388  			if d.GridStore != "" {
  2389  				server.BadRequest(w, r, "Data %q uses an immutable GridStore so cannot received POSTs", d.DataName())
  2390  				return
  2391  			}
  2392  			mutID := d.NewMutationID()
  2393  			mutate := (queryStrings.Get("mutate") == "true")
  2394  			if err := d.PutBlocks(ctx.VersionID(), mutID, bcoord, span, r.Body, mutate); err != nil {
  2395  				server.BadRequest(w, r, err)
  2396  				return
  2397  			}
  2398  		}
  2399  		timedLog.Infof("HTTP %s: Blocks (%s)", r.Method, r.URL)
  2400  
  2401  	case "arb":
  2402  		// GET  <api URL>/node/<UUID>/<data name>/arb/<top left>/<top right>/<bottom left>/<res>[/<format>]
  2403  		if len(parts) < 8 {
  2404  			server.BadRequest(w, r, "%q must be followed by top-left/top-right/bottom-left/res", parts[3])
  2405  			return
  2406  		}
  2407  		if throttle := queryStrings.Get("throttle"); throttle == "on" || throttle == "true" {
  2408  			if server.ThrottledHTTP(w) {
  2409  				return
  2410  			}
  2411  			defer server.ThrottledOpDone()
  2412  		}
  2413  		img, err := d.GetArbitraryImage(ctx, parts[4], parts[5], parts[6], parts[7])
  2414  		if err != nil {
  2415  			server.BadRequest(w, r, err)
  2416  			return
  2417  		}
  2418  		var formatStr string
  2419  		if len(parts) >= 9 {
  2420  			formatStr = parts[8]
  2421  		}
  2422  		err = dvid.WriteImageHttp(w, img.Get(), formatStr)
  2423  		if err != nil {
  2424  			server.BadRequest(w, r, err)
  2425  			return
  2426  		}
  2427  		timedLog.Infof("HTTP %s: Arbitrary image (%s)", r.Method, r.URL)
  2428  
  2429  	case "raw", "isotropic":
  2430  		// GET  <api URL>/node/<UUID>/<data name>/isotropic/<dims>/<size>/<offset>[/<format>]
  2431  		if len(parts) < 7 {
  2432  			server.BadRequest(w, r, "%q must be followed by shape/size/offset", parts[3])
  2433  			return
  2434  		}
  2435  		var isotropic bool = (parts[3] == "isotropic")
  2436  		shapeStr, sizeStr, offsetStr := parts[4], parts[5], parts[6]
  2437  		planeStr := dvid.DataShapeString(shapeStr)
  2438  		plane, err := planeStr.DataShape()
  2439  		if err != nil {
  2440  			server.BadRequest(w, r, err)
  2441  			return
  2442  		}
  2443  		switch plane.ShapeDimensions() {
  2444  		case 2:
  2445  			slice, err := dvid.NewSliceFromStrings(planeStr, offsetStr, sizeStr, "_")
  2446  			if err != nil {
  2447  				server.BadRequest(w, r, err)
  2448  				return
  2449  			}
  2450  			if action != "get" {
  2451  				server.BadRequest(w, r, "DVID does not permit 2d mutations, only 3d block-aligned stores")
  2452  				return
  2453  			}
  2454  			rawSlice, err := dvid.Isotropy2D(d.Properties.VoxelSize, slice, isotropic)
  2455  			if err != nil {
  2456  				server.BadRequest(w, r, err)
  2457  				return
  2458  			}
  2459  			vox, err := d.NewVoxels(rawSlice, nil)
  2460  			if err != nil {
  2461  				server.BadRequest(w, r, err)
  2462  				return
  2463  			}
  2464  			img, err := d.GetImage(ctx.VersionID(), vox, roiname)
  2465  			if err != nil {
  2466  				server.BadRequest(w, r, err)
  2467  				return
  2468  			}
  2469  			if isotropic {
  2470  				dstW := int(slice.Size().Value(0))
  2471  				dstH := int(slice.Size().Value(1))
  2472  				img, err = img.ScaleImage(dstW, dstH)
  2473  				if err != nil {
  2474  					server.BadRequest(w, r, err)
  2475  					return
  2476  				}
  2477  			}
  2478  			var formatStr string
  2479  			if len(parts) >= 8 {
  2480  				formatStr = parts[7]
  2481  			}
  2482  			err = dvid.WriteImageHttp(w, img.Get(), formatStr)
  2483  			if err != nil {
  2484  				server.BadRequest(w, r, err)
  2485  				return
  2486  			}
  2487  			timedLog.Infof("HTTP %s: %s (%s)", r.Method, plane, r.URL)
  2488  		case 3:
  2489  			if throttle := queryStrings.Get("throttle"); throttle == "on" || throttle == "true" {
  2490  				if server.ThrottledHTTP(w) {
  2491  					return
  2492  				}
  2493  				defer server.ThrottledOpDone()
  2494  			}
  2495  			subvol, err := dvid.NewSubvolumeFromStrings(offsetStr, sizeStr, "_")
  2496  			if err != nil {
  2497  				server.BadRequest(w, r, err)
  2498  				return
  2499  			}
  2500  			if action == "get" {
  2501  				vox, err := d.NewVoxels(subvol, nil)
  2502  				if err != nil {
  2503  					server.BadRequest(w, r, err)
  2504  					return
  2505  				}
  2506  
  2507  				if len(parts) >= 8 && (parts[7] == "jpeg" || parts[7] == "jpg") {
  2508  
  2509  					// extract volume
  2510  					if err := d.GetVoxels(ctx.VersionID(), vox, roiname); err != nil {
  2511  						server.BadRequest(w, r, err)
  2512  						return
  2513  					}
  2514  
  2515  					// convert 3D volume to an 2D image
  2516  					size3d := vox.Geometry.Size()
  2517  					size2d := dvid.Point2d{size3d.Value(0), size3d.Value(1) * size3d.Value(2)}
  2518  					geo2d, err := dvid.NewOrthogSlice(dvid.XY, vox.Geometry.StartPoint(), size2d)
  2519  					if err != nil {
  2520  						server.BadRequest(w, r, err)
  2521  						return
  2522  					}
  2523  					vox.Geometry = geo2d
  2524  
  2525  					img, err := vox.GetImage2d()
  2526  					if err != nil {
  2527  						server.BadRequest(w, r, err)
  2528  						return
  2529  					}
  2530  
  2531  					formatStr := parts[7]
  2532  					err = dvid.WriteImageHttp(w, img.Get(), formatStr)
  2533  					if err != nil {
  2534  						server.BadRequest(w, r, err)
  2535  						return
  2536  					}
  2537  				} else {
  2538  
  2539  					data, err := d.GetVolume(ctx.VersionID(), vox, roiname)
  2540  					if err != nil {
  2541  						server.BadRequest(w, r, err)
  2542  						return
  2543  					}
  2544  					w.Header().Set("Content-type", "application/octet-stream")
  2545  					_, err = w.Write(data)
  2546  					if err != nil {
  2547  						server.BadRequest(w, r, err)
  2548  						return
  2549  					}
  2550  				}
  2551  			} else {
  2552  				if isotropic {
  2553  					err := fmt.Errorf("can only POST 'raw' not 'isotropic' images")
  2554  					server.BadRequest(w, r, err)
  2555  					return
  2556  				}
  2557  				if d.GridStore != "" {
  2558  					server.BadRequest(w, r, "Data %q uses an immutable GridStore so cannot received POSTs", d.DataName())
  2559  					return
  2560  				}
  2561  				data, err := ioutil.ReadAll(r.Body)
  2562  				if err != nil {
  2563  					server.BadRequest(w, r, err)
  2564  					return
  2565  				}
  2566  				vox, err := d.NewVoxels(subvol, data)
  2567  				if err != nil {
  2568  					server.BadRequest(w, r, err)
  2569  					return
  2570  				}
  2571  				mutID := d.NewMutationID()
  2572  				mutate := (queryStrings.Get("mutate") == "true")
  2573  				if err = d.PutVoxels(ctx.VersionID(), mutID, vox, roiname, mutate); err != nil {
  2574  					server.BadRequest(w, r, err)
  2575  					return
  2576  				}
  2577  			}
  2578  			timedLog.Infof("HTTP %s: %s (%s)", r.Method, subvol, r.URL)
  2579  		default:
  2580  			server.BadRequest(w, r, "DVID currently supports shapes of only 2 and 3 dimensions")
  2581  		}
  2582  	default:
  2583  		server.BadAPIRequest(w, r, d)
  2584  	}
  2585  	return
  2586  }