简介

HUAWEI HiAI是面向智能终端的AI能力开放平台,基于 "芯、端、云"三层开放架构,即芯片能力开放、应用能力开放、服务能力开放,构筑全面开放的智慧生态,让开发者能够快速地利用华为强大的AI处理能力,为用户提供更好的智慧应用体验。

HiAI Engine为应用能力开放,提供丰富的AI能力供应用集成。美学评分是其中一个AI能力,它将允许您的应用:

要了解更多信息,请参阅我们官网:
https://developer.huawei.com/consumer/cn/hiai/engine/aesthetic-score

您将建立什么

在本次Codelab中,您将运用此项AI能力建立一款Android应用程序,它可以多种维度对图片进行综合评分
例如:

您将会学到什么

您需要什么

硬件要求

开发计算机(台式机或笔记本电脑)
芯片为麒麟970及以上版本,且操作系统为EMUI8.1.0及以上版本的华为手机

软件要求

JAVA JDK安装包
Android Studio 3.1或更高版本
Android SDK包
HiAI SDK包

下载HiAI SDK包

步骤1 启动Android Studio,选择创建新的android工程:

步骤2 通过工程向导,创建新的android工程:

Application name(应用名) 填写为:visionfun(自定义)

Company domain (公司或单位域名) 填写为:hiai.plx.huawei.com (自定义)

Project location(工程路径)填写为:D:/code/visionfun(自定义)

选择API版本为26以上:

选择默认窗口为Empty Activity:

填写Activty的名称为默认的MainActivity

填写布局名称为默认的activity_main,并点击Finish,创建工程代码。

Android studio开始创建和编译构建工程代码:

当左下角出现"gradle build finished" 的字样时候,工程编译完成。否则,可能出现一些错误,一般是网络连接出错导致的,请检查网络路线,代理设置,数字证书等配置。

步骤1 将下载好的HiAI SDK包放到工程项目app目录下libs目录中

步骤2 配置项目的build.gradle文件

确保您的项目build.gradle包含Google的Maven存储库:

allprojects { repositories { google() ... mavenLocal() }

确认您的应用build.gradle有以下条目。

android { compileSdkVersion 28 defaultConfig { ... targetSdkVersion 28 ... } repositories{ flatDir { dirs 'libs' } } } dependencies { ... implementation (name: 'huawei-hiai-vision', ext: 'aar') implementation 'com.google.code.gson:gson:2.8.2' ... }

步骤3 更新你的 AndroidManifest.xml

确认您的应用的清单文件(在Android Studio中,app-> manifests-> AndroidManifest.xml)请求CAMERA访问权限。

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="..." > ... <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> ...

您可以参考如下示例进行界面设计:

步骤1 初始化hiai

public class MainActivity extends AppCompatActivity { ...... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //To connect vision service VisionBase.init(getApplicationContext(),new ConnectionCallback(){ @Override public void onServiceConnect() { Log.i(TAG, "onServiceConnect "); } @Override public void onServiceDisconnect() { Log.i(TAG, "onServiceDisconnect"); } }); ....... }

步骤2 初始化控件并设置点击监听

public class MainActivity extends AppCompatActivity { Button btnCamera; Button btnGallery; Button btnRun; ImageView imgMain; Thread mThread; Bitmap mBitmap; private Uri imageUri; ...... @Override protected void onCreate(Bundle savedInstanceState) { ....... imgMain = (ImageView) findViewById(R.id.img_main); btnCamera = (Button) findViewById(R.id.btn_camera); btnGallery = (Button) findViewById(R.id.btn_gallery); btnRun = (Button) findViewById(R.id.btn_run); btnCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onCameraClick(); } }); btnGallery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onGalleryClick(); } }); btnRun.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mThread = new Thread(r); mThread.start(); } }); requestPermissions(); }

步骤3 向系统申请相机和执行存储权限

public class MainActivity extends AppCompatActivity { ...... private void requestPermissions(){ try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); if(permission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_IMAGE_GALLERY); } permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA); if(permission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.CAMERA}, REQUEST_IMAGE_CAPTURE); } } } catch (Exception e) { e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }

步骤4 增加打开相机、打开图库和执行指定的图片的操作方法:

public class MainActivity extends AppCompatActivity { ...... private void onCameraClick() { takePictureFromCamera(); } private void onGalleryClick() { chooseImageFromStorage(); } }

上面两个未定义的方法takePictureFromCamera()chooseImageFromStorage()分别表示拍照和选择图库相片,定义如下:

