Skip to content

Commit

Permalink
Merge pull request #17 from onaio/support-dynamically-setting-rdt-con…
Browse files Browse the repository at this point in the history
…figs

Add RDT constructor to allow dynamic passing of rdt configurations
  • Loading branch information
vincent-karuri committed Nov 6, 2020
2 parents 8ea2381 + 7fb377f commit e9ad215
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 87 deletions.
2 changes: 1 addition & 1 deletion lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.bintray'

group = "io.ona.rdt-capture"
version = '2.0.0'
version = '2.1.0'
def home = System.properties['user.home']

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import android.content.Intent;
import android.os.Bundle;

import org.json.JSONException;
import org.json.JSONObject;

import edu.washington.cs.ubicomplab.rdt_reader.R;
import edu.washington.cs.ubicomplab.rdt_reader.core.RDTCaptureResult;
import edu.washington.cs.ubicomplab.rdt_reader.core.RDTInterpretationResult;
Expand All @@ -12,6 +15,7 @@
import edu.washington.cs.ubicomplab.rdt_reader.views.ImageQualityView;

import static edu.washington.cs.ubicomplab.rdt_reader.core.Constants.DEFAULT_RDT_NAME;
import static edu.washington.cs.ubicomplab.rdt_reader.core.Constants.RDT_JSON_CONFIG;

/**
* The {@link android.app.Activity} for showing a real-time camera feed during image capture and
Expand Down Expand Up @@ -43,6 +47,16 @@ protected void onCreate(Bundle savedInstanceState) {
} else {
mImageQualityView.setRDTName(DEFAULT_RDT_NAME);
}

try {
// Extract config json obj
String rdtConfig = b.getString(RDT_JSON_CONFIG, null);
if (rdtConfig != null) {
mImageQualityView.setRdtJsonConfig(new JSONObject(rdtConfig));
}
} catch (JSONException e) {
e.printStackTrace();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,6 @@ public final class Constants {
public static final int REQUEST_CAMERA_PERMISSION = 1;
public static String SAVED_IMAGE_FILE_PATH = "saved_image_file_path";
public static String SAVED_IMAGE_RESULT = "saved_image_result";

public static String RDT_JSON_CONFIG = "rdt_json_config";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.content.Context;
import android.util.Log;

import org.json.JSONObject;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
Expand Down Expand Up @@ -127,12 +128,17 @@ public ImageProcessor(Activity activity, String rdtName) {
Log.d(TAG, "REFERENCE LOAD/DETECT/COMPUTE: " + (System.currentTimeMillis() - startTime));
}

/**
* Singleton creation method for this class
* @param activity: the activity that is using this code
* @param rdtName: the name of the target RDT
* @return an instance of this class if one already exists, otherwise creates a new one
*/
private ImageProcessor(JSONObject rdtConfig) {
mRDT = new RDT(rdtConfig);
mRDT.refImgSharpness = measureSharpness(mRDT.refImg);
}

public static ImageProcessor getInstance(JSONObject rdtConfig) {
if (instance == null)
instance = new ImageProcessor(rdtConfig);
return instance;
}

