Skip to content

Repository files navigation

captcha-ocr

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.

Requirements

  • 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.

Install

pnpm add captcha-ocr

The package includes ESM, CommonJS, and TypeScript declaration builds.

Quick start

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:

  • Buffer
  • Uint8Array
  • A raw Base64 string
  • An image Data URL such as data:image/png;base64,...

Reuse a recognizer

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.

Recognition result

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);

Built-in arithmetic rule

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';

Custom profiles and rules

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.

CommonJS

const {
  arithmeticProfile,
  createRecognizer
} = require('captcha-ocr');

Error handling

  • OCR may succeed while the configured rule fails to parse the text. In that case, the result has ok: false and a structured error.
  • 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.

Local demo

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 start

Open http://127.0.0.1:3000 after the server starts.

About

A TypeScript OCR toolkit for image-based CAPTCHAs with Base64 input, built-in arithmetic rules, and extensible recognition profiles.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages