溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

android基于虹軟的人臉識(shí)別+測(cè)溫+道閘項(xiàng)目的實(shí)現(xiàn)方法

發(fā)布時(shí)間:2021-07-09 17:35:30 來(lái)源:億速云 閱讀:445 作者:chen 欄目:編程語(yǔ)言

本篇內(nèi)容主要講解“android基于虹軟的人臉識(shí)別+測(cè)溫+道閘項(xiàng)目的實(shí)現(xiàn)方法”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“android基于虹軟的人臉識(shí)別+測(cè)溫+道閘項(xiàng)目的實(shí)現(xiàn)方法”吧!

軟硬件環(huán)境

平臺(tái)為Android平臺(tái),采用kotlin+java混編 虹軟SDK版本為最新的4.0可以戴口罩識(shí)別 終端攝像頭采用雙目攝像頭模組IR活體識(shí)別 掃碼頭、測(cè)溫頭、身份證讀卡器皆為本公司設(shè)備,就不一一介紹了

UI界面和機(jī)器展示

android基于虹軟的人臉識(shí)別+測(cè)溫+道閘項(xiàng)目的實(shí)現(xiàn)方法

使用說(shuō)明

人臉識(shí)別通過(guò)后自動(dòng)測(cè)溫,然后向后臺(tái)上傳溫度和人員信息,后臺(tái)判斷溫度是否異常,并且保存人員通行記錄

項(xiàng)目總體流程

人臉注冊(cè): 人臉注冊(cè)采用另一種終端和小程序注冊(cè)兩種方式,這里只說(shuō)小程序。 用戶使用小程序采集人臉照片上傳至服務(wù)器-->人臉終端起服務(wù)定時(shí)向服務(wù)端請(qǐng)求終端沒(méi)有注冊(cè)過(guò)的人臉-->終端拿到人臉照片之后注冊(cè)至本地。另外定時(shí)請(qǐng)求需要?jiǎng)h除和更改的人臉信息,然后本地做刪除更改操作。(直接同步人臉照片而不是特征值是因?yàn)楹畿浤壳皼](méi)有小程序的人臉識(shí)別sdk)

開(kāi)門條件 以人臉識(shí)別+測(cè)溫、刷身份證+測(cè)溫、刷健康碼+測(cè)溫為開(kāi)門條件。 本文主要講解人臉+測(cè)溫

項(xiàng)目主要類介紹

PullDataServerHelper 拉取人臉信息幫助類,實(shí)現(xiàn)了拿到信息之后注冊(cè)人臉、刪除人臉、更改信息的操作

DataSyncService 數(shù)據(jù)同步服務(wù),此類為server,主要功能是定時(shí)調(diào)用PullDataServerHelper做網(wǎng)絡(luò)請(qǐng)求

facedb包 此包中為數(shù)據(jù)庫(kù)操作相關(guān)文件,本項(xiàng)目數(shù)據(jù)操作使用greendao,不了解的可以了解一下,非常好用。

項(xiàng)目的一些東西就先說(shuō)這么多,文章最后會(huì)附上源碼,接下來(lái)著重講一些虹軟SDK的使用

人臉識(shí)別部分(核心代碼)

1.sdk的激活

SDK為一次激活永久使用,不可多次激活,本文使用在線激活的方式,后端錄入終端綁定激活碼,app帶著終端唯一標(biāo)識(shí)向后端請(qǐng)求激活碼。 激活之前先判斷是否已經(jīng)激活,沒(méi)有激活才繼續(xù)激活操作,下面為代碼:

       fun Active() {
     //獲取激活文件
     val activeFileInfo = ActiveFileInfo()
     val code = FaceEngine.getActiveFileInfo(mContext, activeFileInfo)
     if (code == ErrorInfo.MOK) {
         //已經(jīng)激活
         isActive.value = true
         return
     } else {
         //未激活 讀取本地存儲(chǔ)的激活碼
         var sdkKey = readString(
             mContext,
             Constants.APP_SDK_KEY
         )
         var appId = readString(
             mContext,
             Constants.APP_ID_KEY
         )
         var activeKey = readString(
             mContext,
             Constants.APP_ACTIVE_KEY
         )
         if (sdkKey.isNullOrEmpty()) {
             //本地?zé)o激活碼 從網(wǎng)絡(luò)獲取
             getSdkInfo()
         } else {
             val code1 = FaceEngine.activeOnline(
                 mContext,
                 activeKey,
                 appId,
                 sdkKey
             )
             if (code1 == ErrorInfo.MOK) {
                 isActive.value = true
                 return
             } else {
                 getSdkInfo()
             }
         }
     }
 }


