-
Notifications
You must be signed in to change notification settings - Fork 1
/
dhash.ts
100 lines (84 loc) · 2.41 KB
/
dhash.ts
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
import {
decode,
GIF,
Image,
} from "https://deno.land/x/[email protected]/mod.ts";
import { normalize, join } from "https://deno.land/[email protected]/path/mod.ts";
export const dhash = async (pathOrSrc: string | Uint8Array) => {
let file = pathOrSrc;
if (typeof pathOrSrc === "string") {
const resolvedPath = join(Deno.cwd(), normalize(pathOrSrc));
try {
file = await Deno.readFile(resolvedPath);
} catch {
throw new Error(`Failed to open "${resolvedPath}"`);
}
}
const image = await decode(file);
if (image instanceof GIF) {
throw new Error("GIF not supported");
}
const grayscale = image.saturation(0);
const resized = grayscale.resize(9, 8);
const out = [];
for (let x = 1; x <= resized.height; x++) {
for (let y = 1; y <= resized.height; y++) {
const left = resized.getPixelAt(x, y);
const right = resized.getPixelAt(x + 1, y);
out.push(left < right ? 1 : 0);
}
}
return parseInt(out.join(""), 2).toString(16);
};
export const compare = (hash1: string, hash2: string) => {
if (hash1.length !== hash2.length) {
throw new Error(`
Hashes should be of the same length.
Got ${hash1} of ${hash1.length} and ${hash2} of ${hash2.length}
`);
}
let counter = 0;
for (const [idx, c] of hash1.split("").entries()) {
if (c !== hash2[idx]) {
counter++;
}
}
return counter;
};
export const toAscii = (hash: string, chars = ["░░", "██"]) => {
const bin = parseInt(hash, 16).toString(2).split("");
let counter = 0;
let row = "";
for (const bit of bin) {
row += bit === "0" ? chars[0] : chars[1];
counter++;
if (counter === 8) {
row += "\n";
counter = 0;
}
}
return row + chars[0];
};
export const raw = async (hash: string): Promise<Uint8Array> => {
const bin = parseInt(hash, 16).toString(2).split("");
const out = new Image(8, 8);
const white = Image.rgbToColor(255, 255, 255);
const black = Image.rgbToColor(0, 0, 0);
let column = 1;
let row = 1;
for (const bit of bin) {
console.log(column, row);
out.setPixelAt(column, row, parseInt(bit) === 1 ? black : white);
row++;
if (row === 9) {
column++;
row = 1;
}
}
out.setPixelAt(8, 8, white);
return await out.encode();
};
export const save = async (hash: string, file: string): Promise<void> => {
const enc = await raw(hash);
await Deno.writeFile(`${file}.png`, enc);
};