diff --git a/dmz_all.cpp b/dmz_all.cpp index 45ee186..8dbf4f8 100644 --- a/dmz_all.cpp +++ b/dmz_all.cpp @@ -31,6 +31,7 @@ #include "./scan/n_vseg.cpp" #include "./scan/scan.cpp" #include "./scan/scan_analytics.cpp" +#include "./scan/ocre.cpp" #if SCAN_EXPIRY #include "./models/expiry/modelc_bf4dd6c8.cpp" diff --git a/scan/ocre.cpp b/scan/ocre.cpp new file mode 100644 index 0000000..8cfcf46 --- /dev/null +++ b/scan/ocre.cpp @@ -0,0 +1,178 @@ + +#if COMPILE_DMZ +#include "ocre.h" +#include "opencv2/core/core.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "dmz_debug.h" +#include "dmz_olm.h" /* Luhn Check*/ + + +tesseract::TessBaseAPI *_tessBaseAPI; +char* _cardNumber; + +const int NUMDEFS = 7; +const std::string ROTATIONS = "1"; +const int DEFINITIONS[7][4][4] = { + + + /*laCaixa Visa New*/ + { + {45, 155, 75, 25}, + {115, 155, 75, 25}, + {195, 155, 75, 25}, + {275, 155, 75, 25} + }, + /*CBGC Buy Powercard*/ + { + {15, 256, 45, 20}, + {60, 256, 45, 20}, + {105, 256, 45, 20}, + {150, 256, 45, 20}, + }, + /*Venture Card Laser Personalization Vertical*/ + { + {20, 78, 70, 23}, + {20, 116, 70, 23}, + {20, 153, 70, 23}, + {20, 192, 70, 23} + }, + /*CMC World Elite*/ + { + {18, 108, 56, 20}, + {74, 108, 56, 20}, + {134, 174, 56, 20}, + {194, 174, 56, 20} + }, + /*Quicksilver Ultragraphics flat print Horizontal*/ + { + {16, 197, 50, 20}, + {70, 197, 50, 20}, + {122, 197, 50, 20}, + {175, 197, 50, 20} + }, + /*Capital One All Point 360*/ + { + {12, 174, 65, 24}, + {75, 174, 65, 24}, + {145, 174, 65, 24}, + {210, 174, 65, 24} + }, + /*Venture Card Laser Personalization Horizontal*/ + { + {16, 200, 50, 20}, + {62, 200, 50, 20}, + {112, 200, 50, 20}, + {160, 200, 50, 20} + } +}; + +/* + * Initialize Tesseract API + * Loads the custom trained data for Capital One Cards. + */ +void ocre_init() { + _tessBaseAPI = new tesseract::TessBaseAPI(); + if ( _tessBaseAPI->Init(NULL, "co") ) { + dmz_debug_print("Failed to init Tesseract. Are you missing tessdata?\n"); + _tessBaseAPI = NULL; + } +} + +/** + * Scan an cropped IplImage + * param grayscaleCroppedImage the pre-cropped image passed + * from card.io in grayscale + * param digits the number of digits we are looking for (15-16) + */ +void ocre_scanImage( IplImage *grayscaleCroppedImage, int digits) { + if( grayscaleCroppedImage == NULL || _tessBaseAPI == NULL ) { + return; + } + //convert to something we can manipulate. + CvMat header, *mat = cvGetMat( grayscaleCroppedImage, &header ); + cv::Mat grayscaleMat= cv::Mat(mat); + cv::Mat rotatedMat = grayscaleMat.clone(); + //prep rotated matrix + cv::transpose(rotatedMat, rotatedMat); + cv::flip(rotatedMat, rotatedMat, 1); + std::string numberWithoutSpaces = ""; + uint8_t * luhnCheck = (uint8_t*)malloc(sizeof(uint8_t)*digits); + + _tessBaseAPI->SetImage(grayscaleMat.data, grayscaleMat.cols, grayscaleMat.rows, grayscaleMat.channels(), grayscaleMat.step1()); + _tessBaseAPI->SetVariable("tessedit_char_whitelist", "0123456789"); + + for(int i = 0; iSetImage(rotatedMat.data, rotatedMat.cols, rotatedMat.rows, rotatedMat.channels(), rotatedMat.step1()); + } + for(int j = 0; j<4; j++) { + _tessBaseAPI->SetRectangle(DEFINITIONS[i][j][0], DEFINITIONS[i][j][1], DEFINITIONS[i][j][2], DEFINITIONS[i][j][3]); + _tessBaseAPI->Recognize(0); + numberWithoutSpaces.append(_tessBaseAPI->GetUTF8Text()); + } + if( needsRotation ) { + //reset the image + _tessBaseAPI->SetImage(grayscaleMat.data, grayscaleMat.cols, grayscaleMat.rows, grayscaleMat.channels(), grayscaleMat.step1()); + } + + numberWithoutSpaces.erase(remove_if(numberWithoutSpaces.begin(), numberWithoutSpaces.end(), (int(*)(int)) isspace), numberWithoutSpaces.end()); + + if ( numberWithoutSpaces.length() != digits) + { + numberWithoutSpaces = ""; + continue; + } + + for(int i=0; iEnd(); +} + +#endif \ No newline at end of file diff --git a/scan/ocre.h b/scan/ocre.h new file mode 100644 index 0000000..da88189 --- /dev/null +++ b/scan/ocre.h @@ -0,0 +1,33 @@ + + + + + + +#ifndef OCRE +#define OCRE + +#include "tesseract/baseapi.h" +#include "leptonica/allheaders.h" +#include "frame.h" + +void ocre_init(); +void ocre_scanImage( IplImage *croppedImage, int digits = 16 ); +bool ocre_complete(); +char* ocre_result(); +void ocre_reset(); +void ocre_destroy(); + +/* Android 'to_string' std support */ +#include +#include + +template +std::string convert_to_string(T value) +{ + std::ostringstream os ; + os << value ; + return os.str() ; +} + +#endif \ No newline at end of file diff --git a/scan/scan.cpp b/scan/scan.cpp index 9d8e40b..1e3dd01 100644 --- a/scan/scan.cpp +++ b/scan/scan.cpp @@ -9,6 +9,7 @@ #include "scan.h" #include "expiry_categorize.h" #include "expiry_seg.h" +#include "ocre.h" #define SCAN_FOREVER 0 // useful for performance profiling #define EXTRA_TIME_FOR_EXPIRY_IN_MICROSECONDS 1000 // once the card number has been successfully identified, allow a bit more time to figure out the expiry @@ -18,6 +19,31 @@ void scanner_initialize(ScannerState *state) { scanner_reset(state); + ocre_init(); +} + +void ocre_scanner_init() { + ocre_init(); +} + +void ocre_scanner_scan(IplImage *y) { + ocre_scanImage(y); +} + +bool ocre_scanner_complete() { + return ocre_complete(); +} + +char* ocre_scanner_result() { + return ocre_result(); +} + +void ocre_scanner_reset() { + ocre_reset(); +} + +void ocre_scanner_destroy() { + ocre_destroy(); } void scanner_reset(ScannerState *state) { @@ -194,7 +220,7 @@ void scanner_result(ScannerState *state, ScannerResult *result) { } void scanner_destroy(ScannerState *state) { - // currently a no-op + ocre_scanner_destroy(); } diff --git a/scan/scan.h b/scan/scan.h index 397bd9e..09b55b6 100644 --- a/scan/scan.h +++ b/scan/scan.h @@ -12,6 +12,7 @@ #include "expiry_seg.h" #include + // TODO: Somewhere expose some data+analytics to send to a server for future model training... typedef Eigen::Matrix NumberPredictions; // one prediction per number (up to 16 of them) @@ -63,6 +64,12 @@ void scanner_reset(ScannerState *state); // will be set to true (and result->usable to false) void scanner_add_frame(ScannerState *state, IplImage *y, FrameScanResult *result); // pre-expiry backward-compatible version void scanner_add_frame_with_expiry(ScannerState *state, IplImage *y, bool scan_expiry, FrameScanResult *result); +void ocre_scanner_init(); +void ocre_scanner_scan(IplImage *y); +bool ocre_scanner_complete(); +char* ocre_scanner_result(); +void ocre_scanner_reset(); +void ocre_scanner_destroy(); // Ask the scanner for its number predictions. // If result.complete is false, the rest of the result must be ignored.