react-native-vision-camera icon indicating copy to clipboard operation
react-native-vision-camera copied to clipboard

🐛 Can't position square representing detected QR code frame properly

Open antoinerousseau opened this issue 2 years ago • 1 comments

What's happening?

On my test phone (an iPhone XR), I managed to position it quite well:

but on my colleague's phone (iPhone 14 pro) it's quite off:

Reproduceable Code

// the relevant code of onCodeScanned:
if (code.frame) {
  // TODO: handle device.sensorOrientation (here it's assumed "landscape")

  const top = code.frame.x / WINDOW.scale - cameraViewSizes.y;
  const height = Math.max(100, code.frame.height / WINDOW.scale);

  const right = code.frame.y / WINDOW.scale;
  const width = Math.max(100, code.frame.width / WINDOW.scale);

  squareBounds.value = {
    right: right - (width / cameraViewSizes.width) * right,
    width,
    top: top - (height / cameraViewSizes.height) * top,
    height,
  };
}

// the relevant JSX:
<TouchableWithoutFeedback
  onPress={onPressCamera}
  onPressIn={onHoldCamera}
  onPressOut={onReleaseCamera}
>
  <View style={[styles.main, style]} onLayout={onCameraWrapperLayout}>
    <VisionCamera
      ref={cameraRef}
      torch={flashMode}
      style={styles.scanner}
      device={device}
      isActive={!disableCamera}
      codeScanner={codeScanner}
      enableZoomGesture
    />

    <Reanimated.View style={animatedStyle}>
      <Text style={styles.holdText}>Hold to scan</Text>
    </Reanimated.View>

    // ... & some more absolute views here for controls
  </View>
</TouchableWithoutFeedback>

// the relevant styles:
const styles = StyleSheet.create({
  main: {
    position: "relative",
    backgroundColor: "#050505",
  },
  scanner: {
    flex: 1,
    backgroundColor: "#050505",
  },
});

Relevant log output

frame {"height": 190.0366759300232, "width": 181.95819854736328, "x": 1022.731819152832, "y": 289.8436903953552}
squareBounds {"height": 100, "right": 110.41120430697566, "top": 368.3117007502803, "width": 100}

Camera Device

for my iPhone XR:

{
  "hardwareLevel": "full",
  "isMultiCam": false,
  "supportsLowLightBoost": false,
  "maxExposure": 8,
  "sensorOrientation": "landscape-right",
  "neutralZoom": 1,
  "supportsFocus": true,
  "supportsRawCapture": false,
  "physicalDevices": [
    "wide-angle-camera"
  ],
  "minExposure": -8,
  "name": "Back Camera",
  "hasFlash": true,
  "id": "com.apple.avfoundation.avcapturedevice.built-in_video:0",
  "maxZoom": 16,
  "minZoom": 1,
  "position": "back",
  "hasTorch": true
}

Device

my colleague's: iPhone 14 Pro

VisionCamera Version

3.6.12

Can you reproduce this issue in the VisionCamera Example app?

I didn't try (⚠️ your issue might get ignored & closed if you don't try this)

Additional information

antoinerousseau avatar Dec 01 '23 11:12 antoinerousseau

any update ?

nobodyzzz01 avatar Jan 03 '24 03:01 nobodyzzz01

Hey @antoinerousseau Any update? Please, could you share your code?

DevUser0491 avatar Jan 12 '24 16:01 DevUser0491

Hey - the CodeScanner now gives you a frame/bounding box as well, hope that helps.

mrousavy avatar Jan 15 '24 13:01 mrousavy

@mrousavy do you mean #2117? If so, it was released in v3.6.5 and this issue is with v3.6.12, and the second params just gives the width/height of the scanning area

antoinerousseau avatar Jan 15 '24 13:01 antoinerousseau

Yes exactly - using that you can figure out where exactly the Frame was scanned in, then convert that to screen coordinates yourself - no?

mrousavy avatar Jan 15 '24 13:01 mrousavy

@antoinerousseau any fix? I'm facing same issue. Looks like frame data is wrong

dann1609 avatar Jun 07 '24 21:06 dann1609

I updated from 4.0.4 to 4.3.2 today and run into the same issue. Prior to the version update I had to recalculate the coordinates, but they were correct. My code in 4.0.4 (with some minor changes):

function onCodeScanned(codes: Code[], frame: CodeScannerFrame) {
    const temporaryCornerPointsSets: CornerPointsSets = [];
    codes.forEach((code) => {
        if (!code || !code.corners || !code.frame || !code.value) return;

        /*
         * Returned code coordinates are on different coordinate system than UI coordinate
         * system (frame coordinates vs device-independent pixels). Recalculate them:
         */
        // height and width reversed, since frame is (always?) landscaped
        const frameHeight = Math.max(frame.height, frame.width);
        const frameWidth = Math.min(frame.height, frame.width);
        const transformationX = dimensions.width / frameWidth;
        const transformationY = dimensions.height / frameHeight;

        // todo: rewrite with .map
        const point0X = code.corners[0].x * transformationX;
        const point0Y = code.corners[0].y * transformationY;

        const point1X = code.corners[1].x * transformationX;
        const point1Y = code.corners[1].y * transformationY;

        const point2X = code.corners[2].x * transformationX;
        const point2Y = code.corners[2].y * transformationY;

        const point3X = code.corners[3].x * transformationX;
        const point3Y = code.corners[3].y * transformationY;

        const translatedCornerPoints = [
            {x: point0X, y: point0Y},
            {x: point1X, y: point1Y},
            {x: point2X, y: point2Y},
            {x: point3X, y: point3Y},
        ];

        if (translatedCornerPoints.every(isCoordinateInFinder)) {
            temporaryCornerPointsSets.push(translatedCornerPoints);
        }
    });

    if (temporaryCornerPointsSets.length === 0) {
        setCornerPointsSets(undefined);
    } else if (temporaryCornerPointsSets.length === 1) {
        setCornerPointsSets(temporaryCornerPointsSets);
    } else {
        setCornerPointsSets(temporaryCornerPointsSets);
    }

}

Now the coordinates just don't make sense anymore. Has anything changed?

daankennes avatar Jun 24 '24 12:06 daankennes

My problem appears to be the default resizeMode (which is cover). Because of this, codes are also scanned outside the preview which caused the incorrect mapping. I now use resizeMode contain and that seems to fix it. I don't understand how it worked before.

daankennes avatar Jun 24 '24 15:06 daankennes