-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[firebase_ml_vision] v2 embedding API (#275)
- Loading branch information
1 parent
cf038c8
commit 921aad0
Showing
16 changed files
with
371 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1 @@ | ||
org.gradle.jvmargs=-Xmx1536M | ||
android.enableJetifier=true | ||
android.useAndroidX=true |
192 changes: 192 additions & 0 deletions
192
...on/android/src/main/java/io/flutter/plugins/firebasemlvision/FirebaseMlVisionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
package io.flutter.plugins.firebasemlvision; | ||
|
||
import android.content.Context; | ||
import android.graphics.Bitmap; | ||
import android.graphics.BitmapFactory; | ||
import android.graphics.Matrix; | ||
import android.net.Uri; | ||
import android.util.SparseArray; | ||
import androidx.exifinterface.media.ExifInterface; | ||
import com.google.firebase.ml.vision.FirebaseVision; | ||
import com.google.firebase.ml.vision.common.FirebaseVisionImage; | ||
import com.google.firebase.ml.vision.common.FirebaseVisionImageMetadata; | ||
import io.flutter.plugin.common.MethodCall; | ||
import io.flutter.plugin.common.MethodChannel; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.util.Map; | ||
|
||
class FirebaseMlVisionHandler implements MethodChannel.MethodCallHandler { | ||
private final SparseArray<Detector> detectors = new SparseArray<>(); | ||
private final Context applicationContext; | ||
|
||
FirebaseMlVisionHandler(Context applicationContext) { | ||
this.applicationContext = applicationContext; | ||
} | ||
|
||
@Override | ||
public void onMethodCall(MethodCall call, MethodChannel.Result result) { | ||
switch (call.method) { | ||
case "BarcodeDetector#detectInImage": | ||
case "FaceDetector#processImage": | ||
case "ImageLabeler#processImage": | ||
case "TextRecognizer#processImage": | ||
handleDetection(call, result); | ||
break; | ||
case "BarcodeDetector#close": | ||
case "FaceDetector#close": | ||
case "ImageLabeler#close": | ||
case "TextRecognizer#close": | ||
closeDetector(call, result); | ||
break; | ||
default: | ||
result.notImplemented(); | ||
} | ||
} | ||
|
||
private void handleDetection(MethodCall call, MethodChannel.Result result) { | ||
Map<String, Object> options = call.argument("options"); | ||
|
||
FirebaseVisionImage image; | ||
Map<String, Object> imageData = call.arguments(); | ||
try { | ||
image = dataToVisionImage(imageData); | ||
} catch (IOException exception) { | ||
result.error("MLVisionDetectorIOError", exception.getLocalizedMessage(), null); | ||
return; | ||
} | ||
|
||
Detector detector = getDetector(call); | ||
if (detector == null) { | ||
switch (call.method.split("#")[0]) { | ||
case "BarcodeDetector": | ||
detector = new BarcodeDetector(FirebaseVision.getInstance(), options); | ||
break; | ||
case "FaceDetector": | ||
detector = new FaceDetector(FirebaseVision.getInstance(), options); | ||
break; | ||
case "ImageLabeler": | ||
detector = new ImageLabeler(FirebaseVision.getInstance(), options); | ||
break; | ||
case "TextRecognizer": | ||
detector = new TextRecognizer(FirebaseVision.getInstance(), options); | ||
break; | ||
} | ||
|
||
final Integer handle = call.argument("handle"); | ||
addDetector(handle, detector); | ||
} | ||
|
||
detector.handleDetection(image, result); | ||
} | ||
|
||
private void closeDetector(final MethodCall call, final MethodChannel.Result result) { | ||
final Detector detector = getDetector(call); | ||
|
||
if (detector == null) { | ||
final Integer handle = call.argument("handle"); | ||
final String message = String.format("Object for handle does not exists: %s", handle); | ||
throw new IllegalArgumentException(message); | ||
} | ||
|
||
try { | ||
detector.close(); | ||
result.success(null); | ||
} catch (IOException e) { | ||
final String code = String.format("%sIOError", detector.getClass().getSimpleName()); | ||
result.error(code, e.getLocalizedMessage(), null); | ||
} finally { | ||
final Integer handle = call.argument("handle"); | ||
detectors.remove(handle); | ||
} | ||
} | ||
|
||
private FirebaseVisionImage dataToVisionImage(Map<String, Object> imageData) throws IOException { | ||
String imageType = (String) imageData.get("type"); | ||
assert imageType != null; | ||
|
||
switch (imageType) { | ||
case "file": | ||
final String imageFilePath = (String) imageData.get("path"); | ||
final int rotation = getImageExifOrientation(imageFilePath); | ||
|
||
if (rotation == 0) { | ||
File file = new File(imageFilePath); | ||
return FirebaseVisionImage.fromFilePath(applicationContext, Uri.fromFile(file)); | ||
} | ||
|
||
Matrix matrix = new Matrix(); | ||
matrix.postRotate(rotation); | ||
|
||
final Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath); | ||
final Bitmap rotatedBitmap = | ||
Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); | ||
|
||
return FirebaseVisionImage.fromBitmap(rotatedBitmap); | ||
case "bytes": | ||
@SuppressWarnings("unchecked") | ||
Map<String, Object> metadataData = (Map<String, Object>) imageData.get("metadata"); | ||
|
||
FirebaseVisionImageMetadata metadata = | ||
new FirebaseVisionImageMetadata.Builder() | ||
.setWidth((int) (double) metadataData.get("width")) | ||
.setHeight((int) (double) metadataData.get("height")) | ||
.setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) | ||
.setRotation(getRotation((int) metadataData.get("rotation"))) | ||
.build(); | ||
|
||
byte[] bytes = (byte[]) imageData.get("bytes"); | ||
assert bytes != null; | ||
|
||
return FirebaseVisionImage.fromByteArray(bytes, metadata); | ||
default: | ||
throw new IllegalArgumentException(String.format("No image type for: %s", imageType)); | ||
} | ||
} | ||
|
||
private int getImageExifOrientation(String imageFilePath) throws IOException { | ||
ExifInterface exif = new ExifInterface(imageFilePath); | ||
int orientation = | ||
exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); | ||
|
||
switch (orientation) { | ||
case ExifInterface.ORIENTATION_ROTATE_90: | ||
return 90; | ||
case ExifInterface.ORIENTATION_ROTATE_180: | ||
return 180; | ||
case ExifInterface.ORIENTATION_ROTATE_270: | ||
return 270; | ||
default: | ||
return 0; | ||
} | ||
} | ||
|
||
private int getRotation(int rotation) { | ||
switch (rotation) { | ||
case 0: | ||
return FirebaseVisionImageMetadata.ROTATION_0; | ||
case 90: | ||
return FirebaseVisionImageMetadata.ROTATION_90; | ||
case 180: | ||
return FirebaseVisionImageMetadata.ROTATION_180; | ||
case 270: | ||
return FirebaseVisionImageMetadata.ROTATION_270; | ||
default: | ||
throw new IllegalArgumentException(String.format("No rotation for: %d", rotation)); | ||
} | ||
} | ||
|
||
private void addDetector(final int handle, final Detector detector) { | ||
if (detectors.get(handle) != null) { | ||
final String message = String.format("Object for handle already exists: %s", handle); | ||
throw new IllegalArgumentException(message); | ||
} | ||
|
||
detectors.put(handle, detector); | ||
} | ||
|
||
private Detector getDetector(final MethodCall call) { | ||
final Integer handle = call.argument("handle"); | ||
return detectors.get(handle); | ||
} | ||
} |
Oops, something went wrong.