public static ImageProcessor getInstance(Activity activity, String rdtName) {
if (instance == null)
instance = new ImageProcessor(activity, rdtName);
Expand Down
184 changes: 105 additions & 79 deletions lib/src/main/java/edu/washington/cs/ubicomplab/rdt_reader/core/RDT.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package edu.washington.cs.ubicomplab.rdt_reader.core;

import android.app.ListActivity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;

import org.json.JSONArray;
import org.json.JSONException;
Expand Down Expand Up @@ -60,6 +60,15 @@ public class RDT {

public boolean rotated = false;


public RDT(JSONObject rdtConfig) {
try {
init(rdtConfig, convertBase64StrToBitmap(rdtConfig.optString("REF_IMG")));
} catch (Exception ex) {
ex.printStackTrace();
}
}

public RDT(Context context, String rdtName) {
try {
// Read config.json
Expand All @@ -69,89 +78,106 @@ public RDT(Context context, String rdtName) {
is.read(buffer);
is.close();
JSONObject obj = new JSONObject(new String(buffer, "UTF-8")).getJSONObject(rdtName);

// Load the template image
refImageID = context.getResources().getIdentifier(obj.getString("REF_IMG"),
"drawable", context.getPackageName());
refImg = new Mat();
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), refImageID);
Utils.bitmapToMat(bitmap, refImg);

if(refImg.height() > refImg.width()) {
Core.rotate(refImg, refImg, Core.ROTATE_90_COUNTERCLOCKWISE);
rotated = true;
}

cvtColor(refImg, refImg, Imgproc.COLOR_RGB2GRAY);
this.rdtName = rdtName;

// Pull data related to UI
viewFinderScaleH = obj.getDouble("VIEW_FINDER_SCALE");
viewFinderScaleW = (viewFinderScaleH * (double)refImg.height()/(double)refImg.width())+Constants.VIEW_FINDER_SCALE_W_PADDING;
//viewFinderScaleW = obj.getDouble("VIEW_FINDER_SCALE_W");
JSONArray rectTL = obj.getJSONArray("RESULT_WINDOW_TOP_LEFT");
JSONArray rectBR = obj.getJSONArray("RESULT_WINDOW_BOTTOM_RIGHT");
resultWindowRect = rotated ? new Rect(new Point(rectTL.getDouble(1), rectTL.getDouble(0)),
new Point(rectBR.getDouble(1), rectBR.getDouble(0))) :
new Rect(new Point(rectTL.getDouble(0), rectTL.getDouble(1)),
new Point(rectBR.getDouble(0), rectBR.getDouble(1)));

// Pull data related to the result window
topLinePosition = rotated ? obj.getJSONArray("TOP_LINE_POSITION").getDouble(1) - resultWindowRect.x : obj.getJSONArray("TOP_LINE_POSITION").getDouble(0) - resultWindowRect.x;
middleLinePosition = rotated ? obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(1) - resultWindowRect.x: obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(0) - resultWindowRect.x;
bottomLinePosition = rotated ? obj.getJSONArray("BOTTOM_LINE_POSITION").getDouble(1) - resultWindowRect.x: obj.getJSONArray("BOTTOM_LINE_POSITION").getDouble(0) - resultWindowRect.x;
topLineName = obj.getString("TOP_LINE_NAME");
middleLineName = obj.getString("MIDDLE_LINE_NAME");
bottomLineName = obj.getString("BOTTOM_LINE_NAME");
lineIntensity = obj.getInt("LINE_INTENSITY");
lineSearchWidth = obj.has("LINE_SEARCH_WIDTH") ? obj.getInt("LINE_SEARCH_WIDTH") :
Math.max((int)((middleLinePosition-topLinePosition)/2.0),(int)((bottomLinePosition-middleLinePosition)/2.0));

checkGlare = obj.has("CHECK_GLARE") ? obj.getBoolean("CHECK_GLARE") : false;

// Pull data related to fiducials
fiducials = obj.has("FIDUCIALS") ? obj.getJSONArray("FIDUCIALS") : new JSONArray();
hasFiducial = fiducials.length() > 0;
distanctFromFiducialToResultWindow = 0;

if (hasFiducial && fiducials.length() == 2) {
JSONArray trueFiducial1 = fiducials.getJSONArray(0);
Point trueFiducialTL1 = rotated
? new Point(trueFiducial1.getJSONArray(0).getDouble(1), trueFiducial1.getJSONArray(0).getDouble(0))
: new Point(trueFiducial1.getJSONArray(0).getDouble(0), trueFiducial1.getJSONArray(0).getDouble(1));
Point trueFiducialBR1 = rotated
? new Point(trueFiducial1.getJSONArray(1).getDouble(1), trueFiducial1.getJSONArray(1).getDouble(0))
: new Point(trueFiducial1.getJSONArray(1).getDouble(0), trueFiducial1.getJSONArray(1).getDouble(1));

JSONArray trueFiducial2 = fiducials.getJSONArray(1);
Point trueFiducialTL2 = rotated
? new Point(trueFiducial2.getJSONArray(0).getDouble(1), trueFiducial2.getJSONArray(0).getDouble(0))
: new Point(trueFiducial2.getJSONArray(0).getDouble(0), trueFiducial2.getJSONArray(0).getDouble(1));
Point trueFiducialBR2 = rotated
? new Point(trueFiducial2.getJSONArray(1).getDouble(1), trueFiducial2.getJSONArray(1).getDouble(0))
: new Point(trueFiducial2.getJSONArray(1).getDouble(0), trueFiducial2.getJSONArray(1).getDouble(1));

fiducialRects = new ArrayList<>();

fiducialRects.add(new Rect(trueFiducialTL1, trueFiducialBR1));
fiducialRects.add(new Rect(trueFiducialTL2, trueFiducialBR2));

distanctFromFiducialToResultWindow = resultWindowRect.x - (trueFiducialBR2.x + trueFiducialBR1.x)/2.0;
}

// Store the reference's sharpness
Size kernel = new Size(SHARPNESS_GAUSSIAN_BLUR_WINDOW,
SHARPNESS_GAUSSIAN_BLUR_WINDOW);
Imgproc.GaussianBlur(refImg, refImg, kernel, 0, 0);

// Load the reference image's features
refDescriptor = new Mat();
refKeypoints = new MatOfKeyPoint();
detector = SIFT.create();
matcher = BFMatcher.create(BFMatcher.BRUTEFORCE, false);
detector.detectAndCompute(refImg, new Mat(), refKeypoints, refDescriptor);
init(obj, bitmap);
} catch (Exception ex) {
ex.printStackTrace();
}
}

private Bitmap convertBase64StrToBitmap(String base64Str) {
return convertByteArrayToBitmap(Base64.decode(base64Str.getBytes(), Base64.DEFAULT));
}

private Bitmap convertByteArrayToBitmap(final byte[] src) {
return BitmapFactory.decodeByteArray(src, 0, src.length);
}

private void init(JSONObject obj, Bitmap bitmap) throws JSONException {
// Load the template image
refImg = new Mat();
Utils.bitmapToMat(bitmap, refImg);

if(refImg.height() > refImg.width()) {
Core.rotate(refImg, refImg, Core.ROTATE_90_COUNTERCLOCKWISE);
rotated = true;
}

cvtColor(refImg, refImg, Imgproc.COLOR_RGB2GRAY);
this.rdtName = rdtName;

// Pull data related to UI
viewFinderScaleH = obj.getDouble("VIEW_FINDER_SCALE");
viewFinderScaleW = (viewFinderScaleH * (double)refImg.height()/(double)refImg.width())+Constants.VIEW_FINDER_SCALE_W_PADDING;
//viewFinderScaleW = obj.getDouble("VIEW_FINDER_SCALE_W");
JSONArray rectTL = obj.getJSONArray("RESULT_WINDOW_TOP_LEFT");
JSONArray rectBR = obj.getJSONArray("RESULT_WINDOW_BOTTOM_RIGHT");
resultWindowRect = rotated ? new Rect(new Point(rectTL.getDouble(1), rectTL.getDouble(0)),
new Point(rectBR.getDouble(1), rectBR.getDouble(0))) :
new Rect(new Point(rectTL.getDouble(0), rectTL.getDouble(1)),
new Point(rectBR.getDouble(0), rectBR.getDouble(1)));

// Pull data related to the result window
topLinePosition = rotated ? obj.getJSONArray("TOP_LINE_POSITION").getDouble(1) - resultWindowRect.x : obj.getJSONArray("TOP_LINE_POSITION").getDouble(0) - resultWindowRect.x;
middleLinePosition = rotated ? obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(1) - resultWindowRect.x: obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(0) - resultWindowRect.x;
bottomLinePosition = getBottomLinePosition(obj, rotated);
topLineName = obj.getString("TOP_LINE_NAME");
middleLineName = obj.getString("MIDDLE_LINE_NAME");
bottomLineName = obj.optString("BOTTOM_LINE_NAME");
lineIntensity = obj.getInt("LINE_INTENSITY");
lineSearchWidth = obj.has("LINE_SEARCH_WIDTH") ? obj.getInt("LINE_SEARCH_WIDTH") :
Math.max((int)((middleLinePosition-topLinePosition)/2.0),(int)((bottomLinePosition-middleLinePosition)/2.0));

checkGlare = obj.has("CHECK_GLARE") ? obj.getBoolean("CHECK_GLARE") : false;

// Pull data related to fiducials
fiducials = obj.has("FIDUCIALS") ? obj.getJSONArray("FIDUCIALS") : new JSONArray();
hasFiducial = fiducials.length() > 0;
distanctFromFiducialToResultWindow = 0;

if (hasFiducial && fiducials.length() == 2) {
JSONArray trueFiducial1 = fiducials.getJSONArray(0);
Point trueFiducialTL1 = rotated
? new Point(trueFiducial1.getJSONArray(0).getDouble(1), trueFiducial1.getJSONArray(0).getDouble(0))
: new Point(trueFiducial1.getJSONArray(0).getDouble(0), trueFiducial1.getJSONArray(0).getDouble(1));
Point trueFiducialBR1 = rotated
? new Point(trueFiducial1.getJSONArray(1).getDouble(1), trueFiducial1.getJSONArray(1).getDouble(0))
: new Point(trueFiducial1.getJSONArray(1).getDouble(0), trueFiducial1.getJSONArray(1).getDouble(1));

JSONArray trueFiducial2 = fiducials.getJSONArray(1);
Point trueFiducialTL2 = rotated
? new Point(trueFiducial2.getJSONArray(0).getDouble(1), trueFiducial2.getJSONArray(0).getDouble(0))
: new Point(trueFiducial2.getJSONArray(0).getDouble(0), trueFiducial2.getJSONArray(0).getDouble(1));
Point trueFiducialBR2 = rotated
? new Point(trueFiducial2.getJSONArray(1).getDouble(1), trueFiducial2.getJSONArray(1).getDouble(0))
: new Point(trueFiducial2.getJSONArray(1).getDouble(0), trueFiducial2.getJSONArray(1).getDouble(1));

fiducialRects = new ArrayList<>();

fiducialRects.add(new Rect(trueFiducialTL1, trueFiducialBR1));
fiducialRects.add(new Rect(trueFiducialTL2, trueFiducialBR2));

distanctFromFiducialToResultWindow = resultWindowRect.x - (trueFiducialBR2.x + trueFiducialBR1.x)/2.0;
}

// Store the reference's sharpness
Size kernel = new Size(SHARPNESS_GAUSSIAN_BLUR_WINDOW,
SHARPNESS_GAUSSIAN_BLUR_WINDOW);
Imgproc.GaussianBlur(refImg, refImg, kernel, 0, 0);

// Load the reference image's features
refDescriptor = new Mat();
refKeypoints = new MatOfKeyPoint();
detector = SIFT.create();
matcher = BFMatcher.create(BFMatcher.BRUTEFORCE, false);
detector.detectAndCompute(refImg, new Mat(), refKeypoints, refDescriptor);
}

private double getBottomLinePosition(JSONObject rdtConfig, boolean rotated) throws JSONException {
return rdtConfig.optJSONArray("BOTTOM_LINE_POSITION") == null ? 0
: rotated ? rdtConfig.getJSONArray("BOTTOM_LINE_POSITION").getDouble(1) - resultWindowRect.x
: rdtConfig.getJSONArray("BOTTOM_LINE_POSITION").getDouble(0) - resultWindowRect.x;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONObject;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.core.Mat;
Expand Down Expand Up @@ -96,6 +97,7 @@ public class ImageQualityView extends LinearLayout implements View.OnClickListen
private boolean showViewport;
private boolean showFeedback;
private AutoFitTextureView mTextureView;
private JSONObject rdtJsonConfig;

// Image processing variables
private ImageProcessor processor;
Expand Down Expand Up @@ -258,7 +260,8 @@ public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
Log.i(TAG, "OpenCV loaded successfully");
processor = ImageProcessor.getInstance(mActivity, rdtName);
processor = getRdtJsonConfig() == null ? ImageProcessor.getInstance(mActivity, rdtName)
: ImageProcessor.getInstance(getRdtJsonConfig());
ViewportUsingBitmap viewport = findViewById(R.id.img_quality_check_viewport);
viewport.hScale = (float) processor.mRDT.viewFinderScaleH;
viewport.wScale = (float) processor.mRDT.viewFinderScaleW;
Expand Down Expand Up @@ -1060,4 +1063,12 @@ private boolean continueProcessingImg(Image image) {
}
return true;
}

public JSONObject getRdtJsonConfig() {
return rdtJsonConfig;
}

public void setRdtJsonConfig(JSONObject rdtJsonConfig) {
this.rdtJsonConfig = rdtJsonConfig;
}
}

0 comments on commit e9ad215

Please sign in to comment.