Demos and Sample Code
AnylineOCR Module
AUTO: Scrabble1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | {
"captureResolution":"720",
"cutout": {
"style": "rect",
"maxWidthPercent": "80%",
"maxHeightPercent": "80%",
"alignment": "center",
"width": 600,
"ratioFromSize": {
"width": 4,
"height": 1
},
"strokeWidth": 2,
"cornerRadius": 4,
"strokeColor": "FFFFFF",
"outerColor": "000000",
"outerAlpha": 0.3
},
"flash": {
"mode": "manual",
"alignment": "bottom_right"
},
"beepOnResult": true,
"vibrateOnResult": true,
"blinkAnimationOnResult": true,
"cancelOnResult": true,
"visualFeedback": {
"style": "rect",
"strokeColor": "0099FF",
"strokeWidth": 4
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | import android.content.Intent;
import android.graphics.PointF;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import java.util.List;
import at.nineyards.anyline.camera.AnylineViewConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrResult;
import at.nineyards.anyline.modules.ocr.AnylineOcrResultListener;
import at.nineyards.anyline.modules.ocr.AnylineOcrScanView;
import io.anyline.examples.R;
import io.anyline.examples.SettingsFragment;
import io.anyline.examples.ocr.apis.AnagramActivity;
public class ScanScrabbleActivity extends AppCompatActivity {
private static final String TAG = ScanScrabbleActivity.class.getSimpleName();
private AnylineOcrScanView scanView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_anyline_ocr);
String lic = getString(R.string.anyline_license_key);
// Get the view from the layout
scanView = (AnylineOcrScanView) findViewById(R.id.scan_view);
// Copies given traineddata-file to a place where the core can access it.
// This MUST be called for every traineddata file that is used (before startScanning() is called).
// The file must be located directly in the assets directory (or in tessdata/ but no other folders are allowed)
scanView.copyTrainedData("tessdata/scrabble.traineddata", "855d8088928ee058257f64ccac2837eb");
//Configure the OCR for Scrabble
AnylineOcrConfig anylineOcrConfig = new AnylineOcrConfig();
// use the AUTO mode which automatically adjusts the scanning parameters to deliver the best result
anylineOcrConfig.setScanMode(AnylineOcrConfig.ScanMode.AUTO);
// set the languages used for OCR
anylineOcrConfig.setTesseractLanguages("scrabble");
// allow only capital letters plus some german Umlaute
anylineOcrConfig.setCharWhitelist("ABCDEFGHIJKLMNOPQRSTUVWXYZÄÜÖ");
// the minimum confidence required to return a result, a value between 0 and 100.
// (higher confidence means less likely to get a wrong result, but may be slower to get a result)
anylineOcrConfig.setMinConfidence(80);
// a simple regex for a basic validation of the scrabble characters. We require at least 4 characters, and a maximum of 10 (usually it would be 7, but Umlaute may use two ASCII symbols)
anylineOcrConfig.setValidationRegex("^[A-ZÄÜÖ]{7,10}$");
// set the ocr config
scanView.setAnylineOcrConfig(anylineOcrConfig);
// Configure the view (cutout, the camera resolution, etc.) via json (can also be done in xml in the layout)
scanView.setConfig(new AnylineViewConfig(this, "scrabble_view_config.json"));
scanView.initAnyline(lic, new AnylineOcrResultListener() {
@Override
public void onResult(AnylineOcrResult anylineOcrResult) {
// Called when a valid result is found (minimum confidence is exceeded and validation with regex was ok)
if (!anylineOcrResult.getResult().isEmpty()) {
Intent i = new Intent(ScanScrabbleActivity.this, AnagramActivity.class);
i.putExtra(AnagramActivity.SCRABBLE_INPUT, anylineOcrResult.getResult().trim());
startActivity(i);
}
}
});
// disable the reporting if set to off in preferences
scanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
.KEY_PREF_REPORTING_ON, true));
}
@Override
protected void onResume() {
super.onResume();
//we use a postdelay for 'start scanning' to improve the user experience:
//otherwise the scrabble result would be shown faster as the user realizes the scanning
//process has already started
scanView.postDelayed(new Runnable() {
@Override
public void run() {
if (!isFinishing()) {
scanView.startScanning();
}
}
}, 1500);
}
@Override
protected void onPause() {
super.onPause();
scanView.cancelScanning();
scanView.releaseCameraInBackground();
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | #import "ALScrabbleScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALScrabbleViewController.h"
#import "ALIntroHelpView.h"
#import "ALCustomBarButton.h"
#import "ScanHistory.h"
#import "NSUserDefaults+ALExamplesAdditions.h"
// This is the license key for the examples project used to set up Aynline below
NSString * const kScrabbleLicenseKey = @"";
// The controller has to conform to <AnylineOCRModuleDelegate> to be able to receive results
@interface ALScrabbleScanViewController ()<AnylineOCRModuleDelegate,ALIntroHelpViewDelegate>
// The Anyline module used for OCR
@property (nonatomic, strong) AnylineOCRModuleView *ocrModuleView;
@end
@implementation ALScrabbleScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Scrabble";
self.controllerType = ALScanHistoryScrabble;
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
// Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
self.ocrModuleView = [[AnylineOCRModuleView alloc] initWithFrame:frame];
ALOCRConfig *config = [[ALOCRConfig alloc] init];
config.scanMode = ALAuto;
config.tesseractLanguages = @[@"scrabble"];
config.charWhiteList = @"ABCDEFGHIJKLMNOPQRSTUVWXYZÄÜÖ";
config.validationRegex = @"^[A-ZÄÜÖ]{7,10}$";
config.minConfidence = 80;
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// by the module once we start receiving results.
BOOL success = [self.ocrModuleView setupWithLicenseKey:kScrabbleLicenseKey
delegate:self
ocrConfig:config
error:&error];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if (!success) {
// Something went wrong. The error object contains the error description
NSAssert(success, @"Setup Error: %@", error.debugDescription);
}
[self.ocrModuleView enableReporting:YES];
NSString *confPath = [[NSBundle mainBundle] pathForResource:@"scrabble_config" ofType:@"json"];
ALUIConfiguration *scrabbleConf = [ALUIConfiguration cutoutConfigurationFromJsonFile:confPath];
self.ocrModuleView.currentConfiguration = scrabbleConf;
self.controllerType = ALScanHistoryScrabble;
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.ocrModuleView];
[self.view sendSubviewToBack:self.ocrModuleView];
[self startListeningForMotion];
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSError *error;
BOOL success = [self.ocrModuleView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
}
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.ocrModuleView cancelScanningAndReturnError:nil];
}
- (void)viewDidLayoutSubviews {
[self updateWarningPosition:
self.ocrModuleView.cutoutRect.origin.y +
self.ocrModuleView.cutoutRect.size.height +
self.ocrModuleView.frame.origin.y +
90];
}
/*
This method is used to tell Anyline to start scanning. It gets called in
viewDidAppear to start scanning the moment the view appears. Once a result
is found scanning will stop automatically (you can change this behaviour
with cancelOnResult:). When the user dismisses self.identificationView this
method will get called again.
*/
- (void)startAnyline {
NSError *error;
BOOL success = [self.ocrModuleView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
}
self.startTime = CACurrentMediaTime();
}
#pragma mark -- AnylineOCRModuleDelegate
/*
This is the main delegate method Anyline uses to report its results
*/
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
didFindResult:(ALOCRResult *)result {
NSLog(@" result: %@", result.result);
NSLog(@" result image: %@", result.image);
}
/*
Deprecated since 3.10.0
*/
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
didFindResult:(ALOCRResult *)result {
NSLog(@" result: %@", result.text);
NSLog(@" result image: %@", result.image);
}
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
reportsVariable:(NSString *)variableName
value:(id)value {
NSLog(@" name: %@ value: %@", variableName, value);
}
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
reportsRunFailure:(ALOCRError)error {
switch (error) {
case ALOCRErrorResultNotValid:
break;
case ALOCRErrorConfidenceNotReached:
break;
case ALOCRErrorNoLinesFound:
break;
case ALOCRErrorNoTextFound:
break;
case ALOCRErrorUnkown:
break;
default:
break;
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSError *error = nil;
BOOL success = [self.ocrModuleView startScanningAndReturnError:&error];
NSAssert(success, @"We failed starting: %@",error.debugDescription);
}
- (IBAction)helpPressed:(id)sender {
[self.ocrModuleView cancelScanningAndReturnError:nil];
ALIntroHelpView *helpView = [[ALIntroHelpView alloc] initWithFrame:self.ocrModuleView.frame];
helpView.cutoutPath = self.ocrModuleView.currentConfiguration.cutoutPath;
helpView.delegate = self;
helpView.sampleImageView.image = [UIImage imageNamed:@"intro_scrabble"];
[self.view addSubview:helpView];
}
#pragma mark - ALIntroHelpViewDelegate
- (void)goButtonPressedOnIntroHelpView:(ALIntroHelpView *)introHelpView {
[introHelpView removeFromSuperview];
[self.ocrModuleView startScanningAndReturnError:nil];
}
- (BOOL)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
textOutlineDetected:(ALSquare *)outline {
NSLog(@"outline: %@", outline);
}
@end
|
AnylineOCR Module
GRID: Bottlecap1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | {
"captureResolution":"720",
"cutout": {
"style": "rect",
"maxWidthPercent": "80%",
"maxHeightPercent": "80%",
"alignment": "center",
"width": 300,
"ratioFromSize": {
"width": 1,
"height": 1
},
"strokeWidth": 2,
"cornerRadius": 300,
"strokeColor": "FFFFFF",
"outerColor": "000000",
"outerAlpha": 0.3,
"offset": {
"x": 0,
"y": 0
},
"cropOffset": {
"x": 0,
"y": 0
},
"cropPadding": {
"x": 10,
"y": 10
}
},
"flash": {
"mode": "manual",
"alignment": "bottom_right"
},
"beepOnResult": true,
"vibrateOnResult": true,
"blinkAnimationOnResult": true,
"cancelOnResult": true,
"visualFeedback": {
"style": "rect",
"strokeWidth": 3,
"strokeColor": "0099FF",
"fillColor": "330099FF",
"cornerRadius": 10
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import android.graphics.PointF;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import java.util.List;
import at.nineyards.anyline.camera.AnylineViewConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrResult;
import at.nineyards.anyline.modules.ocr.AnylineOcrResultListener;
import at.nineyards.anyline.modules.ocr.AnylineOcrScanView;
import io.anyline.examples.R;
import io.anyline.examples.SettingsFragment;
import io.anyline.examples.ocr.result.BottlecapResultView;
public class ScanBottlecapActivity extends AppCompatActivity {
private static final String TAG = ScanBottlecapActivity.class.getSimpleName();
private AnylineOcrScanView scanView;
private BottlecapResultView bottlecapResultView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_anyline_ocr);
addBottlecapResultView();
String lic = getString(R.string.anyline_license_key);
scanView = (AnylineOcrScanView) findViewById(R.id.scan_view);
scanView.copyTrainedData("tessdata/bottlecap.traineddata", "a8224bfaf4d2085f5b0de7018dee29eb");
// see ScanScrabbleActivity for a more detailed description
AnylineOcrConfig anylineOcrConfig = new AnylineOcrConfig();
anylineOcrConfig.setTesseractLanguages("bottlecap");
anylineOcrConfig.setCharWhitelist("123456789ABCDEFGHJKLMNPRSTUVWXYZ");
anylineOcrConfig.setMinCharHeight(14);
anylineOcrConfig.setMaxCharHeight(65);
anylineOcrConfig.setMinConfidence(75);
anylineOcrConfig.setValidationRegex("^[0-9A-Z]{3}\n[0-9A-Z]{3}\n[0-9A-Z]{3}");
anylineOcrConfig.setScanMode(AnylineOcrConfig.ScanMode.GRID);
anylineOcrConfig.setCharCountX(3);
anylineOcrConfig.setCharCountY(3);
anylineOcrConfig.setCharPaddingXFactor(0.3);
anylineOcrConfig.setCharPaddingYFactor(0.5);
anylineOcrConfig.setIsBrightTextOnDark(true);
scanView.setAnylineOcrConfig(anylineOcrConfig);
scanView.setConfig(new AnylineViewConfig(this, "bottlecap_view_config.json"));
scanView.initAnyline(lic, new AnylineOcrResultListener() {
@Override
public void onResult(AnylineOcrResult anylineOcrResult) {
bottlecapResultView.setResult(anylineOcrResult.getResult());
bottlecapResultView.setVisibility(View.VISIBLE);
}
});
// disable the reporting if set to off in preferences
scanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
.KEY_PREF_REPORTING_ON, true));
bottlecapResultView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bottlecapResultView.setVisibility(View.INVISIBLE);
if (!scanView.isRunning()) {
scanView.startScanning();
}
}
});
}
private void addBottlecapResultView() {
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_layout);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
params.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
bottlecapResultView = new BottlecapResultView(this);
bottlecapResultView.setVisibility(View.INVISIBLE);
mainLayout.addView(bottlecapResultView, params);
}
@Override
protected void onResume() {
super.onResume();
scanView.startScanning();
}
@Override
protected void onPause() {
super.onPause();
scanView.cancelScanning();
scanView.releaseCameraInBackground();
}
@Override
public void onBackPressed() {
//close the result view on back press if it is open
if (bottlecapResultView.getVisibility() == View.VISIBLE) {
bottlecapResultView.setVisibility(View.INVISIBLE);
if (!scanView.isRunning()) {
scanView.startScanning();
}
} else {
super.onBackPressed();
}
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | #import "ALBottlecapScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALResultOverlayView.h"
#import "ALAppDemoLicenses.h"
// This is the license key for the examples project used to set up Aynline below
NSString * const kBottlecapLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineOCRModuleDelegate> to be able to receive results
@interface ALBottlecapScanViewController ()<AnylineOCRModuleDelegate, AnylineDebugDelegate>
// The Anyline module used for OCR
@property (nonatomic, strong) AnylineOCRModuleView *ocrModuleView;
@end
@implementation ALBottlecapScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
self.title = @"Bottlecap";
// Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
self.ocrModuleView = [[AnylineOCRModuleView alloc] initWithFrame:frame];
ALOCRConfig *config = [[ALOCRConfig alloc] init];
config.scanMode = ALGrid;
config.charHeight = ALRangeMake(21, 97);
config.tesseractLanguages = @[@"bottlecap"];
config.charWhiteList = @"123456789ABCDEFGHJKLMNPRSTUVWXYZ";
config.minConfidence = 75;
config.validationRegex = @"^[0-9A-Z]{3}\n[0-9A-Z]{3}\n[0-9A-Z]{3}";
config.charCountX = 3;
config.charCountY = 3;
config.charPaddingXFactor = 0.3;
config.charPaddingYFactor = 0.5;
config.isBrightTextOnDark = YES;
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// by the module once we start receiving results.
BOOL success = [self.ocrModuleView setupWithLicenseKey:kBottlecapLicenseKey
delegate:self
ocrConfig:config
error:&error];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if (!success) {
// Something went wrong. The error object contains the error description
NSAssert(success, @"Setup Error: %@", error.debugDescription);
}
NSString *confPath = [[NSBundle mainBundle] pathForResource:@"bottlecap_config" ofType:@"json"];
ALUIConfiguration *bottlecapConfig = [ALUIConfiguration cutoutConfigurationFromJsonFile:confPath];
self.ocrModuleView.currentConfiguration = bottlecapConfig;
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.ocrModuleView];
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// We use this subroutine to start Anyline. The reason it has its own subroutine is
// so that we can later use it to restart the scanning process.
[self startAnyline];
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[self.ocrModuleView cancelScanningAndReturnError:nil];
}
/*
This method is used to tell Anyline to start scanning. It gets called in
viewDidAppear to start scanning the moment the view appears. Once a result
is found scanning will stop automatically (you can change this behaviour
with cancelOnResult:). When the user dismisses self.identificationView this
method will get called again.
*/
- (void)startAnyline {
NSError *error;
BOOL success = [self.ocrModuleView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
}
}
#pragma mark -- AnylineOCRModuleDelegate
/*
This is the main delegate method Anyline uses to report its results
*/
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
didFindResult:(ALOCRResult *)result {
// Display an overlay showing the result
UIImage *image = [UIImage imageNamed:@"bottle_background"];
ALResultOverlayView *overlay = [[ALResultOverlayView alloc] initWithFrame:self.view.bounds];
__weak typeof(self) welf = self;
__weak ALResultOverlayView * woverlay = overlay;
[overlay setImage:image];
[overlay setText:(NSString *)result.result];
[overlay setTouchDownBlock:^{
// Remove the view when touched and restart scanning
[welf startAnyline];
[woverlay removeFromSuperview];
}];
[self.view addSubview:overlay];
}
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
reportsVariable:(NSString *)variableName
value:(id)value {
}
-(void) anylineModuleView:(AnylineAbstractModuleView *)anylineModuleView runSkipped:(ALRunFailure)runFailure {
switch (runFailure) {
case ALRunFailureResultNotValid:
break;
case ALRunFailureSharpnessNotReached:
break;
case ALRunFailureNoLinesFound:
break;
case ALRunFailureNoTextFound:
break;
case ALRunFailureUnkown:
break;
default:
break;
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSError *error = nil;
BOOL success = [self.ocrModuleView startScanningAndReturnError:&error];
NSAssert(success, @"We failed starting: %@",error.debugDescription);
}
@end
|
AnylineOCR Module
LINE: Record Numbers1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | {
"captureResolution":"720",
"cutout": {
"style": "rect",
"maxWidthPercent": "80%",
"maxHeightPercent": "80%",
"alignment": "center",
"width": 340,
"ratioFromSize": {
"width": 4,
"height": 1
},
"strokeWidth": 2,
"cornerRadius": 10,
"strokeColor": "FFFFFF",
"outerColor": "000000",
"outerAlpha": 0.3
},
"flash": {
"mode": "manual",
"alignment": "bottom_right"
},
"beepOnResult": true,
"vibrateOnResult": true,
"blinkAnimationOnResult": true,
"cancelOnResult": true,
"visualFeedback": {
"style": "contour_underline",
"strokeColor": "0099FF",
"strokeWidth": 3
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import android.content.Intent;
import android.graphics.PointF;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import java.util.List;
import at.nineyards.anyline.camera.AnylineViewConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrResult;
import at.nineyards.anyline.modules.ocr.AnylineOcrResultListener;
import at.nineyards.anyline.modules.ocr.AnylineOcrScanView;
import io.anyline.examples.R;
import io.anyline.examples.SettingsFragment;
import io.anyline.examples.ocr.apis.RecordSearchActivity;
public class ScanRecordActivity extends AppCompatActivity {
private static final String TAG = ScanRecordActivity.class.getSimpleName();
private AnylineOcrScanView scanView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_anyline_ocr);
String lic = getString(R.string.anyline_license_key);
scanView = (AnylineOcrScanView) findViewById(R.id.scan_view);
// Copies given traineddata-file to a place where the core can access it.
// This MUST be called for every traineddata file that is used (before startScanning() is called).
// The file must be located directly in the assets directory (or in tessdata/ but no other folders are allowed)
scanView.copyTrainedData("tessdata/eng_no_dict.traineddata", "d142032d86da1be4dbe22dce2eec18d7");
scanView.copyTrainedData("tessdata/deu.traineddata", "2d5190b9b62e28fa6d17b728ca195776");
//Configure the OCR for Record Numbers
AnylineOcrConfig anylineOcrConfig = new AnylineOcrConfig();
// use the LINE mode, since the numbers can be of different length
anylineOcrConfig.setScanMode(AnylineOcrConfig.ScanMode.LINE);
// set the languages used for OCR
anylineOcrConfig.setTesseractLanguages("eng_no_dict", "deu");
// allow only capital letters and some numbers - these are the only characters the SDK will consider
anylineOcrConfig.setCharWhitelist("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.");
// a simple regex for a basic validation of the record numbers
anylineOcrConfig.setValidationRegex("^([A-Z]+\\s*-*\\s*)?[0-9A-Z-\\s\\.]{3,}$");
// the characters height is 15 pixels minimum (make sure your cutout size is aligned to this)
anylineOcrConfig.setMinCharHeight(15);
// the characters height is 70 pixels maximum (make sure your cutout size is aligned to this)
anylineOcrConfig.setMaxCharHeight(70);
// the minimum confidence required to return a result, a value between 0 and 100.
// (higher confidence means less likely to get an incorrect result, but may be slower to deliver a result)
anylineOcrConfig.setMinConfidence(75);
// we don't want to remove small contours, as a . for example should be kept in the result
anylineOcrConfig.setRemoveSmallContours(false);
// we also don't want whitespaces to be removed - they are required for a search with the scanned record number
anylineOcrConfig.setRemoveWhitespaces(false);
scanView.setAnylineOcrConfig(anylineOcrConfig);
scanView.setConfig(new AnylineViewConfig(this, "record_view_config.json"));
scanView.initAnyline(lic, new AnylineOcrResultListener() {
@Override
public void onResult(AnylineOcrResult anylineOcrResult) {
if (!anylineOcrResult.getResult().isEmpty()) {
Intent i = new Intent(ScanRecordActivity.this, RecordSearchActivity.class);
i.putExtra(RecordSearchActivity.RECORD_INPUT, anylineOcrResult.getResult().trim());
startActivity(i);
}
}
});
// disable the reporting if set to off in preferences
scanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
.KEY_PREF_REPORTING_ON, true));
}
@Override
protected void onResume() {
super.onResume();
scanView.startScanning();
}
@Override
protected void onPause() {
super.onPause();
scanView.cancelScanning();
scanView.releaseCameraInBackground();
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | #import "ALRecordNumberScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALBaseViewController.h"
#import "ALIntroHelpView.h"
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"
// This is the license key for the examples project used to set up Aynline below
NSString * const kRecordNumberLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineOCRModuleDelegate> to be able to receive results
@interface ALRecordNumberScanViewController ()<AnylineOCRModuleDelegate,ALIntroHelpViewDelegate, AnylineDebugDelegate>
// The Anyline module used for OCR
@property (nonatomic, strong) AnylineOCRModuleView *ocrModuleView;
@end
@implementation ALRecordNumberScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Record Number";
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
// Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
self.ocrModuleView = [[AnylineOCRModuleView alloc] initWithFrame:frame];
ALOCRConfig *config = [[ALOCRConfig alloc] init];
config.charHeight = ALRangeMake(22, 105);
config.tesseractLanguages = @[@"eng_no_dict", @"deu"];
config.charWhiteList = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.";
config.minConfidence = 75;
config.validationRegex = @"^([A-Z]+\\s*-*\\s*)?[0-9A-Z-\\s\\.]{3,}$";
config.scanMode = ALLine;
config.removeSmallContours = NO;
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// by the module once we start receiving results.
BOOL success = [self.ocrModuleView setupWithLicenseKey:kRecordNumberLicenseKey
delegate:self
ocrConfig:config
error:&error];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if (!success) {
// Something went wrong. The error object contains the error description
NSAssert(success, @"Setup Error: %@", error.debugDescription);
}
[self.ocrModuleView enableReporting:[NSUserDefaults AL_reportingEnabled]];
NSString *confPath = [[NSBundle mainBundle] pathForResource:@"record_number_config" ofType:@"json"];
ALUIConfiguration *ibanConf = [ALUIConfiguration cutoutConfigurationFromJsonFile:confPath];
self.ocrModuleView.currentConfiguration = ibanConf;
self.controllerType = ALScanHistoryRecordNumber;
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.ocrModuleView];
[self.view sendSubviewToBack:self.ocrModuleView];
[self startListeningForMotion];
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// We use this subroutine to start Anyline. The reason it has its own subroutine is
// so that we can later use it to restart the scanning process.
[self startAnyline];
BOOL wasHelpNotShown = [NSUserDefaults AL_createEntryOnce:NSStringFromClass([self class])];
if(wasHelpNotShown) {
[self helpPressed:nil];
}
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[self.ocrModuleView cancelScanningAndReturnError:nil];
}
- (void)viewDidLayoutSubviews {
[self updateWarningPosition:
self.ocrModuleView.cutoutRect.origin.y +
self.ocrModuleView.cutoutRect.size.height +
self.ocrModuleView.frame.origin.y +
90];
}
/*
This method is used to tell Anyline to start scanning. It gets called in
viewDidAppear to start scanning the moment the view appears. Once a result
is found scanning will stop automatically (you can change this behaviour
with cancelOnResult:). When the user dismisses self.identificationView this
method will get called again.
*/
- (void)startAnyline {
NSError *error;
BOOL success = [self.ocrModuleView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
}
self.startTime = CACurrentMediaTime();
}
#pragma mark -- AnylineOCRModuleDelegate
/*
This is the main delegate method Anyline uses to report its results
*/
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
didFindResult:(ALOCRResult *)result {
// We are done. Cancel scanning
[self anylineDidFindResult:result.result barcodeResult:@"" image:result.image module:anylineOCRModuleView completion:^{
ALBaseViewController *vc = [[ALBaseViewController alloc] init];
vc.result = result.result;
NSString *url = [NSString stringWithFormat:@"https://www.google.at/search?q=\"%@\" site:discogs.com OR site:musbrainz.org OR site:allmusic.com", result.result];
[vc startWebSearchWithURL:url];
[self.navigationController pushViewController:vc animated:YES];
}];
}
- (void)anylineOCRModuleView:(AnylineOCRModuleView *)anylineOCRModuleView
reportsVariable:(NSString *)variableName
value:(id)value {
if ([variableName isEqualToString:@"$brightness"]) {
[self updateBrightness:[value floatValue] forModule:self.ocrModuleView];
}
}
- (void)anylineModuleView:(AnylineAbstractModuleView *)anylineModuleView
runSkipped:(ALRunFailure)runFailure {
switch (runFailure) {
case ALRunFailureResultNotValid:
break;
case ALRunFailureConfidenceNotReached:
break;
case ALRunFailureNoLinesFound:
break;
case ALRunFailureNoTextFound:
break;
case ALRunFailureUnkown:
break;
default:
break;
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSError *error = nil;
BOOL success = [self.ocrModuleView startScanningAndReturnError:&error];
NSAssert(success, @"We failed starting: %@",error.debugDescription);
}
- (IBAction)helpPressed:(id)sender {
[self.ocrModuleView cancelScanningAndReturnError:nil];
ALIntroHelpView *helpView = [[ALIntroHelpView alloc] initWithFrame:self.ocrModuleView.frame];
helpView.cutoutPath = self.ocrModuleView.currentConfiguration.cutoutPath;
helpView.delegate = self;
helpView.sampleImageView.image = [UIImage imageNamed:@"intro_record"];
[self.view addSubview:helpView];
}
#pragma mark - ALIntroHelpViewDelegate
- (void)goButtonPressedOnIntroHelpView:(ALIntroHelpView *)introHelpView {
[introHelpView removeFromSuperview];
[self.ocrModuleView startScanningAndReturnError:nil];
}
@end
|
Energy Module Analog/Digital Automatic
<at.nineyards.anyline.modules.energy.EnergyScanView
android:id="@+id/energy_scan_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cutout_alignment="top"
app:cutout_style="rect"
app:cutout_outside_color="#55000000"
app:cutout_offset_y="120"
app:cutout_rect_corner_radius_in_dp="4"
app:cutout_stroke_width_in_dp="2"
app:cutout_stroke_color="#FFFFFF"
app:flash_mode="manual"
app:flash_alignment="bottom_right"
app:beep_on_result="true"
app:vibrate_on_result="true"
app:blink_animation_on_result="true"
app:cancel_on_result="true"
app:visual_feedback_style="rect"
app:visual_feedback_stroke_color="#0099FF"
/>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.Switch;
import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.modules.barcode.NativeBarcodeResultListener;
import at.nineyards.anyline.modules.energy.EnergyResult;
import at.nineyards.anyline.modules.energy.EnergyResultListener;
import at.nineyards.anyline.modules.energy.EnergyScanView;
import io.anyline.examples.R;
import io.anyline.examples.ResultDialogBuilder;
import io.anyline.examples.SettingsFragment;
/**
* Example activity for the automatic Analog/digital Scan Mode of the Anyline-Energy-Module
*/
public class ScanAutoAnalogDigitalMeterActivity extends AppCompatActivity implements CameraOpenListener {
private static final String TAG = ScanAutoAnalogDigitalMeterActivity.class.getSimpleName();
protected EnergyScanView energyScanView;
private String lastDetectedBarcodeValue = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan_energy);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// get the view from the layout (check out the xml for the configuration of the view)
energyScanView = (EnergyScanView) findViewById(R.id.energy_scan_view);
Switch barcodeDetectionSwitch = (Switch) findViewById(R.id.barcode_scanner_switch);
// set a listener on the switch to enable and disable barcode detection
barcodeDetectionSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
lastDetectedBarcodeValue = "";
if (isChecked) {
Log.d(TAG, "barcode detection enabled");
// enables the barcode detection for the full image (the preview view, rather than the cutout only)
energyScanView.enableBarcodeDetection(true, new NativeBarcodeResultListener() {
@Override
public void onBarcodesReceived(SparseArray<com.google.android.gms.vision.barcode.Barcode> sparseArray) {
// For this demonstration purpose, we only use the latest barcode that has been found.
// However, note that you receive a list of barcodes, e.g. it detects multiple barcodes at once.
// Also the listener is called every time barcodes are found on a frame,
// so it is independent from the energy result
if (sparseArray.size() > 0) {
lastDetectedBarcodeValue = sparseArray.valueAt(0).displayValue;
}
}
});
} else {
Log.d(TAG, "barcode detection disabled");
energyScanView.disableBarcodeDetection();
}
}
});
// add a camera open listener that will be called when the camera is opened or an error occurred
// this is optional (if not set a RuntimeException will be thrown if an error occurs)
energyScanView.setCameraOpenListener(this);
// set reporting according to prefs or true on default
energyScanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
.KEY_PREF_REPORTING_ON, true));
// initialize Anyline with the license key and a Listener that is called if a result is found
energyScanView.initAnyline(getString(R.string.anyline_license_key), new EnergyResultListener() {
@Override
public void onResult(EnergyResult energyResult) {
// This is called when a result is found.
// The scanMode is the mode the result was found for. The result is the actual result.
// If the a meter reading was scanned two images are provided as well, one shows the targeted area only
// the other shows the full image. (Images are null in barcode mode)
// The result for meter readings is a String with leading zeros (if any) and no decimals.
new ResultDialogBuilder(ScanAutoAnalogDigitalMeterActivity.this)
.setResultImage(energyResult.getCutoutImage())
.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22)
.setTextGravity(Gravity.CENTER)
.setText(getFormattedResult(energyResult.getResult(), lastDetectedBarcodeValue))
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// reset the last detected barcode value, as it has already been displayed
lastDetectedBarcodeValue = "";
if (!energyScanView.isRunning()) {
energyScanView.startScanning();
}
}
})
.setTitle(getString(R.string.title_auto_analog_digital_meter))
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
// reset the last detected barcode value, as it has already been displayed
lastDetectedBarcodeValue = "";
if (!energyScanView.isRunning()) {
energyScanView.startScanning();
}
}
})
.show();
}
});
// AUTO_ANALOG_DIGITAL_METER will work for both analog and digital meters and
// automatically detects the correct meter type
energyScanView.setScanMode(EnergyScanView.ScanMode.AUTO_ANALOG_DIGITAL_METER);
}
@Override
protected void onResume() {
super.onResume();
//start the actual scanning
energyScanView.startScanning();
}
@Override
protected void onPause() {
super.onPause();
//stop the scanning
energyScanView.cancelScanning();
//release the camera (must be called in onPause, because there are situations where
// it cannot be auto-detected that the camera should be released)
energyScanView.releaseCameraInBackground();
}
@Override
public void onCameraOpened(final CameraController cameraController, int width, int height) {
//the camera is opened async and this is called when the opening is finished
Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
}
@Override
public void onCameraError(Exception e) {
//This is called if the camera could not be opened.
// (e.g. If there is no camera or the permission is denied)
// This is useful to present an alternative way to enter the required data if no camera exists.
throw new RuntimeException(e);
}
/**
* Format a meter reading to look a bit like a meter.
*
* @param result the meter reading
* @return the formatted string
*/
private Spanned getFormattedResult(String result, String barcodeValue) {
SpannableStringBuilder sb = new SpannableStringBuilder();
for (int i = 0, n = result.length(); i < n; i++) {
char text = result.charAt(i);
sb.append(" ");
sb.append(text);
sb.append(" ");
sb.setSpan(new BackgroundColorSpan(Color.BLACK), i * 4, i * 4 + 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.setSpan(new ForegroundColorSpan(Color.WHITE), i * 4, i * 4 + 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append(" ");
}
if (barcodeValue != null && !barcodeValue.isEmpty()) {
sb.append("\n\nBarcode: ");
sb.append(barcodeValue);
}
return sb;
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | #import "ALAutoAnalogDigitalMeterScanViewController.h"
#import "ALMeterScanResultViewController.h"
#import <Anyline/Anyline.h>
#import "ALAppDemoLicenses.h"
// This is the license key for the examples project used to set up Aynline below
NSString * const kAutoAnalogDigitalMeterScanLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineEnergyModuleDelegate> to be able to receive results
@interface ALAutoAnalogDigitalMeterScanViewController ()<AnylineEnergyModuleDelegate, AnylineNativeBarcodeDelegate, AnylineDebugDelegate>
// The Anyline module used to scan
@property (nonatomic, strong) AnylineEnergyModuleView *anylineEnergyView;
@property (nonatomic, strong) NSMutableDictionary *barcodeResults;
@property (nonatomic, strong) UIView *enableBarcodeView;
@property (nonatomic, strong) UISwitch *enableBarcodeSwitch;
@property (nonatomic, strong) UILabel *label;
@end
@implementation ALAutoAnalogDigitalMeterScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
self.title = @"Analog/Digital Meter";
// Initializing the energy module. Its a UIView subclass. We set its frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
self.anylineEnergyView = [[AnylineEnergyModuleView alloc] initWithFrame:frame];
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// once we start receiving results.
BOOL success = [self.anylineEnergyView setupWithLicenseKey:kAutoAnalogDigitalMeterScanLicenseKey delegate:self error:&error];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Setup Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
self.anylineEnergyView.translatesAutoresizingMaskIntoConstraints = NO;
//Set ScanMode to ALAnalogMeter
success = [self.anylineEnergyView setScanMode:ALAutoAnalogDigitalMeter error:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Set ScanMode Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.anylineEnergyView];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.anylineEnergyView}]];
id topGuide = self.topLayoutGuide;
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.anylineEnergyView, @"topGuide" : topGuide}]];
//Use this line if you want to use/actvitave the simultaneous barcode
//set delegate for nativeBarcodeScanning => simultaneous barcode scanning
// [self.anylineEnergyView.videoView setBarcodeDelegate:self];
self.barcodeResults = [NSMutableDictionary new];
//Add UISwitch for toggling barcode scanning
self.enableBarcodeView = [[UIView alloc] init];
self.enableBarcodeView.frame = CGRectMake(0, 0, 150, 50);
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
self.label.text = @"Detect Barcodes";
UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightThin];
self.label.font = font;
self.label.numberOfLines = 0;
self.label.textColor = [UIColor whiteColor];
[self.label sizeToFit];
self.enableBarcodeSwitch = [[UISwitch alloc] init];
[self.enableBarcodeSwitch setOn:false];
self.enableBarcodeSwitch.onTintColor = [UIColor whiteColor];
[self.enableBarcodeSwitch setOnTintColor:[UIColor colorWithRed:0.0/255.0 green:153.0/255.0 blue:255.0/255.0 alpha:1.0]];
[self.enableBarcodeSwitch addTarget:self action:@selector(toggleBarcodeScanning:) forControlEvents:UIControlEventValueChanged];
[self.enableBarcodeView addSubview:self.label];
[self.enableBarcodeView addSubview:self.enableBarcodeSwitch];
[self.anylineEnergyView addSubview:self.enableBarcodeView];
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
/*
This is the place where we tell Anyline to start receiving and displaying images from the camera.
Success/error tells us if everything went fine.
*/
NSError *error = nil;
BOOL success = [self.anylineEnergyView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.label.center = CGPointMake(self.label.frame.size.width/2, self.enableBarcodeView.frame.size.height/2);
self.enableBarcodeSwitch.center = CGPointMake(self.label.frame.size.width + self.enableBarcodeSwitch.frame.size.width/2 + 7, self.enableBarcodeView.frame.size.height/2);
CGFloat width = self.enableBarcodeSwitch.frame.size.width + 7 + self.label.frame.size.width;
self.enableBarcodeView.frame = CGRectMake(self.anylineEnergyView.frame.size.width-width-15, self.anylineEnergyView.frame.size.height-self.enableBarcodeView.frame.size.height-5, width, 50);
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[self.anylineEnergyView cancelScanningAndReturnError:nil];
}
- (IBAction)toggleBarcodeScanning:(id)sender {
if (self.anylineEnergyView.videoView.barcodeDelegate) {
self.enableBarcodeSwitch.on = false;
[self.anylineEnergyView.videoView setBarcodeDelegate:nil];
//reset found barcodes
self.barcodeResults = [NSMutableDictionary new];
} else {
self.enableBarcodeSwitch.on = true;
[self.anylineEnergyView.videoView setBarcodeDelegate:self];
}
}
#pragma mark - AnylineControllerDelegate methods
/*
The main delegate method Anyline uses to report its scanned codes
*/
- (void)anylineEnergyModuleView:(AnylineEnergyModuleView *)anylineEnergyModuleView didFindResult:(ALEnergyResult *)scanResult {
ALMeterScanResultViewController *vc = [[ALMeterScanResultViewController alloc] init];
/*
To present the scanned result to the user we use a custom view controller.
*/
vc.scanMode = scanResult.scanMode;
vc.meterImage = scanResult.image;
vc.result = (NSString *)scanResult.result;
vc.barcodeResults = self.barcodeResults;
[self.navigationController pushViewController:vc animated:YES];
//reset found barcodes
self.barcodeResults = [NSMutableDictionary new];
}
#pragma mark - AnylineNativeBarcodeDelegate methods
/*
An additional delegate which will add all found, and unique, barcodes to a Dictionary simultaneously.
*/
-(void)anylineVideoView:(AnylineVideoView *)videoView didFindBarcodeResult:(NSString *)scanResult type:(NSString *)barcodeType {
dispatch_async(dispatch_get_main_queue(), ^{
if (![self.barcodeResults objectForKey:scanResult]) {
[self.barcodeResults setObject:barcodeType forKey:scanResult];
}
});
}
#pragma mark - AnylineDebugDelegate
-(void) anylineModuleView:(AnylineAbstractModuleView *)anylineModuleView runSkipped:(ALRunFailure)runFailure {
switch (runFailure) {
case ALRunFailureResultNotValid:
break;
case ALRunFailureSharpnessNotReached:
break;
case ALRunFailureNoLinesFound:
break;
case ALRunFailureNoTextFound:
break;
case ALRunFailureUnkown:
break;
default:
break;
}
}
@end
|
Energy Module ANALOG (w/simultaneous barcode scanning)
<at.nineyards.anyline.modules.energy.EnergyScanView
android:id="@+id/energy_scan_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cutout_alignment="top"
app:cutout_style="rect"
app:cutout_outside_color="#55000000"
app:cutout_offset_y="120"
app:cutout_rect_corner_radius_in_dp="4"
app:cutout_stroke_width_in_dp="2"
app:cutout_stroke_color="#FFFFFF"
app:flash_mode="manual"
app:flash_alignment="bottom_right"
app:beep_on_result="true"
app:vibrate_on_result="true"
app:blink_animation_on_result="true"
app:cancel_on_result="true"
app:visual_feedback_style="rect"
app:visual_feedback_stroke_color="#0099FF"
/>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | package io.anyline.examples.meter;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.Switch;
import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.barcode.NativeBarcodeResultListener;
import at.nineyards.anyline.modules.energy.EnergyResult;
import at.nineyards.anyline.modules.energy.EnergyResultListener;
import at.nineyards.anyline.modules.energy.EnergyScanView;
import io.anyline.examples.R;
import io.anyline.examples.ResultDialogBuilder;
import io.anyline.examples.SettingsFragment;
/**
* Example activity for the Anyline-Energy-Module
*/
public class ScanAnalogMeterActivity extends AppCompatActivity implements CameraOpenListener {
private static final String TAG = ScanAnalogMeterActivity.class.getSimpleName();
protected EnergyScanView energyScanView;
private String lastDetectedBarcodeValue = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan_energy);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// get the view from the layout (check out the xml for the configuration of the view)
energyScanView = (EnergyScanView) findViewById(R.id.energy_scan_view);
Switch barcodeDetectionSwitch = (Switch) findViewById(R.id.barcode_scanner_switch);
// set a listener on the switch to enable and disable barcode detection
barcodeDetectionSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
lastDetectedBarcodeValue = "";
if (isChecked) {
Log.d(TAG, "barcode detection enabled");
// enables the barcode detection for the full image (the preview view, rather than the cutout only)
energyScanView.enableBarcodeDetection(true, new NativeBarcodeResultListener() {
@Override
public void onBarcodesReceived(SparseArray<com.google.android.gms.vision.barcode.Barcode> sparseArray) {
// For this demonstration purpose, we only use the latest barcode that has been found.
// However, note that you receive a list of barcodes, e.g. it detects multiple barcodes at once.
// Also the listener is called every time barcodes are found on a frame,
// so it is independent from the energy result
if (sparseArray.size() > 0) {
lastDetectedBarcodeValue = sparseArray.valueAt(0).displayValue;
}
}
});
} else {
Log.d(TAG, "barcode detection disabled");
energyScanView.disableBarcodeDetection();
}
}
});
// add a camera open listener that will be called when the camera is opened or an error occurred
// this is optional (if not set a RuntimeException will be thrown if an error occurs)
energyScanView.setCameraOpenListener(this);
// set reporting according to prefs or true on default
energyScanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
.KEY_PREF_REPORTING_ON, true));
// initialize Anyline with the license key and a Listener that is called if a result is found
energyScanView.initAnyline(getString(R.string.anyline_license_key), new EnergyResultListener() {
@Override
public void onResult(EnergyResult energyResult) {
// This is called when a result is found.
// The scanMode is the mode the result was found for. The result is the actual result.
// If the a meter reading was scanned two images are provided as well, one shows the targeted area only
// the other shows the full image. (Images are null in barcode mode)
// The result for meter readings is a String with leading zeros (if any) and no decimals.
new ResultDialogBuilder(ScanAnalogMeterActivity.this)
.setResultImage(energyResult.getCutoutImage())
.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22)
.setTextGravity(Gravity.CENTER)
.setText(getFormattedResult(energyResult.getResult(), lastDetectedBarcodeValue))
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// reset the last detected barcode value, as it has already been displayed
lastDetectedBarcodeValue = "";
if (!energyScanView.isRunning()) {
energyScanView.startScanning();
}
}
})
.setTitle(getString(R.string.title_analog_meter))
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
// reset the last detected barcode value, as it has already been displayed
lastDetectedBarcodeValue = "";
if (!energyScanView.isRunning()) {
energyScanView.startScanning();
}
}
})
.show();
}
});
// ANALOG_METER will work for all types of analog meters (gas, electric, water) and
// automatically detects digits before and after the point
energyScanView.setScanMode(EnergyScanView.ScanMode.ANALOG_METER);
}
@Override
protected void onResume() {
super.onResume();
//start the actual scanning
energyScanView.startScanning();
}
@Override
protected void onPause() {
super.onPause();
//stop the scanning
energyScanView.cancelScanning();
//release the camera (must be called in onPause, because there are situations where
// it cannot be auto-detected that the camera should be released)
energyScanView.releaseCameraInBackground();
}
@Override
public void onCameraOpened(final CameraController cameraController, int width, int height) {
//the camera is opened async and this is called when the opening is finished
Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
}
@Override
public void onCameraError(Exception e) {
//This is called if the camera could not be opened.
// (e.g. If there is no camera or the permission is denied)
// This is useful to present an alternative way to enter the required data if no camera exists.
throw new RuntimeException(e);
}
/**
* Format a meter reading to look a bit like a meter.
*
* @param result the meter reading
* @return the formatted string
*/
private Spanned getFormattedResult(String result, String barcodeValue) {
SpannableStringBuilder sb = new SpannableStringBuilder();
for (int i = 0, n = result.length(); i < n; i++) {
char text = result.charAt(i);
sb.append(" ");
sb.append(text);
sb.append(" ");
sb.setSpan(new BackgroundColorSpan(Color.BLACK), i * 4, i * 4 + 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.setSpan(new ForegroundColorSpan(Color.WHITE), i * 4, i * 4 + 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append(" ");
}
if (barcodeValue != null && !barcodeValue.isEmpty()) {
sb.append("\n\nBarcode: ");
sb.append(barcodeValue);
}
return sb;
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | #import "ALMeterScanResultViewController.h"
#import <Anyline/Anyline.h>
// This is the license key for the examples project used to set up Aynline below
NSString * const kAnalogMeterScanLicenseKey = @"eyJzY29wZSI6WyJBTEwiXSwicGxhdGZvcm0iOlsiaU9TIiwiQW5kcm9pZCIsIldpbmRvd3MiXSwidmFsaWQiOiIyMDE3LTA4LTMwIiwibWFqb3JWZXJzaW9uIjoiMyIsImlzQ29tbWVyY2lhbCI6ZmFsc2UsInRvbGVyYW5jZURheXMiOjYwLCJpb3NJZGVudGlmaWVyIjpbImlvLmFueWxpbmUuZXhhbXBsZXMuYnVuZGxlIl0sImFuZHJvaWRJZGVudGlmaWVyIjpbImlvLmFueWxpbmUuZXhhbXBsZXMuYnVuZGxlIl0sIndpbmRvd3NJZGVudGlmaWVyIjpbImlvLmFueWxpbmUuZXhhbXBsZXMuYnVuZGxlIl19CkIxbU5LZEEvb0JZMlBvRlpsVGV4d3QraHltZTh1S25ON1ZYUStXbE1DY2dYc3RjTnJTL2ZOWVduSHJaSUVORk0vbmNFYWdlVU9Vem9tbmhFNG1tTFY1c3Mxbi8zc2tBQjdjM3pmd25MNkV2Mmx4Y1k4L0htN3Bna2t0K01NanRYODdXMTdWNjBGZWdXTmpXbWF0dmNJSHRFMkhmTEdjUkprQ3BHNFpacm5KWEltVnlkSVJtQmNsamwvWktuZzY1Nm5Rb3ZhMUZzc1p5Q2Vsb3VXSVhpRi9Odk1EcmVraUlaR2JreWVTRk9TT0VxLzgra0xFdHlmZG1yUy8vRjNVZ055YWtXN3NRQXFlNjlUQmN6ak5kVXdQU1lnY3BnSXd0d2puVUJsV2FmdGJ3aW9EKzlNRkowc1JFR2p0OFd5REJ6RHRZSi9EL3NRUm5sSXA2akFjQTNBQT09";
// The controller has to conform to <AnylineEnergyModuleDelegate> to be able to receive results
@interface ALAnalogMeterScanViewController ()<AnylineEnergyModuleDelegate, AnylineNativeBarcodeDelegate>
// The Anyline module used to scan
@property (nonatomic, strong) AnylineEnergyModuleView *anylineEnergyView;
@property (nonatomic, strong) NSMutableDictionary *barcodeResults;
@property (nonatomic, strong) UIView *enableBarcodeView;
@property (nonatomic, strong) UISwitch *enableBarcodeSwitch;
@property (nonatomic, strong) UILabel *label;
@end
@implementation ALAnalogMeterScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
self.title = @"Analog Meter";
// Initializing the energy module. Its a UIView subclass. We set its frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
self.anylineEnergyView = [[AnylineEnergyModuleView alloc] initWithFrame:frame];
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// once we start receiving results.
BOOL success = [self.anylineEnergyView setupWithLicenseKey:kAnalogMeterScanLicenseKey delegate:self error:&error];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Setup Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
self.anylineEnergyView.translatesAutoresizingMaskIntoConstraints = NO;
//Set ScanMode to ALAnalogMeter
success = [self.anylineEnergyView setScanMode:ALAnalogMeter error:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Set ScanMode Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.anylineEnergyView];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.anylineEnergyView}]];
id topGuide = self.topLayoutGuide;
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.anylineEnergyView, @"topGuide" : topGuide}]];
//set delegate for nativeBarcodeScanning => simultaneus barcode scanning
[self.anylineEnergyView.videoView setBarcodeDelegate:self];
self.barcodeResults = [NSMutableDictionary new];
//Add UISwitch for toggling barcode scanning
self.enableBarcodeView = [[UIView alloc] init];
self.enableBarcodeView.frame = CGRectMake(0, 0, 150, 50);
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
self.label.text = @"Detect Barcodes";
UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightThin];
self.label.font = font;
self.label.numberOfLines = 0;
self.label.textColor = [UIColor whiteColor];
[self.label sizeToFit];
self.enableBarcodeSwitch = [[UISwitch alloc] init];
[self.enableBarcodeSwitch setOn:true];
self.enableBarcodeSwitch.onTintColor = [UIColor whiteColor];
[self.enableBarcodeSwitch setOnTintColor:[UIColor colorWithRed:0.0/255.0 green:153.0/255.0 blue:255.0/255.0 alpha:1.0]];
[self.enableBarcodeSwitch addTarget:self action:@selector(toggleBarcodeScanning:) forControlEvents:UIControlEventValueChanged];
[self.enableBarcodeView addSubview:self.label];
[self.enableBarcodeView addSubview:self.enableBarcodeSwitch];
[self.anylineEnergyView addSubview:self.enableBarcodeView];
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
/*
This is the place where we tell Anyline to start receiving and displaying images from the camera.
Success/error tells us if everything went fine.
*/
NSError *error = nil;
BOOL success = [self.anylineEnergyView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.label.center = CGPointMake(self.label.frame.size.width/2, self.enableBarcodeView.frame.size.height/2);
self.enableBarcodeSwitch.center = CGPointMake(self.label.frame.size.width + self.enableBarcodeSwitch.frame.size.width/2 + 7, self.enableBarcodeView.frame.size.height/2);
CGFloat width = self.enableBarcodeSwitch.frame.size.width + 7 + self.label.frame.size.width;
self.enableBarcodeView.frame = CGRectMake(self.anylineEnergyView.frame.size.width-width-15, self.anylineEnergyView.frame.size.height-self.enableBarcodeView.frame.size.height-5, width, 50);
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[self.anylineEnergyView cancelScanningAndReturnError:nil];
}
- (IBAction)toggleBarcodeScanning:(id)sender {
if (self.anylineEnergyView.videoView.barcodeDelegate) {
self.enableBarcodeSwitch.on = false;
[self.anylineEnergyView.videoView setBarcodeDelegate:nil];
//reset found barcodes
self.barcodeResults = [NSMutableDictionary new];
} else {
self.enableBarcodeSwitch.on = true;
[self.anylineEnergyView.videoView setBarcodeDelegate:self];
}
}
#pragma mark - AnylineControllerDelegate methods
/*
The main delegate method Anyline uses to report its scanned codes
*/
- (void)anylineEnergyModuleView:(AnylineEnergyModuleView *)anylineEnergyModuleView didFindResult:(ALEnergyResult *)scanResult {
ALMeterScanResultViewController *vc = [[ALMeterScanResultViewController alloc] init];
/*
To present the scanned result to the user we use a custom view controller.
*/
vc.scanMode = scanResult.scanMode;
vc.meterImage = scanResult.image;
vc.result = scanResult.result;
vc.barcodeResults = self.barcodeResults;
[self.navigationController pushViewController:vc animated:YES];
//reset found barcodes
self.barcodeResults = [NSMutableDictionary new];
}
/*
Deprecated since 3.10.0 - delegate Anyline uses to report the detected scan result
*/
- (void)anylineEnergyModuleView:(AnylineEnergyModuleView *)anylineEnergyModuleView didFindScanResult:(NSString *)scanResult cropImage:(UIImage *)image fullImage:(UIImage *)fullImage inMode:(ALScanMode)scanMode {
ALMeterScanResultViewController *vc = [[ALMeterScanResultViewController alloc] init];
/*
To present the scanned result to the user we use a custom view controller.
*/
vc.scanMode = scanMode;
vc.meterImage = image;
vc.result = scanResult;
vc.barcodeResults = self.barcodeResults;
[self.navigationController pushViewController:vc animated:YES];
//reset found barcodes
self.barcodeResults = [NSMutableDictionary new];
}
#pragma mark - AnylineNativeBarcodeDelegate methods
/*
An additional delegate which will add all found, and unique, barcodes to a Dictionary simultaneously.
*/
-(void)anylineVideoView:(AnylineVideoView *)videoView didFindBarcodeResult:(NSString *)scanResult type:(NSString *)barcodeType {
dispatch_async(dispatch_get_main_queue(), ^{
if (![self.barcodeResults objectForKey:scanResult]) {
[self.barcodeResults setObject:barcodeType forKey:scanResult];
}
});
}
@end
|
MRZ Module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | {
"captureResolution":"1080p",
"cutout": {
"style": "rect",
"maxWidthPercent": "90%",
"maxHeightPercent": "90%",
"alignment": "center",
"strokeWidth": 2,
"cornerRadius": 4,
"strokeColor": "FFFFFF",
"outerColor": "000000",
"outerAlpha": 0.3,
"feedbackStrokeColor": "0099FF",
"cropOffset": {
"x": 10,
"y": 20
}
},
"flash": {
"mode": "manual",
"alignment": "bottom_right"
},
"beepOnResult": true,
"vibrateOnResult": true,
"blinkAnimationOnResult": true,
"cancelOnResult": true,
"visualFeedback": {
"style": "rect",
"strokeColor": "0099FF",
"strokeWidth": 2
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.mrz.Identification;
import at.nineyards.anyline.modules.mrz.MrzResult;
import at.nineyards.anyline.modules.mrz.MrzResultListener;
import at.nineyards.anyline.modules.mrz.MrzScanView;
import io.anyline.examples.R;
/**
* Example Activity for the Anyline-MRZ-Module.
*/
public class ScanMrzActivity extends AppCompatActivity implements CameraOpenListener {
private static final String TAG = ScanMrzActivity.class.getSimpleName();
private MrzScanView mrzScanView;
private MrzResultView mrzResultView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan_mrz);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mrzScanView = (MrzScanView) findViewById(R.id.mrz_view);
mrzResultView = (MrzResultView) findViewById(R.id.mrz_result);
// add a camera open listener that will be called when the camera is opened or an error occurred
// this is optional (if not set a RuntimeException will be thrown if an error occurs)
mrzScanView.setCameraOpenListener(this);
// the view can be configured via a json file in the assets, and this config is set here
// (alternatively it can be configured via xml, see the Energy Example for that)
mrzScanView.setConfigFromAsset("mrz_view_config.json");
// initialize Anyline with the license key and a Listener that is called if a result is found
mrzScanView.initAnyline(getString(R.string.anyline_license_key), new MrzResultListener() {
@Override
public void onResult(MrzResult mrzResult) {
// This is called when a result is found.
// The Identification includes all the data read from the MRZ
// as scanned and the given image shows the scanned ID/Passport
mrzResultView.setIdentification(mrzResult.getResult());
mrzResultView.setVisibility(View.VISIBLE);
}
});
mrzResultView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mrzResultView.setVisibility(View.INVISIBLE);
if (!mrzScanView.isRunning()) {
mrzScanView.startScanning();
}
}
});
}
@Override
protected void onResume() {
super.onResume();
//start the actual scanning
mrzScanView.startScanning();
}
@Override
protected void onPause() {
super.onPause();
//stop the scanning
mrzScanView.cancelScanning();
//release the camera (must be called in onPause, because there are situations where
// it cannot be auto-detected that the camera should be released)
mrzScanView.releaseCameraInBackground();
}
@Override
public void onBackPressed() {
//close the result view on back press if it is open
if (mrzResultView.getVisibility() == View.VISIBLE) {
mrzResultView.setVisibility(View.INVISIBLE);
if (!mrzScanView.isRunning()) {
mrzScanView.startScanning();
}
} else {
super.onBackPressed();
}
}
@Override
public void onCameraOpened(CameraController cameraController, int width, int height) {
//the camera is opened async and this is called when the opening is finished
Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
}
@Override
public void onCameraError(Exception e) {
//This is called if the camera could not be opened.
// (e.g. If there is no camera or the permission is denied)
// This is useful to present an alternative way to enter the required data if no camera exists.
throw new RuntimeException(e);
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | #import "ALMRZScanViewController.h"
#import "ALIdentificationView.h"
#import <Anyline/Anyline.h>
// This is the license key for the examples project used to set up Aynline below
NSString * const kMRZLicenseKey = @"eyJzY29wZSI6WyJBTEwiXSwicGxhdGZvcm0iOlsiaU9TIiwiQW5kcm9pZCIsIldpbmRvd3MiXSwidmFsaWQiOiIyMDE2LTExLTAxIiwibWFqb3JWZXJzaW9uIjoiMyIsImlzQ29tbWVyY2lhbCI6ZmFsc2UsImlvc0lkZW50aWZpZXIiOlsiYXQuYW55bGluZS5BbnlsaW5lRXhhbXBsZXMiXSwiYW5kcm9pZElkZW50aWZpZXIiOlsiYXQuYW55bGluZS5BbnlsaW5lRXhhbXBsZXMiXSwid2luZG93c0lkZW50aWZpZXIiOlsiYXQuYW55bGluZS5BbnlsaW5lRXhhbXBsZXMiXX0KRWFBaXdNenRyZkxOZzJtNVd0bWpNTk9haGFqU1AwazVjLzJYcmlwVmV2NjFJdmF5cmdDMERENXk5ODdmenB1UkQ5ejNteEUyajY5cE1hNXJKMFhsMTFiSmNUbFFQRFltczJ2M0xCZC9xVVl0anJMbVNhcWtOREhXWi9uMXhSUWdsUDZjaHYwaEFNTDM4bjNrZk9WMnJqSWlhQWljZ0tBUElTZnNuUlFaUkovRTBBNk93aUxyVW5JNFp2MThieml5MG1KQ1NVLzJNakJOT1JjYnB2NE9WeXVpcmlPNGVuN0Q4K3E2UFFmR2FRNXlvREhLK1NyeFU2SzI0S016VVBzN3d2a0lCaWZzcGEzaXNzYXlRcDVucnpUemRlN3RyM2dha2JYeUVOSy9UL21ONjNnMlBDd3JzbU80NnY4R3FPLzZGMStZUDlhUVJpUWdmS0s0Ly8vemVRPT0=";
// The controller has to conform to <AnylineMRZModuleDelegate> to be able to receive results
@interface ALMRZScanViewController ()<AnylineMRZModuleDelegate>
// The Anyline module used to scan machine readable zones
@property (nonatomic, strong) AnylineMRZModuleView *mrzModuleView;
// A view to present the scanned results
@property (nonatomic, strong) ALIdentificationView *identificationView;
@end
@implementation ALMRZScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
// Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
self.mrzModuleView = [[AnylineMRZModuleView alloc] initWithFrame:frame];
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// by the module once we start receiving results.
BOOL success = [self.mrzModuleView setupWithLicenseKey:kMRZLicenseKey delegate:self error:&error];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Setup Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
self.mrzModuleView.translatesAutoresizingMaskIntoConstraints = NO;
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.mrzModuleView];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.mrzModuleView}]];
id topGuide = self.topLayoutGuide;
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.mrzModuleView, @"topGuide" : topGuide}]];
/*
ALIdentificationView will present the scanned values. Here we start listening for taps
to later dismiss the view.
*/
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapIdentificationView:)];
self.identificationView = [[ALIdentificationView alloc] initWithFrame:CGRectMake(0, 0, 300, 300/1.4)];
[self.identificationView addGestureRecognizer:gr];
self.identificationView.center = self.view.center;
[self.view insertSubview:self.identificationView belowSubview:self.mrzModuleView];
self.identificationView.alpha = 0;
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// The view to present the results should be hidden by default
self.identificationView.alpha = 0;
// We use this subroutine to start Anyline. The reason it has its own subroutine is
// so that we can later use it to restart the scanning process.
[self startAnyline];
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[self.mrzModuleView cancelScanningAndReturnError:nil];
}
/*
A little animation for the user to see the scanned document.
*/
-(void)animateScanDidComplete {
[self.view bringSubviewToFront:self.identificationView];
dispatch_async(dispatch_get_main_queue(), ^{
self.identificationView.center = self.view.center;
self.identificationView.alpha = 0;
[UIView animateWithDuration:0.4 animations:^{
self.identificationView.alpha = 1.0;
self.identificationView.transform = CGAffineTransformMakeScale(1.1, 1.1);
}];
[UIView animateWithDuration:1.7 delay:0.3 usingSpringWithDamping:0.5 initialSpringVelocity:10 options:(UIViewAnimationOptionAllowAnimatedContent) animations:^{
self.identificationView.transform = CGAffineTransformMakeScale(0.87, 0.87);
self.identificationView.center = self.view.center;
} completion:^(BOOL finished) {
}];
});
}
/*
Dismiss the view if the user taps the screen
*/
-(void)didTapIdentificationView:(id) sender {
if(self.identificationView.alpha == 1.0) {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.4 animations:^{
self.identificationView.transform = CGAffineTransformMakeScale(0.3, 0.3);
self.identificationView.alpha = 0;
} completion:^(BOOL finished) {
// We start scanning again once the animation completed
[self startAnyline];
}];
});
}
}
/*
This method is used to tell Anyline to start scanning. It gets called in
viewDidAppear to start scanning the moment the view appears. Once a result
is found scanning will stop automatically (you can change this behaviour
with cancelOnResult:). When the user dismisses self.identificationView this
method will get called again.
*/
- (void)startAnyline {
NSError *error;
BOOL success = [self.mrzModuleView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
}
#pragma mark -- AnylineMRZModuleDelegate
/*
This is the main delegate method Anyline uses to report its results
*/
- (void)anylineMRZModuleView:(AnylineMRZModuleView *)anylineMRZModuleView didFindResult:(ALMRZResult *)scanResult {
// Because there is a lot of information to be passed along the module
// uses ALIdentification.
[self.identificationView updateIdentification:scanResult.result];
// Because cancelOnResult: is YES by default scanning has stopped.
// Present the information to the user
[self animateScanDidComplete];
}
/*
Deprecated since 3.10.0
*/
- (void)anylineMRZModuleView:(AnylineMRZModuleView *)anylineMRZModuleView
didFindScanResult:(ALIdentification *)scanResult
allCheckDigitsValid:(BOOL)allCheckDigitsValid
atImage:(UIImage *)image {
// Because there is a lot of information to be passed along the module
// uses ALIdentification.
[self.identificationView updateIdentification:scanResult];
// Because cancelOnResult: is YES by default scanning has stopped.
// Present the information to the user
[self animateScanDidComplete];
}
@end
|
Barcode Module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | {
"captureResolution":"720p",
"cutout": {
"style": "rect",
"maxWidthPercent": "80%",
"alignment": "center",
"ratioFromSize": {
"width": 100,
"height": 80
},
"strokeWidth": 4,
"cornerRadius": 10,
"strokeColor": "000000",
"outerColor": "000000",
"outerAlpha": 0.3,
"feedbackStrokeColor": "0099FF"
},
"flash": {
"mode": "manual",
"alignment": "bottom_right"
},
"beepOnResult": true,
"vibrateOnResult": true,
"blinkAnimationOnResult": true,
"cancelOnResult": false,
"visualFeedback": {
"style": "rect",
"strokeColor": "0099FF",
"fillColor": "220099FF",
"animationDuration": 150
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;
import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.barcode.BarcodeResult;
import at.nineyards.anyline.modules.barcode.BarcodeResultListener;
import at.nineyards.anyline.modules.barcode.BarcodeScanView;
import io.anyline.examples.R;
/**
* Example activity for the Anyline-Barcode-Module
*/
public class ScanBarcodeActivity extends AppCompatActivity implements CameraOpenListener {
private static final String TAG = ScanBarcodeActivity.class.getSimpleName();
private BarcodeScanView barcodeScanView;
private TextView resultText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan_barcode);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
resultText = (TextView) findViewById(R.id.text_result);
barcodeScanView = (BarcodeScanView) findViewById(R.id.barcode_scan_view);
// add a camera open listener that will be called when the camera is opened or an error occurred
// this is optional (if not set a RuntimeException will be thrown if an error occurs)
barcodeScanView.setCameraOpenListener(this);
// the view can be configured via a json file in the assets, and this config is set here
// (alternatively it can be configured via xml, see the Energy Example for that)
barcodeScanView.setConfigFromAsset("barcode_view_config.json");
// optionally limit the barcode format to (multiple) specific types
//barcodeScanView.setBarcodeFormats(BarcodeScanView.BarcodeFormat.QR_CODE,
// BarcodeScanView.BarcodeFormat.CODE_128);
// initialize Anyline with the license key and a Listener that is called if a result is found
barcodeScanView.initAnyline(getString(R.string.anyline_license_key), new BarcodeResultListener() {
@Override
public void onResult(BarcodeResult barcodeResult) {
// This is called when a result is found.
resultText.setText(barcodeResult.getResult());
}
});
}
@Override
protected void onResume() {
super.onResume();
//start the actual scanning
barcodeScanView.startScanning();
}
@Override
protected void onPause() {
super.onPause();
//stop the scanning
barcodeScanView.cancelScanning();
//release the camera (must be called in onPause, because there are situations where
// it cannot be auto-detected that the camera should be released)
barcodeScanView.releaseCameraInBackground();
}
@Override
public void onCameraOpened(CameraController cameraController, int width, int height) {
//the camera is opened async and this is called when the opening is finished
Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
}
@Override
public void onCameraError(Exception e) {
//This is called if the camera could not be opened.
// (e.g. If there is no camera or the permission is denied)
// This is useful to present an alternative way to enter the required data if no camera exists.
throw new RuntimeException(e);
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | #import "ALMultiformatBarcodeScanViewController.h"
#import <Anyline/Anyline.h>
// This is the license key for the examples project used to set up Aynline below
NSString * const kBarcodeScanLicenseKey = @"eyJzY29wZSI6WyJBTEwiXSwicGxhdGZvcm0iOlsiaU9TIiwiQW5kcm9pZCIsIldpbmRvd3MiXSwidmFsaWQiOiIyMDE2LTExLTAxIiwibWFqb3JWZXJzaW9uIjoiMyIsImlzQ29tbWVyY2lhbCI6ZmFsc2UsImlvc0lkZW50aWZpZXIiOlsiYXQuYW55bGluZS5BbnlsaW5lRXhhbXBsZXMiXSwiYW5kcm9pZElkZW50aWZpZXIiOlsiYXQuYW55bGluZS5BbnlsaW5lRXhhbXBsZXMiXSwid2luZG93c0lkZW50aWZpZXIiOlsiYXQuYW55bGluZS5BbnlsaW5lRXhhbXBsZXMiXX0KRWFBaXdNenRyZkxOZzJtNVd0bWpNTk9haGFqU1AwazVjLzJYcmlwVmV2NjFJdmF5cmdDMERENXk5ODdmenB1UkQ5ejNteEUyajY5cE1hNXJKMFhsMTFiSmNUbFFQRFltczJ2M0xCZC9xVVl0anJMbVNhcWtOREhXWi9uMXhSUWdsUDZjaHYwaEFNTDM4bjNrZk9WMnJqSWlhQWljZ0tBUElTZnNuUlFaUkovRTBBNk93aUxyVW5JNFp2MThieml5MG1KQ1NVLzJNakJOT1JjYnB2NE9WeXVpcmlPNGVuN0Q4K3E2UFFmR2FRNXlvREhLK1NyeFU2SzI0S016VVBzN3d2a0lCaWZzcGEzaXNzYXlRcDVucnpUemRlN3RyM2dha2JYeUVOSy9UL21ONjNnMlBDd3JzbU80NnY4R3FPLzZGMStZUDlhUVJpUWdmS0s0Ly8vemVRPT0=";
// The controller has to conform to <AnylineBarcodeModuleDelegate> to be able to receive results
@interface ALMultiformatBarcodeScanViewController() <AnylineBarcodeModuleDelegate>
// The Anyline module used to scan barcodes
@property (nonatomic, strong) AnylineBarcodeModuleView *barcodeModuleView;
// A debug label to show scanned results
@property (nonatomic, strong) UILabel *resultLabel;
@end
@implementation ALMultiformatBarcodeScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
self.title = NSLocalizedString(@"Scan Barcode",@"Scan Barcode");
// Initializing the barcode module. Its a UIView subclass. We set the frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
self.barcodeModuleView = [[AnylineBarcodeModuleView alloc] initWithFrame:frame];
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// by the module once we start receiving results.
BOOL success = [self.barcodeModuleView setupWithLicenseKey:kBarcodeScanLicenseKey delegate:self error:&error];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Setup Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
self.barcodeModuleView.translatesAutoresizingMaskIntoConstraints = NO;
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.barcodeModuleView];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.barcodeModuleView}]];
id topGuide = self.topLayoutGuide;
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.barcodeModuleView, @"topGuide" : topGuide}]];
// The resultLabel is used as a debug view to see the scanned results. We set its text
// in anylineBarcodeModuleView:didFindScanResult:atImage below
self.resultLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 100, self.view.frame.size.width, 50)];
self.resultLabel.textAlignment = NSTextAlignmentCenter;
self.resultLabel.textColor = [UIColor whiteColor];
self.resultLabel.font = [UIFont fontWithName:@"HelveticaNeue-UltraLight" size:35.0];
self.resultLabel.adjustsFontSizeToFitWidth = YES;
[self.view addSubview:self.resultLabel];
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
/*
This is the place where we tell Anyline to start receiving and displaying images from the camera.
Success/error tells us if everything went fine.
*/
NSError *error;
BOOL success = [self.barcodeModuleView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[self.barcodeModuleView cancelScanningAndReturnError:nil];
}
#pragma mark -- AnylineBarcodeModuleDelegate
/*
This is the main delegate method Anyline uses to report its scanned codes
*/
- (void)anylineBarcodeModuleView:(AnylineBarcodeModuleView *)anylineBarcodeModuleView
didFindResult:(ALBarcodeResult *)scanResult {
// Because in this case scanResult is a simple string, we are able to forward it to the debug label
self.resultLabel.text = scanResult.result;
}
/*
Deprectated since 3.10.0
*/
- (void)anylineBarcodeModuleView:(AnylineBarcodeModuleView *)anylineBarcodeModuleView
didFindScanResult:(NSString *)scanResult
withBarcodeFormat:(ALBarcodeFormat)barcodeFormat
atImage:(UIImage *)image {
// Because in this case scanResult is a simple string, we are able to forward it to the debug label
self.resultLabel.text = scanResult;
}
@end
|
Document Module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | {
"captureResolution":"720p",
"pictureResolution":"1080p",
"pictureAspectRatios":["16:9"],
"cutout": {
"style": "rect",
"maxWidthPercent": "100%",
"maxHeightPercent": "100%",
"width": 720,
"ratioFromSize": {
"width": 10,
"height": 15
},
"alignment": "center",
"strokeWidth": 2,
"cornerRadius": 0,
"strokeColor": "00000000"
},
"flash": {
"mode": "manual",
"alignment": "bottom_left",
"offset": {
"x": 10,
"y": 0
}
},
"beepOnResult": true,
"vibrateOnResult": true,
"blinkAnimationOnResult": true,
"cancelOnResult": false
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | package io.anyline.examples.document;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.util.List;
import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.document.DocumentResult;
import at.nineyards.anyline.modules.document.DocumentResultListener;
import at.nineyards.anyline.modules.document.DocumentScanView;
import io.anyline.examples.R;
/**
* Example activity for the Anyline-Document-Detection-Module
*/
public class ScanDocumentActivity extends AppCompatActivity implements CameraOpenListener {
private static final String TAG = ScanDocumentActivity.class.getSimpleName();
private DocumentScanView documentScanView;
private Toast notificationToast;
private ImageView imageViewResult;
private ProgressDialog progressDialog;
private List<PointF> lastOutline;
private ObjectAnimator errorMessageAnimator;
private FrameLayout errorMessageLayout;
private TextView errorMessage;
private long lastErrorRecieved = 0;
private android.os.Handler handler = new android.os.Handler();
// takes care of fading the error message out after some time with no error reported from the SDK
private Runnable errorMessageCleanup = new Runnable(){
@Override
public void run() {
if( System.currentTimeMillis() > lastErrorRecieved + getApplication().getResources().getInteger(R.integer.error_message_delay)) {
if(errorMessage == null || errorMessageAnimator == null){
return;
}
if(errorMessage.getAlpha() == 0f){
errorMessage.setText("");
} else if(!errorMessageAnimator.isRunning()){
errorMessageAnimator = ObjectAnimator.ofFloat(errorMessage, "alpha", errorMessage.getAlpha(), 0f);
errorMessageAnimator.setDuration(getResources().getInteger(R.integer.error_message_delay));
errorMessageAnimator.setInterpolator(new AccelerateInterpolator());
errorMessageAnimator.start();
}
}
handler.postDelayed(errorMessageCleanup, getResources().getInteger(R.integer.error_message_delay));
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan_document);
//Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
imageViewResult = (ImageView) findViewById(R.id.image_result);
errorMessageLayout = (FrameLayout) findViewById(R.id.error_message_layout);
errorMessage = (TextView) findViewById(R.id.error_message);
documentScanView = (DocumentScanView) findViewById(R.id.document_scan_view);
// add a camera open listener that will be called when the camera is opened or an error occurred
// this is optional (if not set a RuntimeException will be thrown if an error occurs)
documentScanView.setCameraOpenListener(this);
// the view can be configured via a json file in the assets, and this config is set here
// (alternatively it can be configured via xml, see the Energy Example for that)
documentScanView.setConfigFromAsset("document_view_config.json");
// Optional: Set a ratio you want the documents to be restricted to. default is set to DIN_AX
documentScanView.setDocumentRatios(DocumentScanView.DocumentRatio.DIN_AX_PORTRAIT.getRatio());
// Optional: Set a maximum deviation for the ratio. 0.15 is the default
documentScanView.setMaxDocumentRatioDeviation(0.15);
// initialize Anyline with the license key and a Listener that is called if a result is found
documentScanView.initAnyline(getString(R.string.anyline_license_key), new DocumentResultListener() {
@Override
public void onResult(DocumentResult documentResult) {
// handle the result document images here
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
AnylineImage transformedImage = documentResult.getResult();
AnylineImage fullFrame = documentResult.getFullImage();
imageViewResult.setImageBitmap(Bitmap.createScaledBitmap(transformedImage.getBitmap(), 100, 160, false));
/**
* IMPORTANT: cache provided frames here, and release them at the end of this onResult. Because
* keeping them in memory (e.g. setting the full frame to an ImageView)
* will result in a OutOfMemoryError soon. This error is reported in [email protected] #onTakePictureError
* (Throwable)}
*
* Use a DiskCache http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#disk-cache
* for example
*
*/
File outDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "ok");
outDir.mkdir();
// change the file ending to png if you want a png
File outFile = new File(outDir, "" + System.currentTimeMillis() + ".jpg");
try {
// convert the transformed image into a gray scaled image internally
// transformedImage.getGrayCvMat(false);
// get the transformed image as bitmap
// Bitmap bmp = transformedImage.getBitmap();
// save the image with quality 100 (only used for jpeg, ignored for png)
transformedImage.save(outFile, 100);
showToast(getString(R.string.document_image_saved_to) +" " + outFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
// release the images
transformedImage.release();
fullFrame.release();
}
@Override
public void onPreviewProcessingSuccess(AnylineImage anylineImage) {
// this is called after the preview of the document is completed, and a full picture will be
// processed automatically
}
@Override
public void onPreviewProcessingFailure(DocumentScanView.DocumentError documentError) {
// this is called on any error while processing the document image
// Note: this is called every time an error occurs in a run, so that might be quite often
// An error message should only be presented to the user after some time
showErrorMessageFor(documentError);
}
@Override
public void onPictureProcessingFailure(DocumentScanView.DocumentError documentError) {
showErrorMessageFor(documentError,true);
if(progressDialog != null && progressDialog.isShowing()){
progressDialog.dismiss();
}
// if there is a problem, here is how images could be saved in the error case
// this will be a full, not cropped, not transformed image
AnylineImage image = documentScanView.getCurrentFullImage();
if (image != null) {
File outDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "error");
outDir.mkdir();
File outFile = new File(outDir, "" + System.currentTimeMillis() + documentError.name() + ".jpg");
try {
image.save(outFile, 100);
Log.d(TAG, "error image saved to " + outFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
image.release();
}
}
@Override
public boolean onDocumentOutlineDetected(List<PointF> list, boolean documentShapeAndBrightnessValid) {
// is called when the outline of the document is detected. return true if the outline is consumed by
// the implementation here, false if the outline should be drawn by the DocumentScanView
lastOutline = list; // saving the outline for the animations
return false;
}
@Override
public void onTakePictureSuccess() {
// this is called after the image has been captured from the camera and is about to be processed
progressDialog = ProgressDialog.show(ScanDocumentActivity.this, getString(R.string
.document_processing_picture_header), getString(R
.string
.document_processing_picture),
true);
if (errorMessageAnimator != null && errorMessageAnimator.isRunning()) {
handler.post(new Runnable() {
@Override
public void run() {
errorMessageAnimator.cancel();
errorMessageLayout.setVisibility(View.GONE);
}
});
}
}
@Override
public void onTakePictureError(Throwable throwable) {
// This is called if the image could not be captured from the camera (most probably because of an
// OutOfMemoryError)
throw new RuntimeException(throwable);
}
});
// optionally stop the scan once a valid result was returned
// documentScanView.cancelOnResult(true);
}
private void showErrorMessageFor(DocumentScanView.DocumentError documentError){
showErrorMessageFor(documentError,false);
}
private void showErrorMessageFor(DocumentScanView.DocumentError documentError, boolean highlight) {
String text = getString(R.string.document_picture_error);
switch (documentError) {
case DOCUMENT_NOT_SHARP:
text += getString(R.string.document_error_not_sharp);
break;
case DOCUMENT_SKEW_TOO_HIGH:
text += getString(R.string.document_error_skew_too_high);
break;
case DOCUMENT_OUTLINE_NOT_FOUND:
//text += getString(R.string.document_error_outline_not_found);
return; // exit and show no error message for now!
case IMAGE_TOO_DARK:
text += getString(R.string.document_error_too_dark);
break;
case SHAKE_DETECTED:
text += getString(R.string.document_error_shake);
break;
case DOCUMENT_BOUNDS_OUTSIDE_OF_TOLERANCE:
text += getString(R.string.document_error_closer);
break;
case DOCUMENT_RATIO_OUTSIDE_OF_TOLERANCE:
text += getString(R.string.document_error_format);
break;
case UNKNOWN:
break;
default:
text += getString(R.string.document_error_unknown);
return; // exit and show no error message for now!
}
if(highlight) {
showHighlightErrorMessageUiAnimated(text);
}
else {
showErrorMessageUiAnimated(text);
}
}
private void showErrorMessageUiAnimated(String message) {
if(lastErrorRecieved == 0) {
// the cleanup takes care of removing the message after some time if the error did not show up again
handler.post(errorMessageCleanup);
}
lastErrorRecieved = System.currentTimeMillis();
if (errorMessageAnimator != null && (errorMessageAnimator.isRunning() || errorMessage.getText().equals
(message))) {
return;
}
errorMessageLayout.setVisibility(View.VISIBLE);
errorMessage.setBackgroundColor(ContextCompat.getColor(this, R.color.anyline_blue_darker));
errorMessage.setAlpha(0f);
errorMessage.setText(message);
errorMessageAnimator = ObjectAnimator.ofFloat(errorMessage, "alpha", 0f, 1f);
errorMessageAnimator.setDuration(getResources().getInteger(R.integer.error_message_delay));
errorMessageAnimator.setInterpolator(new DecelerateInterpolator());
errorMessageAnimator.start();
}
private void showHighlightErrorMessageUiAnimated(String message){
lastErrorRecieved = System.currentTimeMillis();
errorMessageLayout.setVisibility(View.VISIBLE);
errorMessage.setBackgroundColor(ContextCompat.getColor(this,R.color.anyline_red));
errorMessage.setAlpha(0f);
errorMessage.setText(message);
if(errorMessageAnimator != null && errorMessageAnimator.isRunning()){
errorMessageAnimator.cancel();
}
errorMessageAnimator = ObjectAnimator.ofFloat(errorMessage, "alpha", 0f, 1f);
errorMessageAnimator.setDuration(getResources().getInteger(R.integer.error_message_delay));
errorMessageAnimator.setInterpolator(new DecelerateInterpolator());
errorMessageAnimator.setRepeatMode(ValueAnimator.REVERSE);
errorMessageAnimator.setRepeatCount(1);
errorMessageAnimator.start();
}
private void showToast(String text) {
try {
notificationToast.setText(text);
} catch (Exception e) {
notificationToast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
}
notificationToast.show();
}
@Override
protected void onResume() {
super.onResume();
//start the actual scanning
documentScanView.startScanning();
}
@Override
protected void onPause() {
super.onPause();
//stop the scanning
documentScanView.cancelScanning();
//release the camera (must be called in onPause, because there are situations where
// it cannot be auto-detected that the camera should be released)
documentScanView.releaseCameraInBackground();
}
@Override
public void onCameraOpened(CameraController cameraController, int width, int height) {
//the camera is opened async and this is called when the opening is finished
Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
}
@Override
public void onCameraError(Exception e) {
//This is called if the camera could not be opened.
// (e.g. If there is no camera or the permission is denied)
// This is useful to present an alternative way to enter the required data if no camera exists.
throw new RuntimeException(e);
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | #import "ALDocumentScanViewController.h"
#import "Anyline/AnylineDocumentModuleView.h"
#import "ALRoundedView.h"
NSString * const kDocumentScanLicenseKey = @"eyJzY29wZSI6WyJHRU5FUklDIl0sInBsYXRmb3JtIjpbImlPUyJdLCJ2YWxpZCI6IjIwMTctMDEtMDEiLCJtYWpvclZlcnNpb24iOiIzIiwiaXNDb21tZXJjaWFsIjpmYWxzZSwiaW9zSWRlbnRpZmllciI6WyJhdC5hbnlsaW5lLkFueWxpbmVFeGFtcGxlcyJdfQpJRFNINHFnd1ROQytCVlB1b29Pd2RNMmVBa2hjNDRKbFJpRjNkMTM0anc0VkgyaUtHVHN1QVg0dEpjTjhIdGdHT2Qydmh1UnB3OWkwRTAvblRsbmFTUnZ5ZjlreGpHZnBEb3VsQ0hsMk84SUpURjREdGQxVGw3L252aHBJaGUzaDlCaDhZSGROak10TE1TemJpM25jZjQ4eHREbkFCVXN0NitnWXRPeGFVUmNwdjlYOGpwc2lmV0lBdXVkTTRPVWNnYi9wdUVHVUI3TUNQZlBZcXFlSjFMbFM2WWlVWVlXUjZ6bU5IVjZscVNmdWk3Zk4xcU0ra2JvaDBmS2JLbFVTUzBNSmRXeDNPekVsVTR2dWZzb2NQNjdBSVVWMDAyQ0NPUThXK2loNmtodGxCUXdDN000QVhWN2ppaExHQy90ZUErWmluQ3E3M3VUdURRZFp6U1Rxd2c9PQ==";
@class AnylineDocumentModuleView;
@interface ALDocumentScanViewController () <AnylineDocumentModuleDelegate>
@property (nonatomic, strong) AnylineDocumentModuleView *documentModuleView;
@property (nonatomic, strong) ALRoundedView *roundedView;
@property (nonatomic, assign) NSInteger showingLabel;
@end
@implementation ALDocumentScanViewController
/*
We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
*/
- (void)viewDidLoad {
[super viewDidLoad];
// Set the background color to black to have a nicer transition
self.view.backgroundColor = [UIColor blackColor];
self.title = NSLocalizedString(@"Scan Document", @"Scan Document");
// Initializing the the module. Its a UIView subclass. We set the frame to fill the whole screen
CGRect frame = [[UIScreen mainScreen] applicationFrame];
frame = CGRectMake(
frame.origin.x,
frame.origin.y + self.navigationController.navigationBar.frame.size.height,
frame.size.width,
frame.size.height - self.navigationController.navigationBar.frame.size.height
);
self.documentModuleView = [[AnylineDocumentModuleView alloc] initWithFrame:frame];
NSError *error = nil;
// We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
// by the module once we start receiving results.
BOOL success = [self.documentModuleView setupWithLicenseKey:kDocumentScanLicenseKey delegate:self error:&error];
self.documentModuleView.cancelOnResult = YES;
[self.documentModuleView setDocumentRatios:@[ALDocumentRatioLetterPortrait], @[ALDocumentRatioLetterLandscape]];
// setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
// we have to check the error object for the error message.
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Setup Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
self.documentModukeView.maxDocumentRatioDeviation = @(0.15);
self.documentModuleView.translatesAutoresizingMaskIntoConstraints = NO;
// After setup is complete we add the module to the view of this view controller
[self.view addSubview:self.documentModuleView];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.documentModuleView}]];
id topGuide = self.topLayoutGuide;
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.documentModuleView, @"topGuide" : topGuide}]];
// This view notifies the user of any problems that occur while he is scanning
self.roundedView = [[ALRoundedView alloc] initWithFrame:CGRectMake(20, 115, self.view.bounds.size.width - 40, 30)];
self.roundedView.fillColor = [UIColor colorWithRed:98.0/255.0 green:39.0/255.0 blue:232.0/255.0 alpha:0.6];
self.roundedView.textLabel.text = @"";
self.roundedView.alpha = 0;
[self.view addSubview:self.roundedView];
}
/*
This method will be called once the view controller and its subviews have appeared on screen
*/
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
/*
This is the place where we tell Anyline to start receiving and displaying images from the camera.
Success/error tells us if everything went fine.
*/
NSError *error;
BOOL success = [self.documentModuleView startScanningAndReturnError:&error];
if( !success ) {
// Something went wrong. The error object contains the error description
[[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
message:error.debugDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
}
/*
Cancel scanning to allow the module to clean up
*/
- (void)viewWillDisappear:(BOOL)animated {
[self.documentModuleView cancelScanningAndReturnError:nil];
}
#pragma mark -- AnylineDocumentModuleDelegate
/*
This is the main delegate method Anyline uses to report its scanned codes
*/
- (void)anylineDocumentModuleView:(AnylineDocumentModuleView *)anylineDocumentModuleView
hasResult:(UIImage *)transformedImage
fullImage:(UIImage *)fullFrame
documentCorners:(ALSquare *)corners {
UIViewController *viewController = [[UIViewController alloc] init];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:viewController.view.bounds];
imageView.center = CGPointMake(imageView.center.x, imageView.center.y + 30);
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = transformedImage;
[viewController.view addSubview:imageView];
[self.navigationController pushViewController:viewController animated:YES];
}
/*
This method receives errors that occured during the scan.
*/
- (void)anylineDocumentModuleView:(AnylineDocumentModuleView *)anylineDocumentModuleView
reportsPictureProcessingFailure:(ALDocumentError)error {
[self showUserLabel:error];
}
/*
This method receives errors that occured during the scan.
*/
- (void)anylineDocumentModuleView:(AnylineDocumentModuleView *)anylineDocumentModuleView
reportsPreviewProcessingFailure:(ALDocumentError)error {
[self showUserLabel:error];
}
#pragma mark -- Helper Methods
/*
Shows a little round label at the bottom of the screen to inform the user what happended
*/
- (void)showUserLabel:(ALDocumentError)error {
NSString * helpString = nil;
switch (error) {
case ALDocumentErrorNotSharp:
helpString = @"Document not Sharp";
break;
case ALDocumentErrorSkewTooHigh:
helpString = @"Wrong Perspective";
break;
case ALDocumentErrorImageTooDark:
helpString = @"Too Dark";
break;
case ALDocumentErrorShakeDetected:
helpString = @"Hold Still";
break;
case ALDocumentErrorBoundsOutsideOfTolerance:
helpString = @"Closer";
break;
default:
break;
}
// The error is not in the list above or a label is on screen at the moment
if(!helpString || self.showingLabel == 1) {
return;
}
self.showingLabel = 1;
self.roundedView.textLabel.text = helpString;
// Animate the appearance of the label
CGFloat fadeDuration = 0.8;
[UIView animateWithDuration:fadeDuration animations:^{
self.roundedView.alpha = 1;
} completion:^(BOOL finished) {
[UIView animateWithDuration:fadeDuration animations:^{
self.roundedView.alpha = 0;
} completion:^(BOOL finished) {
self.showingLabel = 0;
}];
}];
}
@end
|
Tutorials and How To Guides
A growing list of tutorials for developers can be found at https://www.anyline.io/tutorials/