Emojify 人脸识别表情

效果图:

人脸识别成表情需要playservice服务支持

grandle:

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:design:28.0.0'
implementation 'com.google.android.gms:play-services-vision:10.2.0'
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
implementation 'com.jakewharton.timber:timber:4.7.0'
}


activity.main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.admin.emojify.MainActivity">

<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/view_margin"
android:contentDescription="@string/imageview_description"
android:scaleType="fitStart" />

<TextView
android:id="@+id/title_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/emojify_button"
android:layout_centerHorizontal="true"
android:layout_margin="@dimen/view_margin"
android:text="@string/emojify_me"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

<Button
android:id="@+id/emojify_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/go"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"/>

<android.support.design.widget.FloatingActionButton
android:id="@+id/clear_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:visibility="gone"
android:src="@drawable/ic_clear"
app:backgroundTint="@android:color/white"
app:fabSize="mini" />

<android.support.design.widget.FloatingActionButton
android:id="@+id/save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins"
android:layout_marginRight="@dimen/fab_margins"
android:src="@drawable/ic_save"
android:visibility="gone"
app:backgroundTint="@android:color/white" />

<android.support.design.widget.FloatingActionButton
android:id="@+id/share_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginLeft="@dimen/fab_margins"
android:layout_marginStart="@dimen/fab_margins"
android:src="@drawable/ic_share"
android:visibility="gone"
app:backgroundTint="@android:color/white" />

</RelativeLayout>



file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="my_cache" path="." />
<external-path name="my_images" path="Pictures/" />
</paths>


mainfest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.admin.emojify">

<!-- 不支持相机无法安装-->
<uses-feature
android:name="android.hardware.camera2"
android:required="true"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>

</manifest>


BitmapUtils
package com.example.admin.emojify;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.support.v4.content.FileProvider;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class BitmapUtils {

private static final String FILE_PROVIDER_AUTHORITY = "com.example.android.fileprovider";



static Bitmap resamplePic(Context context, String imagePath) {
// Get device screen size information
DisplayMetrics metrics = new DisplayMetrics();
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
manager.getDefaultDisplay().getMetrics(metrics);
int targetH = metrics.heightPixels;
int targetW = metrics.widthPixels;
// Get the dimensions of the original bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
return BitmapFactory.decodeFile(imagePath);
}


static File createTempImageFile(Context context) throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = context.getExternalCacheDir();
return File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
}




static boolean deleteImageFile(Context context, String imagePath) {
File imageFile = new File(imagePath);
boolean deleted = imageFile.delete();
if (!deleted) {
String errorMessage = context.getString(R.string.error);
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show();
}
return deleted;
}


//通知相册有新的图片
private static void galleryAddPic(Context context, String imagePath) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(imagePath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
}

static String saveImage(Context context, Bitmap image) {
String savedImagePath = null;
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + ".jpg";
File storageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ "/Emojify");
boolean success = true;
if (!storageDir.exists()) {
success = storageDir.mkdirs();
}
if (success) {
File imageFile = new File(storageDir, imageFileName);
savedImagePath = imageFile.getAbsolutePath();
try {
OutputStream fOut = new FileOutputStream(imageFile);
image.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.close();
} catch (Exception e) {
e.printStackTrace();
}
galleryAddPic(context, savedImagePath);
String savedMessage = context.getString(R.string.saved_message, savedImagePath);
Toast.makeText(context, savedMessage, Toast.LENGTH_SHORT).show();

}
return savedImagePath;
}


static void shareImage(Context context, String imagePath) {
// Create the share intent and start the share activity
File imageFile = new File(imagePath);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
Uri photoURI = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, imageFile);
shareIntent.putExtra(Intent.EXTRA_STREAM, photoURI);
context.startActivity(shareIntent);
}

}

Emojifier
 
package com.example.admin.emojify;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.SparseArray;
import android.widget.Toast;
import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.face.Face;
import com.google.android.gms.vision.face.FaceDetector;
import timber.log.Timber;

