Skip to content
This repository was archived by the owner on Oct 20, 2024. It is now read-only.

Commit 8fedc3b

Browse files
committed
添加了新类型验证码 dots_and_chars
dots_and_chars's score: 96.0% (48/50) 使用 sharp 和纯 JavaScript 的 CV 算法,不依赖OpenCV
1 parent a4882ef commit 8fedc3b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1163
-65
lines changed

.vscode/launch.json

Lines changed: 0 additions & 26 deletions
This file was deleted.

README.md

Lines changed: 22 additions & 23 deletions
2.42 KB
3.27 KB
3.02 KB
3.13 KB
2.61 KB
2.14 KB
3.07 KB
3.12 KB
1.86 KB
2.8 KB
2.49 KB
2.5 KB
2.43 KB
2.39 KB
1.95 KB
2.25 KB
1.89 KB
2.18 KB
2.1 KB
2.12 KB
1.79 KB
2.97 KB
2.84 KB
3.13 KB
3.34 KB
1.96 KB
1.96 KB
3.31 KB
2.85 KB
2.2 KB
2.37 KB
2.36 KB
2.64 KB
2.6 KB
2.01 KB
3.31 KB
3 KB
3.23 KB
2.25 KB
2.09 KB
2.35 KB
2.72 KB
2.44 KB
1.92 KB
2.34 KB
2.5 KB
2.82 KB
2.09 KB
2.36 KB
2.21 KB

codes/dots_and_chars/index.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const tesseract_ocr = require("./tesseract_ocr");
2+
const sharp_cv = require("./sharp_cv");
3+
4+
var ocr;
5+
const cv = sharp_cv;
6+
7+
class dots_and_chars {
8+
recognize = async (image) =>{
9+
var timeBegin = Date.now();
10+
var cvResult = await cv(image);
11+
if (debugFlag) {
12+
let cvDebugInfo = {result: "length=" + cvResult.result.length, time : cvResult.time}
13+
console.log("cv", cvDebugInfo)
14+
}
15+
16+
var charPromise = [];
17+
cvResult.result.forEach((value, index) => {
18+
charPromise[index] = ocr.recognize(value); //console.log(value);
19+
})
20+
21+
var charList = await Promise.all(charPromise);
22+
charList.forEach((value, index) => {
23+
if (['1', 'I'].includes(value.result)){
24+
console.log(`index: ${index}, char: ${value.result}, w: ${cvResult.marks[index].w}, h: ${cvResult.marks[index].h}, h/w: ${cvResult.marks[index].h/cvResult.marks[index].w}`);
25+
if (cvResult.marks[index].h/cvResult.marks[index].w < 2.2) value.result = '1'
26+
else value.result = 'I';
27+
}
28+
if (['0', 'O'].includes(value.result)){
29+
console.log(`index: ${index}, char: ${value.result}, w: ${cvResult.marks[index].w}, h: ${cvResult.marks[index].h}, h/w: ${cvResult.marks[index].h/cvResult.marks[index].w}`);
30+
if (cvResult.marks[index].h/cvResult.marks[index].w < 1.1) value.result = 'O'
31+
else value.result = '0';
32+
}
33+
})
34+
35+
if (debugFlag) console.log(charList);
36+
var chars = charList.map((value, index) => value.result);
37+
return { result: chars.join(''), time: Date.now() - timeBegin };
38+
}
39+
40+
init = async (workers = 4) =>{
41+
ocr = new tesseract_ocr(workers)
42+
await ocr.init();
43+
}
44+
}
45+
46+
module.exports = new dots_and_chars();

