Merge branch 'feature/improving-detection' into develop
This commit is contained in:
@@ -117,7 +117,7 @@ const file2canvas = async function (imageFile: File): Promise<HTMLCanvasElement>
|
|||||||
canvas.height = img.height;
|
canvas.height = img.height;
|
||||||
|
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
reject("Context is not set")
|
reject(new Error("Context is not set"))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +232,7 @@ const canvasToImageData = (canvas: HTMLCanvasElement): ImageData => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches the given canvas for all PDF417 codes and decodes them.
|
* Searches the given canvas for all PDF417 codes and decodes them.
|
||||||
|
* Uses a slicing strategy to improve detection when multiple barcodes are present.
|
||||||
* @param {HTMLCanvasElement} canvas - the canvas to search for PDF417 codes
|
* @param {HTMLCanvasElement} canvas - the canvas to search for PDF417 codes
|
||||||
* @return {Promise<Array<DecodeResult> | null>} - an array of decoded results
|
* @return {Promise<Array<DecodeResult> | null>} - an array of decoded results
|
||||||
* */
|
* */
|
||||||
@@ -240,28 +241,101 @@ const decodeFromCanvas = async (canvas: HTMLCanvasElement): Promise<Array<Decode
|
|||||||
const readerOptions: ReaderOptions = {
|
const readerOptions: ReaderOptions = {
|
||||||
tryHarder: true,
|
tryHarder: true,
|
||||||
formats: ['PDF417'],
|
formats: ['PDF417'],
|
||||||
maxNumberOfSymbols: 10,
|
maxNumberOfSymbols: 1, // Decode one barcode per slice
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const width = canvas.width;
|
||||||
|
const height = canvas.height;
|
||||||
|
|
||||||
|
// Canvas can contain multiple PDF417 codes, so we need to try to find them all
|
||||||
|
// We will try splitting the canvas into different numbers of horizontal subsections
|
||||||
|
// and decode each subsection separately. The best result will be the one with the most codes found.
|
||||||
|
const splits = [5, 4, 3, 2, 1, 0];
|
||||||
|
|
||||||
|
// Pre-allocate canvas pool (max 6 canvases needed for split=5)
|
||||||
|
const canvasPool = Array.from({ length: 6 }, () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) {
|
||||||
|
throw new Error('Failed to get canvas context');
|
||||||
|
}
|
||||||
|
return { canvas, ctx };
|
||||||
|
});
|
||||||
|
|
||||||
|
let bestResult: Array<DecodeResult> | null = null;
|
||||||
|
|
||||||
|
for (let splitIx = 0; splitIx < splits.length; splitIx++) {
|
||||||
|
const split = splits[splitIx];
|
||||||
|
const sectionsNeeded = split + 1;
|
||||||
|
|
||||||
|
// Add overlap to ensure we don't miss codes at section boundaries
|
||||||
|
const overlap = split === 0 ? 0 : Math.round(height / 50); // 2% overlap
|
||||||
|
const sectionHeight = split === 0 ? height : (Math.floor(Math.floor(height / split) + overlap));
|
||||||
|
|
||||||
|
// Prepare canvases from pool
|
||||||
|
for (let i = 0; i < canvasPool.length; i++) {
|
||||||
|
const { canvas: sectionCanvas, ctx: sectionContext } = canvasPool[i];
|
||||||
|
|
||||||
|
if (i < sectionsNeeded) {
|
||||||
|
// Resize and use this canvas
|
||||||
|
sectionCanvas.width = width;
|
||||||
|
sectionCanvas.height = sectionHeight;
|
||||||
|
|
||||||
|
// Calculate the starting Y position for each section
|
||||||
|
const startY = i === 0 ? 0 : i * sectionHeight - overlap;
|
||||||
|
|
||||||
|
// Draw the section of the original canvas onto this section canvas
|
||||||
|
sectionContext.drawImage(canvas, 0, startY, width, sectionHeight, 0, 0, width, sectionHeight);
|
||||||
|
} else {
|
||||||
|
// Free unused canvases for this strategy
|
||||||
|
sectionCanvas.width = 0;
|
||||||
|
sectionCanvas.height = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const codesFoundInSection: Array<DecodeResult> = [];
|
||||||
|
|
||||||
|
// Try to decode each section (only the ones we're using)
|
||||||
|
for (let i = 0; i < sectionsNeeded; i++) {
|
||||||
|
const { canvas: sectionCanvas } = canvasPool[i];
|
||||||
|
|
||||||
|
try {
|
||||||
// give browser a chance to re-paint
|
// give browser a chance to re-paint
|
||||||
// this is needed to avoid UI freezing when decoding large images
|
// this is needed to avoid UI freezing when decoding large images
|
||||||
await yieldToBrowser('decodeFromCanvas');
|
await yieldToBrowser('decodeFromCanvas');
|
||||||
|
|
||||||
const imageData = canvasToImageData(canvas);
|
const imageData = canvasToImageData(sectionCanvas);
|
||||||
const results = await readBarcodes(imageData, readerOptions);
|
const results = await readBarcodes(imageData, readerOptions);
|
||||||
|
|
||||||
const codesFound: Array<DecodeResult> = results
|
if (results.length > 0 && results[0].text) {
|
||||||
.filter(result => result.text)
|
const hub3aText = results[0].text;
|
||||||
.map((result) => ({
|
codesFoundInSection.push({
|
||||||
hub3aText: result.text,
|
hub3aText,
|
||||||
billInfo: parseHubText(result.text),
|
billInfo: parseHubText(hub3aText),
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (codesFound);
|
} catch (error) {
|
||||||
|
// If no code was found in the current section, continue to next section
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await yieldToBrowser('after decodeFromCanvas');
|
||||||
|
|
||||||
|
// If in this iteration we found fewer or equal codes than in the previous best result,
|
||||||
|
// we can return the best result. This is an optimization.
|
||||||
|
if (bestResult && codesFoundInSection.length <= bestResult.length) {
|
||||||
|
return bestResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
bestResult = codesFoundInSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestResult;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return (null);
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user