├── .gitignore ├── README.md ├── addon_config.mk ├── mobileVisionExample ├── .gitignore ├── AndroidManifest.xml ├── addons.make └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── ofxAndroidMobileVisionLib ├── .gitignore ├── build.gradle ├── ofxandroidmobilevisionlib.iml ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cc │ │ └── ofxandroidmobilevisionlib │ │ └── ofxAndroidMobileVisionLib.java │ └── res │ └── values │ └── strings.xml ├── ofxaddons_thumbnail.png └── src ├── ofxAndroidMobileVision.cpp └── ofxAndroidMobileVision.h /.gitignore: -------------------------------------------------------------------------------- 1 | mobileVisionExample/libs 2 | .idea 3 | .DS_Store 4 | mobileVisionExample/gradle 5 | mobileVisionExample/gradlew 6 | mobileVisionExample/gradlew.bat 7 | mobileVisionExample/build.gradle 8 | mobileVisionExample/mobileVisionExample.iml 9 | mobileVisionExample/res 10 | mobileVisionExample/settings.gradle 11 | ofxAndroidMobileVisionLib/ofxAndroidMobileVision.iml 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ofxAndroidMobileVision 2 | An addon that enables access to the [Google Mobile Vision API](https://developers.google.com/vision/). 3 | This addon is useful for simple and easy face detection, landmark detection, eye open probability estimation and smile estimation. 4 | 5 | The addons works only on Android, as it accesses a native Android library. 6 | 7 | ## Example app 8 | ![Preview](ofxaddons_thumbnail.png) 9 | 10 | There is one example app that demonstrates how to work with landmarks and eye and smile probabilities. Use the project generator to generate the project files for Android Studio. 11 | 12 | ## Requirements 13 | Only tested on experimental Android Studio branch of openFrameworks. [Follow these instructions](https://github.com/HalfdanJ/openFrameworks/blob/gradle-experimental/docs/android_studio.md). 14 | 15 | -------------------------------------------------------------------------------- /addon_config.mk: -------------------------------------------------------------------------------- 1 | # All variables and this file are optional, if they are not present the PG and the 2 | # makefiles will try to parse the correct values from the file system. 3 | # 4 | # Variables that specify exclusions can use % as a wildcard to specify that anything in 5 | # that position will match. A partial path can also be specified to, for example, exclude 6 | # a whole folder from the parsed paths from the file system 7 | # 8 | # Variables can be specified using = or += 9 | # = will clear the contents of that variable both specified from the file or the ones parsed 10 | # from the file system 11 | # += will add the values to the previous ones in the file or the ones parsed from the file 12 | # system 13 | # 14 | # The PG can be used to detect errors in this file, just create a new project with this addon 15 | # and the PG will write to the console the kind of error and in which line it is 16 | 17 | meta: 18 | ADDON_NAME = ofxAndroidMobileVision 19 | ADDON_DESCRIPTION = Addon for accessing native sensors on android 20 | ADDON_AUTHOR = Jonas Jongejan 21 | ADDON_TAGS = "utils" 22 | ADDON_URL = http://github.com/halfdanj/ofxAndroidSensors 23 | 24 | common: 25 | # dependencies with other addons, a list of them separated by spaces 26 | # or use += in several lines 27 | # ADDON_DEPENDENCIES = 28 | 29 | # include search paths, this will be usually parsed from the file system 30 | # but if the addon or addon libraries need special search paths they can be 31 | # specified here separated by spaces or one per line using += 32 | # ADDON_INCLUDES = 33 | 34 | # any special flag that should be passed to the compiler when using this 35 | # addon 36 | # ADDON_CFLAGS = 37 | 38 | # any special flag that should be passed to the linker when using this 39 | # addon, also used for system libraries with -lname 40 | # ADDON_LDFLAGS = -landroid 41 | 42 | # linux only, any library that should be included in the project using 43 | # pkg-config 44 | # ADDON_PKG_CONFIG_LIBRARIES = 45 | 46 | # osx/iOS only, any framework that should be included in the project 47 | # ADDON_FRAMEWORKS = 48 | 49 | # source files, these will be usually parsed from the file system looking 50 | # in the src folders in libs and the root of the addon. if your addon needs 51 | # to include files in different places or a different set of files per platform 52 | # they can be specified here 53 | # ADDON_SOURCES = 54 | 55 | # some addons need resources to be copied to the bin/data folder of the project 56 | # specify here any files that need to be copied, you can use wildcards like * and ? 57 | # ADDON_DATA = 58 | 59 | # when parsing the file system looking for libraries exclude this for all or 60 | # a specific platform 61 | # ADDON_LIBS_EXCLUDE = 62 | 63 | android: 64 | ADDON_ANDROID_LIBS = ofxAndroidMobileVisionLib 65 | # ADDON_INCLUDES = "src" 66 | -------------------------------------------------------------------------------- /mobileVisionExample/.gitignore: -------------------------------------------------------------------------------- 1 | ### Android ### 2 | # Built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # Files for the Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | ### Android Patch ### 33 | gen-external-apklibs 34 | reports 35 | obj 36 | assets 37 | -------------------------------------------------------------------------------- /mobileVisionExample/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mobileVisionExample/addons.make: -------------------------------------------------------------------------------- 1 | ofxAndroidMobileVision 2 | -------------------------------------------------------------------------------- /mobileVisionExample/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | #include "ofApp.h" 3 | 4 | int main(){ 5 | ofSetupOpenGL(1024,768, OF_WINDOW); // <-------- setup the GL context 6 | 7 | // this kicks off the running of my app 8 | // can be OF_WINDOW or OF_FULLSCREEN 9 | // pass in width and height too: 10 | ofRunApp( new ofApp() ); 11 | return 0; 12 | } 13 | 14 | 15 | #ifdef TARGET_ANDROID 16 | void ofAndroidApplicationInit() 17 | { 18 | //application scope init 19 | } 20 | 21 | void ofAndroidActivityInit() 22 | { 23 | //activity scope init 24 | main(); 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /mobileVisionExample/src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | 3 | //-------------------------------------------------------------- 4 | void ofApp::setup(){ 5 | // Use frontal facing camera 6 | grabber.setDeviceID(1); 7 | 8 | // Use native (NV21) pixel format for best performance 9 | grabber.setPixelFormat(OF_PIXELS_NV21); 10 | 11 | grabber.setup(640,360); 12 | 13 | vision.setup(); 14 | 15 | // Get orientation of camera, and force orientation of app 16 | int cameraOrientation = ((ofxAndroidVideoGrabber*)grabber.getGrabber().get())->getCameraOrientation(); 17 | if(cameraOrientation == 270){ 18 | ofSetOrientation(OF_ORIENTATION_90_LEFT); 19 | } else if(cameraOrientation == 90){ 20 | ofSetOrientation(OF_ORIENTATION_90_RIGHT); 21 | } 22 | 23 | 24 | font.load("Roboto-Regular.ttf",30); 25 | } 26 | 27 | //-------------------------------------------------------------- 28 | void ofApp::update(){ 29 | grabber.update(); 30 | if(grabber.isFrameNew()) { 31 | vision.update(grabber.getPixels()); 32 | } 33 | } 34 | 35 | //-------------------------------------------------------------- 36 | void ofApp::draw(){ 37 | ofBackground(0); 38 | ofSetColor(170); 39 | 40 | // Calculate scaling factor in order for image to fill screen 41 | float scale = ofGetWidth() / grabber.getWidth(); 42 | ofPushMatrix(); 43 | ofScale(scale,scale); 44 | 45 | grabber.draw(0,0); 46 | 47 | ofNoFill(); 48 | // Iterate over all faces and draw circles on landmarks 49 | for(auto face : vision.getFaces()){ 50 | for(int i=0;i<12;i++){ 51 | ofSetColor(0,150); 52 | ofSetLineWidth(25); 53 | ofDrawCircle(face.landmarks[i].x,face.landmarks[i].y, 10); 54 | ofSetColor(255); 55 | ofSetLineWidth(2); 56 | ofDrawCircle(face.landmarks[i].x,face.landmarks[i].y, 10); 57 | } 58 | } 59 | ofSetLineWidth(1); 60 | ofFill(); 61 | ofPopMatrix(); 62 | 63 | ofPushMatrix(); 64 | 65 | ofTranslate(0, ofGetHeight() - 200); 66 | ofSetColor(0); 67 | ofDrawRectangle(0,0, ofGetWidth(), 200); 68 | 69 | ofSetColor(255); 70 | // Iterate faces and draw bars with probabilities 71 | if(vision.getFaces().size() > 0){ 72 | auto face = vision.getFaces()[0]; 73 | 74 | // Draw bar graph titles 75 | if(face.leftEyeOpenProbability == -1) ofSetColor(70); 76 | else ofSetColor(255); 77 | font.drawString("Left Eye Open", 30, 70); 78 | 79 | if(face.rightEyeOpenProbability == -1) ofSetColor(70); 80 | else ofSetColor(255); 81 | font.drawString("Right Eye Open", 400, 70); 82 | 83 | if(face.smileProbability == -1) ofSetColor(70); 84 | else ofSetColor(255); 85 | font.drawString("Smile", 800, 70); 86 | 87 | // Draw bar graphs backgrounds 88 | ofSetColor(56, 90, 138, 100); 89 | ofDrawRectangle(30, 110, 280, 20); 90 | ofDrawRectangle(400, 110, 280, 20); 91 | ofDrawRectangle(800, 110, 280, 20); 92 | 93 | // Draw bar graphs 94 | ofSetColor(56, 90, 138); 95 | ofDrawRectangle(30, 110, 280 * MAX(0,face.leftEyeOpenProbability), 20); 96 | ofDrawRectangle(400, 110, 280 * MAX(0,face.rightEyeOpenProbability), 20); 97 | ofDrawRectangle(800, 110, 280 * MAX(0,face.smileProbability), 20); 98 | 99 | ofTranslate(0,200); 100 | } else { 101 | font.drawString("No faces detected", 30, 70); 102 | } 103 | ofPopMatrix(); 104 | } 105 | -------------------------------------------------------------------------------- /mobileVisionExample/src/ofApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "ofxAndroid.h" 5 | #include "ofxAndroidMobileVision.h" 6 | 7 | class ofApp : public ofxAndroidApp{ 8 | 9 | public: 10 | void setup(); 11 | void update(); 12 | void draw(); 13 | 14 | ofxAndroidMobileVision vision; 15 | ofVideoGrabber grabber; 16 | 17 | ofTrueTypeFont font; 18 | }; 19 | -------------------------------------------------------------------------------- /ofxAndroidMobileVisionLib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ofxAndroidMobileVisionLib/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle-experimental:0.7.0-alpha4' 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | jcenter() 14 | } 15 | } 16 | apply plugin: 'com.android.model.library' 17 | 18 | model { 19 | android { 20 | compileSdkVersion = 22 21 | buildToolsVersion = "23.0.2" 22 | 23 | defaultConfig.with { 24 | minSdkVersion.apiLevel 9 25 | targetSdkVersion.apiLevel 22 26 | versionCode 1 27 | versionName "1.0" 28 | } 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | } 33 | } 34 | } 35 | 36 | } 37 | 38 | 39 | dependencies { 40 | compile 'com.android.support:appcompat-v7:22.+' 41 | compile 'com.google.android.gms:play-services-vision:8.4.0' 42 | compile fileTree(dir: 'libs', include: ['*.jar']) 43 | compile project(path: ':ofAndroidLib') 44 | } 45 | -------------------------------------------------------------------------------- /ofxAndroidMobileVisionLib/ofxandroidmobilevisionlib.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /ofxAndroidMobileVisionLib/settings.gradle: -------------------------------------------------------------------------------- 1 | // openFrameworks-relative root directories (don't touch) 2 | def ofRoot = '../../../' 3 | 4 | 5 | 6 | include ':ofAndroidLib' 7 | project(':ofAndroidLib').projectDir = new File(ofRoot + 'addons/ofxAndroid/ofAndroidLib') 8 | -------------------------------------------------------------------------------- /ofxAndroidMobileVisionLib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ofxAndroidMobileVisionLib/src/main/java/cc/ofxandroidmobilevisionlib/ofxAndroidMobileVisionLib.java: -------------------------------------------------------------------------------- 1 | package cc.ofxandroidmobilevisionlib; 2 | 3 | import android.content.Context; 4 | 5 | import com.google.android.gms.vision.Detector; 6 | import com.google.android.gms.vision.Frame; 7 | import com.google.android.gms.vision.face.Face; 8 | import com.google.android.gms.vision.face.FaceDetector; 9 | import com.google.android.gms.vision.face.Landmark; 10 | 11 | import android.graphics.ImageFormat; 12 | import android.util.Log; 13 | import android.util.SparseArray; 14 | 15 | import java.nio.ByteBuffer; 16 | import java.util.List; 17 | 18 | import cc.openframeworks.OFAndroid; 19 | 20 | public class ofxAndroidMobileVisionLib { 21 | private static final String TAG = "ofxAndroidMobileVision"; 22 | 23 | private FaceDetector detector; 24 | private SparseArray faces; 25 | private boolean isInitialized = false; 26 | 27 | private float minFaceSize = 0.15f; 28 | private boolean prominentFaceOnly = true; 29 | 30 | public void setup(){ 31 | Context context = OFAndroid.getContext(); 32 | 33 | detector = new FaceDetector.Builder(context) 34 | .setClassificationType(FaceDetector.ALL_CLASSIFICATIONS) 35 | .setLandmarkType(FaceDetector.ALL_LANDMARKS) 36 | .setMode(FaceDetector.FAST_MODE) 37 | .setProminentFaceOnly(prominentFaceOnly) 38 | .setMinFaceSize(minFaceSize) 39 | .build(); 40 | 41 | if (!detector.isOperational()) { 42 | // Note: The first time that an app using face API is installed on a device, GMS will 43 | // download a native library to the device in order to do detection. Usually this 44 | // completes before the app is run for the first time. But if that download has not yet 45 | // completed, then the above call will not detect any faces. 46 | // 47 | // isOperational() can be used to check if the required native library is currently 48 | // available. The detector will automatically become operational once the library 49 | // download completes on device. 50 | Log.w(TAG, "Face detector dependencies are not yet available."); 51 | } 52 | 53 | Detector.Processor processor; 54 | 55 | isInitialized = true; 56 | } 57 | 58 | 59 | public void setMinFaceSize(float _minFaceSize){ 60 | if(isInitialized) Log.w(TAG,"setMinFaceSize() cannot be called after setup()"); 61 | minFaceSize = _minFaceSize; 62 | } 63 | 64 | public void setProminentFaceOnly(boolean _prominentFaceOnly){ 65 | if(isInitialized) Log.w(TAG,"setProminentFaceOnly() cannot be called after setup()"); 66 | prominentFaceOnly = _prominentFaceOnly; 67 | } 68 | 69 | public float[] getData(int face){ 70 | float ret[] = new float[3+24]; 71 | if(faces.size() > face) { 72 | ret[0] = faces.get(faces.keyAt(face)).getIsSmilingProbability(); 73 | ret[1] = faces.get(faces.keyAt(face)).getIsLeftEyeOpenProbability(); 74 | ret[2] = faces.get(faces.keyAt(face)).getIsRightEyeOpenProbability(); 75 | 76 | List landmarks = faces.get(faces.keyAt(face)).getLandmarks(); 77 | for (int i = 0; i < 12; i++) { 78 | if (landmarks.size() > i) { 79 | ret[i * 2 + 3] = landmarks.get(i).getPosition().x; 80 | ret[i * 2 + 3 + 1] = landmarks.get(i).getPosition().y; 81 | } else { 82 | ret[i * 2 + 3] = -1; 83 | ret[i * 2 + 3 + 1] = -1; 84 | } 85 | } 86 | } 87 | 88 | return ret; 89 | } 90 | 91 | public int update(byte[] bytes, int width, int height){ 92 | if(!detector.isOperational()) return -1; 93 | 94 | // Wrap image bytes 95 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 96 | Frame frame = new Frame.Builder() 97 | .setImageData(buffer, width, height, ImageFormat.NV21) 98 | .build(); 99 | 100 | // Run face detector 101 | faces = detector.detect(frame); 102 | return faces.size(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ofxAndroidMobileVisionLib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ofxAndroidMobileVisionLib 3 | 4 | -------------------------------------------------------------------------------- /ofxaddons_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HalfdanJ/ofxAndroidMobileVision/4f6434e4e619ada1490bdc85705dbce901bed4b6/ofxaddons_thumbnail.png -------------------------------------------------------------------------------- /src/ofxAndroidMobileVision.cpp: -------------------------------------------------------------------------------- 1 | #include "ofxAndroidMobileVision.h" 2 | #ifdef TARGET_ANDROID 3 | #include "ofxAndroidUtils.h" 4 | 5 | 6 | ofxAndroidMobileVision::ofxAndroidMobileVision() 7 | :threaded(true){ 8 | 9 | if(!ofGetJavaVMPtr()){ 10 | ofLogNotice("ofxAndroidMobileVision") << "couldn't find java virtual machine"; 11 | return; 12 | } 13 | 14 | JNIEnv *env; 15 | if (ofGetJavaVMPtr()->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 16 | ofLogNotice("ofxAndroidMobileVision") << "failed to get environment using GetEnv()"; 17 | return; 18 | } 19 | jclass localClass = env->FindClass("cc/ofxandroidmobilevisionlib/ofxAndroidMobileVisionLib"); 20 | javaClass = (jclass) env->NewGlobalRef(localClass); 21 | 22 | if(!javaClass){ 23 | ofLogError("ofxAndroidMobileVision") << "constructor: couldn't get java class for MobileVision"; 24 | return; 25 | } 26 | 27 | jmethodID constructor = env->GetMethodID(javaClass,"","()V"); 28 | if(!constructor){ 29 | ofLogError("ofxAndroidMobileVision") << "constructor: couldn't get java constructor for MobileVision"; 30 | return; 31 | } 32 | 33 | javaMobileVision = env->NewObject(javaClass,constructor); 34 | if(!javaMobileVision){ 35 | ofLogError("ofxAndroidMobileVision") << "constructor: couldn't create java MobileVision"; 36 | return; 37 | } 38 | 39 | javaMobileVision = (jobject)env->NewGlobalRef(javaMobileVision); 40 | } 41 | 42 | ofxAndroidMobileVision::~ofxAndroidMobileVision(){ 43 | toAnalyze.close(); 44 | } 45 | 46 | bool ofxAndroidMobileVision::setThreaded(bool _threaded){ 47 | threaded = _threaded; 48 | } 49 | 50 | void ofxAndroidMobileVision::setup(){ 51 | if(!javaMobileVision){ 52 | ofLogError("ofxAndroidMobileVision") << "setup(): java MobileVision not loaded"; 53 | return; 54 | } 55 | 56 | JNIEnv *env = ofGetJNIEnv(); 57 | jmethodID javaSetupMethod = env->GetMethodID(javaClass,"setup","()V"); 58 | if(!javaSetupMethod){ 59 | ofLogError("ofxAndroidMobileVision") << "setup(): couldn't get java setup for MobileVision"; 60 | return; 61 | } 62 | 63 | env->CallVoidMethod(javaMobileVision,javaSetupMethod); 64 | 65 | if(threaded){ 66 | startThread(); 67 | } 68 | return; 69 | } 70 | 71 | void ofxAndroidMobileVision::setTrackProminentFaceOnly(bool prominentOnly){ 72 | if(!javaMobileVision) return; 73 | JNIEnv *env = ofGetJNIEnv(); 74 | env->CallVoidMethod(javaMobileVision,env->GetMethodID(javaClass,"setProminentFaceOnly","(Z)V"), prominentOnly); 75 | } 76 | 77 | void ofxAndroidMobileVision::setMinFaceSize(float minFaceSize){ 78 | if(!javaMobileVision) return; 79 | JNIEnv *env = ofGetJNIEnv(); 80 | env->CallVoidMethod(javaMobileVision,env->GetMethodID(javaClass,"setMinFaceSize","(F)V"), minFaceSize); 81 | } 82 | 83 | void ofxAndroidMobileVision::update(ofPixels &pixels){ 84 | if(threaded) { 85 | if (toAnalyze.empty()) { 86 | ToAnalyzeData d = ToAnalyzeData(); 87 | d.pixels = pixels; 88 | toAnalyze.send(d); 89 | } 90 | } else { 91 | process(pixels); 92 | } 93 | 94 | fromAnalyze.tryReceive(faces); 95 | 96 | } 97 | 98 | vector & ofxAndroidMobileVision::getFaces(){ 99 | return faces; 100 | } 101 | 102 | void ofxAndroidMobileVision::process(ofPixels &pixels){ 103 | if(!javaMobileVision){ 104 | ofLogError("ofxAndroidMobileVision") << "update(): java not loaded"; 105 | return; 106 | } 107 | 108 | JNIEnv *env = ofGetJNIEnv(); 109 | jmethodID javaMethod = env->GetMethodID(javaClass,"update","([BII)I"); 110 | if(!javaMethod ){ 111 | ofLogError("ofxAndroidMobileVision") << "update(): couldn't get java update for MobileVision"; 112 | return; 113 | } 114 | 115 | jbyteArray arr = env->NewByteArray(pixels.size()); 116 | env->SetByteArrayRegion( arr, 0, pixels.size(), (const signed char*) pixels.getData()); 117 | int numFaces = env->CallIntMethod(javaMobileVision, javaMethod, arr, pixels.getWidth(), pixels.getHeight()); 118 | env->DeleteLocalRef(arr); 119 | 120 | vector analyzedfaces; 121 | for(int i=0;iGetMethodID(javaClass, "getData", "(I)[F"); 124 | jfloatArray data = (jfloatArray) env->CallObjectMethod(javaMobileVision, method, 0); 125 | 126 | jboolean isCopy; 127 | jfloat *body = env->GetFloatArrayElements(data, &isCopy); 128 | 129 | ofxAndroidMobileVisionFace face; 130 | face.smileProbability = body[0]; 131 | face.leftEyeOpenProbability = body[1]; 132 | face.rightEyeOpenProbability = body[2]; 133 | for(int j=0;j<12;j++){ 134 | ofVec2f p; 135 | p.x = body[j*2+3]; 136 | p.y = body[j*2+4]; 137 | face.landmarks.push_back(p); 138 | } 139 | analyzedfaces.push_back(face); 140 | 141 | env->DeleteLocalRef(data); 142 | } 143 | 144 | fromAnalyze.send(analyzedfaces); 145 | } 146 | 147 | void ofxAndroidMobileVision::threadedFunction(){ 148 | ToAnalyzeData d; 149 | while(toAnalyze.receive(d)){ 150 | process(d.pixels); 151 | } 152 | } 153 | #endif -------------------------------------------------------------------------------- /src/ofxAndroidMobileVision.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ofConstants.h" 3 | #include "ofThread.h" 4 | #include "ofThreadChannel.h" 5 | 6 | #ifdef TARGET_ANDROID 7 | 8 | #include 9 | 10 | #include "ofBaseTypes.h" 11 | 12 | 13 | struct ofxAndroidMobileVisionFace { 14 | float smileProbability; 15 | float leftEyeOpenProbability; 16 | float rightEyeOpenProbability; 17 | 18 | vector landmarks; 19 | }; 20 | 21 | struct ToAnalyzeData{ 22 | ofPixels pixels; 23 | }; 24 | 25 | class ofxAndroidMobileVision : ofThread{ 26 | public: 27 | 28 | ofxAndroidMobileVision(); 29 | ~ofxAndroidMobileVision(); 30 | 31 | void setup(); 32 | void update(ofPixels & pixels); 33 | 34 | bool setThreaded(bool threaded); 35 | 36 | void setTrackProminentFaceOnly(bool prominentOnly); 37 | void setMinFaceSize(float minFaceSize); 38 | 39 | vector &getFaces(); 40 | 41 | private: 42 | jclass javaClass; 43 | jobject javaMobileVision; 44 | 45 | bool threaded; 46 | 47 | ofThreadChannel toAnalyze; 48 | ofThreadChannel > fromAnalyze; 49 | 50 | vector faces; 51 | 52 | void threadedFunction(); 53 | void process(ofPixels &pixels); 54 | }; 55 | 56 | #endif --------------------------------------------------------------------------------