@camstack/addon-pipeline 1.2.101 → 1.2.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{addon-utils-eUS6n_Zj.js → addon-utils-CLc6yHCN.js} +1 -1
- package/dist/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/detection-pipeline/index.js +163 -42
- package/dist/detection-pipeline/index.mjs +161 -40
- package/dist/{dist-dm3t4BOt.js → dist-C11WuNUP.js} +87 -51
- package/dist/{dist-gXdWP96z.mjs → dist-Ccmt3fGJ.mjs} +82 -52
- package/dist/{event-loop-stall-monitor-Lki2_mYY.js → event-loop-stall-monitor-Cq_NeC4o.js} +175 -132
- package/dist/{event-loop-stall-monitor-TnQ21mu0.mjs → event-loop-stall-monitor-OJrOMeuu.mjs} +170 -133
- package/dist/{lazy-sharp-oYppDIZR.js → lazy-sharp-RxUs6on_.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +5 -4
- package/dist/pipeline-runner/index.mjs +4 -3
- package/dist/{process-memory-CKWh609B.mjs → process-memory-D0zDmXLI.mjs} +1 -1
- package/dist/{process-memory-vkFVOdkV.js → process-memory-DOjQ3MgC.js} +1 -1
- package/dist/recorder/index.js +2 -2
- package/dist/recorder/index.mjs +1 -1
- package/dist/session-decode/decode-worker-child.js +2 -2
- package/dist/session-decode/decode-worker-child.mjs +1 -1
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C4fRkYcj.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BdgcF1lL.mjs} +2 -2
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-zT-cYUdF.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DY31bUCj.mjs} +1 -1
- package/dist/stream-broker/{hostInit-CS6xpCdb.mjs → hostInit-Da9wVA2r.mjs} +2 -2
- package/dist/stream-broker/index.js +31 -11
- package/dist/stream-broker/index.mjs +31 -11
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-BrlrMxXu.js → worker-protocol-BePduZVV.js} +1 -1
- package/dist/{worker-protocol-DJWdPLKF.mjs → worker-protocol-DDpliBIW.mjs} +1 -1
- package/package.json +1 -1
- package/python/postprocessors/ctc.py +56 -3
- package/python/postprocessors/test_ctc.py +37 -0
|
@@ -2,9 +2,9 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_process_memory = require("../process-memory-
|
|
7
|
-
const require_addon_utils = require("../addon-utils-
|
|
5
|
+
const require_dist = require("../dist-C11WuNUP.js");
|
|
6
|
+
const require_process_memory = require("../process-memory-DOjQ3MgC.js");
|
|
7
|
+
const require_addon_utils = require("../addon-utils-CLc6yHCN.js");
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
node_path = require_dist.__toESM(node_path);
|
|
10
10
|
let node_fs = require("node:fs");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __require } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { D as audioAnalysisCapability,
|
|
3
|
-
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-
|
|
2
|
+
import { D as audioAnalysisCapability, G as mapAudioLabelToMacro, O as audioAnalyzerCapability, bt as hydrateSchema, h as HF_BASE_URL, ht as BaseAddon, lt as errMsg, n as AUDIO_BACKEND_CHOICES, nt as resolvePoolMemoryPolicy, s as DEFAULT_AUDIO_ANALYZER_CONFIG, y as PoolMemoryWatchdog } from "../dist-Ccmt3fGJ.mjs";
|
|
3
|
+
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-D0zDmXLI.mjs";
|
|
4
4
|
import { i as downloadFile } from "../addon-utils-A2S9D7pu.mjs";
|
|
5
5
|
import * as path$1 from "node:path";
|
|
6
6
|
import * as fs from "node:fs";
|
|
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-
|
|
7
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
8
|
-
const require_process_memory = require("../process-memory-
|
|
9
|
-
const require_addon_utils = require("../addon-utils-
|
|
5
|
+
const require_dist = require("../dist-C11WuNUP.js");
|
|
6
|
+
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-Cq_NeC4o.js");
|
|
7
|
+
const require_lazy_sharp = require("../lazy-sharp-RxUs6on_.js");
|
|
8
|
+
const require_process_memory = require("../process-memory-DOjQ3MgC.js");
|
|
9
|
+
const require_addon_utils = require("../addon-utils-CLc6yHCN.js");
|
|
10
10
|
let sharp = require("sharp");
|
|
11
11
|
sharp = require_dist.__toESM(sharp);
|
|
12
12
|
let node_child_process = require("node:child_process");
|
|
@@ -3040,7 +3040,9 @@ async function nativeChildCrop(provider, bbox, imageWidth, imageHeight, maxWidth
|
|
|
3040
3040
|
* peaky (a few high rows) rather than smeared across many rows.
|
|
3041
3041
|
* 3. pick the angle maximising the score (coarse 2° sweep + a 0.5° local
|
|
3042
3042
|
* refinement); apply it to the full-resolution RGB tile only when it is
|
|
3043
|
-
* meaningfully non-zero
|
|
3043
|
+
* meaningfully non-zero AND the optimum was BRACKETED by the sweep — an
|
|
3044
|
+
* argmax sitting on the boundary means the profile never turned over and
|
|
3045
|
+
* the "angle" is an artefact of where the search stopped.
|
|
3044
3046
|
*
|
|
3045
3047
|
* All of the geometry is pure functions over raw buffers, so the estimator is
|
|
3046
3048
|
* fully deterministic and unit-testable without any image I/O.
|
|
@@ -3219,22 +3221,28 @@ function scoreAngle(gradient, angleRad) {
|
|
|
3219
3221
|
height: rotated.height
|
|
3220
3222
|
});
|
|
3221
3223
|
}
|
|
3224
|
+
var DEFAULT_COARSE_STEP_DEG = 2;
|
|
3225
|
+
var NO_SKEW = {
|
|
3226
|
+
angleDeg: 0,
|
|
3227
|
+
score: 0
|
|
3228
|
+
};
|
|
3222
3229
|
/**
|
|
3223
3230
|
* Estimate the dominant text-baseline skew of a grayscale plate crop and return
|
|
3224
3231
|
* the CORRECTION angle (degrees) that levels it. A coarse sweep over
|
|
3225
3232
|
* ±`maxAngleDeg` in `coarseStepDeg` steps finds the best bucket; a `fineStepDeg`
|
|
3226
3233
|
* local search refines it. Returns `0` when the flat (no-rotation) profile is
|
|
3227
|
-
* already the strongest response
|
|
3234
|
+
* already the strongest response, and — critically — also when the coarse
|
|
3235
|
+
* winner is not BRACKETED by the sweep (see {@link BOUNDARY_REJECT_DEG}): an
|
|
3236
|
+
* optimum sitting on the edge of the searched interval is not a measurement,
|
|
3237
|
+
* it is the place the search ran out of road.
|
|
3228
3238
|
*/
|
|
3229
3239
|
function estimateSkewAngle(gray, opts = {}) {
|
|
3230
|
-
const maxAngleDeg = opts.maxAngleDeg ??
|
|
3231
|
-
const coarseStepDeg = opts.coarseStepDeg ??
|
|
3240
|
+
const maxAngleDeg = opts.maxAngleDeg ?? 16;
|
|
3241
|
+
const coarseStepDeg = opts.coarseStepDeg ?? DEFAULT_COARSE_STEP_DEG;
|
|
3232
3242
|
const fineStepDeg = opts.fineStepDeg ?? .5;
|
|
3233
3243
|
const scoreMaxDim = opts.scoreMaxDim ?? 480;
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
score: 0
|
|
3237
|
-
};
|
|
3244
|
+
const boundaryRejectDeg = Math.max(0, maxAngleDeg - coarseStepDeg);
|
|
3245
|
+
if (gray.width < 4 || gray.height < 4) return NO_SKEW;
|
|
3238
3246
|
const gradient = sobelMagnitude(downscaleGray(gray, scoreMaxDim));
|
|
3239
3247
|
let bestAngle = 0;
|
|
3240
3248
|
let bestScore = scoreAngle(gradient, 0);
|
|
@@ -3246,6 +3254,7 @@ function estimateSkewAngle(gray, opts = {}) {
|
|
|
3246
3254
|
bestAngle = deg;
|
|
3247
3255
|
}
|
|
3248
3256
|
}
|
|
3257
|
+
if (Math.abs(bestAngle) >= boundaryRejectDeg) return NO_SKEW;
|
|
3249
3258
|
const lo = bestAngle - coarseStepDeg;
|
|
3250
3259
|
const hi = bestAngle + coarseStepDeg;
|
|
3251
3260
|
for (let deg = lo; deg <= hi + 1e-9; deg += fineStepDeg) {
|
|
@@ -3261,6 +3270,50 @@ function estimateSkewAngle(gray, opts = {}) {
|
|
|
3261
3270
|
score: bestScore
|
|
3262
3271
|
};
|
|
3263
3272
|
}
|
|
3273
|
+
function toSharpChannels(channels) {
|
|
3274
|
+
switch (channels) {
|
|
3275
|
+
case 1: return 1;
|
|
3276
|
+
case 2: return 2;
|
|
3277
|
+
case 4: return 4;
|
|
3278
|
+
default: return 3;
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
/**
|
|
3282
|
+
* Cut an expand-rotated canvas back to the LEVELLED SOURCE BAND.
|
|
3283
|
+
*
|
|
3284
|
+
* `rotateRaw(expand: true)` grows the canvas to `w·|cos| + h·|sin|` ×
|
|
3285
|
+
* `w·|sin| + h·|cos|` so no corner is clipped — but the `|sin|` terms are pure
|
|
3286
|
+
* corner spread: flat fill above/below (and left/right of) the plate. Handing
|
|
3287
|
+
* that to OCR is not free. The recognizer letterboxes the whole tile into a
|
|
3288
|
+
* fixed 64px-tall input, so a 155px crop returned at 194px shrinks every glyph
|
|
3289
|
+
* by ~20% before a single character is read — the warp paying back part of what
|
|
3290
|
+
* it just won.
|
|
3291
|
+
*
|
|
3292
|
+
* The band kept is the source rectangle scaled by `|cos angle|`, centred: a
|
|
3293
|
+
* source edge of length L, once levelled, spans `L·|cos|` along its own axis.
|
|
3294
|
+
* Everything outside is the fill the rotation introduced.
|
|
3295
|
+
*/
|
|
3296
|
+
function cropLevelledBand(rotated, srcWidth, srcHeight, angleRad) {
|
|
3297
|
+
const c = Math.abs(Math.cos(angleRad));
|
|
3298
|
+
const bw = Math.max(1, Math.min(rotated.width, Math.round(srcWidth * c)));
|
|
3299
|
+
const bh = Math.max(1, Math.min(rotated.height, Math.round(srcHeight * c)));
|
|
3300
|
+
if (bw === rotated.width && bh === rotated.height) return rotated;
|
|
3301
|
+
const ch = rotated.channels;
|
|
3302
|
+
const x0 = Math.max(0, Math.floor((rotated.width - bw) / 2));
|
|
3303
|
+
const y0 = Math.max(0, Math.floor((rotated.height - bh) / 2));
|
|
3304
|
+
const out = new Uint8Array(bw * bh * ch);
|
|
3305
|
+
const rowBytes = bw * ch;
|
|
3306
|
+
for (let y = 0; y < bh; y++) {
|
|
3307
|
+
const srcOff = ((y0 + y) * rotated.width + x0) * ch;
|
|
3308
|
+
out.set(rotated.data.subarray(srcOff, srcOff + rowBytes), y * rowBytes);
|
|
3309
|
+
}
|
|
3310
|
+
return {
|
|
3311
|
+
data: out,
|
|
3312
|
+
width: bw,
|
|
3313
|
+
height: bh,
|
|
3314
|
+
channels: ch
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3264
3317
|
/**
|
|
3265
3318
|
* Rectify an oblique plate crop: decode the JPEG, estimate the baseline skew,
|
|
3266
3319
|
* and — only when it exceeds {@link MIN_RECTIFY_ANGLE_DEG} — rotate the
|
|
@@ -3288,19 +3341,20 @@ async function rectifyPlateJpeg(jpeg, opts = {}) {
|
|
|
3288
3341
|
let mean = 0;
|
|
3289
3342
|
for (let i = 0; i < gray.data.length; i++) mean += gray.data[i];
|
|
3290
3343
|
const fill = gray.data.length > 0 ? mean / gray.data.length | 0 : 0;
|
|
3291
|
-
const
|
|
3344
|
+
const angleRad = angleDeg * DEG2RAD;
|
|
3345
|
+
const band = cropLevelledBand(rotateRaw(rgb, angleRad, {
|
|
3292
3346
|
expand: true,
|
|
3293
3347
|
fill
|
|
3294
|
-
});
|
|
3348
|
+
}), rgb.width, rgb.height, angleRad);
|
|
3295
3349
|
try {
|
|
3296
3350
|
return {
|
|
3297
|
-
jpeg: await (0, sharp.default)(Buffer.from(
|
|
3298
|
-
width:
|
|
3299
|
-
height:
|
|
3300
|
-
channels:
|
|
3351
|
+
jpeg: await (0, sharp.default)(Buffer.from(band.data), { raw: {
|
|
3352
|
+
width: band.width,
|
|
3353
|
+
height: band.height,
|
|
3354
|
+
channels: toSharpChannels(band.channels)
|
|
3301
3355
|
} }).jpeg({ quality: opts.quality ?? 90 }).toBuffer(),
|
|
3302
|
-
width:
|
|
3303
|
-
height:
|
|
3356
|
+
width: band.width,
|
|
3357
|
+
height: band.height,
|
|
3304
3358
|
angleDeg
|
|
3305
3359
|
};
|
|
3306
3360
|
} catch {
|
|
@@ -3309,6 +3363,12 @@ async function rectifyPlateJpeg(jpeg, opts = {}) {
|
|
|
3309
3363
|
}
|
|
3310
3364
|
//#endregion
|
|
3311
3365
|
//#region src/detection-pipeline/pipeline/result-assembler.ts
|
|
3366
|
+
/** L2 norm of a raw embedding vector. */
|
|
3367
|
+
function vectorMagnitude(v) {
|
|
3368
|
+
let sum = 0;
|
|
3369
|
+
for (const x of v) sum += x * x;
|
|
3370
|
+
return Math.sqrt(sum);
|
|
3371
|
+
}
|
|
3312
3372
|
/**
|
|
3313
3373
|
* Small deterministic id generator scoped to a single frame. Each call
|
|
3314
3374
|
* returns the next `d{n}` id so detail parentId references can be set
|
|
@@ -3402,6 +3462,7 @@ function applyChildOutput(parent, childStep, output, stepLatencyMs, ctx) {
|
|
|
3402
3462
|
case "embedding":
|
|
3403
3463
|
parent.embedding = output.embeddingNorm;
|
|
3404
3464
|
parent.embeddingModelId = childStep.modelId;
|
|
3465
|
+
parent.embeddingMagnitude = vectorMagnitude(output.embedding);
|
|
3405
3466
|
parent.ownLabels.push({
|
|
3406
3467
|
label: `embedding:${output.embedding.length}d`,
|
|
3407
3468
|
score: 1
|
|
@@ -3624,20 +3685,50 @@ var MIN_FACE_DETECTION_FLOOR_PX = 6;
|
|
|
3624
3685
|
*/
|
|
3625
3686
|
var NATIVE_FACE_CROP_MAX_WIDTH = 320;
|
|
3626
3687
|
/**
|
|
3627
|
-
* Minimum
|
|
3628
|
-
*
|
|
3629
|
-
*
|
|
3630
|
-
*
|
|
3631
|
-
*
|
|
3632
|
-
*
|
|
3633
|
-
*
|
|
3634
|
-
*
|
|
3688
|
+
* Minimum LONG side (px) of the ACTUAL crop fed to the plate-OCR (`ctc`) leaf —
|
|
3689
|
+
* the plate analogue of the face path's min-face-size gate.
|
|
3690
|
+
*
|
|
3691
|
+
* The long side, not the short one. A plate's information lives along its text
|
|
3692
|
+
* run: the recognizer resizes the tile to a fixed 64px height and reads left to
|
|
3693
|
+
* right, so what decides whether a glyph survives is how many horizontal pixels
|
|
3694
|
+
* each character got. The previous 20px SHORT-side floor measured the wrong
|
|
3695
|
+
* axis and passed essentially everything.
|
|
3696
|
+
*
|
|
3697
|
+
* The number comes from the 2026-08 audit of 11 cam-617 plate tracks with
|
|
3698
|
+
* ground truth. Native long side vs read:
|
|
3699
|
+
*
|
|
3700
|
+
* 117px → "DNRO3O9" (truth DNRO309 — one glyph out, the only near-miss)
|
|
3701
|
+
* 82px → "VONND9592" (truth DNDS597)
|
|
3702
|
+
* 77px → "DEPNOQ1" (truth DNP1091)
|
|
3703
|
+
* 76px → "VDHPXOE8" (truth DNPK838)
|
|
3704
|
+
* 73px → "DAEOR2D8" (truth DNDR298)
|
|
3705
|
+
* ≤62px → "I0IAZ", "EUSM9", "0IA3", "DYAGI2", "I0AA", "IC1E"
|
|
3706
|
+
*
|
|
3707
|
+
* Not one read under 100px resembled its plate, and the gap between 82 and 117
|
|
3708
|
+
* is empty — 100 sits in it. Below this floor the OCR call is pure cost: it
|
|
3709
|
+
* produces a label that is wrong, and a wrong plate label is worse than none.
|
|
3710
|
+
*
|
|
3711
|
+
* Measured on the NATIVE crop when the native ROI fetch succeeded (its
|
|
3712
|
+
* dimensions ARE the plate's real native size — `nativeChildCrop`'s 1920 cap
|
|
3713
|
+
* never binds for a plate), else on the fallback detection-space tile — which
|
|
3714
|
+
* IS the input OCR would read, so the same floor applies honestly in both cases.
|
|
3635
3715
|
*/
|
|
3636
|
-
var
|
|
3716
|
+
var MIN_PLATE_OCR_LONG_SIDE_PX = 100;
|
|
3637
3717
|
function isEnrichmentOutput(output) {
|
|
3638
3718
|
return output.kind === "classifications" || output.kind === "embedding" || output.kind === "text";
|
|
3639
3719
|
}
|
|
3640
3720
|
/**
|
|
3721
|
+
* Read a numeric step setting, falling back to `fallback` when the key is
|
|
3722
|
+
* absent or not a finite number. A type guard rather than a cast — a step's
|
|
3723
|
+
* `settings` bag is `Record<string, unknown>` because it is operator data, and
|
|
3724
|
+
* asserting `as number` over it is how a string from a form becomes a silent
|
|
3725
|
+
* NaN comparison that admits everything.
|
|
3726
|
+
*/
|
|
3727
|
+
function numericSetting(settings, key, fallback) {
|
|
3728
|
+
const raw = settings?.[key];
|
|
3729
|
+
return typeof raw === "number" && Number.isFinite(raw) ? raw : fallback;
|
|
3730
|
+
}
|
|
3731
|
+
/**
|
|
3641
3732
|
* Stamp the camera onto an inference input so a failure inside the SHARED pool
|
|
3642
3733
|
* can name it (`InferenceInput.deviceId` — diagnostic only, it selects
|
|
3643
3734
|
* nothing). Immutable, and a no-op for a caller with no real camera: the
|
|
@@ -4078,12 +4169,27 @@ var PipelineExecutor = class {
|
|
|
4078
4169
|
let cropW;
|
|
4079
4170
|
let cropH;
|
|
4080
4171
|
if (modelEntry?.faceAlignment) {
|
|
4081
|
-
const minFaceSize =
|
|
4172
|
+
const minFaceSize = numericSetting(child.settings, "minFaceSize", DEFAULT_MIN_FACE_SIZE_PX);
|
|
4082
4173
|
const detShortSide = bboxShortSide(parentDetection.bbox);
|
|
4083
4174
|
if (detShortSide < MIN_FACE_DETECTION_FLOOR_PX) {
|
|
4084
4175
|
this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0, deviceId);
|
|
4085
4176
|
continue;
|
|
4086
4177
|
}
|
|
4178
|
+
const precision = require_event_loop_stall_monitor.landmarkPrecisionVerdict({
|
|
4179
|
+
detectionShortSidePx: detShortSide,
|
|
4180
|
+
floorPx: numericSetting(child.settings, "minLandmarkFaceSize", 24)
|
|
4181
|
+
});
|
|
4182
|
+
if (precision !== null) {
|
|
4183
|
+
this.throttledWarn(`face-landmark-floor:${child.stepId}:${deviceId}`, 3e4, "face detected but its landmarks are below the precision floor — no embedding", {
|
|
4184
|
+
step: child.stepId,
|
|
4185
|
+
model: child.modelId,
|
|
4186
|
+
detectionShortSidePx: Math.round(precision.detectionShortSidePx),
|
|
4187
|
+
minLandmarkFaceSizePx: precision.floorPx,
|
|
4188
|
+
tileW: imageWidth,
|
|
4189
|
+
tileH: imageHeight
|
|
4190
|
+
}, deviceId);
|
|
4191
|
+
continue;
|
|
4192
|
+
}
|
|
4087
4193
|
const lms = parentDetection.landmarks;
|
|
4088
4194
|
if (lms && lms.length >= 5) {
|
|
4089
4195
|
if (!nativeCropProvider && detShortSide < minFaceSize) {
|
|
@@ -4147,15 +4253,16 @@ var PipelineExecutor = class {
|
|
|
4147
4253
|
cropH = crop.height;
|
|
4148
4254
|
}
|
|
4149
4255
|
if (child.definition.postprocessor === "ctc") {
|
|
4150
|
-
const
|
|
4151
|
-
if (
|
|
4152
|
-
this.opts.logger?.
|
|
4256
|
+
const plateLongSidePx = Math.max(cropW, cropH);
|
|
4257
|
+
if (plateLongSidePx < MIN_PLATE_OCR_LONG_SIDE_PX) {
|
|
4258
|
+
this.opts.logger?.info("plate ROI too small for OCR — skipping", {
|
|
4153
4259
|
tags: { deviceId },
|
|
4154
4260
|
meta: {
|
|
4155
4261
|
step: child.stepId,
|
|
4156
4262
|
model: child.modelId,
|
|
4157
|
-
|
|
4158
|
-
|
|
4263
|
+
plateLongSidePx: Math.round(plateLongSidePx),
|
|
4264
|
+
plateShortSidePx: Math.round(Math.min(cropW, cropH)),
|
|
4265
|
+
minPlateLongSidePx: MIN_PLATE_OCR_LONG_SIDE_PX,
|
|
4159
4266
|
source: nativeChild ? "native" : "detection-tile"
|
|
4160
4267
|
}
|
|
4161
4268
|
});
|
|
@@ -4168,12 +4275,15 @@ var PipelineExecutor = class {
|
|
|
4168
4275
|
cropJpegBuf = rectified.jpeg;
|
|
4169
4276
|
cropW = rectified.width;
|
|
4170
4277
|
cropH = rectified.height;
|
|
4171
|
-
this.opts.logger?.
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4278
|
+
this.opts.logger?.info("plate crop deskewed before OCR", {
|
|
4279
|
+
tags: { deviceId },
|
|
4280
|
+
meta: {
|
|
4281
|
+
step: child.stepId,
|
|
4282
|
+
angleDeg: Number(rectified.angleDeg.toFixed(2)),
|
|
4283
|
+
width: cropW,
|
|
4284
|
+
height: cropH
|
|
4285
|
+
}
|
|
4286
|
+
});
|
|
4177
4287
|
}
|
|
4178
4288
|
} catch {}
|
|
4179
4289
|
}
|
|
@@ -4183,6 +4293,17 @@ var PipelineExecutor = class {
|
|
|
4183
4293
|
}, deviceId), cropW, cropH, "crop-roi", parentDetection.bbox, parentDetection.macroClass, traceBuilder, stepTimings, poolAgg);
|
|
4184
4294
|
const childMs = Date.now() - childStart;
|
|
4185
4295
|
if (childOutput === null) continue;
|
|
4296
|
+
if (child.definition.postprocessor === "ctc" && childOutput.kind === "text") this.opts.logger?.info("plate OCR read", {
|
|
4297
|
+
tags: { deviceId },
|
|
4298
|
+
meta: {
|
|
4299
|
+
step: child.stepId,
|
|
4300
|
+
model: child.modelId,
|
|
4301
|
+
text: childOutput.text,
|
|
4302
|
+
confidence: Number(childOutput.confidence.toFixed(3)),
|
|
4303
|
+
formatValid: childOutput.formatValid ?? null,
|
|
4304
|
+
tilePx: `${cropW}x${cropH}`
|
|
4305
|
+
}
|
|
4306
|
+
});
|
|
4186
4307
|
const detailsBefore = ctx.details.length;
|
|
4187
4308
|
applyChildOutput(parentDetection, child, childOutput, childMs, ctx);
|
|
4188
4309
|
if (childOutput.kind === "detections" && child.children.length > 0) {
|