recognitionInput function
Recognition preprocessing for one cropped text line: resize to the
network's fixed targetHeight keeping aspect, then normalize to NCHW
float32 in [-1, 1] (PP-OCR rec convention: (pixel/255 - 0.5) / 0.5).
The tensor is paddedWidth wide: the line's own width, but never less
than minWidth (zero-padded on the right, like PaddleOCR's
resize_norm_img, which pads to max(320/48, w/h) * 48). The recognizer
is fully convolutional along x, so a long line keeps its aspect instead of
being squeezed into a fixed width - squashing it is what turns legible
characters into a different string. maxWidth is only a safety cap on
pathological crops (the width it scales a wider line down to).
Returns the tensor, the width the line actually filled, and the tensor's width.
Implementation
({Float32List tensor, int width, int paddedWidth}) recognitionInput(
OcrImage crop, {
int targetHeight = 48,
int minWidth = 320,
int maxWidth = 3200,
}) {
final scaledW =
(crop.width * targetHeight / crop.height).round().clamp(1, maxWidth);
final paddedW = scaledW > minWidth ? scaledW : minWidth;
final resized = crop.resize(scaledW, targetHeight);
final out = Float32List(3 * targetHeight * paddedW); // zero-padded
final plane = targetHeight * paddedW;
final rgba = resized.rgba;
for (var y = 0; y < targetHeight; y++) {
for (var x = 0; x < scaledW; x++) {
final p = (y * scaledW + x) * 4;
final o = y * paddedW + x;
out[o] = (rgba[p] / 255.0 - 0.5) / 0.5;
out[plane + o] = (rgba[p + 1] / 255.0 - 0.5) / 0.5;
out[2 * plane + o] = (rgba[p + 2] / 255.0 - 0.5) / 0.5;
}
}
return (tensor: out, width: scaledW, paddedWidth: paddedW);
}