feat(ios): Add ability to scan inverted-color barcodes on iOS using Google Vision (#2851)
* Add required plumbing for supporiting inverted colors * Update package.json * Update package.json * Update RNCamera.js * Update package.json * Update RNCameraManager.m * Read barcode detection mode * Update package.json * Declare invertImageData as a property * Access the property correctly * Refer to the property correctly * Update RNCamera.m * Update RNCamera.md * Set default scan mode to Alternate * Update package.json * Set the default barcode detection mode to normal * Update package.json * Make the property type consistent * Update RNCamera.h * Update package.json * Rename variables for readability * Update package.json * Add barcode mode usage * Revert version update Co-authored-by: Manish Kumar <mkumar@acvauctions.com>
This commit is contained in:
parent
ee88b38e22
commit
fa61fce613
@ -20,7 +20,7 @@ class ExampleApp extends PureComponent {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<RNCamera
|
||||
ref={ref => {
|
||||
ref={(ref) => {
|
||||
this.camera = ref;
|
||||
}}
|
||||
style={styles.preview}
|
||||
@ -144,7 +144,7 @@ class ExampleApp extends PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
takePicture = async function(camera) {
|
||||
takePicture = async function (camera) {
|
||||
const options = { quality: 0.5, base64: true };
|
||||
const data = await camera.takePictureAsync(options);
|
||||
// eslint-disable-next-line
|
||||
@ -436,6 +436,7 @@ Function to be called when native code stops recording video, but before all vid
|
||||
Function to be called when a touch within the camera view is recognized.
|
||||
The function is also called on the first touch of double tap.
|
||||
Event will contain the following fields:
|
||||
|
||||
- `x`
|
||||
- `y`
|
||||
|
||||
@ -443,6 +444,7 @@ Event will contain the following fields:
|
||||
|
||||
Function to be called when a double touch within the camera view is recognized.
|
||||
Event will contain the following fields:
|
||||
|
||||
- `x`
|
||||
- `y`
|
||||
|
||||
@ -537,7 +539,7 @@ Available settings:
|
||||
- DATA_MATRIX
|
||||
- ALL
|
||||
|
||||
### `Android` `googleVisionBarcodeMode`
|
||||
### `googleVisionBarcodeMode`
|
||||
|
||||
Change the mode in order to scan "inverted" barcodes. You can either change it to `alternate`, which will inverted the image data every second screen and be able to read both normal and inverted barcodes, or `inverted`, which will only read inverted barcodes. Default is `normal`, which only reads "normal" barcodes. Note: this property only applies to the Google Vision barcode detector.
|
||||
Example: `<RNCamera googleVisionBarcodeMode={RNCamera.Constants.GoogleVisionBarcodeDetection.BarcodeMode.ALTERNATE} />`
|
||||
|
||||
@ -87,14 +87,14 @@ export default class CameraScreen extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
takePicture = async function() {
|
||||
takePicture = async function () {
|
||||
if (this.camera) {
|
||||
const data = await this.camera.takePictureAsync();
|
||||
console.warn('takePicture ', data);
|
||||
}
|
||||
};
|
||||
|
||||
takeVideo = async function() {
|
||||
takeVideo = async function () {
|
||||
if (this.camera) {
|
||||
try {
|
||||
const promise = this.camera.recordAsync(this.state.recordOptions);
|
||||
@ -111,7 +111,7 @@ export default class CameraScreen extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
toggle = value => () => this.setState(prevState => ({ [value]: !prevState[value] }));
|
||||
toggle = (value) => () => this.setState((prevState) => ({ [value]: !prevState[value] }));
|
||||
|
||||
facesDetected = ({ faces }) => this.setState({ faces });
|
||||
|
||||
@ -139,7 +139,7 @@ export default class CameraScreen extends React.Component {
|
||||
);
|
||||
|
||||
renderLandmarksOfFace(face) {
|
||||
const renderLandmark = position =>
|
||||
const renderLandmark = (position) =>
|
||||
position && (
|
||||
<View
|
||||
style={[
|
||||
@ -204,7 +204,7 @@ export default class CameraScreen extends React.Component {
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
textRecognized = object => {
|
||||
textRecognized = (object) => {
|
||||
const { textBlocks } = object;
|
||||
this.setState({ textBlocks });
|
||||
};
|
||||
@ -238,7 +238,7 @@ export default class CameraScreen extends React.Component {
|
||||
const { canDetectFaces, canDetectText, canDetectBarcode } = this.state;
|
||||
return (
|
||||
<RNCamera
|
||||
ref={ref => {
|
||||
ref={(ref) => {
|
||||
this.camera = ref;
|
||||
}}
|
||||
style={{
|
||||
@ -272,6 +272,7 @@ export default class CameraScreen extends React.Component {
|
||||
onTextRecognized={canDetectText ? this.textRecognized : null}
|
||||
onGoogleVisionBarcodesDetected={canDetectBarcode ? this.barcodeRecognized : null}
|
||||
googleVisionBarcodeType={RNCamera.Constants.GoogleVisionBarcodeDetection.BarcodeType.ALL}
|
||||
googleVisionBarcodeMode={RNCamera.Constants.GoogleVisionBarcodeMode.ALTERNATE}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
|
||||
@ -10,7 +10,9 @@ typedef void(^postRecognitionBlock)(NSArray *barcodes);
|
||||
- (instancetype)init;
|
||||
|
||||
-(BOOL)isRealDetector;
|
||||
-(NSInteger)fetchDetectionMode;
|
||||
-(void)setType:(id)json queue:(dispatch_queue_t)sessionQueue;
|
||||
-(void)setMode:(id)json queue:(dispatch_queue_t)sessionQueue;
|
||||
-(void)findBarcodesInFrame:(UIImage *)image scaleX:(float)scaleX scaleY:(float)scaleY completed:(postRecognitionBlock)completed;
|
||||
+(NSDictionary *)constants;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
@property(nonatomic, strong) FIRVisionBarcodeDetector *barcodeRecognizer;
|
||||
@property(nonatomic, strong) FIRVision *vision;
|
||||
@property(nonatomic, assign) FIRVisionBarcodeFormat setOption;
|
||||
@property(nonatomic, assign) NSInteger detectionMode;
|
||||
@property(nonatomic, assign) float scaleX;
|
||||
@property(nonatomic, assign) float scaleY;
|
||||
@end
|
||||
@ -26,6 +27,11 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
-(NSInteger)fetchDetectionMode
|
||||
{
|
||||
return self.detectionMode;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)constants
|
||||
{
|
||||
return @{
|
||||
@ -63,6 +69,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
-(void)setMode:(id)json queue:(dispatch_queue_t)sessionQueue
|
||||
{
|
||||
NSInteger requestedValue = [RCTConvert NSInteger:json];
|
||||
self.detectionMode = requestedValue;
|
||||
}
|
||||
|
||||
- (void)findBarcodesInFrame:(UIImage *)uiImage
|
||||
scaleX:(float)scaleX
|
||||
scaleY:(float)scaleY
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
@property(nonatomic, strong) NSArray *barCodeTypes;
|
||||
@property(nonatomic, strong) NSArray *googleVisionBarcodeTypes;
|
||||
|
||||
@property(nonatomic, assign) NSInteger *googleVisionBarcodeMode;
|
||||
@property(nonatomic, assign) NSInteger presetCamera;
|
||||
@property(nonatomic, copy) NSString *cameraId; // copy required for strings/pointers
|
||||
@property(assign, nonatomic) NSInteger flashMode;
|
||||
@ -76,6 +77,7 @@
|
||||
- (void)updateRectOfInterest;
|
||||
// google Barcode props
|
||||
- (void)updateGoogleVisionBarcodeType:(id)requestedTypes;
|
||||
- (void)updateGoogleVisionBarcodeMode:(id)requestedMode;
|
||||
|
||||
- (void)takePicture:(NSDictionary *)options
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onSubjectAreaChanged;
|
||||
@property (nonatomic, assign) BOOL isFocusedOnPoint;
|
||||
@property (nonatomic, assign) BOOL isExposedOnPoint;
|
||||
@property (nonatomic, assign) BOOL invertImageData;
|
||||
|
||||
@end
|
||||
|
||||
@ -89,6 +90,7 @@ BOOL _sessionInterrupted = NO;
|
||||
self.cameraId = nil;
|
||||
self.isFocusedOnPoint = NO;
|
||||
self.isExposedOnPoint = NO;
|
||||
self.invertImageData = false;
|
||||
_recordRequested = NO;
|
||||
_sessionInterrupted = NO;
|
||||
|
||||
@ -2175,6 +2177,11 @@ BOOL _sessionInterrupted = NO;
|
||||
[self.barcodeDetector setType:requestedTypes queue:self.sessionQueue];
|
||||
}
|
||||
|
||||
- (void)updateGoogleVisionBarcodeMode:(id)requestedMode
|
||||
{
|
||||
[self.barcodeDetector setMode:requestedMode queue:self.sessionQueue];
|
||||
}
|
||||
|
||||
- (void)onBarcodesDetected:(NSDictionary *)event
|
||||
{
|
||||
if (_onGoogleVisionBarcodesDetected && _session) {
|
||||
@ -2275,6 +2282,27 @@ BOOL _sessionInterrupted = NO;
|
||||
if (canSubmitForBarcodeDetection) {
|
||||
_finishedDetectingBarcodes = false;
|
||||
self.startBarcode = [NSDate date];
|
||||
|
||||
// Check for the barcode detection mode (Normal, Alternate, Inverted)
|
||||
switch ([self.barcodeDetector fetchDetectionMode]) {
|
||||
case RNCameraGoogleVisionBarcodeModeNormal:
|
||||
self.invertImageData = false;
|
||||
break;
|
||||
case RNCameraGoogleVisionBarcodeModeAlternate:
|
||||
self.invertImageData = !self.invertImageData;
|
||||
break;
|
||||
case RNCameraGoogleVisionBarcodeModeInverted:
|
||||
self.invertImageData = true;
|
||||
break;
|
||||
default:
|
||||
self.invertImageData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (self.invertImageData) {
|
||||
image = [RNImageUtils invertColors:image];
|
||||
}
|
||||
|
||||
[self.barcodeDetector findBarcodesInFrame:image scaleX:scaleX scaleY:scaleY completed:^(NSArray * barcodes) {
|
||||
NSDictionary *eventBarcode = @{@"type" : @"barcode", @"barcodes" : barcodes};
|
||||
[self onBarcodesDetected:eventBarcode];
|
||||
|
||||
@ -55,6 +55,12 @@ typedef NS_ENUM(NSInteger, RNCameraVideoResolution) {
|
||||
RNCameraVideo288p = 4,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNCameraGoogleVisionBarcodeMode) {
|
||||
RNCameraGoogleVisionBarcodeModeNormal = 0,
|
||||
RNCameraGoogleVisionBarcodeModeAlternate = 1,
|
||||
RNCameraGoogleVisionBarcodeModeInverted = 2,
|
||||
};
|
||||
|
||||
@interface RNCameraManager : RCTViewManager <RCTBridgeModule>
|
||||
|
||||
+ (NSDictionary *)validBarCodeTypes;
|
||||
|
||||
@ -81,7 +81,12 @@ RCT_EXPORT_VIEW_PROPERTY(onTouch, RCTDirectEventBlock);
|
||||
@"VideoStabilization": [[self class] validVideoStabilizationModes],
|
||||
@"GoogleVisionBarcodeDetection": @{
|
||||
@"BarcodeType": [[self class] barcodeDetectorConstants],
|
||||
}
|
||||
},
|
||||
@"GoogleVisionBarcodeMode" : @{
|
||||
@"NORMAL" : @(RNCameraGoogleVisionBarcodeModeNormal),
|
||||
@"ALTERNATE" : @(RNCameraGoogleVisionBarcodeModeAlternate),
|
||||
@"INVERTED" : @(RNCameraGoogleVisionBarcodeModeInverted),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -297,6 +302,11 @@ RCT_CUSTOM_VIEW_PROPERTY(googleVisionBarcodeType, NSString, RNCamera)
|
||||
[view updateGoogleVisionBarcodeType:json];
|
||||
}
|
||||
|
||||
RCT_CUSTOM_VIEW_PROPERTY(googleVisionBarcodeMode, NSInteger, RNCamera)
|
||||
{
|
||||
[view updateGoogleVisionBarcodeMode:json];
|
||||
}
|
||||
|
||||
RCT_CUSTOM_VIEW_PROPERTY(googleVisionBarcodeDetectorEnabled, BOOL, RNCamera)
|
||||
{
|
||||
view.canDetectBarcodes = [RCTConvert BOOL:json];
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
+ (NSString *)writeImage:(NSData *)image toPath:(NSString *)path;
|
||||
+ (UIImage *) scaleImage:(UIImage*)image toWidth:(NSInteger)width;
|
||||
+ (void)updatePhotoMetadata:(CMSampleBufferRef)imageSampleBuffer withAdditionalData:(NSDictionary *)additionalData inResponse:(NSMutableDictionary *)response;
|
||||
+ (UIImage *)invertColors:(UIImage *)image;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -115,5 +115,23 @@
|
||||
response[@"exif"] = metadata;
|
||||
}
|
||||
|
||||
+ (UIImage *)invertColors:(UIImage *)image
|
||||
{
|
||||
CIImage *inputCIImage = [[CIImage alloc] initWithImage:image];
|
||||
|
||||
// Invert colors
|
||||
CIFilter *filterColorInvert = [CIFilter filterWithName:@"CIColorInvert"];
|
||||
[filterColorInvert setValue:inputCIImage forKey:kCIInputImageKey];
|
||||
CIImage *outputCIImage = [filterColorInvert valueForKey:kCIOutputImageKey];
|
||||
|
||||
// A UIImage initialized directly from CIImage has its CGImage property set to NULL. So it has
|
||||
// to be converted to a CGImage first.
|
||||
CIContext *context = [CIContext context];
|
||||
CGImageRef outputCGImage = [context createCGImage:outputCIImage fromRect:[outputCIImage extent]];
|
||||
|
||||
UIImage *outputUIImage = [UIImage imageWithCGImage:outputCGImage];
|
||||
return outputUIImage;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0314E39D1B661A460092D183 /* CameraFocusSquare.m in Sources */ = {isa = PBXBuildFile; fileRef = 0314E39C1B661A460092D183 /* CameraFocusSquare.m */; };
|
||||
1354413B2487FF390024C19F /* BarcodeDetectorManagerMlkit.m in Sources */ = {isa = PBXBuildFile; fileRef = 1354413A2487FF390024C19F /* BarcodeDetectorManagerMlkit.m */; };
|
||||
2647D6712256BBD5007D2F91 /* FaceDetectorManagerMlkit.m in Sources */ = {isa = PBXBuildFile; fileRef = 2647D6702256BBD5007D2F91 /* FaceDetectorManagerMlkit.m */; };
|
||||
2647D6742256BBE8007D2F91 /* RNFaceDetectorModuleMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 2647D6722256BBE8007D2F91 /* RNFaceDetectorModuleMLKit.m */; };
|
||||
4107014D1ACB732B00C6AA39 /* RCTCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = 410701481ACB732B00C6AA39 /* RCTCamera.m */; };
|
||||
@ -37,6 +38,8 @@
|
||||
/* Begin PBXFileReference section */
|
||||
0314E39B1B661A0C0092D183 /* CameraFocusSquare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraFocusSquare.h; sourceTree = "<group>"; };
|
||||
0314E39C1B661A460092D183 /* CameraFocusSquare.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraFocusSquare.m; sourceTree = "<group>"; };
|
||||
135441392487FF390024C19F /* BarcodeDetectorManagerMlkit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BarcodeDetectorManagerMlkit.h; sourceTree = "<group>"; };
|
||||
1354413A2487FF390024C19F /* BarcodeDetectorManagerMlkit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BarcodeDetectorManagerMlkit.m; sourceTree = "<group>"; };
|
||||
2647D66F2256BBD5007D2F91 /* FaceDetectorManagerMlkit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FaceDetectorManagerMlkit.h; sourceTree = "<group>"; };
|
||||
2647D6702256BBD5007D2F91 /* FaceDetectorManagerMlkit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FaceDetectorManagerMlkit.m; sourceTree = "<group>"; };
|
||||
2647D6722256BBE8007D2F91 /* RNFaceDetectorModuleMLKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFaceDetectorModuleMLKit.m; sourceTree = "<group>"; };
|
||||
@ -112,6 +115,8 @@
|
||||
714166162013E1B600EE9FCC /* RN */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
135441392487FF390024C19F /* BarcodeDetectorManagerMlkit.h */,
|
||||
1354413A2487FF390024C19F /* BarcodeDetectorManagerMlkit.m */,
|
||||
F8393BEA21469C0000AB1995 /* RNSensorOrientationChecker.h */,
|
||||
F8393BEB21469C0000AB1995 /* RNSensorOrientationChecker.m */,
|
||||
2647D66F2256BBD5007D2F91 /* FaceDetectorManagerMlkit.h */,
|
||||
@ -194,6 +199,7 @@
|
||||
71C7FFD62013C824006EB75A /* RNFileSystem.m in Sources */,
|
||||
4107014E1ACB732B00C6AA39 /* RCTCameraManager.m in Sources */,
|
||||
4107014D1ACB732B00C6AA39 /* RCTCamera.m in Sources */,
|
||||
1354413B2487FF390024C19F /* BarcodeDetectorManagerMlkit.m in Sources */,
|
||||
F8393BEC21469C0000AB1995 /* RNSensorOrientationChecker.m in Sources */,
|
||||
71C7FFD02013C7E5006EB75A /* RNCameraUtils.m in Sources */,
|
||||
7162BE682013EAA400FE51FF /* RNCameraManager.m in Sources */,
|
||||
|
||||
@ -247,7 +247,7 @@ type Rect = {
|
||||
|
||||
type PropsType = typeof View.props & {
|
||||
zoom?: number,
|
||||
useNativeZoom?:boolean,
|
||||
useNativeZoom?: boolean,
|
||||
maxZoom?: number,
|
||||
ratio?: string,
|
||||
focusDepth?: number,
|
||||
@ -362,6 +362,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
VideoCodec: CameraManager.VideoCodec,
|
||||
BarCodeType: CameraManager.BarCodeType,
|
||||
GoogleVisionBarcodeDetection: CameraManager.GoogleVisionBarcodeDetection,
|
||||
GoogleVisionBarcodeMode: CameraManager.GoogleVisionBarcodeMode,
|
||||
FaceDetection: CameraManager.FaceDetection,
|
||||
CameraStatus,
|
||||
RecordAudioPermissionStatus: RecordAudioPermissionStatusEnum,
|
||||
@ -379,6 +380,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
static ConversionTables = {
|
||||
type: CameraManager.Type,
|
||||
flashMode: CameraManager.FlashMode,
|
||||
googleVisionBarcodeMode: CameraManager.GoogleVisionBarcodeMode,
|
||||
exposure: CameraManager.Exposure,
|
||||
autoFocus: CameraManager.AutoFocus,
|
||||
whiteBalance: CameraManager.WhiteBalance,
|
||||
@ -392,7 +394,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
static propTypes = {
|
||||
...ViewPropTypes,
|
||||
zoom: PropTypes.number,
|
||||
useNativeZoom:PropTypes.bool,
|
||||
useNativeZoom: PropTypes.bool,
|
||||
maxZoom: PropTypes.number,
|
||||
ratio: PropTypes.string,
|
||||
focusDepth: PropTypes.number,
|
||||
@ -445,7 +447,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
|
||||
static defaultProps: Object = {
|
||||
zoom: 0,
|
||||
useNativeZoom:false,
|
||||
useNativeZoom: false,
|
||||
maxZoom: 0,
|
||||
ratio: '4:3',
|
||||
focusDepth: 0,
|
||||
@ -651,7 +653,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
if (this.props.onTap && !nativeEvent.isDoubleTap) {
|
||||
this.props.onTap(nativeEvent.touchOrigin);
|
||||
}
|
||||
if (this.props.onDoubleTap && nativeEvent.isDoubleTap){
|
||||
if (this.props.onDoubleTap && nativeEvent.isDoubleTap) {
|
||||
this.props.onTap(nativeEvent.touchOrigin);
|
||||
}
|
||||
};
|
||||
@ -694,7 +696,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
}
|
||||
};
|
||||
|
||||
_onSubjectAreaChanged = e => {
|
||||
_onSubjectAreaChanged = (e) => {
|
||||
if (this.props.onSubjectAreaChanged) {
|
||||
this.props.onSubjectAreaChanged(e);
|
||||
}
|
||||
@ -869,7 +871,6 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
}
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
delete newProps.googleVisionBarcodeMode;
|
||||
delete newProps.ratio;
|
||||
}
|
||||
|
||||
@ -893,7 +894,7 @@ const RNCamera = requireNativeComponent('RNCamera', Camera, {
|
||||
accessibilityLabel: true,
|
||||
accessibilityLiveRegion: true,
|
||||
barCodeScannerEnabled: true,
|
||||
touchDetectorEnabled:true,
|
||||
touchDetectorEnabled: true,
|
||||
googleVisionBarcodeDetectorEnabled: true,
|
||||
faceDetectorEnabled: true,
|
||||
textRecognizerEnabled: true,
|
||||
@ -905,7 +906,7 @@ const RNCamera = requireNativeComponent('RNCamera', Camera, {
|
||||
onAudioConnected: true,
|
||||
onPictureSaved: true,
|
||||
onFaceDetected: true,
|
||||
onTouch:true,
|
||||
onTouch: true,
|
||||
onLayout: true,
|
||||
onMountError: true,
|
||||
onSubjectAreaChanged: true,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user