private fun getSdkInfo() {
 RetrofitManager.getInstance().createReq(ApiServer::class.java)
     .getSdkInfo(AppUtils.getMac())
     .subscribeOn(Schedulers.io())
     .observeOn(AndroidSchedulers.mainThread())
     .subscribe(object : BaseObserver<SdkInfoResult>() {
         override fun onSuccees(data: SdkInfoResult) {
             if (data.code == 200 && null != data.data) {
                 write(mContext, Constants.APP_SDK_KEY, data.data.SdkKey)
                 write(mContext, Constants.APP_ID_KEY, data.data.AppId)
                 write(mContext, Constants.APP_ACTIVE_KEY, data.data.ActiveKey)
                 val code1 = FaceEngine.activeOnline(
                     mContext,
                     data.data.activeKey,
                     data.data.appId,
                     data.data.sdkKey
                 )
                 if (code1 == ErrorInfo.MOK) {
                     isActive.value = true
                     return
                 } else {
                     isActive.value = false
                 }
             }
         }
         override fun onFailure(message: String?) {
             isActive.value = false
         }

     })


     }

2、sdk初始化 初始化的各個(gè)屬性官方文檔都有詳細(xì)講解,這里就不贅述了

 public void init() {
        Context context = CustomApplication.Companion.getMContext();
        FaceServer.getInstance().init(context);

        ftEngine = new FaceEngine();
        int ftEngineMask = FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_MASK_DETECT;
        int ftCode = ftEngine.init(context, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_90_ONLY, FaceConfig.RECOGNIZE_MAX_DETECT_FACENUM, ftEngineMask);
        ftInitCode.postValue(ftCode);

        frEngine = new FaceEngine();
        int frEngineMask = FaceEngine.ASF_FACE_RECOGNITION;
        if (FaceConfig.ENABLE_FACE_QUALITY_DETECT) {
            frEngineMask |= FaceEngine.ASF_IMAGEQUALITY;
        }
        int frCode = frEngine.init(context, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_90_ONLY,
                10, frEngineMask);
        frInitCode.postValue(frCode);

        //啟用活體檢測(cè)時(shí),才初始化活體引擎
        int flCode = -1;
        if (FaceConfig.ENABLE_LIVENESS) {
            flEngine = new FaceEngine();
            int flEngineMask = (livenessType == LivenessType.RGB ? FaceEngine.ASF_LIVENESS : (FaceEngine.ASF_IR_LIVENESS | FaceEngine.ASF_FACE_DETECT));
            if (needUpdateFaceData) {
                flEngineMask |= FaceEngine.ASF_UPDATE_FACEDATA;
            }
            flCode = flEngine.init(context, DetectMode.ASF_DETECT_MODE_IMAGE,
                    DetectFaceOrientPriority.ASF_OP_90_ONLY, FaceConfig.RECOGNIZE_MAX_DETECT_FACENUM, flEngineMask);
            flInitCode.postValue(flCode);
            LivenessParam livenessParam = new LivenessParam(FaceConfig.RECOMMEND_RGB_LIVENESS_THRESHOLD, FaceConfig.RECOMMEND_IR_LIVENESS_THRESHOLD);
            flEngine.setLivenessParam(livenessParam);
        }

        if (ftCode == ErrorInfo.MOK && frCode == ErrorInfo.MOK && flCode == ErrorInfo.MOK) {
            Constants.isInitEnt = true;
        }

    }

