主頁 > 後端開發 > JavaCV人臉識別三部曲之三:識別和預覽

JavaCV人臉識別三部曲之三:識別和預覽

2023-06-30 08:54:14 後端開發

歡迎訪問我的GitHub

這里分類和匯總了欣宸的全部原創(含配套原始碼):https://github.com/zq2599/blog_demos

《JavaCV人臉識別三部曲》鏈接

  1. 《視頻中的人臉保存為圖片》
  2. 《訓練》
  3. 《識別和預覽》

本篇概覽

  • 作為《JavaCV人臉識別三部曲》的終篇,今天咱們要開發一個實用的功能:有人出現在攝像頭中時,應用程式在預覽視窗標注出此人的身份,效果如下圖所示:

  • 簡單來說,本篇要做的事情如下:

  1. 理解重點概念:confidence
  2. 理解重點概念:threshold
  3. 編碼
  4. 驗證
  • 今天撰寫的代碼,主要功能如下圖所示:
    在這里插入圖片描述

理解重點概念:confidence

  • confidence和threshold是OpenCV的人臉識別中非常重要的兩個概念,咱們先把這兩個概念搞清楚,再去編碼就非常容易了

  • 假設,咱們用下面六張照片訓練出包含兩個類別的模型:
    在這里插入圖片描述

  • 用一張新的照片去訓練好的模型中做識別,如下圖,識別結果有兩部分內容:label和confidence
    在這里插入圖片描述

  • 先說lable,這個好理解,與訓練時的lable一致(回顧上一篇的代碼,lable如下圖紅框所示),前面圖中lable等于2,表示被判定為郭富城:
    在這里插入圖片描述

  • 按照上面的說法,lable等于2就能確定照片中的人像是郭富城嗎?

  • 當然不能!!!此時confidence欄位就非常重要了,先看JavaCV原始碼中對confidence的解釋,如下圖紅框所示,我的理解是:與lable值相關聯的置信度,或者說這張臉是郭富城的可能性
    在這里插入圖片描述

  • 如果理解為可能性,那么問題來了,這是個double型的值,這個值越大,表示可能性越大還是越小?

  • 上圖并沒有明說,但是那一句e.g. distance,讓我想起了機器學習中的K-means,此時我腦海中的畫面如下:
    在這里插入圖片描述
    -若真如上圖所示,那么顯然confidence越小,是郭富城的可能性就越大了,接下來再去找一些權威的說法:

  • OpenCV的官方論壇有個帖子的說法如下圖:代碼中的confidence變數屬于命名不當,其含義不是可信度,而是與模型中的類別的距離:
    在這里插入圖片描述

  • 再看第二個解釋,如下圖紅框,說得很清楚了,值越小,與模型中類別的相似度越高,0表示完全匹配:
    在這里插入圖片描述

  • 再看一個Stack Overflow的解釋:
    在這里插入圖片描述

  • 至此,相信您對confidence已經足夠理解了,lable等于2,confidence=30.01,意思是:被識別照片與郭富城最相似,距離為30.01,距離越小,是郭富城的可能性越大

理解重點概念:threshold

  • 在聊threshold之前,咱們先看一個場景,還是劉德華郭富城的模型,這次咱們拿喜洋洋的照片給模型識別,識別結果如下:
    在這里插入圖片描述
  • 顯然,模型不會告訴你照片里是誰,只會告訴你:和郭富城的距離是3000.01
  • 看到這里,聰明的您可能會這么想:那我就寫一段代碼吧,識別結果的confidence如果太大(例如超過100),就判定用于識別的人不屬于訓練模型的任何一個類別
  • 上述功能,OpenCV已經幫咱們想到了,那就是:threshold,翻譯過來即門限,如果咱們設定了threshold等于100,那么,一旦距離超過100,OpenCV的lable回傳值就是-1
  • 理解了confidence和threshold,接下來可以寫人臉識別的代碼了,感謝咱們的充分準備,接下來是絲般順滑的編碼程序...