public class MainActivity extends AppCompatActivity { ...... private void takePictureFromCamera() { File fileImage = new File(getExternalCacheDir(), "image.jpg"); try { if (fileImage.exists()){ fileImage.delete(); } fileImage.createNewFile(); } catch (IOException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= 24){ imageUri = FileProvider.getUriForFile(MainActivity.this, "com.huawei.hiai.tutorial.visionfun.fileProvider", fileImage); } else { imageUri = Uri.fromFile(fileImage); } Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); if (intent.resolveActivity(getPackageManager()) != null) { startActivityForResult(intent, REQUEST_IMAGE_CAPTURE); } } private void chooseImageFromStorage() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); if(intent.resolveActivity(getPackageManager() )!= null){ startActivityForResult(intent, REQUEST_IMAGE_GALLERY); } } }

上面的代码解释一下:getExternalCacheDir() 可以获取 SD 卡中专门用于存放当前应用缓存数据的位置。

getUriForFile() 方法接收三个参数:Context对象、任意唯一的字符串与 File对象。从 android 7.0+ 系统开始,直接使用本地真实的路径被认为是不安全的,会抛出一个 FileExposedException 异常,而 FileProvider 是一种特殊的内容提供器,它使用与内容提供器类似的机制对数据进行保护。

下面在AndroidManifest.xml 中注册刚才定义的内容提供器,在<Application>标签下追加如下provider

<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.huawei.hiai.tutorial.visionfun.fileProvider" android:exported="false" android:grantUriPermissions="true"> <!--指定 Uri 的共享路径--> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" /> </provider>

这里的meta-data引用了xml来指定共享路径。

在缺失的资源定义(红色的@xml/file_provider_paths)悬停鼠标,在行首出现的泡泡上选择createxmlresource"file_provider_path.xml"

选择OK后,在创建的xml文件中增加定义:

<external-path name="img" path=""/>

效果如下:

然后定义onActivityResult方法来接收用户拍照或选择图片结果:

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { //super.onActivityResult(requestCode, resultCode, data); if(resultCode == RESULT_CANCELED) return; if (resultCode == RESULT_OK){ try { if (requestCode == REQUEST_IMAGE_CAPTURE) mBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); imgMain.setImageBitmap(mBitmap); else if (requestCode == REQUEST_IMAGE_GALLERY) mBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(data.getData())); imgMain.setImageBitmap(mBitmap); }catch (FileNotFoundException e) { e.printStackTrace(); } } }

步骤5 在子线程中执行我们的API接口

mThread作为主线程外的另一个独立线程运行,运行过程中会等待mWaitResult对象的notify事件,每获取到一次事件,调用指定的CV HiAI模型或算法执行一次图像处理,同步处理完成后显示执行结果。

执行结果的显示和处理后的图片更新,使用JavaHandler消息机制 (mHander),通过消息驱动的异步方式将结果处理集中到一处,提升系统的扩展能力,方便后期维护,详见后续描述。

public class MainActivity extends AppCompatActivity { ...... private Runnable r= new Runnable() { @Override public void run() { AestheticsScoreDetector aestheticsScoreDetector = new AestheticsScoreDetector(MainActivity.this); AEModelConfiguration aeModelConfiguration = new AEModelConfiguration(); aeModelConfiguration.getDetectImageConf().setDetectImageMode(AEConstants.AEImageDetectMode.OSP_MODE | AEConstants.AEImageDetectMode.HF_MODE); aeModelConfiguration.getDetectImageConf().setDetectImageOutputType (AEConstants.AEImageDetectOutputType.OSP_DETAIL); aestheticsScoreDetector.setAEModelConfiguration(aeModelConfiguration); Frame frame = new Frame(); frame.setBitmap(mBitmap); JSONObject jsonObject = aestheticsScoreDetector.detect(frame, null); if (!jsonObject.optString("resultCode").equals("0")) { return; } AestheticsScore aestheticsScores = aestheticsScoreDetector.convertResult(jsonObject); Gson gson = new Gson(); String score = gson.toJson(aestheticsScores); //通过Handler将消息返回主线程 Message msg = new Message(); msg.what = 100; msg.obj = score; mHander.sendMessage(msg); } }; }

步骤6 Handler消息处理

public class MainActivity extends AppCompatActivity { ...... private Handler mHander = new Handler() @Override public void handleMessage(Message msg) { super.handleMessage(msg); int status = msg.what; switch (status) { ....... case 100: if (msg.obj == null) { return; } String score= (String) msg.obj; //拿到结果后执行你的操作 break; ....... } }; } }

步骤1 验证设备连接

要验证您的设备是否通过USB连接到开发计算机,请打开命令控制台并输入以下命令:

adb devices

这将连接到设备并输出设备的序列号。输出应该如下所示:

List of devices attached * daemon not running; starting now at tcp:5037 * daemon started successfully 712KPJP1076540 device

则表示连接成功

步骤2 运行应用程序

在 Android Studio中,单击"运行"。然后,选择您的设备作为部署目标,然后单击" 确定"以在设备上启动示例应用程序。

干得好,你已经成功完成了开发并学到了:

更多HUAWEI HiAI相关信息请关注我们的官网:

https://developer.huawei.com/consumer/cn/hiai

已复制代码