1
0
Fork 0

Delete script/openseadragon/openseadragon-filtering.js

This commit is contained in:
TED A. ⭕ 2023-09-30 18:14:43 +00:00 committed by GitHub
parent 5f98e66651
commit 4f9f9411cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 0 additions and 493 deletions

View File

@ -1,493 +0,0 @@
/*
* This software was developed at the National Institute of Standards and
* Technology by employees of the Federal Government in the course of
* their official duties. Pursuant to title 17 Section 105 of the United
* States Code this software is not subject to copyright protection and is
* in the public domain. This software is an experimental system. NIST assumes
* no responsibility whatsoever for its use by other parties, and makes no
* guarantees, expressed or implied, about its quality, reliability, or
* any other characteristic. We would appreciate acknowledgement if the
* software is used.
*/
/**
*
* @author Antoine Vandecreme <antoine.vandecreme@nist.gov>
*/
(function() {
'use strict';
var $ = window.OpenSeadragon;
if (!$) {
$ = require('openseadragon');
if (!$) {
throw new Error('OpenSeadragon is missing.');
}
}
// Requires OpenSeadragon >=2.1
if (!$.version || $.version.major < 2 ||
$.version.major === 2 && $.version.minor < 1) {
throw new Error(
'Filtering plugin requires OpenSeadragon version >= 2.1');
}
$.Viewer.prototype.setFilterOptions = function(options) {
if (!this.filterPluginInstance) {
options = options || {};
options.viewer = this;
this.filterPluginInstance = new $.FilterPlugin(options);
} else {
setOptions(this.filterPluginInstance, options);
}
};
/**
* @class FilterPlugin
* @param {Object} options The options
* @param {OpenSeadragon.Viewer} options.viewer The viewer to attach this
* plugin to.
* @param {String} [options.loadMode='async'] Set to sync to have the filters
* applied synchronously. It will only work if the filters are all synchronous.
* Note that depending on how complex the filters are, it may also hang the browser.
* @param {Object[]} options.filters The filters to apply to the images.
* @param {OpenSeadragon.TiledImage[]} options.filters[x].items The tiled images
* on which to apply the filter.
* @param {function|function[]} options.filters[x].processors The processing
* function(s) to apply to the images. The parameters of this function are
* the context to modify and a callback to call upon completion.
*/
$.FilterPlugin = function(options) {
options = options || {};
if (!options.viewer) {
throw new Error('A viewer must be specified.');
}
var self = this;
this.viewer = options.viewer;
this.viewer.addHandler('tile-loaded', tileLoadedHandler);
this.viewer.addHandler('tile-drawing', tileDrawingHandler);
// filterIncrement allows to determine whether a tile contains the
// latest filters results.
this.filterIncrement = 0;
setOptions(this, options);
function tileLoadedHandler(event) {
var processors = getFiltersProcessors(self, event.tiledImage);
if (processors.length === 0) {
return;
}
var tile = event.tile;
var image = event.data;
if (image !== null && image !== undefined) {
var canvas = window.document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
tile._renderedContext = context;
var callback = event.getCompletionCallback();
applyFilters(context, processors, callback);
tile._filterIncrement = self.filterIncrement;
}
}
function applyFilters(context, filtersProcessors, callback) {
if (callback) {
var currentIncrement = self.filterIncrement;
var callbacks = [];
for (var i = 0; i < filtersProcessors.length - 1; i++) {
(function(i) {
callbacks[i] = function() {
// If the increment has changed, stop the computation
// chain immediately.
if (self.filterIncrement !== currentIncrement) {
return;
}
filtersProcessors[i + 1](context, callbacks[i + 1]);
};
})(i);
}
callbacks[filtersProcessors.length - 1] = function() {
// If the increment has changed, do not call the callback.
// (We don't want OSD to draw an outdated tile in the canvas).
if (self.filterIncrement !== currentIncrement) {
return;
}
callback();
};
filtersProcessors[0](context, callbacks[0]);
} else {
for (var i = 0; i < filtersProcessors.length; i++) {
filtersProcessors[i](context, function() {
});
}
}
}
function tileDrawingHandler(event) {
var tile = event.tile;
var rendered = event.rendered;
if (rendered._filterIncrement === self.filterIncrement) {
return;
}
var processors = getFiltersProcessors(self, event.tiledImage);
if (processors.length === 0) {
if (rendered._originalImageData) {
// Restore initial data.
rendered.putImageData(rendered._originalImageData, 0, 0);
delete rendered._originalImageData;
}
rendered._filterIncrement = self.filterIncrement;
return;
}
if (rendered._originalImageData) {
// The tile has been previously filtered (by another filter),
// restore it first.
rendered.putImageData(rendered._originalImageData, 0, 0);
} else {
rendered._originalImageData = rendered.getImageData(
0, 0, rendered.canvas.width, rendered.canvas.height);
}
if (tile._renderedContext) {
if (tile._filterIncrement === self.filterIncrement) {
var imgData = tile._renderedContext.getImageData(0, 0,
tile._renderedContext.canvas.width,
tile._renderedContext.canvas.height);
rendered.putImageData(imgData, 0, 0);
delete tile._renderedContext;
delete tile._filterIncrement;
rendered._filterIncrement = self.filterIncrement;
return;
}
delete tile._renderedContext;
delete tile._filterIncrement;
}
applyFilters(rendered, processors);
rendered._filterIncrement = self.filterIncrement;
}
};
function setOptions(instance, options) {
options = options || {};
var filters = options.filters;
instance.filters = !filters ? [] :
$.isArray(filters) ? filters : [filters];
for (var i = 0; i < instance.filters.length; i++) {
var filter = instance.filters[i];
if (!filter.processors) {
throw new Error('Filter processors must be specified.');
}
filter.processors = $.isArray(filter.processors) ?
filter.processors : [filter.processors];
}
instance.filterIncrement++;
if (options.loadMode === 'sync') {
instance.viewer.forceRedraw();
} else {
var itemsToReset = [];
for (var i = 0; i < instance.filters.length; i++) {
var filter = instance.filters[i];
if (!filter.items) {
itemsToReset = getAllItems(instance.viewer.world);
break;
}
if ($.isArray(filter.items)) {
for (var j = 0; j < filter.items.length; j++) {
addItemToReset(filter.items[j], itemsToReset);
}
} else {
addItemToReset(filter.items, itemsToReset);
}
}
for (var i = 0; i < itemsToReset.length; i++) {
itemsToReset[i].reset();
}
}
}
function addItemToReset(item, itemsToReset) {
if (itemsToReset.indexOf(item) >= 0) {
throw new Error('An item can not have filters ' +
'assigned multiple times.');
}
itemsToReset.push(item);
}
function getAllItems(world) {
var result = [];
for (var i = 0; i < world.getItemCount(); i++) {
result.push(world.getItemAt(i));
}
return result;
}
function getFiltersProcessors(instance, item) {
if (instance.filters.length === 0) {
return [];
}
var globalProcessors = null;
for (var i = 0; i < instance.filters.length; i++) {
var filter = instance.filters[i];
if (!filter.items) {
globalProcessors = filter.processors;
} else if (filter.items === item ||
$.isArray(filter.items) && filter.items.indexOf(item) >= 0) {
return filter.processors;
}
}
return globalProcessors ? globalProcessors : [];
}
$.Filters = {
THRESHOLDING: function(threshold) {
if (threshold < 0 || threshold > 255) {
throw new Error('Threshold must be between 0 and 255.');
}
return function(context, callback) {
var imgData = context.getImageData(
0, 0, context.canvas.width, context.canvas.height);
var pixels = imgData.data;
for (var i = 0; i < pixels.length; i += 4) {
var r = pixels[i];
var g = pixels[i + 1];
var b = pixels[i + 2];
var v = (r + g + b) / 3;
pixels[i] = pixels[i + 1] = pixels[i + 2] =
v < threshold ? 0 : 255;
}
context.putImageData(imgData, 0, 0);
callback();
};
},
BRIGHTNESS: function(adjustment) {
if (adjustment < -255 || adjustment > 255) {
throw new Error(
'Brightness adjustment must be between -255 and 255.');
}
var precomputedBrightness = [];
for (var i = 0; i < 256; i++) {
precomputedBrightness[i] = i + adjustment;
}
return function(context, callback) {
var imgData = context.getImageData(
0, 0, context.canvas.width, context.canvas.height);
var pixels = imgData.data;
for (var i = 0; i < pixels.length; i += 4) {
pixels[i] = precomputedBrightness[pixels[i]];
pixels[i + 1] = precomputedBrightness[pixels[i + 1]];
pixels[i + 2] = precomputedBrightness[pixels[i + 2]];
}
context.putImageData(imgData, 0, 0);
callback();
};
},
CONTRAST: function(adjustment) {
if (adjustment < 0) {
throw new Error('Contrast adjustment must be positive.');
}
var precomputedContrast = [];
for (var i = 0; i < 256; i++) {
precomputedContrast[i] = i * adjustment;
}
return function(context, callback) {
var imgData = context.getImageData(
0, 0, context.canvas.width, context.canvas.height);
var pixels = imgData.data;
for (var i = 0; i < pixels.length; i += 4) {
pixels[i] = precomputedContrast[pixels[i]];
pixels[i + 1] = precomputedContrast[pixels[i + 1]];
pixels[i + 2] = precomputedContrast[pixels[i + 2]];
}
context.putImageData(imgData, 0, 0);
callback();
};
},
GAMMA: function(adjustment) {
if (adjustment < 0) {
throw new Error('Gamma adjustment must be positive.');
}
var precomputedGamma = [];
for (var i = 0; i < 256; i++) {
precomputedGamma[i] = Math.pow(i / 255, adjustment) * 255;
}
return function(context, callback) {
var imgData = context.getImageData(
0, 0, context.canvas.width, context.canvas.height);
var pixels = imgData.data;
for (var i = 0; i < pixels.length; i += 4) {
pixels[i] = precomputedGamma[pixels[i]];
pixels[i + 1] = precomputedGamma[pixels[i + 1]];
pixels[i + 2] = precomputedGamma[pixels[i + 2]];
}
context.putImageData(imgData, 0, 0);
callback();
};
},
GREYSCALE: function() {
return function(context, callback) {
var imgData = context.getImageData(
0, 0, context.canvas.width, context.canvas.height);
var pixels = imgData.data;
for (var i = 0; i < pixels.length; i += 4) {
var val = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
pixels[i] = val;
pixels[i + 1] = val;
pixels[i + 2] = val;
}
context.putImageData(imgData, 0, 0);
callback();
};
},
INVERT: function() {
var precomputedInvert = [];
for (var i = 0; i < 256; i++) {
precomputedInvert[i] = 255 - i;
}
return function(context, callback) {
var imgData = context.getImageData(
0, 0, context.canvas.width, context.canvas.height);
var pixels = imgData.data;
for (var i = 0; i < pixels.length; i += 4) {
pixels[i] = precomputedInvert[pixels[i]];
pixels[i + 1] = precomputedInvert[pixels[i + 1]];
pixels[i + 2] = precomputedInvert[pixels[i + 2]];
}
context.putImageData(imgData, 0, 0);
callback();
};
},
MORPHOLOGICAL_OPERATION: function(kernelSize, comparator) {
if (kernelSize % 2 === 0) {
throw new Error('The kernel size must be an odd number.');
}
var kernelHalfSize = Math.floor(kernelSize / 2);
if (!comparator) {
throw new Error('A comparator must be defined.');
}
return function(context, callback) {
var width = context.canvas.width;
var height = context.canvas.height;
var imgData = context.getImageData(0, 0, width, height);
var originalPixels = context.getImageData(0, 0, width, height)
.data;
var offset;
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
offset = (y * width + x) * 4;
var r = originalPixels[offset];
var g = originalPixels[offset + 1];
var b = originalPixels[offset + 2];
for (var j = 0; j < kernelSize; j++) {
for (var i = 0; i < kernelSize; i++) {
var pixelX = x + i - kernelHalfSize;
var pixelY = y + j - kernelHalfSize;
if (pixelX >= 0 && pixelX < width &&
pixelY >= 0 && pixelY < height) {
offset = (pixelY * width + pixelX) * 4;
r = comparator(originalPixels[offset], r);
g = comparator(
originalPixels[offset + 1], g);
b = comparator(
originalPixels[offset + 2], b);
}
}
}
imgData.data[offset] = r;
imgData.data[offset + 1] = g;
imgData.data[offset + 2] = b;
}
}
context.putImageData(imgData, 0, 0);
callback();
};
},
CONVOLUTION: function(kernel) {
if (!$.isArray(kernel)) {
throw new Error('The kernel must be an array.');
}
var kernelSize = Math.sqrt(kernel.length);
if ((kernelSize + 1) % 2 !== 0) {
throw new Error('The kernel must be a square matrix with odd' +
'width and height.');
}
var kernelHalfSize = (kernelSize - 1) / 2;
return function(context, callback) {
var width = context.canvas.width;
var height = context.canvas.height;
var imgData = context.getImageData(0, 0, width, height);
var originalPixels = context.getImageData(0, 0, width, height)
.data;
var offset;
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var r = 0;
var g = 0;
var b = 0;
for (var j = 0; j < kernelSize; j++) {
for (var i = 0; i < kernelSize; i++) {
var pixelX = x + i - kernelHalfSize;
var pixelY = y + j - kernelHalfSize;
if (pixelX >= 0 && pixelX < width &&
pixelY >= 0 && pixelY < height) {
offset = (pixelY * width + pixelX) * 4;
var weight = kernel[j * kernelSize + i];
r += originalPixels[offset] * weight;
g += originalPixels[offset + 1] * weight;
b += originalPixels[offset + 2] * weight;
}
}
}
offset = (y * width + x) * 4;
imgData.data[offset] = r;
imgData.data[offset + 1] = g;
imgData.data[offset + 2] = b;
}
}
context.putImageData(imgData, 0, 0);
callback();
};
},
COLORMAP: function(cmap, ctr) {
var resampledCmap = cmap.slice(0);
var diff = 255 - ctr;
for(var i = 0; i < 256; i++) {
var position = 0;
if(i > ctr) {
position = Math.min((i - ctr) / diff * 128 + 128,255) | 0;
}else{
position = Math.max(0, i / (ctr / 128)) | 0;
}
resampledCmap[i] = cmap[position];
}
return function(context, callback) {
var imgData = context.getImageData(
0, 0, context.canvas.width, context.canvas.height);
var pxl = imgData.data;
for (var i = 0; i < pxl.length; i += 4) {
var v = (pxl[i] + pxl[i + 1] + pxl[i + 2]) / 3 | 0;
var c = resampledCmap[v];
pxl[i] = c[0];
pxl[i + 1] = c[1];
pxl[i + 2] = c[2];
}
context.putImageData(imgData, 0, 0);
callback();
};
}
};
}());