Commit 51c1fd9c authored by redatawfik's avatar redatawfik Committed by Commit Bot

Create CreditCardScannerImageProcessor.

- Create class "CreditCardScannerImageProcessor" which handles
  processing a credit card image and call the consumer with the
  results.
- Move all text recognition logic from mediator to
  "CreditCardScannerImageProcessor".

Bug: 984545
Change-Id: Ifbdb1b6fbc22804ba969c2c2dec89fadbaa095d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1783443
Commit-Queue: Reda Tawfik <redatawfik@google.com>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Reviewed-by: default avatarJavier Ernesto Flores Robles <javierrobles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695580}
parent ac10e900
......@@ -12,11 +12,13 @@ source_set("credit_card_scanner") {
"credit_card_scanner_camera_controller_delegate.h",
"credit_card_scanner_coordinator.h",
"credit_card_scanner_coordinator.mm",
"credit_card_scanner_image_processor.h",
"credit_card_scanner_image_processor.mm",
"credit_card_scanner_mediator.h",
"credit_card_scanner_mediator.mm",
"credit_card_scanner_mediator_delegate.h",
"credit_card_scanner_mediator_util.h",
"credit_card_scanner_mediator_util.mm",
"credit_card_scanner_string_util.h",
"credit_card_scanner_string_util.mm",
"credit_card_scanner_view.h",
"credit_card_scanner_view.mm",
"credit_card_scanner_view_controller.h",
......@@ -42,7 +44,7 @@ source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [
"credit_card_scanner_mediator_util_unittest.mm",
"credit_card_scanner_string_util_unittest.mm",
]
deps = [
"//ios/chrome/browser/ui/settings/credit_card_scanner",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_IMAGE_PROCESSOR_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_IMAGE_PROCESSOR_H_
#import <Foundation/Foundation.h>
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanned_image_delegate.h"
@protocol CreditCardConsumer;
// A class process credit card images to recognise the text.
API_AVAILABLE(ios(13.0))
@interface CreditCardScannerImageProcessor
: NSObject <CreditCardScannedImageDelegate>
// Initializes with Credit Card consumer.
- (instancetype)initWithConsumer:(id<CreditCardConsumer>)creditCardConsumer
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_IMAGE_PROCESSOR_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_image_processor.h"
#import <CoreMedia/CoreMedia.h>
#import <Vision/Vision.h>
#include "base/logging.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_consumer.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_string_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface CreditCardScannerImageProcessor ()
// An image analysis request that finds and recognizes text in an image.
@property(nonatomic, strong) VNRecognizeTextRequest* textRecognitionRequest;
// This property is for an interface which notfies the credit card consumer.
@property(nonatomic, weak) id<CreditCardConsumer> creditCardConsumer;
// The card number set after |textRecognitionRequest| from recognised text on
// the card.
@property(nonatomic, strong) NSString* cardNumber;
// The card expiration month set after |textRecognitionRequest| from recognised
// text on the card.
@property(nonatomic, strong) NSString* expirationMonth;
// The card expiration year set after |textRecognitionRequest| from recognised
// text on the card.
@property(nonatomic, strong) NSString* expirationYear;
@end
@implementation CreditCardScannerImageProcessor
#pragma mark - Lifecycle
- (instancetype)initWithConsumer:(id<CreditCardConsumer>)creditCardConsumer {
self = [super init];
if (self) {
_creditCardConsumer = creditCardConsumer;
}
return self;
}
#pragma mark - CreditCardScannerImageDelegate
- (void)processOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
viewport:(CGRect)viewport {
// Current thread is unknown background thread as is a callback from UIKit.
DCHECK(!NSThread.isMainThread);
if (!self.textRecognitionRequest) {
__weak __typeof(self) weakSelf = self;
auto completionHandler = ^(VNRequest* request, NSError* error) {
if (request.results.count != 0) {
[weakSelf searchInRecognizedText:request.results];
}
};
self.textRecognitionRequest = [[VNRecognizeTextRequest alloc]
initWithCompletionHandler:completionHandler];
// Sets the region of interest of the request to the scanner viewport to
// focus on the scan area. This improves performance by ignoring irrelevant
// background text.
self.textRecognitionRequest.regionOfInterest = viewport;
// Fast option doesn't recognise card number correctly.
self.textRecognitionRequest.recognitionLevel =
VNRequestTextRecognitionLevelAccurate;
// For time performance as we scan for numbers and date only.
self.textRecognitionRequest.usesLanguageCorrection = false;
}
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
DCHECK(pixelBuffer);
NSMutableDictionary* options = [[NSMutableDictionary alloc] init];
VNImageRequestHandler* handler =
[[VNImageRequestHandler alloc] initWithCVPixelBuffer:pixelBuffer
options:options];
NSError* requestError;
[handler performRequests:@[ self.textRecognitionRequest ]
error:&requestError];
// Error code 11 is unknown exception. It happens for some frames.
DCHECK(!requestError || requestError.code == 11);
}
#pragma mark - Helper Methods
// Searches in |recognizedText| for credit card number and expiration date.
- (void)searchInRecognizedText:
(NSArray<VNRecognizedTextObservation*>*)recognizedText {
// Current thread is unknown background thread as is a callback from UIKit.
DCHECK(!NSThread.isMainThread);
NSUInteger maximumCandidates = 1;
for (VNRecognizedTextObservation* observation in recognizedText) {
VNRecognizedText* candidate =
[[observation topCandidates:maximumCandidates] firstObject];
if (candidate == nil) {
continue;
}
[self extractDataFromText:candidate.string];
}
if (self.cardNumber) {
// Send the result to the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[self.creditCardConsumer setCreditCardNumber:self.cardNumber
expirationMonth:self.expirationMonth
expirationYear:self.expirationYear];
});
}
}
// Checks the type of |text| to assign it to appropriate property.
- (void)extractDataFromText:(NSString*)text {
if (!self.expirationMonth || !self.expirationYear) {
NSDateComponents* components = ios::ExtractExpirationDateFromText(text);
if (components) {
self.expirationMonth = [@([components month]) stringValue];
self.expirationYear = [@([components year]) stringValue];
}
}
if (!self.cardNumber) {
self.cardNumber = ios::ExtractCreditCardNumber(text);
}
}
@end
......@@ -7,14 +7,15 @@
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_consumer.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanned_image_delegate.h"
@protocol CreditCardConsumer;
@protocol CreditCardScannerMediatorDelegate;
// A mediator for CreditCardScanner which manages processing images.
API_AVAILABLE(ios(13.0))
@interface CreditCardScannerMediator : NSObject <CreditCardScannedImageDelegate>
@interface CreditCardScannerMediator
: NSObject <CreditCardConsumer, CreditCardScannedImageDelegate>
// Initializes with Credit Card mediator delegate and Credit Card consumer.
- (instancetype)initWithDelegate:(id<CreditCardScannerMediatorDelegate>)
......
......@@ -4,15 +4,10 @@
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_mediator.h"
#import <CoreMedia/CoreMedia.h>
#import <Vision/Vision.h>
#include "base/logging.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_consumer.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_image_processor.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_mediator_delegate.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_mediator_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
......@@ -22,9 +17,6 @@ using base::UserMetricsAction;
@interface CreditCardScannerMediator ()
// An image analysis request that finds and recognizes text in an image.
@property(nonatomic, strong) VNRecognizeTextRequest* textRecognitionRequest;
// Delegate notified when a card has been scanned.
@property(nonatomic, weak) id<CreditCardScannerMediatorDelegate>
creditCardScannerMediatorDelegate;
......@@ -44,6 +36,10 @@ using base::UserMetricsAction;
// text on the card.
@property(nonatomic, strong) NSString* expirationYear;
// Object to Perform image processing and return the text on the image.
@property(nonatomic, strong)
CreditCardScannerImageProcessor* creditCardImageScanner;
@end
@implementation CreditCardScannerMediator
......@@ -57,7 +53,10 @@ using base::UserMetricsAction;
if (self) {
_creditCardScannerMediatorDelegate = creditCardScannerMediatorDelegate;
_creditCardConsumer = creditCardConsumer;
_creditCardImageScanner =
[[CreditCardScannerImageProcessor alloc] initWithConsumer:self];
}
return self;
}
......@@ -65,46 +64,22 @@ using base::UserMetricsAction;
- (void)processOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
viewport:(CGRect)viewport {
if (!self.textRecognitionRequest) {
__weak __typeof(self) weakSelf = self;
auto completionHandler = ^(VNRequest* request, NSError* error) {
if (request.results.count != 0) {
[weakSelf searchInRecognizedText:request.results];
if (self.cardNumber) {
[self dismissScannerOnCardScanned];
}
}
};
self.textRecognitionRequest = [[VNRecognizeTextRequest alloc]
initWithCompletionHandler:completionHandler];
// Sets the region of interest of the request to the scanner viewport to
// focus on the scan area. This improves performance by ignoring irrelevant
// background text.
self.textRecognitionRequest.regionOfInterest = viewport;
// Fast option doesn't recognise card number correctly.
self.textRecognitionRequest.recognitionLevel =
VNRequestTextRecognitionLevelAccurate;
// For time performance as we scan for numbers and date only.
self.textRecognitionRequest.usesLanguageCorrection = false;
}
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
DCHECK(pixelBuffer);
NSMutableDictionary* options = [[NSMutableDictionary alloc] init];
VNImageRequestHandler* handler =
[[VNImageRequestHandler alloc] initWithCVPixelBuffer:pixelBuffer
options:options];
// Current thread is unknown background thread as is a callback from UIKit.
DCHECK(!NSThread.isMainThread);
[self.creditCardImageScanner processOutputSampleBuffer:sampleBuffer
viewport:viewport];
}
NSError* requestError;
[handler performRequests:@[ self.textRecognitionRequest ]
error:&requestError];
#pragma mark - CreditCardConsumer
// Error code 11 is unknown exception. It happens for some frames.
DCHECK(!requestError || requestError.code == 11);
- (void)setCreditCardNumber:(NSString*)cardNumber
expirationMonth:(NSString*)expirationMonth
expirationYear:(NSString*)expirationYear {
[self.creditCardConsumer setCreditCardNumber:cardNumber
expirationMonth:expirationMonth
expirationYear:expirationYear];
self.creditCardImageScanner = nil;
[self dismissScannerOnCardScanned];
}
#pragma mark - Helper Methods
......@@ -116,40 +91,4 @@ using base::UserMetricsAction;
creditCardScannerMediatorDidFinishScan:self];
}
// Searches in |recognizedText| for credit card number and expiration date.
- (void)searchInRecognizedText:
(NSArray<VNRecognizedTextObservation*>*)recognizedText {
NSUInteger maximumCandidates = 1;
for (VNRecognizedTextObservation* observation in recognizedText) {
VNRecognizedText* candidate =
[[observation topCandidates:maximumCandidates] firstObject];
if (candidate == nil) {
continue;
}
[self extractDataFromText:candidate.string];
}
if (self.cardNumber) {
[self.creditCardConsumer setCreditCardNumber:self.cardNumber
expirationMonth:self.expirationMonth
expirationYear:self.expirationYear];
[self.creditCardScannerMediatorDelegate
creditCardScannerMediatorDidFinishScan:self];
}
}
// Checks the type of |text| to assign it to appropriate property.
- (void)extractDataFromText:(NSString*)text {
NSDateComponents* components = ios::ExtractExpirationDateFromText(text);
if (components) {
self.expirationMonth = [@([components month]) stringValue];
self.expirationYear = [@([components year]) stringValue];
}
if (!self.cardNumber) {
self.cardNumber = ios::ExtractCreditCardNumber(text);
}
}
@end
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_MEDIATOR_UTIL_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_MEDIATOR_UTIL_H_
#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_STRING_UTIL_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_STRING_UTIL_H_
#import <Foundation/Foundation.h>
......@@ -21,4 +21,4 @@ NSString* SubstituteSimilarCharactersInRecognizedText(NSString* recognizedText);
} // namespace ios
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_MEDIATOR_UTIL_H_
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_CREDIT_CARD_SCANNER_CREDIT_CARD_SCANNER_STRING_UTIL_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_mediator_util.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_string_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_mediator_util.h"
#import "ios/chrome/browser/ui/settings/credit_card_scanner/credit_card_scanner_string_util.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
......@@ -11,12 +11,12 @@
#error "This file requires ARC support."
#endif
using CreditCardScannerMediatorUtilTest = PlatformTest;
using CreditCardScannerStringUtilUnitTest = PlatformTest;
#pragma mark - Test ExtractExpirationDateFromText
// Tests extracting month and year from valid date text.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractExpirationDateFromValidDateText) {
NSDateComponents* components = ios::ExtractExpirationDateFromText(@"10/25");
......@@ -25,7 +25,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests extracting month and year from invalid date text.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractExpirationDateFromInvalidDateText) {
NSDateComponents* components = ios::ExtractExpirationDateFromText(@"13/888");
......@@ -33,7 +33,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests extracting month and year from invalid text.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractExpirationDateFromInvalidText) {
NSDateComponents* components = ios::ExtractExpirationDateFromText(@"aaaaa");
......@@ -41,7 +41,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests extracting month and year from invalid text with correct format.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractExpirationDateFromInvalidFormattedText) {
NSDateComponents* components = ios::ExtractExpirationDateFromText(@"aa/aa");
......@@ -51,7 +51,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
#pragma mark - Test ExtractCreditCardNumber
// Tests extracting card number from valid card number text (16 digits).
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractCardNumberFromValidCreditCardNumber16Digits) {
NSString* cardNumber = ios::ExtractCreditCardNumber(@"4111111111111111");
......@@ -59,7 +59,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests extracting card number from valid card number text (14 digits).
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractCardNumberFromValidCreditCardNumber14Digits) {
NSString* cardNumber = ios::ExtractCreditCardNumber(@"4111111111111");
......@@ -68,7 +68,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
// Tests extracting card number from valid card number text contains wrong
// characters.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractCardNumberFromValidCreditCardNumberWithWrongCharacters) {
NSString* cardNumber = ios::ExtractCreditCardNumber(@"41/11-1111 1111.11:11");
......@@ -77,7 +77,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
// Tests extracting card number from text after converting
// illegal characters.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractCardNumberFromValidCreditCardNumberAfterConversion) {
NSString* cardNumber = ios::ExtractCreditCardNumber(@"41b1C1g1D1i1L1z1");
......@@ -85,7 +85,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests extracting card number from invalid card number text (10 digits).
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractCardNumberFromInvalidCreditCardNumber10Digits) {
NSString* cardNumber = ios::ExtractCreditCardNumber(@"4111111111");
......@@ -93,7 +93,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests extracting card number from invalid card number text.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestExtractCardNumberFromInvalidCreditCardNumber) {
NSString* cardNumber = ios::ExtractCreditCardNumber(@"4111a11b11c11");
......@@ -103,7 +103,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
#pragma mark - Test SubstituteSimilarCharactersInRecognizedText
// Tests substituting convertible characters with digits.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestSubstitutingTrueCharactersWithDigits) {
NSString* number =
ios::SubstituteSimilarCharactersInRecognizedText(@"bCdGiLoQsTuZ");
......@@ -112,7 +112,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests substituting text without characters with digits.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestSubstitutingTextWithoutCharacters) {
NSString* number =
ios::SubstituteSimilarCharactersInRecognizedText(@"4111111111111111");
......@@ -121,7 +121,7 @@ TEST_F(CreditCardScannerMediatorUtilTest,
}
// Tests substituting inconvertible characters with digits.
TEST_F(CreditCardScannerMediatorUtilTest,
TEST_F(CreditCardScannerStringUtilUnitTest,
TestSubstitutingFalseCharactersWithDigits) {
NSString* cardNumber =
ios::SubstituteSimilarCharactersInRecognizedText(@"abcdefghi");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment