主頁 > 後端開發 > 從零玩轉人臉識別

從零玩轉人臉識別

2022-05-22 11:50:52 後端開發

前言

在線demo

(前往享受人臉識別)

文章作者個人博客

(前往作者博客)

本期教程人臉識別第三方平臺為虹軟科技,基于Java開發,本文章講解的是人臉識別RGB活體追蹤技術,免費的功能很多可以自行搭配,希望在你看完本章課程有所識訓,

人臉追蹤示例

ArcFace 離線SDK,包含人臉檢測、性別檢測、年齡檢測、人臉識別、影像質量檢測、RGB活體檢測、IR活體檢測等能力,初次使用時需聯網激活,激活后即可在本地無網路環境下作業,可根據具體的業務需求結合人臉識別SDK靈活地進行應用層開發,

SDK功能模塊圖.png

功能介紹

1. 人臉檢測

對傳入的影像資料進行人臉檢測,回傳人臉的邊框以及朝向資訊,可用于后續的人臉識別、特征提取、活體檢測等操作;

  • 支持IMAGE模式和VIDEO模式人臉檢測,
  • 支持單人臉、多人臉檢測,最多支持檢測人臉數為50,

2.人臉追蹤

對來自于視頻流中的影像資料,進行人臉檢測,并對檢測到的人臉進行持續跟蹤,(我們是實時的所以就只能使用第三方操作,先不使用這個)

3.人臉特征提取

提取人臉特征資訊,用于人臉的特征比對,

4.人臉屬性檢測

人臉屬性,支持檢測年齡、性別以及3D角度,

人臉3D角度:俯仰角(pitch), 橫滾角(roll), 偏航角(yaw),

3D角度.png

5.活體檢測

離線活體檢測,靜默式識別,在人臉識別程序中判斷操作用戶是否為真人,有效防御照片、視頻、紙張等不同型別的作弊攻擊,提高業務安全性,讓人臉識別更安全、更快捷,體驗更佳,支持單目RGB活體檢測、雙目(IR/RGB)活體檢測,可滿足各類人臉識別終端產品活體檢測應用,

開造

訪問地址: https://ai.arcsoft.com.cn/technology/faceTracking.html 進入開發者中心進行注冊以及認證個人資訊
1. 點擊我的應用 > 新建應用

image-20210702134809401

2.填寫資訊立即創建 點擊 添加SDK

image-20210702135034579

3.選中免費版人臉識別

image-20210702135111937

4. 填寫授權碼資訊
選擇平臺先選擇windows的根據你的電腦配置來 是64位還是32位的, 語言選擇Java

image-20210702135225642

sdk

5. 介紹sdk檔案

image-20210702135945966

構建專案工程

image-1652620445459

匯入專案依賴



    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
        <druid-spring-boot-starter.version>1.2.6</druid-spring-boot-starter.version>
        <mybatis-spring-boot.version>2.1.4</mybatis-spring-boot.version>
        <pagehelper.boot.version>1.3.0</pagehelper.boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--  人臉識別  -->
        <dependency>
            <groupId>com.arcsoft.face</groupId>
            <artifactId>arcsoft-sdk-face</artifactId>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/arcsoft-sdk-face-3.0.0.0.jar</systemPath>
            <version>3.0.0.0</version>
        </dependency>

        <!-- pool 物件池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <!-- guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>27.0.1-jre</version>
        </dependency>

        <!-- Mysql驅動包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- mybatis-plus 增強CRUD -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!-- pagehelper 分頁插件 內置mybatis 依賴-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>${pagehelper.boot.version}</version>
        </dependency>

        <!-- 阿里資料源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid-spring-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Spring框架基本的核心工具 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- JSON工具類 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.0</version>
        </dependency>
        <!-- SpringWeb模塊 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>

        <!-- servlet包 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <!-- aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>top.yangbuyi.YangbuyiFaceDemoApplication</mainClass>
                    <!-- 檔案要配置includeSystemScope屬性,否則可能會導致arcsoft-sdk-face-3.0.0.0.jar獲取不到 -->
                    <includeSystemScope>true</includeSystemScope>
                    <fork>true</fork>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

使用代碼生成器生成CURD
版本請對應圖片當中的jebat全家桶群里獲取3秒破解使用

image-1652622813045
image-1652621615948

生成完畢后在根目錄創建lib目錄將下載下來的人臉識別依賴匯入->右擊添加到庫

image-1652621634861

application.yml 修改組態檔

# 開發環境配置
server:
  # 服務器的HTTP埠,默認為8080
  port: 8080
  servlet:
    # 應用的訪問路徑
    context-path: /
  tomcat:
    # tomcat的URI編碼
    uri-encoding: UTF-8
    # tomcat最大執行緒數,默認為200
    max-threads: 800
    # Tomcat啟動初始化的執行緒數,默認值25
    min-spare-threads: 30

# Spring配置
spring:
  # 同時執行其它組態檔
  profiles:
    active: druid
  mvc: # 把前端的接收到的時間格式 格式化為 yyyy-MM-dd HH:mm:ss
    date-format: yyyy-MM-dd HH:mm:ss
  jackson: # 把后臺的時間格式 格式化為 yyyy-MM-dd HH:mm:ss
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  # 服務模塊
  devtools:
    restart:
      # 熱部署開關
      enabled: true
# MyBatis配置
mybatis-plus:
  # 搜索指定包別名
  typeAliasesPackage: top.yangbuyi.domain
  # 配置mapper的掃描,找到所有的mapper.xml映射檔案
  mapperLocations: classpath*:mapper/*Mapper.xml
  # 加載全域的組態檔
  configLocation: classpath:mybatis/mybatis-config.xml

# PageHelper分頁插件
pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  params: count=countSql

# 人臉識別配置

# WIND64
config:
  sdk-lib-path: M:\yangbuyiya-RBAC\libs\WIN64
  app-id: 4QKtmacvsKqaCsoXyyujcs21JTAr79pTczPdZpuaEjhH
  sdk-key: EgBjrmidnqstaL46msfHukeKanYXCujzeHokf2qcC3br
  thread-pool-size: 5

application-druid.yml 資料源配置

# 資料源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      url: jdbc:mysql://127.0.0.1:3308/face?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
      username: root
      password: 123456
      # 初始連接數
      initialSize: 5
      # 最小連接池數量
      minIdle: 10
      # 最大連接池數量
      maxActive: 20
      # 配置獲取連接等待超時的時間
      maxWait: 60000
      # 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一個連接在池中最小生存的時間,單位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一個連接在池中最大生存的時間,單位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置檢測連接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # 設定白名單,不填則允許所有訪問
        allow:
        url-pattern: /yangbuyi/druid/*
      filter:
        stat:
          enabled: true
          # 慢SQL記錄
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true

resources 下創建mybatis檔案夾創建mybatis-config.xml檔案

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <setting name="cacheEnabled" value="https://www.cnblogs.com/Yangbuyi/p/true"/>  <!-- 全域映射器啟用快取 -->
        <setting name="useGeneratedKeys" value="https://www.cnblogs.com/Yangbuyi/p/true"/>  <!-- 允許 JDBC 支持自動生成主鍵 -->
        <setting name="defaultExecutorType" value="https://www.cnblogs.com/Yangbuyi/p/REUSE"/> <!-- 配置默認的執行器 -->
        <setting name="logImpl" value="https://www.cnblogs.com/Yangbuyi/p/SLF4J"/> <!-- 指定 MyBatis 所用日志的具體實作 -->
        <!-- <setting name="mapUnderscoreToCamelCase" value="https://www.cnblogs.com/Yangbuyi/p/true"/>  駝峰式命名 -->
    </settings>

</configuration>

專案基礎檔案配置

創建config檔案
創建ApplicationConfig全域配置類

/**
 * 程式注解配置
 *
 * @author yangbuyi
 */
@Configuration
// 表示通過aop框架暴露該代理物件,AopContext能夠訪問
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要掃描的Mapper類的包的路徑
@MapperScan("top.yangbuyi.mapper")
public class ApplicationConfig {
    /**
     * 時區配置
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization () {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
    }

    /**
     * 處理Long型別精度丟失
     *
     * @return
     */
    @Bean("jackson2ObjectMapperBuilderCustomizer")
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer () {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
                                                     .serializerByType(Long.TYPE, ToStringSerializer.instance);
    }
}

創建全域MybatisPlusConfig配置


/**
 * @program: yangbuyi-rbac
 * @ClassName: MybatisPlusConfig
 * @create: 2022-04-25 15:48
 * @author: yangbuyi.top
 * @since: JDK1.8
 * @MybatisPlusConfig: $
 **/

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor () {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分頁插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // 樂觀鎖插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        // 阻斷插件
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
        return interceptor;
    }

    /**
     * 分頁插件,自動識別資料庫型別 https://baomidou.com/guide/interceptor-pagination.html
     */
    public PaginationInnerInterceptor paginationInnerInterceptor () {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 設定資料庫型別為mysql
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        // 設定最大單頁限制數量,默認 500 條,-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        return paginationInnerInterceptor;
    }

    /**
     * 樂觀鎖插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
     */
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor () {
        return new OptimisticLockerInnerInterceptor();
    }

    /**
     * 如果是對全表的洗掉或更新操作,就會終止該操作 https://baomidou.com/guide/interceptor-block-attack.html
     */
    public BlockAttackInnerInterceptor blockAttackInnerInterceptor () {
        return new BlockAttackInnerInterceptor();
    }
}

創建dto檔案夾

創建人臉回傳物體 FaceSearchResDto

/**
 * @author yangbuyi.top
 */
@Data
public class FaceSearchResDto {
    /**
     * 唯一人臉Id
     */
    private String faceId;
    /**
     * 人臉名稱
     */
    private String name;
    private Integer similarValue;
    /**
     * 年齡
     */
    private Integer age;
    /**
     * 性別
     */
    private String gender;
    /**
     * 圖片
     */
    private String image;

}

