Skip to content

Commit

Permalink
1. engineConfigured property has been added.
Browse files Browse the repository at this point in the history
2. initialization logic gas been changed a bit.
3. new test to check language setter has been added.
  • Loading branch information
ws233 committed Jul 29, 2015
1 parent 7822f96 commit e0cf49e
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 111 deletions.
28 changes: 24 additions & 4 deletions TesseractOCR/G8Tesseract.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ extern NSInteger const kG8MaxCredibleResolution;
* file must exist in the "tessdata" folder of the project. For example, if
* you set `language` to "foo", then "foo.traineddata" must exist in the
* "tessdata" folder.
* You should always check that the languages have been set correctly and
* and tesseract has been initialized its engine for the languages specified
* with isEngineConfigured property.
*/
@property (nonatomic, copy) NSString* language;

Expand All @@ -76,6 +79,11 @@ extern NSInteger const kG8MaxCredibleResolution;
*/
@property (nonatomic, assign) G8PageSegmentationMode pageSegmentationMode;

/**
* YES when tesseract of succesfully initialized, NO otherwise.
*/
@property (nonatomic, readonly, getter=isEngineConfigured) BOOL engineConfigured;

/**
* A white list of characters that Tesseract should recognize. Any
* recognition string that Tesseract returns will only contain characters from
Expand Down Expand Up @@ -273,7 +281,10 @@ extern NSInteger const kG8MaxCredibleResolution;
*
* @param language The language to use in recognition. See `language`.
*
* @return The initialized Tesseract object, or `nil` if there was an error.
* @return The initialized Tesseract object. You should check
* that the language property match the languages you have specified.
* Also you may ensure that tesseract is properly initialized with
* isEngineConfigured property.
*/
- (instancetype)initWithLanguage:(NSString*)language;

Expand All @@ -283,7 +294,10 @@ extern NSInteger const kG8MaxCredibleResolution;
* @param language The language to use in recognition. See `language`.
* @param engineMode The engine mode to use in recognition. See `engineMode`.
*
* @return The initialized Tesseract object, or `nil` if there was an error.
* @return The initialized Tesseract object. You should check
* that the language property match the languages you have specified.
* Also you may ensure that tesseract is properly initialized with
* isEngineConfigured property.
*/
- (instancetype)initWithLanguage:(NSString*)language
engineMode:(G8OCREngineMode)engineMode;
Expand Down Expand Up @@ -311,7 +325,10 @@ extern NSInteger const kG8MaxCredibleResolution;
* @param engineMode The engine mode to use in recognition. See
* `engineMode`.
*
* @return The initialized Tesseract object, or `nil` if there was an error.
* @return The initialized Tesseract object.You should check
* that the language property match the languages you have specified.
* Also you may ensure that tesseract is properly initialized with
* isEngineConfigured property.
*/

- (instancetype)initWithLanguage:(NSString *)language
Expand Down Expand Up @@ -351,7 +368,10 @@ extern NSInteger const kG8MaxCredibleResolution;
* `engineMode`.
*
*
* @return The initialized Tesseract object, or `nil` if there was an error.
* @return The initialized Tesseract object. You should check
* that the language property match the languages you have specified.
* Also you may ensure that tesseract is properly initialized with
* isEngineConfigured property.
*/
- (instancetype)initWithLanguage:(NSString *)language
configDictionary:(NSDictionary *)configDictionary
Expand Down
243 changes: 138 additions & 105 deletions TesseractOCR/G8Tesseract.mm
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ @interface G8Tesseract () {
ETEXT_DESC *_monitor;
}

@property (nonatomic, assign, readonly) tesseract::TessBaseAPI *tesseract;

@property (nonatomic, strong) NSDictionary *configDictionary;
@property (nonatomic, strong) NSArray *configFileNames;
@property (nonatomic, strong) NSMutableDictionary *variables;
Expand Down Expand Up @@ -133,8 +135,7 @@ - (instancetype)initWithLanguage:(NSString *)language
return nil;
}
}
_absoluteDataPath = [absoluteDataPath copy];
_language = [language copy];
_absoluteDataPath = absoluteDataPath.copy;
_configDictionary = configDictionary;
_configFileNames = configFileNames;
_engineMode = engineMode;
Expand All @@ -154,12 +155,7 @@ - (instancetype)initWithLanguage:(NSString *)language

setenv("TESSDATA_PREFIX", [_absoluteDataPath stringByAppendingString:@"/"].fileSystemRepresentation, 1);

_tesseract = new tesseract::TessBaseAPI();

BOOL success = [self configEngine];
if (success == NO) {
self = nil;
}
self.language = language.copy;
}
return self;
}
Expand All @@ -170,6 +166,11 @@ - (void)dealloc
delete _monitor;
_monitor = nullptr;
}
[self freeTesseract];
}

