src: clean up, add test code

This commit is contained in:
Artur Tamborski 2021-01-17 12:58:28 +01:00
parent bac9a2dcf4
commit bf74693edb
4 changed files with 63 additions and 27 deletions

View File

@ -8,10 +8,6 @@
"test": "react-scripts test" "test": "react-scripts test"
}, },
"dependencies": { "dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0", "@types/node": "^12.0.0",
"@types/react": "^16.9.53", "@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8", "@types/react-dom": "^16.9.8",
@ -21,8 +17,7 @@
"react-scripts": "4.0.0", "react-scripts": "4.0.0",
"sass": "^1.29.0", "sass": "^1.29.0",
"tesseract.js": "^2.1.4", "tesseract.js": "^2.1.4",
"typescript": "^4.0.3", "typescript": "^4.0.3"
"web-vitals": "^0.2.4"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [

View File

@ -1,9 +1,12 @@
import React from 'react'; import React from 'react';
import ImageUploader from 'react-images-upload';
import Logo from '../Logo/Logo'; import Logo from '../Logo/Logo';
import Board from '../Board/Board'; import Board from '../Board/Board';
import './App.scss'; import './App.scss';
import {findTextRegions} from "../../helpers/findTextRegions";
export type Point = { export type Point = {
x: number; x: number;
@ -35,7 +38,28 @@ export default class App extends React.Component {
super(props); super(props);
const gameId = (Math.trunc(Math.random() * 100) % 2) + 1; const gameId = (Math.trunc(Math.random() * 100) % 2) + 1;
this.game = require(`../../constants/${gameId}.json`) this.game = require(`../../constants/${gameId}.json`);
}
handleTakePhoto(pictures: any[], _: any[]): void {
console.log("handleTakePhoto: loading image...")
const image = pictures[0];
const {grid, gridWidth, gridHeight} = findTextRegions(image);
console.log(grid, gridWidth, gridHeight);
if (!grid) {
return;
}
debugger;
let g = grid[0][0];
let ot = document.createElement('canvas');
ot.width = g.w;
ot.height = g.h;
ot.getContext('2d')?.putImageData(g.data, 0, 0);
document.body.appendChild(ot);
} }
renderAnswers(): Array<JSX.Element> { renderAnswers(): Array<JSX.Element> {
@ -55,7 +79,15 @@ export default class App extends React.Component {
</div> </div>
</div> </div>
<div /> <div />
<div /> <div style={{paddingLeft: '300px'}}>
<ImageUploader
withIcon={false}
buttonText='Wrzuć zdjęcie!'
onChange={this.handleTakePhoto.bind(this)}
imgExtension={['.jpg', '.jpeg', '.png']}
maxFileSize={5242880 * 5} // 5MB * 5
/>
</div>
<div> <div>
<Board <Board
cells={this.game.cells} cells={this.game.cells}

View File

@ -22,6 +22,7 @@ export function findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextW
for (let y = 0; y < image.height; y++) { for (let y = 0; y < image.height; y++) {
for (let x = 0; x < image.width; x++) { for (let x = 0; x < image.width; x++) {
let offset = ((y * image.width) + x) * 4; let offset = ((y * image.width) + x) * 4;
// noinspection PointlessArithmeticExpressionJS
let r = data.data[offset + 0] * 0.30; // 30% let r = data.data[offset + 0] * 0.30; // 30%
let g = data.data[offset + 1] * 0.59; // 59% let g = data.data[offset + 1] * 0.59; // 59%
let b = data.data[offset + 2] * 0.11; // 11% let b = data.data[offset + 2] * 0.11; // 11%
@ -29,6 +30,7 @@ export function findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextW
r = g = b = c < grayScaleThreshold ? 0 : 255; r = g = b = c < grayScaleThreshold ? 0 : 255;
// noinspection PointlessArithmeticExpressionJS
data.data[offset + 0] = r; data.data[offset + 0] = r;
data.data[offset + 1] = g; data.data[offset + 1] = g;
data.data[offset + 2] = b; data.data[offset + 2] = b;
@ -50,6 +52,7 @@ export function findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextW
for (let x = 0; x < image.width; x++) { for (let x = 0; x < image.width; x++) {
let o = (y * image.width + x) * 4; let o = (y * image.width + x) * 4;
// noinspection PointlessArithmeticExpressionJS
let r = data.data[o + 0] << 16; let r = data.data[o + 0] << 16;
let g = data.data[o + 1] << 8; let g = data.data[o + 1] << 8;
let b = data.data[o + 2] << 0; let b = data.data[o + 2] << 0;
@ -134,8 +137,8 @@ export function findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextW
const y2 = list[i][3]; const y2 = list[i][3];
if (x1 !== -1 && y1 !== -1 && x2 !== -1 && y2 !== -1) { if (x1 !== -1 && y1 !== -1 && x2 !== -1 && y2 !== -1) {
let w = (x2 - x1) + 1; const w = (x2 - x1) + 1;
let h = (y2 - y1) + 1; const h = (y2 - y1) + 1;
if (w >= minTextWidth && h >= minTextWidth) { if (w >= minTextWidth && h >= minTextWidth) {
foundSegments.push({ foundSegments.push({
@ -151,7 +154,7 @@ export function findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextW
// hopefully we found at least something // hopefully we found at least something
if (!foundSegments.length) { if (!foundSegments.length) {
console.assert("Error: findTextRegions() did not found anything"); console.error("findTextRegions(): Error: findTextRegions() did not found anything");
return {}; return {};
} }
@ -176,29 +179,28 @@ export function findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextW
// hopefully we matched every letter in the grid // hopefully we matched every letter in the grid
if (gridWidth * gridHeight !== segments.length) { if (gridWidth * gridHeight !== segments.length) {
console.assert("Dimentions are not equal to the number of matches"); console.warning("findTextRegions(): Dimensions are not equal to the number of matches");
} }
// prepare grid for letters // prepare grid for letters
let grid = Array.from(Array(gridHeight), () => new Array(gridWidth)); let grid = Array.from(Array(gridHeight), () => new Array(gridWidth));
// fill grid with imageData objects (captured cutouts) // fill grid with imageData / captured cutouts
for (let j = 0; j < gridHeight; j++) { for (let j = 0; j < gridHeight; j++) {
const segmentOffset = j * gridWidth; const segmentOffset = j * gridWidth;
const indexMap = foundSegments const indexMap = foundSegments
.slice(segmentOffset, segmentOffset + gridWidth); .slice(segmentOffset, segmentOffset + gridWidth)
const reverseIndexMap = indexMap
.map(s => s.x) .map(s => s.x)
.sort((a, b) => a - b) .sort((a, b) => a - b)
.reduce((o, x, i) => (o[x] = i, o), {}); .reduce((o, x, i) => {
o[x] = i;
return o
}, {});
for (let i = 0; i < gridWidth; i++) { for (let i = 0; i < gridWidth; i++) {
let s = foundSegments[segmentOffset + i]; let s = foundSegments[segmentOffset + i];
let croppedData = ctx.getImageData(s.x, s.y, s.w, s.h); let croppedData = ctx.getImageData(s.x, s.y, s.w, s.h);
grid[j][reverseIndexMap[s.x]] = { grid[j][indexMap[s.x]] = {data: croppedData, ...s}
data: croppedData,
...s,
}
} }
} }

View File

@ -1,16 +1,23 @@
import {createWorker, ImageLike, PSM} from 'tesseract.js';
export function recognise(): string { export function recognizeTextOnImage(image: ImageLike, isColumn: boolean): string {
let out; let out;
(async () => { (async () => {
const {data: {text}} = await Tesseract.recognize(image, 'pol', { const worker = createWorker();
workerPath: 'https://unpkg.com/tesseract.js@v2.0.0/dist/worker.min.js', await worker.load();
langPath: 'https://tessdata.projectnaptha.com/4.0.0', await worker.loadLanguage('pol');
corePath: 'https://unpkg.com/tesseract.js-core@v2.0.0/tesseract-core.wasm.js', await worker.initialize('pol');
cacheMethod: 'none', await worker.setParameters({
// @ts-ignore, i don't know why this PSM.... stuff is mad at me
tessedit_pageseg_mode: isColumn ? PSM.SINGLE_COLUMN : PSM.SINGLE_CHAR,
tessedit_char_whitelist: 'AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWXYZŹŻ',
tessjs_create_box: '1',
}); });
const { data: { text } } = await worker.recognize(image);
out = text; out = text;
console.log("recognised: ", text); await worker.terminate();
})(); })();
return out || ""; return out || "";