創建人臉映射 FaceUserInfo


/**
 * @author yangbuyi.top
 */
@Data
public class FaceUserInfo {
    private int id;
    private int groupId;
    private String faceId;
    private String name;
    private Integer similarValue;
    private byte[] faceFeature;
}

創建年齡映射


/**
 * @author yangbuyi.top
 */
public class ProcessInfo {
    private Integer age;
    private Integer gender;

    public Integer getAge () {
        return age;
    }

    public void setAge (Integer age) {
        this.age = age;
    }

    public Integer getGender () {
        return gender;
    }

    public void setGender (Integer gender) {
        this.gender = gender;
    }
}

創建enums檔案夾

創建ErrorCodeEnum列舉類


/**
 * @author yangbuyi.top
 */


public enum ErrorCodeEnum {

    MOK(0, "成功"),
    UNKNOWN(1, "未知錯誤"),
    INVALID_PARAM(2, "無效引數"),
    UNSUPPORTED(3, "引擎不支持"),
    NO_MEMORY(4, "記憶體不足"),
    BAD_STATE(5, "狀態錯誤"),
    USER_CANCEL(6, "用戶取消相關操作"),
    EXPIRED(7, "操作時間過期"),
    USER_PAUSE(8, "用戶暫停操作"),
    BUFFER_OVERFLOW(9, "緩沖上溢"),
    BUFFER_UNDERFLOW(10, "緩沖下溢"),
    NO_DISKSPACE(11, "存貯空間不足"),
    COMPONENT_NOT_EXIST(12, "組件不存在"),
    GLOBAL_DATA_NOT_EXIST(13, "全域資料不存在"),
    NO_FACE_DETECTED(14, "未檢出到人臉"),
    FACE_DOES_NOT_MATCH(15, "人臉不匹配"),
    INVALID_APP_ID(28673, "無效的AppId"),
    INVALID_SDK_ID(28674, "無效的SdkKey"),
    INVALID_ID_PAIR(28675, "AppId和SdkKey不匹配"),
    MISMATCH_ID_AND_SDK(28676, "SdkKey 和使用的SDK 不匹配"),
    SYSTEM_VERSION_UNSUPPORTED(28677, "系統版本不被當前SDK所支持"),
    LICENCE_EXPIRED(28678, "SDK有效期過期,需要重新下載更新"),
    APS_ENGINE_HANDLE(69633, "引擎句柄非法"),
    APS_MEMMGR_HANDLE(69634, "記憶體句柄非法"),
    APS_DEVICEID_INVALID(69635, " Device ID 非法"),
    APS_DEVICEID_UNSUPPORTED(69636, "Device ID 不支持"),
    APS_MODEL_HANDLE(69637, "模板資料指標非法"),
    APS_MODEL_SIZE(69638, "模板資料長度非法"),
    APS_IMAGE_HANDLE(69639, "影像結構體指標非法"),
    APS_IMAGE_FORMAT_UNSUPPORTED(69640, "影像格式不支持"),
    APS_IMAGE_PARAM(69641, "影像引數非法"),
    APS_IMAGE_SIZE(69642, "影像尺寸大小超過支持范圍"),
    APS_DEVICE_AVX2_UNSUPPORTED(69643, "處理器不支持AVX2指令"),
    FR_INVALID_MEMORY_INFO(73729, "無效的輸入記憶體"),
    FR_INVALID_IMAGE_INFO(73730, "無效的輸入影像引數"),
    FR_INVALID_FACE_INFO(73731, "無效的臉部資訊"),
    FR_NO_GPU_AVAILABLE(73732, "當前設備無GPU可用"),
    FR_MISMATCHED_FEATURE_LEVEL(73733, "待比較的兩個人臉特征的版本不一致"),
    FACEFEATURE_UNKNOWN(81921, "人臉特征檢測錯誤未知"),
    FACEFEATURE_MEMORY(81922, "人臉特征檢測記憶體錯誤"),
    FACEFEATURE_INVALID_FORMAT(81923, "人臉特征檢測格式錯誤"),
    FACEFEATURE_INVALID_PARAM(81924, "人臉特征檢測引數錯誤"),
    FACEFEATURE_LOW_CONFIDENCE_LEVEL(81925, "人臉特征檢測結果置信度低"),
    ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_INIT(86017, "Engine不支持的檢測驗性"),
    ASF_EX_BASE_FEATURE_UNINITED(86018, "需要檢測的屬性未初始化"),
    ASF_EX_BASE_FEATURE_UNPROCESSED(86019, "待獲取的屬性未在process中處理過"),
    ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_PROCESS(86020, "PROCESS不支持的檢測驗性,例如FR,有自己獨立的處理函式"),
    ASF_EX_BASE_INVALID_IMAGE_INFO(86021, "無效的輸入影像"),
    ASF_EX_BASE_INVALID_FACE_INFO(86022, "無效的臉部資訊"),
    ASF_BASE_ACTIVATION_FAIL(90113, "人臉比對SDK激活失敗,請打開讀寫權限"),
    ASF_BASE_ALREADY_ACTIVATED(90114, "人臉比對SDK已激活"),
    ASF_BASE_NOT_ACTIVATED(90115, "人臉比對SDK未激活"),
    ASF_BASE_SCALE_NOT_SUPPORT(90116, "detectFaceScaleVal 不支持"),
    ASF_BASE_VERION_MISMATCH(90117, "SDK版本不匹配"),
    ASF_BASE_DEVICE_MISMATCH(90118, "設備不匹配"),
    ASF_BASE_UNIQUE_IDENTIFIER_MISMATCH(90119, "唯一標識不匹配"),
    ASF_BASE_PARAM_NULL(90120, "引數為空"),
    ASF_BASE_SDK_EXPIRED(90121, "SDK已過期"),
    ASF_BASE_VERSION_NOT_SUPPORT(90122, "版本不支持"),
    ASF_BASE_SIGN_ERROR(90123, "簽名錯誤"),
    ASF_BASE_DATABASE_ERROR(90124, "資料庫插入錯誤"),
    ASF_BASE_UNIQUE_CHECKOUT_FAIL(90125, "唯一識別符號校驗失敗"),
    ASF_BASE_COLOR_SPACE_NOT_SUPPORT(90126, "輸入的顏色空間不支持"),
    ASF_BASE_IMAGE_WIDTH_NOT_SUPPORT(90127, "輸入影像的byte資料長度不正確"),
    ASF_NETWORK_BASE_COULDNT_RESOLVE_HOST(94209, "無法決議主機地址"),
    ASF_NETWORK_BASE_COULDNT_CONNECT_SERVER(94210, "無法連接服務器"),
    ASF_NETWORK_BASE_CONNECT_TIMEOUT(94211, "網路連接超時"),
    ASF_NETWORK_BASE_UNKNOWN_ERROR(94212, "未知錯誤");


