You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
7.9 KiB
212 lines
7.9 KiB
|
3 years ago
|
/**
|
||
|
|
* @module resample
|
||
|
|
*/
|
||
|
|
|
||
|
|
function copyNewSize(array, width, height, samplesPerPixel = 1) {
|
||
|
|
return new (Object.getPrototypeOf(array).constructor)(width * height * samplesPerPixel);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resample the input arrays using nearest neighbor value selection.
|
||
|
|
* @param {TypedArray[]} valueArrays The input arrays to resample
|
||
|
|
* @param {number} inWidth The width of the input rasters
|
||
|
|
* @param {number} inHeight The height of the input rasters
|
||
|
|
* @param {number} outWidth The desired width of the output rasters
|
||
|
|
* @param {number} outHeight The desired height of the output rasters
|
||
|
|
* @returns {TypedArray[]} The resampled rasters
|
||
|
|
*/
|
||
|
|
export function resampleNearest(valueArrays, inWidth, inHeight, outWidth, outHeight) {
|
||
|
|
const relX = inWidth / outWidth;
|
||
|
|
const relY = inHeight / outHeight;
|
||
|
|
return valueArrays.map((array) => {
|
||
|
|
const newArray = copyNewSize(array, outWidth, outHeight);
|
||
|
|
for (let y = 0; y < outHeight; ++y) {
|
||
|
|
const cy = Math.min(Math.round(relY * y), inHeight - 1);
|
||
|
|
for (let x = 0; x < outWidth; ++x) {
|
||
|
|
const cx = Math.min(Math.round(relX * x), inWidth - 1);
|
||
|
|
const value = array[(cy * inWidth) + cx];
|
||
|
|
newArray[(y * outWidth) + x] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return newArray;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// simple linear interpolation, code from:
|
||
|
|
// https://en.wikipedia.org/wiki/Linear_interpolation#Programming_language_support
|
||
|
|
function lerp(v0, v1, t) {
|
||
|
|
return ((1 - t) * v0) + (t * v1);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resample the input arrays using bilinear interpolation.
|
||
|
|
* @param {TypedArray[]} valueArrays The input arrays to resample
|
||
|
|
* @param {number} inWidth The width of the input rasters
|
||
|
|
* @param {number} inHeight The height of the input rasters
|
||
|
|
* @param {number} outWidth The desired width of the output rasters
|
||
|
|
* @param {number} outHeight The desired height of the output rasters
|
||
|
|
* @returns {TypedArray[]} The resampled rasters
|
||
|
|
*/
|
||
|
|
export function resampleBilinear(valueArrays, inWidth, inHeight, outWidth, outHeight) {
|
||
|
|
const relX = inWidth / outWidth;
|
||
|
|
const relY = inHeight / outHeight;
|
||
|
|
|
||
|
|
return valueArrays.map((array) => {
|
||
|
|
const newArray = copyNewSize(array, outWidth, outHeight);
|
||
|
|
for (let y = 0; y < outHeight; ++y) {
|
||
|
|
const rawY = relY * y;
|
||
|
|
|
||
|
|
const yl = Math.floor(rawY);
|
||
|
|
const yh = Math.min(Math.ceil(rawY), (inHeight - 1));
|
||
|
|
|
||
|
|
for (let x = 0; x < outWidth; ++x) {
|
||
|
|
const rawX = relX * x;
|
||
|
|
const tx = rawX % 1;
|
||
|
|
|
||
|
|
const xl = Math.floor(rawX);
|
||
|
|
const xh = Math.min(Math.ceil(rawX), (inWidth - 1));
|
||
|
|
|
||
|
|
const ll = array[(yl * inWidth) + xl];
|
||
|
|
const hl = array[(yl * inWidth) + xh];
|
||
|
|
const lh = array[(yh * inWidth) + xl];
|
||
|
|
const hh = array[(yh * inWidth) + xh];
|
||
|
|
|
||
|
|
const value = lerp(
|
||
|
|
lerp(ll, hl, tx),
|
||
|
|
lerp(lh, hh, tx),
|
||
|
|
rawY % 1,
|
||
|
|
);
|
||
|
|
newArray[(y * outWidth) + x] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return newArray;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resample the input arrays using the selected resampling method.
|
||
|
|
* @param {TypedArray[]} valueArrays The input arrays to resample
|
||
|
|
* @param {number} inWidth The width of the input rasters
|
||
|
|
* @param {number} inHeight The height of the input rasters
|
||
|
|
* @param {number} outWidth The desired width of the output rasters
|
||
|
|
* @param {number} outHeight The desired height of the output rasters
|
||
|
|
* @param {string} [method = 'nearest'] The desired resampling method
|
||
|
|
* @returns {TypedArray[]} The resampled rasters
|
||
|
|
*/
|
||
|
|
export function resample(valueArrays, inWidth, inHeight, outWidth, outHeight, method = 'nearest') {
|
||
|
|
switch (method.toLowerCase()) {
|
||
|
|
case 'nearest':
|
||
|
|
return resampleNearest(valueArrays, inWidth, inHeight, outWidth, outHeight);
|
||
|
|
case 'bilinear':
|
||
|
|
case 'linear':
|
||
|
|
return resampleBilinear(valueArrays, inWidth, inHeight, outWidth, outHeight);
|
||
|
|
default:
|
||
|
|
throw new Error(`Unsupported resampling method: '${method}'`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resample the pixel interleaved input array using nearest neighbor value selection.
|
||
|
|
* @param {TypedArray} valueArrays The input arrays to resample
|
||
|
|
* @param {number} inWidth The width of the input rasters
|
||
|
|
* @param {number} inHeight The height of the input rasters
|
||
|
|
* @param {number} outWidth The desired width of the output rasters
|
||
|
|
* @param {number} outHeight The desired height of the output rasters
|
||
|
|
* @param {number} samples The number of samples per pixel for pixel
|
||
|
|
* interleaved data
|
||
|
|
* @returns {TypedArray} The resampled raster
|
||
|
|
*/
|
||
|
|
export function resampleNearestInterleaved(
|
||
|
|
valueArray, inWidth, inHeight, outWidth, outHeight, samples) {
|
||
|
|
const relX = inWidth / outWidth;
|
||
|
|
const relY = inHeight / outHeight;
|
||
|
|
|
||
|
|
const newArray = copyNewSize(valueArray, outWidth, outHeight, samples);
|
||
|
|
for (let y = 0; y < outHeight; ++y) {
|
||
|
|
const cy = Math.min(Math.round(relY * y), inHeight - 1);
|
||
|
|
for (let x = 0; x < outWidth; ++x) {
|
||
|
|
const cx = Math.min(Math.round(relX * x), inWidth - 1);
|
||
|
|
for (let i = 0; i < samples; ++i) {
|
||
|
|
const value = valueArray[(cy * inWidth * samples) + (cx * samples) + i];
|
||
|
|
newArray[(y * outWidth * samples) + (x * samples) + i] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return newArray;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resample the pixel interleaved input array using bilinear interpolation.
|
||
|
|
* @param {TypedArray} valueArrays The input arrays to resample
|
||
|
|
* @param {number} inWidth The width of the input rasters
|
||
|
|
* @param {number} inHeight The height of the input rasters
|
||
|
|
* @param {number} outWidth The desired width of the output rasters
|
||
|
|
* @param {number} outHeight The desired height of the output rasters
|
||
|
|
* @param {number} samples The number of samples per pixel for pixel
|
||
|
|
* interleaved data
|
||
|
|
* @returns {TypedArray} The resampled raster
|
||
|
|
*/
|
||
|
|
export function resampleBilinearInterleaved(
|
||
|
|
valueArray, inWidth, inHeight, outWidth, outHeight, samples) {
|
||
|
|
const relX = inWidth / outWidth;
|
||
|
|
const relY = inHeight / outHeight;
|
||
|
|
const newArray = copyNewSize(valueArray, outWidth, outHeight, samples);
|
||
|
|
for (let y = 0; y < outHeight; ++y) {
|
||
|
|
const rawY = relY * y;
|
||
|
|
|
||
|
|
const yl = Math.floor(rawY);
|
||
|
|
const yh = Math.min(Math.ceil(rawY), (inHeight - 1));
|
||
|
|
|
||
|
|
for (let x = 0; x < outWidth; ++x) {
|
||
|
|
const rawX = relX * x;
|
||
|
|
const tx = rawX % 1;
|
||
|
|
|
||
|
|
const xl = Math.floor(rawX);
|
||
|
|
const xh = Math.min(Math.ceil(rawX), (inWidth - 1));
|
||
|
|
|
||
|
|
for (let i = 0; i < samples; ++i) {
|
||
|
|
const ll = valueArray[(yl * inWidth * samples) + (xl * samples) + i];
|
||
|
|
const hl = valueArray[(yl * inWidth * samples) + (xh * samples) + i];
|
||
|
|
const lh = valueArray[(yh * inWidth * samples) + (xl * samples) + i];
|
||
|
|
const hh = valueArray[(yh * inWidth * samples) + (xh * samples) + i];
|
||
|
|
|
||
|
|
const value = lerp(
|
||
|
|
lerp(ll, hl, tx),
|
||
|
|
lerp(lh, hh, tx),
|
||
|
|
rawY % 1,
|
||
|
|
);
|
||
|
|
newArray[(y * outWidth * samples) + (x * samples) + i] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return newArray;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resample the pixel interleaved input array using the selected resampling method.
|
||
|
|
* @param {TypedArray} valueArray The input array to resample
|
||
|
|
* @param {number} inWidth The width of the input rasters
|
||
|
|
* @param {number} inHeight The height of the input rasters
|
||
|
|
* @param {number} outWidth The desired width of the output rasters
|
||
|
|
* @param {number} outHeight The desired height of the output rasters
|
||
|
|
* @param {number} samples The number of samples per pixel for pixel
|
||
|
|
* interleaved data
|
||
|
|
* @param {string} [method = 'nearest'] The desired resampling method
|
||
|
|
* @returns {TypedArray} The resampled rasters
|
||
|
|
*/
|
||
|
|
export function resampleInterleaved(valueArray, inWidth, inHeight, outWidth, outHeight, samples, method = 'nearest') {
|
||
|
|
switch (method.toLowerCase()) {
|
||
|
|
case 'nearest':
|
||
|
|
return resampleNearestInterleaved(
|
||
|
|
valueArray, inWidth, inHeight, outWidth, outHeight, samples,
|
||
|
|
);
|
||
|
|
case 'bilinear':
|
||
|
|
case 'linear':
|
||
|
|
return resampleBilinearInterleaved(
|
||
|
|
valueArray, inWidth, inHeight, outWidth, outHeight, samples,
|
||
|
|
);
|
||
|
|
default:
|
||
|
|
throw new Error(`Unsupported resampling method: '${method}'`);
|
||
|
|
}
|
||
|
|
}
|