public class Emojifier {


private static final float EMOJI_SCALE_FACTOR = .9f;
private static final double SMILING_PROB_THRESHOLD = .15;
private static final double EYE_OPEN_PROB_THRESHOLD = .5;


static Bitmap detectFacesandOverlayEmoji(Context context, Bitmap picture) {
// Create the face detector, disable tracking and enable classifications
FaceDetector detector = new FaceDetector.Builder(context)
.setTrackingEnabled(false)
.setClassificationType(FaceDetector.ALL_CLASSIFICATIONS)
.build();

// Build the frame
Frame frame = new Frame.Builder().setBitmap(picture).build();
// Detect the faces
SparseArray<Face> faces = detector.detect(frame);
// Log the number of faces
Timber.d( "detectFaces: number of faces = " + faces.size());
// Initialize result bitmap to original picture
Bitmap resultBitmap = picture;
// If there are no faces detected, show a Toast message
if (faces.size() == 0) {
Toast.makeText(context, R.string.no_faces_message, Toast.LENGTH_SHORT).show();
} else {

// Iterate through the faces
for (int i = 0; i < faces.size(); ++i) {
Face face = faces.valueAt(i);

Bitmap emojiBitmap;
switch (whichEmoji(face)) {
case SMILE:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.smile);
break;
case FROWN:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.frown);
break;
case LEFT_WINK:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.leftwink);
break;
case RIGHT_WINK:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.rightwink);
break;
case LEFT_WINK_FROWN:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.leftwinkfrown);
break;
case RIGHT_WINK_FROWN:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.rightwinkfrown);
break;
case CLOSED_EYE_SMILE:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.closed_smile);
break;
case CLOSED_EYE_FROWN:
emojiBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.closed_frown);
break;
default:
emojiBitmap = null;
Toast.makeText(context, R.string.no_emoji, Toast.LENGTH_SHORT).show();
}

// Add the emojiBitmap to the proper position in the original image
resultBitmap = addBitmapToFace(resultBitmap, emojiBitmap, face);
}
}

// Release the detector
detector.release();
return resultBitmap;
}


private static Emoji whichEmoji(Face face) {
// Log all the probabilities
Timber.d( "whichEmoji: smilingProb = " + face.getIsSmilingProbability());
Timber.d( "whichEmoji: leftEyeOpenProb = "
+ face.getIsLeftEyeOpenProbability());
Timber.d( "whichEmoji: rightEyeOpenProb = "
+ face.getIsRightEyeOpenProbability());

boolean smiling = face.getIsSmilingProbability() > SMILING_PROB_THRESHOLD;
boolean leftEyeClosed = face.getIsLeftEyeOpenProbability() < EYE_OPEN_PROB_THRESHOLD;
boolean rightEyeClosed = face.getIsRightEyeOpenProbability() < EYE_OPEN_PROB_THRESHOLD;

// Determine and log the appropriate emoji
Emoji emoji;
if(smiling) {
if (leftEyeClosed && !rightEyeClosed) {
emoji = Emoji.LEFT_WINK;
} else if(rightEyeClosed && !leftEyeClosed){
emoji = Emoji.RIGHT_WINK;
} else if (leftEyeClosed){
emoji = Emoji.CLOSED_EYE_SMILE;
} else {
emoji = Emoji.SMILE;
}
} else {
if (leftEyeClosed && !rightEyeClosed) {
emoji = Emoji.LEFT_WINK_FROWN;
} else if(rightEyeClosed && !leftEyeClosed){
emoji = Emoji.RIGHT_WINK_FROWN;
} else if (leftEyeClosed){
emoji = Emoji.CLOSED_EYE_FROWN;
} else {
emoji = Emoji.FROWN;
}
}
// Log the chosen Emoji
Timber.d( "whichEmoji: " + emoji.name());
return emoji;
}



private static Bitmap addBitmapToFace(Bitmap backgroundBitmap, Bitmap emojiBitmap, Face face) {

// Initialize the results bitmap to be a mutable copy of the original image
Bitmap resultBitmap = Bitmap.createBitmap(backgroundBitmap.getWidth(),
backgroundBitmap.getHeight(), backgroundBitmap.getConfig());

// Scale the emoji so it looks better on the face
float scaleFactor = EMOJI_SCALE_FACTOR;

// Determine the size of the emoji to match the width of the face and preserve aspect ratio
int newEmojiWidth = (int) (face.getWidth() * scaleFactor);
int newEmojiHeight = (int) (emojiBitmap.getHeight() *
newEmojiWidth / emojiBitmap.getWidth() * scaleFactor);


// Scale the emoji
emojiBitmap = Bitmap.createScaledBitmap(emojiBitmap, newEmojiWidth, newEmojiHeight, false);

// Determine the emoji position so it best lines up with the face
float emojiPositionX =
(face.getPosition().x + face.getWidth() / 2) - emojiBitmap.getWidth() / 2;
float emojiPositionY =
(face.getPosition().y + face.getHeight() / 2) - emojiBitmap.getHeight() / 3;

// Create the canvas and draw the bitmaps to it
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(backgroundBitmap, 0, 0, null);
canvas.drawBitmap(emojiBitmap, emojiPositionX, emojiPositionY, null);
return resultBitmap;
}


