表情包斗图,作为人生新晋一大乐事,已经是广大网友每天必经的聊天互动环节。什么捧腹大笑、满腹槽点、彻底无语……这些万千语言无法贴切描述的情绪细节,总能被一张看似平平无奇的表情包完美形容,可谓一图胜千言,四两拨千斤!
如今,在表情包斗图进入白热化阶段,人们对表情包的定制化需求日益增多,人人是表情包的搬运工,人人也是潜力热图的创造者!赋予个人制作个性化表情包的能力很有必要。使用机器学习的图像分割功能,轻松分割复杂图片背景,让表情包制作简单而高效,让我们来看看如何将一张图片变成传情达意的表情包吧!
开发准备
Maven仓和SDK的配置步骤可以参考开发者网站中的应用开发介绍
https://developer.huawei.com/...
配置集成的SDK包
图像分割提供了两种SDK集成方式,一种是预先将分割算法包预先集成在应用中,另一种是在应用安装运行后,再将所需的算法包下载到应用中,可以根据应用的使用场景和所需效果进行选择。
本文是使用了Full SDK的集成方案,在应用的build.gradle文件中,dependencies内添加图像分割的SDK依赖
implementation 'com.huawei.hms:ml-computer-vision-segmentation:2.2.0.300'
implementation 'com.huawei.hms:ml-computer-vision-image-segmentation-body-model:2.2.0.300'
配置AndroidManifest.xml
打开main文件夹中的AndroidManifest.xml文件,可以根据场景和使用需要,配置读取和写入手机存储的权限,在<application>前添加
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
在<application>内添加如下语句,当应用安装后,会自动更新最新的机器学习模型到设备
<meta-data
android:name="com.huawei.hms.ml.DEPENDENCY"
android:value= "imagesuperresolution"/>
开发步骤
配置存储权限申请
手机的存储权限,除了在Manifest中声明,还需要在Activity中进行动态申请。
在MainActivity的onCreate()方法中,调用requestPermission方法,对WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限进行申请
protected void onCreate(Bundle savedInstanceState) {
……
requestPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
……
}
requestPermission方法的实现如下,首先对权限进行判断,如果未获取权限,则进行申请,如果已经获取了,则返回
private void requestPermission(String permisssions) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return;
}
if (ContextCompat.checkSelfPermission(this, permisssions)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{permisssions}, REQUEST_CODE);
} else {
return;
}
}
读取相册中的图片
我们先从相册中选取要进行表情包制作的图片,在xml文件中创建一个按钮chooseImg,点击后调用selectLocalImage方法
this.relativeLayoutLoadPhoto = this.findViewById(R.id.chooseImg);
this.relativeLayoutLoadPhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.selectLocalImage(StillCutPhotoActivity.this.REQUEST_CHOOSE_ORIGINPIC);
}
});
selectLocalImage方法的实现如下
private void selectLocalImage(int requestCode) {
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
this.startActivityForResult(intent, requestCode);
}
进行图像分割制作表情包
再创建一个按钮cut,点击后调用createImageTransactor方法,进行图像分割分析器的创建
this.relativeLayoutCut = this.findViewById(R.id.cut);
this.relativeLayoutCut.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (MainActivity.this.imageUri == null) {
Toast.makeText(MainActivity.this.getApplicationContext(), R.string.please_select_picture, Toast.LENGTH_SHORT).show();
} else {
MainActivity.this.createImageTransactor();
Toast.makeText(MainActivity.this.getApplicationContext(), R.string.cut_success, Toast.LENGTH_SHORT).show();
}
}
});
在createImageTransactor方法中,我们首先创建一个图像分割的分析器,再进行分析器的配置,将其设置为人像分割模式
private MLImageSegmentationAnalyzer analyzer;
MLImageSegmentationSetting setting = new MLImageSegmentationSetting.Factory().setAnalyzerType(MLImageSegmentationSetting.BODY_SEG).create();
this.analyzer = MLAnalyzerFactory.getInstance().getImageSegmentationAnalyzer(setting);
从相册中选取的文件,通过imageUri,将其打开为Bitmap格式
Pair<Integer, Integer> targetedSize = this.getTargetSize();
int targetWidth = targetedSize.first;
int targetHeight = targetedSize.second;
this.originBitmap = BitmapUtils.loadFromPath(StillCutPhotoActivity.this, this.imageUri, targetWidth, targetHeight);
之后创建MLFrame,调用asyncAnalyseFrame进行异步图像分割处理
MLFrame mlFrame = new MLFrame.Creator().setBitmap(this.originBitmap).create();
Task<MLImageSegmentation> task = this.analyzer.asyncAnalyseFrame(mlFrame);
图像分割完成后,对返回的结果进行处理,将去除背景后的图像保存到processedImage中
task.addOnSuccessListener(new OnSuccessListener<MLImageSegmentation>() {
@Override
public void onSuccess(MLImageSegmentation mlImageSegmentationResults) {
// Transacting logic for segment success.
if (mlImageSegmentationResults != null) {
StillCutPhotoActivity.this.foreground = mlImageSegmentationResults.getForeground();
StillCutPhotoActivity.this.preview.setImageBitmap(StillCutPhotoActivity.this.foreground);
StillCutPhotoActivity.this.processedImage = ((BitmapDrawable) ((ImageView) StillCutPhotoActivity.this.preview).getDrawable()).getBitmap();
} else {
StillCutPhotoActivity.this.displayFailure();
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Transacting logic for segment failure.
StillCutPhotoActivity.this.displayFailure();
return;
}
});
保存表情包
最后将处理好的图像转换成png格式,存储到系统相册中
public void saveToAlbum(Bitmap bitmap){
File file = null;
String fileName = System.currentTimeMillis() +".png";
File root = new File(Environment.getExternalStorageDirectory().getAbsoluteFile(), this.context.getPackageName());
File dir = new File(root, "image");
if(dir.mkdirs() || dir.isDirectory()){
file = new File(dir, fileName);
}
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush();
} catch (FileNotFoundException e) {
Log.e(TAG, e.getMessage());
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}finally {
try {
if(os != null) {
os.close();
}
}catch (IOException e){
Log.e(TAG, e.getMessage());
}
}
if(file == null){
return;
}
if(imageUtilCallBack != null) {
try {
imageUtilCallBack.callSavePath(file.getCanonicalPath());
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
// Gallery refresh.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
String path = null;
try {
path = file.getCanonicalPath();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
MediaScannerConnection.scanFile(this.context, new String[]{path}, null,
new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(uri);
ImageUtils.this.context.sendBroadcast(mediaScanIntent);
}
});
} else {
String relationDir = file.getParent();
File file1 = new File(relationDir);
this.context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(file1.getAbsoluteFile())));
}
}
效果示例
编译运行后,可以从相册中选择想要制作表情包的照片,点击Cut,ML Kit会完成之后的人像识别和图像分割步骤,将杂乱的背景去除,返回人像的图片,之后点击Save即可将其保存为没有背景的表情包。
保存后即可将表情包添加到社交软件中啦,快来试试吧!
了解更多相关内容
访问华为机器学习图像分割服务官网
解决集成问题请到Stack Overflow
点击关注,第一时间了解HMS Core最新技术~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。