Browse Source

fix preprocessing and shit

master 0.56
coomdev 9 months ago
parent
commit
4ce8872cdc
  1. 2
      .eslintrc.json
  2. BIN
      144.png
  3. BIN
      HSXVW.png
  4. 90
      a
  5. BIN
      b.png
  6. 2970
      chrome/dist/main.js
  7. 5
      chrome/manifest.json
  8. 5008
      dist/main.js
  9. 76
      model_gen/a.ts
  10. 3
      model_gen/captcha/group1-shard1of1.bin
  11. 3
      model_gen/captcha/group1-shard1of4.bin
  12. 3
      model_gen/captcha/group1-shard2of4.bin
  13. 3
      model_gen/captcha/group1-shard3of4.bin
  14. 3
      model_gen/captcha/group1-shard4of4.bin
  15. 1
      model_gen/captcha/model.json
  16. 2
      model_gen/captcha_75_25.keras
  17. 109
      model_gen/tsconfig.json
  18. 1809
      package-lock.json
  19. 7
      package.json
  20. 78
      src/ccl.js
  21. 98
      src/ccl.ts
  22. 3
      src/group1-shard1of1.bin
  23. 369
      src/main.ts
  24. 249
      src/model.json
  25. 4
      src/model.weights.bin
  26. 17
      src/type.d.ts
  27. 109
      tsconfig.json

2
.eslintrc.json

@ -12,6 +12,6 @@
"no-undef": ["off"],
"no-unused-vars": "off",
"semi": "off",
"space-before-function-paren": "off"
"space-before-function-paren": "off",
}
}

BIN
144.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
HSXVW.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

90
a

@ -0,0 +1,90 @@
/home/lillie/JKCS/chrome/dist/main.js
23692,11: class_name: this.optimizer.getClassName(),
55897,9: class_name: "Sequential",
55902,15: class_name: "InputLayer",
55912,15: class_name: "Conv2D",
55928,19: class_name: "GlorotUniform",
55931,37: bias_initializer: { class_name: "Zeros", config: {} },
55940,15: class_name: "MaxPooling2D",
55952,15: class_name: "Conv2D",
55967,19: class_name: "GlorotUniform",
55970,37: bias_initializer: { class_name: "Zeros", config: {} },
55979,15: class_name: "MaxPooling2D",
55991,15: class_name: "Reshape",
56000,15: class_name: "Bidirectional",
56006,19: class_name: "LSTM",
56022,23: class_name: "GlorotUniform",
56026,23: class_name: "Orthogonal",
56029,41: bias_initializer: { class_name: "Zeros", config: {} },
56047,15: class_name: "Dense",
56056,19: class_name: "GlorotUniform",
56059,37: bias_initializer: { class_name: "Zeros", config: {} },
56076,11: class_name: "RMSprop",
/home/lillie/JKCS/dist/main.js
23035,11: class_name: this.optimizer.getClassName(),
54157,9: class_name: "Sequential",
54162,15: class_name: "InputLayer",
54172,15: class_name: "Conv2D",
54188,19: class_name: "GlorotUniform",
54191,37: bias_initializer: { class_name: "Zeros", config: {} },
54200,15: class_name: "MaxPooling2D",
54212,15: class_name: "Conv2D",
54227,19: class_name: "GlorotUniform",
54230,37: bias_initializer: { class_name: "Zeros", config: {} },
54239,15: class_name: "MaxPooling2D",
54251,15: class_name: "Reshape",
54260,15: class_name: "Bidirectional",
54266,19: class_name: "LSTM",
54282,23: class_name: "GlorotUniform",
54286,23: class_name: "Orthogonal",
54289,41: bias_initializer: { class_name: "Zeros", config: {} },
54307,15: class_name: "Dense",
54316,19: class_name: "GlorotUniform",
54319,37: bias_initializer: { class_name: "Zeros", config: {} },
54336,11: class_name: "RMSprop",
/home/lillie/JKCS/src/model.json
9,8: "class_name": "Sequential",
14,14: "class_name": "InputLayer",
24,14: "class_name": "Conv2D",
40,18: "class_name": "GlorotUniform",
43,38: "bias_initializer": { "class_name": "Zeros", "config": {} },
52,14: "class_name": "MaxPooling2D",
64,14: "class_name": "Conv2D",
79,18: "class_name": "GlorotUniform",
82,38: "bias_initializer": { "class_name": "Zeros", "config": {} },
91,14: "class_name": "MaxPooling2D",
103,14: "class_name": "Reshape",
112,14: "class_name": "Bidirectional",
118,18: "class_name": "LSTM",
134,22: "class_name": "GlorotUniform",
138,22: "class_name": "Orthogonal",
141,42: "bias_initializer": { "class_name": "Zeros", "config": {} },
159,14: "class_name": "Dense",
168,18: "class_name": "GlorotUniform",
171,38: "bias_initializer": { "class_name": "Zeros", "config": {} },
188,10: "class_name": "RMSprop",
/home/lillie/JKCS/JKCS.user.js
23767,11: class_name: this.optimizer.getClassName(),
55972,9: class_name: "Sequential",
55977,15: class_name: "InputLayer",
55987,15: class_name: "Conv2D",
56003,19: class_name: "GlorotUniform",
56006,37: bias_initializer: { class_name: "Zeros", config: {} },
56015,15: class_name: "MaxPooling2D",
56027,15: class_name: "Conv2D",
56042,19: class_name: "GlorotUniform",
56045,37: bias_initializer: { class_name: "Zeros", config: {} },
56054,15: class_name: "MaxPooling2D",
56066,15: class_name: "Reshape",
56075,15: class_name: "Bidirectional",
56081,19: class_name: "LSTM",
56097,23: class_name: "GlorotUniform",
56101,23: class_name: "Orthogonal",
56104,41: bias_initializer: { class_name: "Zeros", config: {} },
56122,15: class_name: "Dense",
56131,19: class_name: "GlorotUniform",
56134,37: bias_initializer: { class_name: "Zeros", config: {} },
56151,11: class_name: "RMSprop",

