昆蟲識別

基本步驟:打開相機(jī)和相冊獲取一張照片灶搜,轉(zhuǎn)為base64,異步上傳到百度ai平臺共啃,拿到返回結(jié)果占调。
總結(jié)知識點:

  • 安卓系統(tǒng)相機(jī)和相冊的使用
  • 百度開放平臺識別接口的使用
  • okhttp
  • 使用handler異步上傳圖片
  • 使用bundle線程間傳遞數(shù)據(jù)
  • 識別結(jié)果JSON的解析
  • dialog的使用
    由于幾個比較簡單就不詳述,重點講下相機(jī)移剪,百度,handler薪者,bundle

通過相機(jī)和相冊獲取照片

權(quán)限注冊:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  1. 彈出一個dialog詢問是相機(jī)還是相冊:
String items [] = {"相機(jī)","相冊"};// 用于作為dialog的選項
                AlertDialog dialog = new AlertDialog.Builder(getContext())
                        .setTitle("請選擇圖片獲取途徑")
                        .setItems(items, new DialogInterface.OnClickListener() {
                            @Override //  dialog的點擊事件 
                            public void onClick(DialogInterface dialogInterface, int i) {
                                switch (i){
                                    case 0:
                                        openCamera();
                                        break;
                                    case 1:
                                        openAlbum();
                                        break;
                                }
                            }
                        }).create();
                dialog.show();

2.詳解openCamera()

public static final int PHOTO_REQUEST_CAMERA = 1;// 拍照
 private Intent intent_camera;
public static File tempFile;
 private void openCamera() {
        //獲取系統(tǒng)版本
        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        // 創(chuàng)建隱式調(diào)用相機(jī)的intent
         intent_camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 判斷存儲卡是否可以用纵苛,可用進(jìn)行存儲
        if (hasSdcard()) {
            // 取得當(dāng)前時間命名文件
            SimpleDateFormat timeStampFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
            String filename = timeStampFormat.format(new Date());
            //  創(chuàng)建存放照片的文件
            tempFile = new File(Environment.getExternalStorageDirectory(), filename + ".jpg");
            if (currentapiVersion < 24) {
                // 從文件中創(chuàng)建uri
                imageUri = Uri.fromFile(tempFile);
                //將當(dāng)前uri放到系統(tǒng)指定的位置,(系統(tǒng)會自動將照片放到這個位置的uri中)
                intent_camera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                startActivityForResult(intent_camera, PHOTO_REQUEST_CAMERA);
            } else {
                //兼容android7.0 使用共享文件的形式
                contentValues = new ContentValues(1);
                contentValues.put(MediaStore.Images.Media.DATA, tempFile.getAbsolutePath());
                imageUri = getActivity().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
                //檢查是否有存儲權(quán)限言津,以免崩潰
                if ( ContextCompat.checkSelfPermission(getActivity(),  
                        Manifest.permission.WRITE_EXTERNAL_STORAGE )
                        != PackageManager.PERMISSION_GRANTED ||
                        ContextCompat.checkSelfPermission(getActivity(),
                        Manifest.permission.CAMERA)
                        !=PackageManager.PERMISSION_GRANTED ) {
                    //申請WRITE_EXTERNAL_STORAGE權(quán)限
                    ActivityCompat.requestPermissions(getActivity(),
                            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA},
                            REQUEST_PERMISSSION_CAMERA);
                }else {     // 有權(quán)限
                    intent_camera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                    // 開啟一個帶有返回值的Activity攻人,請求碼為PHOTO_REQUEST_CMERA
                    startActivityForResult(intent, PHOTO_REQUEST_CAMERA);
                }

            }
        }
    }