人臉注冊(cè)

 public FaceEntity registerJpeg(Context context, FaceImageResult.DataBean data) throws RegisterFailedException {
        if (faceRegisterInfoList != null && faceRegisterInfoList.size() >= MAX_REGISTER_FACE_COUNT) {
            Log.e(TAG, "registerJpeg: registered face count limited " + faceRegisterInfoList.size());
            // 已達(dá)注冊(cè)上限,超過(guò)該值會(huì)影響識(shí)別率
            throw new RegisterFailedException("registered face count limited");
        }
        Bitmap bitmap = ImageUtil.jpegToScaledBitmap( Base64.decode(data.getImage(), Base64.DEFAULT), ImageUtil.DEFAULT_MAX_WIDTH, ImageUtil.DEFAULT_MAX_HEIGHT);
        bitmap = ArcSoftImageUtil.getAlignedBitmap(bitmap, true);
        byte[] imageData = ArcSoftImageUtil.createImageData(bitmap.getWidth(), bitmap.getHeight(), ArcSoftImageFormat.BGR24);
        int code = ArcSoftImageUtil.bitmapToImageData(bitmap, imageData, ArcSoftImageFormat.BGR24);
        if (code != ArcSoftImageUtilError.CODE_SUCCESS) {
            throw new RuntimeException("bitmapToImageData failed, code is " + code);
        }
        return registerBgr24(context, imageData, bitmap.getWidth(), bitmap.getHeight(), data);
    }

    /**
     * 用于注冊(cè)照片人臉
     *
     * @param context 上下文對(duì)象
     * @param bgr24   bgr24數(shù)據(jù)
     * @param width   bgr24寬度
     * @param height  bgr24高度
     * @param name    保存的名字,若為空則使用時(shí)間戳
     * @return 注冊(cè)成功后的人臉信息
     */
    public FaceEntity registerBgr24(Context context, byte[] bgr24, int width, int height, String name,String idCard) {
        if (faceEngine == null || context == null || bgr24 == null || width % 4 != 0 || bgr24.length != width * height * 3) {
            Log.e(TAG, "registerBgr24:  invalid params");
            return null;
        }
        //人臉檢測(cè)
        List<FaceInfo> faceInfoList = new ArrayList<>();
        int code;
        synchronized (faceEngine) {
            code = faceEngine.detectFaces(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList);
        }
        if (code == ErrorInfo.MOK && !faceInfoList.isEmpty()) {
            code = faceEngine.process(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList,
                    FaceEngine.ASF_MASK_DETECT);
            if (code == ErrorInfo.MOK) {
                List<MaskInfo> maskInfoList = new ArrayList<>();
                faceEngine.getMask(maskInfoList);
                if (!maskInfoList.isEmpty()) {
                    int isMask = maskInfoList.get(0).getMask();
                    if (isMask == MaskInfo.WORN) {
                        /*
                         * 注冊(cè)照要求不戴口罩
                         */
                        Log.e(TAG, "registerBgr24: maskInfo is worn");
                        return null;
                    }
                }
            }

            FaceFeature faceFeature = new FaceFeature();
            /*
             * 特征提取,注冊(cè)人臉時(shí)參數(shù)extractType值為ExtractType.REGISTER,參數(shù)mask的值為MaskInfo.NOT_WORN
             */
            synchronized (faceEngine) {
                code = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList.get(0),
                        ExtractType.REGISTER, MaskInfo.NOT_WORN, faceFeature);
            }
            String userName = name == null ? String.valueOf(System.currentTimeMillis()) : name;

            //保存注冊(cè)結(jié)果(注冊(cè)圖、特征數(shù)據(jù))
            if (code == ErrorInfo.MOK) {
                //為了美觀,擴(kuò)大rect截取注冊(cè)圖
                Rect cropRect = getBestRect(width, height, faceInfoList.get(0).getRect());
                if (cropRect == null) {
                    Log.e(TAG, "registerBgr24: cropRect is null");
                    return null;
                }

                cropRect.left &= ~3;
                cropRect.top &= ~3;
                cropRect.right &= ~3;
                cropRect.bottom &= ~3;

                String imgPath = getImagePath(userName);

                // 創(chuàng)建一個(gè)頭像的Bitmap,存放旋轉(zhuǎn)結(jié)果圖
                Bitmap headBmp = getHeadImage(bgr24, width, height, faceInfoList.get(0).getOrient(), cropRect, ArcSoftImageFormat.BGR24);

                try {
                    FileOutputStream fos = new FileOutputStream(imgPath);
                    headBmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                }

                // 內(nèi)存中的數(shù)據(jù)同步
                if (faceRegisterInfoList == null) {
                    faceRegisterInfoList = new ArrayList<>();
                }

                FaceEntity faceEntity = new FaceEntity(name,idCard, imgPath, faceFeature.getFeatureData(),0L);
                //判斷是否存在這個(gè)人,如果存在覆蓋,否則新增(解決 因重置人臉刪除和注冊(cè)同事進(jìn)行問(wèn)題)
                if (faceRegisterInfoList.contains(faceEntity)) {
                    faceRegisterInfoList.remove(faceEntity);
                    List<FaceEntity> faceEntities = GreendaoUtils.Companion.getGreendaoUtils().searchFaceForIdcard(idCard);
                    if (faceEntities == null || faceEntities.isEmpty()) {
                        long faceId = GreendaoUtils.Companion.getGreendaoUtils().insert(faceEntity);
                        faceEntity.setFaceId(faceId);
                    }else {
                        faceEntities.get(0).setFeatureData(faceFeature.getFeatureData());
                       GreendaoUtils.Companion.getGreendaoUtils().update(faceEntities.get(0));
                    }
                } else {
                    long faceId = GreendaoUtils.Companion.getGreendaoUtils().insert(faceEntity);
                    faceEntity.setFaceId(faceId);
                }
                faceRegisterInfoList.add(faceEntity);

                return faceEntity;
            } else {
                Log.e(TAG, "registerBgr24: extract face feature failed, code is " + code);
                return null;
            }
        } else {
            Log.e(TAG, "registerBgr24: no face detected, code is " + code);
            return null;
        }
    }

