├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── example
├── baboon2.png
└── example.js
├── package.json
├── resample.js
└── test
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 |
10 | pids
11 | logs
12 | results
13 |
14 | npm-debug.log
15 | node_modules/*
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.8"
4 | - "0.10"
5 | - "0.12"
6 | before_install:
7 | - npm install -g npm@">=1.4.6"
8 | sudo: false
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2013 Mikola Lysenko
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ndarray-resample
2 | ================
3 | Resamples an ndarray by an arbitrary (rational) factor using a sinc kernel.
4 |
5 | [](http://travis-ci.org/scijs/ndarray-resample)
6 |
7 | ## Example
8 | Here is a simple example showing how to downsample an image:
9 |
10 | ```javascript
11 | var baboon = require("luminance")(require("baboon-image"))
12 | var x = require("zeros")([256,256])
13 | require("ndarray-resample")(x, baboon)
14 | require("save-pixels")(x, "png").pipe(process.stdout)
15 | ```
16 |
17 | #### Output
18 | 
19 |
20 | ##Install
21 | Install using [npm](https://www.npmjs.com/):
22 |
23 | npm install ndarray-resample
24 |
25 | ## API
26 | #### `require("ndarray-resample")(output, input[, clamp_lo, clamp_hi])`
27 | Resamples input by a factor of `output.shape/input.shape`, storing the result in output (this also means the factor can differ per dimension).
28 |
29 | * `output` gets the result of resampling
30 | * `input` is the array that gets resampled
31 | * `clamp_lo` is a threshold placed on the pixels
32 | * `clamp_hi` is an upper threhsold placed on the pixels
33 |
34 | **Note** that the pixel at the "origin" in the output corresponds to the pixel at the origin in the input. Also, the boundary conditions are periodic (for now).
35 |
36 | ## License
37 | (c) 2013-2015 Mikola Lysenko, Jasper van de Gronde. MIT License
38 |
--------------------------------------------------------------------------------
/example/baboon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scijs/ndarray-resample/73fcb86993d8cb3bad0f7fd829b95a912101c994/example/baboon2.png
--------------------------------------------------------------------------------
/example/example.js:
--------------------------------------------------------------------------------
1 | var baboon = require("luminance")(require("baboon-image"))
2 | var x = require("zeros")([256,256])
3 | require("../resample.js")(x, baboon)
4 | require("save-pixels")(x, "png").pipe(process.stdout)
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ndarray-resample",
3 | "version": "1.0.1",
4 | "description": "Resample using sinc kernels",
5 | "main": "resample.js",
6 | "directories": {
7 | "example": "example"
8 | },
9 | "browserify": {
10 | "transform": ["cwise"]
11 | },
12 | "dependencies": {
13 | "ndarray-ops": "^1.2.2",
14 | "ndarray-fft": "^1.0.0",
15 | "ndarray-scratch": "^1.1.1",
16 | "cwise": "^1.0.7"
17 | },
18 | "devDependencies": {
19 | "ndarray": "^1.0.18",
20 | "baboon-image": "^2.0.0",
21 | "luminance": "^1.0.3",
22 | "ndarray-unpack": "^1.0.0",
23 | "save-pixels": "^2.2.0",
24 | "zeros": "^1.0.0",
25 | "tape": "^4.0.0",
26 | "test-fuzzy-array": "^1.0.1"
27 | },
28 | "scripts": {
29 | "test": "tape test/*.js"
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "git://github.com/scijs/ndarray-resample.git"
34 | },
35 | "keywords": [
36 | "scijs",
37 | "downsample",
38 | "upsample",
39 | "resample",
40 | "ndarray",
41 | "sinc",
42 | "interpolation"
43 | ],
44 | "author": "Mikola Lysenko",
45 | "contributors": [
46 | "Jasper van de Gronde
"
47 | ],
48 | "license": "MIT",
49 | "readmeFilename": "README.md",
50 | "gitHead": "5018571a31388da17be61d3a026df4ac0b2d179d",
51 | "bugs": {
52 | "url": "https://github.com/scijs/ndarray-resample/issues"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/resample.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | var fft = require("ndarray-fft")
4 | var pool = require("ndarray-scratch")
5 | var ops = require("ndarray-ops")
6 | var cwise = require("cwise")
7 |
8 | var clampScale = cwise({
9 | args:["array", "array", "scalar", "scalar", "scalar"],
10 | body: function clampScale(out, inp, s, l, h) {
11 | var x = inp * s
12 | if(x < l) { x = l }
13 | if(x > h) { x = h }
14 | out = x
15 | }
16 | })
17 |
18 |
19 | function resample(out, inp, clamp_lo, clamp_hi) {
20 | if(typeof clamp_lo === "undefined") {
21 | clamp_lo = -Infinity
22 | }
23 | if(typeof clamp_hi === "undefined") {
24 | clamp_hi = Infinity
25 | }
26 |
27 | var ishp = inp.shape
28 | var oshp = out.shape
29 |
30 | if (inp.shape.length !== out.shape.length) throw new Error("ndarray-resample: input and output arrays should have the same dimensions")
31 |
32 | var v, zeroInds = ishp.map(function(){return 0})
33 | if(out.size === 1) {
34 | v = ops.sum(inp)/inp.size
35 | if(v < clamp_lo) { v = clamp_lo }
36 | if(v > clamp_hi) { v = clamp_hi }
37 | out.set.apply(out, zeroInds.concat(v))
38 | return
39 | } else if (inp.size === 1) {
40 | v = inp.get.apply(inp, zeroInds)
41 | if(v < clamp_lo) { v = clamp_lo }
42 | if(v > clamp_hi) { v = clamp_hi }
43 | ops.assigns(out, v)
44 | return
45 | }
46 |
47 | var d = ishp.length
48 | var mshp = new Array(d), initToZero = false
49 | for(var i=0; i ishp[i]) initToZero = true // When upsampling, initialize the Fourier components of the output to zero
52 | }
53 |
54 | var x = pool.malloc(ishp)
55 | , y = pool.malloc(ishp)
56 |
57 | ops.assign(x, inp)
58 | ops.assigns(y, 0.0)
59 |
60 | fft(1, x, y)
61 |
62 | var lo = x.lo
63 | , hi = x.hi
64 |
65 | var s = pool.malloc(oshp)
66 | , t = pool.malloc(oshp)
67 | if (initToZero) {
68 | ops.assigns(s, 0.0)
69 | ops.assigns(t, 0.0)
70 | }
71 |
72 | var nr = new Array(d)
73 | , a = new Array(d)
74 | , b = new Array(d)
75 | , io = new Array(d)
76 | for(var i=0; i<1<>>1 // Take ceil(mshp[j]/2)) low frequencies (for example [0,1] for both mshp[j]==3 and mshp[j]==4)
80 | a[j] = 0
81 | b[j] = 0
82 | io[j] = 0
83 | } else { // Take the negative frequencies for this dimension
84 | nr[j] = mshp[j] - ((mshp[j]+1)>>>1) // Take the rest ([-1] for mshp[j]==3, and [-2,-1] for mshp[j]==4)
85 | if(nr[j] === 0) {
86 | continue
87 | }
88 | a[j] = oshp[j] - nr[j]
89 | b[j] = ishp[j] - nr[j]
90 | // If mshp[j] is even, set the first imaginary values (along this dimension) to zero.
91 | // For example, if mshp[j]==4, 2 and -2 correspond to the same frequency, and should be the average of the amplitudes for 2 and -2.
92 | // Since the input is real, the Fourier transform has Hermitian symmetry, and we can simply take one or the other and set the corresponding imaginary coefficient(s) to zero.
93 | // Note that when upsampling, this means that we get a asymmetric response (for example, -2, but not 2 has a non-zero weight), but this does not matter, since the weight is real anyway (again, given Hermitian symmetry).
94 | io[j] = (mshp[j]&1) ? 0 : 1
95 | }
96 | }
97 | ops.assign(hi.apply(lo.apply(s, a), nr), hi.apply(lo.apply(x, b), nr))
98 | ops.assign(lo.apply(hi.apply(lo.apply(t, a), nr), io), lo.apply(hi.apply(lo.apply(y, b), nr), io))
99 | ops.assigns(hi.apply(hi.apply(lo.apply(t, a), nr), io), 0.0)
100 | }
101 |
102 | fft(-1, s, t)
103 | clampScale(out, s, out.size/inp.size, clamp_lo, clamp_hi)
104 |
105 | pool.free(x)
106 | pool.free(y)
107 | pool.free(s)
108 | pool.free(t)
109 | }
110 |
111 | module.exports = resample
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | var resample = require("../resample.js")
2 | var ndarray = require("ndarray")
3 | var fuzz = require("test-fuzzy-array")
4 |
5 | require("tape")("resample 1D", function(t) {
6 | var almostEqual = fuzz(t, 0.000001)
7 | var array1 = ndarray([3.5])
8 | , array1Out = ndarray([0])
9 | , array3 = ndarray([1,2,3])
10 | , array6Out = ndarray([0,0,0,0,0,0])
11 | , ref3 = [1,1,2,3,3,2]
12 | , array4 = ndarray([1,2,3,4])
13 | , array8Out = ndarray([0,0,0,0,0,0,0,0])
14 | , ref4 = [1.,1.0857864376269049,2.,2.5,3.,3.914213562373095,4.,2.5]
15 | , array6 = ndarray([1,2,3,4,5,6])
16 | , array3Out = ndarray([0,0,0])
17 | , ref6 = [2.5,2.5,5.5]
18 | , array8 = ndarray([1,2,3,4,5,6,7,8])
19 | , array4Out = ndarray([0,0,0,0])
20 | , ref8 = [3.,2.585786437626905,5.,7.414213562373095]
21 | , ref83 = [3.5,2.9092297248239727,7.090770275176027]
22 |
23 | resample(array3Out, array1)
24 | almostEqual(array3Out.data, [3.5,3.5,3.5], "input length: 1, output length: 6, not clamped")
25 |
26 | resample(array1Out, array6)
27 | almostEqual(array1Out.data, [3.5], "input length: 6, output length: 1, not clamped")
28 |
29 | resample(array3Out, array6)
30 | almostEqual(array3Out.data, ref6, "input length: 6, output length: 3, not clamped")
31 |
32 | resample(array6Out, array3)
33 | almostEqual(array6Out.data, ref3, "input length: 3, output length: 6, not clamped")
34 |
35 | resample(array4Out, array8)
36 | almostEqual(array4Out.data, ref8, "input length: 8, output length: 4, not clamped")
37 |
38 | resample(array8Out, array4)
39 | almostEqual(array8Out.data, ref4, "input length: 4, output length: 8, not clamped")
40 |
41 | resample(array3Out, array8)
42 | almostEqual(array3Out.data, ref83, "input length: 8, output length: 3, not clamped")
43 |
44 | t.end()
45 | })
--------------------------------------------------------------------------------