src/helpers: add photo recognision functions
This commit is contained in:
parent
618d538876
commit
492913df70
210
src/helpers/findTextRegions.js
Normal file
210
src/helpers/findTextRegions.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
export function findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextWidth, grayScaleThreshold, widthTolerance, heightTolerance) {
|
||||||
|
maxWhiteSpace = maxWhiteSpace ?? 8;
|
||||||
|
maxFontLineWidth = maxFontLineWidth ?? maxWhiteSpace * 3;
|
||||||
|
minTextWidth = minTextWidth ?? maxWhiteSpace;
|
||||||
|
grayScaleThreshold = grayScaleThreshold ?? 100;
|
||||||
|
widthTolerance = widthTolerance ?? 3;
|
||||||
|
heightTolerance = heightTolerance ?? 8;
|
||||||
|
|
||||||
|
if (!image.width) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
let canvas = document.createElement('canvas');
|
||||||
|
canvas.width = image.width;
|
||||||
|
canvas.height = image.height;
|
||||||
|
|
||||||
|
let ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(image, 0, 0);
|
||||||
|
let data = ctx.getImageData(0, 0, image.width, image.height);
|
||||||
|
|
||||||
|
// convert to black & white picture
|
||||||
|
for (let y = 0; y < image.height; y++) {
|
||||||
|
for (let x = 0; x < image.width; x++) {
|
||||||
|
let offset = ((y * image.width) + x) * 4;
|
||||||
|
let r = data.data[offset + 0] * 0.30; // 30%
|
||||||
|
let g = data.data[offset + 1] * 0.59; // 59%
|
||||||
|
let b = data.data[offset + 2] * 0.11; // 11%
|
||||||
|
let c = Math.ceil(r + g + b);
|
||||||
|
|
||||||
|
r = g = b = c < grayScaleThreshold ? 0 : 255;
|
||||||
|
|
||||||
|
data.data[offset + 0] = r;
|
||||||
|
data.data[offset + 1] = g;
|
||||||
|
data.data[offset + 2] = b;
|
||||||
|
data.data[offset + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// redraw after grayscaling
|
||||||
|
ctx.putImageData(data, 0, 0);
|
||||||
|
|
||||||
|
let whitePixels = 0;
|
||||||
|
let blackPixels = 0;
|
||||||
|
let patternLength = 0;
|
||||||
|
let patternStartX = -1;
|
||||||
|
let segments = [];
|
||||||
|
|
||||||
|
for (let y = 0; y < image.height; y++) {
|
||||||
|
segments.push([]);
|
||||||
|
|
||||||
|
for (let x = 0; x < image.width; x++) {
|
||||||
|
let o = (y * image.width + x) * 4;
|
||||||
|
let r = data.data[o + 0] << 16;
|
||||||
|
let g = data.data[o + 1] << 8;
|
||||||
|
let b = data.data[o + 2] << 0;
|
||||||
|
let a = data.data[o + 3] << 24;
|
||||||
|
let c = 0x100000000 + a + r + g + b;
|
||||||
|
|
||||||
|
if (c === 0xFFFFFFFF && patternStartX !== -1) {
|
||||||
|
whitePixels++;
|
||||||
|
blackPixels = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c === 0xFF000000) {
|
||||||
|
blackPixels++;
|
||||||
|
whitePixels = 0;
|
||||||
|
|
||||||
|
if (patternStartX === -1) {
|
||||||
|
patternStartX = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check white and black pattern maximum lenghts
|
||||||
|
if (whitePixels > maxWhiteSpace || blackPixels > maxFontLineWidth || x === image.width - 1) {
|
||||||
|
if (patternLength >= minTextWidth) {
|
||||||
|
segments[y].push([patternStartX, y, patternStartX + patternLength, y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
whitePixels = 0;
|
||||||
|
blackPixels = 0;
|
||||||
|
patternLength = 0;
|
||||||
|
patternStartX = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patternStartX !== -1) {
|
||||||
|
patternLength++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let y = 0; y < image.height - 2; y++) {
|
||||||
|
let listY = segments[y];
|
||||||
|
|
||||||
|
for (let w = y + 1; w <= y + 2; w++) {
|
||||||
|
let listW = segments[w];
|
||||||
|
|
||||||
|
for (let i = 0; i < listY.length; i++) {
|
||||||
|
let sA = listY[i];
|
||||||
|
|
||||||
|
for (let j = 0; j < listW.length; j++) {
|
||||||
|
let sB = listW[j];
|
||||||
|
|
||||||
|
// horizontal intersection
|
||||||
|
if
|
||||||
|
(
|
||||||
|
(sA[0] <= sB[0] && sA[2] >= sB[2]) ||
|
||||||
|
(sA[0] >= sB[0] && sA[0] <= sB[2]) ||
|
||||||
|
(sA[2] >= sB[0] && sA[2] <= sB[2])
|
||||||
|
|
||||||
|
) {
|
||||||
|
sA[0] = Math.min(sA[0], sB[0]);
|
||||||
|
sA[2] = Math.max(sA[2], sB[2]);
|
||||||
|
sA[3] = sB[3];
|
||||||
|
|
||||||
|
listY.splice(i--, 1);
|
||||||
|
listW.splice(j, 1);
|
||||||
|
listW.push(sA);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let foundSegments = [];
|
||||||
|
for (let y = 0; y < image.height; y++) {
|
||||||
|
let list = segments[y];
|
||||||
|
|
||||||
|
for (let i in list) {
|
||||||
|
const x1 = list[i][0];
|
||||||
|
const y1 = list[i][1];
|
||||||
|
const x2 = list[i][2];
|
||||||
|
const y2 = list[i][3];
|
||||||
|
|
||||||
|
if (x1 !== -1 && y1 !== -1 && x2 !== -1 && y2 !== -1) {
|
||||||
|
let w = (x2 - x1) + 1;
|
||||||
|
let h = (y2 - y1) + 1;
|
||||||
|
|
||||||
|
if (w >= minTextWidth && h >= minTextWidth) {
|
||||||
|
foundSegments.push({
|
||||||
|
x: x1 - widthTolerance * 2,
|
||||||
|
y: y1 - heightTolerance,
|
||||||
|
w: w + widthTolerance,
|
||||||
|
h: h + heightTolerance * 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hopefully we found at least something
|
||||||
|
if (!foundSegments.length) {
|
||||||
|
console.assert("Error: findTextRegions() did not found anything");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
let gridWidth = 0;
|
||||||
|
let gridHeight = 0;
|
||||||
|
|
||||||
|
// find grid dimensions
|
||||||
|
let lastY = foundSegments[0].y;
|
||||||
|
let lastX = foundSegments[0].x;
|
||||||
|
|
||||||
|
for (let s of foundSegments) {
|
||||||
|
if (Math.abs(lastY - s.y) < minTextWidth) {
|
||||||
|
lastY = Math.min(lastY, s.y);
|
||||||
|
gridWidth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(lastX - s.x) < minTextWidth) {
|
||||||
|
lastX = Math.min(lastX, s.x);
|
||||||
|
gridHeight++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hopefully we matched every letter in the grid
|
||||||
|
if (gridWidth * gridHeight !== segments.length) {
|
||||||
|
console.assert("Dimentions are not equal to the number of matches");
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare grid for letters
|
||||||
|
let grid = Array.from(Array(gridHeight), () => new Array(gridWidth));
|
||||||
|
|
||||||
|
// fill grid with imageData objects (captured cutouts)
|
||||||
|
for (let j = 0; j < gridHeight; j++) {
|
||||||
|
const segmentOffset = j * gridWidth;
|
||||||
|
const indexMap = foundSegments
|
||||||
|
.slice(segmentOffset, segmentOffset + gridWidth);
|
||||||
|
const reverseIndexMap = indexMap
|
||||||
|
.map(s => s.x)
|
||||||
|
.sort((a, b) => a - b)
|
||||||
|
.reduce((o, x, i) => (o[x] = i, o), {});
|
||||||
|
|
||||||
|
for (let i = 0; i < gridWidth; i++) {
|
||||||
|
let s = foundSegments[segmentOffset + i];
|
||||||
|
let croppedData = ctx.getImageData(s.x, s.y, s.w, s.h);
|
||||||
|
grid[j][reverseIndexMap[s.x]] = {
|
||||||
|
data: croppedData,
|
||||||
|
...s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
grid,
|
||||||
|
gridWidth,
|
||||||
|
gridHeight,
|
||||||
|
}
|
||||||
|
}
|
17
src/helpers/recognizeTextOnImage.ts
Normal file
17
src/helpers/recognizeTextOnImage.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export function recognise(): string {
|
||||||
|
let out;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const {data: {text}} = await Tesseract.recognize(image, 'pol', {
|
||||||
|
workerPath: 'https://unpkg.com/tesseract.js@v2.0.0/dist/worker.min.js',
|
||||||
|
langPath: 'https://tessdata.projectnaptha.com/4.0.0',
|
||||||
|
corePath: 'https://unpkg.com/tesseract.js-core@v2.0.0/tesseract-core.wasm.js',
|
||||||
|
cacheMethod: 'none',
|
||||||
|
});
|
||||||
|
out = text;
|
||||||
|
console.log("recognised: ", text);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return out || "";
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user