private enum Emoji {
SMILE,
FROWN,
LEFT_WINK,
RIGHT_WINK,
LEFT_WINK_FROWN,
RIGHT_WINK_FROWN,
CLOSED_EYE_SMILE,
CLOSED_EYE_FROWN
}

}
 
MainActivity
 
package com.example.admin.emojify;

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.annotation.NonNull;

import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import timber.log.Timber;

public class MainActivity extends AppCompatActivity {



private static final int REQUEST_IMAGE_CAPTURE = 1;
private static final int REQUEST_STORAGE_PERMISSION = 1;
private static final String FILE_PROVIDER_AUTHORITY = "com.example.android.fileprovider";


@BindView(R.id.image_view) ImageView mImageView;
@BindView(R.id.emojify_button) Button mEmojifyButton;
@BindView(R.id.share_button) FloatingActionButton mShareFab;
@BindView(R.id.save_button) FloatingActionButton mSaveFab;
@BindView(R.id.clear_button) FloatingActionButton mClearFab;
@BindView(R.id.title_text_view) TextView mTitleTextView;

private String mTempPhotoPath;
private Bitmap mResultsBitmap;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
Timber.plant(new Timber.DebugTree());
}


@OnClick(R.id.emojify_button)
public void emojifyMe(){
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_STORAGE_PERMISSION);
} else {
// Launch the camera if the permission exists
launchCamera();

}
}



@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case REQUEST_STORAGE_PERMISSION:{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
launchCamera();
}else {
Toast.makeText(MainActivity.this,getString(R.string.refused_authorization),Toast.LENGTH_SHORT).show();
new AlertDialog.Builder(MainActivity.this)
.setMessage(getString(R.string.need_permission))
.setPositiveButton(getString(R.string.settings), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

Uri uri=Uri.fromParts("package",getPackageName(),null);
Intent intent=new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,uri);
startActivity(intent);
}
}).setNeutralButton(getString(R.string.cancel),null)
.create()
.show();
}break;

}
}
}

private void launchCamera() {
// Create the capture image intent
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the temporary File where the photo should go
File photoFile = null;
try {
photoFile = BitmapUtils.createTempImageFile(this);
} catch (IOException ex) {
// Error occurred while creating the File
Timber.e("Create file failed");
}
// Continue only if the File was successfully created
if (photoFile != null) {
// Get the path of the temporary file
mTempPhotoPath = photoFile.getAbsolutePath();
// Get the content URI for the image file
Uri photoURI = FileProvider.getUriForFile(this,
FILE_PROVIDER_AUTHORITY,
photoFile);
// Add the URI so the camera can store the image
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
// Launch the camera activity
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE&&resultCode==RESULT_OK){
mEmojifyButton.setVisibility(View.GONE);
mTitleTextView.setVisibility(View.GONE);
mSaveFab.setVisibility(View.VISIBLE);
mShareFab.setVisibility(View.VISIBLE);
mClearFab.setVisibility(View.VISIBLE);
mResultsBitmap = BitmapUtils.resamplePic(this, mTempPhotoPath);
mResultsBitmap = Emojifier.detectFacesandOverlayEmoji(this, mResultsBitmap);
mImageView.setImageBitmap(mResultsBitmap);
}else {
// Otherwise, delete the temporary image file
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
}
}

@OnClick(R.id.save_button)
public void save(View view){
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
BitmapUtils.saveImage(this, mResultsBitmap);
}



@OnClick(R.id.share_button)
public void share(View view){
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
BitmapUtils.saveImage(this, mResultsBitmap);
BitmapUtils.shareImage(this, mTempPhotoPath);
}



@OnClick(R.id.clear_button)
public void clear(View view){
mImageView.setImageResource(0);
mEmojifyButton.setVisibility(View.VISIBLE);
mTitleTextView.setVisibility(View.VISIBLE);
mShareFab.setVisibility(View.GONE);
mSaveFab.setVisibility(View.GONE);
mClearFab.setVisibility(View.GONE);
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
}

}


Github地址:https://github.com/NeoWu55/Emojify
 
 
 

猜你喜欢

转载自www.cnblogs.com/neowu/p/10927022.html