    private Integer code;
    private String description;

    ErrorCodeEnum (Integer code, String description) {
        this.code = code;
        this.description = description;
    }

    public Integer getCode () {
        return code;
    }

    public void setCode (Integer code) {
        this.code = code;
    }

    public String getDescription () {
        return description;
    }

    public void setDescription (String description) {
        this.description = description;
    }

    public static ErrorCodeEnum getDescriptionByCode (Integer code) {
        for (ErrorCodeEnum errorCodeEnum : ErrorCodeEnum.values()) {
            if (code.equals(errorCodeEnum.getCode())) {
                return errorCodeEnum;
            }
        }
        return ErrorCodeEnum.UNKNOWN;
    }

}

創建utils檔案夾

創建Base64DecodeMultipartFile


package top.yangbuyi.utils;

import org.springframework.web.multipart.MultipartFile;

import java.io.*;

/**
 * @author yangbuyi.top
 * @program: yangbuyi-rbac
 * @ClassName: Base64DecodeMultipartFile
 * @create: 2022-04-25 16:25
 * @since: JDK1.8
 * @Base64DecodeMultipartFile: $
 **/

public class Base64DecodeMultipartFile implements MultipartFile {
    private final byte[] imgContent;
    private final String header;

    public Base64DecodeMultipartFile (byte[] imgContent, String header) {
        this.imgContent = imgContent;
        this.header = header.split(";")[0];
    }

    @Override
    public String getName () {
        return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
    }

    @Override
    public String getOriginalFilename () {
        return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
    }

    @Override
    public String getContentType () {
        return header.split(":")[1];
    }

    @Override
    public boolean isEmpty () {
        return imgContent == null || imgContent.length == 0;
    }

    @Override
    public long getSize () {
        return imgContent.length;
    }

    @Override
    public byte[] getBytes () throws IOException {
        return imgContent;
    }

    @Override
    public InputStream getInputStream () throws IOException {
        return new ByteArrayInputStream(imgContent);
    }

    @Override
    public void transferTo (File dest) throws IOException, IllegalStateException {
        new FileOutputStream(dest).write(imgContent);
    }
}

創建ImageUtils


package top.yangbuyi.utils;

import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;

/**
 * 圖片處理工具類
 *
 * @author yangbuyi.top
 */
public class ImageUtils {
    private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);


    public static MultipartFile base64ToMultipartFile (String base64) {
        //base64編碼后的圖片有頭資訊所以要分離出來 [0]data:image/png;base64, 圖片內容為索引[1]
        String[] baseStrs = base64.split(",");

        //取索引為1的元素進行處理
        byte[] b = Base64.decodeBase64(baseStrs[1]);
        for (int i = 0; i < b.length; ++i) {
            if (b[i] < 0) {
                b[i] += 256;
            }
        }

        //處理過后的資料通過Base64DecodeMultipartFile轉換為MultipartFile物件
        return new Base64DecodeMultipartFile(b, baseStrs[0]);
    }

}