首先創(chuàng)建一個用于啟動系統(tǒng)相機(jī)的intent和一個用于存放照片的file,接著獲取當(dāng)前的手機(jī)系統(tǒng)版本悬槽,(由于安卓7.0前后uri的獲取方式有了變化怀吻,7.0以后使用共享文件的形式獲取uri),如果有物理內(nèi)存初婆,獲取當(dāng)前的時間用于命名相冊file蓬坡,相對路徑構(gòu)造file猿棉,如果是7.0以下版本,直接用Uri.fromFile(tempFile)獲得uri屑咳,放到系統(tǒng)指定的位置(MediaStore.EXTRA_OUTPUT)萨赁,系統(tǒng)會將照片復(fù)制到這個uri上(我也不知道是不是復(fù)制啊,反正就是在拍照前指定MediaStore.EXTRA_OUTPUT一個uri兆龙,拍照后這個uri就是照片)杖爽,然后startActivityForResult(intent_camera, PHOTO_REQUEST_CAMERA);在回調(diào)中拿到uri(照片),7.0以上除了uri獲取不一樣其他一模一樣紫皇,就是那三行慰安,核心使用contentValues,思路和7.0以下一樣聪铺。

// 判斷是否含有存儲空間
private boolean hasSdcard() {
        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    }

3.詳解openAlbum()

public static final int PHOTO_REQUEST_ALBUM = 0; // 相冊
private Intent intent_album;
    //  打開相冊
    private void openAlbum() {
         intent_album = new Intent(Intent.ACTION_PICK);
         intent_album.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
         startActivityForResult(intent_album,PHOTO_REQUEST_ALBUM);
    }

