Android人臉識別app——基於Face++,MVP+Retofit+RxJava+Dagger高度解耦

2021-02-24 程式設計師專欄

作者:reggie1996
連結:https://www.jianshu.com/p/920b9c525d2f

前言

最近公司項目比較空,花了點時間寫了個人臉識別的app,可以查看你的性別、年齡、顏值、情緒等信息,利用的是 Face++ 的人臉識別API。本項目採用了 MVP 的架構,使用了 Retrofit、RxJava、Dagger、EventBus 等框架進行開發和解耦,利用 MaterialDesign 進行UI上的布局設計。

主要的功能就是拍照,然後將照片傳至 Face++ 伺服器,進行人臉識別,獲取返回的信息,對信息進行處理。將人臉在照片上標出,並將信息展示出來。

話不多說,先來看一下 app 的效果(吳彥祖還是帥啊,哈哈)。

面部識別主界面面部識別詳情界面多人臉識別

項目我已經放在 github 上,clone 下來即可編譯運行。github 地址: reggie1996 - FaceDetect 。下面文章主要介紹的是本項目的開發過程和碰到的坑。

過程

項目的整個流程很簡單無非就是三步,拍照片,傳照片獲取數據,然後對數據進行處理展示。

拍照獲取照片

拍照需要獲取系統權限,我封裝了一個方法,來判斷App是否有拍照相關的權限,如果沒有就去動態請求權限,並返回 false,如果有就返回 true。

public static boolean checkAndRequestPermission(Context context, int requestCode) {
        if (context.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
                || context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
                || context.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ((Activity) context).requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, requestCode);
            return false;
        }else {
            return true;
        }
    }

獲取到拍照權限後就可以拍照了,但是拍照得到的照片我們需要通過 FileProvider 獲取。FileProvider 相關的內容就不作介紹了,Android 7.0 之後都得用這個。

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.chaochaowu.facedetect.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

拍照之後從文件中讀取照片,我們可以得到一個 BitMap 對象。這裡就有一個很大的坑,如果手機是三星的話,照片從文件裡讀出來,最後得到的照片會被旋轉 90°!!!,這個賊坑啊,調了我好久,以為是自己手機的故障,後來網上查了一下,也請教了一下前輩,原來三星的手機都有這個問題,所以說我們要對文件中取出來的照片進行一下處理。


    public static int getBitmapDegree(String path) {
        int degree = 0;
        try {
            
            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;
                default:
                    degree = 0;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    
    public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
        Bitmap returnBm = null;

        
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        try {
            
            returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
        } catch (OutOfMemoryError | Exception e) {
            e.printStackTrace();
        }
        if (returnBm == null) {
            returnBm = bm;
        }
        if (bm != returnBm) {
            bm.recycle();
        }
        return returnBm;
    }

封裝了兩個方法,依次調用可以解決三星手機照片的問題。兩個方法主要的工作就是,得到取出來的照片被旋轉的角度,然後再將角度旋轉回去,就可以得到原來的照片。因為並不是所有的手機在獲取照片時,照片都會被旋轉,所以得先判斷一下照片有沒有被旋轉,再決定是否需要將它旋轉調整。

行,這樣最後就獲得到了正確的 BitMap 照片,可以進行下一步了。

傳照片獲取數據

傳照片獲取數據,主要是運用了 Retrofit 和 RxJava 的封裝。請求的參數可以參考 Face++ 的官方文檔。


public interface FaceppService {

    
    @POST("facepp/v3/detect")
    @FormUrlEncoded
    Observable<FaceppBean> getFaceInfo(@Field("api_key") String apikey,
                                       @Field("api_secret") String apiSecret,
                                       @Field("image_base64") String imageBase64,
                                       @Field("return_landmark") int returnLandmark,
                                       @Field("return_attributes") String returnAttributes);

}

照片需要進行 base64 轉碼後上傳至伺服器,封裝了一個照片base64轉碼方法。

 public static String base64(Bitmap bitmap){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] bytes = baos.toByteArray();
        return Base64.encodeToString(bytes, Base64.DEFAULT);
    }

處理完成之後就可以進行網絡請求獲取數據。

@Override
    public void getDetectResultFromServer(final Bitmap photo) {
        String s = Utils.base64(photo);
        faceppService.getFaceInfo(BuildConfig.API_KEY, BuildConfig.API_SECRET, s, 1, "gender,age,smiling,emotion,beauty")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<FaceppBean>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        mView.showProgress();
                    }

                    @Override
                    public void onNext(FaceppBean faceppBean) {
                        handleDetectResult(photo,faceppBean);
                    }

                    @Override
                    public void onError(Throwable e) {
                        mView.hideProgress();
                    }

                    @Override
                    public void onComplete() {
                        mView.hideProgress();
                    }
                });
    }

Face++ 伺服器會對我們上傳的照片進行處理,分析照片中的人臉信息,並以 json 形式返回,返回的數據將被放入我們定義的bean類中。


public class FaceppBean {
    

    private String image_id;
    private String request_id;
    private int time_used;
    private List<FacesBean> faces;
    ...顯示部分內容

bean 類中有人臉識別得到的 性別、年齡、顏值、情緒等信息,還有每張人臉在照片中的坐標位置。接下來的工作就是對這些數據進行處理。

獲取信息後的數據處理

數據的處理主要就兩件事,一個是將數據以文字的形式展現,這個很簡單,就不介紹了,還有一個就是將人臉在照片中標示出來,這個需要對 BitMap 進行處理,利用數據中人臉在照片中的坐標位置,我們用方框將人臉標識出來。

private Bitmap markFacesInThePhoto(Bitmap bitmap, List<FaceppBean.FacesBean> faces) {
        Bitmap tempBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        Canvas canvas = new Canvas(tempBitmap);
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);

        for (FaceppBean.FacesBean face : faces) {
            FaceppBean.FacesBean.FaceRectangleBean faceRectangle = face.getFace_rectangle();
            int top = faceRectangle.getTop();
            int left = faceRectangle.getLeft();
            int height = faceRectangle.getHeight();
            int width = faceRectangle.getWidth();
            canvas.drawRect(left, top, left + width, top + height, paint);
        }
        return tempBitmap;
    }

封裝了一個方法,運用 Canvas 在照片上進行繪製,因為照片中的人臉可能不止一個,所以用for循環遍歷。獲取人臉在照片中的坐標,利用人臉左上角的坐標以及人臉的寬高,在照片中繪製一個方框將人臉標出。

剩餘信息我這邊採用 RecyclerView 來展示。左右滑動可以查看每張人臉的信息。RecyclerView 的 item 上展示的是簡要信息,可以點擊 item 進入詳情頁面查看面部識別的詳細信息。RecyclerView 以及詳情界面的實現就不作介紹了,很基本的操作。我這邊也就只使用了 SharedElement 讓界面切換看起來舒服一點。具體的實現可以看 github 上的代碼。

其他就沒什麼操作了,還可以看一下我的項目架構。由於用了各種框架進行解耦,所以代碼文件數量變多了,但是單個文件中的代碼會變少一點,清晰易讀一點,這也是解耦的目的,也方便之後的維護。

具體實現的細節可以看 github 上面的代碼~

最後

寫完這個APP後,我一直在思考一個問題,APP給吳彥祖的顏值打分80多,那100分的顏值會是怎樣?

感興趣的朋友可以把代碼下載下來玩一下,測一下自己或者是朋友的顏值,嘿嘿。github 地址:https://github.com/reggie1996/FaceDetect

【點擊成為Java大神】