codes/dots_and_chars/sharp_cv.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
const cv = require('sharp');
2+
const fakeOpenCV = require("../../lib/fakeOpenCV");
3+
const fs = require("fs");
4+
5+
const main = async (file) => {
6+
var timeBegin = Date.now();
7+
var image = [];
8+
image[0] = cv(file);
9+
var meta = await image[0].metadata();
10+
//console.log(meta);
11+
var size = {width: meta.width, height: meta.height};
12+
13+
//裁边
14+
image[1] = image[0].clone().extract({ left: 1, top: 1, width: meta.width-2, height: meta.height-2})
15+
size.width -= 2;
16+
size.height -= 2;
17+
//await showImage(image[1], '裁边');
18+
19+
//灰度
20+
image[2] = image[1].clone().greyscale();
21+
//debugFlag && (await showImage(image[2], '灰度'));
22+
23+
//二值化
24+
image[3] = image[2].clone().threshold(255).toColourspace('b-w');
25+
debugFlag && (await showImage(image[3], '二值化'));
26+
27+
//空穴填充
28+
image[5] = image[3].clone().linear(-1, 255).convolve({
29+
width: 3,
30+
height: 3,
31+
kernel: [
32+
0, 1/4, 0,
33+
1/4, 1, 1/4,
34+
0, 1/4, 0
35+
],
36+
})
37+
image[5] = await superDeepCopySharp(image[5]);
38+
image[5] = image[5].threshold(128).toColourspace('b-w').linear(-1, 255);
39+
debugFlag && (await showImage(image[5], '空穴填充'));
40+
41+
//形态学膨胀
42+
var buffer = await image[5].clone().raw().toBuffer();
43+
var element = fakeOpenCV.math.ones(3, 3);
44+
var imageMath = fakeOpenCV.transformMath({buffer, size})
45+
var res = fakeOpenCV.dilate(imageMath, element);
46+
var resBuffer = fakeOpenCV.transformBuffer(res);
47+
image[4] = await cv(resBuffer.buffer, { raw: { width: resBuffer.size.width, height:resBuffer.size.height, channels: 1 }});
48+
debugFlag && (await showImage(image[4], '膨胀'));
49+
50+
//连通域
51+
var res2 = fakeOpenCV.connectedComponents(fakeOpenCV.inverse(res));
52+
53+
//分割及演示
54+
var points = [];
55+
var index2 = 0;
56+
var marks = [];
57+
res2.marks.forEach((value, index) => {
58+
var pos = value.position;
59+
function setPoints(index2, pos){
60+
points[index2] = [
61+
[pos.y, pos.x],
62+
[pos.y + pos.h - 1, pos.x],
63+
[pos.y + pos.h - 1, pos.x + pos.w - 1],
64+
[pos.y, pos.x + pos.w - 1],
65+
[pos.y, pos.x]
66+
];
67+
marks[index2] = pos;
68+
marks[index2].area = pos.w * pos.h;
69+
marks[index2].cX = Math.floor(pos.x + pos.w / 2);
70+
marks[index2].cY = Math.floor(pos.y + pos.h / 2);
71+
}
72+
//针对超宽的目标块二次对半分割
73+
if (pos.w > pos.h * 1.5){
74+
pos = {x: pos.x, y: pos.y, w: Math.floor(pos.w / 2), h: pos.h};
75+
setPoints(index2++, pos);
76+
pos = {x: pos.x + pos.w, y: pos.y, w: pos.w, h: pos.h};
77+
setPoints(index2++, pos);
78+
} else{
79+
setPoints(index2++, pos);
80+
}
81+
});
82+
//points = [[[0, 0], [0, 20], [10, 20], [10, 0], [0, 0]]]
83+
var res3 = fakeOpenCV.rectangle(res, points);
84+
var resBuffer = fakeOpenCV.transformBuffer(res3);
85+
image[5] = cv(resBuffer.buffer, { raw: { width: resBuffer.size.width, height:resBuffer.size.height, channels: 1 }});
86+
//debugFlag && (await showImage(image[5], '分割'));
87+
//console.log(res2.marks);
88+
89+
//取出字符块
90+
var imageList = [];
91+
marks.sort((a, b) => b.area - a.area);
92+
marks.splice(4);
93+
marks.sort((a, b) => a.cX - b.cX);
94+
var w = 45, h = 50;
95+
for (let i = 0; i < marks.length; i++){
96+
let imageExt = image[4]
97+
.clone()
98+
.extract({ left: marks[i].x, top: marks[i].y, width: marks[i].w, height: marks[i].h })
99+
.extend({
100+
top: Math.floor((h - marks[i].h) / 2),
101+
bottom: h - Math.floor((h - marks[i].h) / 2) - marks[i].h,
102+
left: Math.floor((w - marks[i].w) / 2),
103+
right: w - Math.floor((w - marks[i].w) / 2) - marks[i].w,
104+
background: "white"
105+
});
106+
let imageJPEG = await imageExt.jpeg({
107+
quality: 100,
108+
chromaSubsampling: '4:4:4'
109+
}).toBuffer();
110+
imageList[i] = imageJPEG;
111+
debugFlag && (await showImage(imageExt, i));
112+
}
113+
return { result: imageList, time: Date.now() - timeBegin, marks };
114+
}
115+
116+
const showImage = async (image, name) => {
117+
var scale = 10;
118+
var imageCopy = await superDeepCopySharp(image);
119+
var meta = await imageCopy.metadata();
120+
imageCopy
121+
.clone()
122+
.resize({
123+
width: meta.width * scale,
124+
kernel: cv.kernel.nearest
125+
})
126+
.jpeg({
127+
quality: 100,
128+
chromaSubsampling: '4:4:4'
129+
})
130+
.toBuffer()
131+
.then( data => {
132+
var tmp = require('tmp');
133+
var tmpobj = tmp.dirSync({prefix: 'sharp_' });
134+
debugFlag && console.log('Dir: ', tmpobj.name);
135+
var join = require("path").join;
136+
var exec = require('child_process').exec;
137+
var tempPNGPath = join(tmpobj.name, `${name}.jpg`);
138+
fs.writeFileSync(tempPNGPath, data);
139+
exec(`explorer.exe "${tempPNGPath}"`);
140+
})
141+
.catch( err => {console.error(err)});
142+
}
143+
144+
const superDeepCopySharp = async (image) => {
145+
var { data, info } = await image.raw().toBuffer({ resolveWithObject: true });
146+
var pixelArray = new Uint8ClampedArray(data.buffer);
147+
var { width, height, channels } = info;
148+
return cv(pixelArray, { raw: { width, height, channels } });
149+
}
150+
151+
module.exports = main;
152+
var debugFlag = false;
153+
154+
155+
//debugFlag = true;
156+
if (debugFlag) {
157+
//let code = "TTKO";
158+
//let code = "54JY";
159+
//let code = "7RVO";
160+
//let code = "R796";
161+
//let code = "XP1R";
162+
//let code = "XSVG";
163+
//let code = "XQKA";
164+
let code = "I8YO";
165+
let file = fs.readFileSync(`./examples/${code}.gif`);
166+
main(file)
167+
.then(res => console.log(res))
168+
.catch(err => {console.error(err)});
169+
};