專案基本需要的配置與工具已經創建完畢接下來我們開始人臉識別業務撰寫

實作BasePooledObjectFactory自定義引擎工廠

創建factory檔案夾->創建FaceEngineFactory類



import com.arcsoft.face.EngineConfiguration;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

/**
 * 引擎工廠
 * @author yangbuyi.top
 */
@Slf4j
public class FaceEngineFactory extends BasePooledObjectFactory<FaceEngine> {

    private final String appId;
    private final String sdkKey;
    private final String sdkLibPath;
    private final EngineConfiguration engineConfiguration;
    private final Integer detectFaceMaxNum = 10;
    private final Integer detectFaceScaleVal = 16;
    private final DetectMode detectMode = DetectMode.ASF_DETECT_MODE_IMAGE;
    private final DetectMode detectVideo = DetectMode.ASF_DETECT_MODE_VIDEO;
    private final DetectOrient detectFaceOrientPriority = DetectOrient.ASF_OP_0_ONLY;


    public FaceEngineFactory (String sdkLibPath, String appId, String sdkKey, EngineConfiguration engineConfiguration) {
        this.sdkLibPath = sdkLibPath;
        this.appId = appId;
        this.sdkKey = sdkKey;
        this.engineConfiguration = engineConfiguration;

    }


    @Override
    public FaceEngine create () throws Exception {
        FaceEngine faceEngine = new FaceEngine(sdkLibPath);
        // 用于在線激活SDK---
        int activeCode = faceEngine.activeOnline(appId, sdkKey);
        log.info("在線激活SDK完畢!,{}", activeCode);
        int initCode = faceEngine.init(engineConfiguration);
        log.info("初始化功能引擎完畢!,{}", initCode);
        return faceEngine;
    }

    @Override
    public PooledObject<FaceEngine> wrap (FaceEngine faceEngine) {
        return new DefaultPooledObject<>(faceEngine);
    }


    @Override
    public void destroyObject (PooledObject<FaceEngine> p) throws Exception {
        FaceEngine faceEngine = p.getObject();
        int unInitCode = faceEngine.unInit();
        super.destroyObject(p);
        log.info("銷毀物件完畢! faceEngineUnInitCode: {}", unInitCode);
    }
}


一、撰寫人臉識別業務介面

在service目錄下創建-> FaceEngineService


package top.yangbuyi.service;

import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.toolkit.ImageInfo;
import top.yangbuyi.dto.FaceUserInfo;
import top.yangbuyi.dto.ProcessInfo;

import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * 人臉識別介面
 */
public interface FaceEngineService {

    /**
     * 人臉檢測
     * @param imageInfo
     * @return
     */
    List<FaceInfo> detectFaces (ImageInfo imageInfo);

    /**
     * 提取年齡-性別
     * @param imageInfo
     * @return
     */
    List<ProcessInfo> process (ImageInfo imageInfo);

    /**
     * 人臉特征
     * @param imageInfo
     * @return
     */
    byte[] extractFaceFeature (ImageInfo imageInfo) throws InterruptedException;

    /**
     * 人臉比對
     * @param groupId
     * @param faceFeature
     * @return
     */
    List<FaceUserInfo> compareFaceFeature (byte[] faceFeature, Integer groupId) throws InterruptedException, ExecutionException;

}

service.impl 包下創建 FaceEngineServiceImpl 實作類



import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.toolkit.ImageInfo;
import com.google.common.collect.Lists;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import top.yangbuyi.dto.FaceUserInfo;
import top.yangbuyi.dto.ProcessInfo;
import top.yangbuyi.factory.FaceEngineFactory;
import top.yangbuyi.mapper.SysUserFaceInfoMapper;
import top.yangbuyi.service.FaceEngineService;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;


/**
 * @author yangbuyi.top
 */
@Service
public class FaceEngineServiceImpl implements FaceEngineService {

    public final static Logger logger = LoggerFactory.getLogger(FaceEngineServiceImpl.class);

    @Value("${config.sdk-lib-path}")
    public String sdkLibPath;

    @Value("${config.app-id}")
    public String appId;

    @Value("${config.sdk-key}")
    public String sdkKey;

    @Value("${config.thread-pool-size}")
    public Integer threadPoolSize;

    /**
     * 人臉識別引擎
     */
    private static FaceEngine faceEngine;

    /**
     * 相似度
     */
    private final Integer passRate = 95;

    private ExecutorService executorService;

    @Autowired
    private SysUserFaceInfoMapper userFaceInfoMapper;

    /**
     * 執行緒池
     */
    private GenericObjectPool<FaceEngine> faceEngineObjectPool;


