Skip to content

Commit

Permalink
Integrate TIFF access into vitessce-image-viewer and Improve Metadata…
Browse files Browse the repository at this point in the history
… Caching (#31)
  • Loading branch information
ilan-gold authored Feb 4, 2020
1 parent 1eb8a92 commit f901168
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 123 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ peer depenedencies from this package into your own:
"@luma.gl/core": "8.0.3",
"@luma.gl/shadertools": "8.0.3",
"deck.gl": "^8.1.0-alpha.1",
"geotiff": "^1.0.0-beta.6",
"math.gl": "^3.1.3",
"nebula.gl": "^0.17.1",
"zarr": "^0.1.4"
Expand Down Expand Up @@ -44,7 +45,7 @@ This component can be used with an already existing `DeckGL` setup.

##### `getTileData` (Function) **POTENIAL FUTURE BREAKING CHANGES WITH NEW FEATURES**

`getTileData` given x, y, z indices of the tile, returns the tile data or a Promise that resolves to the tile data. Alternatively, pass in `useZarr` as true to use `zarr` and our funcionality. Soon, there will be a `useGeoTIFF` as well. Look
`getTileData` given x, y, z indices of the tile, returns the tile data or a Promise that resolves to the tile data. Alternatively, pass in `useZarr` as true to use `zarr` and our functionality. Otherwise, you can use `useTiff` to make range requests directly against a pyramid/tiled tiff. Look
at [this](IMAGE_RENDERING.md) for how the zarr should be laid out.

Receives arguments:
Expand Down
8 changes: 3 additions & 5 deletions demo/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ const OrangeSlider = withStyles({
}
})(Slider)



