mgcrea / vision-camera-barcode-scanner

High performance barcode scanner for React Native using VisionCamera
MIT License
87 stars 11 forks source link

Is it possible to limit scan area? #2

Closed karel-suchomel-ed closed 1 year ago

karel-suchomel-ed commented 1 year ago

Hello, I tried the example app and the QR codes are being scanned even if they are outside of the scan area. I have a screen where in the middle is a 300x300 square camera window and I want to scan codes only if they are visible in this window. Still, currently, even if I set the width and height of the camera component, the codes are being scanned even when they are not visible. Do you have any implementation of this feature?

Thank you for any feedback

karel-suchomel-ed commented 1 year ago

I have managed to get it working correctly.

  1. It's important to have the camera fill the entire screen with resizeMode="contain"
  2. Then apply the overlay absolutely on top of the camera
  3. Use onLayout function on the overlay component to get the x and y coordinates relative to the screen
  4. I recreated the useBarcodeScanner hook, to be able to use inject these coordinates into the onBarcodeScanned function, because it's needed in the frameProcessor dependency list
  5. Also I needed the frame instance so I passed it as the second argument of the onBarcodeScanned function
  6. Inside I have this code:

    
    const handleBarcode = function () {
    barcodes.forEach((barcode) => {
    const isPortrait = window.height > window.width
    const heightScale = window.height / (isPortrait ? Math.max(frame.height, frame.width) : Math.min(frame.height, frame.width))
    const widthScale = window.width / (isPortrait ? Math.min(frame.height, frame.width) : Math.max(frame.height, frame.width))
    
    const boundingLeft = barcode.boundingBox.origin.x * widthScale
    const boundingRight = (barcode.boundingBox.origin.x + barcode.boundingBox.size.width) * widthScale
    const boundingTop = barcode.boundingBox.origin.y * heightScale
    const boundingBottom = (barcode.boundingBox.origin.y + barcode.boundingBox.size.height) * heightScale
    
    const overlayBottom = overlayPosition.y + Math.min(overlayHeight, 310)
    const overlayLeft = overlayPosition.x
    const overlayRight = overlayLeft + Math.min(overlayWidth, 310)
    const overlayTop = overlayPosition.y
    
    if (
      boundingTop > overlayTop &&
      boundingBottom < overlayBottom &&
      boundingLeft > overlayLeft &&
      boundingRight < overlayRight
    ) {
      console.log('scanned')
    }
    })
    }

if (!isScanned.current && barcodes.length > 0) { handleBarcode() }



Hope it helps, feel free to give feedback or leave a better solution, but for me this is enough
DevUser0491 commented 9 months ago

Hello @karel-suchomel-ed, Please, could you explain a little bit more or share an example code? What are the values: window.height, window.width, overlayHeight, overlayWidth? The function handleBarcode comes from props or inside a hook useBarcodeScanner? Like this?

const { props: cameraProps, highlights } = useBarcodeScanner({
  fps: 5,
  barcodeTypes: ['qr'],
  onBarcodeScanned: (barcodes, frame) => {
    'worklet';

    handleBarcode(barcodes);

    console.log(
      `Scanned ${barcodes.length} codes with values=${JSON.stringify(
        barcodes
      )} !`
    );
  },
});
karel-suchomel-ed commented 9 months ago

Hi @DevUser0491 , I no longer use this functionality as we switched it to manual activation by button press, so limiting the scan area was no longer needed. IMG_0830

However, in the useBarcodeScanner hook itself is now a prop called regionOfInterest, which should help you achieve that.

As for your questions:

  1. window.height and window.width are values from the React Native Dimensions API: const window = Dimensions.get('window') and overlayWidth and overlayHeight are the dimensions of the overlay area for example 200x200 square
  2. handleBarcode was defined inside of the onBarcodeScanned

You could also use react-native-hole-view, where you define coordinates of the cut out, so then you don't need to use onLayout