PaddlePaddle/PaddleOCR · error
Unexpected recognition channels: ${String(channels)}
Error message
Unexpected recognition channels: ${String(channels)} What it means
Thrown in rec preprocessing when the first entry of image_shape (channels) parsed from inference.yml is not 3. The rec pipeline only implements 3-channel BGR input construction; a config declaring 1-channel (grayscale) or other channel counts is rejected per-sample rather than at config parse, because imageShape[0] is only checked where pixels are packed.
Source
Thrown at paddleocr-js/packages/core/src/models/rec.ts:194
function preprocess(context: { cv: OpenCv; config: RecModelConfig }, mats: Mat[]): RecSample[] {
const samples: RecSample[] = [];
for (let i = 0; i < mats.length; i += 1) {
samples.push(preprocessSample(context, mats[i], i));
}
return samples;
}
function preprocessSample(
context: { cv: OpenCv; config: RecModelConfig },
cropMat: Mat,
inputIndex: number
): RecSample {
const { cv, config } = context;
const [channels, targetH, baseW] = config.imageShape;
const srcW = cropMat.cols;
const srcH = cropMat.rows;
if (channels !== 3) {
throw new Error(`Unexpected recognition channels: ${String(channels)}`);
}
const ratio = srcW / Math.max(1, srcH);
const maxWhRatio = Math.max(baseW / Math.max(1, targetH), ratio);
const recW = clamp(Math.trunc(targetH * maxWhRatio), 1, MAX_REC_WIDTH);
const resizedW = Math.min(recW, Math.ceil(targetH * ratio));
const resized = new cv.Mat();
const bgr = new cv.Mat();
cv.resize(cropMat, resized, new cv.Size(resizedW, targetH), 0, 0, cv.INTER_LINEAR);
if (resized.channels() === 4) {
cv.cvtColor(resized, bgr, cv.COLOR_RGBA2BGR);
} else if (resized.channels() === 1) {
cv.cvtColor(resized, bgr, cv.COLOR_GRAY2BGR);
} else {
resized.copyTo(bgr);
}
const resizedChw = toBgrFloatCHWFromBgr(bgr.data, resizedW, targetH, REC_NORMALIZE);
const chw = new Float32Array(3 * targetH * recW);
const dstPerChannel = targetH * recW;View on GitHub (pinned to 2661c7c0ef)
Solutions
- Set RecResizeImg.image_shape in inference.yml to a 3-channel form, e.g. [3, 48, 320]
- If the axes were transposed, restore the canonical [C, H, W] order
- Ensure the values are plain YAML integers, not quoted strings
- Use the bundled rec model assets to avoid config drift
Example fix
# before (inference.yml) RecResizeImg: image_shape: [1, 48, 320] # after RecResizeImg: image_shape: [3, 48, 320]
Defensive patterns
Strategy: validation
Validate before calling
const [channels] = recModel.config.imageShape;
if (channels !== 3) {
throw new Error(`Rec model declares ${channels} channels; this runtime requires 3`);
} Type guard
function isThreeChannelShape(shape: unknown): shape is [3, number, number] {
return Array.isArray(shape) && shape.length >= 3 && shape[0] === 3;
} Try / catch
try { await recModel.predict(cv, crops); } catch (e) {
if (e instanceof Error && /Unexpected recognition channels/.test(e.message)) {
// fix inference.yml image_shape to [3, H, W] and reload the model
} else throw e;
} Prevention
- After parsing a rec config, assert image_shape[0] === 3 before loading
- Do not export rec models with grayscale input unless the runtime supports it
- Use canonical [C, H, W] ordering with plain integers in YAML
When it happens
Trigger: Loading a rec model whose inference.yml has RecResizeImg.image_shape: [1, 32, 320] (a grayscale export); a config where image_shape entries are strings (["3","48","320"]) so the !== 3 comparison fails; mis-ordered shape like [48, 320, 3].
Common situations: Re-exporting a rec model trained on grayscale without restoring 3-channel input in the config; hand-editing image_shape and transposing the axes; using an experimental model variant with different channel conventions.
Related errors
- RecResizeImg.image_shape is required in rec inference.yml
- Unexpected rec output dims: [${dims.join(", ")}]
- Unexpected det output dims: [${dims.join(", ")}]
- Unexpected det output dims: [${od.join(", ")}]
- Detection batch output N=${String(nOut)} does not match inpu
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/3bccd44f41c56464.
Report an issue: GitHub.