/
sauvola.js
42 lines (41 loc) · 1.12 KB
/
sauvola.js
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
/**
* sauvola thresholding
*/
export default class SauvolaThresholding {
// https://schima.hatenablog.com/entry/2013/10/19/085019
/**
* @param {number} [n] Size of local range
* @param {number} [k] Tuning parameter
* @param {number} [r] Tuning parameter
*/
constructor(n = 3, k = 0.1, r = 1) {
this._n = n
this._k = k
this._r = r
}
/**
* Returns thresholded values.
*
* @param {Array<Array<number>>} x Training data
* @returns {Array<Array<0 | 1>>} Predicted values
*/
predict(x) {
const offset = Math.floor(this._n / 2)
const p = []
for (let i = 0; i < x.length; i++) {
p[i] = []
for (let j = 0; j < x[i].length; j++) {
const nears = []
for (let s = Math.max(0, i - offset); s <= Math.min(x.length - 1, i + offset); s++) {
for (let t = Math.max(0, j - offset); t <= Math.min(x[i].length - 1, j + offset); t++) {
nears.push(x[s][t])
}
}
const m = nears.reduce((s, v) => s + v, 0) / nears.length
const s = Math.sqrt(nears.reduce((s, v) => s + (v - m) ** 2, 0) / nears.length)
p[i][j] = x[i][j] < m * (1 + this._k * (s / this._r - 1)) ? 0 : 1
}
}
return p
}
}