codes/dots_and_chars/tesseract_ocr.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const { createWorker, createScheduler, PSM, OEM } = require('tesseract.js');
2+
const scheduler = createScheduler();
3+
4+
class tesseract_ocr {
5+
constructor(workers) {
6+
this.workersNum = workers
7+
};
8+
init = async () => {
9+
for (var i = 0; i < this.workersNum; i++) {
10+
const worker = createWorker();
11+
await worker.load();
12+
await worker.loadLanguage('eng');
13+
await worker.initialize('eng');
14+
await worker.setParameters({
15+
tessedit_char_whitelist: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
16+
tessedit_pageseg_mode: PSM.SINGLE_CHAR,
17+
tessedit_ocr_engine_mode: OEM.TESSERACT_LSTM_COMBINED,
18+
//tessedit_ocr_engine_mode: OEM.LSTM_ONLY,
19+
});
20+
scheduler.addWorker(worker);
21+
console.log(`${i + 1}/${this.workersNum} worker(s) initalized`);
22+
}
23+
};
24+
recognize = async (image) => {
25+
var timeBegin = Date.now();
26+
const { data: { text } } = await scheduler.addJob('recognize', image);
27+
return { result: text.replace("\n", ""), time: Date.now() - timeBegin };
28+
};
29+
terminate = async () => {
30+
await scheduler.terminate();
31+
};
32+
getQueueLen = async () => {
33+
var len = await scheduler.getQueueLen();
34+
return len;
35+
};
36+
}
37+
38+
module.exports = tesseract_ocr;

docs/img/dots_and_chars.gif

2.8 KB

judge_and_test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ global.debugFlag = 1;
99
var modeList = {
1010
"simplest" : (ans, rightAns) => ans.result == rightAns,
1111
"grids_and_equations" : (ans, rightAns) => ans.equation.slice(0, 3) == rightAns,
12+
"dots_and_chars" : (ans, rightAns) => ans.result == rightAns
1213
};
1314

1415
(async () => {
1516
var modeI = 0;
1617
for(let mode in modeList){
1718
let cvocr = new cvocrModule(mode);
1819
console.log(`--- ${++modeI}. ${mode} ---\n`);
19-
await cvocr.init(2, 1);
20+
await cvocr.init(4, 2);
2021

2122
let examplePath = path.join(__dirname, "codes", mode, "examples");
2223
let files = fs.readdirSync(examplePath);

0 commit comments

Comments
 (0)