BIN
b.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

2970
chrome/dist/main.js

File diff suppressed because it is too large

5
chrome/manifest.json

@ -2,14 +2,11 @@
"manifest_version": 3,
"name": "Joshi Koukousei Captcha Service",
"description": "The captcha solver made by and for japanese high school girls",
"version": "0.38",
"version": "0.47",
"icons": {
"64": "1449696017588.png"
},
"permissions": [],
"host_permissions": [
"<all_urls>"
],
"web_accessible_resources": [
{
"resources": [

5008
dist/main.js

File diff suppressed because one or more lines are too long

76
model_gen/a.ts

@ -0,0 +1,76 @@
export function connectedComponentLabeling(binaryImage: number[], width: number, height: number) {
const labels = Array(binaryImage.length).fill(0);
const linked: number[][] = [];
let nextLabel = 1;
function getNeighbors(row: number, col: number) {
const neighbors = [];
if (row > 0 && labels[(row - 1) * width + col] > 0) {
neighbors.push(labels[(row - 1) * width + col]);
}
if (col > 0 && labels[row * width + col - 1] > 0) {
neighbors.push(labels[row * width + col - 1]);
}
return neighbors;
}
// First pass
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = row * width + col;
if (binaryImage[idx] !== 0) {
const neighbors = getNeighbors(row, col);
if (neighbors.length === 0) {
linked[nextLabel] = [nextLabel];
labels[idx] = nextLabel;
nextLabel++;
} else {
neighbors.sort();
const smallestLabel = neighbors[0];
labels[idx] = smallestLabel;
for (let i = 1; i < neighbors.length; i++) {
linked[neighbors[i]] = linked[neighbors[i]].concat(linked[smallestLabel]);
linked[smallestLabel] = linked[smallestLabel].concat(linked[neighbors[i]]);
linked[neighbors[i]] = Array.from(new Set(linked[neighbors[i]]));
linked[smallestLabel] = Array.from(new Set(linked[smallestLabel]));
}
}
}
}
}
// Second pass
for (let idx = 0; idx < binaryImage.length; idx++) {
if (binaryImage[idx] !== 0) {
labels[idx] = Math.min(...linked[labels[idx]]);
}
}
return labels;
}
export function computeBounds(labels: number[], width: number, height: number) {
const bounds: Record<number, { minRow: number, minCol: number, maxRow: number, maxCol: number; area: number }> = {};
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = row * width + col;
const label = labels[idx];
if (label > 0) {
if (!bounds[label]) {
bounds[label] = { minRow: row, minCol: col, maxRow: row, maxCol: col, area: 1 };
} else {
if (row < bounds[label].minRow) bounds[label].minRow = row;
if (col < bounds[label].minCol) bounds[label].minCol = col;
if (row > bounds[label].maxRow) bounds[label].maxRow = row;
if (col > bounds[label].maxCol) bounds[label].maxCol = col;
++bounds[label].area;
}
}
}
}
return bounds;
}

3
model_gen/captcha/group1-shard1of1.bin

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b3d1959699ed9381dc680dcb6676e90670c6841c2b97fcf696ff6c2db261e1f5
size 15704896

3
model_gen/captcha/group1-shard1of4.bin

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9a271c199adc63db11c87b3721ade013b929ee384e804a341cc84ba0dd33888e
size 4194304

3
model_gen/captcha/group1-shard2of4.bin

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:570aae0ee973a071a83ebd629c1bc1006da19953bf5103a8813fc24339c55b81
size 4194304

3
model_gen/captcha/group1-shard3of4.bin

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6ce0791c4d68c68a23eda03cec7aad7040b659e65f9a711ae8f4eba4b756b994
size 4194304

3
model_gen/captcha/group1-shard4of4.bin

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:43bad7d4f611233ee28242ae07176846f14372f196aa7f5de3c7011ec03197c0
size 3121984

1
model_gen/captcha/model.json

File diff suppressed because one or more lines are too long

2
model_gen/captcha_75_25.keras

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:436520aeedc174ff1a9e065185d932c6c1d2f69719933b8ab1d86bbc1aa40edf
oid sha256:d07641518ce1f11c7e546e21045ca0f6a64fcb1c8fa34baf6e9c494f4d477f97
size 47163938

109
model_gen/tsconfig.json

@ -0,0 +1,109 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "ES2022", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

1809
package-lock.json

File diff suppressed because it is too large

7
package.json

