BoxFromPatches sometimes returns a larger box than necessary
In boxFromPatches, all of the boxes are rotated by the average radian of the boxes. The bounding box is then determined by finding the minimum and maximum X and Y values containing all rotated boxes. The currently code starts the minimum and maximum values using the image size, which would normally be conservative values that would always be improved on. However, the rotation of the boxes can place some points of them outside those minimum and maximum values. It is better to start the minimum and maximum values using an actual rotated point.
In practice, this is a rare event.
In barcode_locator.js in method boxFromPatches at line 112:
let minx = _binaryImageWrapper.size.x;
let miny = _binaryImageWrapper.size.y;
let maxx = -_binaryImageWrapper.size.x;
let maxy = -_binaryImageWrapper.size.y;
... (omitted code) ...
// find bounding box
for (i = 0; i < patches.length; i++) {
patch = patches[i];
for (j = 0; j < 4; j++) {
if (patch.box[j][0] < minx) {
minx = patch.box[j][0];
}
if (patch.box[j][0] > maxx) {
maxx = patch.box[j][0];
}
if (patch.box[j][1] < miny) {
miny = patch.box[j][1];
}
if (patch.box[j][1] > maxy) {
maxy = patch.box[j][1];
}
}
}
could be changed to:
let minx;
let miny;
let maxx;
let maxy;
... (omitted code unchanged) ...
// find bounding box
// Start min and maximum values to the first patches' upper-left coordinate, and improve them if find better values.
minx = maxx = patches[0].box[0][0];
miny = maxy = patches[0].box[0][1];
for (i = 0; i < patches.length; i++) {
... (omitted code unchanged) ...
An example makes this more clear:
_binaryImageWrapper.size.x = 480
_binaryImageWrapper.size.y = 240
degrees used to build transformational array = 92.28
radians used to build transformational array = 1.61
Patch before rotation
Top Left (x,y): 360,192
Top Right (x,y): 384,192
Bottom Left (x,y): 360,216
Bottom Right (x,y): 384,216
Patch after rotation (approximately 90 degrees)
Top Left (x,y): -206,352
Top Right (x,y): -207,376
Bottom Left (x,y): -230,351
Bottom Right (x,y): -231,375
patches[0].box[0][0] before rotation 360
after rotation -206
patches[0].box[0][1] before rotation 192
after rotation 352
The original code determines miny as 240, which does not get improved as it checks all rotated points. However, the true minimum is 351. Starting with the first coordinate's Y at 352 with the new code will result in a correct minimum Y of 351.
Although the containing box is constructed with coordinates that appear out of range per the original image, the box is then reverse rotated and rescaled.
Other notes about revised code:
- We can rely upon patches[0] existing as we know patches.length is > 0 at this point (or else a division by zero would occur when calculating overAvg)
- We can use any of the rotated points for the starting x and y coordinates, as we are iterating through all of them to find the min and max. We choose the top-left as it as a good as any of the points. This also applies to using an actual point for the starting maxx and maxy instead of -size.x and -size.y.
OK, so, if I'm reading this correctly, you're saying the current code initializes MinY at the entire image's Y size, but as your patches rotate around, the Y doesn't actually change, because in that example, 240 was too small to begin with?
So, wouldn't the best answer be to initialize both Min at Infinity, and both Max at negative Infinity?
i come up with after a little bit of refactoring
let minx = Infinity;
let maxx = -Infinity;
let miny = Infinity;
let maxy = -Infinity;
// find bounding box
patches.forEach((patch) => {
for (j = 0; j < 4; j++) {
minx = Math.min(minx, patch.box[j][0]);
maxx = Math.max(maxx, patch.box[j][0]);
miny = Math.min(miny, patch.box[j][1]);
maxy = Math.max(maxy, patch.box[j][1]);
}
});
Yes, that is correct, initializing to +/- infinity would also work.
I am not a JavaScript developer, so I didn't know that was an option. I am curious what you would get if you were to next divide minx by maxx (infinity/-infinity = ?)
I don't think that would be a solution for the language I ported portions of the code to (C#).
division with Infinity or -Infinity returns NaN (Not a Number) .. but that shouldn't be a thing, because there should always be at least one patch with a x/y of less than Infinity.. right?
If you don't have Infinite numbers, you can use a really big number and a really big negative number :)
Anyway, yes, I was trying to make sure that I understood the crux of the problem, so I could be sure that I was solving it correctly when refactoring the code to use more modern features. :)
Thank you!