Closed shacharcohen1997 closed 6 months ago
https://github.com/mrousavy/react-native-vision-camera/issues/2436#issuecomment-1925577421 made it perfect but i dont know how she made it
i managed to improve the code by using
// useTransposeFromStream.ts
/** Determines the offset & scale values to translate stream coordinates to a layouts coordinate */
const getTransposeProperties = (source: Dimension, target: Dimension) => {
const scaleRatio = Math.max(
target.width / source.width,
target.height / source.height,
);
// Represents the width and height of the stream, scaled down to fit the screen
// This is without taking into account the cut off part of the stream (to fit the screen)
const uncutDimensions = {
width: source.width * scaleRatio,
height: source.height * scaleRatio,
};
// Reprensents how much of the stream is cut off to fit the screen
const cutDimensions = {
width: (uncutDimensions.width - target.width)*(scaleRatio),
height: (uncutDimensions.height - target.height)*scaleRatio,
};
const x = {
offset: cutDimensions.width/ 2 ,
scale: scaleRatio,
};
const y = {
offset: cutDimensions.height / 2,
scale: scaleRatio,
};
// console.log("scaleRatio", scaleRatio, "uncutDimensions", uncutDimensions, "cutDimensions", cutDimensions, "x", x, "y", y);
return { x, y };
};
export const transposeFromStream = (
{ x, y, width, height }: Rectangle,
source: Dimension, // replace 'any' with the actual type of 'stream'
target: Dimension
) => {
const transposeFromStreamProperties = getTransposeProperties(source, target);
return {
x:
x * transposeFromStreamProperties.x.scale -
transposeFromStreamProperties.x.offset,
y:
y * transposeFromStreamProperties.y.scale -
transposeFromStreamProperties.y.offset,
width: width * transposeFromStreamProperties.x.scale,
height: height * transposeFromStreamProperties.y.scale,
};
};
/** Given a set of coordinates in a landscape picture, returns the corresponding coordinates for a vertical plan */
export const rotateCoordinatesToVertical = (
{ x, y }: Point,
{ height }: Dimension,
) => {
return {
x: height - y,
y: x,
};
};
export const rotateRectangleToVertical = (
rectangle: Rectangle,
frame: Dimension,
) => {
// Rotating by 90° means the width and height are swapped
const width = rectangle.height;
const height = rectangle.width;
// Rotate the top left corner coordinates
const rotatedCoordinates = rotateCoordinatesToVertical(rectangle, frame);
// The top left corner coordinates now represent the top right corner of the rectangle in the new plane
// So recalculate the new top left corner coordinates
return {
x: rotatedCoordinates.x - width,
y: rotatedCoordinates.y,
width,
height,
};
};
export type Dimension = {
width: number;
height: number;
};
export type Point = {
x: number;
y: number;
};
export type Rectangle = Point & Dimension;
Hi @shacharcohen1997 I have follow your idea and have a same problem. and I have try like this .maybe this will have help you. Viewbox of my camera is '0 0 414 709' you can get is by using View Ref
And my useCodeScanner function is like this.
By the way I just make a test in Rect . you can try it in Polygon . Many thank for your idea
Here is my result for this detection
@shacharcohen1997 I have try to draw code Scanner Border only when detected code
const topLeftX = codesFrame.x;
const topLeftY = codesFrame.y;
const topRightX = codesFrame.x + codesFrame.width;
const topRightY = codesFrame.y;
const bottomRightX = codesFrame.x + codesFrame.width;
const bottomRightY = codesFrame.y + codesFrame.height;
const bottomLeftX = codesFrame.x;
const bottomLeftY = codesFrame.y + codesFrame.height;
const paths = [
{ d: M${topLeftX + codesFrame.width / 3} ${topLeftY} H${topLeftX} V${topLeftY + codesFrame.height / 3}
},
{ d: M${topRightX - codesFrame.width / 3} ${topRightY} H${topRightX} V${topRightY + codesFrame.height / 3}
},
{ d: M${bottomRightX - codesFrame.width / 3} ${bottomRightY} H${bottomRightX} V${bottomRightY - codesFrame.height / 3}
},
{ d: `M${bottomLeftX + codesFrame.width / 3} ${bottomLeftY} H${bottomLeftX} V${bottomLeftY
divide for 3 just my calculate the stroke width: The Result is:
Hey I think this can be discussed on the community discordi nstead of GitHub issues.
@shacharcohen1997 I have try to draw code Scanner Border only when detected code
const topLeftX = codesFrame.x; const topLeftY = codesFrame.y; const topRightX = codesFrame.x + codesFrame.width; const topRightY = codesFrame.y; const bottomRightX = codesFrame.x + codesFrame.width; const bottomRightY = codesFrame.y + codesFrame.height; const bottomLeftX = codesFrame.x; const bottomLeftY = codesFrame.y + codesFrame.height; const paths = [ { d:
M${topLeftX + codesFrame.width / 3} ${topLeftY} H${topLeftX} V${topLeftY + codesFrame.height / 3}
}, { d:M${topRightX - codesFrame.width / 3} ${topRightY} H${topRightX} V${topRightY + codesFrame.height / 3}
}, { d:M${bottomRightX - codesFrame.width / 3} ${bottomRightY} H${bottomRightX} V${bottomRightY - codesFrame.height / 3}
}, { d: `M${bottomLeftX + codesFrame.width / 3} ${bottomLeftY} H${bottomLeftX} V${bottomLeftYdivide for 3 just my calculate the stroke width: The Result is:
can you send the full snippet or your code?
@shacharcohen1997 I have try to draw code Scanner Border only when detected code const topLeftX = codesFrame.x; const topLeftY = codesFrame.y; const topRightX = codesFrame.x + codesFrame.width; const topRightY = codesFrame.y; const bottomRightX = codesFrame.x + codesFrame.width; const bottomRightY = codesFrame.y + codesFrame.height; const bottomLeftX = codesFrame.x; const bottomLeftY = codesFrame.y + codesFrame.height; const paths = [ { d:
M${topLeftX + codesFrame.width / 3} ${topLeftY} H${topLeftX} V${topLeftY + codesFrame.height / 3}
}, { d:M${topRightX - codesFrame.width / 3} ${topRightY} H${topRightX} V${topRightY + codesFrame.height / 3}
}, { d:M${bottomRightX - codesFrame.width / 3} ${bottomRightY} H${bottomRightX} V${bottomRightY - codesFrame.height / 3}
}, { d: `M${bottomLeftX + codesFrame.width / 3} ${bottomLeftY} H${bottomLeftX} V${bottomLeftY divide for 3 just my calculate the stroke width: The Result is:can you send the full snippet or your code?
@shacharcohen1997 I think this well help
android:
iOS:
ios device correct√ android device Coordinate shift x
android:
iOS:
ios device correct√ android device Coordinate shift x
Can you share your code to discuss more?
refre to:https://medium.com/@susumtang/improve-user-experience-with-barcode-highlight-box-using-react-native-vision-camera-code-scanner-ff7f67089aab android: iOS: ios device correct√ android device Coordinate shift x
Can you share your code to discuss more?
import {useFocusEffect, useIsFocused} from '@react-navigation/native';
import React, {useEffect, useState, useRef} from 'react';
import {
StyleSheet,
Dimensions,
Text,
TouchableWithoutFeedback,
Image,
NativeModules,
StatusBar,
Platform,
AppState,
BackHandler,
DrawerLayoutAndroid,
Linking,
PermissionsAndroid,
View,
Pressable,
} from 'react-native';
import ImagePicker from 'react-native-image-crop-picker';
import RNQRGenerator from 'rn-qr-generator';
import ToastUtils from '../utils/ToastUtils';
import ComUtils from '../utils/ComUtils';
import BarcodeMask from '../components/BarcodeMask';
import {useHeaderHeight} from '@react-navigation/elements';
import {
Camera,
useCameraDevice,
NoCameraDeviceError,
useCodeScanner,
} from 'react-native-vision-camera';
import DeviceInfo from 'react-native-device-info';
const BarCodeScanner = ({navigation, route}) => {
const {StatusBarManager} = NativeModules;
const statusBarHeight = StatusBarManager.HEIGHT; //状态栏高度:ios54 android32
const navigationBarHeader = useHeaderHeight(); //ios103, android 56
const {height, width} = Dimensions.get('window');
// const CAM_VIEW_HEIGHT = Dimensions.get('window').height;
const CAM_VIEW_HEIGHT =
height -
(Platform.OS === 'ios' ? statusBarHeight : statusBarHeight) -
navigationBarHeader;
const CAM_VIEW_WIDTH = Dimensions.get('window').width;
// The following are based on landscape orientation with home button to the right
//andorid全面屏幕中 Dimensions.get('window').height 计算屏幕高度时会自动减少StatusBar 的高度
const maskWidth = width * 0.7;
// left
const topMargin =
(height -
(Platform.OS === 'ios' ? statusBarHeight : 0) -
navigationBarHeader -
maskWidth) /
2;
// top
const leftMargin = (width - maskWidth) / 2;
// width (height of portrait orientation)
const frameWidth = maskWidth;
// height (width of portrait orientation)
const frameHeight = maskWidth;
//TM的计算的百分比
const scanAreaX = leftMargin / CAM_VIEW_HEIGHT;
const scanAreaY = topMargin / CAM_VIEW_WIDTH;
const scanAreaWidth = frameWidth / CAM_VIEW_HEIGHT;
const scanAreaHeight = frameHeight / CAM_VIEW_WIDTH;
//参考地址:https://github.com/react-native-camera/react-native-camera/pull/2719/files/583507f22363d330f903af0215f20250a7ee3851#diff-518dcc98e7451b35bc3b9acfc686894d2bee38b6ced2d9c59ccec56153283f5d
const device = useCameraDevice('back');
const [layout, setLayout] = useState({x: 0, y: 0, width: 0, height: 0});
const [scanFrame, setScanFrame] = useState({height: 1, width: 1});
const [codeScannerHighlights, setCodeScannerHighlights] = useState([]);
const cameraRef = useRef(null);
const mBarCodeRead = useRef(false);
const isFocused = useIsFocused();
const [paths, setPaths] = useState([]);
const [lightSource, setLightSource] = useState(
require('../res/img/scanLigtOff.png'),
);
const scanFromPhoto_img = require('../res/img/scan_from_phone.png');
const [flashMode, setFlashMode] = useState('off');
const [codes, setCodes] = useState([]);
const codeScanner = useCodeScanner({
codeTypes: ['qr', 'ean-13'],
// regionOfInterest: {
// x: leftMargin,
// y: topMargin,
// width: frameWidth,
// height: frameHeight,
// },
onCodeScanned: (codes, frame) => {
console.log('codes===', codes, frame);
setScanFrame(frame);
setCodeScannerHighlights(
codes.map(code => ({
value: code.value,
height: code.frame?.width ?? 0,
width: code.frame?.height ?? 0,
x: code.frame?.y ?? 0,
y: code.frame?.x ?? 0,
})),
);
},
// onCodeScanned: codes => {
// console.log(codes && codes[0].value);
// // [{"corners": [[Object], [Object], [Object], [Object]], "frame": {"height": 43, "width": 48, "x": 141, "y": 302}, "type": "qr", "value": "HN19004070ANS7"}]
// //[{"x": 228, "y": 227}, {"x": 266, "y": 228}, {"x": 267, "y": 264}, {"x": 229, "y": 263}]
// console.log('onCodeScanned', codes, codes && codes[0].corners);
// setCodes(codes);
// return;
// if (codes && codes[0].value) {
// onBarCodeRead(codes[0].value);
// }
// },
});
// eslint-disable-next-line react/no-unstable-nested-components
const HightlightBox = ({highlight, layout, scanFrame}) => {
console.log('highlightBox', highlight, layout, scanFrame);
return (
<Pressable onPress={() => onBarCodeRead(highlight.value)}>
<View
style={[
{
position: 'absolute',
borderWidth: 2,
borderColor: 'green',
},
{
right: highlight.x * (layout.width / scanFrame.height),
top: highlight.y * (layout.height / scanFrame.width),
height: highlight.height * (layout.width / scanFrame.height),
width: highlight.width * (layout.height / scanFrame.width),
},
]}
/>
</Pressable>
);
};
const onBarCodeRead = result => {
console.log('onBarCodeRead', result);
if (!mBarCodeRead.current) {
mBarCodeRead.current = true;
navigation.pop();
if (route.params?.callback) {
route.params?.callback(result);
}
}
};
useEffect(() => {
if (Platform.OS === 'android') {
requestCameraPermission();
}
// const fors = AppState.addEventListener('focus', () => {
// console.log('进来了');
// if (Platform.OS === 'android') {
// requestCameraPermission();
// }
// });
return () => {
// fors.remove();
};
}, []);
const requestCameraPermission = async () => {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('开启摄像头成功');
} else {
ToastUtils.info('请开启摄像头权限');
navigation.pop();
Linking.openSettings();
}
};
const openPicker = () => {
ImagePicker.openPicker({
compressImageQuality: 0.1, //以质量压缩图像(从0到1,其中1为最佳质量)。在iOS上,大于0.8的值在大多数图像中不会产生明显的质量提高,而大于0.8的值将使文件大小比减小1的大小减少一半或更少。
showsSelectedCount: true, //是否显示所选资产的数量 仅iOS
cropperChooseText: '完成', //仅iOS
cropperCancelText: '取消', //仅iOS
// includeBase64: true, //是否在结果中包含base64数据
multiple: false,
cropping: true,
})
.then(async image => {
console.log('openPicker', image);
let url = null;
if (Platform.OS === 'android') {
url = image.path;
} else {
url = image.sourceURL;
}
RNQRGenerator.detect({
uri: url,
})
.then(response => {
const {values} = response; // Array of detected QR code values. Empty if nothing found.
console.log('QR code values', values);
if (values && values.length > 0) {
onBarCodeRead({data: values[0]});
} else {
ToastUtils.show('请扫描清晰的二维码');
}
})
.catch(error => console.log('Cannot detect QR code in image', error));
})
.catch(error => {
console.log('openPicker error', error);
});
};
const scanFromPhoto = () => {
//从手机相册选择
if (Platform.OS === 'android') {
if (Platform.Version >= 33) {
ComUtils.checkAndRequestPermission(
PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES,
() => {
openPicker();
},
() => {},
'需要您的相册权限用于二维码图片,若不允许,将无法选择图片',
);
} else {
ComUtils.checkAndRequestPermission(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
() => {
openPicker();
},
() => {},
'需要您的相册权限用于二维码图片,若不允许,将无法选择图片',
);
}
} else {
openPicker();
}
};
const renderBottomView = () => {
return (
<View
style={{
flex: 1,
alignSelf: 'stretch',
alignItems: 'center',
justifyContent: 'space-around',
}}>
<Text style={{fontSize: 14, color: '#fff', marginTop: 10}}>
将二维码/条码放入框内,即可自动扫描
</Text>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
width: width * 0.7,
}}>
<TouchableWithoutFeedback onPress={handleFlashModeChange}>
<Image source={lightSource} />
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={scanFromPhoto}>
<Image source={scanFromPhoto_img} />
</TouchableWithoutFeedback>
</View>
</View>
);
};
const handleFlashModeChange = () => {
if (cameraRef.current) {
if (flashMode === 'on') {
setFlashMode('off');
setLightSource(require('../res/img/scanLigtOff.png'));
} else {
setFlashMode('on');
setLightSource(require('../res/img/scanLightOn.png'));
}
}
};
if (device == null) {
return <NoCameraDeviceError />;
}
let qrCodeTagControl = null;
if (codes && codes.length > 0) {
qrCodeTagControl = (
<View style={StyleSheet.absoluteFill}>
{codes.map((item, index) => {
return <QRCodeTag key={index} codesFrame={item.frame} />;
})}
</View>
);
}
const onLayout = evt => {
if (evt.nativeEvent.layout) {
setLayout(evt.nativeEvent.layout);
}
};
return (
<View style={{flex: 1, height: '100%', width: '100%'}}>
<Camera
ref={cameraRef}
photo={false}
video={false}
isActive={isFocused}
resizeMode="cover"
onLayout={onLayout}
// style={{
// height: frameWidth,
// width: frameHeight,
// position: 'absolute',
// left: leftMargin,
// top: topMargin,
// }}
torch={flashMode}
onError={error => {
// console.error(error)
}}
style={StyleSheet.absoluteFill}
device={device}
codeScanner={codeScanner}
/>
<BarcodeMask
edgeColor={'#22ff00'}
edgeBorderWidth={3}
animatedLineColor={'#22ff00'}
animatedLineHeight={1}
lineAnimationDuration={3000}
transparency={0.2}
width={frameWidth}
height={frameHeight}
renderBottomView={renderBottomView}
/>
{codeScannerHighlights.map((hightlight, key) => (
<HightlightBox
key={key}
highlight={hightlight}
layout={layout}
scanFrame={scanFrame}
/>
))}
{/* {qrCodeTagControl} */}
</View>
);
};
//[Figuring out dimensions in CodeScanner ]: https://github.com/mrousavy/react-native-vision-camera/issues/2436
//https://github.com/mrousavy/react-native-vision-camera/issues/2736
const QRCodeTag = ({codesFrame}) => {
console.log('codesFrame===', codesFrame);
const topLeftX = codesFrame.x;
const topLeftY = codesFrame.y;
const topRightX = codesFrame.x + codesFrame.width;
const topRightY = codesFrame.y;
const bottomRightX = codesFrame.x + codesFrame.width;
const bottomRightY = codesFrame.y + codesFrame.height;
const bottomLeftX = codesFrame.x;
const bottomLeftY = codesFrame.y + codesFrame.height;
const minY = Math.min(topLeftY, topRightY, bottomLeftY, bottomRightY);
const maxY = Math.max(topLeftY, topRightY, bottomLeftY, bottomRightY);
const minX = Math.min(topLeftX, topRightX, bottomLeftX, bottomRightX);
const maxX = Math.max(topLeftX, topRightX, bottomLeftX, bottomRightX);
const width = maxX - minX;
const height = maxY - minY;
return (
<View
style={{
position: 'absolute',
left: minX,
top: minY,
width: codesFrame.width,
height: codesFrame.height,
borderWidth: 2,
borderColor: 'green',
}}
/>
);
};
export default BarCodeScanner;
System:
OS: macOS 14.5
CPU: (10) arm64 Apple M2 Pro
Memory: 92.67 MB / 16.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 20.0.0
path: /var/folders/kb/p5p48sk13csbptrctf07nlt00000gn/T/yarn--1720674280836-0.11255958323586901/node
Yarn:
version: 1.22.19
path: /var/folders/kb/p5p48sk13csbptrctf07nlt00000gn/T/yarn--1720674280836-0.11255958323586901/yarn
npm:
version: 9.8.1
path: /opt/homebrew/bin/npm
Watchman:
version: 2024.01.22.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.14.3
path: /Users/inkcrazy/.rvm/gems/ruby-3.0.0/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 23.5
- iOS 17.5
- macOS 14.5
- tvOS 17.5
- visionOS 1.2
- watchOS 10.5
Android SDK:
API Levels:
- "28"
- "29"
- "30"
- "31"
- "32"
- "33"
- "33"
- "34"
Build Tools:
- 28.0.3
- 29.0.2
- 29.0.3
- 30.0.2
- 30.0.3
- 31.0.0
- 33.0.0
- 33.0.1
- 33.0.2
- 34.0.0
- 34.0.0
System Images:
- android-31 | Android TV ARM 64 v8a
- android-33 | Google APIs ARM 64 v8a
- android-34 | Google APIs ARM 64 v8a
- android-UpsideDownCake | Google APIs ARM 64 v8a
Android NDK: Not Found
IDEs:
Android Studio: 2022.1 AI-221.6008.13.2211.9619390
Xcode:
version: 15.4/15F31d
path: /usr/bin/xcodebuild
Languages:
Java:
version: 11.0.22
path: /Users/inkcrazy/.sdkman/candidates/java/current/bin/javac
Ruby:
version: 3.0.0
path: /Users/inkcrazy/.rvm/rubies/ruby-3.0.0/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 18.2.0
wanted: 18.2.0
react-native:
installed: 0.74.2
wanted: 0.74.2
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: true
newArchEnabled: false
Question
I am really struggling with a specific task.. i am trying to follow a qr code with a square I tried the code below which almost works in 1 device but not in others and not in iphone
What I tried
VisionCamera Version
3.9.2
Additional information