@ -10,14 +10,17 @@
"ver": "tsc -v",
"build_chrome": "node ./build-chrome.js",
"build_ff": "node ./build-ff.js",
"watch": "esbuild src/main.js --bundle --outfile=dist/main.js --define:global=window --define:execution_mode='\"test\"' --loader:.bin=text --loader:.wasm=binary --watch",
"watch": "esbuild src/main.ts --bundle --outfile=dist/main.js --define:global=window --define:execution_mode='\"test\"' --loader:.bin=binary --loader:.wasm=binary --watch",
"lint": "pnpm exec standard --fix"
},
"author": "/cumg/, formerly AUTOMATIC",
"license": "LGPL-3.0-or-later (Sublicensed under MIT 0)",
"dependencies": {
"@tensorflow/tfjs": "^3.19.0",
"@tensorflow/tfjs-backend-wasm": "^3.19.0"
"@tensorflow/tfjs-backend-wasm": "^3.19.0",
"@tensorflow/tfjs-node-gpu": "^4.10.0",
"sharp": "^0.32.4",
"tfjsctcloss": "^1.0.1"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.32.0",

78
src/ccl.js

@ -1,78 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeBounds = exports.connectedComponentLabeling = void 0;
function connectedComponentLabeling(binaryImage, width, height) {
var labels = Array(binaryImage.length).fill(0);
var linked = [];
var nextLabel = 1;
function getNeighbors(row, col) {
var neighbors = [];
if (row > 0 && labels[(row - 1) * width + col] > 0) {
neighbors.push(labels[(row - 1) * width + col]);
}
if (col > 0 && labels[row * width + col - 1] > 0) {
neighbors.push(labels[row * width + col - 1]);
}
return neighbors;
}
// First pass
for (var row = 0; row < height; row++) {
for (var col = 0; col < width; col++) {
var idx = row * width + col;
if (binaryImage[idx] !== 0) {
var neighbors = getNeighbors(row, col);
if (neighbors.length === 0) {
linked[nextLabel] = [nextLabel];
labels[idx] = nextLabel;
nextLabel++;
}
else {
neighbors.sort();
var smallestLabel = neighbors[0];
labels[idx] = smallestLabel;
for (var i = 1; i < neighbors.length; i++) {
linked[neighbors[i]] = linked[neighbors[i]].concat(linked[smallestLabel]);
linked[smallestLabel] = linked[smallestLabel].concat(linked[neighbors[i]]);
linked[neighbors[i]] = Array.from(new Set(linked[neighbors[i]]));
linked[smallestLabel] = Array.from(new Set(linked[smallestLabel]));
}
}
}
}
}
// Second pass
for (var idx = 0; idx < binaryImage.length; idx++) {
if (binaryImage[idx] !== 0) {
labels[idx] = Math.min.apply(Math, linked[labels[idx]]);
}
}
return labels;
}
exports.connectedComponentLabeling = connectedComponentLabeling;
function computeBounds(labels, width, height) {
var bounds = {};
for (var row = 0; row < height; row++) {
for (var col = 0; col < width; col++) {
var idx = row * width + col;
var label = labels[idx];
if (label > 0) {
if (!bounds[label]) {
bounds[label] = { minRow: row, minCol: col, maxRow: row, maxCol: col, area: 1 };
}
else {
if (row < bounds[label].minRow)
bounds[label].minRow = row;
if (col < bounds[label].minCol)
bounds[label].minCol = col;
if (row > bounds[label].maxRow)
bounds[label].maxRow = row;
if (col > bounds[label].maxCol)
bounds[label].maxCol = col;
++bounds[label].area;
}
}
}
}
return bounds;
}
exports.computeBounds = computeBounds;

98
src/ccl.ts

@ -0,0 +1,98 @@
export function connectedComponentLabeling(binaryImage: ArrayLike<number>, width: number, height: number) {
const labels: number[] = Array(binaryImage.length).fill(0);
const linked: number[] = [];
let nextLabel = 1;
function find(x: number): number {
if (x !== linked[x]) {
linked[x] = find(linked[x]);
}
return linked[x];
}
function union(x: number, y: number) {
linked[find(x)] = find(y);
}
function getNeighbors(row: number, col: number) {
const neighbors: number[] = [];
// Above
if (row > 0 && labels[(row - 1) * width + col] > 0) {
neighbors.push(labels[(row - 1) * width + col]);
}
// Left
if (col > 0 && labels[row * width + col - 1] > 0) {
neighbors.push(labels[row * width + col - 1]);
}
// Above Left
if (row > 0 && col > 0 && labels[(row - 1) * width + col - 1] > 0) {
neighbors.push(labels[(row - 1) * width + col - 1]);
}
// Above Right
if (row > 0 && col < width - 1 && labels[(row - 1) * width + col + 1] > 0) {
neighbors.push(labels[(row - 1) * width + col + 1]);
}
return neighbors;
}
// First pass
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = row * width + col;
if (binaryImage[idx] !== 0) {
const neighbors = getNeighbors(row, col);
if (neighbors.length === 0) {
linked[nextLabel] = nextLabel;
labels[idx] = nextLabel;
nextLabel++;
} else {
neighbors.sort();
const smallestLabel = neighbors[0];
labels[idx] = smallestLabel;
for (const neighbor of neighbors) {
if (neighbor !== smallestLabel) {
union(smallestLabel, neighbor);
}
}
}
}
}
}
// Second pass
for (let idx = 0; idx < binaryImage.length; idx++) {
if (binaryImage[idx] !== 0) {
labels[idx] = find(labels[idx]);
}
}
return labels;
}
export function computeBounds(labels: number[], width: number, height: number) {
const bounds: Record<number, { minRow: number, minCol: number, maxRow: number, maxCol: number; area: number; }> = {};
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = row * width + col;
const label = labels[idx];
if (label > 0) {
if (!bounds[label]) {
bounds[label] = { minRow: row, minCol: col, maxRow: row, maxCol: col, area: 1 };
} else {
if (row < bounds[label].minRow) bounds[label].minRow = row;
if (col < bounds[label].minCol) bounds[label].minCol = col;
if (row > bounds[label].maxRow) bounds[label].maxRow = row;
if (col > bounds[label].maxCol) bounds[label].maxCol = col;
++bounds[label].area;
}
}
}
}
return bounds;
}

3
src/group1-shard1of1.bin

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b3d1959699ed9381dc680dcb6676e90670c6841c2b97fcf696ff6c2db261e1f5
size 15704896

369
src/main.js → src/main.ts