原始碼下載

  • 《JavaCV人臉識別三部曲》的完整原始碼可在GitHub下載到,地址和鏈接資訊如下表所示(https://github.com/zq2599/blog_demos):
名稱 鏈接 備注
專案主頁 https://github.com/zq2599/blog_demos 該專案在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該專案原始碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該專案原始碼的倉庫地址,ssh協議
  • 這個git專案中有多個檔案夾,本篇的原始碼在javacv-tutorials檔案夾下,如下圖紅框所示:
    在這里插入圖片描述
  • javacv-tutorials里面有多個子工程,《JavaCV人臉識別三部曲》系列的代碼在simple-grab-push工程下:
    在這里插入圖片描述

編碼:人臉識別服務

  • 開始正式編碼,今天咱們不會新建工程,而是繼續使用《JavaCV的攝像頭實戰之一:基礎》中創建的simple-grab-push工程
  • 先定義一個Bean類PredictRlt.java,用來保存識別結果(lable和confidence欄位):
package com.bolingcavalry.grabpush.extend;

import lombok.Data;

@Data
public class PredictRlt {
    private int lable;
    private double confidence;
}
  • 然后把人臉識別有關的服務集中在RecognizeService.java中,方便主程式使用,代碼如下,有幾處要注意的地方稍后提到:
package com.bolingcavalry.grabpush.extend;

import com.bolingcavalry.grabpush.Constants;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Size;
import org.bytedeco.opencv.opencv_face.FaceRecognizer;
import org.bytedeco.opencv.opencv_face.FisherFaceRecognizer;
import static org.bytedeco.opencv.global.opencv_imgcodecs.IMREAD_GRAYSCALE;
import static org.bytedeco.opencv.global.opencv_imgproc.resize;

/**
 * @author willzhao
 * @version 1.0
 * @description 把人臉識別的服務集中在這里
 * @date 2021/12/12 21:32
 */
public class RecognizeService {

    private FaceRecognizer faceRecognizer;

    // 推理結果的標簽
    private int[] plabel;

    // 推理結果的置信度
    private double[] pconfidence;

    // 推理結果
    private PredictRlt predictRlt;

    // 用于推理的圖片尺寸,要和訓練時的尺寸保持一致
    private Size size= new Size(Constants.RESIZE_WIDTH, Constants.RESIZE_HEIGHT);

    public RecognizeService(String modelPath) {
        plabel = new int[1];
        pconfidence = new double[1];
        predictRlt = new PredictRlt();
        
        // 識別類的實體化,與訓練時相同
        faceRecognizer = FisherFaceRecognizer.create();
        // 加載的是訓練時生成的模型
        faceRecognizer.read(modelPath);
        // 設定門限,這個可以根據您自身的情況不斷調整
        faceRecognizer.setThreshold(Constants.MAX_CONFIDENCE);
    }

    /**
     * 將Mat實體給模型去推理
     * @param mat
     * @return
     */
    public PredictRlt predict(Mat mat) {
        // 調整到和訓練一致的尺寸
        resize(mat, mat, size);

        boolean isFinish = false;

        try {
            // 推理(這一行可能拋出RuntimeException例外,因此要補貨,否則會導致程式退出)
            faceRecognizer.predict(mat, plabel, pconfidence);
            isFinish = true;
        } catch (RuntimeException runtimeException) {
            runtimeException.printStackTrace();
        }

        // 如果發生過例外,就提前回傳
        if (!isFinish) {
            return null;
        }

        // 將推理結果寫入回傳物件中
        predictRlt.setLable(plabel[0]);
        predictRlt.setConfidence(pconfidence[0]);

        return predictRlt;
    }
}
  • 上述代碼有以下幾處需要注意:
  1. 構造方法中,通過faceRecognizer.setThreshold設定門限,我在實際使用中發現50比較合適,您可以根據自己的情況不斷調整
  2. predict方法中,用于識別的圖片要用resize方法調整大小,尺寸要和訓練時的尺寸一致
  3. 實測發現,在一張照片中出現多個人臉時,faceRecognizer.predict可能拋出RuntimeException例外,因此這里要捕獲例外,避免程式崩潰退出

編碼:檢測和識別

  • 檢測有關的介面DetectService.java,如下,和《JavaCV人臉識別三部曲之一:視頻中的人臉保存為圖片》中的完全一致:
package com.bolingcavalry.grabpush.extend;

import com.bolingcavalry.grabpush.Constants;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_objdetect.CascadeClassifier;
import static org.bytedeco.opencv.global.opencv_core.CV_8UC1;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imwrite;
import static org.bytedeco.opencv.global.opencv_imgproc.*;

/**
 * @author willzhao
 * @version 1.0
 * @description 檢測工具的通用介面
 * @date 2021/12/5 10:57
 */
public interface DetectService {

    /**
     * 根據傳入的MAT構造相同尺寸的MAT,存放灰度圖片用于以后的檢測
     * @param src 原始圖片的MAT物件
     * @return 相同尺寸的灰度圖片的MAT物件
     */
    static Mat buildGrayImage(Mat src) {
        return new Mat(src.rows(), src.cols(), CV_8UC1);
    }
    
    /**
     * 初始化操作,例如模型下載
     * @throws Exception
     */
    void init() throws Exception;

    /**
     * 得到原始幀,做識別,添加框選
     * @param frame
     * @return
     */
    Frame convert(Frame frame);

    /**
     * 釋放資源
     */
    void releaseOutputResource();
}
  • 然后就是DetectService的實作類DetectAndRecognizeService .java,功能是用攝像頭的一幀圖片檢測人臉,再拿檢測到的人臉給RecognizeService做識別,完整代碼如下,有幾處要注意的地方稍后提到:
package com.bolingcavalry.grabpush.extend;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_objdetect.CascadeClassifier;

import java.io.File;
import java.net.URL;
import java.util.Map;

import static org.bytedeco.opencv.global.opencv_imgproc.*;

/**
 * @author willzhao
 * @version 1.0
 * @description 音頻相關的服務
 * @date 2021/12/3 8:09
 */
@Slf4j
public class DetectAndRecognizeService implements DetectService {

    /**
     * 每一幀原始圖片的物件
     */
    private Mat grabbedImage = null;

    /**
     * 原始圖片對應的灰度圖片物件
     */
    private Mat grayImage = null;

    /**
     * 分類器
     */
    private CascadeClassifier classifier;

    /**
     * 轉換器
     */
    private OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();

    /**
     * 檢測模型檔案的下載地址
     */
    private String detectModelFileUrl;

    /**
     * 處理每一幀的服務
     */
    private RecognizeService recognizeService;

    /**
     * 為了顯示的時候更加友好,給每個分類對應一個名稱
     */
    private Map<Integer, String> kindNameMap;

    /**
     * 構造方法
     * @param detectModelFileUrl
     * @param recognizeModelFilePath
     * @param kindNameMap
     */
    public DetectAndRecognizeService(String detectModelFileUrl, String recognizeModelFilePath, Map<Integer, String> kindNameMap) {
        this.detectModelFileUrl = detectModelFileUrl;
        this.recognizeService = new RecognizeService(recognizeModelFilePath);
        this.kindNameMap = kindNameMap;
    }

    /**
     * 音頻采樣物件的初始化
     * @throws Exception
     */
    @Override
    public void init() throws Exception {
        // 下載模型檔案
        URL url = new URL(detectModelFileUrl);

        File file = Loader.cacheResource(url);

        // 模型檔案下載后的完整地址
        String classifierName = file.getAbsolutePath();

        // 根據模型檔案實體化分類器
        classifier = new CascadeClassifier(classifierName);

        if (classifier == null) {
            log.error("Error loading classifier file [{}]", classifierName);
            System.exit(1);
        }
    }

    @Override
    public Frame convert(Frame frame) {
        // 由幀轉為Mat
        grabbedImage = converter.convert(frame);

        // 灰度Mat,用于檢測
        if (null==grayImage) {
            grayImage = DetectService.buildGrayImage(grabbedImage);
        }

        // 進行人臉識別,根據結果做處理得到預覽視窗顯示的幀
        return detectAndRecoginze(classifier, converter, frame, grabbedImage, grayImage, recognizeService, kindNameMap);
    }

    /**
     * 程式結束前,釋放人臉識別的資源
     */
    @Override
    public void releaseOutputResource() {
        if (null!=grabbedImage) {
            grabbedImage.release();
        }

        if (null!=grayImage) {
            grayImage.release();
        }

        if (null==classifier) {
            classifier.close();
        }
    }

    /**
     * 檢測圖片,將檢測結果用矩形標注在原始圖片上
     * @param classifier 分類器
     * @param converter Frame和mat的轉換器
     * @param rawFrame 原始視頻幀
     * @param grabbedImage 原始視頻幀對應的mat
     * @param grayImage 存放灰度圖片的mat
     * @param kindNameMap 每個分類編號對應的名稱
     * @return 標注了識別結果的視頻幀
     */
    static Frame detectAndRecoginze(CascadeClassifier classifier,
                                    OpenCVFrameConverter.ToMat converter,
                                    Frame rawFrame,
                                    Mat grabbedImage,
                                    Mat grayImage,
                                    RecognizeService recognizeService,
                                    Map<Integer, String> kindNameMap) {

        // 當前圖片轉為灰度圖片
        cvtColor(grabbedImage, grayImage, CV_BGR2GRAY);

        // 存放檢測結果的容器
        RectVector objects = new RectVector();

        // 開始檢測
        classifier.detectMultiScale(grayImage, objects);

        // 檢測結果總數
        long total = objects.size();

        // 如果沒有檢測到結果,就用原始幀回傳
        if (total<1) {
            return rawFrame;
        }

        PredictRlt predictRlt;
        int pos_x;
        int pos_y;
        int lable;
        double confidence;
        String content;

        // 如果有檢測結果,就根據結果的資料構造矩形框,畫在原圖上
        for (long i = 0; i < total; i++) {
            Rect r = objects.get(i);
			
			// 核心代碼,把檢測到的人臉拿去識別	
            predictRlt = recognizeService.predict(new Mat(grayImage, r));

            // 如果回傳為空,表示出現過例外,就執行下一個
            if (null==predictRlt) {
                System.out.println("return null");
                continue;
            }

            // 分類的編號(訓練時只有1和2,這里只有有三個值,1和2與訓練的分類一致,還有個-1表示沒有匹配上)
            lable = predictRlt.getLable();
            // 與模型中的分類的距離,值越小表示相似度越高
            confidence = predictRlt.getConfidence();

            // 得到分類編號后,從map中取得名字,用來顯示
            if (kindNameMap.containsKey(predictRlt.getLable())) {
                content = String.format("%s, confidence : %.4f", kindNameMap.get(lable), confidence);
            } else {
                // 取不到名字的時候,就顯示unknown
                content = "unknown(" + predictRlt.getLable() + ")";
                System.out.println(content);
            }

            int x = r.x(), y = r.y(), w = r.width(), h = r.height();
            rectangle(grabbedImage, new Point(x, y), new Point(x + w, y + h), Scalar.RED, 1, CV_AA, 0);

            pos_x = Math.max(r.tl().x()-10, 0);
            pos_y = Math.max(r.tl().y()-10, 0);

            putText(grabbedImage, content, new Point(pos_x, pos_y), FONT_HERSHEY_PLAIN, 1.5, new Scalar(0,255,0,2.0));
        }

        // 釋放檢測結果資源
        objects.close();

        // 將標注過的圖片轉為幀,回傳
        return converter.convert(grabbedImage);
    }
}
  • 上述代碼有幾處要注意:
  1. 重點關注detectAndRecoginze方法,這里面先呼叫classifier.detectMultiScale檢測出當前照片所有的人臉,然后把每一張人臉交個recognizeService進行識別,
  2. 識別結果的lable是個int型的,看起來不夠友好,因此從kindNameMap中根據lable找出對應的名稱來
  3. 最終給每個頭像添加矩形框,還在左上角添加識別結果,以及confidence的值
  4. 處理完畢后轉為Frame物件回傳,這樣的幀顯示在預覽頁面,效果就是視頻中每個人被框選出來,并帶有身份
  • 現在核心代碼已經寫完,需要再寫一些代碼來使用DetectAndRecognizeService

編碼:運行框架

  • 《JavaCV的攝像頭實戰之一:基礎》創建的simple-grab-push工程中已經準備好了父類AbstractCameraApplication,所以本篇繼續使用該工程,創建子類實作那些抽象方法即可
  • 編碼前先回顧父類的基礎結構,如下圖,粗體是父類定義的各個方法,紅色塊都是需要子類來實作抽象方法,所以接下來,咱們以本地視窗預覽為目標實作這三個紅色方法即可:
    在這里插入圖片描述
  • 新建檔案PreviewCameraWithIdentify.java,這是AbstractCameraApplication的子類,其代碼很簡單,接下來按上圖順序依次說明
  • 先定義CanvasFrame型別的成員變數previewCanvas,這是展示視頻幀的本地視窗:
protected CanvasFrame previewCanvas
  • 把前面創建的DetectService作為成員變數,后面檢測的時候會用到:
    /**
     * 檢測工具介面
     */
    private DetectService detectService;
  • PreviewCameraWithIdentify的構造方法,接受DetectService的實體:
    /**
     * 不同的檢測工具,可以通過構造方法傳入
     * @param detectService
     */
    public PreviewCameraWithIdentify(DetectService detectService) {
        this.detectService = detectService;
    }
  • 然后是初始化操作,可見是previewCanvas的實體化和引數設定,還有檢測、識別的初始化操作:
    @Override
    protected void initOutput() throws Exception {
        previewCanvas = new CanvasFrame("攝像頭預覽和身份識別", CanvasFrame.getDefaultGamma() / grabber.getGamma());
        previewCanvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        previewCanvas.setAlwaysOnTop(true);

        // 檢測服務的初始化操作
        detectService.init();
    }
  • 接下來是output方法,定義了拿到每一幀視頻資料后做什么事情,這里呼叫了detectService.convert檢測人臉并保存圖片,然后在本地視窗顯示:
    @Override
    protected void output(Frame frame) {
        // 原始幀先交給檢測服務處理,這個處理包括物體檢測,再將檢測結果標注在原始圖片上,
        // 然后轉換為幀回傳
        Frame detectedFrame = detectService.convert(frame);
        // 預覽視窗上顯示的幀是標注了檢測結果的幀
        previewCanvas.showImage(detectedFrame);
    }
  • 最后是處理視頻的回圈結束后,程式退出前要做的事情,先關閉本地視窗,再釋放檢測服務的資源:
    @Override
    protected void releaseOutputResource() {
        if (null!= previewCanvas) {
            previewCanvas.dispose();
        }

        // 檢測工具也要釋放資源
        detectService.releaseOutputResource();
    }
  • 由于檢測有些耗時,所以兩幀之間的間隔時間要低于普通預覽:
    @Override
    protected int getInterval() {
        return super.getInterval()/8;
    }
  • 至此,功能已開發完成,再寫上main方法,代碼如下,有幾處要注意的地方稍后說明:
    public static void main(String[] args) {
        String modelFileUrl = "https://raw.github.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_alt.xml";
        String recognizeModelFilePath = "E:\\temp\\202112\\18\\001\\faceRecognizer.xml";

        // 這里分類編號的身份的對應關系,和之前訓練時候的設定要保持一致
        Map<Integer, String> kindNameMap = new HashMap();
        kindNameMap.put(1, "Man");
        kindNameMap.put(2, "Woman");

        // 檢測服務
        DetectService detectService = new DetectAndRecognizeService(modelFileUrl,recognizeModelFilePath, kindNameMap);

        // 開始檢測
        new PreviewCameraWithIdentify(detectService).action(1000);
    }
  • 上述main方法中,有以下幾處需要注意:
  1. kindNameMap是個HashMap,里面放這每個分類編號對應的名稱,我訓練的模型中包含了兩位群眾演員的頭像,給他們分別起名ManWoman
  2. modelFileUrl是人臉檢測時用到的模型地址
  3. recognizeModelFilePath是人臉識別時用到的模型地址,這個模型是《JavaCV人臉識別三部曲之二:訓練》一文中訓練的模型
  • 至此,人臉識別的代碼已經寫完,運行main方法,請幾位群眾演員來到攝像頭前面,驗證效果吧

驗證

  • 程式運行起來后,請名為Man的群眾演員A站在攝像頭前面,如下圖,識別成功:
    在這里插入圖片描述

  • 接下來,請名為Woman的群眾演員B過來,和群眾演員A同框,如下圖,同時識別成功,不過偶爾會識別錯誤,提示成unknown(-1)

  • 再請一個沒有參與訓練的小群眾演員過來,與A同框,此刻的識別也是準確的,小演員被標注為unknown(-1)

  • 去看程式的控制臺,發現FaceRecognizer.predict方法會拋出例外,幸好程式捕獲了例外,不會把整個行程中斷退出:
    在這里插入圖片描述

  • 至此,整個《JavaCV人臉識別三部曲》全部完成,如果您是位java程式員,正在尋找人臉識別相關的方案,希望本系列能給您一些參考

  • 另外《JavaCV人臉識別三部曲》是《JavaCV的攝像頭實戰》系列的分支,作為主干的《JavaCV的攝像頭實戰》依然在持續更新中,欣宸原創會繼續與您一路相伴,學習、實戰、提升

歡迎關注博客園:程式員欣宸

學習路上,你不孤單,欣宸原創一路相伴...

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/556380.html

標籤:Java

上一篇:Spring 的依賴注入(DI)

下一篇:返回列表

標籤雲
其他(161902) Python(38266) JavaScript(25517) Java(18285) C(15238) 區塊鏈(8274) C#(7972) AI(7469) 爪哇(7425) MySQL(7278) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5876) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4609) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2438) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1985) HtmlCss(1979) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • JavaCV人臉識別三部曲之三:識別和預覽

    ### 歡迎訪問我的GitHub > 這里分類和匯總了欣宸的全部原創(含配套原始碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 《JavaCV人臉識別三部曲》鏈接 1. [《視頻中的 ......

    uj5u.com 2023-06-30 08:54:14 more
  • Spring 的依賴注入(DI)

    歡迎來到本篇文章,書接上回,本篇說說 Spring 中的依賴注入,包括注入的方式,寫法,該選擇哪個注入方式以及可能出現的回圈依賴問題等內容。

    如果正在閱讀的朋友還不清楚什么是「依賴」,建議先看看我第一篇文章,通過 Employee 和 Department 簡單說了什么是所謂的依賴。 ......

    uj5u.com 2023-06-30 07:49:32 more
  • 6 sleep與yield的比較

    # 6 sleep與yield的比較 ## sleep 1. 呼叫 sleep 會讓當前執行緒從 *Running* 進入 *Timed Waiting* 狀態(阻塞) 2. 其它執行緒可以使用 interrupt 方法打斷正在睡眠的執行緒,這時 sleep 方法會拋出 InterruptedExcept ......

    uj5u.com 2023-06-30 07:49:24 more
  • C++面試八股文:什么是建構式?

    某日二師兄參加XXX科技公司的C++工程師開發崗位第29面: > 面試官:什么是建構式? > > 二師兄:建構式是一種特殊的成員函式,用于創建和初始化類的物件。建構式的名稱與類的名稱相同,并且沒有回傳型別。建構式在物件被創建時自動呼叫。 ```c++ struct Foo { Foo(int ......

    uj5u.com 2023-06-30 07:49:20 more
  • 【numpy基礎】--陣列過濾

    在`numpy`中,陣列可以看作是一系列數值的有序集合,可以通過下標訪問其中的元素。處理陣列的程序中,經常需要用到陣列過濾功能。 過濾功能可以在處理資料時非常有用,因為它可以使資料更加干凈和可讀性更強。例如,在進行資料分析時,通常需要去除例外值,過濾掉不必要的元素可以使資料更加易于分析和處理。 `n ......

    uj5u.com 2023-06-30 07:49:16 more
  • celery筆記九之task運行結果查看

    > 本文首發于公眾號:Hunter后端 > 原文鏈接:[celery筆記九之task運行結果查看](https://mp.weixin.qq.com/s/QabEFiXgEOtFCI_kX-yuIg) 這一篇筆記介紹一下 celery 的 task 運行之后結果的查看。 前面我們使用的配置是這樣的: ......

    uj5u.com 2023-06-30 07:49:09 more
  • 【python爬蟲案例】用python爬豆瓣音樂TOP250排行榜!

    [toc] # 一、爬蟲物件-豆瓣音樂TOP250 今天我們分享一期python爬蟲案例講解。爬取物件是,豆瓣音樂TOP250排行榜資料:https://music.douban.com/top250 ?![豆瓣音樂TOP250頁面](https://img2023.cnblogs.com/blog ......

    uj5u.com 2023-06-30 07:47:38 more
  • 無縫資料轉換!使用C++ 實作 Excel檔案與CSV之間的相互轉換

    CSV格式是一種通用的文本檔案格式,可在多個應用程式之間共享和使用。相比之下,Excel檔案是一種電子表格格式,通常只能在Microsoft Excel中編輯和查看。因此,將Excel檔案轉換為CSV格式可使資料更方便地在其他應用程式中使用;而將CSV檔案轉換為Excel格式則有利于在Microso ......

    uj5u.com 2023-06-30 07:47:26 more
  • 流程控制節點

    ## 流程控制節點 ### 如何打開藍圖控制面板 window->palette (調色板) ### 開啟鍵盤事件 1.方法一 開啟角色控制權 ![](https://img2023.cnblogs.com/blog/3081762/202306/3081762-20230629173847953- ......

    uj5u.com 2023-06-30 07:47:03 more
  • 解決pandas的concat函式導致索引失效的方法

    # concat 最近在寫資料的時候看到用一個concat函式進行整合,但是下面這段代碼之后就碰上個很奇怪的地方 ```python for i, bag in enumerate(bags): coure_result = func() core_df = pd.DataFrame([core_r ......

    uj5u.com 2023-06-30 07:46:40 more