Skip to content

Commit e9ad215

Browse files
Merge pull request #17 from onaio/support-dynamically-setting-rdt-configs
Add RDT constructor to allow dynamic passing of rdt configurations
2 parents 8ea2381 + 7fb377f commit e9ad215

File tree

6 files changed

+146
-87
lines changed

6 files changed

+146
-87
lines changed

lib/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ apply plugin: 'maven-publish'
33
apply plugin: 'com.jfrog.bintray'
44

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

99
android {

lib/src/main/java/edu/washington/cs/ubicomplab/rdt_reader/activities/ImageQualityActivity.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import android.content.Intent;
55
import android.os.Bundle;
66

7+
import org.json.JSONException;
8+
import org.json.JSONObject;
9+
710
import edu.washington.cs.ubicomplab.rdt_reader.R;
811
import edu.washington.cs.ubicomplab.rdt_reader.core.RDTCaptureResult;
912
import edu.washington.cs.ubicomplab.rdt_reader.core.RDTInterpretationResult;
@@ -12,6 +15,7 @@
1215
import edu.washington.cs.ubicomplab.rdt_reader.views.ImageQualityView;
1316

1417
import static edu.washington.cs.ubicomplab.rdt_reader.core.Constants.DEFAULT_RDT_NAME;
18+
import static edu.washington.cs.ubicomplab.rdt_reader.core.Constants.RDT_JSON_CONFIG;
1519

1620
/**
1721
* The {@link android.app.Activity} for showing a real-time camera feed during image capture and
@@ -43,6 +47,16 @@ protected void onCreate(Bundle savedInstanceState) {
4347
} else {
4448
mImageQualityView.setRDTName(DEFAULT_RDT_NAME);
4549
}
50+
51+
try {
52+
// Extract config json obj
53+
String rdtConfig = b.getString(RDT_JSON_CONFIG, null);
54+
if (rdtConfig != null) {
55+
mImageQualityView.setRdtJsonConfig(new JSONObject(rdtConfig));
56+
}
57+
} catch (JSONException e) {
58+
e.printStackTrace();
59+
}
4660
}
4761

4862
/**

lib/src/main/java/edu/washington/cs/ubicomplab/rdt_reader/core/Constants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,6 @@ public final class Constants {
8181
public static final int REQUEST_CAMERA_PERMISSION = 1;
8282
public static String SAVED_IMAGE_FILE_PATH = "saved_image_file_path";
8383
public static String SAVED_IMAGE_RESULT = "saved_image_result";
84+
85+
public static String RDT_JSON_CONFIG = "rdt_json_config";
8486
}

lib/src/main/java/edu/washington/cs/ubicomplab/rdt_reader/core/ImageProcessor.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import android.content.Context;
1313
import android.util.Log;
1414

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

130-
/**
131-
* Singleton creation method for this class
132-
* @param activity: the activity that is using this code
133-
* @param rdtName: the name of the target RDT
134-
* @return an instance of this class if one already exists, otherwise creates a new one
135-
*/
131+
private ImageProcessor(JSONObject rdtConfig) {
132+
mRDT = new RDT(rdtConfig);
133+
mRDT.refImgSharpness = measureSharpness(mRDT.refImg);
134+
}
135+
136+
public static ImageProcessor getInstance(JSONObject rdtConfig) {
137+
if (instance == null)
138+
instance = new ImageProcessor(rdtConfig);
139+
return instance;
140+
}
141+
136142
public static ImageProcessor getInstance(Activity activity, String rdtName) {
137143
if (instance == null)
138144
instance = new ImageProcessor(activity, rdtName);
Lines changed: 105 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package edu.washington.cs.ubicomplab.rdt_reader.core;
22

3-
import android.app.ListActivity;
43
import android.content.Context;
54
import android.graphics.Bitmap;
65
import android.graphics.BitmapFactory;
6+
import android.util.Base64;
77

88
import org.json.JSONArray;
99
import org.json.JSONException;
@@ -60,6 +60,15 @@ public class RDT {
6060

6161
public boolean rotated = false;
6262

63+
64+
public RDT(JSONObject rdtConfig) {
65+
try {
66+
init(rdtConfig, convertBase64StrToBitmap(rdtConfig.optString("REF_IMG")));
67+
} catch (Exception ex) {
68+
ex.printStackTrace();
69+
}
70+
}
71+
6372
public RDT(Context context, String rdtName) {
6473
try {
6574
// Read config.json
@@ -69,89 +78,106 @@ public RDT(Context context, String rdtName) {
6978
is.read(buffer);
7079
is.close();
7180
JSONObject obj = new JSONObject(new String(buffer, "UTF-8")).getJSONObject(rdtName);
72-
73-
// Load the template image
7481
refImageID = context.getResources().getIdentifier(obj.getString("REF_IMG"),
7582
"drawable", context.getPackageName());
76-
refImg = new Mat();
7783
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), refImageID);
78-
Utils.bitmapToMat(bitmap, refImg);
79-
80-
if(refImg.height() > refImg.width()) {
81-
Core.rotate(refImg, refImg, Core.ROTATE_90_COUNTERCLOCKWISE);
82-
rotated = true;
83-
}
84-
85-
cvtColor(refImg, refImg, Imgproc.COLOR_RGB2GRAY);
86-
this.rdtName = rdtName;
87-
88-
// Pull data related to UI
89-
viewFinderScaleH = obj.getDouble("VIEW_FINDER_SCALE");
90-
viewFinderScaleW = (viewFinderScaleH * (double)refImg.height()/(double)refImg.width())+Constants.VIEW_FINDER_SCALE_W_PADDING;
91-
//viewFinderScaleW = obj.getDouble("VIEW_FINDER_SCALE_W");
92-
JSONArray rectTL = obj.getJSONArray("RESULT_WINDOW_TOP_LEFT");
93-
JSONArray rectBR = obj.getJSONArray("RESULT_WINDOW_BOTTOM_RIGHT");
94-
resultWindowRect = rotated ? new Rect(new Point(rectTL.getDouble(1), rectTL.getDouble(0)),
95-
new Point(rectBR.getDouble(1), rectBR.getDouble(0))) :
96-
new Rect(new Point(rectTL.getDouble(0), rectTL.getDouble(1)),
97-
new Point(rectBR.getDouble(0), rectBR.getDouble(1)));
98-
99-
// Pull data related to the result window
100-
topLinePosition = rotated ? obj.getJSONArray("TOP_LINE_POSITION").getDouble(1) - resultWindowRect.x : obj.getJSONArray("TOP_LINE_POSITION").getDouble(0) - resultWindowRect.x;
101-
middleLinePosition = rotated ? obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(1) - resultWindowRect.x: obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(0) - resultWindowRect.x;
102-
bottomLinePosition = rotated ? obj.getJSONArray("BOTTOM_LINE_POSITION").getDouble(1) - resultWindowRect.x: obj.getJSONArray("BOTTOM_LINE_POSITION").getDouble(0) - resultWindowRect.x;
103-
topLineName = obj.getString("TOP_LINE_NAME");
104-
middleLineName = obj.getString("MIDDLE_LINE_NAME");
105-
bottomLineName = obj.getString("BOTTOM_LINE_NAME");
106-
lineIntensity = obj.getInt("LINE_INTENSITY");
107-
lineSearchWidth = obj.has("LINE_SEARCH_WIDTH") ? obj.getInt("LINE_SEARCH_WIDTH") :
108-
Math.max((int)((middleLinePosition-topLinePosition)/2.0),(int)((bottomLinePosition-middleLinePosition)/2.0));
109-
110-
checkGlare = obj.has("CHECK_GLARE") ? obj.getBoolean("CHECK_GLARE") : false;
111-
112-
// Pull data related to fiducials
113-
fiducials = obj.has("FIDUCIALS") ? obj.getJSONArray("FIDUCIALS") : new JSONArray();
114-
hasFiducial = fiducials.length() > 0;
115-
distanctFromFiducialToResultWindow = 0;
116-
117-
if (hasFiducial && fiducials.length() == 2) {
118-
JSONArray trueFiducial1 = fiducials.getJSONArray(0);
119-
Point trueFiducialTL1 = rotated
120-
? new Point(trueFiducial1.getJSONArray(0).getDouble(1), trueFiducial1.getJSONArray(0).getDouble(0))
121-
: new Point(trueFiducial1.getJSONArray(0).getDouble(0), trueFiducial1.getJSONArray(0).getDouble(1));
122-
Point trueFiducialBR1 = rotated
123-
? new Point(trueFiducial1.getJSONArray(1).getDouble(1), trueFiducial1.getJSONArray(1).getDouble(0))
124-
: new Point(trueFiducial1.getJSONArray(1).getDouble(0), trueFiducial1.getJSONArray(1).getDouble(1));
125-
126-
JSONArray trueFiducial2 = fiducials.getJSONArray(1);
127-
Point trueFiducialTL2 = rotated
128-
? new Point(trueFiducial2.getJSONArray(0).getDouble(1), trueFiducial2.getJSONArray(0).getDouble(0))
129-
: new Point(trueFiducial2.getJSONArray(0).getDouble(0), trueFiducial2.getJSONArray(0).getDouble(1));
130-
Point trueFiducialBR2 = rotated
131-
? new Point(trueFiducial2.getJSONArray(1).getDouble(1), trueFiducial2.getJSONArray(1).getDouble(0))
132-
: new Point(trueFiducial2.getJSONArray(1).getDouble(0), trueFiducial2.getJSONArray(1).getDouble(1));
133-
134-
fiducialRects = new ArrayList<>();
135-
136-
fiducialRects.add(new Rect(trueFiducialTL1, trueFiducialBR1));
137-
fiducialRects.add(new Rect(trueFiducialTL2, trueFiducialBR2));
138-
139-
distanctFromFiducialToResultWindow = resultWindowRect.x - (trueFiducialBR2.x + trueFiducialBR1.x)/2.0;
140-
}
141-
142-
// Store the reference's sharpness
143-
Size kernel = new Size(SHARPNESS_GAUSSIAN_BLUR_WINDOW,
144-
SHARPNESS_GAUSSIAN_BLUR_WINDOW);
145-
Imgproc.GaussianBlur(refImg, refImg, kernel, 0, 0);
146-
147-
// Load the reference image's features
148-
refDescriptor = new Mat();
149-
refKeypoints = new MatOfKeyPoint();
150-
detector = SIFT.create();
151-
matcher = BFMatcher.create(BFMatcher.BRUTEFORCE, false);
152-
detector.detectAndCompute(refImg, new Mat(), refKeypoints, refDescriptor);
84+
init(obj, bitmap);
15385
} catch (Exception ex) {
15486
ex.printStackTrace();
15587
}
15688
}
89+
90+
private Bitmap convertBase64StrToBitmap(String base64Str) {
91+
return convertByteArrayToBitmap(Base64.decode(base64Str.getBytes(), Base64.DEFAULT));
92+
}
93+
94+
private Bitmap convertByteArrayToBitmap(final byte[] src) {
95+
return BitmapFactory.decodeByteArray(src, 0, src.length);
96+
}
97+
98+
private void init(JSONObject obj, Bitmap bitmap) throws JSONException {
99+
// Load the template image
100+
refImg = new Mat();
101+
Utils.bitmapToMat(bitmap, refImg);
102+
103+
if(refImg.height() > refImg.width()) {
104+
Core.rotate(refImg, refImg, Core.ROTATE_90_COUNTERCLOCKWISE);
105+
rotated = true;
106+
}
107+
108+
cvtColor(refImg, refImg, Imgproc.COLOR_RGB2GRAY);
109+
this.rdtName = rdtName;
110+
111+
// Pull data related to UI
112+
viewFinderScaleH = obj.getDouble("VIEW_FINDER_SCALE");
113+
viewFinderScaleW = (viewFinderScaleH * (double)refImg.height()/(double)refImg.width())+Constants.VIEW_FINDER_SCALE_W_PADDING;
114+
//viewFinderScaleW = obj.getDouble("VIEW_FINDER_SCALE_W");
115+
JSONArray rectTL = obj.getJSONArray("RESULT_WINDOW_TOP_LEFT");
116+
JSONArray rectBR = obj.getJSONArray("RESULT_WINDOW_BOTTOM_RIGHT");
117+
resultWindowRect = rotated ? new Rect(new Point(rectTL.getDouble(1), rectTL.getDouble(0)),
118+
new Point(rectBR.getDouble(1), rectBR.getDouble(0))) :
119+
new Rect(new Point(rectTL.getDouble(0), rectTL.getDouble(1)),
120+
new Point(rectBR.getDouble(0), rectBR.getDouble(1)));
121+
122+
// Pull data related to the result window
123+
topLinePosition = rotated ? obj.getJSONArray("TOP_LINE_POSITION").getDouble(1) - resultWindowRect.x : obj.getJSONArray("TOP_LINE_POSITION").getDouble(0) - resultWindowRect.x;
124+
middleLinePosition = rotated ? obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(1) - resultWindowRect.x: obj.getJSONArray("MIDDLE_LINE_POSITION").getDouble(0) - resultWindowRect.x;
125+
bottomLinePosition = getBottomLinePosition(obj, rotated);
126+
topLineName = obj.getString("TOP_LINE_NAME");
127+
middleLineName = obj.getString("MIDDLE_LINE_NAME");
128+
bottomLineName = obj.optString("BOTTOM_LINE_NAME");
129+
lineIntensity = obj.getInt("LINE_INTENSITY");
130+
lineSearchWidth = obj.has("LINE_SEARCH_WIDTH") ? obj.getInt("LINE_SEARCH_WIDTH") :
131+
Math.max((int)((middleLinePosition-topLinePosition)/2.0),(int)((bottomLinePosition-middleLinePosition)/2.0));
132+
133+
checkGlare = obj.has("CHECK_GLARE") ? obj.getBoolean("CHECK_GLARE") : false;
134+
135+
// Pull data related to fiducials
136+
fiducials = obj.has("FIDUCIALS") ? obj.getJSONArray("FIDUCIALS") : new JSONArray();
137+
hasFiducial = fiducials.length() > 0;
138+
distanctFromFiducialToResultWindow = 0;
139+
140+
if (hasFiducial && fiducials.length() == 2) {
141+
JSONArray trueFiducial1 = fiducials.getJSONArray(0);
142+
Point trueFiducialTL1 = rotated
143+
? new Point(trueFiducial1.getJSONArray(0).getDouble(1), trueFiducial1.getJSONArray(0).getDouble(0))
144+
: new Point(trueFiducial1.getJSONArray(0).getDouble(0), trueFiducial1.getJSONArray(0).getDouble(1));
145+
Point trueFiducialBR1 = rotated
146+
? new Point(trueFiducial1.getJSONArray(1).getDouble(1), trueFiducial1.getJSONArray(1).getDouble(0))
147+
: new Point(trueFiducial1.getJSONArray(1).getDouble(0), trueFiducial1.getJSONArray(1).getDouble(1));
148+
149+
JSONArray trueFiducial2 = fiducials.getJSONArray(1);
150+
Point trueFiducialTL2 = rotated
151+
? new Point(trueFiducial2.getJSONArray(0).getDouble(1), trueFiducial2.getJSONArray(0).getDouble(0))
152+
: new Point(trueFiducial2.getJSONArray(0).getDouble(0), trueFiducial2.getJSONArray(0).getDouble(1));
153+
Point trueFiducialBR2 = rotated
154+
? new Point(trueFiducial2.getJSONArray(1).getDouble(1), trueFiducial2.getJSONArray(1).getDouble(0))
155+
: new Point(trueFiducial2.getJSONArray(1).getDouble(0), trueFiducial2.getJSONArray(1).getDouble(1));
156+
157+
fiducialRects = new ArrayList<>();
158+
159+
fiducialRects.add(new Rect(trueFiducialTL1, trueFiducialBR1));
160+
fiducialRects.add(new Rect(trueFiducialTL2, trueFiducialBR2));
161+
162+
distanctFromFiducialToResultWindow = resultWindowRect.x - (trueFiducialBR2.x + trueFiducialBR1.x)/2.0;
163+
}
164+
165+
// Store the reference's sharpness
166+
Size kernel = new Size(SHARPNESS_GAUSSIAN_BLUR_WINDOW,
167+
SHARPNESS_GAUSSIAN_BLUR_WINDOW);
168+
Imgproc.GaussianBlur(refImg, refImg, kernel, 0, 0);
169+
170+
// Load the reference image's features
171+
refDescriptor = new Mat();
172+
refKeypoints = new MatOfKeyPoint();
173+
detector = SIFT.create();
174+
matcher = BFMatcher.create(BFMatcher.BRUTEFORCE, false);
175+
detector.detectAndCompute(refImg, new Mat(), refKeypoints, refDescriptor);
176+
}
177+
178+
private double getBottomLinePosition(JSONObject rdtConfig, boolean rotated) throws JSONException {
179+
return rdtConfig.optJSONArray("BOTTOM_LINE_POSITION") == null ? 0
180+
: rotated ? rdtConfig.getJSONArray("BOTTOM_LINE_POSITION").getDouble(1) - resultWindowRect.x
181+
: rdtConfig.getJSONArray("BOTTOM_LINE_POSITION").getDouble(0) - resultWindowRect.x;
182+
}
157183
}

lib/src/main/java/edu/washington/cs/ubicomplab/rdt_reader/views/ImageQualityView.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import android.widget.TextView;
5353
import android.widget.Toast;
5454

55+
import org.json.JSONObject;
5556
import org.opencv.android.BaseLoaderCallback;
5657
import org.opencv.android.LoaderCallbackInterface;
5758
import org.opencv.core.Mat;
@@ -96,6 +97,7 @@ public class ImageQualityView extends LinearLayout implements View.OnClickListen
9697
private boolean showViewport;
9798
private boolean showFeedback;
9899
private AutoFitTextureView mTextureView;
100+
private JSONObject rdtJsonConfig;
99101

100102
// Image processing variables
101103
private ImageProcessor processor;
@@ -258,7 +260,8 @@ public void onManagerConnected(int status) {
258260
switch (status) {
259261
case LoaderCallbackInterface.SUCCESS: {
260262
Log.i(TAG, "OpenCV loaded successfully");
261-
processor = ImageProcessor.getInstance(mActivity, rdtName);
263+
processor = getRdtJsonConfig() == null ? ImageProcessor.getInstance(mActivity, rdtName)
264+
: ImageProcessor.getInstance(getRdtJsonConfig());
262265
ViewportUsingBitmap viewport = findViewById(R.id.img_quality_check_viewport);
263266
viewport.hScale = (float) processor.mRDT.viewFinderScaleH;
264267
viewport.wScale = (float) processor.mRDT.viewFinderScaleW;
@@ -1060,4 +1063,12 @@ private boolean continueProcessingImg(Image image) {
10601063
}
10611064
return true;
10621065
}
1066+
1067+
public JSONObject getRdtJsonConfig() {
1068+
return rdtJsonConfig;
1069+
}
1070+
1071+
public void setRdtJsonConfig(JSONObject rdtJsonConfig) {
1072+
this.rdtJsonConfig = rdtJsonConfig;
1073+
}
10631074
}

0 commit comments

Comments
 (0)