@blazediff/bin 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -25,7 +25,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/index.ts
27
27
  var import_core = __toESM(require("@blazediff/core"));
28
- async function blazeDiffBin(image1Path, image2Path, options) {
28
+ var import_gmsd = __toESM(require("@blazediff/gmsd"));
29
+ var isGmsdModeOptions = (options) => {
30
+ return options.mode === "gmsd";
31
+ };
32
+ async function bin(image1Path, image2Path, options) {
29
33
  const [image1, image2] = await Promise.all([
30
34
  options.transformer.transform(image1Path),
31
35
  options.transformer.transform(image2Path)
@@ -35,6 +39,40 @@ async function blazeDiffBin(image1Path, image2Path, options) {
35
39
  `Image dimensions do not match: ${image1.width}x${image1.height} vs ${image2.width}x${image2.height}`
36
40
  );
37
41
  }
42
+ if (isGmsdModeOptions(options)) {
43
+ let outputData2;
44
+ if (options.outputPath) {
45
+ outputData2 = new Uint8Array(image1.data.length);
46
+ }
47
+ const startTime2 = performance.now();
48
+ const score = (0, import_gmsd.default)(
49
+ image1.data,
50
+ image2.data,
51
+ outputData2,
52
+ image1.width,
53
+ image1.height,
54
+ options.options || {}
55
+ );
56
+ const duration2 = performance.now() - startTime2;
57
+ if (options.outputPath && outputData2) {
58
+ await options.transformer.write(
59
+ {
60
+ data: outputData2,
61
+ width: image1.width,
62
+ height: image1.height
63
+ },
64
+ options.outputPath
65
+ );
66
+ }
67
+ return {
68
+ mode: "gmsd",
69
+ width: image1.width,
70
+ height: image1.height,
71
+ outputData: outputData2,
72
+ duration: duration2,
73
+ score
74
+ };
75
+ }
38
76
  let outputData;
39
77
  if (options.outputPath) {
40
78
  outputData = new Uint8Array(image1.data.length);
@@ -46,7 +84,7 @@ async function blazeDiffBin(image1Path, image2Path, options) {
46
84
  outputData,
47
85
  image1.width,
48
86
  image1.height,
49
- options.coreOptions
87
+ options.options || {}
50
88
  );
51
89
  const duration = performance.now() - startTime;
52
90
  if (diffCount > 0 && options.outputPath && outputData) {
@@ -60,6 +98,7 @@ async function blazeDiffBin(image1Path, image2Path, options) {
60
98
  );
61
99
  }
62
100
  return {
101
+ mode: "diff",
63
102
  diffCount,
64
103
  width: image1.width,
65
104
  height: image1.height,
@@ -77,7 +116,12 @@ Arguments:
77
116
  image1 Path to the first image
78
117
  image2 Path to the second image
79
118
 
80
- Options:
119
+ General Options:
120
+ -m, --mode <mode> Comparison mode: "diff" or "gmsd" (default: diff)
121
+ --transformer <name> Specify transformer to use (e.g. pngjs, sharp)
122
+ -h, --help Show this help message
123
+
124
+ Diff Mode Options (default mode):
81
125
  -o, --output <path> Output path for the diff image
82
126
  -t, --threshold <num> Matching threshold (0 to 1, default: 0.1)
83
127
  -a, --alpha <num> Opacity of original image in diff (default: 0.1)
@@ -86,14 +130,23 @@ Options:
86
130
  --diff-color-alt <r,g,b> Alternative color for dark differences (default: same as diff-color)
87
131
  --include-aa Include anti-aliasing detection
88
132
  --diff-mask Draw diff over transparent background
89
- --transformer <name> Specify transformer to use (e.g. pngjs, sharp)
90
133
  --color-space <name> Specify color space to use (e.g. yiq, ycbcr)
91
- -h, --help Show this help message
134
+
135
+ GMSD Mode Options (--mode gmsd):
136
+ -o, --output <path> Output path for GMS similarity map (grayscale visualization)
137
+ --downsample <0|1> Downsample factor: 0=full-res, 1=2x downsample (default: 0)
138
+ --gmsd-c <num> Stability constant for GMSD (default: 170)
92
139
 
93
140
  Examples:
141
+ # Pixel-by-pixel diff (default)
94
142
  blazediff image1.png image2.png
95
143
  blazediff image1.png image2.png -o diff.png -t 0.05
96
144
  blazediff image1.png image2.png --threshold 0.2 --alpha 0.3
145
+
146
+ # GMSD similarity score
147
+ blazediff image1.png image2.png --mode gmsd
148
+ blazediff image1.png image2.png --mode gmsd --downsample 1
149
+ blazediff image1.png image2.png --mode gmsd -o gms-map.png
97
150
  `);
98
151
  }
99
152
  function parseRGB(colorStr) {
@@ -192,6 +245,40 @@ function parseArgs() {
192
245
  i++;
193
246
  }
194
247
  break;
248
+ case "-m":
249
+ case "--mode":
250
+ if (nextArg) {
251
+ if (nextArg !== "diff" && nextArg !== "gmsd") {
252
+ throw new Error(
253
+ `Invalid mode: ${nextArg}. Must be "diff" or "gmsd"`
254
+ );
255
+ }
256
+ options.mode = nextArg;
257
+ i++;
258
+ }
259
+ break;
260
+ case "--downsample":
261
+ if (nextArg) {
262
+ const downsample = parseInt(nextArg, 10);
263
+ if (downsample !== 0 && downsample !== 1) {
264
+ throw new Error(`Invalid downsample: ${nextArg}. Must be 0 or 1`);
265
+ }
266
+ options.downsample = downsample;
267
+ i++;
268
+ }
269
+ break;
270
+ case "--gmsd-c":
271
+ if (nextArg) {
272
+ const gmsdC = parseFloat(nextArg);
273
+ if (Number.isNaN(gmsdC) || gmsdC <= 0) {
274
+ throw new Error(
275
+ `Invalid gmsd-c: ${nextArg}. Must be a positive number`
276
+ );
277
+ }
278
+ options.gmsdC = gmsdC;
279
+ i++;
280
+ }
281
+ break;
195
282
  default:
196
283
  console.error(`Unknown option: ${arg}`);
197
284
  printUsage();
@@ -215,33 +302,65 @@ async function main() {
215
302
  try {
216
303
  const { image1, image2, options } = parseArgs();
217
304
  const transformer = await getTransformer(options.transformer);
218
- const result = await blazeDiffBin(image1, image2, {
219
- outputPath: options.outputPath,
220
- transformer,
221
- coreOptions: {
222
- threshold: options.threshold,
223
- alpha: options.alpha,
224
- aaColor: options.aaColor,
225
- diffColor: options.diffColor,
226
- diffColorAlt: options.diffColorAlt,
227
- includeAA: options.includeAA,
228
- diffMask: options.diffMask
229
- }
230
- });
231
- console.log(`matched in: ${result.duration.toFixed(2)}ms`);
232
- console.log(`dimensions: ${result.width}x${result.height}`);
233
- console.log(`different pixels: ${result.diffCount}`);
234
- console.log(
235
- `error: ${(result.diffCount / (result.width * result.height) * 100).toFixed(2)}%`
236
- );
237
- if (result.diffCount > 0 && result.outputData) {
238
- console.log(`diff image: ${options.outputPath}`);
305
+ let result;
306
+ if (options.mode === "gmsd") {
307
+ const binOptions = {
308
+ outputPath: options.outputPath,
309
+ transformer,
310
+ mode: "gmsd",
311
+ options: {
312
+ downsample: options.downsample || 0,
313
+ c: options.gmsdC
314
+ }
315
+ };
316
+ result = await bin(image1, image2, binOptions);
317
+ } else {
318
+ const binOptions = {
319
+ outputPath: options.outputPath,
320
+ transformer,
321
+ mode: "diff",
322
+ options: {
323
+ threshold: options.threshold,
324
+ alpha: options.alpha,
325
+ aaColor: options.aaColor,
326
+ diffColor: options.diffColor,
327
+ diffColorAlt: options.diffColorAlt,
328
+ includeAA: options.includeAA,
329
+ diffMask: options.diffMask
330
+ }
331
+ };
332
+ result = await bin(image1, image2, binOptions);
239
333
  }
240
- if (result.diffCount > 0) {
241
- process.exit(1);
334
+ console.log(`completed in: ${result.duration.toFixed(2)}ms`);
335
+ console.log(`dimensions: ${result.width}x${result.height}`);
336
+ if (result.mode === "gmsd") {
337
+ console.log(
338
+ `similarity score: ${result.score.toFixed(6)} (0=different, 1=identical)`
339
+ );
340
+ console.log(`similarity: ${(result.score * 100).toFixed(2)}%`);
341
+ if (options.outputPath && result.outputData) {
342
+ console.log(`GMS map saved to: ${options.outputPath}`);
343
+ }
344
+ if (result.score < 0.95) {
345
+ process.exit(1);
346
+ } else {
347
+ console.log(`Images are highly similar!`);
348
+ process.exit(0);
349
+ }
242
350
  } else {
243
- console.log(`Images are identical!`);
244
- process.exit(0);
351
+ console.log(`different pixels: ${result.diffCount}`);
352
+ console.log(
353
+ `error: ${(result.diffCount / (result.width * result.height) * 100).toFixed(2)}%`
354
+ );
355
+ if (result.diffCount > 0 && result.outputData && options.outputPath) {
356
+ console.log(`diff image: ${options.outputPath}`);
357
+ }
358
+ if (result.diffCount > 0) {
359
+ process.exit(1);
360
+ } else {
361
+ console.log(`Images are identical!`);
362
+ process.exit(0);
363
+ }
245
364
  }
246
365
  } catch (error) {
247
366
  console.error(
package/dist/index.d.mts CHANGED
@@ -1,17 +1,46 @@
1
- import { BlazeDiffTransformer, BlazeDiffOptions } from '@blazediff/types';
1
+ import { CoreOptions } from '@blazediff/core';
2
+ import { GmsdOptions } from '@blazediff/gmsd';
2
3
 
3
- interface BlazeDiffBinOptions {
4
+ interface Image {
5
+ data: Buffer | Uint8Array | Uint8ClampedArray;
6
+ width: number;
7
+ height: number;
8
+ }
9
+ interface Transformer {
10
+ transform: (input: string | Buffer) => Promise<Image>;
11
+ write: (image: Image, output: string | Buffer) => Promise<void>;
12
+ }
13
+ type ComparisonMode = "diff" | "gmsd";
14
+ interface DiffModeOptions {
15
+ outputPath?: string;
16
+ transformer: Transformer;
17
+ mode?: "diff";
18
+ options?: CoreOptions;
19
+ }
20
+ interface GmsdModeOptions {
4
21
  outputPath?: string;
5
- transformer: BlazeDiffTransformer;
6
- coreOptions?: BlazeDiffOptions;
22
+ transformer: Transformer;
23
+ mode: "gmsd";
24
+ options?: GmsdOptions;
7
25
  }
8
- interface BlazeDiffBinResult {
26
+ interface DiffModeResult {
27
+ mode: "diff";
9
28
  diffCount: number;
10
29
  width: number;
11
30
  height: number;
12
31
  outputData?: Uint8Array;
13
32
  duration: number;
14
33
  }
15
- declare function blazeDiffBin(image1Path: string, image2Path: string, options: BlazeDiffBinOptions): Promise<BlazeDiffBinResult>;
34
+ interface GmsdModeResult {
35
+ mode: "gmsd";
36
+ score: number;
37
+ width: number;
38
+ height: number;
39
+ outputData?: Uint8Array;
40
+ duration: number;
41
+ }
42
+ type BlazeDiffBinResult = DiffModeResult | GmsdModeResult;
43
+ declare function bin(image1Path: string, image2Path: string, options: GmsdModeOptions): Promise<GmsdModeResult>;
44
+ declare function bin(image1Path: string, image2Path: string, options: DiffModeOptions): Promise<DiffModeResult>;
16
45
 
17
- export { type BlazeDiffBinOptions, type BlazeDiffBinResult, blazeDiffBin as default };
46
+ export { type BlazeDiffBinResult, type ComparisonMode, type DiffModeOptions, type DiffModeResult, type GmsdModeOptions, type GmsdModeResult, type Image, type Transformer, bin as default };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,46 @@
1
- import { BlazeDiffTransformer, BlazeDiffOptions } from '@blazediff/types';
1
+ import { CoreOptions } from '@blazediff/core';
2
+ import { GmsdOptions } from '@blazediff/gmsd';
2
3
 
3
- interface BlazeDiffBinOptions {
4
+ interface Image {
5
+ data: Buffer | Uint8Array | Uint8ClampedArray;
6
+ width: number;
7
+ height: number;
8
+ }
9
+ interface Transformer {
10
+ transform: (input: string | Buffer) => Promise<Image>;
11
+ write: (image: Image, output: string | Buffer) => Promise<void>;
12
+ }
13
+ type ComparisonMode = "diff" | "gmsd";
14
+ interface DiffModeOptions {
15
+ outputPath?: string;
16
+ transformer: Transformer;
17
+ mode?: "diff";
18
+ options?: CoreOptions;
19
+ }
20
+ interface GmsdModeOptions {
4
21
  outputPath?: string;
5
- transformer: BlazeDiffTransformer;
6
- coreOptions?: BlazeDiffOptions;
22
+ transformer: Transformer;
23
+ mode: "gmsd";
24
+ options?: GmsdOptions;
7
25
  }
8
- interface BlazeDiffBinResult {
26
+ interface DiffModeResult {
27
+ mode: "diff";
9
28
  diffCount: number;
10
29
  width: number;
11
30
  height: number;
12
31
  outputData?: Uint8Array;
13
32
  duration: number;
14
33
  }
15
- declare function blazeDiffBin(image1Path: string, image2Path: string, options: BlazeDiffBinOptions): Promise<BlazeDiffBinResult>;
34
+ interface GmsdModeResult {
35
+ mode: "gmsd";
36
+ score: number;
37
+ width: number;
38
+ height: number;
39
+ outputData?: Uint8Array;
40
+ duration: number;
41
+ }
42
+ type BlazeDiffBinResult = DiffModeResult | GmsdModeResult;
43
+ declare function bin(image1Path: string, image2Path: string, options: GmsdModeOptions): Promise<GmsdModeResult>;
44
+ declare function bin(image1Path: string, image2Path: string, options: DiffModeOptions): Promise<DiffModeResult>;
16
45
 
17
- export { type BlazeDiffBinOptions, type BlazeDiffBinResult, blazeDiffBin as default };
46
+ export { type BlazeDiffBinResult, type ComparisonMode, type DiffModeOptions, type DiffModeResult, type GmsdModeOptions, type GmsdModeResult, type Image, type Transformer, bin as default };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var m=require('@blazediff/core');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var m__default=/*#__PURE__*/_interopDefault(m);async function u(n,f,i){let[t,e]=await Promise.all([i.transformer.transform(n),i.transformer.transform(f)]);if(t.width!==e.width||t.height!==e.height)throw new Error(`Image dimensions do not match: ${t.width}x${t.height} vs ${e.width}x${e.height}`);let r;i.outputPath&&(r=new Uint8Array(t.data.length));let h=performance.now(),a=m__default.default(t.data,e.data,r,t.width,t.height,i.coreOptions),o=performance.now()-h;return a>0&&i.outputPath&&r&&await i.transformer.write({data:r,width:t.width,height:t.height},i.outputPath),{diffCount:a,width:t.width,height:t.height,outputData:r,duration:o}}module.exports=u;
1
+ 'use strict';var g=require('@blazediff/core'),p=require('@blazediff/gmsd');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var g__default=/*#__PURE__*/_interopDefault(g);var p__default=/*#__PURE__*/_interopDefault(p);var w=n=>n.mode==="gmsd";async function c(n,d,e){let[t,i]=await Promise.all([e.transformer.transform(n),e.transformer.transform(d)]);if(t.width!==i.width||t.height!==i.height)throw new Error(`Image dimensions do not match: ${t.width}x${t.height} vs ${i.width}x${i.height}`);if(w(e)){let o;e.outputPath&&(o=new Uint8Array(t.data.length));let m=performance.now(),u=p__default.default(t.data,i.data,o,t.width,t.height,e.options||{}),h=performance.now()-m;return e.outputPath&&o&&await e.transformer.write({data:o,width:t.width,height:t.height},e.outputPath),{mode:"gmsd",width:t.width,height:t.height,outputData:o,duration:h,score:u}}let r;e.outputPath&&(r=new Uint8Array(t.data.length));let s=performance.now(),a=g__default.default(t.data,i.data,r,t.width,t.height,e.options||{}),f=performance.now()-s;return a>0&&e.outputPath&&r&&await e.transformer.write({data:r,width:t.width,height:t.height},e.outputPath),{mode:"diff",diffCount:a,width:t.width,height:t.height,outputData:r,duration:f}}module.exports=c;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import m from'@blazediff/core';async function u(n,f,i){let[t,e]=await Promise.all([i.transformer.transform(n),i.transformer.transform(f)]);if(t.width!==e.width||t.height!==e.height)throw new Error(`Image dimensions do not match: ${t.width}x${t.height} vs ${e.width}x${e.height}`);let r;i.outputPath&&(r=new Uint8Array(t.data.length));let h=performance.now(),a=m(t.data,e.data,r,t.width,t.height,i.coreOptions),o=performance.now()-h;return a>0&&i.outputPath&&r&&await i.transformer.write({data:r,width:t.width,height:t.height},i.outputPath),{diffCount:a,width:t.width,height:t.height,outputData:r,duration:o}}export{u as default};
1
+ import g from'@blazediff/core';import p from'@blazediff/gmsd';var w=n=>n.mode==="gmsd";async function c(n,d,e){let[t,i]=await Promise.all([e.transformer.transform(n),e.transformer.transform(d)]);if(t.width!==i.width||t.height!==i.height)throw new Error(`Image dimensions do not match: ${t.width}x${t.height} vs ${i.width}x${i.height}`);if(w(e)){let o;e.outputPath&&(o=new Uint8Array(t.data.length));let m=performance.now(),u=p(t.data,i.data,o,t.width,t.height,e.options||{}),h=performance.now()-m;return e.outputPath&&o&&await e.transformer.write({data:o,width:t.width,height:t.height},e.outputPath),{mode:"gmsd",width:t.width,height:t.height,outputData:o,duration:h,score:u}}let r;e.outputPath&&(r=new Uint8Array(t.data.length));let s=performance.now(),a=g(t.data,i.data,r,t.width,t.height,e.options||{}),f=performance.now()-s;return a>0&&e.outputPath&&r&&await e.transformer.write({data:r,width:t.width,height:t.height},e.outputPath),{mode:"diff",diffCount:a,width:t.width,height:t.height,outputData:r,duration:f}}export{c as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazediff/bin",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Command-line interface for the blazediff image comparison library",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -37,10 +37,10 @@
37
37
  "homepage": "https://blazediff.dev",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "@blazediff/core": "1.4.0",
41
- "@blazediff/pngjs-transformer": "1.4.0",
42
- "@blazediff/sharp-transformer": "1.4.0",
43
- "@blazediff/types": "1.4.0"
40
+ "@blazediff/core": "1.5.0",
41
+ "@blazediff/gmsd": "1.5.0",
42
+ "@blazediff/pngjs-transformer": "1.5.0",
43
+ "@blazediff/sharp-transformer": "1.5.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^24.3.0",