Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Commit

Permalink
feat(CLI): Adds CLI tool ⌨ (#38)
Browse files Browse the repository at this point in the history
* chore(package): update dependencies

https://greenkeeper.io/

* feat(CLI): Adds CLI tool ⌨

- Adds CLI tool to access SSIM
- Updates dependencies
- Adds babel2016 presets
- Adds tests for default parameters
- Removes some node build checks. They were slowing down the build (6min) and were overkill.
  Testing current version of node and 0.12 should be enough to ensure compatibility
  • Loading branch information
Oscar committed Nov 12, 2016
1 parent c9b6d3d commit daac7ee
Show file tree
Hide file tree
Showing 21 changed files with 216 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"presets": ["es2015"]
"presets": ["es2015", "es2016"]
}
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Run this code on [runkit](https://runkit.com/obartra/runkit-npm-ssim-js).

For more advanced usage, check the wiki [here](https://github.com/obartra/ssim/wiki/Usage).

## CLI

There's also a small CLI tool that you can call from your npm scripts as `ssim <img1> <img2>`. Run `ssim --help` to see additional options.

### Output

| Parameter | Description |
Expand All @@ -66,5 +70,5 @@ The code is fully documented. Documentation is hosted by doclets.io and availabl
| Code Quality | [![Code Climate](https://codeclimate.com/github/obartra/ssim/badges/gpa.svg)](https://codeclimate.com/github/obartra/ssim) [![Issue Count](https://codeclimate.com/github/obartra/ssim/badges/issue_count.svg)](https://codeclimate.com/github/obartra/ssim) |
| Versioning | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) [![npm](https://img.shields.io/npm/v/ssim.js.svg)](https://www.npmjs.com/package/ssim.js) |
| Dependencies | [![Known Vulnerabilities](https://snyk.io/test/github/obartra/ssim/badge.svg)](https://snyk.io/test/github/obartra/ssim) [![DavidDM](https://david-dm.org/obartra/ssim.svg)](https://david-dm.org/obartra/ssim) |
| Environments | ![](https://img.shields.io/badge/node-0.12-brightgreen.svg) ![](https://img.shields.io/badge/node-5.7.0-brightgreen.svg) ![](https://img.shields.io/badge/node-6.1-brightgreen.svg) ![](https://img.shields.io/badge/node-7.0-brightgreen.svg) |
| Environments | ![](https://img.shields.io/badge/node-0.12-brightgreen.svg) ![](https://img.shields.io/badge/node-7.0-brightgreen.svg) |
| Documentation | [![InchCI](https://inch-ci.org/github/obartra/ssim.svg?branch=master)](https://inch-ci.org/github/obartra/ssim) |
8 changes: 2 additions & 6 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
machine:
node:
version: 7.0.0
version: 7.1.0
environment:
NODE_CURRENT: 7.0.0
NODE_6: 6.1.0
NODE_5: 5.7.0
NODE_CURRENT: 7.1.0
NODE_012: 0.12.9
general:
artifacts:
Expand All @@ -23,8 +21,6 @@ test:
- npm run build
# `canvas` needs a different install depending on the node version in use.
- nvm use $NODE_012 && rm -rf node_modules && npm i && npm run e2e:ivc || exit 1
- nvm use $NODE_5 && rm -rf node_modules && npm i && npm run e2e:ivc || exit 1
- nvm install $NODE_6 && rm -rf node_modules && npm i && npm run e2e:ivc || exit 1
# Clean up and switch back to current node version
- nvm use $NODE_CURRENT && rm -rf node_modules && npm i
- npm run e2e || exit 1
Expand Down
23 changes: 23 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env node

const yargs = require('yargs');
const ssim = require('./dist/ssim');
const { version } = require('./version.js');

const argv = yargs
.usage('$0 ssim <img> <img> - Compares 2 images (img can be a URL or a filepath)')
.example('ssim img1.png img2.png')
.example('ssim https://url.jpg https://url2.jpg')
.demand(2)
.help()
.wrap(100)
.version(version)
.argv;

ssim(argv._[0], argv._[1])
.then(({ mssim, performance }) => {
process.stdout.write(`SSIM: ${mssim.toPrecision(4)} (${performance}ms)\n`);
})
.catch((error) => {
process.stdout.write(`Oops something went wrong 😓 ${error.stack}\n`);
});
1 change: 0 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-console */
const { readpixels } = require('./src/readpixels');
const { rgb2gray } = require('./src/matlab');
const { mean2d } = require('./src/math');
Expand Down
30 changes: 19 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"version": "0.0.0-semantically-released",
"scripts": {
"commit": "git-cz",
"cover": "istanbul cover blue-tape spec/unit/{*,**/*}.spec.js",
"cover:check": "istanbul check",
"cover": "babel-istanbul cover blue-tape spec/unit/{*,**/*}.spec.js",
"cover:check": "babel-istanbul check",
"test": "blue-tape spec/unit/{*,**/*}.spec.js | tap-dot",
"e2e": "npm-run-all --parallel e2e:*",
"e2e:live": "blue-tape spec/e2e_dist/live.spec.js | tap-dot",
Expand Down Expand Up @@ -39,41 +39,49 @@
"bugs": {
"url": "https://github.com/obartra/ssim/issues"
},
"bin": {
"ssim": "./cli.js"
},
"files": [
"package.json",
"README.md",
"LICENSE",
"src",
"version.js",
"cli.js",
"dist/ssim.js",
"dist/ssim.js.map"
],
"homepage": "https://github.com/obartra/ssim#readme",
"dependencies": {
"bmp-js": "^0.0.2",
"canvas": "^1.6.2",
"image-type": "^2.1.0"
"image-type": "^2.1.0",
"yargs": "^6.3.0"
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-core": "^6.18.2",
"babel-istanbul": "^0.11.0",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"babel-preset-es2016": "^6.16.0",
"blue-tape": "^1.0.0",
"codeclimate-test-reporter": "^0.4.0",
"commitizen": "^2.8.6",
"condition-circle": "^1.5.0",
"core-js": "^2.4.1",
"cz-conventional-changelog": "^1.2.0",
"eslint": "^3.9.1",
"eslint-config-airbnb": "^12.0.0",
"eslint": "^3.10.0",
"eslint-config-airbnb": "^13.0.0",
"eslint-config-standard": "^6.2.1",
"eslint-plugin-import": "1.16.0",
"eslint-plugin-import": "2.2.0",
"eslint-plugin-jsx-a11y": "^2.2.3",
"eslint-plugin-promise": "^3.3.0",
"eslint-plugin-react": "^6.5.0",
"eslint-plugin-promise": "^3.3.2",
"eslint-plugin-react": "^6.6.0",
"eslint-plugin-standard": "^2.0.1",
"inchjs": "^0.4.1",
"istanbul": "^0.4.5",
"jsdoc": "^3.4.2",
"jsdoc": "^3.4.3",
"json-loader": "^0.5.4",
"nodemon": "^1.11.0",
"npm-run-all": "^3.1.1",
Expand All @@ -92,7 +100,7 @@
},
"greenkeeper": {
"ignore": [
"eslint-plugin-import"
"eslint-plugin-jsx-a11y"
]
}
}
2 changes: 1 addition & 1 deletion spec/e2e/live.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const ssim = require('../../dist/ssim');
const { roundTo } = require('../helpers_dist/round');
const scores = require('../samples/LIVE.json');

const tol = Math.pow(10, -5); // 0.00001, to account for rounding differences on the 6th decimal
const tol = 10 ** -5; // 0.00001, to account for rounding differences on the 6th decimal
const base = 'spec/samples/LIVE';

function createTest({ file, reference, mssim, type }) {
Expand Down
2 changes: 1 addition & 1 deletion spec/helpers/round.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function roundTo(num, precision = 3) {
return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
return Math.round(num * (10 ** precision)) / (10 ** precision);
}

function round(num) {
Expand Down
13 changes: 13 additions & 0 deletions spec/unit/cli.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const test = require('blue-tape');
const { exec } = require('child_process');

test('should be a function', (t) => {
exec('./cli.js ./spec/samples/lena/Q_09372.gif ./spec/samples/lena/color.jpg',
(err, stdout, stderr) => {
t.equal(err, null);
t.equal(stderr, '');
t.equal(stdout.indexOf('SSIM: 0.3006'), 0, stdout);
t.end();
});
});

13 changes: 7 additions & 6 deletions spec/unit/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ test('downsizing should produce comparable results between "fast" and "original"
);

test('ssim should be faster than originalSsim', t =>
Promise.all([
index(samples.avion, samples.avion_j2000_r1, { ssim: 'fast' }),
index(samples.avion, samples.avion_j2000_r1, { ssim: 'fast' })
.then(({ performance: fast }) => {
index(samples.avion, samples.avion_j2000_r1, { ssim: 'original' })
]).then(([{ performance: fast }, { performance: original }]) =>
t.equal(fast < original, true,
`fast SSIM implementation must be faster than original (${fast}ms vs ${original}ms)`)
)
.then(({ performance: original }) => {
t.equal(fast < original, true,
`fast SSIM implementation must be faster than original (${fast}ms vs ${original}ms)`);
});
})
);

function compare({ file, mssim, reference }, t) {
Expand Down
37 changes: 36 additions & 1 deletion spec/unit/matlab/conv2.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,14 @@ test('decomposed convolutions should match the matching matrix if rank is one',
const { data: out } = conv2(A, B, 'valid');
const { data: vhOut } = conv2(A, v, h, 'valid');

t.deepEqual(roundTo(out, 5), roundTo(vhOut, 5));
t.equal(out.height, vhOut.height);
t.equal(out.width, vhOut.width);

for (let i = 0; i < out.height; i++) {
for (let j = 0; j < out.width; j++) {
t.equal(get(out, i, j), get(vhOut, i, j));
}
}
t.end();
});

Expand Down Expand Up @@ -305,3 +312,31 @@ test('should convolve 2 1-D kernels', (t) => {

t.end();
});

test('decomposed convolutions should default to "full"', (t) => {
const A = {
data: [
0.4366211, 0.9054124, 0.5962102,
0.6371818, 0.1158246, 0.6470448,
0.0063498, 0.2951452, 0.6623801
],
width: 3,
height: 3
};
const v = {
data: [-0.30780, -0.38440, -0.30780],
width: 1,
height: 3
};
const h = {
data: [-0.30780, -0.38440, -0.30780],
width: 3,
height: 1
};

const { data: fullOut } = conv2(A, v, h, 'full');
const { data: defaultOut } = conv2(A, v, h);

t.deepEqual(defaultOut, fullOut);
t.end();
});
26 changes: 26 additions & 0 deletions spec/unit/matlab/filter2.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,29 @@ test('filter2 should match conv2 for symmetric filters', (t) => {
t.deepEqual(filterV, convV);
t.end();
});

test('filter2 shape should default to "same"', (t) => {
const mx = {
data: [
1, 2, 3, 4,
5, 6, 7, 8,
9, 0, 1, 2,
3, 4, 5, 6
],
width: 4,
height: 4
};
const f = {
data: [
0, 1,
1, 0
],
width: 2,
height: 2
};
const filterDefault = filter2(f, mx);
const filterSame = filter2(f, mx, 'same');

t.deepEqual(filterDefault, filterSame);
t.end();
});
8 changes: 8 additions & 0 deletions spec/unit/matlab/fspecial.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ test('should create a gaussian low pass filter of different dimensions', (t) =>

t.end();
});

test('should default to "gaussian" with length 3 and sigma of 1.5', (t) => {
const fspecialDefault = fspecial();
const fspecialGaussian = fspecial('gaussian', 3, 1.5);

t.deepEqual(fspecialDefault, fspecialGaussian);
t.end();
});
21 changes: 21 additions & 0 deletions spec/unit/matlab/imfilter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,24 @@ test('should match results between a filter and its decomposed counterparts', (t
t.deepEqual(out, vhOut);
t.end();
});

test('dimfilter should default to symmetric padding and resize size of "same"', (t) => {
const mx = {
data: [
1, 2, 3, 4,
5, 6, 7, 8,
9, 0, 1, 2,
3, 4, 5, 6
],
width: 4,
height: 4
};
const v = { data: [-1, -1], width: 1, height: 2 };
const h = { data: [-1, -1], width: 2, height: 1 };

const outDefault = dimfilter(mx, v, h);
const out = dimfilter(mx, v, h, 'symmetric', 'same');

t.deepEqual(out, outDefault);
t.end();
});
15 changes: 14 additions & 1 deletion spec/unit/matlab/normpdf.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const test = require('blue-tape');
const { normpdf } = require('../../../src/matlab/normpdf');
const { get } = require('../../helpers/round');

test('should match matlab results (normpdf)', (t) => {
test('should match matlab results', (t) => {
const A = {
data: [2, 1, 0, 1, 2],
width: 5,
Expand All @@ -24,3 +24,16 @@ test('should match matlab results (normpdf)', (t) => {
}
t.end();
});

test('should default to a length of 0 and sigma of 1', (t) => {
const A = {
data: [2, 1, 0, 1, 2],
width: 5,
height: 1
};
const out = normpdf(A, 0, 1);
const outDefault = normpdf(A);

t.deepEqual(out, outDefault);
t.end();
});
28 changes: 28 additions & 0 deletions spec/unit/ssim.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,34 @@ Object.keys(sampleCsv).forEach((key) => {
});

[fastSsim, originalSsim].forEach((ssim) => {
test('should honor max size parameter', (t) => {
const A = samples['24x18'].gray;
const B = samples['24x18-degraded'].gray;
const limMaxSize = Object.assign({}, options, {
downsample: 'original',
maxSize: 13
});
const ssimMap = ssim(A, B, limMaxSize);

t.equal(ssimMap.width, 14);
t.equal(ssimMap.height, 8);
t.end();
});

test('should default max size parameter to 256', (t) => {
const limMaxSize = Object.assign({}, options, {
downsample: 'original',
maxSize: undefined
});
const A = samples['24x18'].gray;
const B = samples['24x18-degraded'].gray;
const ssimMap = ssim(A, B, limMaxSize);

t.equal(ssimMap.width, 14);
t.equal(ssimMap.height, 8);
t.end();
});

test('should return 1 for equal data', (t) => {
const A = samples['24x18'].gray;
const ssimMap = ssim(A, A, options);
Expand Down
4 changes: 2 additions & 2 deletions src/matlab/fspecial.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function rangeSquare2d(length) {

for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
data[x * size + y] = Math.pow(x - length, 2) + Math.pow(y - length, 2);
data[x * size + y] = ((x - length) ** 2) + ((y - length) ** 2);
}
}

Expand All @@ -46,7 +46,7 @@ function gaussianFilter2d({ data: ref, width, height }, σ) {
const data = [];

for (let x = 0; x < ref.length; x++) {
data[x] = Math.exp(-ref[x] / (2 * Math.pow(σ, 2)));
data[x] = Math.exp(-ref[x] / (2 * (σ ** 2)));
}

return {
Expand Down

0 comments on commit daac7ee

Please sign in to comment.