DMZ_INTERNAL void scan_card_image(IplImage *y, bool collect_card_number, bool scan_expiry, FrameScanResult *result) { assert(NULL == y->roi); assert(y->width == 428); assert(y->height == 270); assert(y->depth == IPL_DEPTH_8U); assert(y->nChannels == 1); result->upside_down = false; result->usable = false; result->vseg = best_n_vseg(y); // TODO - report this // If the best vseg is in the top half of the card, // return early and indicate that the card is upside-down. if (result->vseg.y_offset < kFlipVSegYOffsetCutoff) { result->upside_down = true; return; } result->usable = result->vseg.score > kMinVSegScore; if(!result->usable) { dmz_debug_log("vseg.score %f unusable", result->vseg.score); return; } if (collect_card_number) { cvSetImageROI(y, cvRect(0, result->vseg.y_offset, kCreditCardTargetWidth, kNumberHeight)); result->hseg = best_n_hseg(y, result->vseg); // I've not found the hseg score to be a reliable indicator of quality at all // Unsurprising, since this is the hardest phase of the pipeline, and we're struggling // just to find anything at all! // // if(!result->usable) { // cvResetImageROI(y); // return result; // } result->scores = number_scores(y, result->hseg); float number_score = result->hseg.n_offsets - result->scores.sum(); result->usable = number_score < kMaxNumberScoreDelta; if (!result->usable) { dmz_debug_log("number_score %f unusable", number_score); } cvResetImageROI(y); } #if SCAN_EXPIRY if (scan_expiry && result->vseg.y_offset < kCreditCardTargetHeight - 2 * kSmallCharacterHeight) { best_expiry_seg(y, result->vseg.y_offset, result->expiry_groups, result->name_groups); #if DMZ_DEBUG if (result->expiry_groups.empty()) { dmz_debug_log("Expiry segmentation failed."); } #endif } #endif }
void logDinfo(JNIEnv* env, jobject dinfo) { dmz_debug_log("dinfo: complete=%i", env->GetBooleanField(dinfo, detectionInfoId.complete)); jintArray digitArray = (jintArray) env->GetObjectField(dinfo, detectionInfoId.prediction); dmz_debug_log("dinfo: prediction[0-3]=%i%i%i%i...", env->GetIntArrayElements(digitArray, NULL)[0], env->GetIntArrayElements(digitArray, NULL)[1], env->GetIntArrayElements(digitArray, NULL)[2], env->GetIntArrayElements(digitArray, NULL)[3]); }
void setScanCardNumberResult(JNIEnv* env, jobject dinfo, ScannerResult* scanResult) { jint numbers[16]; jint offsets[16]; for (int i = 0; i < scanResult->n_numbers; i++) { numbers[i] = scanResult->predictions(i); dmz_debug_log("prediction[%i]= %i", i, scanResult->predictions(i)); offsets[i] = scanResult->hseg.offsets[i]; dmz_debug_log("offsets[%i]= %i", i, scanResult->hseg.offsets[i]); } jobject digitArray = env->GetObjectField(dinfo, detectionInfoId.prediction); dmz_debug_log("setting prediction array region"); env->SetIntArrayRegion((jintArray)digitArray, 0, scanResult->n_numbers, numbers); jobject cardObj = env->GetObjectField(dinfo, detectionInfoId.detectedCard); dmz_debug_log("got cardObj: %x", cardObj); env->SetIntField(cardObj, creditCardId.yoff, scanResult->vseg.y_offset); jobject xoffArray = env->GetObjectField(cardObj, creditCardId.xoff); dmz_debug_log("setting xoffset array region: %x", xoffArray); env->SetIntArrayRegion((jintArray)xoffArray, 0, scanResult->n_numbers, offsets); dmz_debug_log("setting expiry to %i/%i", scanResult->expiry_month, scanResult->expiry_year); env->SetIntField(dinfo, detectionInfoId.expiry_month, scanResult->expiry_month); env->SetIntField(dinfo, detectionInfoId.expiry_year, scanResult->expiry_year); dmz_debug_log("setting detectionInfoId.complete=true"); env->SetBooleanField(dinfo, detectionInfoId.complete, true); dmz_debug_log("done in setScanCardNumberResult()"); }
JNIEXPORT void JNICALL Java_io_card_payment_CardScanner_nCleanup(JNIEnv *env, jobject thiz) { dmz_debug_log("Java_io_card_payment_CardScanner_nCleanup"); if (dmz_refcount == 1) { scanner_destroy(&scannerState); dmz_context_destroy(dmz); dmz = NULL; } dmz_refcount--; }
void llcv_gles_teardown(llcv_gles_context *mz) { dmz_debug_log("tearing down gles"); eglMakeCurrent(mz->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mz->egl_display, mz->egl_context); eglDestroySurface(mz->egl_display, mz->egl_surface); eglTerminate(mz->egl_display); mz->egl_display = NULL; llcv_gl_error_count(); }
JNIEXPORT void JNICALL Java_io_card_payment_CardScanner_nSetup(JNIEnv *env, jobject thiz, jboolean shouldOnlyDetectCard, jfloat jMinFocusScore) { dmz_debug_log("Java_io_card_payment_CardScanner_nSetup"); dmz_trace_log("dmz trace enabled"); detectOnly = shouldOnlyDetectCard; minFocusScore = jMinFocusScore; flipped = false; lastFrameWasUsable = false; if (dmz == NULL) { dmz = dmz_context_create(); scanner_initialize(&scannerState); } else { scanner_reset(&scannerState); } dmz_refcount++; cvSetErrMode(CV_ErrModeParent); }
void llcv_gles_setup(llcv_gles_context* mz, int width, int height) { mz->pbufWidth = width; mz->pbufHeight = height; if (dmz_use_gles_warp() && mz->egl_display == NULL) { dmz_debug_log("setting up %i x %i rendering surface", mz->pbufWidth, mz->pbufHeight); mz->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); EGLint major, minor, error; if (eglInitialize(mz->egl_display, &major, &minor) && mz->egl_display != EGL_NO_DISPLAY ) { dmz_debug_log("Initialized GLES and got version %i.%i", major, minor); } else { int n_err = llcv_gl_error_count(); dmz_debug_log("Failed to initialize GLES display with %i errors", n_err); // llcv_gl_supported = false; return; } int attribList[] = { EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; EGLint max_configs = 5; EGLint num_configs = 0; EGLConfig configs[max_configs]; eglChooseConfig(mz->egl_display, attribList, configs, max_configs, &num_configs); if (num_configs == 0) { llcv_gl_error_count(); dmz_debug_log("didn't get any EGL configs!"); // llcv_gl_supported = false; return; } mz->egl_context = eglCreateContext(mz->egl_display, configs[0], EGL_NO_CONTEXT, NULL); if (mz->egl_context == EGL_NO_CONTEXT) { llcv_gl_error_count(); dmz_debug_log("Failed to create an EGL context"); // llcv_gl_supported = false; return; } int pbAttribList[] = { EGL_WIDTH, mz->pbufWidth, EGL_HEIGHT, mz->pbufHeight, EGL_NONE }; mz->egl_surface = eglCreatePbufferSurface(mz->egl_display, configs[0], pbAttribList); if (! eglMakeCurrent(mz->egl_display, mz->egl_surface, mz->egl_surface, mz->egl_context)) { llcv_gl_error_count(); dmz_debug_log("gles warp not available"); return; } // "onSurfaceCreated" glGenTextures(1, &(mz->gl_texture)); glBindTexture(GL_TEXTURE_2D, mz->gl_texture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glEnable(GL_TEXTURE_2D); //Enable Texture Mapping ( NEW ) glShadeModel(GL_SMOOTH); //Enable Smooth Shading glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //Black Background if (llcv_gl_error_count()) return; glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // "onSurfaceChanged" glViewport(0, 0, mz->pbufWidth, mz->pbufHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(-1, 1, -1, 1, -1, 1); if (llcv_gl_error_count()) return; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // onetime setup GLfloat h = 1.0f; GLfloat w = 1.0f; // gl_verts = { // -w, -h, 0.0f, // V1 - bottom left // -w, h, 0.0f, // V2 - top left // w, -h, 0.0f, // V3 - bottom right // w, h, 0.0f // V4 - top right // }; // appease the compiler. GLfloat* gl_verts = mz->gl_verts; gl_verts[0] = gl_verts[3] = -w; gl_verts[1] = gl_verts[7] = -h; gl_verts[2] = gl_verts[5] = gl_verts[8] = gl_verts[11] = 0.0f; gl_verts[4] = gl_verts[10] = h; gl_verts[6] = gl_verts[9] = w; if (llcv_gl_error_count()) return; } }
void llcv_gles_warp_perspective(void* mzv, IplImage *input, const dmz_point corners[4], IplImage *card) { #if ANDROID_USE_GLES_WARP llcv_gles_context* mz = (llcv_gles_context*) mzv; // the docs say somewhere that width & height must be even assert(input->height % 2 == 0); assert(input->width % 2 == 0); assert(input->imageData != NULL); dmz_debug_log("gles warp: engage!"); int err_count = 0; GLfloat texCorners[] = { corners[0].x/input->width, corners[0].y/input->height, corners[2].x/input->width, corners[2].y/input->height, corners[1].x/input->width, corners[1].y/input->height, corners[3].x/input->width, corners[3].y/input->height, }; #if DMZ_DEBUG for (int i=0; i<8; i+=2) { dmz_debug_log("\tsurface corner: (%f, %f)", texCorners[i], texCorners[i+1]); } #endif assert(card->width == mz->pbufWidth); assert(card->height == mz->pbufHeight); GLint bufFormat; if (input->nChannels == 1) bufFormat = GL_LUMINANCE; else if (input->nChannels == 3) bufFormat = GL_RGB; else if (input->nChannels == 4) bufFormat = GL_RGBA; else { dmz_debug_log("unexpected number of channels: %i I expected one of {1, 3, 4}", input->nChannels); return; } glTexImage2D(GL_TEXTURE_2D, 0, bufFormat, input->width, input->height, 0, bufFormat, GL_UNSIGNED_BYTE, input->imageData); if (llcv_gl_error_count()) return; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); if (llcv_gl_error_count()) return; glBindTexture(GL_TEXTURE_2D, mz->gl_texture); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (llcv_gl_error_count()) return; glFrontFace(GL_CW); err_count = llcv_gl_error_count(); if (err_count) dmz_debug_log("%i errors after enable client state", err_count); glVertexPointer(3, GL_FLOAT, 0, mz->gl_verts); glTexCoordPointer(2, GL_FLOAT, 0, texCorners); err_count = llcv_gl_error_count(); if (err_count) dmz_debug_log("%i errors after pointer", err_count); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); err_count = llcv_gl_error_count(); if (err_count) dmz_debug_log("%i errors after drawArrays", err_count); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); err_count = llcv_gl_error_count(); if (err_count) dmz_debug_log("%i errors at end of warp", err_count); dmz_debug_log("reading pixels %i x %i", card->width, card->height); glReadPixels(0, 0, card->width, card->height, GL_RGBA, GL_UNSIGNED_BYTE, card->imageData); err_count = llcv_gl_error_count(); if (err_count) dmz_debug_log("%i errors after readPixels", err_count); #endif // ANDROID_USE_GLES_WARP }