- (void)freeTesseract {

if (_tesseract != nullptr) {
// There is no needs to call Clear() and End() explicitly.
// End() is sufficient to free up all memory of TessBaseAPI.
Expand All @@ -193,15 +194,16 @@ - (BOOL)configEngine
for (int i = 0; i < count; i++) {
configs[i] = ((NSString*)self.configFileNames[i]).fileSystemRepresentation;
}
int returnCode = _tesseract->Init(self.absoluteDataPath.fileSystemRepresentation, self.language.UTF8String,
(tesseract::OcrEngineMode)self.engineMode,
(char **)configs, count,
&tessKeys, &tessValues,
false);
int returnCode = self.tesseract->Init(self.absoluteDataPath.fileSystemRepresentation, self.language.UTF8String,
(tesseract::OcrEngineMode)self.engineMode,
(char **)configs, count,
&tessKeys, &tessValues,
false);
if (configs != nullptr) {
free(configs);
}
return returnCode == 0;
self.engineConfigured = returnCode == 0;
return self.engineConfigured;
}

- (void)resetFlags
Expand All @@ -216,9 +218,14 @@ - (BOOL)resetEngine
if (isInitDone) {
[self loadVariables];
[self resetFlags];
}
else {
if (_image) {
[self setEngineImage:_image];
}
} else {
NSLog(@"ERROR! Can't init Tesseract engine.");
_language = nil;
_engineMode = G8OCREngineModeTesseractOnly;
[self freeTesseract];
}

return isInitDone;
Expand Down Expand Up @@ -322,11 +329,22 @@ - (void)loadVariables

#pragma mark - Getters and setters

- (tesseract::TessBaseAPI *)tesseract {

if (!_tesseract) {
_tesseract = new tesseract::TessBaseAPI();
}
return _tesseract;
}

- (void)setLanguage:(NSString *)language
{
if ([_language isEqualToString:language] == NO) {
_language = [language copy];

if ([language isEqualToString:_language] == NO || (!language && _language) ) {

_language = language.copy;
if (!self.language) {
NSLog(@"WARNING: Setting G8Tesseract language to nill defaults to English, so make sure you either set the language afterward or have eng.traineddata in your tessdata folder, otherwise Tesseract will crash!");
}
/*
* "WARNING: On changing languages, all Tesseract parameters
* are reset back to their default values."
Expand Down Expand Up @@ -375,51 +393,56 @@ - (void)setCharBlacklist:(NSString *)charBlacklist
- (void)setImage:(UIImage *)image
{
if (_image != image) {
if (image.size.width <= 0 || image.size.height <= 0) {
NSLog(@"ERROR: Image has not size!");
return;
}

image = [image fixOrientation];

self.imageSize = image.size; //self.imageSize used in the characterBoxes method

Pix *pix = nullptr;

if ([self.delegate respondsToSelector:@selector(preprocessedImageForTesseract:sourceImage:)]) {
UIImage *thresholdedImage = [self.delegate preprocessedImageForTesseract:self sourceImage:image];
if (thresholdedImage != nil) {
self.imageSize = thresholdedImage.size;

Pix *pixs = [self pixForImage:thresholdedImage];
pix = pixConvertTo1(pixs, UINT8_MAX / 2);
pixDestroy(&pixs);
[self setEngineImage:image];
}
}

if (pix == nullptr) {
NSLog(@"WARNING: Can't create Pix for custom thresholded image!");
}
- (void)setEngineImage:(UIImage *)image {

if (image.size.width <= 0 || image.size.height <= 0) {
NSLog(@"ERROR: Image has not size!");
return;
}

image = [image fixOrientation];

self.imageSize = image.size; //self.imageSize used in the characterBoxes method

Pix *pix = nullptr;

if ([self.delegate respondsToSelector:@selector(preprocessedImageForTesseract:sourceImage:)]) {
UIImage *thresholdedImage = [self.delegate preprocessedImageForTesseract:self sourceImage:image];
if (thresholdedImage != nil) {
self.imageSize = thresholdedImage.size;

Pix *pixs = [self pixForImage:thresholdedImage];
pix = pixConvertTo1(pixs, UINT8_MAX / 2);
pixDestroy(&pixs);

if (pix == nullptr) {
NSLog(@"WARNING: Can't create Pix for custom thresholded image!");
}
}

if (pix == nullptr) {
pix = [self pixForImage:image];
}

@try {
_tesseract->SetImage(pix);
}
//LCOV_EXCL_START
@catch (NSException *exception) {
NSLog(@"ERROR: Can't set image: %@", exception);
}
//LCOV_EXCL_STOP
pixDestroy(&pix);

_image = image;
_rect = (CGRect){CGPointZero, self.imageSize};

[self resetFlags];
}

if (pix == nullptr) {
pix = [self pixForImage:image];
}

@try {
_tesseract->SetImage(pix);
}
//LCOV_EXCL_START
@catch (NSException *exception) {
NSLog(@"ERROR: Can't set image: %@", exception);
}
//LCOV_EXCL_STOP
pixDestroy(&pix);

_image = image;
_rect = (CGRect){CGPointZero, self.imageSize};

[self resetFlags];
}

- (void)setRect:(CGRect)rect
Expand Down Expand Up @@ -646,57 +669,63 @@ - (NSArray *)recognizedBlocksByIteratorLevel:(G8PageIteratorLevel)pageIteratorLe
}

- (NSString *)recognizedHOCRForPageNumber:(int)pageNumber {
char *hocr = _tesseract->GetHOCRText(pageNumber);
if (hocr) {
NSString *text = [NSString stringWithUTF8String:hocr];
free(hocr);
return text;
}

if (self.isEngineConfigured) {
char *hocr = _tesseract->GetHOCRText(pageNumber);
if (hocr) {
NSString *text = [NSString stringWithUTF8String:hocr];
free(hocr);
return text;
}
}
return nil;
}

- (NSData *)recognizedPDFForImages:(NSArray*)images {

NSString *path = [self.absoluteDataPath stringByAppendingPathComponent:@"tessdata"];
tesseract::TessPDFRenderer *renderer = new tesseract::TessPDFRenderer(path.fileSystemRepresentation);

// Begin producing output
const char* kUnknownTitle = "Unknown Title";
if (renderer && !renderer->BeginDocument(kUnknownTitle)) {
return nil; // LCOV_EXCL_LINE
}

bool result = YES;
for (int page = 0; page < images.count && result; page++) {
UIImage *image = images[page];
if ([image isKindOfClass:[UIImage class]]) {
Pix *pixs = [self pixForImage:image];
Pix *pix = pixConvertTo1(pixs, UINT8_MAX / 2);
pixDestroy(&pixs);

const char *pagename = [NSString stringWithFormat:@"page #%i", page].UTF8String;
result = _tesseract->ProcessPage(pix, page, pagename, NULL, 0, renderer);
pixDestroy(&pix);
}
}

// error
if (!result) {
return nil; // LCOV_EXCL_LINE
}

// Finish producing output
if (renderer && !renderer->EndDocument()) {
return nil; // LCOV_EXCL_LINE
}

const char *pdfData = NULL;
int pdfDataLength = 0;
renderer->GetOutput(&pdfData, &pdfDataLength);

NSData *data = [NSData dataWithBytes:pdfData length:pdfDataLength];
return data;
if (!self.isEngineConfigured) {
return nil;
}

NSString *path = [self.absoluteDataPath stringByAppendingPathComponent:@"tessdata"];
tesseract::TessPDFRenderer *renderer = new tesseract::TessPDFRenderer(path.fileSystemRepresentation);

// Begin producing output
const char* kUnknownTitle = "Unknown Title";
if (renderer && !renderer->BeginDocument(kUnknownTitle)) {
return nil; // LCOV_EXCL_LINE
}

bool result = YES;
for (int page = 0; page < images.count && result; page++) {
UIImage *image = images[page];
if ([image isKindOfClass:[UIImage class]]) {
Pix *pixs = [self pixForImage:image];
Pix *pix = pixConvertTo1(pixs, UINT8_MAX / 2);
pixDestroy(&pixs);

const char *pagename = [NSString stringWithFormat:@"page #%i", page].UTF8String;
result = _tesseract->ProcessPage(pix, page, pagename, NULL, 0, renderer);
pixDestroy(&pix);
}
}

// error
if (!result) {
return nil; // LCOV_EXCL_LINE
}

// Finish producing output
if (renderer && !renderer->EndDocument()) {
return nil; // LCOV_EXCL_LINE
}

const char *pdfData = NULL;
int pdfDataLength = 0;
renderer->GetOutput(&pdfData, &pdfDataLength);

NSData *data = [NSData dataWithBytes:pdfData length:pdfDataLength];
return data;
}

- (UIImage *)imageWithBlocks:(NSArray *)blocks drawText:(BOOL)drawText thresholded:(BOOL)thresholded
Expand Down Expand Up @@ -738,6 +767,10 @@ - (UIImage *)imageWithBlocks:(NSArray *)blocks drawText:(BOOL)drawText threshold

- (BOOL)recognize
{
if (!self.isEngineConfigured) {
return NO;
}

if (self.maximumRecognitionTime > FLT_EPSILON) {
_monitor->set_deadline_msecs((inT32)(self.maximumRecognitionTime * 1000));
}
Expand Down
Loading

0 comments on commit e0cf49e

Please sign in to comment.