Improve OCR preprocessing and normalize merchant names

This commit is contained in:
2026-08-11 19:18:46 +03:00
parent 77c597dfd8
commit de41230b9b
3 changed files with 36 additions and 2 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ export function parseStoreHeaderText(text) {
return {
side: match[1].toLowerCase(),
merchant: match[2].trim(),
merchant: match[2].replace(/\s+/g, ""),
rawText: normalized,
};
}
+27 -1
View File
@@ -4,6 +4,16 @@ import { normalizeOcrText } from "./parser.js";
let cvPromise;
let workerPromise;
let progressListener = () => {};
const WHITE_TEXT_MIN_CHANNEL = 150;
const WHITE_TEXT_MAX_SPREAD = 55;
export function isWhiteTextPixel(red, green, blue, alpha = 255) {
return (
alpha >= 128 &&
Math.min(red, green, blue) >= WHITE_TEXT_MIN_CHANNEL &&
Math.max(red, green, blue) - Math.min(red, green, blue) <= WHITE_TEXT_MAX_SPREAD
);
}
export function onOcrProgress(listener) {
progressListener = listener;
@@ -79,7 +89,6 @@ function prepareOcrCrop(sourceCanvas, rect) {
const context = canvas.getContext("2d", { willReadFrequently: true });
context.imageSmoothingEnabled = false;
context.filter = "grayscale(1) contrast(2)";
context.drawImage(
sourceCanvas,
rect.x,
@@ -92,6 +101,23 @@ function prepareOcrCrop(sourceCanvas, rect) {
canvas.height,
);
const image = context.getImageData(0, 0, canvas.width, canvas.height);
for (let index = 0; index < image.data.length; index += 4) {
const value = isWhiteTextPixel(
image.data[index],
image.data[index + 1],
image.data[index + 2],
image.data[index + 3],
)
? 0
: 255;
image.data[index] = value;
image.data[index + 1] = value;
image.data[index + 2] = value;
image.data[index + 3] = 255;
}
context.putImageData(image, 0, 0);
return canvas;
}
+8
View File
@@ -12,6 +12,13 @@ import {
rectFromPoints,
resolveFieldRect,
} from "../src/parser.js";
import { isWhiteTextPixel } from "../src/vision.js";
test("keeps white text pixels and removes colored or dark pixels", () => {
assert.equal(isWhiteTextPixel(220, 215, 195), true);
assert.equal(isWhiteTextPixel(210, 165, 80), false);
assert.equal(isWhiteTextPixel(120, 120, 120), false);
});
test("keeps only letters, digits and spaces in OCR item names", () => {
assert.equal(cleanItemName(" ~~ Soulshot: S-grade ` "), "Soulshot S grade");
@@ -139,5 +146,6 @@ test("parses trade side and merchant from the store header", () => {
merchant: "Dwa",
rawText: "Private Store({Sell) - Dwa",
});
assert.equal(parseStoreHeaderText("Private Store(Buy) - Hik vision")?.merchant, "Hikvision");
assert.equal(parseStoreHeaderText("Items on Sale"), null);
});