    /**
     * 專案啟動時初始化執行緒池與人臉識別引擎配置
     */
    @PostConstruct
    public void init () {
        executorService = Executors.newFixedThreadPool(threadPoolSize);
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxIdle(threadPoolSize);
        poolConfig.setMaxTotal(threadPoolSize);
        poolConfig.setMinIdle(threadPoolSize);
        poolConfig.setLifo(false);

        //引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);

        //功能配置 對應的功能請查看檔案
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(true);
        functionConfiguration.setSupportFace3dAngle(true);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(true);
        functionConfiguration.setSupportLiveness(true);
        functionConfiguration.setSupportIRLiveness(true);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);

        // 底層庫演算法物件池
        faceEngineObjectPool = new GenericObjectPool(new FaceEngineFactory(sdkLibPath, appId, sdkKey, engineConfiguration), poolConfig);
        try {
            faceEngine = faceEngineObjectPool.borrowObject();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 確保精度
     * @param value
     * @return
     */
    private int plusHundred (Float value) {
        BigDecimal target = new BigDecimal(value);
        BigDecimal hundred = new BigDecimal(100f);
        return target.multiply(hundred).intValue();

    }

}

人臉識別邏輯

1、必須進行人臉特征獲取 -> 特征獲取成功 -> 進入人臉對比 -> 人臉檢測 -> 回傳人臉資料
2、必須進行人臉特征獲取 -> 特征獲取失敗 -> 直接跳出回傳 未檢出到人臉

撰寫人臉特征獲取邏輯


/**
 * 人臉特征
 *
 * @param imageInfo
 * @return
 */
@Override
public byte[]extractFaceFeature(ImageInfo imageInfo)throws InterruptedException{

        FaceEngine faceEngine=null;
        try{
        //獲取引擎物件
        faceEngine=faceEngineObjectPool.borrowObject();

        //人臉檢測得到人臉串列
        List<FaceInfo> faceInfoList=new ArrayList<FaceInfo>();

        //人臉檢測
        int i=faceEngine.detectFaces(imageInfo.getImageData(),imageInfo.getWidth(),imageInfo.getHeight(),imageInfo.getImageFormat(),faceInfoList);

        if(CollectionUtil.isNotEmpty(faceInfoList)){
        FaceFeature faceFeature=new FaceFeature();
        //提取人臉特征
        faceEngine.extractFaceFeature(imageInfo.getImageData(),imageInfo.getWidth(),imageInfo.getHeight(),imageInfo.getImageFormat(),faceInfoList.get(0),faceFeature);

        return faceFeature.getFeatureData();
        }
        }catch(Exception e){
        logger.error("",e);
        }finally{
        if(faceEngine!=null){
        //釋放引擎物件
        faceEngineObjectPool.returnObject(faceEngine);
        }

        }

        return null;
        }

撰寫人臉對比邏輯


/**
 * 人臉比對
 * @param groupId
 * @param faceFeature
 * @return 人臉組
 */
@Override
public List<FaceUserInfo> compareFaceFeature(byte[]faceFeature,Integer groupId)throws InterruptedException,ExecutionException{
        // 識別到的人臉串列
        List<FaceUserInfo> resultFaceInfoList=Lists.newLinkedList();
        // 創建人臉特征物件
        FaceFeature targetFaceFeature=new FaceFeature();
        targetFaceFeature.setFeatureData(faceFeature);
        // 根據分組拿人臉庫,從資料庫中取出人臉庫
        List<FaceUserInfo> faceInfoList=userFaceInfoMapper.getUserFaceInfoByGroupId(groupId);
        // 分成50一組,多執行緒處理, 資料量大1000組
        List<List<FaceUserInfo>>faceUserInfoPartList=Lists.partition(faceInfoList,50);
        // 多執行緒
        CompletionService<List<FaceUserInfo>>completionService=new ExecutorCompletionService(executorService);
        for(List<FaceUserInfo> part:faceUserInfoPartList){
        // 開始執行緒掃描人臉匹配度
        completionService.submit(new CompareFaceTask(part,targetFaceFeature));
        }
        // 獲取執行緒任務引數
        for(List<FaceUserInfo> faceUserInfos:faceUserInfoPartList){
        List<FaceUserInfo> faceUserInfoList=completionService.take().get();
        if(CollectionUtil.isNotEmpty(faceInfoList)){
        resultFaceInfoList.addAll(faceUserInfoList);
        }
        }
        // 從大到小排序
        resultFaceInfoList.sort((h1,h2)->h2.getSimilarValue().compareTo(h1.getSimilarValue()));
        return resultFaceInfoList;
        }


/**
 * 多執行緒跑人臉對比邏輯
 */
private class CompareFaceTask implements Callable<List<FaceUserInfo>> {

    private final List<FaceUserInfo> faceUserInfoList;
    private final FaceFeature targetFaceFeature;


    public CompareFaceTask (List<FaceUserInfo> faceUserInfoList, FaceFeature targetFaceFeature) {
        this.faceUserInfoList = faceUserInfoList;
        this.targetFaceFeature = targetFaceFeature;
    }