@ -1,15 +1,15 @@
import * as tf from '@tensorflow/tfjs';
import { setWasmPaths } from '@tensorflow/tfjs-backend-wasm';
import modelJSON from './model.json';
import ccl from './ccl';
import * as ccl from './ccl';
const charset = [' ', '0', '2', '4', '5', '8', 'A', 'D', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'R', 'S', 'T', 'V', 'W', 'X', 'Y'];
let weightsData;
let model;
let weightsData: Uint8Array; // base64 encoded weights
let model: tf.LayersModel;
tf.enableProdMode();
const wasmToUrl = wasm => {
const wasmToUrl = (wasm: any) => {
const blb = new Blob([wasm], { type: 'application/wasm' });
return URL.createObjectURL(blb);
};
@ -19,7 +19,7 @@ const backendloaded = (async () => {
// dead code elimination should occur here
// eslint-disable-next-line camelcase
if (execution_mode === 'userscript' || execution_mode === 'test') {
weightsData = (await import('./model.weights.bin')).default;
weightsData = (await import('./group1-shard1of1.bin')).default;
const tfwasmthreadedsimd = (await import('./tfjs-backend-wasm-threaded-simd.wasm')).default;
const tfwasmsimd = (await import('./tfjs-backend-wasm-simd.wasm')).default;
const tfwasm = (await import('./tfjs-backend-wasm.wasm')).default;
@ -29,7 +29,7 @@ const backendloaded = (async () => {
'tfjs-backend-wasm-threaded-simd.wasm': wasmToUrl(tfwasmthreadedsimd)
});
} else {
weightsData = await (await fetch(chrome.runtime.getURL('./model.weights.bin'))).text();
weightsData = new Uint8Array(await (await fetch(chrome.runtime.getURL('./group1-shard1of1.bin'))).arrayBuffer());
const args = {
'tfjs-backend-wasm.wasm': chrome.runtime.getURL('tfjs-backend-wasm.wasm'),
'tfjs-backend-wasm-simd.wasm': chrome.runtime.getURL('tfjs-backend-wasm-simd.wasm'),
@ -44,28 +44,18 @@ const backendloaded = (async () => {
}
})();
function toggle(obj, v) {
function toggle(obj: HTMLElement, v?: any) {
if (v) obj.style.display = '';
else obj.style.display = 'none';
}
function base64ToArray(base64) {
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
const iohander = {
const iohander: tf.io.IOHandler = {
load: function () {
return new Promise((resolve, reject) => {
resolve({
modelTopology: modelJSON.modelTopology,
weightSpecs: modelJSON.weightsManifest[0].weights,
weightData: base64ToArray(weightsData),
weightSpecs: modelJSON.weightsManifest[0].weights as tf.io.WeightsManifestEntry[],
weightData: weightsData.buffer,
format: modelJSON.format,
generatedBy: modelJSON.generatedBy,
convertedBy: modelJSON.convertedBy
@ -75,32 +65,30 @@ const iohander = {
};
async function load() {
const uploadJSONInput = document.getElementById('upload-json');
const uploadWeightsInput = document.getElementById('upload-weights-1');
model = await tf.loadLayersModel(iohander);
return model;
}
function black(x) {
function black(x: number) {
return x < 64;
}
// Calculates "disorder" of the image. "Disorder" is the percentage of black pixels that have a
// non-black pixel below them. Minimizing this seems to be good enough metric for solving the slider.
function calculateDisorder(imgdata) {
function calculateDisorder(imgdata: ImageData) {
const a = imgdata.data;
const w = imgdata.width;
const h = imgdata.height;
const pic = [];
const visited = [];
const pic: number[] = [];
const visited: number[] = [];
for (let c = 0; c < w * h; c++) {
if (visited[c]) continue;
if (!black(a[c * 4])) continue;
let blackCount = 0;
const items = [];
const toVisit = [c];
const items: number[] = [];
const toVisit: number[] = [c];
while (toVisit.length > 0) {
const cc = toVisit[toVisit.length - 1];
toVisit.splice(toVisit.length - 1, 1);
@ -137,7 +125,7 @@ function calculateDisorder(imgdata) {
}
// returns ImageData from captcha's background image, foreground image, and offset (ranging from 0 to -50)
function imageFromCanvas(img, bg, off) {
function imageFromCanvas(img: HTMLImageElement, bg: HTMLImageElement, off: number) {
const h = img.height;
const w = img.width;
const th = 80;
@ -146,21 +134,17 @@ function imageFromCanvas(img, bg, off) {
const scale = th / h;
const canvas = document.createElement('canvas');
canvas.height = w * scale + pw * 2;
canvas.width = th;
canvas.width = w * scale + pw * 2;
canvas.height = th;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const ctx = canvas.getContext('2d', { willReadFrequently: true })!;
ctx.fillStyle = 'rgb(238,238,238)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.scale(-scale, scale);
ctx.rotate((90 * Math.PI) / 180);
const adf = 1 / 3;
const draw = function (off) {
const draw = function (off: number) {
if (bg) {
const border = 4;
ctx.drawImage(
@ -183,7 +167,7 @@ function imageFromCanvas(img, bg, off) {
// calculateDisorder for the resulting image
if (bg && off == null) {
let bestDisorder = 999;
let bestImagedata = null;
let bestImagedata: ImageData | null = null;
let bestOff = -1;
for (let off = 0; off >= -50; off--) {
@ -204,10 +188,10 @@ function imageFromCanvas(img, bg, off) {
// not the best idea to do this here
setTimeout(function () {
const bg = document.getElementById('t-bg');
const slider = document.getElementById('t-slider');
const slider = document.getElementById('t-slider') as HTMLInputElement;
if (!bg || !slider) return;
slider.value = -bestOff * 2;
slider.value = '' + (-bestOff * 2);
bg.style.backgroundPositionX = bestOff + 'px';
}, 1);
draw(bestOff);
@ -219,9 +203,9 @@ function imageFromCanvas(img, bg, off) {
}
// for debugging purposes
function imagedataToImage(imagedata) {
function imagedataToImage(imagedata: ImageData) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const ctx = canvas.getContext('2d')!;
canvas.width = imagedata.width;
canvas.height = imagedata.height;
ctx.putImageData(imagedata, 0, 0);
@ -231,42 +215,182 @@ function imagedataToImage(imagedata) {
return image;
}
async function predict(img, bg, off) {
function toMonochrome(px: Uint8ClampedArray) {
const ret = Array<number>(px.length >> 2);
for (let i = 0; i < px.length; i += 4) {
ret[i >> 2] = +(px[i] < 128);
}
return ret;
}
const greedyCTCDecode = (yPred: tf.Tensor<tf.Rank>) => tf.tidy(() => yPred.argMax(-1).arraySync());
function imgDisp(pixConv: (f: ArrayLike<number>, w: number, h: number, s: Uint8ClampedArray) => ArrayLike<number>, img: ArrayLike<number>, w: number, h: number, t?: boolean) {
const dt = new ImageData(w, h);
const rgba = pixConv(img, w, h, dt.data);
const imgres = imagedataToImage(dt);
document.body.appendChild(imgres);
if (t) {
imgres.style.transform = 'rotate(90deg) scaleY(-1)';
}
}
let colors = [
[255, 0, 0], // Red
[0, 255, 0], // Green
[0, 0, 255], // Blue
[255, 255, 0], // Yellow
[255, 0, 255], // Magenta
[0, 255, 255], // Cyan
[128, 0, 0], // Dark Red
[0, 128, 0], // Dark Green
[0, 0, 128], // Dark Blue
[128, 128, 0], // Olive
[128, 0, 128], // Purple
[0, 128, 128], // Teal
[192, 192, 192], // Silver
[128, 128, 128], // Gray
[255, 165, 0], // Orange
[0, 128, 64] // Medium Sea Green
];
const monoToPalette = (p: number[][], max: number) =>
function (arr: ArrayLike<number>, w: number, h: number, res: Uint8ClampedArray) {
let choice = p.slice(0);
const choices = new Map<number, number[]>();
for (let i = 0; i < arr.length; ++i) {
let col: number[];
if (choices.has(arr[i])) {
col = choices.get(arr[i])!;
} else {
col = choice.shift()!;
choices.set(arr[i], col);
if (choice.length == 0)
choice = p.slice(0);
}
[res[i * 4], res[i * 4 + 1], res[i * 4 + 2]] = col;
res[i * 4 + 3] = 255;
}
return res;
};
function monoToRgba(arr: ArrayLike<number>, w: number, h: number, res: Uint8ClampedArray) {
for (let i = 0; i < arr.length; ++i) {
res[i * 4] = res[i * 4 + 1] = res[i * 4 + 2] = arr[i] * 255;
res[i * 4 + 3] = 255;
}
return res;
}
function processCTCDecodedSequence(decodedSequence: number[], blankLabel = 0) {
const result = [];
let prevLabel = blankLabel;
for (const label of decodedSequence) {
if (label !== blankLabel && label !== prevLabel) {
result.push(label);
}
prevLabel = label;
}
return result;
}
function indicesToSymbols(decodedIndices: number[]) {
return decodedIndices.map(index => charset[index - 1] || '');
}
async function predict(img: HTMLImageElement, bg: HTMLImageElement, off: number) {
if (!model) {
model = await load();
}
const image = imageFromCanvas(img, bg, off);
const labels = ccl.connectedComponentLabeling(image.data.map(e => +(e > 128)), image.width, image.height);
if (!image)
throw new Error("Failed to gen image");
const mono = toMonochrome(image.data);
console.log(mono.reduce((a, b) => a + b), 0);
const labels = ccl.connectedComponentLabeling(mono, image.width, image.height);
const props = ccl.computeBounds(labels, image.width, image.height);
const sortedByArea = Object.entries(props).sort((a, b) => a[1].area - b[1].area);
const eightBiggest = sortedByArea.slice(-8);
const filtered = new Float32Array(80 * 300);
const n = 8;
let eightBiggest = sortedByArea.slice(0, -n);
//const filtered = new Float32Array(80 * 300);
// TODO: maybe centering?
//imgDisp(monoToPalette(colors, Math.max(...new Set(labels))), labels, image.width, image.height);
for (const [label, region] of eightBiggest) {
if ((region.maxRow - region.minRow) <= 20) {
//if ((region.maxRow - region.minRow) <= 20) {
// continue;
//}
for (let y = region.minRow; y <= region.maxRow; ++y) {
for (let x = region.minCol; x <= region.maxCol; ++x) {
if (labels[y * image!.width + x] === +label) {
labels[y * image!.width + x] = 0;
}
}
}
}
eightBiggest = sortedByArea.slice(-n);
//imgDisp(monoToPalette(colors, Math.max(...new Set(labels))), labels, image.width, image.height);
for (const [label, region] of eightBiggest) {
if ((region.maxRow - region.minRow) > 20) {
continue;
}
for (let y = region.minRow; y < region.maxRow; ++y) {
for (let x = region.minCol; y < region.maxCol; ++x) {
if (labels[y * image.width + x] === label) {
filtered[y * 300 + x] = 1;
for (let y = region.minRow; y <= region.maxRow; ++y) {
for (let x = region.minCol; x <= region.maxCol; ++x) {
if (labels[y * image!.width + x] === +label) {
labels[y * image!.width + x] = 0;
}
}
}
}
//imgDisp(monoToPalette(colors, Math.max(...new Set(labels))), labels, image.width, image.height);
const tensor = tf.tensor3d(filtered, [80, 300, 1], 'float32');
const prediction = await model.predict(tensor.expandDims(0)).data();
for (const [label, region] of eightBiggest) {
if ((region.maxRow - region.minRow) <= 20) {
continue;
}
return createSequence(prediction);
for (let y = region.minRow; y <= region.maxRow; ++y) {
for (let x = region.minCol; x <= region.maxCol; ++x) {
if (labels[y * image!.width + x] === +label) {
labels[y * image!.width + x] = 1;
}
}
}
}
const filtered2 = tf.tensor3d(labels, [image.height, image.width, 1]).concat(tf.zeros([80, 300 - image.width, 1]), 1);
//imgDisp(monoToPalette(colors, Math.max(...new Set(labels))), labels, image.width, image.height);
//const tensor = tf.tensor3d(filtered, [80, 300, 1], 'float32');
//const tr = [1, 0, 2];
//console.log(tensor.shape, tensor.transpose(tr).shape);
const prediction = model.predict(filtered2.transpose([1, 0, 2]).expandDims(0));
let d: tf.TypedArray;
if (!Array.isArray(prediction)) {
const v = greedyCTCDecode(prediction) as number[][];
console.log(v);
const s = processCTCDecodedSequence(v[0], charset.length + 1);
return indicesToSymbols(s).join('').trim();
} else
throw new Error("unexpected inference");
// createSequence(d);
return '';
}
function createSequence(prediction) {
function createSequence(prediction: any) {
const csl = charset.length;
const sequence = [];
const sequence: Record<string, number>[] = [];
// for each prediction
for (let pos = 0; pos < prediction.length; pos += csl) {
@ -274,7 +398,7 @@ function createSequence(prediction) {
const preds = prediction.slice(pos, pos + csl);
const max = Math.max(...preds);
const seqElem = {};
const seqElem: Record<string, number> = {};
for (let i = 0; i < csl; i++) {
const p = preds[i] / max; // normalize probability
@ -291,84 +415,8 @@ function createSequence(prediction) {
return sequence;
}
function postprocess(sequence, overrides) {
const csl = charset.length;
let possibilities = [{ sequence: [] }];
sequence.forEach(function (e, i) {
let additions;
if (overrides && overrides[i] !== undefined) {
additions = [{ sym: overrides[i], off: i, conf: 1 }];
} else {
additions = Object.keys(e).map(function (sym) {
return { sym, off: i, conf: e[sym] };
});
}
if (additions.length === 1 && additions[0].sym === '') return;
const oldpos = possibilities;
possibilities = [];
oldpos.forEach(function (possibility) {
additions.forEach(function (a) {
const seq = [...possibility.sequence];
if (a.sym !== '') seq.push([a.sym, a.off, a.conf]);
const obj = {
sequence: seq
};
possibilities.push(obj);
});
});
});
const res = {};
possibilities.forEach(function (p) {
let line = '';
let lastSym;
let lastOff = -1;
let count = 0;
let prob = 0;
p.sequence.forEach(function (e) {
const sym = e[0];
const off = e[1];
const conf = e[2];
if (sym === lastSym && lastOff + 2 >= off) {
return;
}
line += sym;
lastSym = sym;
lastOff = off;
prob += conf;
count++;
});
if (count > 0) prob /= count;
if (prob > res[line] || !res[line]) {
res[line] = prob;
}
});
let keys = Object.keys(res).sort(function (a, b) {
return res[a] < res[b];
});
const keysFitting = keys.filter(function (x) {
return x.length === 5 || x.length === 6;
});
if (keysFitting.length > 0) keys = keysFitting;
return keys.map(function (x) {
return { seq: x, prob: res[x] };
});
}
async function imageFromUri(uri) {
async function imageFromUri(uri: string) {
if (uri.startsWith('url("')) {
uri = uri.substr(5, uri.length - 7);
}
@ -378,16 +426,19 @@ async function imageFromUri(uri) {
}
const img = new Image();
await new Promise((r) => (img.onload = r), (img.src = uri));
await new Promise((r) => {
img.onload = r;
img.src = uri;
});
return img;
}
async function predictUri(uri, uribg, bgoff) {
async function predictUri(uri: string, uribg: string, bgoff: string | null) {
const img = await imageFromUri(uri);
const bg = uribg ? await imageFromUri(uribg) : null;
const off = bgoff ? parseInt(bgoff) : null;
return await predict(img, bg, off);
return await predict(img!, bg!, off!);
}
const solveButton = document.createElement('input');
@ -407,21 +458,21 @@ altsDiv.id = 't-auto-options';
altsDiv.style.margin = '0';
altsDiv.style.padding = '0';
let storedPalceholder;
let storedPalceholder: string;
let overrides = {};
function placeAfter(elem, sibling) {
function placeAfter(elem: HTMLElement, sibling: HTMLElement) {
if (elem.parentElement !== sibling.parentElement) {
setTimeout(function () {
sibling.parentElement.insertBefore(elem, sibling.nextElementSibling);
sibling.parentElement?.insertBefore(elem, sibling.nextElementSibling);
}, 1);
}
}
let previousText = null;
async function solve(force) {
const resp = document.getElementById('t-resp');
let previousText: string | null = null;
async function solve(force?: any) {
const resp = document.getElementById('t-resp') as HTMLInputElement;
if (!resp) return;
const bg = document.getElementById('t-bg');
@ -453,46 +504,28 @@ async function solve(force) {
previousText = text;
altsDiv.innerHTML = '';
if (!storedPalceholder) storedPalceholder = resp.placeholder;
if (!storedPalceholder)
storedPalceholder = resp.placeholder;
resp.placeholder = 'solving captcha...';
overrides = {};
const sequence = await predictUri(
const result = await predictUri(
text,
bg.style.backgroundImage,
force ? bg.style.backgroundPositionX : null
);
const opts = postprocess(sequence);
resp.placeholder = storedPalceholder;
showOpts(opts);
}
function showOpts(opts) {
const resp = document.getElementById('t-resp');
if (!resp) return;
altsDiv.innerHTML = '';
if (opts.length === 0) {
resp.value = '';
return;
}
resp.value = opts[0].seq;
// for now don't display options since it seems more difficult to pick than type the whole thing
// eslint-disable-next-line no-constant-condition, no-empty
if (opts.length === 1 || true) {
}
resp.value = result;
}
const observer = new MutationObserver(async function (mutationsList, observer) {
solve(false);
});
window.solve = solve;
//window['solve'] = solve;
observer.observe(document.body, {
attributes: true,
childList: true,

249
src/model.json

@ -1,248 +1 @@
{
"format": "layers-model",
"generatedBy": "keras v2.4.0",
"convertedBy": "TensorFlow.js Converter v3.7.0",
"modelTopology": {
"keras_version": "2.4.0",
"backend": "tensorflow",
"model_config": {
"class_name": "Sequential",
"config": {
"name": "sequential",
"layers": [
{
"class_name": "InputLayer",
"config": {
"batch_input_shape": [null, null, 80, 1],
"dtype": "float32",
"sparse": false,
"ragged": false,
"name": "conv2d_input"
}
},
{
"class_name": "Conv2D",
"config": {
"name": "conv2d",
"trainable": true,
"batch_input_shape": [null, null, 80, 1],
"dtype": "float32",
"filters": 40,
"kernel_size": [3, 3],
"strides": [1, 1],
"padding": "same",
"data_format": "channels_last",
"dilation_rate": [1, 1],
"groups": 1,
"activation": "relu",
"use_bias": true,
"kernel_initializer": {
"class_name": "GlorotUniform",
"config": { "seed": null }
},
"bias_initializer": { "class_name": "Zeros", "config": {} },
"kernel_regularizer": null,
"bias_regularizer": null,
"activity_regularizer": null,
"kernel_constraint": null,
"bias_constraint": null
}
},
{
"class_name": "MaxPooling2D",
"config": {
"name": "max_pooling2d",
"trainable": true,
"dtype": "float32",
"pool_size": [2, 2],
"padding": "same",
"strides": [2, 2],
"data_format": "channels_last"
}
},
{
"class_name": "Conv2D",
"config": {
"name": "conv2d_1",
"trainable": true,
"dtype": "float32",
"filters": 60,
"kernel_size": [3, 3],
"strides": [1, 1],
"padding": "same",
"data_format": "channels_last",
"dilation_rate": [1, 1],
"groups": 1,
"activation": "relu",
"use_bias": true,
"kernel_initializer": {
"class_name": "GlorotUniform",
"config": { "seed": null }
},
"bias_initializer": { "class_name": "Zeros", "config": {} },
"kernel_regularizer": null,
"bias_regularizer": null,
"activity_regularizer": null,
"kernel_constraint": null,
"bias_constraint": null
}
},
{
"class_name": "MaxPooling2D",
"config": {
"name": "max_pooling2d_1",
"trainable": true,
"dtype": "float32",
"pool_size": [2, 2],
"padding": "same",
"strides": [2, 2],
"data_format": "channels_last"
}
},
{
"class_name": "Reshape",
"config": {
"name": "reshape",
"trainable": true,
"dtype": "float32",
"target_shape": [-1, 1200]
}
},
{
"class_name": "Bidirectional",
"config": {
"name": "bidi",
"trainable": true,
"dtype": "float32",
"layer": {
"class_name": "LSTM",
"config": {
"name": "lstm",
"trainable": true,
"dtype": "float32",
"return_sequences": true,
"return_state": false,
"go_backwards": false,
"stateful": false,
"unroll": false,
"time_major": false,
"units": 200,
"activation": "tanh",
"recurrent_activation": "sigmoid",
"use_bias": true,
"kernel_initializer": {
"class_name": "GlorotUniform",
"config": { "seed": null }
},
"recurrent_initializer": {
"class_name": "Orthogonal",
"config": { "gain": 1.0, "seed": null }
},
"bias_initializer": { "class_name": "Zeros", "config": {} },
"unit_forget_bias": true,
"kernel_regularizer": null,
"recurrent_regularizer": null,
"bias_regularizer": null,
"activity_regularizer": null,
"kernel_constraint": null,
"recurrent_constraint": null,
"bias_constraint": null,
"dropout": 0.0,
"recurrent_dropout": 0.0,
"implementation": 2
}
},
"merge_mode": "concat"
}
},
{
"class_name": "Dense",
"config": {
"name": "dense",
"trainable": true,
"dtype": "float32",
"units": 22,
"activation": "softmax",
"use_bias": true,
"kernel_initializer": {
"class_name": "GlorotUniform",
"config": { "seed": null }
},
"bias_initializer": { "class_name": "Zeros", "config": {} },
"kernel_regularizer": null,
"bias_regularizer": null,
"activity_regularizer": null,
"kernel_constraint": null,
"bias_constraint": null
}
}
]
}
},
"training_config": {
"loss": null,
"metrics": null,
"weighted_metrics": null,
"loss_weights": null,
"optimizer_config": {
"class_name": "RMSprop",
"config": {
"name": "RMSprop",
"learning_rate": 0.001,
"decay": 0.0,
"rho": 0.9,
"momentum": 0.0,
"epsilon": 1e-7,
"centered": false
}
}
}
},
"weightsManifest": [
{
"paths": ["model.weights.bin"],
"weights": [
{
"name": "bidi/forward_lstm/lstm_cell_4/kernel",
"shape": [1200, 800],
"dtype": "float32"
},
{
"name": "bidi/forward_lstm/lstm_cell_4/recurrent_kernel",
"shape": [200, 800],
"dtype": "float32"
},
{
"name": "bidi/forward_lstm/lstm_cell_4/bias",
"shape": [800],
"dtype": "float32"
},
{
"name": "bidi/backward_lstm/lstm_cell_5/kernel",
"shape": [1200, 800],
"dtype": "float32"
},
{
"name": "bidi/backward_lstm/lstm_cell_5/recurrent_kernel",
"shape": [200, 800],
"dtype": "float32"
},
{
"name": "bidi/backward_lstm/lstm_cell_5/bias",
"shape": [800],
"dtype": "float32"
},
{ "name": "conv2d/kernel", "shape": [3, 3, 1, 40], "dtype": "float32" },
{ "name": "conv2d/bias", "shape": [40], "dtype": "float32" },
{
"name": "conv2d_1/kernel",
"shape": [3, 3, 40, 60],
"dtype": "float32"
},
{ "name": "conv2d_1/bias", "shape": [60], "dtype": "float32" },
{ "name": "dense/kernel", "shape": [400, 22], "dtype": "float32" },
{ "name": "dense/bias", "shape": [22], "dtype": "float32" }
]
}
]
}
{"format": "layers-model", "generatedBy": "keras v2.13.1", "convertedBy": "TensorFlow.js Converter v4.10.0", "modelTopology": {"keras_version": "2.13.1", "backend": "tensorflow", "model_config": {"class_name": "Functional", "config": {"name": "model", "trainable": true, "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": [null, 300, 80, 1], "dtype": "float32", "sparse": false, "ragged": false, "name": "image"}, "name": "image", "inbound_nodes": []}, {"class_name": "Conv2D", "config": {"name": "conv_2d1", "trainable": true, "dtype": "float32", "filters": 60, "kernel_size": [3, 3], "strides": [1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "conv_2d1", "inbound_nodes": [[["image", 0, 0, {}]]]}, {"class_name": "MaxPooling2D", "config": {"name": "pool5", "trainable": true, "dtype": "float32", "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}, "name": "pool5", "inbound_nodes": [[["conv_2d1", 0, 0, {}]]]}, {"class_name": "Reshape", "config": {"name": "reshape", "trainable": true, "dtype": "float32", "target_shape": [75, 4800]}, "name": "reshape", "inbound_nodes": [[["pool5", 0, 0, {}]]]}, {"class_name": "Bidirectional", "config": {"name": "bidirectional", "trainable": true, "dtype": "float32", "layer": {"module": "keras.layers", "class_name": "LSTM", "config": {"name": "lstm", "trainable": true, "dtype": "float32", "return_sequences": true, "return_state": false, "go_backwards": false, "stateful": false, "unroll": false, "time_major": false, "units": 100, "activation": "tanh", "recurrent_activation": "sigmoid", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "recurrent_initializer": {"module": "keras.initializers", "class_name": "Orthogonal", "config": {"gain": 1.0, "seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "unit_forget_bias": true, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.3, "recurrent_dropout": 0.0, "implementation": 2}, "registered_name": null}, "merge_mode": "concat"}, "name": "bidirectional", "inbound_nodes": [[["reshape", 0, 0, {}]]]}, {"class_name": "Dense", "config": {"name": "dense2", "trainable": true, "dtype": "float32", "units": 24, "activation": "softmax", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "dense2", "inbound_nodes": [[["bidirectional", 0, 0, {}]]]}], "input_layers": [["image", 0, 0]], "output_layers": [["dense2", 0, 0]]}}}, "weightsManifest": [{"paths": ["group1-shard1of1.bin"], "weights": [{"name": "bidirectional/forward_lstm/lstm_cell/kernel", "shape": [4800, 400], "dtype": "float32"}, {"name": "bidirectional/forward_lstm/lstm_cell/recurrent_kernel", "shape": [100, 400], "dtype": "float32"}, {"name": "bidirectional/forward_lstm/lstm_cell/bias", "shape": [400], "dtype": "float32"}, {"name": "bidirectional/backward_lstm/lstm_cell/kernel", "shape": [4800, 400], "dtype": "float32"}, {"name": "bidirectional/backward_lstm/lstm_cell/recurrent_kernel", "shape": [100, 400], "dtype": "float32"}, {"name": "bidirectional/backward_lstm/lstm_cell/bias", "shape": [400], "dtype": "float32"}, {"name": "conv_2d1/kernel", "shape": [3, 3, 1, 60], "dtype": "float32"}, {"name": "conv_2d1/bias", "shape": [60], "dtype": "float32"}, {"name": "dense2/kernel", "shape": [200, 24], "dtype": "float32"}, {"name": "dense2/bias", "shape": [24], "dtype": "float32"}]}]}

4
src/model.weights.bin

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5d1015787bc130ac675d7d0f0fbce71e58c2cc232f29f70b3b87b9fa70a7792e
size 12119905
oid sha256:b3d1959699ed9381dc680dcb6676e90670c6841c2b97fcf696ff6c2db261e1f5
size 15704896

17
src/type.d.ts

@ -0,0 +1,17 @@
declare const execution_mode: string;
declare const chrome: any;
declare module '*.bin' {
const df: Uint8Array;
export default df;
}
declare module '*.wasm' {
const df: Uint8Array;
export default df;
}
declare module '*.json' {
const df: any;
export default df;
}

109
tsconfig.json

@ -0,0 +1,109 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "ES2022", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
Loading…
Cancel
Save