Skip to content

Commit

Permalink
Add pansharpening bitmap layer
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle Barron committed Apr 19, 2020
1 parent c5c93f3 commit 19f32c4
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
68 changes: 68 additions & 0 deletions modules/bands-bitmap-layer/src/bands-bitmap-layer-pan-fragment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
export default `
#define SHADER_NAME bands-bitmap-layer-pan-fragment-shader
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D bitmapTexture_r;
uniform sampler2D bitmapTexture_g;
uniform sampler2D bitmapTexture_b;
uniform sampler2D bitmapTexture_pan;
varying vec2 vTexCoord;
uniform float desaturate;
uniform vec4 transparentColor;
uniform vec3 tintColor;
uniform float opacity;
uniform float panWeight;
// calculate pansharpen ratio
float pansharpen_ratio(vec3 rgb, float pan, float weight) {
return pan / ((rgb.r + rgb.g + rgb.b * weight) / (2. + weight));
}
// Brovey Method: Each resampled, multispectral pixel is
// multiplied by the ratio of the corresponding
// panchromatic pixel intensity to the sum of all the
// multispectral intensities.
// Original code from https://github.com/mapbox/rio-pansharpen
vec3 pansharpen(vec3 rgb, float pan, float weight) {
float ratio = pansharpen_ratio(rgb, pan, weight);
vec3 alteredRGB = ratio * rgb;
return clamp(alteredRGB, 0., 1.);
}
// apply desaturation
vec3 color_desaturate(vec3 color) {
float luminance = (color.r + color.g + color.b) * 0.333333333;
return mix(color, vec3(luminance), desaturate);
}
// apply tint
vec3 color_tint(vec3 color) {
return color * tintColor;
}
// blend with background color
vec4 apply_opacity(vec3 color, float alpha) {
return mix(transparentColor, vec4(color, 1.0), alpha);
}
void main(void) {
float r_band = texture2D(bitmapTexture_r, vTexCoord).r;
float g_band = texture2D(bitmapTexture_g, vTexCoord).r;
float b_band = texture2D(bitmapTexture_b, vTexCoord).r;
float pan_band = texture2D(bitmapTexture_pan, vTexCoord).r;
vec3 image = vec3(r_band, g_band, b_band);
vec3 pansharpenedImage = pansharpen(image, pan_band, panWeight);
gl_FragColor = apply_opacity(color_tint(color_desaturate(pansharpenedImage)), opacity);
geometry.uv = vTexCoord;
DECKGL_FILTER_COLOR(gl_FragColor, geometry);
}
`;
168 changes: 168 additions & 0 deletions modules/bands-bitmap-layer/src/bands-bitmap-layer-pan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import GL from "@luma.gl/constants";
import { BitmapLayer } from "@deck.gl/layers";
import { Texture2D } from "@luma.gl/core";

import fs from "./bands-bitmap-layer-pan-fragment";

