上一篇实现了语音播放的功能,接下来就开始实现相机及图像识别功能调用。
首先,画一个大致的流程图方便理解:
以下是对相机/选择相册进行实现。
(大家可以参考其它Android调用相机/选择相册的教程,不一定使用我的代码,只要获取好地址就可以。)
(主要步骤是启动相机-调用相机-保存图片(地址一定要保存好),在相册选择图片也亦然,知道步骤后实现就比较简单)
Button takePhoto = findViewById(R.id.camerabutton);//拍照的按钮
takePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA},24);
}
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//判断版本号
if (Build.VERSION.SDK_INT < 24) {
imageUri = Uri.fromFile(outputImage);
} else {
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.MapScanner.MapScanner", outputImage);
}
// 启动相机程序
Log.d("TAG", "---------b"+outputImage.getAbsolutePath());
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");;
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
}
});
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d("TAG", "---------d----"+requestCode+"----"+resultCode+"----"+data);
//成功的时候requestCode:1 resultCode:1 失败的时候requestCode:1 resultCode:0
switch (requestCode) {
case 1:
try {// 将拍摄的照片显示出来
Date date=new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyyMMddHHmmss");
String fileName=dateFormat.format(date)+".jpeg";
String name=Environment.getExternalStorageDirectory().getPath()+"/DCIM/Camera/"+fileName ;
File file = new File(name);
FileOutputStream out;
Matrix matrix = new Matrix();
matrix.setRotate(90);
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, true);
picture.setImageBitmap(bitmap);
try{
out = new FileOutputStream(file);
// 格式为 JPEG,照相机拍出的图片为JPEG格式的,PNG格式的不能显示在相册中
if(bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out))
{
out.flush();
out.close();
MediaStore.Images.Media.insertImage(this.getContentResolver(), file.getAbsolutePath(), name, null);
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
path=name;
Log.d("TAG", "imagePath====== " + imageUri);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 2:
// 判断手机系统版本号
if (Build.VERSION.SDK_INT >= 19) {
// 4.4及以上系统使用这个方法处理图片
handleImageOnKitKat(data);
} else {
// 4.4以下系统使用这个方法处理图片
handleImageBeforeKitKat(data);
}
break;
default:
break;
}
}
对于选取相册中的图片。
Button choosePicture = findViewById(R.id.picturebutton);
choosePicture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);
}
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO);
}
});
HandleImage的处理
@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
Log.d("TAG", "handleImageOnKitKat: uri is " + uri);
if (DocumentsContract.isDocumentUri(this, uri)) {
// 如果是document类型的Uri,则通过document id处理
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":")[1]; // 解析出数字格式的id
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
}
else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
imagePath = getImagePath(contentUri, null);
}
}
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// 如果是content类型的Uri,则使用普通方式处理
imagePath = getImagePath(uri, null);
}
else if ("file".equalsIgnoreCase(uri.getScheme())) {
// 如果是file类型的Uri,直接获取图片路径即可
imagePath = uri.getPath();
}
displayImage(imagePath); // 根据图片路径显示图片
}
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
displayImage(imagePath);
}
private String getImagePath(Uri uri, String selection) {
String path = null;
// 通过Uri和selection来获取真实的图片路径
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
private void displayImage(String imagePath) {
path=imagePath;
if (imagePath != null) {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
picture.setImageBitmap(bitmap);
}
else {
Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
}
}
(权限的声明在上一篇已经设置好,这里就不重复设置了)
相机设置完毕后,就是实现图像识别调用的功能,因为我们实现的是四个识别,其实界面都是一样,只是方法调用里面只需要修改,所以我们在跳转页面的时候,给它一个标志位,只需要修改调用的内容和把处理返回来数据内容作修改即可。
看图的话直观一点:(两个不同识别是使用同一个界面,只是调用的接口不一样,其他功能可以复用)
上代码:
首先是ViewActivity下,跳转到MainActivity回传选择的是哪一个识别。
(只截取了ViewActivity跳转部分代码,ViewActivity在介绍界面的时候再做细讲。)
public void myClick(View v)
{
//btncommon btnanimal btnplant btnplace
String name="";
switch(v.getId())
{
case R.id.btncommon:
name="btncommon";
break;
case R.id.btnanimal:
name="btnanimal";
break;
case R.id.btnplant:
name="btnplant";
break;
case R.id.btnplace:
name="btnplace";
break;
}
Intent i = new Intent(ViewActivity.this, MainActivity.class);
i.putExtra("myChoose",name);
startActivity(i);
}
跳转到MainActivity中获取name。(这里是把标题修改了)
Intent i=getIntent();//页面选择
final String youChoose=i.getStringExtra("myChoose");
switch (youChoose){
case "btncommon":
setTitle("通用识别");
break;
case "btnanimal":
setTitle("动物识别");
break;
case "btnplant":
setTitle("植物识别");
break;
case "btnplace":
setTitle("地标识别");
break;
}
接下来截取通用识别代码部分讲解。
当我们把图片选择好/调用拍照后,点击识别按钮,就开始调用 通用识别 进行工作。
首先图像识别申请信息必不可少
//图像识别申请信息
String APP_ID="你的百度id";
String API_KEY="你的百度KEY";
String SECRET_KEY="你的百度SECRET_KEY";
String path="";//存地址
Button takePhoto = findViewById(R.id.camerabutton);
Button choosePicture = findViewById(R.id.picturebutton);
Button chooseOk=findViewById(R.id.okbutton);
//按钮的定义按照自己的需求
chooseok点击进行处理
chooseOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (path!="") {
Toast.makeText(getApplicationContext(),"正在识别...请稍等",Toast.LENGTH_LONG).show();
new Thread(){
public void run(){
//线程处理
AipImageClassify client = new AipImageClassify(APP_ID, API_KEY, SECRET_KEY);
HashMap<String, String> options = new HashMap<String, String>();
options.put("baike_num", "4");
String image = path;
switch (youChoose){
case "btncommon":
res = client.advancedGeneral(image, options);//通用
try {
JSONArray jsonArray = res.getJSONArray("result");//返回JsonArray
for(int i=0;i<jsonArray.length();i++){
JSONObject json=(JSONObject)jsonArray.get(i);
String type=json.getString("root");
JSONObject baike_info=json.getJSONObject("baike_info");
String description="";
String name=json.getString("keyword");
if(!type.isEmpty())
{
System.out.println("in");
save_name=name;
save_type=type;
if(baike_info.toString().length()!=2)//最佳识别有可能无描述的情况
{
description= baike_info.getString("description");
Speck_TEXT=""+name+"的简介是:"+description+"";
}
else{
Speck_TEXT=""+name+"的简介并未查找到最佳匹配,若需要获取简介,请再次查询。";
}
//安卓6.0版本,子线程更新UI将报错弹出,需要用handler处理UI更新
Message message = Message.obtain();
message.what=1;
handler.sendMessage(message);
break;
}
}
} catch (JSONException e) {
System.out.println("in Exception");
e.printStackTrace();
Message message = Message.obtain();
message.what=2;
handler.sendMessage(message);
}
break;
case "btnanimal":
/*与上面差不多描写方法*/
break;
}
}
}.start();
}
else{
Toast.makeText(getApplicationContext(),"尚未导入图片",Toast.LENGTH_SHORT).show();
}
}
});
我们可以看到不同识别的调用在于
res = client.advancedGeneral(image, options);//通用
这一句话里面,如果我是动物识别调用就是
res = client.animalDetect(image, options);//动物
所以在switch里面,只需要修改client.xxx函数,还有对返回的数据
res.getJSONObject
进行处理显示,即可达到效果。(不同的识别调用返回的数据格式不一样,需要分开处理,详情阅读 视觉技术 图像识别 API文档 可查阅有关返回的格式信息)
我们还发现,我们在Android6.0版本测试中,ui在此线程中更新的话会报错(UI线程不安全),为解决这个问题,我们通过用Handler方式更新UI。
private Handler handler=new Handler(){
public void handleMessage(android.os.Message msg){//获取消息携带的属性值
int what = msg.what;
System.out.println("-what--->>" + what);
switch (what)
{
case 1:
Mytype.setText("类型: "+save_type);
Myname.setText("名称: "+save_name);
mShowText.setText(Speck_TEXT);
break;
case 2:
Mytype.setText("类型: 无");
Myname.setText("名称: 无");
mShowText.setText("类型无法识别,请尝试重新拍照或按类型进行识别。");
break;
default:break;
}
};
};
结合上一节和这一节,语音播放和拍照/选择图片/图像识别功能局可以成功实现。(核心功能就已经实现)
下一节的话,就介绍界面的设计/启动程序动画。