相關焦點

  • dlib應用之人臉識別
    一win10環境安裝使用dlib庫進行人臉識別,首先要安裝dlib。dlib庫的安裝有依賴庫,包括VS和Cmake。圖3 imageio安裝二 人臉識別1、dlib庫採用68點位置標誌人臉重要部位,比如18-22點標誌右眉毛,51-68標誌嘴巴。
  • 開源人臉識別seetaface入門教程(一)
    簡述 seetaface由中科院計算所山世光研究員帶領的人臉識別研究組研發。代碼基於C++實現,不依賴第三方庫。 注意:本文章不涉及代碼邏輯和原理,只是教大家如何使用seetaface做人臉識別。 引擎 FaceDetection 人臉識別模塊,用於識別出照片中的人臉,染回每個人臉的坐標和人臉總數。
  • Android簡易天氣App
    在app的build.gradle中添加要使用到的依賴。直接都添加好了,已經將後續所有用到的都添加了好了。順便添加compileOptions和dataBinding兩段代碼。android {    ...
  • 隱私終結者:火遍美國的人臉識別App,用一張照片「順藤摸瓜」
    但是很多人也在擔心一個敵託邦的未來,所以在歐美對人臉識別技術的應用非常牴觸。美國一個爬取了30億張公開照片的新興人臉識別app正在贏得美國警方的青睞,但也遭到了新的擔憂。美國聯邦和各州的執法人員說,儘管他們對Clearview的工作原理及其背後知識知之甚少,但他們已經用這個app來幫助破解入店行竊、身份盜竊、信用卡欺詐、謀殺及對兒童性虐等案件。迄今為止,由於會對隱私構成強烈侵犯,可基於人臉輕鬆識別每個人的技術一直是個禁忌。
  • 詳解蘇寧門店的人臉識別技術
    蘇寧擁有18萬員工以及幾千家門店,2018年還會再增加5000家門店,通過人臉識別技術在蘇寧內部以及線上線下的應用,蘇寧的人臉識別算法能力還會進一步的迭代提升。雖然人臉識別發展到今天已經相對成熟,機器對於人臉的識別能力已經遠超人類。但是在商業應用中這種基於二維圖像的人臉識別還是受到很多限制,比如人皮面具的活體攻擊問題,線下各種場景都有複雜的光照情況,比如逆光、光線較弱、室外。
  • 愛奇藝打造的人工智慧——卡通人臉識別算法iCartoonFace
    人臉識別,連動漫卡通領域都要涉足了,如果說三維的卡通因為有「塊面和光影漸變」造成的較明顯的臉部結構,可以識別出來,那我信,可如果二維的純平面的卡通也能被識別出來——不禁感慨——不是我不明白,這世界變化快。
  • 人臉識別技術解析
    更有甚者,將參與者的人臉換臉到了主持人上,然後順利的通過了轉頭、搖頭、微笑等的一系列的人臉識別活體驗證。這一實驗引起了人們對人臉識別技術的擔憂,人臉識別到底靠譜麼?憑照片就可以製作出可欺騙人臉識別技術的模型,以後還能不能愉快地在社交平臺上曬自拍了?接下來就讓我們來聊一聊人臉識別技術。
  • iPhone Face ID與其他人臉識別技術有什麼不一樣?
    例如支付寶的人臉識別登錄與iPhoneX的Face ID技術一樣嗎?iPhoneX和其他手機的人臉識別有什麼不同?今天微模式為你一一揭曉。目前市面上的人臉識別手機原理上與蘋果的iPhone X刷臉解鎖一樣,主要差異在於人臉的精確度上,2D和3D稍有差別,在這一方面,Android手機今年也會陸續實現iPhoneX的3D解鎖了。
  • 人臉識別中的人臉表情識別技術
    隨著人臉識別技術的發展,如今在識別中應用到對人臉的表情進行識別,可以應用在人機互動、安全、機器人製造、醫療、通信和汽車領域等。那麼,暢視智能來告訴人臉識別技術的人臉表情識別要如何進行?圖像獲取:通過人臉識別攝像頭等圖像捕捉工具獲取靜態人臉圖像或動態圖像序列。
  • 「人臉識別」解鎖,到底安全不安全
    (圖片來源:央視網)人臉識別究竟是如何識別人臉的?要探究這個問題,首先就要明白,人臉識別的原理是啥。人臉識別之所以不會受到這些因素幹擾,是因為識別的關鍵是通過對局部「細節」信息的充分收集,進而判斷出「整體」人臉的身份特徵。
  • 硬核科普:一文看懂人臉識別技術流程
    好了,基於以上對計算機視覺模式識別的討論,我們就可以給出人臉識別系統的主要功能模塊了:可能有小夥伴覺得上面這個舉出功能模塊太簡單了,所以我們再精確一些,給出下面的邏輯架構圖,相信不難理解:人臉識別的主流方法在上面一部分,我們主要介紹了人臉識別的基本邏輯流程,其實人臉識別的基本思想是比較類似的,都是要將圖像中的特徵提取出來,轉換到一個合適的子空間裡
  • 拿下抖音小姐姐,我寫了個口紅色號識別器!
    既然可以識別番茄的顏色,那麼,可以識別人像中的口紅色號嗎?接下來,我們需要做的是輸入一張人像圖片,可以自動識別其中的嘴唇區域,並提取出嘴唇區域中的一部分做為顏色提取的源圖像。這裡就要用到 CV 的人臉識別了,還好 Dlib 庫又幫助我們減輕一大部分的工作量。
  • 騰訊優圖亮相PRCV2020,展示人臉分析最新研究與落地成果
    在今年的ECCV中, 騰訊優圖共有8篇論文被收錄,涵蓋目標跟蹤、行人重識別、人臉識別、人體姿態估計、動作識別、物體檢測等熱門及前沿領域,再次展示了騰訊在計算機視覺領域的科研及創新實力。此次PRCV上,優圖研究員邰穎對其中五篇論文研究成果進行了重點分享。
  • 小區人臉識別應用實測:有App明文上傳人臉照片及房產證
    從中國「人臉識別第一案」一審宣判到售樓處暗中使用人臉識別,從北京推出人臉識別垃圾桶到東莞公廁停用人臉識別供紙機,過去一年中,人臉識別應用正在經歷越來越嚴格的公眾審視。12月22日,由南都個人信息保護研究中心主辦的「2020啄木鳥數據治理論壇」在北京舉行。
  • Android P 劉海屏適配全攻略
    如下所示:<meta-data android:name="android.notch_support" android:value="true"/>實際上還有一種代碼實現的方式,不過代碼比較多,這裡就不貼了,有興趣的話可以在文末的連結中點進去看看。
  • 戴口罩手機Face ID人臉識別失效 網友懷念指紋識別
    但以往看一眼就能解鎖、支付的Face ID人臉識別功能成了「廢柴」,每次都要脫下口罩才能識別給手機使用造成了諸多不便,也帶來了一定安全風險。不少蘋果手機用戶開始懷念以前的Touch ID指紋識別。有網友尷尬表示:「天天戴口罩不方便人臉識別換了8P,結果越獄的8P玩不了和平精英,默認為外掛。 ​」
  • 「94歲老人被抱起做人臉識別」「為躲人臉識別戴頭盔看房」?
    最近,隨著「94歲老人被抱起做人臉識別」「為躲人臉識別戴頭盔看房」等事件的發生,讓人臉識別頻上熱搜,技術濫用問題引發廣泛關注。隨之,一些傳言也「悄然出動」:人臉識別系統任何機構想裝就能裝?戴口罩就無法進行人臉識別?在手機銀行上設置人臉識別功能,會增加銀行卡被盜刷的風險……這些說法到底是真是假?
  • 中科視拓聯合創始人講解:姿態魯棒人臉識別關鍵技術及其在智慧園區中的應用【附完整PPT下載】
    在CVPR 2018上,作者提出CosFace的方法,Coseface可以看成再Normface基礎上通過引入超參數m,引入Margin,m可以控制懲罰的力度。cos(θ)-m 比 cos(θ) 更小,所以損失函數值比 Normface 裡面的值更大,進而可以學習到更好的人臉識別特徵(類間拉的更開,類內更緊湊)Arcface:跟Cosface不一樣的是Arcface直接在角度空間最大化分類界限。
  • 人臉識別向保險行業滲透 未來保險會有哪些新玩法?
    | 保險業引入「人臉識別」,成果已初見端倪  基於近幾年大數據和算法的快速發展,國內保險公司紛紛在各自業務中引入人臉識別技術,投保、理賠、客服、保全等傳統模式上都迎來了創新,主要包含以下幾方面:  線上投保書籤名及保費支付時人臉識別身份核驗  保險電子回執單籤收時人臉識別身份核驗  投保人空中保全業務辦理時人臉識別身份核驗
  • 教程| 如何構建自定義人臉識別數據集
    然而,對於大多數人來說,我們希望識別出的人臉往往不包含在任何現有數據集中,例如:我們自己的、朋友的、家人或者同事的人臉圖像。為了完成這個任務,我們需要收集我們想要識別的人臉樣本,並且以某種方式量化它們。這個過程通常被稱為「人臉識別註冊」(facial recognition enrollment)。