Android--智能图像识别(基于百度智能云)

效果图

在这里插入图片描述

引言

效果图是在模拟器上运行的,调用相机的时候是调用的电脑上的相机,所以我选择的是实时截取屏幕,随便选了一个图片,然后就是关于这个智能识别,毕竟是调用的百度的API,所以…注意保密性…百度后台应该是能看到拍的图片的。结果返回的是一个Json字符串我就没有去处理它了,然后朋友帮我处理了一下…
在这里插入图片描述
处理后的效果
在这里插入图片描述

步骤

  1. 首先是调用相机
  2. 然后将得到的图片显示在Activity上
  3. 再然后就是调用百度智能云的API处理图片,当然第三步和第二步是同时处理的
  4. 最后就是想结果显示到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,所以…
在这里插入图片描述

查看官方文档

查看JavaSDK技术文档
在这里插入图片描述

查看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;
        }

}

其他的看一下官方文档问题不大…注意提供的示例代码
应该没什么类缺失了,不确定,有的话…@我…

猜你喜欢

转载自blog.csdn.net/qq_43337254/article/details/113571438