    @Override
    public List<FaceUserInfo> call () throws Exception {
        FaceEngine faceEngine = null;
        //識別到的人臉串列
        List<FaceUserInfo> resultFaceInfoList = Lists.newLinkedList();
        try {
            faceEngine = faceEngineObjectPool.borrowObject();
            for (FaceUserInfo faceUserInfo : faceUserInfoList) {
                FaceFeature sourceFaceFeature = new FaceFeature();
                // 設定人臉特征
                sourceFaceFeature.setFeatureData(faceUserInfo.getFaceFeature());
                FaceSimilar faceSimilar = new FaceSimilar();
                faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
                //獲取相似值
                Integer similarValue = https://www.cnblogs.com/Yangbuyi/p/plusHundred(faceSimilar.getScore());
                //相似值大于配置預期,加入到識別到人臉的串列
                if (similarValue > passRate) {
                    FaceUserInfo info = new FaceUserInfo();
                    info.setName(faceUserInfo.getName());
                    info.setFaceId(faceUserInfo.getFaceId());
                    info.setSimilarValue(similarValue);
                    resultFaceInfoList.add(info);
                }
            }
        } catch (Exception e) {
            logger.error("", e);
        } finally {
            if (faceEngine != null) {
                faceEngineObjectPool.returnObject(faceEngine);
            }
        }
        return resultFaceInfoList;
    }

}

撰寫根據根據分組ID獲取人臉庫資料

在mapper下SysUserFaceInfoMapper -> 創建介面 getUserFaceInfoByGroupId


/**
 * 根據分組Id
 * 從資料庫中取出人臉庫
 *
 * @param groupId
 * @return 人臉庫
 */
    List<FaceUserInfo> getUserFaceInfoByGroupId(Integer groupId);

sql實作

<resultMap id="userFace2" type="top.yangbuyi.dto.FaceUserInfo">
<id column="id" property="id" javaType="int"/>
<result column="group_id" property="groupId" javaType="java.lang.Integer"/>
<result column="name" property="name" javaType="java.lang.String"/>
<result column="face_id" property="faceId" javaType="String"/>
<result column="face_feature" property="faceFeature"/>
</resultMap>

<
select id = "getUserFaceInfoByGroupId" resultMap="userFace2" parameterType="java.lang.Integer"
        resultType="top.yangbuyi.dto.FaceUserInfo">
select id, group_id, face_id, name, face_feature
from `sys_user_face_info`
where group_id = #{groupId}
          < /
select>

二、撰寫人臉添加業務介面

userFaceInfoService 新增人臉業務介面

    /**
 * 新增用戶人臉識別
 *
 * @param sysUserFaceInfo 用戶人臉識別
 * @return 結果
 */
public int insertSysUserFaceInfo(SysUserFaceInfo sysUserFaceInfo);

就是一共簡簡單單的新增插入資料 這個就不用我教了吧?????????

image-1652677051101

三、 撰寫Controller業務控制層



import top.yangbuyi.domain.AjaxResult;

/**
 * (sys_user_face_info)表控制層
 *
 * @author yangbuyi.top
 */
@RestController
@Slf4j
@RequestMapping("face")
@RequiredArgsConstructor
public class SysUserFaceInfoController {
    public final static Logger logger = LoggerFactory.getLogger(SysUserFaceInfoController.class);

    private final FaceEngineService faceEngineService;

    private final SysUserFaceInfoService userFaceInfoService;


    /**
     * 人臉添加
     *
     * @param file    人臉附件
     * @param groupId 分組id
     * @param name    用戶登錄名稱
     */
    @RequestMapping(value = "https://www.cnblogs.com/faceAdd", method = RequestMethod.POST)
    public AjaxResult faceAdd (@RequestBody Map<String, Object> map) {
        // 業務.... yangbuyi.top 著作權所有
        return AjaxResult.success();
    }


    /**
     * 人臉識別
     *
     * @param file    人臉附件
     * @param groupId 分組ID 方便快速識別
     */
    @RequestMapping(value = "https://www.cnblogs.com/faceSearch", method = RequestMethod.POST)
    public AjaxResult faceSearch (@RequestBody Map<String, Object> map) throws Exception {
        // 業務... yangbuyi.top 著作權所有
        return AjaxResult.success();
    }

faceAdd 具體業務撰寫