const DEFAULT_TEXTURE_PARAMETERS = {
[GL.TEXTURE_MIN_FILTER]: GL.LINEAR_MIPMAP_LINEAR,
[GL.TEXTURE_MAG_FILTER]: GL.LINEAR,
[GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE,
[GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE,
};

const defaultProps = {
image_r: { type: "object", value: null, async: true },
image_g: { type: "object", value: null, async: true },
image_b: { type: "object", value: null, async: true },
image_pan: { type: "object", value: null, async: true },
bounds: { type: "array", value: [1, 0, 0, 1], compare: true },

desaturate: { type: "number", min: 0, max: 1, value: 0 },
// Weight of blue band
panWeight: { type: "number", min: 0, max: 1, value: 0.2 },
// More context: because of the blending mode we're using for ground imagery,
// alpha is not effective when blending the bitmap layers with the base map.
// Instead we need to manually dim/blend rgb values with a background color.
transparentColor: { type: "color", value: [0, 0, 0, 0] },
tintColor: { type: "color", value: [255, 255, 255] },
};

export default class BandsBitmapLayer extends BitmapLayer {
draw(opts) {
const { uniforms } = opts;
const {
bitmapTexture_r,
bitmapTexture_g,
bitmapTexture_b,
bitmapTexture_pan,
model,
} = this.state;
const { desaturate, transparentColor, tintColor, panWeight } = this.props;

// // TODO fix zFighting
// Render the image
if (
bitmapTexture_r &&
bitmapTexture_g &&
bitmapTexture_b &&
bitmapTexture_pan &&
model
) {
model
.setUniforms(
Object.assign({}, uniforms, {
bitmapTexture_r,
bitmapTexture_g,
bitmapTexture_b,
bitmapTexture_pan,
panWeight,
desaturate,
transparentColor: transparentColor.map((x) => x / 255),
tintColor: tintColor.slice(0, 3).map((x) => x / 255),
})
)
.draw();
}
}

finalizeState() {
super.finalizeState();

if (this.state.bitmapTexture_r) {
this.state.bitmapTexture_r.delete();
}
if (this.state.bitmapTexture_g) {
this.state.bitmapTexture_g.delete();
}
if (this.state.bitmapTexture_b) {
this.state.bitmapTexture_b.delete();
}
if (this.state.bitmapTexture_pan) {
this.state.bitmapTexture_pan.delete();
}
}

getShaders() {
// use object.assign to make sure we don't overwrite existing fields like `vs`, `modules`...
return Object.assign({}, super.getShaders(), {
fs,
});
}

updateState({ props, oldProps, changeFlags }) {
// setup model first
if (changeFlags.extensionsChanged) {
const { gl } = this.context;
if (this.state.model) {
this.state.model.delete();
}
this.setState({ model: this._getModel(gl) });
this.getAttributeManager().invalidateAll();
}

if (props.image_r !== oldProps.image_r) {
const bitmapTexture_r = this.loadTexture(props.image_r);
if (this.state.bitmapTexture_r) {
this.state.bitmapTexture_r.delete();
}
this.setState({ bitmapTexture_r });
}
if (props.image_g !== oldProps.image_g) {
const bitmapTexture_g = this.loadTexture(props.image_g);
if (this.state.bitmapTexture_g) {
this.state.bitmapTexture_g.delete();
}
this.setState({ bitmapTexture_g });
}
if (props.image_b !== oldProps.image_b) {
const bitmapTexture_b = this.loadTexture(props.image_b);
if (this.state.bitmapTexture_b) {
this.state.bitmapTexture_b.delete();
}
this.setState({ bitmapTexture_b });
}
if (props.image_pan !== oldProps.image_pan) {
const bitmapTexture_pan = this.loadTexture(props.image_pan);
if (this.state.bitmapTexture_pan) {
this.state.bitmapTexture_pan.delete();
}
this.setState({ bitmapTexture_pan });
}

const attributeManager = this.getAttributeManager();

if (props.bounds !== oldProps.bounds) {
attributeManager.invalidate("positions");
}
}

loadTexture(image) {
const { gl } = this.context;

if (image instanceof Texture2D) {
return image;
} else if (image instanceof HTMLVideoElement) {
// Initialize an empty texture while we wait for the video to load
return {
bitmapTexture: new Texture2D(gl, {
width: 1,
height: 1,
parameters: DEFAULT_TEXTURE_PARAMETERS,
mipmaps: false,
}),
};
} else if (image) {
// Browser object: Image, ImageData, HTMLCanvasElement, ImageBitmap
return {
bitmapTexture: new Texture2D(gl, {
data: image,
parameters: DEFAULT_TEXTURE_PARAMETERS,
}),
};
}
}
}

BandsBitmapLayer.defaultProps = defaultProps;
BandsBitmapLayer.layerName = "BandsBitmapLayer";
1 change: 1 addition & 0 deletions modules/bands-bitmap-layer/src/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as BandsBitmapLayer } from "./bands-bitmap-layer";
export { default as PanBandsBitmapLayer } from "./bands-bitmap-layer-pan";

0 comments on commit 19f32c4

Please sign in to comment.