Skip to content

Commit

Permalink
Merge pull request react-native-camera#2719 from bqwang91/master
Browse files Browse the repository at this point in the history
Android - Limiting Scanning Area using rectOfInterest
  • Loading branch information
MateusAndrade committed Mar 13, 2020
2 parents 944c304 + 583507f commit 43a7323
Show file tree
Hide file tree
Showing 66 changed files with 11,847 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,26 @@ public void setGoogleVisionBarcodeMode(RNCameraView view, int barcodeMode) {
public void setTextRecognizing(RNCameraView view, boolean textRecognizerEnabled) {
view.setShouldRecognizeText(textRecognizerEnabled);
}

/**---limit scan area addition---**/
@ReactProp(name = "rectOfInterest")
public void setRectOfInterest(RNCameraView view, ReadableMap coordinates) {
if(coordinates != null){
float x = (float) coordinates.getDouble("x");
float y = (float) coordinates.getDouble("y");
float width = (float) coordinates.getDouble("width");
float height = (float) coordinates.getDouble("height");
view.setRectOfInterest(x, y, width, height);
}
}

@ReactProp(name = "cameraViewDimensions")
public void setCameraViewDimensions(RNCameraView view, ReadableMap dimensions) {
if(dimensions != null){
int cameraViewWidth = (int) dimensions.getDouble("width");
int cameraViewHeight = (int) dimensions.getDouble("height");
view.setCameraViewDimensions(cameraViewWidth, cameraViewHeight);
}
}
/**---limit scan area addition---**/
}
24 changes: 23 additions & 1 deletion android/src/main/java/org/reactnative/camera/RNCameraView.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
private int mPaddingX;
private int mPaddingY;

// Limit Android Scan Area
private boolean mLimitScanArea = false;
private float mScanAreaX = 0.0f;
private float mScanAreaY = 0.0f;
private float mScanAreaWidth = 0.0f;
private float mScanAreaHeight = 0.0f;
private int mCameraViewWidth = 0;
private int mCameraViewHeight = 0;

