Enhance OCR functionality by improving number extraction logic to handle various formats and ensuring better accuracy. Introduce a new method for preprocessing the OCR canvas to enhance contrast for better recognition. Update video frame capture to consider the visible rectangle for improved cropping. Increment version code to 4.72.796 to reflect these changes.
This commit is contained in:
@ -492,10 +492,12 @@
|
||||
return Tesseract.createWorker('eng');
|
||||
}).then(function (worker) {
|
||||
globalOcrWorker = worker;
|
||||
// Chiffres seulement. Pas de SINGLE_LINE : trop fragile sur manuscrit espace.
|
||||
return worker.setParameters({
|
||||
tessedit_char_whitelist: '0123456789'
|
||||
}).then(function () {
|
||||
// Chiffres seulement + bloc (dossard type "1020", pas une ligne unique fragile).
|
||||
var params = {tessedit_char_whitelist: '0123456789'};
|
||||
if (window.Tesseract && Tesseract.PSM && Tesseract.PSM.SINGLE_BLOCK) {
|
||||
params.tessedit_pageseg_mode = Tesseract.PSM.SINGLE_BLOCK;
|
||||
}
|
||||
return worker.setParameters(params).then(function () {
|
||||
return globalOcrWorker;
|
||||
});
|
||||
}).catch(function (err) {
|
||||
@ -511,38 +513,129 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Un numero = tous les chiffres lus dans le cadre (5+2+1 → 521).
|
||||
// MSIN-4401 — Extraire UN numero de dossard :
|
||||
// - manuscrit "5 2 1" → coller les chiffres isoles → 521
|
||||
// - dossard "1 KM" + "1020" → garder le plus long groupe → 1020 (pas 11020)
|
||||
function extractBibNumber(raw) {
|
||||
var digits = String(raw || '').replace(/\D/g, '');
|
||||
if (digits.length < 1 || digits.length > BIB_MAX_LEN) {
|
||||
var matches = String(raw || '').match(/\d+/g);
|
||||
if (!matches || !matches.length) {
|
||||
return '';
|
||||
}
|
||||
return digits;
|
||||
var i;
|
||||
var allSingles = true;
|
||||
for (i = 0; i < matches.length; i++) {
|
||||
if (matches[i].length !== 1) {
|
||||
allSingles = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allSingles) {
|
||||
var joined = matches.join('');
|
||||
return (joined.length >= 1 && joined.length <= BIB_MAX_LEN) ? joined : '';
|
||||
}
|
||||
var best = '';
|
||||
for (i = 0; i < matches.length; i++) {
|
||||
if (matches[i].length <= BIB_MAX_LEN && matches[i].length > best.length) {
|
||||
best = matches[i];
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
// Crop central du flux (region du cadre blanc).
|
||||
function captureCenterFrame(videoEl) {
|
||||
// Zone video visible avec object-fit:cover (alignee sur le carre a l'ecran).
|
||||
function getCoverVisibleRect(videoEl, displayEl) {
|
||||
var vw = videoEl.videoWidth;
|
||||
var vh = videoEl.videoHeight;
|
||||
var dw = displayEl.clientWidth || 1;
|
||||
var dh = displayEl.clientHeight || 1;
|
||||
var videoRatio = vw / vh;
|
||||
var displayRatio = dw / dh;
|
||||
var renderedW;
|
||||
var renderedH;
|
||||
var offsetX;
|
||||
var offsetY;
|
||||
if (videoRatio > displayRatio) {
|
||||
renderedH = vh;
|
||||
renderedW = vh * displayRatio;
|
||||
offsetX = (vw - renderedW) / 2;
|
||||
offsetY = 0;
|
||||
} else {
|
||||
renderedW = vw;
|
||||
renderedH = vw / displayRatio;
|
||||
offsetX = 0;
|
||||
offsetY = (vh - renderedH) / 2;
|
||||
}
|
||||
return {
|
||||
offsetX: offsetX,
|
||||
offsetY: offsetY,
|
||||
renderedW: renderedW,
|
||||
renderedH: renderedH
|
||||
};
|
||||
}
|
||||
|
||||
// Renforce le contraste pour l'OCR (dossard blanc sur fond photo).
|
||||
function preprocessOcrCanvas(srcCanvas) {
|
||||
var w = srcCanvas.width;
|
||||
var h = srcCanvas.height;
|
||||
var scale = (w < 280) ? 2 : 1;
|
||||
var out = document.createElement('canvas');
|
||||
out.width = Math.max(1, Math.round(w * scale));
|
||||
out.height = Math.max(1, Math.round(h * scale));
|
||||
var ctx = out.getContext('2d');
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.drawImage(srcCanvas, 0, 0, out.width, out.height);
|
||||
var img = ctx.getImageData(0, 0, out.width, out.height);
|
||||
var d = img.data;
|
||||
var j;
|
||||
for (j = 0; j < d.length; j += 4) {
|
||||
var gray = (0.299 * d[j] + 0.587 * d[j + 1] + 0.114 * d[j + 2]);
|
||||
// Contraste fort : pousse vers noir/blanc (chiffres blancs sur fond sombre).
|
||||
gray = (gray - 128) * 1.6 + 128;
|
||||
if (gray < 0) { gray = 0; }
|
||||
if (gray > 255) { gray = 255; }
|
||||
d[j] = d[j + 1] = d[j + 2] = gray;
|
||||
}
|
||||
ctx.putImageData(img, 0, 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Crop = cadre blanc (~55% x 30%) sur la zone VISIBLE (cover), pas le flux brut.
|
||||
function captureCenterFrame(videoEl, displayEl) {
|
||||
if (!videoEl || !videoEl.videoWidth) {
|
||||
return null;
|
||||
}
|
||||
var w = videoEl.videoWidth;
|
||||
var h = videoEl.videoHeight;
|
||||
var cropW = Math.round(w * 0.55);
|
||||
var cropH = Math.round(h * 0.3);
|
||||
var sx = Math.round((w - cropW) / 2);
|
||||
var sy = Math.round((h - cropH) / 2);
|
||||
var cropRatioW = 0.55;
|
||||
var cropRatioH = 0.3;
|
||||
var sx;
|
||||
var sy;
|
||||
var cropW;
|
||||
var cropH;
|
||||
if (displayEl && displayEl.clientWidth && displayEl.clientHeight) {
|
||||
var vis = getCoverVisibleRect(videoEl, displayEl);
|
||||
cropW = Math.max(1, Math.round(vis.renderedW * cropRatioW));
|
||||
cropH = Math.max(1, Math.round(vis.renderedH * cropRatioH));
|
||||
sx = Math.round(vis.offsetX + (vis.renderedW - cropW) / 2);
|
||||
sy = Math.round(vis.offsetY + (vis.renderedH - cropH) / 2);
|
||||
} else {
|
||||
var w = videoEl.videoWidth;
|
||||
var h = videoEl.videoHeight;
|
||||
cropW = Math.round(w * cropRatioW);
|
||||
cropH = Math.round(h * cropRatioH);
|
||||
sx = Math.round((w - cropW) / 2);
|
||||
sy = Math.round((h - cropH) / 2);
|
||||
}
|
||||
var canvas = document.createElement('canvas');
|
||||
var outW = cropW;
|
||||
var outH = cropH;
|
||||
if (cropW > OCR_MAX_WIDTH) {
|
||||
var scale = OCR_MAX_WIDTH / cropW;
|
||||
var scaleDown = OCR_MAX_WIDTH / cropW;
|
||||
outW = OCR_MAX_WIDTH;
|
||||
outH = Math.max(1, Math.round(cropH * scale));
|
||||
outH = Math.max(1, Math.round(cropH * scaleDown));
|
||||
}
|
||||
canvas.width = outW;
|
||||
canvas.height = outH;
|
||||
canvas.getContext('2d').drawImage(videoEl, sx, sy, cropW, cropH, 0, 0, outW, outH);
|
||||
return canvas;
|
||||
return preprocessOcrCanvas(canvas);
|
||||
}
|
||||
|
||||
function BibScanSession(block) {
|
||||
@ -720,7 +813,7 @@
|
||||
if (self.ocrBusy || !self.video || activeScan !== self || !self.workerReady) {
|
||||
return;
|
||||
}
|
||||
var canvas = captureCenterFrame(self.video);
|
||||
var canvas = captureCenterFrame(self.video, self.captureEl);
|
||||
if (!canvas) {
|
||||
self.setStatus(scanMsg(self.panel, 'data-msg-camera', 'Impossible d\'accéder à la caméra.', 'Unable to access the camera.'), false);
|
||||
return;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Constantes *
|
||||
*
|
||||
**************/
|
||||
define('_VERSION_CODE', '4.72.795');
|
||||
define('_VERSION_CODE', '4.72.796');
|
||||
define('_DATE_CODE', '2026-07-13');
|
||||
//MSIN-4290
|
||||
define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');
|
||||
|
||||
Reference in New Issue
Block a user