gali8 / Tesseract-OCR-iOS

Tesseract OCR iOS is a Framework for iOS7+, compiled also for armv7s and arm64.
http://www.nexor.it
MIT License
4.22k stars 949 forks source link

High memory when processing multiple images #227

Closed chyt closed 9 years ago

chyt commented 9 years ago

I'm using Tesseract to do text recognition from screenshots, but I'm running into an issue with huge memory usage. For example, recognition on just 6 images puts memory usage at over 200MB. Also, the memory is not getting deallocated after the recognition is complete.

My process is that the user imports screenshots, then I crop each screenshot into 6 smaller images. Then I run each of those images through Tesseract using an NSOperationQueue, similar to what is done in the sample project. I've identified that my Tesseract method is the source of the issue (commenting it out results in memory use of only ~30MB.

Here is the code for in the method:

-(void)processImageWithTesseract:(UIImage *)image WithDataType:(int)dataType AndCropRect:(CGRect)cropRect AndPosition:(int)position AndRuneNumber:(int)runeNumber {

    NSString *dataTypeString;
    if(dataType==0) dataTypeString = @"rune type";
    else if(dataType==1) dataTypeString = @"main stat";
    else dataTypeString = @"substats";

    NSString *positionString;
    if(position==0) positionString = @"top";
    else positionString = @"bottom";

    [self appendLogToTextView:[NSString stringWithFormat:@"   Processing screenshot (position: %@, data type: %@) details...", positionString, dataTypeString]];

    // Create a new `G8RecognitionOperation` to perform the OCR asynchronously
    // It is assumed that there is a .traineddata file for the language pack
    // you want Tesseract to use in the "tessdata" folder in the root of the
    // project AND that the "tessdata" folder is a referenced folder and NOT
    // a symbolic group in your project
    G8RecognitionOperation *operation = [[G8RecognitionOperation alloc] initWithLanguage:@"eng"];

    // Use the original Tesseract engine mode in performing the recognition
    // (see G8Constants.h) for other engine mode options
    operation.tesseract.engineMode = G8OCREngineModeTesseractOnly;

    // Let Tesseract automatically segment the page into blocks of text
    // based on its analysis (see G8Constants.h) for other page segmentation
    // mode options
    operation.tesseract.pageSegmentationMode = G8PageSegmentationModeAutoOnly;

    // Optionally limit the time Tesseract should spend performing the
    // recognition
    //operation.tesseract.maximumRecognitionTime = 1.0;

    // Set the delegate for the recognition to be this class
    // (see `progressImageRecognitionForTesseract` and
    // `shouldCancelImageRecognitionForTesseract` methods below)
    operation.delegate = self;

    // Optionally limit Tesseract's recognition to the following whitelist
    // and blacklist of characters
    operation.tesseract.charWhitelist = @"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890()-+%";
    //operation.tesseract.charBlacklist = @" ";

    // Set the image on which Tesseract should perform recognition
    operation.tesseract.image = image;

    // Optionally limit the region in the image on which Tesseract should
    // perform recognition to a rectangle
    operation.tesseract.rect = cropRect;

    // Specify the function block that should be executed when Tesseract
    // finishes performing recognition on the image
    operation.recognitionCompleteBlock = ^(G8Tesseract *tesseract) {
        // Fetch the recognized text
        NSString *recognizedText = tesseract.recognizedText;

        if(dataType==TITLE_DATA && [recognizedText rangeOfString:@"Rune"].location != NSNotFound) {
            [self appendLogToTextView:[NSString stringWithFormat:@"   RUNE FOUND: position is %@ of screenshot. Adding...", positionString]];
            NSMutableArray *tempRuneData = [NSMutableArray new];
            [tempRuneData addObject:@""];
            [tempRuneData addObject:@""];
            [tempRuneData addObject:@""];
            [self.runeTextDict setObject:tempRuneData forKey:[NSString stringWithFormat:@"%i_%i",runeNumber,position]];
        }

        NSMutableArray *runeArray = [self.runeTextDict objectForKey:[NSString stringWithFormat:@"%i_%i",runeNumber,position]];
        runeArray[dataType] = recognizedText;

        //NSLog(@"Finished processing image %i with position %i and dataType %i", runeNumber, position, dataType);
    };

    // Finally, add the recognition operation to the queue
    [self.operationQueue addOperation:operation];
    [self.doneOp addDependency:operation];
}

Here is a screenshot of the results from the allocation profiler:

profile

Please let me know if you need any further information. Thank you for the help.

chyt commented 9 years ago

I've been trying to do more debugging on my own but I'm really not getting any information. I tried to look at a call tree to see where all the memory was being allocated but all I get are a bunch of memory addresses, which I don't know how to interpret.

memory

Any help would be greatly appreciated.

dxclancy commented 9 years ago

Hi. I'm not really familiar with operationQueues, but the first thing that comes to mind when I look at your code is what happens at the end of the operation? How is the operation being released? Because it is holding your images, and that would be a good candidate for increased memory. How are the operations being release?

chyt commented 9 years ago

Thanks for the help, the issue was indeed with NSOperationQueue and I was able to resolve it. Marking this as closed.