Skip to content

Commit

Permalink
Merge e997383 into b902703
Browse files Browse the repository at this point in the history
  • Loading branch information
bamx23 committed Jan 13, 2015
2 parents b902703 + e997383 commit dd8e782
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 75 deletions.
8 changes: 8 additions & 0 deletions Tesseract OCR iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
413F03321A5FBBDD000C194B /* UIImage+G8FixOrientation.h in Headers */ = {isa = PBXBuildFile; fileRef = 413F03301A5FBBDD000C194B /* UIImage+G8FixOrientation.h */; };
413F03331A5FBBDD000C194B /* UIImage+G8FixOrientation.m in Sources */ = {isa = PBXBuildFile; fileRef = 413F03311A5FBBDD000C194B /* UIImage+G8FixOrientation.m */; };
73C0A7961A5932C400D823D4 /* G8Tesseract.h in Headers */ = {isa = PBXBuildFile; fileRef = 64A029D617307CD0002B12E7 /* G8Tesseract.h */; settings = {ATTRIBUTES = (Public, ); }; };
73C0A7971A5932C800D823D4 /* G8Tesseract.mm in Sources */ = {isa = PBXBuildFile; fileRef = 64A029D717307CD0002B12E7 /* G8Tesseract.mm */; };
73C0A7981A5932CC00D823D4 /* G8TesseractDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4141211F1A4C578800583ED4 /* G8TesseractDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand All @@ -25,6 +27,8 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
413F03301A5FBBDD000C194B /* UIImage+G8FixOrientation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+G8FixOrientation.h"; sourceTree = "<group>"; };
413F03311A5FBBDD000C194B /* UIImage+G8FixOrientation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+G8FixOrientation.m"; sourceTree = "<group>"; };
4141211F1A4C578800583ED4 /* G8TesseractDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = G8TesseractDelegate.h; sourceTree = "<group>"; };
418997A71A42CC8B00D6477C /* G8Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = G8Constants.h; sourceTree = "<group>"; };
41A95DE81A3AF39B0085093C /* G8TesseractParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = G8TesseractParameters.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -246,6 +250,8 @@
418997A71A42CC8B00D6477C /* G8Constants.h */,
6490748A198A5A5600D728CC /* UIImage+G8Filters.h */,
6490748B198A5A5600D728CC /* UIImage+G8Filters.m */,
413F03301A5FBBDD000C194B /* UIImage+G8FixOrientation.h */,
413F03311A5FBBDD000C194B /* UIImage+G8FixOrientation.m */,
41C7E8231A3F0682000DC42B /* Core */,
41C7E8211A3F0650000DC42B /* Readme */,
64A0293017307C1D002B12E7 /* Supporting Files */,
Expand Down Expand Up @@ -465,6 +471,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
413F03321A5FBBDD000C194B /* UIImage+G8FixOrientation.h in Headers */,
73C0A7961A5932C400D823D4 /* G8Tesseract.h in Headers */,
73C0A79D1A5932F900D823D4 /* G8TesseractParameters.h in Headers */,
73C0A7A01A59330A00D823D4 /* UIImage+G8Filters.h in Headers */,
Expand Down Expand Up @@ -548,6 +555,7 @@
73C0A7A11A59330E00D823D4 /* UIImage+G8Filters.m in Sources */,
73C0A79C1A5932F500D823D4 /* G8RecognitionOperation.m in Sources */,
73C0A7971A5932C800D823D4 /* G8Tesseract.mm in Sources */,
413F03331A5FBBDD000C194B /* UIImage+G8FixOrientation.m in Sources */,
73C0A79E1A5932FD00D823D4 /* G8TesseractParameters.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
111 changes: 40 additions & 71 deletions TesseractOCR/G8Tesseract.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "G8Tesseract.h"

#import "UIImage+G8Filters.h"
#import "UIImage+G8FixOrientation.h"
#import "G8TesseractParameters.h"
#import "G8Constants.h"
#import "G8RecognizedBlock.h"
Expand Down Expand Up @@ -349,7 +350,7 @@ - (void)setImage:(UIImage *)image
NSLog(@"ERROR: Image has not size!");
return;
}

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

Pix *pix = nullptr;
Expand All @@ -359,7 +360,7 @@ - (void)setImage:(UIImage *)image
if (thresholdedImage != nil) {
self.imageSize = thresholdedImage.size;

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

Expand All @@ -370,7 +371,7 @@ - (void)setImage:(UIImage *)image
}

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

@try {
Expand Down Expand Up @@ -457,43 +458,48 @@ - (NSString *)recognizedText
- (G8Orientation)orientation
{
if (self.layoutAnalysed == NO) {
[self analyzeLayout];
[self analyseLayout];
}
return _orientation;
}

- (G8WritingDirection)writingDirection
{
if (self.layoutAnalysed == NO) {
[self analyzeLayout];
[self analyseLayout];
}
return _writingDirection;
}

- (G8TextlineOrder)textlineOrder
{
if (self.layoutAnalysed == NO) {
[self analyzeLayout];
[self analyseLayout];
}
return _textlineOrder;
}

- (CGFloat)deskewAngle
{
if (self.layoutAnalysed == NO) {
[self analyzeLayout];
[self analyseLayout];
}
return _deskewAngle;
}

- (void)analyzeLayout
- (void)analyseLayout
{
tesseract::Orientation orientation;
tesseract::WritingDirection direction;
tesseract::TextlineOrder order;
float deskewAngle;

tesseract::PageIterator *iterator = _tesseract->AnalyseLayout();
if (iterator == NULL) {
NSLog(@"Can't analyse layout. Make sure 'osd.traineddata' available in 'tessdata'.");
return;
}

iterator->Orientation(&orientation, &direction, &order, &deskewAngle);
delete iterator;

Expand Down Expand Up @@ -727,76 +733,39 @@ - (UIImage *)imageFromPix:(Pix *)pix
return image;
}

- (Pix *)pixForImage:(UIImage *)image
- (Pix *)pixFromImage:(UIImage *)image
{
int width = image.size.width;
int height = image.size.height;

CGImage *cgImage = image.CGImage;
CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
const UInt8 *pixels = CFDataGetBytePtr(imageData);
CGImageRef cgImage = image.CGImage;
CGAffineTransform transform = [image transformForOrientationFix];
CGSize size = [image sizeForOrientationFix];

size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);

int bpp = MAX(1, (int)bitsPerPixel);
Pix *pix = pixCreate(width, height, bpp == 24 ? 32 : bpp);
l_uint32 *data = pixGetData(pix);
int wpl = pixGetWpl(pix);
switch (bpp) {
case 1:
for (int y = 0; y < height; ++y, data += wpl, pixels += bytesPerRow) {
for (int x = 0; x < width; ++x) {
if (pixels[x / 8] & (0x80 >> (x % 8))) {
CLEAR_DATA_BIT(data, x);
}
else {
SET_DATA_BIT(data, x);
}
}
}
break;
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage) / (int)image.size.width * (int)size.width;

Pix *pix = pixCreateHeader(size.width, size.height, (int)bitsPerPixel);
pixSetPadBits(pix, 0);
pixSetYRes(pix, (l_uint32)self.sourceResolution);

void *data = malloc(4 * pixGetWpl(pix) * pixGetHeight(pix));
CGContextRef ctx = CGBitmapContextCreate(data, size.width, size.height,
CGImageGetBitsPerComponent(cgImage), bytesPerRow,
CGImageGetColorSpace(cgImage),
CGImageGetBitmapInfo(cgImage));
CGContextConcatCTM(ctx, transform);
CGContextDrawImage(ctx, (CGRect){CGPointZero, size}, cgImage);

pixSetData(pix, (l_uint32 *)data);

CGContextRelease(ctx);

case 8:
// Greyscale just copies the bytes in the right order.
for (int y = 0; y < height; ++y, data += wpl, pixels += bytesPerRow) {
for (int x = 0; x < width; ++x) {
SET_DATA_BYTE(data, x, pixels[x]);
}
}
break;

case 24:
// Put the colors in the correct places in the line buffer.
for (int y = 0; y < height; ++y, pixels += bytesPerRow) {
for (int x = 0; x < width; ++x, ++data) {
SET_DATA_BYTE(data, COLOR_RED, pixels[3 * x]);
SET_DATA_BYTE(data, COLOR_GREEN, pixels[3 * x + 1]);
SET_DATA_BYTE(data, COLOR_BLUE, pixels[3 * x + 2]);
}
}
break;

case 32:
// Maintain byte order consistency across different endianness.
for (int y = 0; y < height; ++y, pixels += bytesPerRow, data += wpl) {
for (int x = 0; x < width; ++x) {
data[x] = (pixels[x * 4] << 24) | (pixels[x * 4 + 1] << 16) |
(pixels[x * 4 + 2] << 8) | pixels[x * 4 + 3];
}
}
break;

default:
NSLog(@"Cannot convert image to Pix with bpp = %d", bpp);
}
pixSetYRes(pix, 300);

CFRelease(imageData);

return pix;
}

- (UIImage *)imageByPixConvertation:(UIImage *)image
{
return [self imageFromPix:[self pixFromImage:image]];
}

- (void)tesseractProgressCallbackFunction:(int)words
{
if([self.delegate respondsToSelector:@selector(progressImageRecognitionForTesseract:)]) {
Expand Down
22 changes: 22 additions & 0 deletions TesseractOCR/UIImage+G8FixOrientation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// UIImage+G8FixOrientation.h
// Tesseract OCR iOS
//
// Thanks to `binnyb` for answer from
// http://stackoverflow.com/questions/5427656/ios-uiimagepickercontroller-result-image-orientation-after-upload
//
// Created by Nikolay Volosatov on 09/01/15.
// Copyright (c) 2014 Daniele Galiotto - www.g8production.com.
// All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIImage (G8FixOrientation)

- (CGAffineTransform)transformForOrientationFix;
- (CGSize)sizeForOrientationFix;

- (UIImage *)fixOrientation;

@end
111 changes: 111 additions & 0 deletions TesseractOCR/UIImage+G8FixOrientation.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//
// UIImage+G8FixOrientation.m
// Tesseract OCR iOS
//
// Thanks to `binnyb` for answer from
// http://stackoverflow.com/questions/5427656/ios-uiimagepickercontroller-result-image-orientation-after-upload
//
// Created by Nikolay Volosatov on 09/01/15.
// Copyright (c) 2014 Daniele Galiotto - www.g8production.com.
// All rights reserved.
//

#import "UIImage+G8FixOrientation.h"

@implementation UIImage (G8FixOrientation)

- (CGAffineTransform)transformForOrientationFix
{
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;

switch (self.imageOrientation)
{
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;

case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;

case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, self.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;

case UIImageOrientationUp:
case UIImageOrientationUpMirrored:
break;
}

switch (self.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;

case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, self.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;

case UIImageOrientationUp:
case UIImageOrientationDown:
case UIImageOrientationLeft:
case UIImageOrientationRight:
break;
}

return transform;
}

- (CGSize)sizeForOrientationFix
{
switch (self.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
return CGSizeMake(self.size.height, self.size.width);

default:
return self.size;
}
}

- (UIImage *)fixOrientation
{
// No-op if the orientation is already correct
if (self.imageOrientation == UIImageOrientationUp) return self;

CGAffineTransform transform = [self transformForOrientationFix];

// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGImageRef cgImage = self.CGImage;
CGSize size = [self sizeForOrientationFix];
CGContextRef ctx = CGBitmapContextCreate(NULL, size.width, size.height,
CGImageGetBitsPerComponent(cgImage), 0,
CGImageGetColorSpace(cgImage),
CGImageGetBitmapInfo(cgImage));
CGContextConcatCTM(ctx, transform);
CGContextDrawImage(ctx, (CGRect){CGPointZero, size}, cgImage);

// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}

@end
2 changes: 1 addition & 1 deletion TestsProject/TestsProjectTests/G8RecognitionTestsHelper.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ - (id)init
_sourceResolution = 0;

_customPreprocessingType = G8CustomPreprocessingNone;
_boundingSizeForResizing = CGSizeMake(700.0f, 700.0f);
_boundingSizeForResizing = CGSizeMake(900.0f, 900.0f);
}
return self;
}
Expand Down

0 comments on commit dd8e782

Please sign in to comment.