人臉?biāo)阉?/p>

 /**
     * 在特征庫(kù)中搜索
     *
     * @param faceFeature 傳入特征數(shù)據(jù)
     * @return 比對(duì)結(jié)果
     */
    public CompareResult getTopOfFaceLib(FaceFeature faceFeature) {
        if (faceEngine == null || faceFeature == null || faceRegisterInfoList == null || faceRegisterInfoList.isEmpty()) {
            return null;
        }
        long start = System.currentTimeMillis();
        FaceFeature tempFaceFeature = new FaceFeature();
        FaceSimilar faceSimilar = new FaceSimilar();
        float maxSimilar = 0;
        int maxSimilarIndex = -1;

        int code = ErrorInfo.MOK;

        synchronized (searchLock) {
            for (int i = 0; i < faceRegisterInfoList.size(); i++) {
                tempFaceFeature.setFeatureData(faceRegisterInfoList.get(i).getFeatureData());
                code = faceEngine.compareFaceFeature(faceFeature, tempFaceFeature, faceSimilar);
                if (faceSimilar.getScore() > maxSimilar) {
                    maxSimilar = faceSimilar.getScore();
                    maxSimilarIndex = i;
                }
            }
        }
        if (maxSimilarIndex != -1) {
            return new CompareResult(faceRegisterInfoList.get(maxSimilarIndex), maxSimilar, code, System.currentTimeMillis() - start);
        }
        return null;
    }

測(cè)溫部分(核心代碼)

測(cè)溫頭我們使用usb連接測(cè)溫頭,采用簡(jiǎn)單的usb 模擬鍵盤的方式,測(cè)溫頭測(cè)到溫度模擬鍵盤輸入到終端的文本框中,代碼監(jiān)聽(tīng)鍵盤輸入讀取溫度。當(dāng)然也可以通過(guò)串口連接測(cè)溫頭,主動(dòng)發(fā)指令操作測(cè)溫頭測(cè)溫,這里我采用的是模擬鍵盤的方式。