export default class App extends PureComponent {

constructor(props){
Expand Down Expand Up @@ -91,17 +89,17 @@ export default class App extends PureComponent {
],
}
const propSettings = {
useTiff: true,
imageHeight: source.height * source.tileSize,
imageWidth: source.width * source.tileSize,
tileSize: source.tileSize,
sourceChannels: source.channels,
minZoom: Math.floor(
-1 * Math.log2(Math.max(source.height * source.tileSize, source.width * source.tileSize)),
)
),
maxZoom: -9
}
const useZarr = true
const props = {
useZarr,
initialViewState,
...propSettings,
...this.state
Expand Down
15 changes: 11 additions & 4 deletions demo/src/source-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ export const source = {
height: 141,
width: 206,
tileSize: 256,
// channels: {
// channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0",
// channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1",
// channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2",
// channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3",
// },
channels: {
channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0",
channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1",
channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2",
channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3",
channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_0.ome.tiff",
channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_1.ome.tiff",
channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_2.ome.tiff",
channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_3.ome.tiff",
}

};
17 changes: 15 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"babel-preset-stage-0": "^6.24.1",
"css-loader": "^3.4.2",
"deck.gl": "^8.1.0-alpha.1",
"geotiff": "^1.0.0-beta.6",
"html-webpack-plugin": "^3.2.0",
"math.gl": "^3.1.3",
"mini-css-extract-plugin": "^0.9.0",
Expand Down
5 changes: 3 additions & 2 deletions src/layers/microscopy-viewer-layer/data-utils/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import loadZarr from './zarr-utils';
import {loadZarr, getZarrConnections} from './zarr-utils';
import {loadTiff, getTiffConnections} from './tiff-utils';

export {loadZarr}
export {loadZarr, loadTiff, getTiffConnections, getZarrConnections}
46 changes: 46 additions & 0 deletions src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {fromUrl, Pool, getDecoder } from 'geotiff/dist/geotiff.bundle.min.js';

async function loadTile({image, channel, x, y, pool}) {
var tile = await image.getTileOrStrip(x,y, 0, pool)
var dataObj = {}
const is8Bits = image.fileDirectory.BitsPerSample[0]===8
const is16Bits = image.fileDirectory.BitsPerSample[0]===16
const is32Bits = image.fileDirectory.BitsPerSample[0]===32
dataObj[channel] = (is8Bits && new Uint8Array(tile.data)) ||
(is16Bits && new Uint16Array(tile.data)) ||
(is32Bits && new Uint32Array(tile.data))
return dataObj
}

export function loadTiff({connections, x, y, z, pool}){
const configListPromises = Object.keys(connections).map((channel) => {
var image = connections[channel][z]
return loadTile({image, channel, x, y, pool})
})
return Promise.all(configListPromises).then((dataList) => {
const orderedList = []
const dataObj = Object.assign({}, ...dataList)
Object.keys(dataObj).sort().forEach((key) => {
orderedList.push(dataObj[key]);
})
return orderedList;
});
}

export function getTiffConnections({sourceChannels, maxZoom}){
const tiffConnections = Object.keys(sourceChannels).map(async (channel) => {
var tiff = await fromUrl(sourceChannels[channel])
var imageObj = {}
for(var i = 0; i < -maxZoom; i++) {
var image = await tiff.getImage(i)
if(!imageObj[channel]) {
imageObj[channel] = [image]
} else {
imageObj[channel].push(image)
}

}
return imageObj
})
return Promise.all(tiffConnections)
}
50 changes: 26 additions & 24 deletions src/layers/microscopy-viewer-layer/data-utils/zarr-utils.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,23 @@
import { slice, openArray } from 'zarr';

var zarrArrays = {}

async function getData({ config, tileSize, x, y, stride, tilingWidth }) {
async function getData({ arr, channel, tileSize, x, y, stride, tilingWidth }) {
const arrSlice = slice(stride * tilingWidth * y + stride * x, stride * tilingWidth * y + stride * (x + 1));
const zarrKey = config.zarrConfig.store + config.zarrConfig.path;
if (!zarrArrays[zarrKey]) {
zarrArrays[zarrKey] = await openArray(config.zarrConfig);
}
const arr = zarrArrays[zarrKey];
const dataSlice = await arr.get([arrSlice]);
const { data } = dataSlice;
const { channelName } = config;
const dataObj = {}
dataObj[channelName] = data
dataObj[channel] = data
return dataObj;
}

export default function loadZarr({ sourceChannels, tileSize, x, y, z, imageWidth }) {
export function loadZarr({ connections, tileSize, x, y, z, imageWidth }) {
const tilingWidth = Math.ceil(imageWidth / (tileSize * (2 ** z)));
const dataNames = ['redData', 'greenData', 'blueData']; // denotes order rgb
const configList = Object.keys(sourceChannels).map((channel, i) => ({
channelName: channel,
channelType: dataNames[i],
zarrConfig: {
store: `${sourceChannels[channel]}/`,
path: `pyramid_${z}.zarr`,
mode: 'r',
},
}));
const stride = tileSize * tileSize;
const configListPromises = configList.map((config) => {
const channelPromises = Object.keys(connections).map((channel) => {
return getData({
config, tileSize, x, y, stride, tilingWidth,
arr: connections[channel][z], channel, tileSize, x, y, stride, tilingWidth,
});
});
return Promise.all(configListPromises).then((dataList) => {
return Promise.all(channelPromises).then((dataList) => {
const orderedList = []
const dataObj = Object.assign({}, ...dataList)
Object.keys(dataObj).sort().forEach(function(key) {
Expand All @@ -44,3 +26,23 @@ export default function loadZarr({ sourceChannels, tileSize, x, y, z, imageWidth
return orderedList;
});
}

export function getZarrConnections({sourceChannels, maxZoom}) {
const zarrConnections = Object.keys(sourceChannels).map(async (channel) => {
var zarrObj = {}
for(var i = 0; i < -maxZoom; i++) {
var zarr = await openArray({
store: `${sourceChannels[channel]}/`,
path: `pyramid_${i}.zarr`,
mode: 'r',
})
if(!zarrObj[channel]) {
zarrObj[channel] = [zarr]
} else {
zarrObj[channel].push(zarr)
}
}
return zarrObj
})
return Promise.all(zarrConnections)
}
97 changes: 97 additions & 0 deletions src/layers/microscopy-viewer-layer/microscopy-viewer-layer-base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {BaseTileLayer} from '@deck.gl/layers';
import {Texture2D} from '@luma.gl/webgl'
import GL from '@luma.gl/constants';
import { COORDINATE_SYSTEM } from 'deck.gl';
import { XRLayer } from '../xr-layer';
import {tileToBoundingBox} from './tiling-utils';
import {getTileIndices} from './tiling-utils';
import { loadZarr, loadTiff } from './data-utils';

const defaultProps = Object.assign({}, BaseTileLayer.defaultProps, {
id: `microscopy-tile-layer`,
pickable: false,
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
maxZoom: 0,
onViewportLoad: false,
renderSubLayers: (props) => {
const {
bbox: {
west, south, east, north,
},
} = props.tile;
const { sliderValues, data, colorValues } = props;
const xrl = new XRLayer(props, {
id: `XR-Layer-${west}-${south}-${east}-${north}`,
pickable: false,
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
rgbData: data,
sliderValues,
colorValues,
bounds: [west, south, east, north],
visible: true,
});
return xrl;
},
});

export class MicroscopyViewerLayerBase extends BaseTileLayer {

constructor(props) {
const minZoom = Math.floor(-1 * Math.log2(Math.max(props.imageHeight, props.imageWidth)));
const {sliderValues, colorValues} = props
var orderedSliderValues = []
var orderedColorValues = []
Object.keys(sliderValues).sort().forEach(function(key) {
orderedSliderValues.push(sliderValues[key]);
})
Object.keys(colorValues).sort().forEach(function(key) {
orderedColorValues.push(colorValues[key]);
})
var diff = 6 - orderedSliderValues.length
for (var i = 0; i < diff; i++) {
orderedSliderValues.push(65535);
}
var diff = 6 - orderedColorValues.length
for (var j = 0; j < diff; j++) {
orderedColorValues.push([0,0,0]);
}
orderedColorValues = orderedColorValues.map(color => color.map(ch => ch / 255))
const getZarr = ({ x, y, z }) => {
return loadZarr({
x, y, z: -1 * z, ...props,
});
}
const getTiff = ({ x, y, z }) => {
return loadTiff({
x, y, z: -1 * z, ...props,
});
}
const getTileData = (props.useZarr && getZarr) ||
(props.useTiff && getTiff) ||
props.getTileData
const overrideValuesProps = Object.assign(
{}, props, {
sliderValues: orderedSliderValues,
colorValues: orderedColorValues,
minZoom,
getTileData,
getTileIndices: (viewport, maxZoom, minZoom) => {
return getTileIndices({
viewport, maxZoom, minZoom, ...props,
});
},
tileToBoundingBox: (x, y, z) => {
return tileToBoundingBox({
x, y, z, ...props,
});
},
}
)
const layerProps = Object.assign({}, defaultProps, overrideValuesProps)
super(layerProps)
}

}

MicroscopyViewerLayerBase.layerName = 'MicroscopyViewerLayerBase';
MicroscopyViewerLayerBase.defaultProps = defaultProps;
Loading

0 comments on commit f901168

Please sign in to comment.