Spaces:
Sleeping
Sleeping
File size: 4,252 Bytes
8d955a0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 'use strict'
module.exports = calcSDF
var INF = 1e20
function calcSDF(src, options) {
if (!options) options = {}
var cutoff = options.cutoff == null ? 0.25 : options.cutoff
var radius = options.radius == null ? 8 : options.radius
var channel = options.channel || 0
var w, h, size, data, intData, stride, ctx, canvas, imgData, i, l
// handle image container
if (ArrayBuffer.isView(src) || Array.isArray(src)) {
if (!options.width || !options.height) throw Error('For raw data width and height should be provided by options')
w = options.width, h = options.height
data = src
if (!options.stride) stride = Math.floor(src.length / w / h)
else stride = options.stride
}
else {
if (window.HTMLCanvasElement && src instanceof window.HTMLCanvasElement) {
canvas = src
ctx = canvas.getContext('2d')
w = canvas.width, h = canvas.height
imgData = ctx.getImageData(0, 0, w, h)
data = imgData.data
stride = 4
}
else if (window.CanvasRenderingContext2D && src instanceof window.CanvasRenderingContext2D) {
canvas = src.canvas
ctx = src
w = canvas.width, h = canvas.height
imgData = ctx.getImageData(0, 0, w, h)
data = imgData.data
stride = 4
}
else if (window.ImageData && src instanceof window.ImageData) {
imgData = src
w = src.width, h = src.height
data = imgData.data
stride = 4
}
}
size = Math.max(w, h)
//convert int data to floats
if ((window.Uint8ClampedArray && data instanceof window.Uint8ClampedArray) || (window.Uint8Array && data instanceof window.Uint8Array)) {
intData = data
data = Array(w*h)
for (i = 0, l = Math.floor(intData.length / stride); i < l; i++) {
data[i] = intData[i*stride + channel] / 255
}
}
else {
if (stride !== 1) throw Error('Raw data can have only 1 value per pixel')
}
// temporary arrays for the distance transform
var gridOuter = Array(w * h)
var gridInner = Array(w * h)
var f = Array(size)
var d = Array(size)
var z = Array(size + 1)
var v = Array(size)
for (i = 0, l = w * h; i < l; i++) {
var a = data[i]
gridOuter[i] = a === 1 ? 0 : a === 0 ? INF : Math.pow(Math.max(0, 0.5 - a), 2)
gridInner[i] = a === 1 ? INF : a === 0 ? 0 : Math.pow(Math.max(0, a - 0.5), 2)
}
edt(gridOuter, w, h, f, d, v, z)
edt(gridInner, w, h, f, d, v, z)
var dist = window.Float32Array ? new Float32Array(w * h) : new Array(w * h)
for (i = 0, l = w*h; i < l; i++) {
dist[i] = Math.min(Math.max(1 - ( (gridOuter[i] - gridInner[i]) / radius + cutoff), 0), 1)
}
return dist
}
// 2D Euclidean distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/dt/
function edt(data, width, height, f, d, v, z) {
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
f[y] = data[y * width + x]
}
edt1d(f, d, v, z, height)
for (y = 0; y < height; y++) {
data[y * width + x] = d[y]
}
}
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
f[x] = data[y * width + x]
}
edt1d(f, d, v, z, width)
for (x = 0; x < width; x++) {
data[y * width + x] = Math.sqrt(d[x])
}
}
}
// 1D squared distance transform
function edt1d(f, d, v, z, n) {
v[0] = 0;
z[0] = -INF
z[1] = +INF
for (var q = 1, k = 0; q < n; q++) {
var s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k])
while (s <= z[k]) {
k--
s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k])
}
k++
v[k] = q
z[k] = s
z[k + 1] = +INF
}
for (q = 0, k = 0; q < n; q++) {
while (z[k + 1] < q) k++
d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]
}
}
|