mirror of
https://github.com/status-im/react-native-camera.git
synced 2025-02-24 09:48:17 +00:00
Merge pull request #1388 from alculquicondor/ocr
Integrating Google Vision's text recognition in Android
This commit is contained in:
commit
ebe27e0b5b
@ -1,6 +1,6 @@
|
|||||||
# React Native Camera [](#backers) [](#sponsors) [](http://badge.fury.io/js/react-native-camera) [](https://www.npmjs.com/package/react-native-camera)
|
# React Native Camera [](#backers) [](#sponsors) [](http://badge.fury.io/js/react-native-camera) [](https://www.npmjs.com/package/react-native-camera)
|
||||||
|
|
||||||
The comprehensive camera module for React Native. Including photographs, videos, face detection and barcode scanning!
|
The comprehensive camera module for React Native. Including photographs, videos, face detection, barcode scanning and text recognition (Android only)!
|
||||||
|
|
||||||
`import { RNCamera, FaceDetector } from 'react-native-camera';`
|
`import { RNCamera, FaceDetector } from 'react-native-camera';`
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ pod 'react-native-camera', path: '../node_modules/react-native-camera'
|
|||||||
4. In XCode, in the project navigator, select your project. Add `libRNCamera.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
|
4. In XCode, in the project navigator, select your project. Add `libRNCamera.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
|
||||||
5. Click `RNCamera.xcodeproj` in the project navigator and go the `Build Settings` tab. Make sure 'All' is toggled on (instead of 'Basic'). In the `Search Paths` section, look for `Header Search Paths` and make sure it contains both `$(SRCROOT)/../../react-native/React` and `$(SRCROOT)/../../../React` - mark both as `recursive`.
|
5. Click `RNCamera.xcodeproj` in the project navigator and go the `Build Settings` tab. Make sure 'All' is toggled on (instead of 'Basic'). In the `Search Paths` section, look for `Header Search Paths` and make sure it contains both `$(SRCROOT)/../../react-native/React` and `$(SRCROOT)/../../../React` - mark both as `recursive`.
|
||||||
|
|
||||||
### Face Detection Steps
|
### Face Detection or Text Recognition Steps
|
||||||
|
|
||||||
Face Detection is optional on iOS. If you want it, you are going to need to install Google Mobile Vision frameworks in your project, as mentioned in the next section.
|
Face Detection is optional on iOS. If you want it, you are going to need to install Google Mobile Vision frameworks in your project, as mentioned in the next section.
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
|||||||
EVENT_ON_MOUNT_ERROR("onMountError"),
|
EVENT_ON_MOUNT_ERROR("onMountError"),
|
||||||
EVENT_ON_BAR_CODE_READ("onBarCodeRead"),
|
EVENT_ON_BAR_CODE_READ("onBarCodeRead"),
|
||||||
EVENT_ON_FACES_DETECTED("onFacesDetected"),
|
EVENT_ON_FACES_DETECTED("onFacesDetected"),
|
||||||
EVENT_ON_FACE_DETECTION_ERROR("onFaceDetectionError");
|
EVENT_ON_FACE_DETECTION_ERROR("onFaceDetectionError"),
|
||||||
|
EVENT_ON_TEXT_RECOGNIZED("onTextRecognized");
|
||||||
|
|
||||||
private final String mName;
|
private final String mName;
|
||||||
|
|
||||||
@ -138,4 +139,9 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
|||||||
public void setFaceDetectionClassifications(RNCameraView view, int classifications) {
|
public void setFaceDetectionClassifications(RNCameraView view, int classifications) {
|
||||||
view.setFaceDetectionClassifications(classifications);
|
view.setFaceDetectionClassifications(classifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = "textRecognizerEnabled")
|
||||||
|
public void setTextRecognizing(RNCameraView view, boolean textRecognizerEnabled) {
|
||||||
|
view.setShouldRecognizeText(textRecognizerEnabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@ import com.facebook.react.bridge.WritableMap;
|
|||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.google.android.cameraview.CameraView;
|
import com.google.android.cameraview.CameraView;
|
||||||
import com.google.android.gms.vision.face.Face;
|
import com.google.android.gms.vision.face.Face;
|
||||||
|
import com.google.android.gms.vision.text.Text;
|
||||||
|
import com.google.android.gms.vision.text.TextBlock;
|
||||||
|
import com.google.android.gms.vision.text.TextRecognizer;
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
import com.google.zxing.DecodeHintType;
|
import com.google.zxing.DecodeHintType;
|
||||||
import com.google.zxing.MultiFormatReader;
|
import com.google.zxing.MultiFormatReader;
|
||||||
@ -27,6 +30,8 @@ import org.reactnative.camera.tasks.BarCodeScannerAsyncTaskDelegate;
|
|||||||
import org.reactnative.camera.tasks.FaceDetectorAsyncTask;
|
import org.reactnative.camera.tasks.FaceDetectorAsyncTask;
|
||||||
import org.reactnative.camera.tasks.FaceDetectorAsyncTaskDelegate;
|
import org.reactnative.camera.tasks.FaceDetectorAsyncTaskDelegate;
|
||||||
import org.reactnative.camera.tasks.ResolveTakenPictureAsyncTask;
|
import org.reactnative.camera.tasks.ResolveTakenPictureAsyncTask;
|
||||||
|
import org.reactnative.camera.tasks.TextRecognizerAsyncTask;
|
||||||
|
import org.reactnative.camera.tasks.TextRecognizerAsyncTaskDelegate;
|
||||||
import org.reactnative.camera.utils.ImageDimensions;
|
import org.reactnative.camera.utils.ImageDimensions;
|
||||||
import org.reactnative.camera.utils.RNFileUtils;
|
import org.reactnative.camera.utils.RNFileUtils;
|
||||||
import org.reactnative.facedetector.RNFaceDetector;
|
import org.reactnative.facedetector.RNFaceDetector;
|
||||||
@ -41,7 +46,8 @@ import java.util.Queue;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
public class RNCameraView extends CameraView implements LifecycleEventListener, BarCodeScannerAsyncTaskDelegate, FaceDetectorAsyncTaskDelegate {
|
public class RNCameraView extends CameraView implements LifecycleEventListener, BarCodeScannerAsyncTaskDelegate, FaceDetectorAsyncTaskDelegate,
|
||||||
|
TextRecognizerAsyncTaskDelegate {
|
||||||
private ThemedReactContext mThemedReactContext;
|
private ThemedReactContext mThemedReactContext;
|
||||||
private Queue<Promise> mPictureTakenPromises = new ConcurrentLinkedQueue<>();
|
private Queue<Promise> mPictureTakenPromises = new ConcurrentLinkedQueue<>();
|
||||||
private Map<Promise, ReadableMap> mPictureTakenOptions = new ConcurrentHashMap<>();
|
private Map<Promise, ReadableMap> mPictureTakenOptions = new ConcurrentHashMap<>();
|
||||||
@ -55,12 +61,15 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
|||||||
// Concurrency lock for scanners to avoid flooding the runtime
|
// Concurrency lock for scanners to avoid flooding the runtime
|
||||||
public volatile boolean barCodeScannerTaskLock = false;
|
public volatile boolean barCodeScannerTaskLock = false;
|
||||||
public volatile boolean faceDetectorTaskLock = false;
|
public volatile boolean faceDetectorTaskLock = false;
|
||||||
|
public volatile boolean textRecognizerTaskLock = false;
|
||||||
|
|
||||||
// Scanning-related properties
|
// Scanning-related properties
|
||||||
private final MultiFormatReader mMultiFormatReader = new MultiFormatReader();
|
private final MultiFormatReader mMultiFormatReader = new MultiFormatReader();
|
||||||
private final RNFaceDetector mFaceDetector;
|
private final RNFaceDetector mFaceDetector;
|
||||||
|
private final TextRecognizer mTextRecognizer;
|
||||||
private boolean mShouldDetectFaces = false;
|
private boolean mShouldDetectFaces = false;
|
||||||
private boolean mShouldScanBarCodes = false;
|
private boolean mShouldScanBarCodes = false;
|
||||||
|
private boolean mShouldRecognizeText = false;
|
||||||
private int mFaceDetectorMode = RNFaceDetector.FAST_MODE;
|
private int mFaceDetectorMode = RNFaceDetector.FAST_MODE;
|
||||||
private int mFaceDetectionLandmarks = RNFaceDetector.NO_LANDMARKS;
|
private int mFaceDetectionLandmarks = RNFaceDetector.NO_LANDMARKS;
|
||||||
private int mFaceDetectionClassifications = RNFaceDetector.NO_CLASSIFICATIONS;
|
private int mFaceDetectionClassifications = RNFaceDetector.NO_CLASSIFICATIONS;
|
||||||
@ -71,6 +80,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
|||||||
mThemedReactContext = themedReactContext;
|
mThemedReactContext = themedReactContext;
|
||||||
mFaceDetector = new RNFaceDetector(themedReactContext);
|
mFaceDetector = new RNFaceDetector(themedReactContext);
|
||||||
setupFaceDetector();
|
setupFaceDetector();
|
||||||
|
mTextRecognizer = new TextRecognizer.Builder(themedReactContext).build();
|
||||||
themedReactContext.addLifecycleEventListener(this);
|
themedReactContext.addLifecycleEventListener(this);
|
||||||
|
|
||||||
addCallback(new Callback() {
|
addCallback(new Callback() {
|
||||||
@ -121,6 +131,12 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
|||||||
FaceDetectorAsyncTaskDelegate delegate = (FaceDetectorAsyncTaskDelegate) cameraView;
|
FaceDetectorAsyncTaskDelegate delegate = (FaceDetectorAsyncTaskDelegate) cameraView;
|
||||||
new FaceDetectorAsyncTask(delegate, mFaceDetector, data, width, height, correctRotation).execute();
|
new FaceDetectorAsyncTask(delegate, mFaceDetector, data, width, height, correctRotation).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mShouldRecognizeText && !textRecognizerTaskLock && cameraView instanceof TextRecognizerAsyncTaskDelegate) {
|
||||||
|
textRecognizerTaskLock = true;
|
||||||
|
TextRecognizerAsyncTaskDelegate delegate = (TextRecognizerAsyncTaskDelegate) cameraView;
|
||||||
|
new TextRecognizerAsyncTask(delegate, mTextRecognizer, data, width, height, correctRotation).execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -145,7 +161,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
|||||||
@Override
|
@Override
|
||||||
public void onViewAdded(View child) {
|
public void onViewAdded(View child) {
|
||||||
if (this.getView() == child || this.getView() == null) return;
|
if (this.getView() == child || this.getView() == null) return;
|
||||||
// remove and readd view to make sure it is in the back.
|
// remove and read view to make sure it is in the back.
|
||||||
// @TODO figure out why there was a z order issue in the first place and fix accordingly.
|
// @TODO figure out why there was a z order issue in the first place and fix accordingly.
|
||||||
this.removeView(this.getView());
|
this.removeView(this.getView());
|
||||||
this.addView(this.getView(), 0);
|
this.addView(this.getView(), 0);
|
||||||
@ -210,7 +226,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
|||||||
|
|
||||||
public void setShouldScanBarCodes(boolean shouldScanBarCodes) {
|
public void setShouldScanBarCodes(boolean shouldScanBarCodes) {
|
||||||
this.mShouldScanBarCodes = shouldScanBarCodes;
|
this.mShouldScanBarCodes = shouldScanBarCodes;
|
||||||
setScanning(mShouldDetectFaces || mShouldScanBarCodes);
|
setScanning(mShouldDetectFaces || mShouldScanBarCodes || mShouldRecognizeText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onBarCodeRead(Result barCode) {
|
public void onBarCodeRead(Result barCode) {
|
||||||
@ -260,7 +276,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
|||||||
|
|
||||||
public void setShouldDetectFaces(boolean shouldDetectFaces) {
|
public void setShouldDetectFaces(boolean shouldDetectFaces) {
|
||||||
this.mShouldDetectFaces = shouldDetectFaces;
|
this.mShouldDetectFaces = shouldDetectFaces;
|
||||||
setScanning(mShouldDetectFaces || mShouldScanBarCodes);
|
setScanning(mShouldDetectFaces || mShouldScanBarCodes || mShouldRecognizeText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFacesDetected(SparseArray<Face> facesReported, int sourceWidth, int sourceHeight, int sourceRotation) {
|
public void onFacesDetected(SparseArray<Face> facesReported, int sourceWidth, int sourceHeight, int sourceRotation) {
|
||||||
@ -287,6 +303,28 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
|||||||
faceDetectorTaskLock = false;
|
faceDetectorTaskLock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setShouldRecognizeText(boolean shouldRecognizeText) {
|
||||||
|
this.mShouldRecognizeText = shouldRecognizeText;
|
||||||
|
setScanning(mShouldDetectFaces || mShouldScanBarCodes || mShouldRecognizeText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextRecognized(SparseArray<TextBlock> textBlocks, int sourceWidth, int sourceHeight, int sourceRotation) {
|
||||||
|
if (!mShouldRecognizeText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SparseArray<TextBlock> textBlocksDetected = textBlocks == null ? new SparseArray<TextBlock>() : textBlocks;
|
||||||
|
ImageDimensions dimensions = new ImageDimensions(sourceWidth, sourceHeight, sourceRotation, getFacing());
|
||||||
|
|
||||||
|
RNCameraViewHelper.emitTextRecognizedEvent(this, textBlocksDetected, dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextRecognizerTaskCompleted() {
|
||||||
|
textRecognizerTaskLock = false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHostResume() {
|
public void onHostResume() {
|
||||||
if (hasCameraPermissions()) {
|
if (hasCameraPermissions()) {
|
||||||
|
@ -16,6 +16,7 @@ import com.facebook.react.bridge.WritableMap;
|
|||||||
import com.facebook.react.uimanager.UIManagerModule;
|
import com.facebook.react.uimanager.UIManagerModule;
|
||||||
import com.google.android.cameraview.CameraView;
|
import com.google.android.cameraview.CameraView;
|
||||||
import com.google.android.gms.vision.face.Face;
|
import com.google.android.gms.vision.face.Face;
|
||||||
|
import com.google.android.gms.vision.text.TextBlock;
|
||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
import org.reactnative.camera.events.BarCodeReadEvent;
|
import org.reactnative.camera.events.BarCodeReadEvent;
|
||||||
@ -23,6 +24,7 @@ import org.reactnative.camera.events.CameraMountErrorEvent;
|
|||||||
import org.reactnative.camera.events.CameraReadyEvent;
|
import org.reactnative.camera.events.CameraReadyEvent;
|
||||||
import org.reactnative.camera.events.FaceDetectionErrorEvent;
|
import org.reactnative.camera.events.FaceDetectionErrorEvent;
|
||||||
import org.reactnative.camera.events.FacesDetectedEvent;
|
import org.reactnative.camera.events.FacesDetectedEvent;
|
||||||
|
import org.reactnative.camera.events.TextRecognizedEvent;
|
||||||
import org.reactnative.camera.utils.ImageDimensions;
|
import org.reactnative.camera.utils.ImageDimensions;
|
||||||
import org.reactnative.facedetector.RNFaceDetector;
|
import org.reactnative.facedetector.RNFaceDetector;
|
||||||
|
|
||||||
@ -217,6 +219,29 @@ public class RNCameraViewHelper {
|
|||||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Text recognition event
|
||||||
|
|
||||||
|
public static void emitTextRecognizedEvent(
|
||||||
|
ViewGroup view,
|
||||||
|
SparseArray<TextBlock> textBlocks,
|
||||||
|
ImageDimensions dimensions) {
|
||||||
|
float density = view.getResources().getDisplayMetrics().density;
|
||||||
|
|
||||||
|
double scaleX = (double) view.getWidth() / (dimensions.getWidth() * density);
|
||||||
|
double scaleY = (double) view.getHeight() / (dimensions.getHeight() * density);
|
||||||
|
|
||||||
|
TextRecognizedEvent event = TextRecognizedEvent.obtain(
|
||||||
|
view.getId(),
|
||||||
|
textBlocks,
|
||||||
|
dimensions,
|
||||||
|
scaleX,
|
||||||
|
scaleY
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactContext reactContext = (ReactContext) view.getContext();
|
||||||
|
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
|
|
||||||
public static int getCorrectCameraRotation(int rotation, int facing) {
|
public static int getCorrectCameraRotation(int rotation, int facing) {
|
||||||
|
@ -0,0 +1,157 @@
|
|||||||
|
package org.reactnative.camera.events;
|
||||||
|
|
||||||
|
import android.support.v4.util.Pools;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.bridge.WritableArray;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
import com.facebook.react.uimanager.events.Event;
|
||||||
|
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||||
|
import com.google.android.cameraview.CameraView;
|
||||||
|
import com.google.android.gms.vision.text.Line;
|
||||||
|
import com.google.android.gms.vision.text.Text;
|
||||||
|
import com.google.android.gms.vision.text.TextBlock;
|
||||||
|
import org.reactnative.camera.CameraViewManager;
|
||||||
|
import org.reactnative.camera.utils.ImageDimensions;
|
||||||
|
import org.reactnative.facedetector.FaceDetectorUtils;
|
||||||
|
|
||||||
|
|
||||||
|
public class TextRecognizedEvent extends Event<TextRecognizedEvent> {
|
||||||
|
|
||||||
|
private static final Pools.SynchronizedPool<TextRecognizedEvent> EVENTS_POOL =
|
||||||
|
new Pools.SynchronizedPool<>(3);
|
||||||
|
|
||||||
|
|
||||||
|
private double mScaleX;
|
||||||
|
private double mScaleY;
|
||||||
|
private SparseArray<TextBlock> mTextBlocks;
|
||||||
|
private ImageDimensions mImageDimensions;
|
||||||
|
|
||||||
|
private TextRecognizedEvent() {}
|
||||||
|
|
||||||
|
public static TextRecognizedEvent obtain(
|
||||||
|
int viewTag,
|
||||||
|
SparseArray<TextBlock> textBlocks,
|
||||||
|
ImageDimensions dimensions,
|
||||||
|
double scaleX,
|
||||||
|
double scaleY) {
|
||||||
|
TextRecognizedEvent event = EVENTS_POOL.acquire();
|
||||||
|
if (event == null) {
|
||||||
|
event = new TextRecognizedEvent();
|
||||||
|
}
|
||||||
|
event.init(viewTag, textBlocks, dimensions, scaleX, scaleY);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(
|
||||||
|
int viewTag,
|
||||||
|
SparseArray<TextBlock> textBlocks,
|
||||||
|
ImageDimensions dimensions,
|
||||||
|
double scaleX,
|
||||||
|
double scaleY) {
|
||||||
|
super.init(viewTag);
|
||||||
|
mTextBlocks = textBlocks;
|
||||||
|
mImageDimensions = dimensions;
|
||||||
|
mScaleX = scaleX;
|
||||||
|
mScaleY = scaleY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventName() {
|
||||||
|
return CameraViewManager.Events.EVENT_ON_TEXT_RECOGNIZED.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||||
|
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
|
||||||
|
}
|
||||||
|
|
||||||
|
private WritableMap serializeEventData() {
|
||||||
|
WritableArray textBlocksList = Arguments.createArray();
|
||||||
|
for (int i = 0; i < mTextBlocks.size(); ++i) {
|
||||||
|
TextBlock textBlock = mTextBlocks.valueAt(i);
|
||||||
|
WritableMap serializedTextBlock = serializeText(textBlock);
|
||||||
|
if (mImageDimensions.getFacing() == CameraView.FACING_FRONT) {
|
||||||
|
serializedTextBlock = rotateTextX(serializedTextBlock);
|
||||||
|
}
|
||||||
|
textBlocksList.pushMap(serializedTextBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
WritableMap event = Arguments.createMap();
|
||||||
|
event.putString("type", "textBlock");
|
||||||
|
event.putArray("textBlocks", textBlocksList);
|
||||||
|
event.putInt("target", getViewTag());
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WritableMap serializeText(Text text) {
|
||||||
|
WritableMap encodedText = Arguments.createMap();
|
||||||
|
|
||||||
|
WritableArray components = Arguments.createArray();
|
||||||
|
for (Text component : text.getComponents()) {
|
||||||
|
components.pushMap(serializeText(component));
|
||||||
|
}
|
||||||
|
encodedText.putArray("components", components);
|
||||||
|
|
||||||
|
encodedText.putString("value", text.getValue());
|
||||||
|
|
||||||
|
WritableMap origin = Arguments.createMap();
|
||||||
|
origin.putDouble("x", text.getBoundingBox().left * this.mScaleX);
|
||||||
|
origin.putDouble("y", text.getBoundingBox().top * this.mScaleY);
|
||||||
|
|
||||||
|
WritableMap size = Arguments.createMap();
|
||||||
|
size.putDouble("width", text.getBoundingBox().width() * this.mScaleX);
|
||||||
|
size.putDouble("height", text.getBoundingBox().width() * this.mScaleY);
|
||||||
|
|
||||||
|
WritableMap bounds = Arguments.createMap();
|
||||||
|
bounds.putMap("origin", origin);
|
||||||
|
bounds.putMap("size", size);
|
||||||
|
|
||||||
|
encodedText.putMap("bounds", bounds);
|
||||||
|
|
||||||
|
String type_;
|
||||||
|
if (text instanceof TextBlock) {
|
||||||
|
type_ = "block";
|
||||||
|
} else if (text instanceof Line) {
|
||||||
|
type_ = "line";
|
||||||
|
} else /*if (text instanceof Element)*/ {
|
||||||
|
type_ = "element";
|
||||||
|
}
|
||||||
|
encodedText.putString("type", type_);
|
||||||
|
|
||||||
|
return encodedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WritableMap rotateTextX(WritableMap text) {
|
||||||
|
ReadableMap faceBounds = text.getMap("bounds");
|
||||||
|
|
||||||
|
ReadableMap oldOrigin = faceBounds.getMap("origin");
|
||||||
|
WritableMap mirroredOrigin = FaceDetectorUtils.positionMirroredHorizontally(
|
||||||
|
oldOrigin, mImageDimensions.getWidth(), mScaleX);
|
||||||
|
|
||||||
|
double translateX = -faceBounds.getMap("size").getDouble("width");
|
||||||
|
WritableMap translatedMirroredOrigin = FaceDetectorUtils.positionTranslatedHorizontally(mirroredOrigin, translateX);
|
||||||
|
|
||||||
|
WritableMap newBounds = Arguments.createMap();
|
||||||
|
newBounds.merge(faceBounds);
|
||||||
|
newBounds.putMap("origin", translatedMirroredOrigin);
|
||||||
|
|
||||||
|
text.putMap("bounds", newBounds);
|
||||||
|
|
||||||
|
ReadableArray oldComponents = text.getArray("components");
|
||||||
|
WritableArray newComponents = Arguments.createArray();
|
||||||
|
for (int i = 0; i < oldComponents.size(); ++i) {
|
||||||
|
WritableMap component = Arguments.createMap();
|
||||||
|
component.merge(oldComponents.getMap(i));
|
||||||
|
rotateTextX(component);
|
||||||
|
newComponents.pushMap(component);
|
||||||
|
}
|
||||||
|
text.putArray("components", newComponents);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package org.reactnative.camera.tasks;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import com.google.android.gms.vision.text.TextBlock;
|
||||||
|
import com.google.android.gms.vision.text.TextRecognizer;
|
||||||
|
import org.reactnative.facedetector.RNFrame;
|
||||||
|
import org.reactnative.facedetector.RNFrameFactory;
|
||||||
|
|
||||||
|
|
||||||
|
public class TextRecognizerAsyncTask extends android.os.AsyncTask<Void, Void, SparseArray<TextBlock>> {
|
||||||
|
|
||||||
|
private TextRecognizerAsyncTaskDelegate mDelegate;
|
||||||
|
private TextRecognizer mTextRecognizer;
|
||||||
|
private byte[] mImageData;
|
||||||
|
private int mWidth;
|
||||||
|
private int mHeight;
|
||||||
|
private int mRotation;
|
||||||
|
|
||||||
|
public TextRecognizerAsyncTask(
|
||||||
|
TextRecognizerAsyncTaskDelegate delegate,
|
||||||
|
TextRecognizer textRecognizer,
|
||||||
|
byte[] imageData,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int rotation
|
||||||
|
) {
|
||||||
|
mDelegate = delegate;
|
||||||
|
mTextRecognizer = textRecognizer;
|
||||||
|
mImageData = imageData;
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
mRotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SparseArray<TextBlock> doInBackground(Void... ignored) {
|
||||||
|
if (isCancelled() || mDelegate == null || mTextRecognizer == null || !mTextRecognizer.isOperational()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RNFrame frame = RNFrameFactory.buildFrame(mImageData, mWidth, mHeight, mRotation);
|
||||||
|
return mTextRecognizer.detect(frame.getFrame());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(SparseArray<TextBlock> textBlocks) {
|
||||||
|
super.onPostExecute(textBlocks);
|
||||||
|
|
||||||
|
if (textBlocks != null) {
|
||||||
|
mDelegate.onTextRecognized(textBlocks, mWidth, mHeight, mRotation);
|
||||||
|
}
|
||||||
|
mDelegate.onTextRecognizerTaskCompleted();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.reactnative.camera.tasks;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import com.google.android.gms.vision.text.TextBlock;
|
||||||
|
|
||||||
|
public interface TextRecognizerAsyncTaskDelegate {
|
||||||
|
void onTextRecognized(SparseArray<TextBlock> textBlocks, int sourceWidth, int sourceHeight, int sourceRotation);
|
||||||
|
void onTextRecognizerTaskCompleted();
|
||||||
|
}
|
@ -233,6 +233,14 @@ Values: `RNCamera.Constants.FaceDetection.Classifications.all` or `RNCamera.Cons
|
|||||||
|
|
||||||
Classification is determining whether a certain facial characteristic is present. For example, a face can be classified with regards to whether its eyes are open or closed. Another example is whether the face is smiling or not.
|
Classification is determining whether a certain facial characteristic is present. For example, a face can be classified with regards to whether its eyes are open or closed. Another example is whether the face is smiling or not.
|
||||||
|
|
||||||
|
### Text Recognition Related props
|
||||||
|
|
||||||
|
Only available in Android. RNCamera uses the Google Mobile Vision frameworks for Text Recognition, you can read more info about it [here](https://developers.google.com/vision/android/text-overview).
|
||||||
|
|
||||||
|
#### `onTextRecognized`
|
||||||
|
|
||||||
|
Method to be called when text is detected. Receives a Text Recognized Event object. The interesting value of this object is the `textBlocks` value, which is an array with objects of the [TextBlock](https://developers.google.com/android/reference/com/google/android/gms/vision/text/TextBlock) properties.
|
||||||
|
|
||||||
#### `takePictureAsync([options]): Promise`
|
#### `takePictureAsync([options]): Promise`
|
||||||
|
|
||||||
Takes a picture, saves in your app's cache directory and returns a promise.
|
Takes a picture, saves in your app's cache directory and returns a promise.
|
||||||
|
@ -31,6 +31,22 @@ type TrackedFaceFeature = FaceFeature & {
|
|||||||
faceID?: number,
|
faceID?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TrackedTextFeature = {
|
||||||
|
type: string,
|
||||||
|
bounds: {
|
||||||
|
size: {
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
},
|
||||||
|
origin: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: string,
|
||||||
|
components: Array<TrackedTextFeature>,
|
||||||
|
};
|
||||||
|
|
||||||
type RecordingOptions = {
|
type RecordingOptions = {
|
||||||
maxDuration?: number,
|
maxDuration?: number,
|
||||||
maxFileSize?: number,
|
maxFileSize?: number,
|
||||||
@ -58,6 +74,7 @@ type PropsType = ViewPropTypes & {
|
|||||||
autoFocus?: string | boolean | number,
|
autoFocus?: string | boolean | number,
|
||||||
faceDetectionClassifications?: number,
|
faceDetectionClassifications?: number,
|
||||||
onFacesDetected?: ({ faces: Array<TrackedFaceFeature> }) => void,
|
onFacesDetected?: ({ faces: Array<TrackedFaceFeature> }) => void,
|
||||||
|
onTextRecognized?: ({ textBlocks: Array<TrackedTextFeature> }) => void,
|
||||||
captureAudio?: boolean,
|
captureAudio?: boolean,
|
||||||
useCamera2Api?: boolean,
|
useCamera2Api?: boolean,
|
||||||
};
|
};
|
||||||
@ -122,6 +139,7 @@ export default class Camera extends React.Component<PropsType> {
|
|||||||
onCameraReady: PropTypes.func,
|
onCameraReady: PropTypes.func,
|
||||||
onBarCodeRead: PropTypes.func,
|
onBarCodeRead: PropTypes.func,
|
||||||
onFacesDetected: PropTypes.func,
|
onFacesDetected: PropTypes.func,
|
||||||
|
onTextRecognized: PropTypes.func,
|
||||||
faceDetectionMode: PropTypes.number,
|
faceDetectionMode: PropTypes.number,
|
||||||
faceDetectionLandmarks: PropTypes.number,
|
faceDetectionLandmarks: PropTypes.number,
|
||||||
faceDetectionClassifications: PropTypes.number,
|
faceDetectionClassifications: PropTypes.number,
|
||||||
@ -295,6 +313,7 @@ export default class Camera extends React.Component<PropsType> {
|
|||||||
onCameraReady={this._onCameraReady}
|
onCameraReady={this._onCameraReady}
|
||||||
onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)}
|
onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)}
|
||||||
onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
|
onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
|
||||||
|
onTextRecognized={this._onObjectDetected(this.props.onTextRecognized)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (!this.state.isAuthorizationChecked) {
|
} else if (!this.state.isAuthorizationChecked) {
|
||||||
@ -315,8 +334,13 @@ export default class Camera extends React.Component<PropsType> {
|
|||||||
newProps.faceDetectorEnabled = true;
|
newProps.faceDetectorEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.onTextRecognized) {
|
||||||
|
newProps.textRecognizerEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (Platform.OS === 'ios') {
|
if (Platform.OS === 'ios') {
|
||||||
delete newProps.ratio;
|
delete newProps.ratio;
|
||||||
|
delete newProps.textRecognizerEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
return newProps;
|
return newProps;
|
||||||
@ -340,6 +364,7 @@ const RNCamera = requireNativeComponent('RNCamera', Camera, {
|
|||||||
accessibilityLiveRegion: true,
|
accessibilityLiveRegion: true,
|
||||||
barCodeScannerEnabled: true,
|
barCodeScannerEnabled: true,
|
||||||
faceDetectorEnabled: true,
|
faceDetectorEnabled: true,
|
||||||
|
textRecognizerEnabled: true,
|
||||||
importantForAccessibility: true,
|
importantForAccessibility: true,
|
||||||
onBarCodeRead: true,
|
onBarCodeRead: true,
|
||||||
onCameraReady: true,
|
onCameraReady: true,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user