  1. 根據前端傳遞的
    file -> 人臉圖片
    groupId -> 用戶分組(用于縮小范圍查詢該人員的位置有效減少資料量大的問題)
    name -> 當前用戶名稱(實際開發當中應該是存盤id)
        String file = String.valueOf(map.get("file"));
        String groupId = String.valueOf(map.get("groupId"));
        String name = String.valueOf(map.get("name"));
        try {
            if (file == null) {
                return AjaxResult.error("file is null");
            }
            if (groupId == null) {
                return AjaxResult.error("file is null");
            }
            if (name == null) {
                return AjaxResult.error("file is null");
            }
            // 轉換物體
            byte[] decode = Base64.decode(base64Process(file));
            ImageInfo imageInfo = ImageFactory.getRGBData(decode);

            //人臉特征獲取
            byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
            if (bytes == null) {
                return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
            }
            List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
            // 開始將人臉識別Base64轉換檔案流->用于檔案上傳
            // final MultipartFile multipartFile = ImageUtils.base64ToMultipartFile(file);
            final SysUserFaceInfo one = this.userFaceInfoService.lambdaQuery().eq(SysUserFaceInfo::getName, name).one();
            // 如果存在則更新人臉和特征
            if (null != one) {
                this.userFaceInfoService.lambdaUpdate().set(SysUserFaceInfo::getFaceFeature, bytes)
                        .set(SysUserFaceInfo::getFpath, "存盤頭像地址")
                        .eq(SysUserFaceInfo::getFaceId, name).update();
            } else {
                // 組裝人臉物體
                SysUserFaceInfo userFaceInfo = new SysUserFaceInfo();
                userFaceInfo.setName(name);
                userFaceInfo.setAge(processInfoList.get(0).getAge());
                userFaceInfo.setGender(processInfoList.get(0).getGender().shortValue());
                userFaceInfo.setGroupId(Integer.valueOf(groupId));
                userFaceInfo.setFaceFeature(bytes);
                userFaceInfo.setFpath("存盤頭像地址");
                // 存盤用戶ID -> 我這里先使用name代替 -> 假如是唯一性
                userFaceInfo.setFaceId(name);
                //人臉特征插入到資料庫
                userFaceInfoService.insertSysUserFaceInfo(userFaceInfo);
            }
            logger.info("faceAdd:" + name);
            return AjaxResult.success("人臉系結成功!");
        } catch (Exception e) {
            logger.error("", e);
        }
        // 錯誤回傳
        return AjaxResult.error(ErrorCodeEnum.UNKNOWN.getDescription());

faceSearch 具體業務撰寫

  1. 根據前端傳遞的
    file -> 人臉圖片
    groupId -> 用戶分組(用于縮小范圍查詢該人員的位置有效減少資料量大的問題)
String file = String.valueOf(map.get("file"));
        String groupId = String.valueOf(map.get("groupId"));

        if (groupId == null) {
            return AjaxResult.error("groupId is null");
        }
        byte[] decode = Base64.decode(base64Process(file));
        BufferedImage bufImage = ImageIO.read(new ByteArrayInputStream(decode));
        ImageInfo imageInfo = ImageFactory.bufferedImage2ImageInfo(bufImage);


        //人臉特征獲取
        byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
        // 校驗是否顯示出人臉
        if (bytes == null) {
            return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
        }

        //人臉比對,獲取比對結果
        List<FaceUserInfo> userFaceInfoList = faceEngineService.compareFaceFeature(bytes, Integer.valueOf(groupId));

        if (CollectionUtil.isNotEmpty(userFaceInfoList)) {
            FaceUserInfo faceUserInfo = userFaceInfoList.get(0);
            FaceSearchResDto faceSearchResDto = new FaceSearchResDto();
            BeanUtil.copyProperties(faceUserInfo, faceSearchResDto);
            List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
            if (CollectionUtil.isNotEmpty(processInfoList)) {
                //人臉檢測
                List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
                int left = faceInfoList.get(0).getRect().getLeft();
                int top = faceInfoList.get(0).getRect().getTop();
                int width = faceInfoList.get(0).getRect().getRight() - left;
                int height = faceInfoList.get(0).getRect().getBottom() - top;

                Graphics2D graphics2D = bufImage.createGraphics();
                // 紅色
                graphics2D.setColor(Color.RED);
                BasicStroke stroke = new BasicStroke(5f);
                graphics2D.setStroke(stroke);
                graphics2D.drawRect(left, top, width, height);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ImageIO.write(bufImage, "jpg", outputStream);
                byte[] bytes1 = outputStream.toByteArray();
                faceSearchResDto.setImage("data:image/jpeg;base64," + Base64Utils.encodeToString(bytes1));
                faceSearchResDto.setAge(processInfoList.get(0).getAge());
                faceSearchResDto.setGender(processInfoList.get(0).getGender().equals(1) ? "女" : "男");
            }
            return AjaxResult.success(faceSearchResDto);
        }
        return AjaxResult.error(ErrorCodeEnum.FACE_DOES_NOT_MATCH.getDescription());

測驗介面流程

準備兩張圖片
百度隨便搜索個在線轉換 Base64

(在線圖片轉Base64)

image-1652703631886

1、人臉添加

新增有臉的

image-1652702349484

新增測驗無臉的

image-1652703497056

2、人臉識別

有臉的

image-1652703318357

無臉的

image-1652703532069

結尾在線演示

(前往享受人臉識別)

image-1652677885135

你的壓力來源于無法自律,只是假裝努力,現狀跟不上內心欲望,所以你焦慮又恐慌,——楊不易

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

標籤:Java

上一篇:OutputStream詳解

下一篇:Java執行緒數過多解決之路——利用Arthas解決Jenkins執行緒數飆升問題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(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
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more