contour = face.getAllContours();
+ for (FaceContour faceContour : contour) {
+ for (PointF point : faceContour.getPoints()) {
+ float px = translateX(point.x);
+ float py = translateY(point.y);
+ canvas.drawCircle(px, py, FACE_POSITION_RADIUS, facePositionPaint);
+ }
}
- if (face.getSmilingProbability() >= 0) {
+ if (face.getSmilingProbability() != null) {
canvas.drawText(
"happiness: " + String.format("%.2f", face.getSmilingProbability()),
x + ID_X_OFFSET * 3,
@@ -94,51 +99,51 @@ public void draw(Canvas canvas) {
idPaint);
}
- if (face.getRightEyeOpenProbability() >= 0) {
+ if (face.getRightEyeOpenProbability() != null) {
canvas.drawText(
"right eye: " + String.format("%.2f", face.getRightEyeOpenProbability()),
x - ID_X_OFFSET,
y,
idPaint);
}
- if (face.getLeftEyeOpenProbability() >= 0) {
+ if (face.getLeftEyeOpenProbability() != null) {
canvas.drawText(
"left eye: " + String.format("%.2f", face.getLeftEyeOpenProbability()),
x + ID_X_OFFSET * 6,
y,
idPaint);
}
- FirebaseVisionFaceLandmark leftEye = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EYE);
- if (leftEye != null && leftEye.getPosition() != null) {
+ FaceLandmark leftEye = face.getLandmark(FaceLandmark.LEFT_EYE);
+ if (leftEye != null) {
canvas.drawCircle(
- translateX(leftEye.getPosition().getX()),
- translateY(leftEye.getPosition().getY()),
+ translateX(leftEye.getPosition().x),
+ translateY(leftEye.getPosition().y),
FACE_POSITION_RADIUS,
facePositionPaint);
}
- FirebaseVisionFaceLandmark rightEye = face.getLandmark(FirebaseVisionFaceLandmark.RIGHT_EYE);
- if (rightEye != null && rightEye.getPosition() != null) {
+ FaceLandmark rightEye = face.getLandmark(FaceLandmark.RIGHT_EYE);
+ if (rightEye != null) {
canvas.drawCircle(
- translateX(rightEye.getPosition().getX()),
- translateY(rightEye.getPosition().getY()),
+ translateX(rightEye.getPosition().x),
+ translateY(rightEye.getPosition().y),
FACE_POSITION_RADIUS,
facePositionPaint);
}
- FirebaseVisionFaceLandmark leftCheek = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_CHEEK);
- if (leftCheek != null && leftCheek.getPosition() != null) {
+ FaceLandmark leftCheek = face.getLandmark(FaceLandmark.LEFT_CHEEK);
+ if (leftCheek != null) {
canvas.drawCircle(
- translateX(leftCheek.getPosition().getX()),
- translateY(leftCheek.getPosition().getY()),
+ translateX(leftCheek.getPosition().x),
+ translateY(leftCheek.getPosition().y),
FACE_POSITION_RADIUS,
facePositionPaint);
}
- FirebaseVisionFaceLandmark rightCheek =
- face.getLandmark(FirebaseVisionFaceLandmark.RIGHT_CHEEK);
- if (rightCheek != null && rightCheek.getPosition() != null) {
+ FaceLandmark rightCheek =
+ face.getLandmark(FaceLandmark.RIGHT_CHEEK);
+ if (rightCheek != null) {
canvas.drawCircle(
- translateX(rightCheek.getPosition().getX()),
- translateY(rightCheek.getPosition().getY()),
+ translateX(rightCheek.getPosition().x),
+ translateY(rightCheek.getPosition().y),
FACE_POSITION_RADIUS,
facePositionPaint);
}
diff --git a/vision/final/app/src/main/java/com/google/codelab/mlkit/GraphicOverlay.java b/vision/final/app/src/main/java/com/google/codelab/mlkit/GraphicOverlay.java
new file mode 100755
index 0000000..3f47dff
--- /dev/null
+++ b/vision/final/app/src/main/java/com/google/codelab/mlkit/GraphicOverlay.java
@@ -0,0 +1,189 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.codelab.mlkit;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A view which renders a series of custom graphics to be overlayed on top of an associated preview
+ * (i.e., the camera preview). The creator can add graphics objects, update the objects, and remove
+ * them, triggering the appropriate drawing and invalidation within the view.
+ *
+ *
Supports scaling and mirroring of the graphics relative the camera's preview properties. The
+ * idea is that detection items are expressed in terms of a preview size, but need to be scaled up
+ * to the full view size, and also mirrored in the case of the front-facing camera.
+ *
+ *
Associated {@link Graphic} items should use the following methods to convert to view
+ * coordinates for the graphics that are drawn:
+ *
+ *
+ * - {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the
+ * supplied value from the preview scale to the view scale.
+ *
- {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
+ * coordinate from the preview's coordinate system to the view coordinate system.
+ *
+ */
+public class GraphicOverlay extends View {
+ private final Object lock = new Object();
+ private int previewWidth;
+ private float widthScaleFactor = 1.0f;
+ private int previewHeight;
+ private float heightScaleFactor = 1.0f;
+ private int facing = CameraCharacteristics.LENS_FACING_BACK;
+ private Set graphics = new HashSet<>();
+
+ /**
+ * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass
+ * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add
+ * instances to the overlay using {@link GraphicOverlay#add(Graphic)}.
+ */
+ public abstract static class Graphic {
+ private GraphicOverlay overlay;
+
+ public Graphic(GraphicOverlay overlay) {
+ this.overlay = overlay;
+ }
+
+ /**
+ * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
+ * to view coordinates for the graphics that are drawn:
+ *
+ *
+ * - {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the
+ * supplied value from the preview scale to the view scale.
+ *
- {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
+ * coordinate from the preview's coordinate system to the view coordinate system.
+ *
+ *
+ * @param canvas drawing canvas
+ */
+ public abstract void draw(Canvas canvas);
+
+ /**
+ * Adjusts a horizontal value of the supplied value from the preview scale to the view scale.
+ */
+ public float scaleX(float horizontal) {
+ return horizontal * overlay.widthScaleFactor;
+ }
+
+ /**
+ * Adjusts a vertical value of the supplied value from the preview scale to the view scale.
+ */
+ public float scaleY(float vertical) {
+ return vertical * overlay.heightScaleFactor;
+ }
+
+ /**
+ * Returns the application context of the app.
+ */
+ public Context getApplicationContext() {
+ return overlay.getContext().getApplicationContext();
+ }
+
+ /**
+ * Adjusts the x coordinate from the preview's coordinate system to the view coordinate system.
+ */
+ public float translateX(float x) {
+ if (overlay.facing == CameraCharacteristics.LENS_FACING_FRONT) {
+ return overlay.getWidth() - scaleX(x);
+ } else {
+ return scaleX(x);
+ }
+ }
+
+ /**
+ * Adjusts the y coordinate from the preview's coordinate system to the view coordinate system.
+ */
+ public float translateY(float y) {
+ return scaleY(y);
+ }
+
+ public void postInvalidate() {
+ overlay.postInvalidate();
+ }
+ }
+
+ public GraphicOverlay(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * Removes all graphics from the overlay.
+ */
+ public void clear() {
+ synchronized (lock) {
+ graphics.clear();
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Adds a graphic to the overlay.
+ */
+ public void add(Graphic graphic) {
+ synchronized (lock) {
+ graphics.add(graphic);
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Removes a graphic from the overlay.
+ */
+ public void remove(Graphic graphic) {
+ synchronized (lock) {
+ graphics.remove(graphic);
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Sets the camera attributes for size and facing direction, which informs how to transform image
+ * coordinates later.
+ */
+ public void setCameraInfo(int previewWidth, int previewHeight, int facing) {
+ synchronized (lock) {
+ this.previewWidth = previewWidth;
+ this.previewHeight = previewHeight;
+ this.facing = facing;
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Draws the overlay with its associated graphic objects.
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ synchronized (lock) {
+ if ((previewWidth != 0) && (previewHeight != 0)) {
+ widthScaleFactor = (float) canvas.getWidth() / (float) previewWidth;
+ heightScaleFactor = (float) canvas.getHeight() / (float) previewHeight;
+ }
+
+ for (Graphic graphic : graphics) {
+ graphic.draw(canvas);
+ }
+ }
+ }
+}
diff --git a/starter/app/src/main/java/com/google/firebase/codelab/mlkit/LabelGraphic.java b/vision/final/app/src/main/java/com/google/codelab/mlkit/LabelGraphic.java
similarity index 98%
rename from starter/app/src/main/java/com/google/firebase/codelab/mlkit/LabelGraphic.java
rename to vision/final/app/src/main/java/com/google/codelab/mlkit/LabelGraphic.java
index d178e28..8815777 100755
--- a/starter/app/src/main/java/com/google/firebase/codelab/mlkit/LabelGraphic.java
+++ b/vision/final/app/src/main/java/com/google/codelab/mlkit/LabelGraphic.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.firebase.codelab.mlkit;
+package com.google.codelab.mlkit;
import android.graphics.Canvas;
import android.graphics.Color;
diff --git a/vision/final/app/src/main/java/com/google/codelab/mlkit/MainActivity.java b/vision/final/app/src/main/java/com/google/codelab/mlkit/MainActivity.java
new file mode 100755
index 0000000..870ca42
--- /dev/null
+++ b/vision/final/app/src/main/java/com/google/codelab/mlkit/MainActivity.java
@@ -0,0 +1,309 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.codelab.mlkit;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.util.Pair;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+import com.google.android.gms.tasks.OnFailureListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.codelab.mlkit.GraphicOverlay.Graphic;
+import com.google.mlkit.vision.common.InputImage;
+import com.google.mlkit.vision.face.Face;
+import com.google.mlkit.vision.face.FaceDetection;
+import com.google.mlkit.vision.face.FaceDetector;
+import com.google.mlkit.vision.face.FaceDetectorOptions;
+import com.google.mlkit.vision.text.Text;
+import com.google.mlkit.vision.text.TextRecognition;
+import com.google.mlkit.vision.text.TextRecognizer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+
+public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
+ private static final String TAG = "MainActivity";
+ private ImageView mImageView;
+ private Button mTextButton;
+ private Button mFaceButton;
+ private Bitmap mSelectedImage;
+ private GraphicOverlay mGraphicOverlay;
+ // Max width (portrait mode)
+ private Integer mImageMaxWidth;
+ // Max height (portrait mode)
+ private Integer mImageMaxHeight;
+
+ /**
+ * Number of results to show in the UI.
+ */
+ private static final int RESULTS_TO_SHOW = 3;
+ /**
+ * Dimensions of inputs.
+ */
+ private static final int DIM_IMG_SIZE_X = 224;
+ private static final int DIM_IMG_SIZE_Y = 224;
+
+ private final PriorityQueue> sortedLabels =
+ new PriorityQueue<>(
+ RESULTS_TO_SHOW,
+ new Comparator>() {
+ @Override
+ public int compare(Map.Entry o1, Map.Entry
+ o2) {
+ return (o1.getValue()).compareTo(o2.getValue());
+ }
+ });
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mImageView = findViewById(R.id.image_view);
+
+ mTextButton = findViewById(R.id.button_text);
+ mFaceButton = findViewById(R.id.button_face);
+
+ mGraphicOverlay = findViewById(R.id.graphic_overlay);
+ mTextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ runTextRecognition();
+ }
+ });
+ mFaceButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ runFaceContourDetection();
+ }
+ });
+ Spinner dropdown = findViewById(R.id.spinner);
+ String[] items = new String[]{"Test Image 1 (Text)", "Test Image 2 (Face)"};
+ ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout
+ .simple_spinner_dropdown_item, items);
+ dropdown.setAdapter(adapter);
+ dropdown.setOnItemSelectedListener(this);
+ }
+
+ private void runTextRecognition() {
+ InputImage image = InputImage.fromBitmap(mSelectedImage, 0);
+ TextRecognizer recognizer = TextRecognition.getClient();
+ mTextButton.setEnabled(false);
+ recognizer.process(image)
+ .addOnSuccessListener(
+ new OnSuccessListener() {
+ @Override
+ public void onSuccess(Text texts) {
+ mTextButton.setEnabled(true);
+ processTextRecognitionResult(texts);
+ }
+ })
+ .addOnFailureListener(
+ new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ // Task failed with an exception
+ mTextButton.setEnabled(true);
+ e.printStackTrace();
+ }
+ });
+ }
+
+ private void processTextRecognitionResult(Text texts) {
+ List blocks = texts.getTextBlocks();
+ if (blocks.size() == 0) {
+ showToast("No text found");
+ return;
+ }
+ mGraphicOverlay.clear();
+ for (int i = 0; i < blocks.size(); i++) {
+ List lines = blocks.get(i).getLines();
+ for (int j = 0; j < lines.size(); j++) {
+ List elements = lines.get(j).getElements();
+ for (int k = 0; k < elements.size(); k++) {
+ Graphic textGraphic = new TextGraphic(mGraphicOverlay, elements.get(k));
+ mGraphicOverlay.add(textGraphic);
+
+ }
+ }
+ }
+ }
+
+ private void runFaceContourDetection() {
+ InputImage image = InputImage.fromBitmap(mSelectedImage, 0);
+ FaceDetectorOptions options =
+ new FaceDetectorOptions.Builder()
+ .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
+ .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
+ .build();
+
+ mFaceButton.setEnabled(false);
+ FaceDetector detector = FaceDetection.getClient(options);
+ detector.process(image)
+ .addOnSuccessListener(
+ new OnSuccessListener>() {
+ @Override
+ public void onSuccess(List faces) {
+ mFaceButton.setEnabled(true);
+ processFaceContourDetectionResult(faces);
+ }
+ })
+ .addOnFailureListener(
+ new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ // Task failed with an exception
+ mFaceButton.setEnabled(true);
+ e.printStackTrace();
+ }
+ });
+
+ }
+
+ private void processFaceContourDetectionResult(List faces) {
+ // Task completed successfully
+ if (faces.size() == 0) {
+ showToast("No face found");
+ return;
+ }
+ mGraphicOverlay.clear();
+ for (int i = 0; i < faces.size(); ++i) {
+ Face face = faces.get(i);
+ FaceContourGraphic faceGraphic = new FaceContourGraphic(mGraphicOverlay);
+ mGraphicOverlay.add(faceGraphic);
+ faceGraphic.updateFace(face);
+ }
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
+ }
+
+ // Functions for loading images from app assets.
+
+ // Returns max image width, always for portrait mode. Caller needs to swap width / height for
+ // landscape mode.
+ private Integer getImageMaxWidth() {
+ if (mImageMaxWidth == null) {
+ // Calculate the max width in portrait mode. This is done lazily since we need to
+ // wait for
+ // a UI layout pass to get the right values. So delay it to first time image
+ // rendering time.
+ mImageMaxWidth = mImageView.getWidth();
+ }
+
+ return mImageMaxWidth;
+ }
+
+ // Returns max image height, always for portrait mode. Caller needs to swap width / height for
+ // landscape mode.
+ private Integer getImageMaxHeight() {
+ if (mImageMaxHeight == null) {
+ // Calculate the max width in portrait mode. This is done lazily since we need to
+ // wait for
+ // a UI layout pass to get the right values. So delay it to first time image
+ // rendering time.
+ mImageMaxHeight =
+ mImageView.getHeight();
+ }
+
+ return mImageMaxHeight;
+ }
+
+ // Gets the targeted width / height.
+ private Pair getTargetedWidthHeight() {
+ int targetWidth;
+ int targetHeight;
+ int maxWidthForPortraitMode = getImageMaxWidth();
+ int maxHeightForPortraitMode = getImageMaxHeight();
+ targetWidth = maxWidthForPortraitMode;
+ targetHeight = maxHeightForPortraitMode;
+ return new Pair<>(targetWidth, targetHeight);
+ }
+
+ public void onItemSelected(AdapterView> parent, View v, int position, long id) {
+ mGraphicOverlay.clear();
+ switch (position) {
+ case 0:
+ mSelectedImage = getBitmapFromAsset(this, "Please_walk_on_the_grass.jpg");
+ break;
+ case 1:
+ // Whatever you want to happen when the thrid item gets selected
+ mSelectedImage = getBitmapFromAsset(this, "grace_hopper.jpg");
+ break;
+ }
+ if (mSelectedImage != null) {
+ // Get the dimensions of the View
+ Pair targetedSize = getTargetedWidthHeight();
+
+ int targetWidth = targetedSize.first;
+ int maxHeight = targetedSize.second;
+
+ // Determine how much to scale down the image
+ float scaleFactor =
+ Math.max(
+ (float) mSelectedImage.getWidth() / (float) targetWidth,
+ (float) mSelectedImage.getHeight() / (float) maxHeight);
+
+ Bitmap resizedBitmap =
+ Bitmap.createScaledBitmap(
+ mSelectedImage,
+ (int) (mSelectedImage.getWidth() / scaleFactor),
+ (int) (mSelectedImage.getHeight() / scaleFactor),
+ true);
+
+ mImageView.setImageBitmap(resizedBitmap);
+ mSelectedImage = resizedBitmap;
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ // Do nothing
+ }
+
+ public static Bitmap getBitmapFromAsset(Context context, String filePath) {
+ AssetManager assetManager = context.getAssets();
+
+ InputStream is;
+ Bitmap bitmap = null;
+ try {
+ is = assetManager.open(filePath);
+ bitmap = BitmapFactory.decodeStream(is);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return bitmap;
+ }
+}
diff --git a/starter/app/src/main/java/com/google/firebase/codelab/mlkit/TextGraphic.java b/vision/final/app/src/main/java/com/google/codelab/mlkit/TextGraphic.java
similarity index 88%
rename from starter/app/src/main/java/com/google/firebase/codelab/mlkit/TextGraphic.java
rename to vision/final/app/src/main/java/com/google/codelab/mlkit/TextGraphic.java
index 874e02c..3d2cd10 100755
--- a/starter/app/src/main/java/com/google/firebase/codelab/mlkit/TextGraphic.java
+++ b/vision/final/app/src/main/java/com/google/codelab/mlkit/TextGraphic.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.firebase.codelab.mlkit;
+package com.google.codelab.mlkit;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -20,8 +20,8 @@
import android.graphics.RectF;
import android.util.Log;
-import com.google.firebase.ml.vision.text.FirebaseVisionText;
-import com.google.firebase.codelab.mlkit.GraphicOverlay.Graphic;
+import com.google.codelab.mlkit.GraphicOverlay.Graphic;
+import com.google.mlkit.vision.text.Text;
/**
* Graphic instance for rendering TextBlock position, size, and ID within an associated graphic
@@ -36,9 +36,9 @@ public class TextGraphic extends Graphic {
private final Paint rectPaint;
private final Paint textPaint;
- private final FirebaseVisionText.Element element;
+ private final Text.Element element;
- TextGraphic(GraphicOverlay overlay, FirebaseVisionText.Element element) {
+ TextGraphic(GraphicOverlay overlay, Text.Element element) {
super(overlay);
this.element = element;
diff --git a/final/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/vision/final/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
similarity index 100%
rename from final/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
rename to vision/final/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/final/app/src/main/res/drawable/ic_launcher_background.xml b/vision/final/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from final/app/src/main/res/drawable/ic_launcher_background.xml
rename to vision/final/app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/final/app/src/main/res/layout/activity_main.xml b/vision/final/app/src/main/res/layout/activity_main.xml
similarity index 64%
rename from final/app/src/main/res/layout/activity_main.xml
rename to vision/final/app/src/main/res/layout/activity_main.xml
index a4a82eb..ae2dabb 100755
--- a/final/app/src/main/res/layout/activity_main.xml
+++ b/vision/final/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,5 @@
-
-
+
-
-
+ android:text="@string/find_face_contour_button"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintLeft_toRightOf="@id/button_text" />
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vision/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/vision/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..eca70cf
--- /dev/null
+++ b/vision/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/vision/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/vision/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..eca70cf
--- /dev/null
+++ b/vision/final/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/final/app/src/main/res/mipmap-hdpi/ic_launcher.png b/vision/final/app/src/main/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from final/app/src/main/res/mipmap-hdpi/ic_launcher.png
rename to vision/final/app/src/main/res/mipmap-hdpi/ic_launcher.png
diff --git a/final/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/vision/final/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
similarity index 100%
rename from final/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
rename to vision/final/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
diff --git a/final/app/src/main/res/mipmap-mdpi/ic_launcher.png b/vision/final/app/src/main/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from final/app/src/main/res/mipmap-mdpi/ic_launcher.png
rename to vision/final/app/src/main/res/mipmap-mdpi/ic_launcher.png
diff --git a/final/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/vision/final/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
similarity index 100%
rename from final/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
rename to vision/final/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
diff --git a/final/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/vision/final/app/src/main/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from final/app/src/main/res/mipmap-xhdpi/ic_launcher.png
rename to vision/final/app/src/main/res/mipmap-xhdpi/ic_launcher.png
diff --git a/final/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/vision/final/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
similarity index 100%
rename from final/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
rename to vision/final/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
diff --git a/final/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/vision/final/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from final/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
rename to vision/final/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/final/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/vision/final/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
similarity index 100%
rename from final/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
rename to vision/final/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
diff --git a/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/vision/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from final/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
rename to vision/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/vision/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
similarity index 100%
rename from final/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
rename to vision/final/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
diff --git a/final/app/src/main/res/values/colors.xml b/vision/final/app/src/main/res/values/colors.xml
similarity index 100%
rename from final/app/src/main/res/values/colors.xml
rename to vision/final/app/src/main/res/values/colors.xml
diff --git a/final/app/src/main/res/values/strings.xml b/vision/final/app/src/main/res/values/strings.xml
similarity index 100%
rename from final/app/src/main/res/values/strings.xml
rename to vision/final/app/src/main/res/values/strings.xml
diff --git a/vision/final/app/src/main/res/values/styles.xml b/vision/final/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..5885930
--- /dev/null
+++ b/vision/final/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/vision/final/build.gradle b/vision/final/build.gradle
new file mode 100755
index 0000000..0a7c236
--- /dev/null
+++ b/vision/final/build.gradle
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+
+ repositories {
+ maven { url rootProject.projectDir.getAbsolutePath() + '/libraries' }
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.0'
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ maven { url rootProject.projectDir.getAbsolutePath() + '/libraries' }
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/final/gradle.properties b/vision/final/gradle.properties
similarity index 93%
rename from final/gradle.properties
rename to vision/final/gradle.properties
index 743d692..8de5058 100755
--- a/final/gradle.properties
+++ b/vision/final/gradle.properties
@@ -6,6 +6,8 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
diff --git a/final/gradle/wrapper/gradle-wrapper.jar b/vision/final/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from final/gradle/wrapper/gradle-wrapper.jar
rename to vision/final/gradle/wrapper/gradle-wrapper.jar
diff --git a/vision/final/gradle/wrapper/gradle-wrapper.properties b/vision/final/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..3980692
--- /dev/null
+++ b/vision/final/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri May 03 10:49:49 EDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/vision/final/gradlew b/vision/final/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/vision/final/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/vision/final/gradlew.bat b/vision/final/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/vision/final/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/final/settings.gradle b/vision/final/settings.gradle
similarity index 100%
rename from final/settings.gradle
rename to vision/final/settings.gradle
diff --git a/vision/starter/app/.gitignore b/vision/starter/app/.gitignore
new file mode 100755
index 0000000..796b96d
--- /dev/null
+++ b/vision/starter/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/vision/starter/app/build.gradle b/vision/starter/app/build.gradle
new file mode 100755
index 0000000..524b7d7
--- /dev/null
+++ b/vision/starter/app/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.google.codelab.mlkit"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ packagingOptions {
+ exclude 'META-INF/androidx.exifinterface_exifinterface.version'
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'androidx.exifinterface:exifinterface:1.2.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ testImplementation 'junit:junit:4.13'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+ // Face features
+ implementation 'com.google.mlkit:face-detection:16.0.0'
+
+ // Text features
+ implementation 'com.google.android.gms:play-services-mlkit-text-recognition:16.0.0'
+}
diff --git a/vision/starter/app/proguard-rules.pro b/vision/starter/app/proguard-rules.pro
new file mode 100755
index 0000000..f1b4245
--- /dev/null
+++ b/vision/starter/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/final/app/src/main/AndroidManifest.xml b/vision/starter/app/src/main/AndroidManifest.xml
similarity index 95%
rename from final/app/src/main/AndroidManifest.xml
rename to vision/starter/app/src/main/AndroidManifest.xml
index f85c7b3..bc7bc5f 100755
--- a/final/app/src/main/AndroidManifest.xml
+++ b/vision/starter/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+ package="com.google.codelab.mlkit">
contour = face.getAllContours();
+ for (FaceContour faceContour : contour) {
+ for (PointF point : faceContour.getPoints()) {
+ float px = translateX(point.x);
+ float py = translateY(point.y);
+ canvas.drawCircle(px, py, FACE_POSITION_RADIUS, facePositionPaint);
+ }
+ }
+
+ if (face.getSmilingProbability() != null) {
+ canvas.drawText(
+ "happiness: " + String.format("%.2f", face.getSmilingProbability()),
+ x + ID_X_OFFSET * 3,
+ y - ID_Y_OFFSET,
+ idPaint);
+ }
+
+ if (face.getRightEyeOpenProbability() != null) {
+ canvas.drawText(
+ "right eye: " + String.format("%.2f", face.getRightEyeOpenProbability()),
+ x - ID_X_OFFSET,
+ y,
+ idPaint);
+ }
+ if (face.getLeftEyeOpenProbability() != null) {
+ canvas.drawText(
+ "left eye: " + String.format("%.2f", face.getLeftEyeOpenProbability()),
+ x + ID_X_OFFSET * 6,
+ y,
+ idPaint);
+ }
+ FaceLandmark leftEye = face.getLandmark(FaceLandmark.LEFT_EYE);
+ if (leftEye != null) {
+ canvas.drawCircle(
+ translateX(leftEye.getPosition().x),
+ translateY(leftEye.getPosition().y),
+ FACE_POSITION_RADIUS,
+ facePositionPaint);
+ }
+ FaceLandmark rightEye = face.getLandmark(FaceLandmark.RIGHT_EYE);
+ if (rightEye != null) {
+ canvas.drawCircle(
+ translateX(rightEye.getPosition().x),
+ translateY(rightEye.getPosition().y),
+ FACE_POSITION_RADIUS,
+ facePositionPaint);
+ }
+
+ FaceLandmark leftCheek = face.getLandmark(FaceLandmark.LEFT_CHEEK);
+ if (leftCheek != null) {
+ canvas.drawCircle(
+ translateX(leftCheek.getPosition().x),
+ translateY(leftCheek.getPosition().y),
+ FACE_POSITION_RADIUS,
+ facePositionPaint);
+ }
+ FaceLandmark rightCheek =
+ face.getLandmark(FaceLandmark.RIGHT_CHEEK);
+ if (rightCheek != null) {
+ canvas.drawCircle(
+ translateX(rightCheek.getPosition().x),
+ translateY(rightCheek.getPosition().y),
+ FACE_POSITION_RADIUS,
+ facePositionPaint);
+ }
+ }
+}
+
diff --git a/vision/starter/app/src/main/java/com/google/codelab/mlkit/GraphicOverlay.java b/vision/starter/app/src/main/java/com/google/codelab/mlkit/GraphicOverlay.java
new file mode 100755
index 0000000..3f47dff
--- /dev/null
+++ b/vision/starter/app/src/main/java/com/google/codelab/mlkit/GraphicOverlay.java
@@ -0,0 +1,189 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.codelab.mlkit;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A view which renders a series of custom graphics to be overlayed on top of an associated preview
+ * (i.e., the camera preview). The creator can add graphics objects, update the objects, and remove
+ * them, triggering the appropriate drawing and invalidation within the view.
+ *
+ *
Supports scaling and mirroring of the graphics relative the camera's preview properties. The
+ * idea is that detection items are expressed in terms of a preview size, but need to be scaled up
+ * to the full view size, and also mirrored in the case of the front-facing camera.
+ *
+ *
Associated {@link Graphic} items should use the following methods to convert to view
+ * coordinates for the graphics that are drawn:
+ *
+ *
+ * - {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the
+ * supplied value from the preview scale to the view scale.
+ *
- {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
+ * coordinate from the preview's coordinate system to the view coordinate system.
+ *
+ */
+public class GraphicOverlay extends View {
+ private final Object lock = new Object();
+ private int previewWidth;
+ private float widthScaleFactor = 1.0f;
+ private int previewHeight;
+ private float heightScaleFactor = 1.0f;
+ private int facing = CameraCharacteristics.LENS_FACING_BACK;
+ private Set graphics = new HashSet<>();
+
+ /**
+ * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass
+ * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add
+ * instances to the overlay using {@link GraphicOverlay#add(Graphic)}.
+ */
+ public abstract static class Graphic {
+ private GraphicOverlay overlay;
+
+ public Graphic(GraphicOverlay overlay) {
+ this.overlay = overlay;
+ }
+
+ /**
+ * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
+ * to view coordinates for the graphics that are drawn:
+ *
+ *
+ * - {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the
+ * supplied value from the preview scale to the view scale.
+ *
- {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
+ * coordinate from the preview's coordinate system to the view coordinate system.
+ *
+ *
+ * @param canvas drawing canvas
+ */
+ public abstract void draw(Canvas canvas);
+
+ /**
+ * Adjusts a horizontal value of the supplied value from the preview scale to the view scale.
+ */
+ public float scaleX(float horizontal) {
+ return horizontal * overlay.widthScaleFactor;
+ }
+
+ /**
+ * Adjusts a vertical value of the supplied value from the preview scale to the view scale.
+ */
+ public float scaleY(float vertical) {
+ return vertical * overlay.heightScaleFactor;
+ }
+
+ /**
+ * Returns the application context of the app.
+ */
+ public Context getApplicationContext() {
+ return overlay.getContext().getApplicationContext();
+ }
+
+ /**
+ * Adjusts the x coordinate from the preview's coordinate system to the view coordinate system.
+ */
+ public float translateX(float x) {
+ if (overlay.facing == CameraCharacteristics.LENS_FACING_FRONT) {
+ return overlay.getWidth() - scaleX(x);
+ } else {
+ return scaleX(x);
+ }
+ }
+
+ /**
+ * Adjusts the y coordinate from the preview's coordinate system to the view coordinate system.
+ */
+ public float translateY(float y) {
+ return scaleY(y);
+ }
+
+ public void postInvalidate() {
+ overlay.postInvalidate();
+ }
+ }
+
+ public GraphicOverlay(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * Removes all graphics from the overlay.
+ */
+ public void clear() {
+ synchronized (lock) {
+ graphics.clear();
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Adds a graphic to the overlay.
+ */
+ public void add(Graphic graphic) {
+ synchronized (lock) {
+ graphics.add(graphic);
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Removes a graphic from the overlay.
+ */
+ public void remove(Graphic graphic) {
+ synchronized (lock) {
+ graphics.remove(graphic);
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Sets the camera attributes for size and facing direction, which informs how to transform image
+ * coordinates later.
+ */
+ public void setCameraInfo(int previewWidth, int previewHeight, int facing) {
+ synchronized (lock) {
+ this.previewWidth = previewWidth;
+ this.previewHeight = previewHeight;
+ this.facing = facing;
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Draws the overlay with its associated graphic objects.
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ synchronized (lock) {
+ if ((previewWidth != 0) && (previewHeight != 0)) {
+ widthScaleFactor = (float) canvas.getWidth() / (float) previewWidth;
+ heightScaleFactor = (float) canvas.getHeight() / (float) previewHeight;
+ }
+
+ for (Graphic graphic : graphics) {
+ graphic.draw(canvas);
+ }
+ }
+ }
+}
diff --git a/final/app/src/main/java/com/google/firebase/codelab/mlkit/LabelGraphic.java b/vision/starter/app/src/main/java/com/google/codelab/mlkit/LabelGraphic.java
similarity index 98%
rename from final/app/src/main/java/com/google/firebase/codelab/mlkit/LabelGraphic.java
rename to vision/starter/app/src/main/java/com/google/codelab/mlkit/LabelGraphic.java
index d178e28..8815777 100755
--- a/final/app/src/main/java/com/google/firebase/codelab/mlkit/LabelGraphic.java
+++ b/vision/starter/app/src/main/java/com/google/codelab/mlkit/LabelGraphic.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.firebase.codelab.mlkit;
+package com.google.codelab.mlkit;
import android.graphics.Canvas;
import android.graphics.Color;
diff --git a/starter/app/src/main/java/com/google/firebase/codelab/mlkit/MainActivity.java b/vision/starter/app/src/main/java/com/google/codelab/mlkit/MainActivity.java
similarity index 52%
rename from starter/app/src/main/java/com/google/firebase/codelab/mlkit/MainActivity.java
rename to vision/starter/app/src/main/java/com/google/codelab/mlkit/MainActivity.java
index b7ce215..27db519 100755
--- a/starter/app/src/main/java/com/google/firebase/codelab/mlkit/MainActivity.java
+++ b/vision/starter/app/src/main/java/com/google/codelab/mlkit/MainActivity.java
@@ -12,17 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.firebase.codelab.mlkit;
+package com.google.codelab.mlkit;
-import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
+
+import androidx.appcompat.app.AppCompatActivity;
+
import android.util.Pair;
import android.view.View;
import android.widget.AdapterView;
@@ -32,40 +31,11 @@
import android.widget.Spinner;
import android.widget.Toast;
-import com.google.android.gms.tasks.Continuation;
-import com.google.android.gms.tasks.OnFailureListener;
-import com.google.android.gms.tasks.OnSuccessListener;
-import com.google.android.gms.tasks.Task;
-import com.google.firebase.codelab.mlkit.GraphicOverlay.Graphic;
-import com.google.firebase.ml.common.FirebaseMLException;
-import com.google.firebase.ml.custom.FirebaseModelDataType;
-import com.google.firebase.ml.custom.FirebaseModelInputOutputOptions;
-import com.google.firebase.ml.custom.FirebaseModelInputs;
-import com.google.firebase.ml.custom.FirebaseModelInterpreter;
-import com.google.firebase.ml.custom.FirebaseModelManager;
-import com.google.firebase.ml.custom.FirebaseModelOptions;
-import com.google.firebase.ml.custom.FirebaseModelOutputs;
-import com.google.firebase.ml.custom.model.FirebaseCloudModelSource;
-import com.google.firebase.ml.custom.model.FirebaseLocalModelSource;
-import com.google.firebase.ml.custom.model.FirebaseModelDownloadConditions;
-import com.google.firebase.ml.vision.FirebaseVision;
-import com.google.firebase.ml.vision.common.FirebaseVisionImage;
-import com.google.firebase.ml.vision.document.FirebaseVisionDocumentText;
-import com.google.firebase.ml.vision.document.FirebaseVisionDocumentTextRecognizer;
-import com.google.firebase.ml.vision.face.FirebaseVisionFace;
-import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetector;
-import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetectorOptions;
-import com.google.firebase.ml.vision.text.FirebaseVisionText;
-import com.google.firebase.ml.vision.text.FirebaseVisionTextRecognizer;
-
-import java.io.BufferedReader;
+import com.google.mlkit.vision.face.Face;
+import com.google.mlkit.vision.text.Text;
+
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.AbstractMap;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -76,23 +46,13 @@ public class MainActivity extends AppCompatActivity implements AdapterView.OnIte
private ImageView mImageView;
private Button mTextButton;
private Button mFaceButton;
- private Button mCloudButton;
- private Button mRunCustomModelButton;
private Bitmap mSelectedImage;
private GraphicOverlay mGraphicOverlay;
// Max width (portrait mode)
private Integer mImageMaxWidth;
// Max height (portrait mode)
private Integer mImageMaxHeight;
- /**
- * Name of the model file hosted with Firebase.
- */
- private static final String HOSTED_MODEL_NAME = "cloud_model_1";
- private static final String LOCAL_MODEL_ASSET = "mobilenet_v1_1.0_224_quant.tflite";
- /**
- * Name of the label file stored in Assets.
- */
- private static final String LABEL_PATH = "labels.txt";
+
/**
* Number of results to show in the UI.
*/
@@ -104,10 +64,6 @@ public class MainActivity extends AppCompatActivity implements AdapterView.OnIte
private static final int DIM_PIXEL_SIZE = 3;
private static final int DIM_IMG_SIZE_X = 224;
private static final int DIM_IMG_SIZE_Y = 224;
- /**
- * Labels corresponding to the output of the vision model.
- */
- private List mLabelList;
private final PriorityQueue> sortedLabels =
new PriorityQueue<>(
@@ -131,8 +87,6 @@ protected void onCreate(Bundle savedInstanceState) {
mTextButton = findViewById(R.id.button_text);
mFaceButton = findViewById(R.id.button_face);
- mCloudButton = findViewById(R.id.button_cloud_text);
- mRunCustomModelButton = findViewById(R.id.button_run_custom_model);
mGraphicOverlay = findViewById(R.id.graphic_overlay);
mTextButton.setOnClickListener(new View.OnClickListener() {
@@ -147,33 +101,19 @@ public void onClick(View view) {
runFaceContourDetection();
}
});
- mCloudButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- runCloudTextRecognition();
- }
- });
- mRunCustomModelButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- runModelInference();
- }
- });
Spinner dropdown = findViewById(R.id.spinner);
- String[] items = new String[]{"Test Image 1 (Text)", "Test Image 2 (Text)", "Test Image 3" +
- " (Face)", "Test Image 4 (Object)", "Test Image 5 (Object)"};
+ String[] items = new String[]{"Test Image 1 (Text)", "Test Image 2 (Face)"};
ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout
.simple_spinner_dropdown_item, items);
dropdown.setAdapter(adapter);
dropdown.setOnItemSelectedListener(this);
- initCustomModel();
}
private void runTextRecognition() {
// Replace with code from the codelab to run text recognition.
}
- private void processTextRecognitionResult(FirebaseVisionText texts) {
+ private void processTextRecognitionResult(Text texts) {
// Replace with code from the codelab to process the text recognition result.
}
@@ -181,93 +121,10 @@ private void runFaceContourDetection() {
// Replace with code from the codelab to run face contour detection.
}
- private void processFaceContourDetectionResult(List faces) {
+ private void processFaceContourDetectionResult(List faces) {
// Replace with code from the codelab to process the face contour detection result.
}
- private void initCustomModel() {
- // Replace with code from the codelab to initialize your custom model
- }
-
- private void runModelInference() {
- // Replace with code from the codelab to run inference using your custom model.
- }
-
- private void runCloudTextRecognition() {
- // Replace with code from the codelab to run cloud text recognition.
- }
-
- private void processCloudTextRecognitionResult(FirebaseVisionDocumentText text) {
- // Replace with code from the codelab to process the cloud text recognition result.
- }
-
- /**
- * Gets the top labels in the results.
- */
- private synchronized List getTopLabels(byte[][] labelProbArray) {
- for (int i = 0; i < mLabelList.size(); ++i) {
- sortedLabels.add(
- new AbstractMap.SimpleEntry<>(mLabelList.get(i), (labelProbArray[0][i] &
- 0xff) / 255.0f));
- if (sortedLabels.size() > RESULTS_TO_SHOW) {
- sortedLabels.poll();
- }
- }
- List result = new ArrayList<>();
- final int size = sortedLabels.size();
- for (int i = 0; i < size; ++i) {
- Map.Entry label = sortedLabels.poll();
- result.add(label.getKey() + ":" + label.getValue());
- }
- Log.d(TAG, "labels: " + result.toString());
- return result;
- }
-
- /**
- * Reads label list from Assets.
- */
- private List loadLabelList(Activity activity) {
- List labelList = new ArrayList<>();
- try (BufferedReader reader =
- new BufferedReader(new InputStreamReader(activity.getAssets().open
- (LABEL_PATH)))) {
- String line;
- while ((line = reader.readLine()) != null) {
- labelList.add(line);
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to read label list.", e);
- }
- return labelList;
- }
-
- /**
- * Writes Image data into a {@code ByteBuffer}.
- */
- private synchronized ByteBuffer convertBitmapToByteBuffer(
- Bitmap bitmap, int width, int height) {
- ByteBuffer imgData =
- ByteBuffer.allocateDirect(
- DIM_BATCH_SIZE * DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y * DIM_PIXEL_SIZE);
- imgData.order(ByteOrder.nativeOrder());
- Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, DIM_IMG_SIZE_X, DIM_IMG_SIZE_Y,
- true);
- imgData.rewind();
- scaledBitmap.getPixels(intValues, 0, scaledBitmap.getWidth(), 0, 0,
- scaledBitmap.getWidth(), scaledBitmap.getHeight());
- // Convert the image to int points.
- int pixel = 0;
- for (int i = 0; i < DIM_IMG_SIZE_X; ++i) {
- for (int j = 0; j < DIM_IMG_SIZE_Y; ++j) {
- final int val = intValues[pixel++];
- imgData.put((byte) ((val >> 16) & 0xFF));
- imgData.put((byte) ((val >> 8) & 0xFF));
- imgData.put((byte) (val & 0xFF));
- }
- }
- return imgData;
- }
-
private void showToast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
@@ -321,21 +178,9 @@ public void onItemSelected(AdapterView> parent, View v, int position, long id)
mSelectedImage = getBitmapFromAsset(this, "Please_walk_on_the_grass.jpg");
break;
case 1:
- // Whatever you want to happen when the thrid item gets selected
- mSelectedImage = getBitmapFromAsset(this, "nl2.jpg");
- break;
- case 2:
// Whatever you want to happen when the thrid item gets selected
mSelectedImage = getBitmapFromAsset(this, "grace_hopper.jpg");
break;
- case 3:
- // Whatever you want to happen when the thrid item gets selected
- mSelectedImage = getBitmapFromAsset(this, "tennis.jpg");
- break;
- case 4:
- // Whatever you want to happen when the thrid item gets selected
- mSelectedImage = getBitmapFromAsset(this, "mountain.jpg");
- break;
}
if (mSelectedImage != null) {
// Get the dimensions of the View
diff --git a/final/app/src/main/java/com/google/firebase/codelab/mlkit/TextGraphic.java b/vision/starter/app/src/main/java/com/google/codelab/mlkit/TextGraphic.java
similarity index 88%
rename from final/app/src/main/java/com/google/firebase/codelab/mlkit/TextGraphic.java
rename to vision/starter/app/src/main/java/com/google/codelab/mlkit/TextGraphic.java
index 874e02c..3d2cd10 100755
--- a/final/app/src/main/java/com/google/firebase/codelab/mlkit/TextGraphic.java
+++ b/vision/starter/app/src/main/java/com/google/codelab/mlkit/TextGraphic.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.firebase.codelab.mlkit;
+package com.google.codelab.mlkit;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -20,8 +20,8 @@
import android.graphics.RectF;
import android.util.Log;
-import com.google.firebase.ml.vision.text.FirebaseVisionText;
-import com.google.firebase.codelab.mlkit.GraphicOverlay.Graphic;
+import com.google.codelab.mlkit.GraphicOverlay.Graphic;
+import com.google.mlkit.vision.text.Text;
/**
* Graphic instance for rendering TextBlock position, size, and ID within an associated graphic
@@ -36,9 +36,9 @@ public class TextGraphic extends Graphic {
private final Paint rectPaint;
private final Paint textPaint;
- private final FirebaseVisionText.Element element;
+ private final Text.Element element;
- TextGraphic(GraphicOverlay overlay, FirebaseVisionText.Element element) {
+ TextGraphic(GraphicOverlay overlay, Text.Element element) {
super(overlay);
this.element = element;
diff --git a/starter/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/vision/starter/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
similarity index 100%
rename from starter/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
rename to vision/starter/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/starter/app/src/main/res/drawable/ic_launcher_background.xml b/vision/starter/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from starter/app/src/main/res/drawable/ic_launcher_background.xml
rename to vision/starter/app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/starter/app/src/main/res/layout/activity_main.xml b/vision/starter/app/src/main/res/layout/activity_main.xml
similarity index 64%
rename from starter/app/src/main/res/layout/activity_main.xml
rename to vision/starter/app/src/main/res/layout/activity_main.xml
index a4a82eb..ae2dabb 100755
--- a/starter/app/src/main/res/layout/activity_main.xml
+++ b/vision/starter/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,5 @@
-
-
+
-
-
+ android:text="@string/find_face_contour_button"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintLeft_toRightOf="@id/button_text" />
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vision/starter/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/vision/starter/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..eca70cf
--- /dev/null
+++ b/vision/starter/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/vision/starter/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/vision/starter/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..eca70cf
--- /dev/null
+++ b/vision/starter/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/starter/app/src/main/res/mipmap-hdpi/ic_launcher.png b/vision/starter/app/src/main/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-hdpi/ic_launcher.png
rename to vision/starter/app/src/main/res/mipmap-hdpi/ic_launcher.png
diff --git a/starter/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/vision/starter/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
rename to vision/starter/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
diff --git a/starter/app/src/main/res/mipmap-mdpi/ic_launcher.png b/vision/starter/app/src/main/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-mdpi/ic_launcher.png
rename to vision/starter/app/src/main/res/mipmap-mdpi/ic_launcher.png
diff --git a/starter/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/vision/starter/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
rename to vision/starter/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
diff --git a/starter/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/vision/starter/app/src/main/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-xhdpi/ic_launcher.png
rename to vision/starter/app/src/main/res/mipmap-xhdpi/ic_launcher.png
diff --git a/starter/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/vision/starter/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
rename to vision/starter/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
diff --git a/starter/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/vision/starter/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
rename to vision/starter/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/starter/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/vision/starter/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
rename to vision/starter/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
diff --git a/starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/vision/starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
rename to vision/starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/vision/starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
similarity index 100%
rename from starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
rename to vision/starter/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
diff --git a/starter/app/src/main/res/values/colors.xml b/vision/starter/app/src/main/res/values/colors.xml
similarity index 100%
rename from starter/app/src/main/res/values/colors.xml
rename to vision/starter/app/src/main/res/values/colors.xml
diff --git a/starter/app/src/main/res/values/strings.xml b/vision/starter/app/src/main/res/values/strings.xml
similarity index 100%
rename from starter/app/src/main/res/values/strings.xml
rename to vision/starter/app/src/main/res/values/strings.xml
diff --git a/vision/starter/app/src/main/res/values/styles.xml b/vision/starter/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..5885930
--- /dev/null
+++ b/vision/starter/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/vision/starter/build.gradle b/vision/starter/build.gradle
new file mode 100755
index 0000000..0a7c236
--- /dev/null
+++ b/vision/starter/build.gradle
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+
+ repositories {
+ maven { url rootProject.projectDir.getAbsolutePath() + '/libraries' }
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.0'
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ maven { url rootProject.projectDir.getAbsolutePath() + '/libraries' }
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/starter/gradle.properties b/vision/starter/gradle.properties
similarity index 93%
rename from starter/gradle.properties
rename to vision/starter/gradle.properties
index 743d692..8de5058 100755
--- a/starter/gradle.properties
+++ b/vision/starter/gradle.properties
@@ -6,6 +6,8 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
diff --git a/starter/gradle/wrapper/gradle-wrapper.jar b/vision/starter/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from starter/gradle/wrapper/gradle-wrapper.jar
rename to vision/starter/gradle/wrapper/gradle-wrapper.jar
diff --git a/vision/starter/gradle/wrapper/gradle-wrapper.properties b/vision/starter/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..a0f876e
--- /dev/null
+++ b/vision/starter/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri May 03 11:00:21 EDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/vision/starter/gradlew b/vision/starter/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/vision/starter/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/vision/starter/gradlew.bat b/vision/starter/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/vision/starter/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/starter/settings.gradle b/vision/starter/settings.gradle
similarity index 100%
rename from starter/settings.gradle
rename to vision/starter/settings.gradle