目录
效果图
引言
效果图是在模拟器上运行的,调用相机的时候是调用的电脑上的相机,所以我选择的是实时截取屏幕,随便选了一个图片,然后就是关于这个智能识别,毕竟是调用的百度的API,所以…注意保密性…百度后台应该是能看到拍的图片的。结果返回的是一个Json字符串我就没有去处理它了,然后朋友帮我处理了一下…
处理后的效果
步骤
- 首先是调用相机
- 然后将得到的图片显示在Activity上
- 再然后就是调用百度智能云的API处理图片,当然第三步和第二步是同时处理的
- 最后就是想结果显示到Activity上
调用相机
- 先在AndroidManifest.xml里面添加一些配置信息
添加权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
在< application></ application>标签里面添加相机内容提供者
<!-- 为调用相机拍照设置内容提供者 -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.mydemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:ignore="WrongManifestParent">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
并在< activity></ activity>里面添加
- 然后就是通过按钮点击调用相机,并且将图片传到相应Activity显示
public class capture extends AppCompatActivity {
public static final int TAKE_PHOTO = 1;//声明一个请求码,用于识别返回的结果
private Uri imageUri;
private final String filePath = getExternalFilesDir(null) + File.separator + "output_image.jpg";//根目录下
//Environment.getExternalStorageDirectory();这个方法已经废弃,上一行代码的替换没测试过是否成功
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_capture);
//点击事件进行拍照
Button takephoto = findViewById(R.id.take_photo);
textView = findViewById(R.id.textView);
takephoto.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//动态请求相机权限
requestPermission(); //在其中若用户给予权限则请求相机拍照
}
});
//设置默认图片
setDefualtImage();
}
//动态请求权限
private void requestPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//请求权限
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);
} else {
//调用
requestCamera();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults != null && grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
switch (requestCode) {
case 1: {
requestCamera();
}
break;
}
}
}
private void requestCamera() {
File outputImage = new File(filePath);
/*
创建一个File文件对象,用于存放摄像头拍下的图片,我们把这个图片命名为output_image.jpg
并把它存放在应用关联缓存目录下,调用getExternalCacheDir()可以得到这个目录,为什么要
用关联缓存目录呢?由于android6.0开始,读写sd卡列为了危险权限,使用的时候必须要有权限,
应用关联目录则可以跳过这一步
*/
try//判断图片是否存在,存在则删除在创建,不存在则直接创建
{
if (!outputImage.getParentFile().exists()) {
outputImage.getParentFile().mkdirs();
}
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
if (Build.VERSION.SDK_INT >= 24) {
imageUri = FileProvider.getUriForFile(this,
"com.example.mydemo.fileprovider", outputImage);
} else {
imageUri = Uri.fromFile(outputImage);
}
//使用隐示的Intent,系统会找到与它对应的活动,即调用摄像头,并把它存储
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
//调用会返回结果的开启方式,返回成功的话,则把它显示出来
} catch (IOException e) {
e.printStackTrace();
}
}
//处理返回结果的函数,下面是隐示Intent的返回结果的处理方式,具体见以前我所发的intent讲解
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
int Matrix = getBitmapDegree(filePath);
Intent intent = new Intent(capture.this,baiduapi.class);//传过去的activity里面进行识别
intent.putExtra("filepath",filePath);
bitmap=rotateBitmapByDegree(bitmap,Matrix);
//picture.setImageBitmap(bitmap);
//将图片解析成Bitmap对象,并把它显现出来
ByteArrayOutputStream baos=new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 20, baos);//这个是压缩图片
byte [] bitmapByte =baos.toByteArray();
intent.putExtra("bitmap", bitmapByte);
startActivity(intent);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
//设置保存拍照图片——>再次关闭app重新打开显示为上次拍照照片
private void setDefualtImage() {
File outputImage = new File(filePath);
if (!outputImage.exists()) {
return;
}
//picture.setImageBitmap(BitmapFactory.decodeFile(filePath));
}
/**
* 获取图片的旋转角度
*
* @param path 图片绝对路径
* @return 图片的旋转角度
*/
public static int getBitmapDegree(String path) {
int degree = 0;
try {
// 从指定路径下读取图片,并获取其EXIF信息
ExifInterface exifInterface = new ExifInterface(path);
// 获取图片的旋转信息
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
/**
* 将图片按照指定的角度进行旋转
*
* @param bitmap 需要旋转的图片
* @param degree 指定的旋转角度
* @return 旋转后的图片
*/
public static Bitmap rotateBitmapByDegree(Bitmap bitmap, int degree) {
// 根据旋转角度,生成旋转矩阵
Matrix matrix = new Matrix();
matrix.postRotate(degree);
// 将原始图片按照旋转矩阵进行旋转,并得到新的图片
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// if (bitmap != null && !bitmap.isRecycled()) {
// bitmap.recycle();//这个方法注释掉是因为之后还要用这个,如果不注释这一句,资源被释放,后面调用不了这个图片,就会报错
// }
return newBitmap;
}
}
显示图片到Activity并识别
- 布局文件(约束布局就两个控件)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".baiduapi">
<ImageView
android:id="@+id/main_picture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/baidu"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/baidu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="200dp"
android:text="结果"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 逻辑文件(缺失的类在后文补充)
public class baiduapi extends AppCompatActivity {
private AipImageClassify aipImageClassify;
String result;
public static final String APP_ID = "23047597";//申请的百度智能云应用,后面再演示
public static final String API_KEY = "QqZaOp0TqAfwKiuljhSGN8NC";
public static final String SECRET_KEY = "dEv0GyimsAh4MTy41bcxn7sdSLw7hS1q";
ImageView imageView;
String url = "https://aip.baidubce.com/rest/2.0/image-classify/v2/advanced_general";//todo
// private Bitmap bitmap;
Handler mHandler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
//重写handleMessage方法
super.handleMessage(msg);
result1.setText(result);
}
};
TextView result1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_baiduapi);
result1 = findViewById(R.id.baidu);
final AipImageClassify client = new AipImageClassify(APP_ID, API_KEY, SECRET_KEY);
imageView = findViewById(R.id.main_picture);
final Intent intent = getIntent();
if (intent != null) {
byte[] bis = intent.getByteArrayExtra("bitmap");
Bitmap bitmap = BitmapFactory.decodeByteArray(bis, 0, bis.length);
imageView.setImageBitmap(bitmap);
}
Thread thread = new Thread() {
@Override
public void run() {
super.run();
try {
String filePath = intent.getStringExtra("filepath");
System.out.println("图片路径"+filePath);
byte[] imgData = FileUtil.readFileByBytes(filePath);
String imgStr = Base64Util.encode(imgData);
String imgParam = URLEncoder.encode(imgStr, "UTF-8");
String param = "image=" + imgParam;
String Token = getAuth(API_KEY, SECRET_KEY);
// 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
String accessToken = Token;//"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id="+API_KEY+"&client_secret="+SECRET_KEY;
result = HttpUtil.post(url, accessToken, param);
Log.i("result结果", result);
mHandler.sendEmptyMessage(1);
} catch (Exception e) {
e.printStackTrace();
}
}
};
thread.start();
//result1.setText("结果:"+result);
}
}
代码运行一遍就OK了
创建百度智能云平台
这一步是放在最开始做的,但是后面有几个坑点写一下所以就没有放在开始写
进入官方网站后注册一个账号或者登陆
然后点击立即使用进入管理中心后创建应用
填好相关信息后进入这个界面
这里有三个值需要在代码中应用
APP_ID、API_KEY、SECRET_KEY
下一步下载官方提供的SDK
我用的Java,所以…
查看官方文档
查看API接口文档(在SDK文档的同一个界面)
这个token可以通过文档提供的连接进去查看,我也是被误导了才以为token可以直接自己拼接…
坑点:没注意提供的代码示例
这里他提供了一些类也就是我之前没给出的类…
* 重要提示代码中所需工具类
* FileUtil,Base64Util,HttpUtil,GsonUtils请从
* https://ai.baidu.com/file/658A35ABAB2D404FBF903F64D47C1F72
* https://ai.baidu.com/file/C8D81F3301E24D2892968F09AE1AD6E2
* https://ai.baidu.com/file/544D677F5D4E4F17B4122FBD60DB82B3
* https://ai.baidu.com/file/470B3ACCA3FE43788B5A963BF0B625F3
* 下载
然后就是他的这个access_token值的获取要通过他提供的类来获取不能直接拼接…
所以要加一个类去生成token
/**
* 获取token类
*/
class AuthService {
/**
* 获取权限token
* @return 返回示例:
* {
* "access_token": "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567",
* "expires_in": 2592000
* }
*/
public static String getAuth() {
// 官网获取的 API Key 更新为你注册的
String clientId = "g8KagvuAgnhjm55m0Ls01t8u";
// 官网获取的 Secret Key 更新为你注册的
String clientSecret = "ru81GzTQADSSzpgeWDnW04aAxbSdaXnI";
return getAuth(clientId, clientSecret);
}
/**
* 获取API访问token
* 该token有一定的有效期,需要自行管理,当失效时需重新获取.
* @param ak - 百度云官网获取的 API Key
* @param sk - 百度云官网获取的 Securet Key
* @return assess_token 示例:
* "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567"
*/
public static String getAuth(String ak, String sk) {
// 获取token地址
String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
String getAccessTokenUrl = authHost
// 1. grant_type为固定参数
+ "grant_type=client_credentials"
// 2. 官网获取的 API Key
+ "&client_id=" + ak
// 3. 官网获取的 Secret Key
+ "&client_secret=" + sk;
try {
URL realUrl = new URL(getAccessTokenUrl);
// 打开和URL之间的连接
HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
connection.setRequestMethod("POST");
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段
for (String key : map.keySet()) {
System.err.println(key + "--->" + map.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String result = "";
String line;
while ((line = in.readLine()) != null) {
result += line;
}
/**
* 返回结果示例
*/
System.err.println("result:" + result);
JSONObject jsonObject = new JSONObject(result);
String access_token = jsonObject.getString("access_token");
return access_token;
} catch (Exception e) {
System.err.printf("获取token失败!");
e.printStackTrace(System.err);
}
return null;
}
}
其他的看一下官方文档问题不大…注意提供的示例代码
应该没什么类缺失了,不确定,有的话…@我…