先創(chuàng)建一個隱式打開相冊的intent泻帮,(其實這個是我百度過來的,沒看懂计寇,缺個解析锣杂,19.4.6)
4.處理回調(diào)

 // 處理活動回調(diào)
    Bitmap bitmap;
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case PHOTO_REQUEST_CAREMA :  // 處理相機(jī)的回調(diào)
                // 自定義handler接收子線程傳回來的result
                myHandler = new MyHandler();
                if (resultCode == RESULT_OK){
                    // 處理照片
                    try {
                        // 照片的bitmap
                        bitmap = BitmapFactory.decodeStream(getActivity().getContentResolver().openInputStream(imageUri));
                        imageView.setImageBitmap(bitmap);
                        // 識別
                        identify(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case PHOTO_REQUEST_ALBUM:  // 相冊的回調(diào)
                myHandler = new MyHandler();
                try {
                    Uri selectedImage = data.getData();
                    String[] filePathColumns = {MediaStore.Images.Media.DATA};
                    Cursor c = getActivity().getContentResolver().query(selectedImage, filePathColumns, null, null, null);
                    c.moveToFirst();
                    int columnIndex = c.getColumnIndex(filePathColumns[0]);
                    String imagePath = c.getString(columnIndex);
                    Bitmap bitmap = BitmapFactory.decodeFile(imaePath);
                    imageView.setImageBitmap(bitmap);
                    identify(bitmap);
                    c.close();
                }catch (NullPointerException e){
                    Log.d("E",e.getMessage());
                }
                break;

        }
    }
  • 先說相機(jī)回調(diào):通過ContentValues拿到上文的uri,在變變變成bitmap番宁,傳入 identify(bitmap)方法中去識別元莫,在下文百度ai中詳細(xì)講這個方法。
  • 再談相冊回調(diào):都說是百度的了蝶押,前面都沒看懂后面怎么可能會踱蠢,先欠著,大致是一頓操作過后拿到bitmap傳入identify()中去棋电,和相機(jī)的回調(diào)收尾一樣茎截。

下面詳細(xì)講解identify()

百度ai開放平臺

我們先貼上identify()代碼

// 識別
    String access_token ;
    String param;
    private void identify(Bitmap bitmap) {
        // 壓縮到0.1 拿到base64編碼,合成param
        ByteArrayOutputStream bStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 10, bStream);
        byte[] bytes = bStream.toByteArray();
        String base64 = Base64Util.encode(bytes);
        try {
            String image_param = URLEncoder.encode(base64,"UTF-8");
             param = "image=" + image_param + "&top_num=" + 6;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 獲得access token 24.1b561ba2365e2ab484e69e953f09a48c.2592000.1555152863.282335-15652740
        String request_access_token = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=aHWDOH5MERuoXkwUIOtbwU7j&client_secret=2QbMhquS9aRY2Gd4hpjlU8SXpIGfC3zm&";
        OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request.Builder()
                .get()
                .url(request_access_token)
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                ToastUtil.toast(getContext(),"access__token請求失敗"+e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response)  {
                // 解析拿到acces_stoken
                String jsonString = null;
                try {
                    jsonString = response.body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Log.d("tag",jsonString);
                Gson gson = new Gson();
                JsonAccess jsonAccess  = gson.fromJson(jsonString ,JsonAccess.class);
                 access_token = jsonAccess.access_token;
            }
        });

        // 開啟子線程上傳base64
        new Thread(new Runnable() {
            String url = "https://aip.baidubce.com/rest/2.0/image-classify/v1/animal";
                @Override
                public void run() {
                    try {
                        String result = HttpUtil.post(url,"24.1b561ba2365e2ab484e69e953f09a48c.2592000.1555152863.282335-15652740",param);
                            // bundle用于傳遞線程數(shù)據(jù)
                        Bundle bundle = new Bundle();
                        bundle.putString("result",result);
                        // 通過message發(fā)送到主線程
                        Message message = new Message();
                        message.setData(bundle);
                        message.what = 1;
                        myHandler.sendMessage(message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    }

使用的是百度ai的動物識別接口,官方文檔地址:https://ai.baidu.com/docs#/ImageClassify-API/top
在看之前赶盔,可以先看一下我的觀后感:

  1. 先注冊賬號企锌,創(chuàng)建應(yīng)用,拿到Api key和Secret key于未。
  2. 然后看文檔

我們傳進(jìn)來的是bitmap撕攒,百度需要的base64編碼,去掉編碼頭后再進(jìn)行urlencode烘浦,所以第一步先將我們的bitmap轉(zhuǎn)變?yōu)閎ase64抖坪,先創(chuàng)建ByteArrayOutputStream流,將bitmap壓縮到bStream流里(通過compress方法闷叉,參數(shù)1為壓縮類型擦俐,參數(shù)2為壓縮比例10代表原圖片的0.1,參數(shù)3為bitmap壓縮的目標(biāo)流)握侧,將流轉(zhuǎn)為byte[]蚯瞧,通過百度官方提供的Base64轉(zhuǎn)換類的encode方法將byte轉(zhuǎn)為base64嘿期,base64轉(zhuǎn)換工具下載地址:https://ai.baidu.com/file/C8D81F3301E24D2892968F09AE1AD6E2
將得到的base64指定為UTF-8,然和合成post請求的param,指定參數(shù)image和top_num状知,下面一段代碼是按照官方提示獲得accessToken秽五,沒什么好說的,(注意我在代碼中拿到的accesstoken是在子線程中的饥悴,我在下文使用的時候直接傳的值坦喘,沒有傳變量,如果傳變量需要用到bundle和handler)我們現(xiàn)在所有的操作都是在主線程中西设,無法進(jìn)行耗時操作瓣铣,發(fā)送圖片為耗時操作,所以我們開啟一個線程去post我們的圖片給百度贷揽,請求返回結(jié)果棠笑,在子線程中,我們準(zhǔn)備好請求的uri地址禽绪,accessToken和存放圖片的param蓖救,通過百度的請求工具獲取result,請求工具地址:https://ai.baidu.com/file/544D677F5D4E4F17B4122FBD60DB82B3
現(xiàn)在我們已經(jīng)在子線程中拿到識別結(jié)果了印屁,下面需要將他們傳到主線程循捺,解析,展示雄人。

handler與bundle線程間傳遞數(shù)據(jù)

先定義MyHandler類用于處理子線程發(fā)送來的message

 class MyHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 取出bundle得到結(jié)果
            Bundle bundle = msg.getData();
            String result = bundle.getString("result");
            String s = "";
            try {
                JSONObject jsonObject = new JSONObject(result);
                JSONArray jsonArray = jsonObject.getJSONArray("result");
                for (int i = 0; i <1;i++ ){
                    JSONObject obj = (JSONObject) jsonArray.get(i);
                    s = s+obj.getString("name")+"\n";
                }
                textView.setText(s);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            textView.setVisibility(View.VISIBLE);
            intent_show = new Intent(getContext(),ShowActivity.class);
            intent_show.putExtra("result",result);
          //  getContext().startActivity(intent_show);
        }

    }

子線程發(fā)送數(shù)據(jù)(上文identify()中的子線程)

 new Thread(new Runnable() {
            String url = "https://aip.baidubce.com/rest/2.0/image-classify/v1/animal";
                @Override
                public void run() {
                    try {
                        String result = HttpUtil.post(url,"24.1b561ba2365e2ab484e69e953f09a48c.2592000.1555152863.282335-15652740",param);
                            // bundle用于傳遞線程數(shù)據(jù)
                        Bundle bundle = new Bundle();
                        bundle.putString("result",result);
                        // 通過message發(fā)送到主線程
                        Message message = new Message();
                        message.setData(bundle);
                        message.what = 1;
                        myHandler.sendMessage(message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();

在子線程拿到了返回的result从橘,通過bundle和myHandler傳遞到主線程,bundle用于存放數(shù)據(jù)础钠,handler用于發(fā)送數(shù)據(jù)(需要借助handler的message類)恰力,在自定義的MyHandler類中我們handleMessage,取出bundle旗吁,取出result踩萎,解析JSON,然后展示給用戶阵漏,這些handleMessage的邏輯是寫在類里面的驻民,什么時候執(zhí)行啊,在我們的MyHandler被實例話的時候會執(zhí)行履怯,仔細(xì)去看下上文的onActivityResult在處理相機(jī)和相冊的兩個回調(diào)中,我們是不是初始化了我們的myHandler裆泳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叹洲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子工禾,更是在濱河造成了極大的恐慌运提,老刑警劉巖蝗柔,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異民泵,居然都是意外死亡癣丧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門栈妆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胁编,“玉大人,你說我怎么就攤上這事鳞尔℃页龋” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵寥假,是天一觀的道長市框。 經(jīng)常有香客問我,道長糕韧,這世上最難降的妖魔是什么枫振? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮萤彩,結(jié)果婚禮上粪滤,老公的妹妹穿的比我還像新娘。我一直安慰自己乒疏,他們只是感情好额衙,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布怕吴。 她就那樣靜靜地躺著,像睡著了一般转绷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上议经,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天斧账,我揣著相機(jī)與錄音,去河邊找鬼咧织。 笑死,一個胖子當(dāng)著我的面吹牛习绢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼闪萄,長吁一口氣:“原來是場噩夢啊……” “哼梧却!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起放航,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤圆裕,失蹤者是張志新(化名)和其女友劉穎广鳍,沒想到半個月后葫辐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蛋叼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年剂陡,在試婚紗的時候發(fā)現(xiàn)自己被綠了狈涮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸭栖。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖松却,靈堂內(nèi)的尸體忽然破棺而出溅话,到底是詐尸還是另有隱情晓锻,我是刑警寧澤飞几,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站躁锁,受9級特大地震影響卵史,放射性物質(zhì)發(fā)生泄漏战转。R本人自食惡果不足惜以躯,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望色鸳。 院中可真熱鬧,春花似錦命雀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狐血。三九已至易核,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牡直,已是汗流浹背佛点。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留隘庄,地道東北人髓抑。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像胳喷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子厌蔽,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • ¥開啟¥ 【iAPP實現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程奴饮,因...
    小菜c閱讀 6,409評論 0 17
  • (1)鬧鐘 創(chuàng)建鬧鐘(ACTION_SET_ALARM)示例Intent: 注:為了調(diào)用ACTION_SET_AL...
    sunnygarden閱讀 1,628評論 0 10
  • //gradle 下載慢 //可以直接下載gradle之后放在對應(yīng)的目錄里//或者修改 根目錄下的文件bul...
    zeromemcpy閱讀 885評論 0 0
  • 最近用到從系統(tǒng)圖庫和相機(jī)獲取圖片并裁剪當(dāng)頭像戴卜,根據(jù)郭霖大神的第一行代碼調(diào)用相機(jī)和圖冊,來進(jìn)行擴(kuò)展和總結(jié)投剥。 1、獲取...
    axiaochao閱讀 2,566評論 0 5
  • 愛情需得小心翼翼吃警,有時候我們以為的一個不經(jīng)意的小玩笑可能就是別人心里的一道疤糕篇。
    夢里何思?xì)w閱讀 138評論 0 0