{"record":{"id":"3bccd44f41c56464","repo":"PaddlePaddle/PaddleOCR","slug":"unexpected-recognition-channels-string-channels","errorCode":null,"errorMessage":"Unexpected recognition channels: ${String(channels)}","messagePattern":"Unexpected recognition channels: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"paddleocr-js/packages/core/src/models/rec.ts","lineNumber":194,"sourceCode":"function preprocess(context: { cv: OpenCv; config: RecModelConfig }, mats: Mat[]): RecSample[] {\n  const samples: RecSample[] = [];\n  for (let i = 0; i < mats.length; i += 1) {\n    samples.push(preprocessSample(context, mats[i], i));\n  }\n  return samples;\n}\n\nfunction preprocessSample(\n  context: { cv: OpenCv; config: RecModelConfig },\n  cropMat: Mat,\n  inputIndex: number\n): RecSample {\n  const { cv, config } = context;\n  const [channels, targetH, baseW] = config.imageShape;\n  const srcW = cropMat.cols;\n  const srcH = cropMat.rows;\n  if (channels !== 3) {\n    throw new Error(`Unexpected recognition channels: ${String(channels)}`);\n  }\n  const ratio = srcW / Math.max(1, srcH);\n  const maxWhRatio = Math.max(baseW / Math.max(1, targetH), ratio);\n  const recW = clamp(Math.trunc(targetH * maxWhRatio), 1, MAX_REC_WIDTH);\n  const resizedW = Math.min(recW, Math.ceil(targetH * ratio));\n  const resized = new cv.Mat();\n  const bgr = new cv.Mat();\n  cv.resize(cropMat, resized, new cv.Size(resizedW, targetH), 0, 0, cv.INTER_LINEAR);\n  if (resized.channels() === 4) {\n    cv.cvtColor(resized, bgr, cv.COLOR_RGBA2BGR);\n  } else if (resized.channels() === 1) {\n    cv.cvtColor(resized, bgr, cv.COLOR_GRAY2BGR);\n  } else {\n    resized.copyTo(bgr);\n  }\n  const resizedChw = toBgrFloatCHWFromBgr(bgr.data, resizedW, targetH, REC_NORMALIZE);\n  const chw = new Float32Array(3 * targetH * recW);\n  const dstPerChannel = targetH * recW;","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr-js/packages/core/src/models/rec.ts#L176-L212","documentation":"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.","triggerScenarios":"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].","commonSituations":"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.","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"],"exampleFix":"# before (inference.yml)\nRecResizeImg:\n  image_shape: [1, 48, 320]\n\n# after\nRecResizeImg:\n  image_shape: [3, 48, 320]","handlingStrategy":"validation","validationCode":"const [channels] = recModel.config.imageShape;\nif (channels !== 3) {\n  throw new Error(`Rec model declares ${channels} channels; this runtime requires 3`);\n}","typeGuard":"function isThreeChannelShape(shape: unknown): shape is [3, number, number] {\n  return Array.isArray(shape) && shape.length >= 3 && shape[0] === 3;\n}","tryCatchPattern":"try { await recModel.predict(cv, crops); } catch (e) {\n  if (e instanceof Error && /Unexpected recognition channels/.test(e.message)) {\n    // fix inference.yml image_shape to [3, H, W] and reload the model\n  } else throw e;\n}","preventionTips":["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"],"tags":["paddleocr","recognition","config","model-mismatch"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}