public class ReadTemperatureHelper {

    private StringBuffer mStringBufferResult;           //掃描內(nèi)容
    private boolean mCaps;
    private boolean isCtrl;//大小寫
    private OnReadSuccessListener onReadSuccessListener;


    public ReadTemperatureHelper(OnReadSuccessListener onReadSuccessListener) {
        this.onReadSuccessListener = onReadSuccessListener;
        mStringBufferResult = new StringBuffer();
    }


    /**
     * 事件解析
     *
     * @param event
     */
    public void analysisKeyEvent(KeyEvent event) {
        int keyCode = event.getKeyCode();
        //判斷字母大小寫
        checkLetterStatus(event);
        checkInputEnt(event);
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            char aChar = getInputCode(event);
            if (aChar != 0) {
                mStringBufferResult.append(aChar);
            }
            Log.i("123123", "keyCode:" + keyCode);
            if (keyCode == KeyEvent.KEYCODE_ENTER) {
                //回車鍵 返回
                Log.i("123123", "dispatchKeyEvent:" + mStringBufferResult.toString());
                String s = mStringBufferResult.toString();
//                int i = s.lastIndexOf(":");
//                String substring = s.substring(i);
//                String[] s1 = substring.split(" ");
                Log.i("123123", "體溫為:" + s);
                onReadSuccessListener.onReadSuccess(s.trim());
                mStringBufferResult.setLength(0);

            }

        }
    }

    /**
     * ctrl
     */
    private void checkInputEnt(KeyEvent event) {

        if (event.getKeyCode() == KeyEvent.KEYCODE_CTRL_LEFT) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                isCtrl = true;
            } else {
                isCtrl = false;
            }
        }

    }

    /**
     * shift鍵
     *
     * @param keyEvent
     */
    private void checkLetterStatus(KeyEvent keyEvent) {
        int keyCode = keyEvent.getKeyCode();
        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
            if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
                //按住shift鍵 大寫
                mCaps = true;
            } else {
                //小寫
                mCaps = false;
            }
        }
    }

    /**
     * 獲取掃描內(nèi)容
     *
     * @param keyEvent
     * @return
     */
    private char getInputCode(KeyEvent keyEvent) {
        char aChar;
        int keyCode = keyEvent.getKeyCode();
        Log.i("TAGKEYCODE", keyCode + "");
        if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= keyEvent.KEYCODE_Z)//29< keycode <54
        {
            //字母

            aChar = (char) ((mCaps ? 'A' : 'a') + keyCode - KeyEvent.KEYCODE_A);//
        } else if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
            //數(shù)字
            if (mCaps)//是否按住了shift鍵
            {
                //按住了 需要將數(shù)字轉(zhuǎn)換為對(duì)應(yīng)的字符
                switch (keyCode) {
                    case KeyEvent.KEYCODE_0:
                        aChar = ')';
                        break;
                    case KeyEvent.KEYCODE_1:
                        aChar = '!';
                        break;
                    case KeyEvent.KEYCODE_2:
                        aChar = '@';
                        break;
                    case KeyEvent.KEYCODE_3:
                        aChar = '#';
                        break;
                    case KeyEvent.KEYCODE_4:
                        aChar = '$';
                        break;
                    case KeyEvent.KEYCODE_5:
                        aChar = '%';
                        break;
                    case KeyEvent.KEYCODE_6:
                        aChar = '^';
                        break;
                    case KeyEvent.KEYCODE_7:
                        aChar = '&';
                        break;
                    case KeyEvent.KEYCODE_8:
                        aChar = '*';
                        break;
                    case KeyEvent.KEYCODE_9:
                        aChar = '(';
                        break;
                    default:
                        aChar = ' ';
                        break;
                }
            } else {
                aChar = (char) ('0' + keyCode - KeyEvent.KEYCODE_0);
            }

        } else {
            //其他符號(hào)
            switch (keyCode) {
                case KeyEvent.KEYCODE_PERIOD:
                    aChar = '.';
                    break;
                case KeyEvent.KEYCODE_MINUS:
                    aChar = mCaps ? '_' : '-';
                    break;
                case KeyEvent.KEYCODE_SLASH:
                    aChar = '/';
                    break;
                case KeyEvent.KEYCODE_STAR:
                    aChar = '*';
                    break;
                case KeyEvent.KEYCODE_POUND:
                    aChar = '#';
                    break;
                case KeyEvent.KEYCODE_SEMICOLON:
                    aChar = mCaps ? ':' : ';';
                    break;
                case KeyEvent.KEYCODE_AT:
                    aChar = '@';
                    break;
                case KeyEvent.KEYCODE_BACKSLASH:
                    aChar = mCaps ? '|' : '\\';
                    break;
                default:
                    aChar = ' ';
                    break;
            }
        }

        return aChar;
    }


    public interface OnReadSuccessListener {
        void onReadSuccess(String temperature);
    }


}

