Example #1
0
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
}