English | 中文
captcha-ocr is a TypeScript OCR package for image-based arithmetic captchas. It accepts image bytes or Base64 data, preprocesses difficult images, runs Tesseract OCR, and evaluates the recognized expression through a configurable profile and rule system.
- Node.js 20 or newer
- A native runtime supported by
sharp
The first OCR call may download the English Tesseract training data (eng.traineddata). Make sure the runtime can access the network during the first initialization, or provide the training data through your Tesseract setup.
pnpm add captcha-ocrThe package includes ESM, CommonJS, and TypeScript declaration builds.
import {
closeDefaultRecognizer,
recognizeCaptcha
} from 'captcha-ocr';
const result = await recognizeCaptcha(imageData);
if (result.ok) {
console.log(result.expression); // 7*9
console.log(result.answer); // 63
} else {
console.error(result.error?.code, result.error?.message);
}
await closeDefaultRecognizer();imageData can be any of the following:
BufferUint8Array- A raw Base64 string
- An image Data URL such as
data:image/png;base64,...
For server-side workloads, create one recognizer and reuse it for multiple images. This avoids initializing an OCR worker for every request.
import {
arithmeticProfile,
createRecognizer
} from 'captcha-ocr';
const recognizer = createRecognizer({
profile: arithmeticProfile
});
const first = await recognizer.recognize(firstImage);
const second = await recognizer.recognize(secondImage);
await recognizer.close();Call close() when the recognizer is no longer needed so its worker resources can be released.
interface RecognitionResult<TAnswer> {
ok: boolean;
profileId: string;
ocrText: string;
normalizedText: string;
expression: string | null;
answer: TAnswer | null;
error: {
code: string;
message: string;
} | null;
ocrVariant: 'original' | 'threshold';
durationMs: number;
source: {
width: number | null;
height: number | null;
format: string | null;
};
preprocess: {
mode: 'original' | 'threshold';
scale: number;
threshold: number | null;
};
processedImage?: Buffer;
}The processed image is omitted by default. Enable it only when you need to inspect preprocessing results:
const result = await recognizer.recognize(imageData, {
includeProcessedImage: true
});
console.log(result.processedImage);The default arithmeticProfile supports:
+-*/
Common OCR variants such as x, X, ×, and · are normalized to *; ÷ is normalized to /. Expressions are evaluated with a fixed parser and do not use eval.
You can import the arithmetic rule directly:
import {
arithmeticProfile,
createArithmeticRule
} from 'captcha-ocr/rules/arithmetic';The recognizer is not limited to arithmetic. A profile defines OCR options and preprocessing, while a rule normalizes, parses, and evaluates recognized text.
import {
createRecognizer,
type CaptchaProfile,
type CaptchaRule
} from 'captcha-ocr';
interface ParsedCode {
value: string;
}
const rule: CaptchaRule<ParsedCode, string> = {
id: 'fixed-code-v1',
normalize(text) {
return text.trim().toUpperCase();
},
parse(text) {
const normalized = this.normalize(text);
if (!/^[A-F0-9]{4}$/.test(normalized)) {
return {
ok: false,
normalized,
error: {
code: 'CODE_NOT_MATCHED',
message: 'The value is not a four-character hexadecimal code.'
}
};
}
return {
ok: true,
normalized,
expression: normalized,
parsed: {
value: normalized
}
};
},
evaluate(parsed) {
return parsed.value;
}
};
const profile: CaptchaProfile<ParsedCode, string> = {
id: 'fixed-code-v1',
ocr: {
language: 'eng',
whitelist: '0123456789ABCDEF',
pageSegMode: 7
},
rule
};
const recognizer = createRecognizer({ profile });
const result = await recognizer.recognize(imageData);
await recognizer.close();This keeps the OCR pipeline reusable while allowing each captcha format to define its own parsing and evaluation behavior.
const {
arithmeticProfile,
createRecognizer
} = require('captcha-ocr');- OCR may succeed while the configured rule fails to parse the text. In that case, the result has
ok: falseand a structurederror. - Invalid image input throws an
ImageInputError. - OCR worker initialization, image decoding, and native dependency errors are thrown to the caller and should be handled according to the host application's needs.
The repository also contains a small Web UI and CLI for manual testing. They are development tools and are not included in the published package.
pnpm install
pnpm startOpen http://127.0.0.1:3000 after the server starts.