How to limit codeScanner to View height and width❓
Question
Let's say the Camera has 200 width and 200 height and the device height and width is 600 and 400.
Currently, it's scanning QRs out of view, I tried to implement a boundary check with onLayout and a few more tricks but it varies so much between devices (iOS and Android, orientation...) that it works on most but it doesn't on a few that I still need to support.
Any tips on doing this in the JS side in a more elegant way without using resizeMode='contain'?
What I tried
No response
VisionCamera Version
3.6.4
Additional information
- [ ] I am using Expo
- [X] I have read the Troubleshooting Guide
- [X] I agree to follow this project's Code of Conduct
- [X] I searched for similar questions in the issues page as well as in the discussions page and found none.
did you find the solution?
did you find the solution?
Using resizeMode for now.
https://react-native-vision-camera.com/docs/api/interfaces/CodeScanner#regionofinterest
https://react-native-vision-camera.com/docs/api/interfaces/CodeScanner#regionofinterest
Doesn't work
What exactly is not working about it?
What exactly is not working about it?
I was running into the same issues as before (mentioned in the first comment) when I was using onLayout to find out the frame size and ignore any scans outside of the view. How are you using it to only scan QRs that appear on the resized view of a Camera component?
Perhaps see here: https://github.com/mrousavy/react-native-vision-camera/issues/2014#issuecomment-1769294150
@spsaucier I have checked your soultion, those corner points calculated are still relative to camera view not to phone screen right, since values are too big to be screen relative? Do you have idea or solution how to convert those coordinates to screen relative values, I'm having hard time figuring that out?
@SocDario The solution I posted works 90% of the time, but there are a couple of iPhone models which do not properly report the resolution, so I have stopped using it. There is a solution in newer versions of vision camera though.
@spsaucier Yeah they recently added corners property on scan result, but also as far as I know camera view relative coordinates and not well documented about implementation
You're right that it's not documented well. It does seem to be working everywhere, though. I'll post my new version tomorrow.
@spsaucier Thanks mate, you are life saver!
const actOnBestFitCode = useCallback(
(data: Code[], frame: CodeScannerFrame) => {
if (!frame.width || !frame.height) return;
const frameLongSide = Math.max(frame.width, frame.height);
const frameShortSide = Math.min(frame.width, frame.height);
const foundCodes = data.filter(code => {
if (code.value) {
const xValues = code.corners?.map(p => p.x) || [];
const yValues = code.corners?.map(p => p.y) || [];
const minX = Math.min(...xValues);
const maxX = Math.max(...xValues);
const minY = Math.min(...yValues);
const maxY = Math.max(...yValues);
const isLandscape =
(device?.sensorOrientation || '').indexOf('landscape') > -1;
const minShortSide = isLandscape ? minY : minX;
const maxShortSide = isLandscape ? maxY : maxX;
const minLongSide = isLandscape ? minX : minY;
const maxLongSide = isLandscape ? maxX : maxY;
// Ensure code is within the 'scanning area' square in the center
const shortSidePad = frameShortSide / 18;
const longSidePad = frameLongSide / 3.5;
if (
minShortSide > shortSidePad &&
maxShortSide < frameShortSide - shortSidePad &&
minLongSide > longSidePad &&
maxLongSide < frameLongSide - longSidePad
) {
return true;
}
}
return false;
});
if (!foundCodes.length) {
return;
}
const fitScores = foundCodes.map(code => {
if (!code.frame) return 5000; // This is already filtered out above
const xValues = code.corners?.map(p => p.x) || [];
const yValues = code.corners?.map(p => p.y) || [];
const minX = Math.min(...xValues);
const maxX = Math.max(...xValues);
const minY = Math.min(...yValues);
const maxY = Math.max(...yValues);
const isLandscape =
(device?.sensorOrientation || '').indexOf('landscape') > -1;
const minShortSide = isLandscape ? minY : minX;
const maxShortSide = isLandscape ? maxY : maxX;
const minLongSide = isLandscape ? minX : minY;
const maxLongSide = isLandscape ? maxX : maxY;
const actualXCenter = (minShortSide + maxShortSide) / 2;
const idealXCenter = frameShortSide / 2;
const distanceToXCenter = Math.abs(idealXCenter - actualXCenter);
const actualYCenter = (minLongSide + maxLongSide) / 2;
const idealYCenter = frameLongSide / 2;
const distanceToYCenter = Math.abs(idealYCenter - actualYCenter);
const fitScore = distanceToXCenter + distanceToYCenter;
return fitScore;
});
const bestFitScore = Math.min(...fitScores);
const bestFitIndex = fitScores.indexOf(bestFitScore);
const type = foundCodes[bestFitIndex].type;
const value = cleanBarcode(foundCodes[bestFitIndex].value || '');
if (
value &&
type !== 'unknown' &&
barcodeFormats.includes(type) &&
value &&
(validateBarcode(value) || validatePickupBarcode(value)) &&
value !== currentBarcode
) {
setCurrentBarcode(value);
}
},
[barcodeFormats, currentBarcode, device]
);
@spsaucier
Thanks for example mate, I'll try it out today!
On iOS there's regionOfInterest. Otherwise the Frame has a bounding box. And yes, resizeMode also exists for the preview
This should be re-opened until regionOfInterest is within Android, closing it with that comment is unfair really.
This should be re-opened until
regionOfInterestis within Android, closing it with that comment is unfair really.
Thank you for your reply JshGrn, if you open a PR to add regionOfInterest to Android I will gladly review and merge it. Otherwise I don't plan on working on this, simply because it is not part of the native Android API and will probably require HUGE efforts to implement this (as we need to resize the Frame ourselves manually, causing additional performance overhead).