• Canmv K210开发板案例——人脸识别


    学习目标:

    • 在Canmv IDE环境下使用K210学习人脸识别功能,将摄像头检测到的人脸通过按键进行保存、系统进行编号,若下次识别到保存编号过的人脸,则通过变色框框出识别到的人脸,并标出序号与置信度

    学习内容:

    1、导入相关库,并初始化摄像头和LCD显示屏

    import sensor, image, time, lcd
    import gc                          # 内存管理模块
    from maix import KPU               # 加速AI计算
    from maix import GPIO, utils       # 使用Pin脚的GPIO功能、基础功能
    from fpioa_manager import fm       # 管理内部功能和引脚映射关系的功能模块
    from board import board_info       # 板子board_info类
    
    
    lcd.init()  # 初始化LCD显示屏
    sensor.reset()  # 复位并初始化摄像头
    sensor.set_pixformat(sensor.RGB565)  # 设置摄像头输出格式为 RGB565
    sensor.set_framesize(sensor.QVGA) # 设置摄像头输出大小为 QVGA (320x240)
    sensor.skip_frames(time = 1000) # 等待摄像头稳定
    clock = time.clock() # 创建一个clock对象,用来计算帧率
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、新建人脸特性图像大小为64*64,建立标准人脸关键点坐标

    feature_img = image.Image(size=(64,64), copy_to_fb=False) # 人脸特性图像为64*64图输入,这里初始化一个image
    feature_img.pix_to_ai()  # 对rgb565的image生成ai运算需要的r8g8b8格式存储
    
    # 标准人脸关键点坐标
    FACE_PIC_SIZE = 64
    dst_point =[(int(38.2946 * FACE_PIC_SIZE / 112), int(51.6963 * FACE_PIC_SIZE / 112)),
                (int(73.5318 * FACE_PIC_SIZE / 112), int(51.5014 * FACE_PIC_SIZE / 112)),
                (int(56.0252 * FACE_PIC_SIZE / 112), int(71.7366 * FACE_PIC_SIZE / 112)),
                (int(41.5493 * FACE_PIC_SIZE / 112), int(92.3655 * FACE_PIC_SIZE / 112)),
                (int(70.7299 * FACE_PIC_SIZE / 112), int(92.2041 * FACE_PIC_SIZE / 112)) ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3、初始化KPU相关的参数,加载人脸检测模型以及使用yolo2网络算法,kpu需要加载kmodel文件

    # 人脸检测锚点;锚点参数与模型参数一致,同一个模型这个参数是固定的(即训练模型时确定了)
    anchor = (0.1075, 0.126875, 0.126875, 0.175, 0.1465625, 0.2246875, 0.1953125, 0.25375, 0.2440625, 0.351875, 0.341875, 0.4721875, 0.5078125, 0.6696875, 0.8984375, 1.099687, 2.129062, 2.425937)
    kpu = KPU()# 创建一个kpu对象,用于人脸检测
    kpu.load_kmodel("/sd/KPU/yolo_face_detect/face_detect_320x240.kmodel") # 导入人脸检测模型
    # yolo2初始化
    kpu.init_yolo2(anchor, anchor_num=9, img_w=320, img_h=240, net_w=320 , net_h=240 ,layer_w=10 ,layer_h=8, threshold=0.7, nms_value=0.2, classes=1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4、初始化KPU相关的参数,加载ld5模型,kpu需要加载kmodel文件

    ld5_kpu = KPU()# 创建一个kpu对象,用于人脸5关键点检测
    print("ready load model")
    ld5_kpu.load_kmodel("/sd/KPU/face_recognization/ld5.kmodel") # 导入人脸5关键点检测模型
    
    • 1
    • 2
    • 3

    5、初始化KPU相关的参数,加载特征模型,kpu需要加载kmodel文件

    fea_kpu = KPU()# 创建一个kpu对象,用于人脸属性检测
    print("ready load model")
    fea_kpu.load_kmodel("/sd/KPU/face_recognization/feature_extraction.kmodel") # 导入人脸属性检测模型
    
    • 1
    • 2
    • 3

    6、新建按键功能,上升沿触发按键中断,用来记录需要识别的人脸

    start_processing = False 
    BOUNCE_PROTECTION = 50 # 定义外部中断时间为50ms
    
    fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0) # 将IO16配置为GPIOHS0
    key_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN) # 配置GPIOHS0为输入
    def set_key_state(*_): # 配置外部中断及中断回调函数,配置GPIOHS0为输入
        global start_processing # 发送数据
        start_processing = True
        time.sleep_ms(BOUNCE_PROTECTION)
    # 配置GPIOHS0上升沿触发中断
    key_gpio.irq(set_key_state, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7、新建人脸识别变量。变量record_ftrs用于存储按键记录下的人脸特征的数组;THRESHOLD为人脸识别相似度分数阈值,超过此值则认为是识别到的人脸,需要精准识别可以上调;recog_flag表示检测到的人脸是否已识别的状态 ,已识别为True,未识别为Flase

    record_ftrs = [] #空列表 用于存储按键记录下的人脸特征的数组
    THRESHOLD = 80.5   # 人脸识别相似度分数阈值为80.5,需要精准识别可以上调
    recog_flag = False  # 表示检测到的人脸是否已识别的状态 ,已识别为True,未识别为Flase
    
    • 1
    • 2
    • 3

    8、提取检测到的人脸的信息,目标检测中的边界框(x,y,w,h形式转换与绘制)

    def extend_box(x, y, w, h, scale):  # 目标检测中的边界框(x,y,w,h形式转换与绘制)
        x1_t = x - scale*w
        x2_t = x + w + scale*w
        y1_t = y - scale*h
        y2_t = y + h + scale*h
        x1 = int(x1_t) if x1_t>1 else 1
        x2 = int(x2_t) if x2_t<320 else 319
        y1 = int(y1_t) if y1_t>1 else 1
        y2 = int(y2_t) if y2_t<240 else 239
        cut_img_w = x2-x1+1
        cut_img_h = y2-y1+1
        return x1, y1, cut_img_w, cut_img_h
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    9、使用while循环将图像传入KPU循环进行计算,使用yolo2神经网络算法解算

    • (1)将检测到的人脸用白色框标记出来,kpu运算并获取结果,根据结果算出人脸5关键点,由关键点算出变换矩阵,相似变换修正人脸图, 检测人脸属性获取结果。当识别到BOOT按键按下时,记录人脸信息,每次运算结束后删除对象,管理内存
    • (2)人脸识别时,将检测到的人脸与储存的特征进行比对,比对两个特征数据并给出相似度分数,取最大相似度分数,当检测到的人脸信息相似度分数大于识别阈值,表示人脸识别成功,显示出persion:ID+置信度;未识别到是保存过的人脸,显示出unregistered+置信度每次运算结束后删除对象,管理内存
    • (3)检测到按键BOOT按下,上升沿按键中断,记录下当前检测到人脸特征,打印人脸记录次数。判断是否为保存过的人脸,并且用方框的颜色区分。显示屏显示FPS、显示提示用户操作的字符串。最后将创建的kpu对象去初始化,释放模型内存。每次运算结束后删除对象,管理内存
    while True:
        gc.collect() # 内存管理:手动启动垃圾收集
        print("mem free:",gc.mem_free()) #查看剩余内存信息(栈)
        print("heap free:",utils.heap_free())  #查看剩余内存信息(堆)
        clock.tick() # 更新计算帧率的clock
        img = sensor.snapshot() # 拍照,获取一张图像
        kpu.run_with_output(img) # 对输入图像进行kpu运算
        dect = kpu.regionlayer_yolo2() # yolo2后处理
        fps = clock.fps() # 获取帧率
        if len(dect) > 0:
            for l in dect :
                # 对检测到的人脸框裁剪
                x1, y1, cut_img_w, cut_img_h= extend_box(l[0], l[1], l[2], l[3], scale=0)
                face_cut = img.cut(x1, y1, cut_img_w, cut_img_h) # 从img中裁剪出人脸图
                face_cut_128 = face_cut.resize(128, 128) # 对人脸图调整大小到128*128
                face_cut_128.pix_to_ai() # 对rgb565格式的128人脸图生成ai运算需要的rgb888格式存储
                out = ld5_kpu.run_with_output(face_cut_128, getlist=True) # kpu运算并获取结果
                face_key_point = []
                for j in range(5): # 根据结果算出人脸5关键点,并在图中标出
                    x = int(KPU.sigmoid(out[2 * j])*cut_img_w + x1)
                    y = int(KPU.sigmoid(out[2 * j + 1])*cut_img_h + y1)
                    face_key_point.append((x,y))
                T = image.get_affine_transform(face_key_point, dst_point) #由关键点算出变换矩阵
                image.warp_affine_ai(img, feature_img, T) # 相似变换修正人脸图
                feature = fea_kpu.run_with_output(feature_img, get_feature = True)  # 检测人脸属性获取结果
                del face_key_point # 删除对象
    
                scores = []
                for j in range(len(record_ftrs)):
                    score = kpu.feature_compare(record_ftrs[j], feature) #  特征比对,比对两个特征数据并给出相似度分数
                    scores.append(score) # 将新元素score附加到数组的末尾,使其增长
                if len(scores):
                    max_score = max(scores) # 取最大相似度分数
                    index = scores.index(max_score)
                    if max_score > THRESHOLD:
                        # 检测到是保存过的人脸,显示出persion:ID 置信度
                        img.draw_string(0, 195, "persion:%d,score:%2.1f" %(index, max_score), color=(0, 255, 0), scale=2)
                        recog_flag = True
                    else:
                        # 未识别到是保存过的人脸,显示出unregistered 置信度
                        img.draw_string(0, 195, "unregistered,score:%2.1f" %(max_score), color=(255, 0, 0), scale=2)
                del scores # 删除对象
    
                if start_processing: # 检测到按键BOOT按下
                    record_ftrs.append(feature) # 记录特征
                    print("record_ftrs:%d" % len(record_ftrs)) # 打印人脸记录次数
                    start_processing = False # 按键BOOT关
    
                if recog_flag:
                    # 识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 绿色框
                    img.draw_rectangle(l[0],l[1],l[2],l[3], color=(0, 255, 0))
                    recog_flag = False
                else:
                    # 未识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 白色框
                    img.draw_rectangle(l[0],l[1],l[2],l[3], color=(255, 255, 255))
                del (face_cut_128) # 删除对象
                del (face_cut) # 删除对象
    
        img.draw_string(0, 0, "%2.1ffps" %(fps), color=(0, 60, 255), scale=2.0) # 显示屏显示FPS
        #显示字符串press boot key to regist face(0, 215)
        img.draw_string(0, 215, "press boot key to regist face", color=(255, 100, 0), scale=2.0)
        lcd.display(img)
    
    # 创建的kpu对象去初始化,释放模型内存, 立即释放
    kpu.deinit()
    ld5_kpu.deinit()
    fea_kpu.deinit()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    实验分析:

    BOOT保存人脸信息后进行人脸检测:可以看出绿色框标记出了人脸,屏幕显示出了人员标签号、相似度分数信息

    在这里插入图片描述

    在这里插入图片描述


    为什么两个不同的人会识别为同一个人???: 为什么两个明显不同的人会识别成功为一个人呢?,我们对比结果分析得出结论,都显示为标签信息2,但是相似度分数却相差较大,前者相似度分数97.5,后者相似度分数82.4。所以设定合适的相似度分数阈值是非常重要的

    在这里插入图片描述

    在这里插入图片描述


    未保存前与保存后识别对比: 未保存人脸信息时,我们识别人脸会检测到相似度的分数较低(69.5),低于设定的阈值(80.5),显示为白色图框,显示不能辨别字样;按键BOOT保存人脸信息后,我们识别人脸会检测到相似度的分数较高(99.5),低于设定的阈值(80.5),显示为绿色图框,显示识别到的标签信息;

    在这里插入图片描述

    在这里插入图片描述


    可以同时检测多个人脸: 可以将各自的标签与相似度分数显示出来,由于未开发此功能,所以显示重叠,大家可以修改开发:若有多个标签,将显示的标签分别打印在不同的位置

    在这里插入图片描述


    文件夹文件:由于此实验需要BOOT按键来保存人脸信息,所以不能在Canmv IDE运行人脸识别代码,我们需要将写的Python文件重命名为main.py下载到我们的开发板中,参考上一节Canmv K210开发板文件下载——将程序烧录到Canmv Cam点击下载修改过的主程序main.py

    在这里插入图片描述


    串口打印数据: 分析串口打印的数据,我们未保存人脸信息之前,剩余内存信息(栈):665472、剩余内存信息(堆):430080;保存一张人脸信息之后:剩余内存信息(栈):657216、剩余内存信息(堆):430080;对比可以得到结论:我们按键BOOT保存的图片信息会保存在栈内存,一张人脸的数据用掉了堆内存8256字节
    在这里插入图片描述


    实验过程与总结:

    实验过程:

    • 在Canmv IDE环境下实现人脸识别功能,首先需要导入必要的库,并初始化K210开发板上的摄像头和LCD显示屏。接着编写程序以启动摄像头显示图像,并利用人脸识别算法检测图像中的人脸。检测到人脸之后,若按键BOOT触发中断检测人脸特征信息,对人脸进行编号并存储其相关信息。

    • 在后续的识别过程中,系统会将当前检测到的人脸与已保存的人脸数据库进行比对。若检测的相似度大于设定的相似度分数阈值,即识别出之前编号的人脸,系统会用不同颜色的框标记该人脸,还会在框旁显示其序号和识别的置信度

    • 点击下载需要用到的模型

    总结:

    • 在Canmv IDE环境下,通过导入关键库并初始化K210开发板的摄像头和LCD显示屏,实现了人脸识别系统。该系统能检测图像中的人脸,通过按键保存人脸数据并进行编号。再次识别时,系统将匹配并标记已知人脸,展示其序号和置信度,增强了人脸检测的交互性和个性化。此处检测人脸的阈值:THRESHOLD=80.5,如果需要识别人脸更加准确,可以适当调整阈值。

    完整代码展示:

    # Face_recog - By: Jack - 周三 4月 24 2024
    
    import sensor, image, time, lcd
    import gc                          # 内存管理模块
    from maix import KPU               # 加速AI计算
    from maix import GPIO, utils       # 使用Pin脚的GPIO功能、基础功能
    from fpioa_manager import fm       # 管理内部功能和引脚映射关系的功能模块
    from board import board_info       # 板子board_info类
    
    
    lcd.init()  # 初始化LCD显示屏
    sensor.reset()  # 复位并初始化摄像头
    sensor.set_pixformat(sensor.RGB565)  # 设置摄像头输出格式为 RGB565
    sensor.set_framesize(sensor.QVGA) # 设置摄像头输出大小为 QVGA (320x240)
    sensor.skip_frames(time = 1000) # 等待摄像头稳定
    clock = time.clock() # 创建一个clock对象,用来计算帧率
    
    feature_img = image.Image(size=(64,64), copy_to_fb=False) # 人脸特性图像为64*64图输入,这里初始化一个image
    feature_img.pix_to_ai()  # 对rgb565的image生成ai运算需要的r8g8b8格式存储
    
    # 标准人脸关键点坐标
    FACE_PIC_SIZE = 64
    dst_point =[(int(38.2946 * FACE_PIC_SIZE / 112), int(51.6963 * FACE_PIC_SIZE / 112)),
                (int(73.5318 * FACE_PIC_SIZE / 112), int(51.5014 * FACE_PIC_SIZE / 112)),
                (int(56.0252 * FACE_PIC_SIZE / 112), int(71.7366 * FACE_PIC_SIZE / 112)),
                (int(41.5493 * FACE_PIC_SIZE / 112), int(92.3655 * FACE_PIC_SIZE / 112)),
                (int(70.7299 * FACE_PIC_SIZE / 112), int(92.2041 * FACE_PIC_SIZE / 112)) ]
    
    # 人脸检测锚点;锚点参数与模型参数一致,同一个模型这个参数是固定的(即训练模型时确定了)
    anchor = (0.1075, 0.126875, 0.126875, 0.175, 0.1465625, 0.2246875, 0.1953125, 0.25375, 0.2440625, 0.351875, 0.341875, 0.4721875, 0.5078125, 0.6696875, 0.8984375, 1.099687, 2.129062, 2.425937)
    kpu = KPU()# 创建一个kpu对象,用于人脸检测
    kpu.load_kmodel("/sd/KPU/yolo_face_detect/face_detect_320x240.kmodel") # 导入人脸检测模型
    # yolo2初始化
    kpu.init_yolo2(anchor, anchor_num=9, img_w=320, img_h=240, net_w=320 , net_h=240 ,layer_w=10 ,layer_h=8, threshold=0.7, nms_value=0.2, classes=1)
    
    
    ld5_kpu = KPU()# 创建一个kpu对象,用于人脸5关键点检测
    print("ready load model")
    ld5_kpu.load_kmodel("/sd/KPU/face_recognization/ld5.kmodel") # 导入人脸5关键点检测模型
    
    fea_kpu = KPU()# 创建一个kpu对象,用于人脸属性检测
    print("ready load model")
    fea_kpu.load_kmodel("/sd/KPU/face_recognization/feature_extraction.kmodel") # 导入人脸属性检测模型
    
    start_processing = False
    BOUNCE_PROTECTION = 50 # 定义外部中断时间为50ms
    
    fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0) # 将IO16配置为GPIOHS0
    key_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN) # 配置GPIOHS0为输入
    def set_key_state(*_): # 配置外部中断及中断回调函数,配置GPIOHS0为输入
        global start_processing # 发送数据
        start_processing = True
        time.sleep_ms(BOUNCE_PROTECTION)
    # 配置GPIOHS0上升沿触发中断
    key_gpio.irq(set_key_state, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)
    
    
    record_ftrs = [] #空列表 用于存储按键记录下的人脸特征的数组
    THRESHOLD = 80.5   # 人脸识别相似度分数阈值为80.5,需要精准识别可以上调
    recog_flag = False  # 表示检测到的人脸是否已识别的状态 ,已识别为True,未识别为Flase
    
    def extend_box(x, y, w, h, scale):  # 目标检测中的边界框(x,y,w,h形式转换与绘制)
        x1_t = x - scale*w
        x2_t = x + w + scale*w
        y1_t = y - scale*h
        y2_t = y + h + scale*h
        x1 = int(x1_t) if x1_t>1 else 1
        x2 = int(x2_t) if x2_t<320 else 319
        y1 = int(y1_t) if y1_t>1 else 1
        y2 = int(y2_t) if y2_t<240 else 239
        cut_img_w = x2-x1+1
        cut_img_h = y2-y1+1
        return x1, y1, cut_img_w, cut_img_h
    
    
    while True:
        gc.collect() # 内存管理:手动启动垃圾收集
        print("mem free:",gc.mem_free()) #查看剩余内存信息(栈)
        print("heap free:",utils.heap_free())  #查看剩余内存信息(堆)
        clock.tick() # 更新计算帧率的clock
        img = sensor.snapshot() # 拍照,获取一张图像
        kpu.run_with_output(img) # 对输入图像进行kpu运算
        dect = kpu.regionlayer_yolo2() # yolo2后处理
        fps = clock.fps() # 获取帧率
        if len(dect) > 0:
            for l in dect :
                # 对检测到的人脸框裁剪
                x1, y1, cut_img_w, cut_img_h= extend_box(l[0], l[1], l[2], l[3], scale=0)
                face_cut = img.cut(x1, y1, cut_img_w, cut_img_h) # 从img中裁剪出人脸图
                face_cut_128 = face_cut.resize(128, 128) # 对人脸图调整大小到128*128
                face_cut_128.pix_to_ai() # 对rgb565格式的128人脸图生成ai运算需要的rgb888格式存储
                out = ld5_kpu.run_with_output(face_cut_128, getlist=True) # kpu运算并获取结果
                face_key_point = []
                for j in range(5): # 根据结果算出人脸5关键点,并在图中标出
                    x = int(KPU.sigmoid(out[2 * j])*cut_img_w + x1)
                    y = int(KPU.sigmoid(out[2 * j + 1])*cut_img_h + y1)
                    face_key_point.append((x,y))
                T = image.get_affine_transform(face_key_point, dst_point) #由关键点算出变换矩阵
                image.warp_affine_ai(img, feature_img, T) # 相似变换修正人脸图
                feature = fea_kpu.run_with_output(feature_img, get_feature = True)  # 检测人脸属性获取结果
                del face_key_point # 删除对象
    
                scores = []
                for j in range(len(record_ftrs)):
                    score = kpu.feature_compare(record_ftrs[j], feature) #  特征比对,比对两个特征数据并给出相似度分数
                    scores.append(score) # 将新元素score附加到数组的末尾,使其增长
                if len(scores):
                    max_score = max(scores) # 取最大相似度分数
                    index = scores.index(max_score)
                    if max_score > THRESHOLD:
                        # 检测到是保存过的人脸,显示出persion:ID 置信度
                        img.draw_string(0, 195, "persion:%d,score:%2.1f" %(index, max_score), color=(0, 255, 0), scale=2)
                        recog_flag = True
                    else:
                        # 未识别到是保存过的人脸,显示出unregistered 置信度
                        img.draw_string(0, 195, "unregistered,score:%2.1f" %(max_score), color=(255, 0, 0), scale=2)
                del scores # 删除对象
    
                if start_processing: # 检测到按键BOOT按下
                    record_ftrs.append(feature) # 记录特征
                    print("record_ftrs:%d" % len(record_ftrs)) # 打印人脸记录次数
                    start_processing = False # 按键BOOT关
    
                if recog_flag:
                    # 识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 绿色框
                    img.draw_rectangle(l[0],l[1],l[2],l[3], color=(0, 255, 0))
                    recog_flag = False
                else:
                    # 未识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 白色框
                    img.draw_rectangle(l[0],l[1],l[2],l[3], color=(255, 255, 255))
                del (face_cut_128) # 删除对象
                del (face_cut) # 删除对象
    
        img.draw_string(0, 0, "%2.1ffps" %(fps), color=(0, 60, 255), scale=2.0) # 显示屏显示FPS
        #显示字符串press boot key to regist face(0, 215)
        img.draw_string(0, 215, "press boot key to regist face", color=(255, 100, 0), scale=2.0)
        lcd.display(img)
    
    # 创建的kpu对象去初始化,释放模型内存, 立即释放
    kpu.deinit()
    ld5_kpu.deinit()
    fea_kpu.deinit()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
  • 相关阅读:
    个人练习-PAT甲级-1143 Lowest Common Ancestor
    坏消息:AI会写代码,好消息:客户说不清需求
    Redis在Windows和Linux下的安装方法(超级详细)
    计算整数列表中的中位数
    字体锯齿问题
    spacy.load(“en_core_web_trf“)报错TypeError: issubclass() arg 1 must be a class
    DP83TG720SWRHARQ1 IC TRANSCEIVER 接口芯片、TCAN1051VDRBTQ1
    人保爱无忧易核版重疾险怎么样?好不好?
    大话超越菜鸟C#的实践入门进阶必知点,深入浅出解析 32 算法入门 循环和递归
    vue之计算属性,属性侦听器,自定义属性,生命周期函数,简单的讲解一下组件
  • 原文地址:https://blog.csdn.net/qq_45780647/article/details/138161894