public RNCameraView(ThemedReactContext themedReactContext) {
super(themedReactContext, true);
mThemedReactContext = themedReactContext;
Expand Down Expand Up @@ -152,7 +161,7 @@ public void onFramePreview(CameraView cameraView, byte[] data, int width, int he
if (willCallBarCodeTask) {
barCodeScannerTaskLock = true;
BarCodeScannerAsyncTaskDelegate delegate = (BarCodeScannerAsyncTaskDelegate) cameraView;
new BarCodeScannerAsyncTask(delegate, mMultiFormatReader, data, width, height).execute();
new BarCodeScannerAsyncTask(delegate, mMultiFormatReader, data, width, height, mLimitScanArea, mScanAreaX, mScanAreaY, mScanAreaWidth, mScanAreaHeight, mCameraViewWidth, mCameraViewHeight, getAspectRatio().toFloat()).execute();
}

if (willCallFaceTask) {
Expand Down Expand Up @@ -356,6 +365,19 @@ public void onBarCodeScanningTaskCompleted() {
}
}

// Limit Scan Area
public void setRectOfInterest(float x, float y, float width, float height) {
this.mLimitScanArea = true;
this.mScanAreaX = x;
this.mScanAreaY = y;
this.mScanAreaWidth = width;
this.mScanAreaHeight = height;
}
public void setCameraViewDimensions(int width, int height) {
this.mCameraViewWidth = width;
this.mCameraViewHeight = height;
}

/**
* Initial setup of the face detector
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,44 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
private int mHeight;
private BarCodeScannerAsyncTaskDelegate mDelegate;
private final MultiFormatReader mMultiFormatReader;
private boolean mLimitScanArea;
private float mScanAreaX;
private float mScanAreaY;
private float mScanAreaWidth;
private float mScanAreaHeight;
private int mCameraViewWidth;
private int mCameraViewHeight;
private float mRatio;

// note(sjchmiela): From my short research it's ok to ignore rotation of the image.
public BarCodeScannerAsyncTask(
BarCodeScannerAsyncTaskDelegate delegate,
MultiFormatReader multiFormatReader,
byte[] imageData,
int width,
int height
int height,
boolean limitScanArea,
float scanAreaX,
float scanAreaY,
float scanAreaWidth,
float scanAreaHeight,
int cameraViewWidth,
int cameraViewHeight,
float ratio
) {
mImageData = imageData;
mWidth = width;
mHeight = height;
mDelegate = delegate;
mMultiFormatReader = multiFormatReader;
mLimitScanArea = limitScanArea;
mScanAreaX = scanAreaX;
mScanAreaY = scanAreaY;
mScanAreaWidth = scanAreaWidth;
mScanAreaHeight = scanAreaHeight;
mCameraViewWidth = cameraViewWidth;
mCameraViewHeight = cameraViewHeight;
mRatio = ratio;
}

@Override
Expand All @@ -36,21 +60,41 @@ protected Result doInBackground(Void... ignored) {
}

Result result = null;
/**
* mCameraViewWidth and mCameraViewHeight are obtained from portait orientation
* mWidth and mHeight are measured with landscape orientation with Home button to the right
* adjustedCamViewWidth is the adjusted width fromt the Aspect ratio setting
*/
int adjustedCamViewWidth = (int) (mCameraViewHeight / mRatio);
float adjustedScanY = (((adjustedCamViewWidth - mCameraViewWidth) / 2) + (mScanAreaY * mCameraViewWidth)) / adjustedCamViewWidth;

int left = (int) (mScanAreaX * mWidth);
int top = (int) (adjustedScanY * mHeight);
int scanWidth = (int) (mScanAreaWidth * mWidth);
int scanHeight = (int) (((mScanAreaHeight * mCameraViewWidth) / adjustedCamViewWidth) * mHeight);

try {
BinaryBitmap bitmap = generateBitmapFromImageData(
mImageData,
mWidth,
mHeight,
false
false,
left,
top,
scanWidth,
scanHeight
);
result = mMultiFormatReader.decodeWithState(bitmap);
} catch (NotFoundException e) {
BinaryBitmap bitmap = generateBitmapFromImageData(
rotateImage(mImageData,mWidth, mHeight),
mHeight,
mWidth,
false
false,
mHeight - scanHeight - top,
left,
scanHeight,
scanWidth
);
try {
result = mMultiFormatReader.decodeWithState(bitmap);
Expand All @@ -59,7 +103,11 @@ protected Result doInBackground(Void... ignored) {
mImageData,
mWidth,
mHeight,
true
true,
mWidth - scanWidth - left,
mHeight - scanHeight - top,
scanWidth,
scanHeight
);
try {
result = mMultiFormatReader.decodeWithState(invertedBitmap);
Expand All @@ -68,7 +116,11 @@ protected Result doInBackground(Void... ignored) {
rotateImage(mImageData,mWidth, mHeight),
mHeight,
mWidth,
true
true,
top,
mWidth - scanWidth - left,
scanHeight,
scanWidth
);
try {
result = mMultiFormatReader.decodeWithState(invertedRotatedBitmap);
Expand Down Expand Up @@ -101,8 +153,21 @@ protected void onPostExecute(Result result) {
mDelegate.onBarCodeScanningTaskCompleted();
}

private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, int height, boolean inverse) {
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, int height, boolean inverse, int left, int top, int sWidth, int sHeight) {
PlanarYUVLuminanceSource source;
if (mLimitScanArea) {
source = new PlanarYUVLuminanceSource(
imageData, // byte[] yuvData
width, // int dataWidth
height, // int dataHeight
left, // int left
top, // int top
sWidth, // int width
sHeight, // int height
false // boolean reverseHorizontal
);
} else {
source = new PlanarYUVLuminanceSource(
imageData, // byte[] yuvData
width, // int dataWidth
height, // int dataHeight
Expand All @@ -111,7 +176,8 @@ private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, in
width, // int width
height, // int height
false // boolean reverseHorizontal
);
);
}
if (inverse) {
return new BinaryBitmap(new HybridBinarizer(source.invert()));
} else {
Expand Down
9 changes: 8 additions & 1 deletion docs/RNCamera.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,17 @@ By default a `Camera not authorized` message will be displayed when access to th

By default a <ActivityIndicator> will be displayed while the component is waiting for the user to grant/deny access to the camera, if set displays the passed react element instead of the default one.

#### `iOS` `rectOfInterest`
#### `rectOfInterest`

An `{x: , y:, width:, height: }` object which defines the rect of interst as normalized coordinates from `(0,0)` top left corner to `(1,1)` bottom right corner.

Note: Must also provide cameraViewDimensions prop for Android device

### `Android` `cameraViewDimensions`

An `{width:, height: }` object which defines the width and height of the cameraView. This prop is used to adjust the effect of Aspect Raio for rectOfInterest area on Android


### `iOS` `videoStabilizationMode`

The video stabilization mode used for a video recording. The possible values are:
Expand Down
6 changes: 6 additions & 0 deletions examples/rectOfInterest/.buckconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

[android]
target = Google Inc.:Google APIs:23

[maven_repositories]
central = https://repo1.maven.org/maven2
4 changes: 4 additions & 0 deletions examples/rectOfInterest/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: '@react-native-community',
};
75 changes: 75 additions & 0 deletions examples/rectOfInterest/.flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js

; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/

; Ignore polyfills
node_modules/react-native/Libraries/polyfills/.*

; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/warning')
node_modules/warning/.*

; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js

[untyped]
.*/node_modules/@react-native-community/cli/.*/.*

[include]

[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/

[options]
emoji=true

esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable

module.file_ext=.js
module.file_ext=.json
module.file_ext=.ios.js

munge_underscores=true

module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'

suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState

suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError

[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
inexact-spread=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error

[strict]
deprecated-type
nonstrict-import
sketchy-null
unclear-type
unsafe-getters-setters
untyped-import
untyped-type-import

[version]
^0.105.0
1 change: 1 addition & 0 deletions examples/rectOfInterest/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.pbxproj -text
59 changes: 59 additions & 0 deletions examples/rectOfInterest/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# OSX
#
.DS_Store

# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate

# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml

# node.js
#
node_modules/
npm-debug.log
yarn-error.log

# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/

*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots

# Bundle artifact
*.jsbundle

# CocoaPods
/ios/Pods/
6 changes: 6 additions & 0 deletions examples/rectOfInterest/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
bracketSpacing: false,
jsxBracketSameLine: true,
singleQuote: true,
trailingComma: 'all',
};
Loading

0 comments on commit 43a7323

Please sign in to comment.