在activity的dispatchKeyEvent方法,監(jiān)聽(tīng)鍵盤輸入事件

 override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
        if (isReadTemp) {
            read.analysisKeyEvent(event)
            if (event!!.keyCode == KeyEvent.KEYCODE_ENTER) {
                return true
            }
        }
        return super.dispatchKeyEvent(event)

    }

開(kāi)門(繼電器方式 核心代碼)

  public void openG() {
        String status = "1";
        try {
            FileOutputStream fos = new FileOutputStream("/sys/exgpio/relay1");
            fos.write(status.getBytes());
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String status1 = "0";
        SystemClock.sleep(200);
        try {
            FileOutputStream fos = new FileOutputStream("/sys/exgpio/relay1");
            fos.write(status1.getBytes());
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

常見(jiàn)問(wèn)題

本項(xiàng)目的開(kāi)發(fā)和使用中也遇到了很多問(wèn)題,我認(rèn)為比較值得注意的有兩個(gè) 1、室外復(fù)雜環(huán)境下,存在人臉識(shí)別久久不通過(guò)問(wèn)題 這個(gè)問(wèn)題不是偶發(fā)的問(wèn)題,經(jīng)過(guò)反復(fù)測(cè)試,室外較為昏暗的光線下,因?yàn)殚_(kāi)啟了ir紅外活體檢測(cè),存在熱源光不足導(dǎo)致活體檢測(cè)不通過(guò)

2、室外環(huán)境導(dǎo)致測(cè)溫不準(zhǔn)確 這個(gè)問(wèn)題是紅外測(cè)溫技術(shù)原理導(dǎo)致的,因?yàn)槭彝鉁囟冗^(guò)高或者過(guò)低無(wú)法保證測(cè)溫準(zhǔn)確率,或者測(cè)不到溫度。目前沒(méi)有解決方案,后期會(huì)測(cè)量整個(gè)人臉框這以區(qū)域每個(gè)點(diǎn)的溫度,作一定補(bǔ)償取平均值。

補(bǔ)充

上述簡(jiǎn)單的羅列了一些核心的代碼塊,后面附源碼,源碼中有詳細(xì)的業(yè)務(wù)代碼,包含讀身份證和掃碼,因?yàn)樯矸葑C讀卡器是公司產(chǎn)品,與其它的身份證讀卡器讀卡sdk不一樣,所以刪除了讀卡sdk,業(yè)務(wù)代碼保留。

讀到身份證后回去后臺(tái)驗(yàn)證此人健康碼狀態(tài),然后確定是否開(kāi)門

讀取健康碼使用串口讀取,代碼里有寫,讀到健康碼后,去后臺(tái)驗(yàn)證此健康碼狀態(tài)確認(rèn)是否開(kāi)門

因?yàn)闇y(cè)試需要,所以健康碼部分代碼注釋掉了,項(xiàng)目中隨機(jī)給的溫度以便測(cè)試

到此,相信大家對(duì)“android基于虹軟的人臉識(shí)別+測(cè)溫+道閘項(xiàng)目的實(shí)現(xiàn)方法”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI