草庐IT

基于树莓派的人脸识别门禁系统

F l e 2024-05-27 原文

一、树莓派Opencv以及扩展模块的安装

1、概述:本次在树莓派上安装Opencv及其扩展模块,考虑到树莓派的SD卡容量和内存的限制,不采用直接pip安装方法,而采用编译Opencv源码的方式进行安装。
2、遇到的问题及解决方法

遇到的问题解决方法
缺少”cuda.hpp”将/home/pi/opencv_contrib3.4.1/modules/xfeatures2d/include/opencv2下的xfeatures2d文件夹复制到home/pi/opencv-3.4.1/modules/stitching/include/opencv2下
缺少”bosstdesc_bgm.i”下载对应的文件到opencv_contrib/modules/xfeatures2d/src下
运行至99%时树莓派卡死原本采用make -j4进行源码编译加速,但是多次尝试仍然卡死,之后采用make解决了问题,可能原因是make -j4所需的交换空间太大导致卡死。

3、运行结果

导入opencv库没有问题,说明安装成功。

二、树莓派人脸检测

1、概述:本次在树莓派上检测人脸用Opencv自带的Haar特征分类器。
2、代码编写:
将.xml文件拷贝到mu_code文件夹下,在mu_code下编写代码,则工程的根目录默认在mu_code。

import cv2
cap=cv2.VideoCapture(0)
cascadePath = "/home/pi/opencv-3.4.3/data/haarcascades/haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath)
while (True):
    ret, img = cap.read()
    # 灰度化处理
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = faceCascade.detectMultiScale(gray, 1.3, 5)
    if len(faces)!=0:
        x = faces[0][0]
        y = faces[0][1]
        w = faces[0][2]
        h = faces[0][3]
        cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
    cv2.imshow("fle",img)
    cv2.waitKey(10)

3、运行结果:

由图中可以看出可以正确检测出人脸。

三、树莓派人脸识别

1、概述:
本次在树莓派上进行人脸识别采用OpenCV人脸识别类LBPHFaceRecognizer。
2、代码编写:

import cv2
from PIL import Image
import numpy as np
imagePath="face.png"
img_face=cv2.imread(imagePath,0)
cv2.imshow("fle",img_face)
cv2.waitKey(0)
recognizer = cv2.face.LBPHFaceRecognizer_create()
PIL_img = Image.open(imagePath).convert('L')
img_numpy = np.array(PIL_img, 'uint8')
faces=[]
ids=[]
faces.append(img_numpy)
ids.append(2)
recognizer.train(faces, np.array(ids))
id, confidence = recognizer.predict(img_face)
print("id=",id)
print("confidence=",confidence)

3、运行结果:


人脸图片用于训练,用于训练的id是2,这里只是为了检测代码的正确性,所以依然用这张图片是做识别,识别的结果是id=2,confidence≈9.8。Id号识别正确,confidence接近于0,说明识别率很高。

四、树莓派利用双色LED模拟开关门动作

1、概述:
设计一个双色LED类,该类应该能够向外提供两个方法,分别设置红灯和绿灯的PWM。
这里在主函数调用是都是设置为满PWM。
2、接线

树莓派3mm双色LED模块
GND负极(-)
P17红色灯正极(中间引脚)
P18绿色灯正极

3、代码编写

import RPi.GPIO as GPIO
import time
class Double_LED_Class:
    def __init__(self):  # double_led 初始化工作
        makerobo_pins = (11, 12)  # PIN管脚字典
        GPIO.setmode(GPIO.BOARD)  # 采用实际的物理管脚给GPIO口
        GPIO.setwarnings(False)  # 去除GPIO口警告
        GPIO.setup(makerobo_pins, GPIO.OUT)  # 设置Pin模式为输出模式
        GPIO.output(makerobo_pins, GPIO.LOW)  # 设置Pin管脚为低电平(0V)关闭LED
        self.p_R = GPIO.PWM(makerobo_pins[0], 2000)  # 设置频率为2KHz
        self.p_G = GPIO.PWM(makerobo_pins[1], 2000)  # 设置频率为2KHz
        # 初始化占空比为0(led关闭)
        self.p_R.start(0)
        self.p_G.start(0)
    def makerobo_pwm_map(self,x, in_min, in_max, out_min, out_max):
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
    def makerobo_set_red_Color(self,col):  # 例如:col = 0x1122
        # 把0-255的范围同比例缩小到0-100之间
        R_val = self.makerobo_pwm_map(col, 0, 255, 0, 100)
        self.p_R.ChangeDutyCycle(R_val)  # 改变占空比
    def makerobo_set_green_Color(self,col):  # 例如:col = 0x1122
        # 把0-255的范围同比例缩小到0-100之间
        G_val = self.makerobo_pwm_map(col, 0, 255, 0, 100)
        self.p_G.ChangeDutyCycle(G_val)  # 改变占空比
    # 释放资源
    def makerobo_destroy(self):
        self.p_G.stop()
        self.p_R.stop()
        GPIO.output(self.makerobo_pins, GPIO.LOW)  # 关闭所有LED
        GPIO.cleanup()  # 释放资源

# 测试用例
if __name__ == "__main__":
    Hardware_double_led=Double_LED_Class()
    Hardware_double_led.makerobo_set_red_Color(200)
    time.sleep(3)#显示红灯3s后显示绿灯
    Hardware_double_led.makerobo_set_red_Color(0)
    Hardware_double_led.makerobo_set_green_Color(200)

4、运行结果

五、树莓派门禁系统界面设计与整体逻辑代码整合

1、概述:
树莓派门禁系统总共包括4个界面设计,分别是人脸识别开门界面、管理员登录界面、人脸录入界面、人脸数据库展示界面。这四个界面都用PyQt5进行设计,先在Window上用Qt Designer搭建界面,用Python编写逻辑关系,最后移植到树莓派上,树莓派上只需要安装PyQt库即可运行程序。
下面为四个界面的展示图:




界面控件的逻辑关系如下图:

2、代码编写:
代码思路:

创建了四个界面类,分别继承于用Qt Designer创建 的四个界面类,这样做是为了能够在更改界面的时候不会改变逻辑部分代码。另外创建了一个数据库操作类,主要是为了能够查询,读写数据库,这里采用的是SQlite数据库。
Main文件:

#########################################
#Author:郭先达
#date:2021.12.22
#######################################33
import sys
import cv2
import threading
from PyQt5.QtCore import QBasicTimer
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QLineEdit, QGridLayout, QMessageBox, QGroupBox
from PyQt5 import QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from PIL import Image
import numpy as np
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QPalette, QBrush, QPixmap
from PyQt5.QtSql import *
import time
from double_led import Double_LED_Class
import os

from MainWindow import Ui_Dialog as Ui_Dialog_MainWindow
from Admin_enter import Ui_Dialog as Ui_Dialog_Admin_enter
from Face_rec import Ui_Dialog as Ui_Dialog_Face_rec
from SQliteWindow import Ui_Dialog as Ui_Dialog_SQliteWindow

# 导入OpenCV自带的数据集,定义多个是因为在后面有多次调用,用一个的话会报错
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade1 = cv2.CascadeClassifier(cascadePath)
faceCascade2= cv2.CascadeClassifier(cascadePath)
faceCascade3 = cv2.CascadeClassifier(cascadePath)
faceCascade4=cv2.CascadeClassifier(cascadePath)

#人脸识别开门界面
class Fle_MainWindow(QDialog,Ui_Dialog_MainWindow):
    def __init__(self):
        super(Fle_MainWindow,self).__init__()
        self.setupUi(self)
        # 创建定时器,定时器用来定时拍照
        self.timer_camera = QtCore.QTimer()
        self.user = []
        #进入人脸识别开门界面之前先把所有数据训练一遍,以满足新数据的录入
        self.recognizer = cv2.face.LBPHFaceRecognizer_create()
        faces, ids = self.getImagesAndLabels("./Face_data")#拍摄的照片放在这个文件夹下
        self.recognizer.train(faces, np.array(ids))#开始训练

        self.font = cv2.FONT_HERSHEY_SIMPLEX
        #摄像头初始化
        self.camera_init()
        #绑定函数show_camera
        self.timer_camera.timeout.connect(self.show_camera)
        #30ms拍一次照片
        self.timer_camera.start(30)

        # 点击管理员按钮事件
        self.pushButton_administrators.clicked.connect(self.slot_btn_admin)
        #新建一个双色LED类
        self.Hardware_double_led=Double_LED_Class()

    # 函数获取图像和标签数据
    def getImagesAndLabels(self,path):
        imagePaths = [os.path.join(path, f) for f in os.listdir(path)]
        faceSamples = []#人脸数据
        ids = []#人脸对应的id标签
        for imagePath in imagePaths:
            # 转换为灰度
            PIL_img = Image.open(imagePath).convert('L')
            img_numpy = np.array(PIL_img, 'uint8')
            #print(imagePath),打印出具体信息,怎么分割就怎么设置序号
            id = int(imagePath.split("/")[2].split(".")[1])
            faces = faceCascade3.detectMultiScale(img_numpy)
            for (x, y, w, h) in faces:
                faceSamples.append(img_numpy[y:y + h, x:x + w])
                ids.append(id)
        print(ids)
        return faceSamples, ids

    def camera_init(self):
        # 打开设置摄像头对象
        self.cap = cv2.VideoCapture(0)
        self.__flag_work = 0
        self.x = 0
        self.count = 0
        #这里的minW和minH用于限制人脸的最小宽高,防止误测
        self.minW = 0.2 * self.cap.get(3)#视频流的帧宽度
        self.minH = 0.2 * self.cap.get(4)#视频流的帧高度

    def show_camera(self):
        flag, self.image = self.cap.read()
        # 将图片变化成灰度图
        gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        # 探测图片中的人脸
        faces = faceCascade1.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=5,
            minSize=(int(self.minW), int(self.minH)),
        )
        # 判断是否检测到人脸,检测到设置为绿灯,没检测到设置为红灯
        if len(faces)!=0:
            #下面这个操作是为了能够只检测到一张脸,检测面积最大的那个脸
            WxH_max=0 #人脸矩形的面积
            WxH_max_face=faces[0]
            for i in faces:
                if(i[2]*i[3]>WxH_max):
                    WxH_max=i[2]*i[3]
                    WxH_max_face=i
            # 围绕脸的框
            x=WxH_max_face[0]
            y = WxH_max_face[1]
            w = WxH_max_face[2]
            h = WxH_max_face[3]
            cv2.rectangle(self.image, (x, y), (x + w, y + h), (0, 255, 0), 2)
            #用recognizer进行识别判断
            id, confidence = self.recognizer.predict(gray[y:y + h, x:x + w])
            # 对置信度进行判断,这里设定为70
            if (confidence < 70):
                confidence = "  {0}%".format(round(100 - confidence))
                for i in range(0,mysqlite.get_rows()):
                    if mysqlite.find_data(i,0)==id:
                        self.label_ID.setText(str(mysqlite.find_data(i,1)))
                        self.label_name.setText(str(mysqlite.find_data(i,2)))
                self.Hardware_double_led.makerobo_set_red_Color(0)
                self.Hardware_double_led.makerobo_set_green_Color(100)
            else:
                confidence = "  {0}%".format(round(100 - confidence))
                self.label_ID.setText("不认识")
                self.label_name.setText("不认识")
                self.Hardware_double_led.makerobo_set_red_Color(100)
                self.Hardware_double_led.makerobo_set_green_Color(0)
            # 给图片添加文本 图片矩阵, 添加文本名称, 设置文本显示位置,
            # 字体样式, 字体大小, 字体颜色, 字体粗细
            cv2.putText(self.image, str(id), (x + 5, y - 5), self.font, 1, (255, 255, 255), 2)
            cv2.putText(self.image, str(confidence), (x + 5, y + h - 5), self.font, 1, (255, 255, 0), 1)
        else:
            self.Hardware_double_led.makerobo_set_red_Color(0)
            self.Hardware_double_led.makerobo_set_green_Color(0)
        # 将视频显示在了label上
        show = cv2.resize(self.image, (640, 480))
        show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
        showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888)
        self.lab_face.setPixmap(QtGui.QPixmap.fromImage(showImage))


    # 点点击管理员按钮事件
    def slot_btn_admin(self):
        #应该在释放摄像头之前先关闭定时器
        self.timer_camera.stop()
        self.cap.release()
        self.logon = Fle_Admin_enter()
        self.logon.show()
        self.hide()
        #其他界面不用双色LED模拟开关门,把灯灭掉
        self.Hardware_double_led.makerobo_set_red_Color(0)
        self.Hardware_double_led.makerobo_set_green_Color(0)

# 管理员登录界面
class Fle_Admin_enter(QDialog,Ui_Dialog_Admin_enter):
    def __init__(self):
        super(Fle_Admin_enter, self).__init__()
        self.setupUi(self)

        #将输入信息初始化为空
        self.lineEdit_admin_ID.setText("")
        self.lineEdit_admin_key.setText("")
        #设置密码为隐藏方式显示
        self.lineEdit_admin_key.setEchoMode(QLineEdit.Password)

        # lineEdit改变事件
        self.lineEdit_admin_ID.textEdited[str].connect(self.changeEdit_ID)
        self.lineEdit_admin_key.textEdited[str].connect(self.changeEdit_key)

        # 点击返回按钮事件
        self.pushButton_admin_back.clicked.connect(self.slot_btn_back)
        # 点击登录按钮事件
        self.pushButton_admin_enter.clicked.connect(self.slot_btn_logon)


    # 点击Edit_ID事件
    def changeEdit_ID(self):
        Edit_ID = self.lineEdit_admin_ID.text()
        print("Edit_ID=",Edit_ID)

    # 点击Edit_key事件
    def changeEdit_key(self):
        Edit_key = self.lineEdit_admin_key.text()
        print("Edit_ID=",Edit_key)

    # 点击返回按钮事件
    def slot_btn_back(self):
        self.menu = Fle_MainWindow()
        self.menu.show()
        self.hide()

    # 点击登录按钮事件
    def slot_btn_logon(self):
        # 判断账号和密码是否输入正确
        print(self.lineEdit_admin_ID.text)
        print(self.lineEdit_admin_key.text)
        #这里设置管理员的账号和密码,这里都设置为1
        if self.lineEdit_admin_ID.text() == "1" and self.lineEdit_admin_key.text() == "1":
            self.manager_face = Fle_Face_rec()
            self.manager_face.show()
            self.hide()
            #print("enter Ui_manager_face")
        else:
            QMessageBox.warning(self, "提示", "账号或密码错误!", QMessageBox.Close)

#人脸录入界面
class Fle_Face_rec(QDialog,Ui_Dialog_Face_rec):
    def __init__(self):
        super(Fle_Face_rec, self).__init__()
        self.setupUi(self)
        # 初始化为空
        self.lineEdit_ID.setText("")
        self.lineEdit_name.setText("")

        # 初始化进度条定时器
        self.timer = QBasicTimer()
        self.step = 0
        # 创建定时器
        self.timer_camera = QtCore.QTimer()
        # 初始化摄像头数据
        self.camera_init()
        # 定时器函数,用来显示人脸检测结果,并不录入数据
        self.timer_camera.timeout.connect(self.show_camera)
        self.timer_camera.start(30)

        self.pushButton_begin_rec.clicked.connect(self.slot_btn_enter)
        self.pushButton_back.clicked.connect(self.slot_btn_back)
        self.pushButton_show_sqlite.clicked.connect(self.show_sqlitedata)
    # 初始化摄像头数据
    def camera_init(self):
        self.cap = cv2.VideoCapture(0)
        self.__flag_work = 0
        self.x =0
        self.count = 0
        #设置分辨率为640*480
        self.cap.set(4,640)
        self.cap.set(3,480)

    # 点击返回按键返回上一界面
    def slot_btn_back(self):
        self.timer_camera.stop()
        self.cap.release()
        self.logon = Fle_MainWindow()
        self.logon.show()
        self.hide()

    def show_camera(self):
        flag, self.image = self.cap.read()
        gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        faceCascade2 = cv2.CascadeClassifier(cascadePath);
        faces = faceCascade2.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=5,
            minSize=(200, 200)
        )
        if len(faces)!=0:
            WxH_max = 0
            WxH_max_face = faces[0]
            for i in faces:
                if (i[2] * i[3] > WxH_max):
                    WxH_max = i[2] * i[3]
                    WxH_max_face = i
            # 围绕脸的框
            x = WxH_max_face[0]
            y = WxH_max_face[1]
            w = WxH_max_face[2]
            h = WxH_max_face[3]

            cv2.rectangle(self.image, (x, y), (x + w, y + h), (255, 0, 0), 2)
            roi_gray = gray[y:y + h, x:x + w]
            roi_color = self.image[y:y + h, x:x + w]

        # 将视频显示在了label上
        show = cv2.resize(self.image, (640, 480))
        show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
        showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888)
        self.label_face.setPixmap(QtGui.QPixmap.fromImage(showImage))

    # 点击按钮开启人脸录入线程
    def slot_btn_enter(self):
        self.count = 0
        # 创建线程并开启
        self.thread = threading.Thread(target=self.thread_pic)
        self.thread.start()
        # 开启进度条定时器
        self.timer.start(100, self)

    # 加载进度条
    def timerEvent(self, e):
        self.progressBar.setValue(self.count)


    # 录入人脸线程
    def thread_pic(self):
        tip="正在录入"+str(self.lineEdit_ID.text())+str(self.lineEdit_name.text())+"的人脸!!"
        print(tip)
        # 创建目录,将获取的人脸照片放入指定的文件夹
        self.file = "./Face_data"
        file_ID=str(self.lineEdit_ID.text())
        while (True):
            # 灰度化处理
            gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
            faces = faceCascade4.detectMultiScale(gray, 1.3, 5)
            if len(faces)!=0:
                WxH_max = 0
                WxH_max_face = faces[0]
                for i in faces:
                    if (i[2] * i[3] > WxH_max):
                        WxH_max = i[2] * i[3]
                        WxH_max_face = i
                # 围绕脸的框
                x = WxH_max_face[0]
                y = WxH_max_face[1]
                w = WxH_max_face[2]
                h = WxH_max_face[3]

                self.count += 1
                # 将捕获的图像命名为User.0.1.png,其中0是id号
                print(self.file + "/User." + file_ID + '.' + str(self.count) + ".png")
                bool = cv2.imwrite(self.file + "/User." + file_ID + '.' + str(self.count) + ".png",
                                   gray[y:y + h, x:x + w])

                # 取100张人脸样本,停止录像
                if self.count >= 100:
                    print("人脸数据采集已完成!")
                    break
        #将数据存入数据库
        mysqlite.add_row(self.lineEdit_ID.text(),self.lineEdit_xuehao.text(),str(self.lineEdit_name.text()))


    def show_sqlitedata(self):
        self.logon = Fle_SQliteWindow()
        self.logon.show()
        #self.hide()

#人脸数据库展示界面
class Fle_SQliteWindow(QDialog,Ui_Dialog_SQliteWindow):
    def __init__(self):
        super(Fle_SQliteWindow,self).__init__()
        self.setupUi(self)
        self.tableView.setModel(mysqlite.model)
        self.pushButton_add.clicked.connect(self.addrow)
        self.pushButton_delete.clicked.connect(lambda: mysqlite.model.removeRow(self.tableView.currentIndex().row()))
    def addrow(self):
        # 不是在QTableView上添加,而是在模型上添加,会自动将数据保存到数据库中!
        # 参数一:数据库共有几行数据  参数二:添加几行
        ret = mysqlite.model.insertRows(mysqlite.model.rowCount(), 1)  # 返回是否插入
        print('数据库共有%d行数据' % mysqlite.model.rowCount())
        print('insertRow=%s' % str(ret))


#数据库操作类
class Fle_Sqlite():
    def __init__(self):
        self.db = QSqlDatabase.addDatabase('QSQLITE')
        self.db.setDatabaseName('./people_data.db')
        if not self.db.open():
            print('无法建立与数据库的连接')
        query = QSqlQuery()
        query.exec('create table people(id varcahr(10),xuehao varcahr(15),name varcahr(50))')
        self.model = QSqlTableModel()  # MVC模式中的模型
        # 初始化将数据装载到模型当中
        self.initializeModel()

    # 初始化
    def initializeModel(self):
        self.model.setTable('people')
        # 当字段变化时会触发一些事件
        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
        # 将整个数据装载到model中
        self.model.select()
        # 设置字段头
        #只是设置字段头,不是显示
        self.model.setHeaderData(0, Qt.Horizontal, 'ID')
        self.model.setHeaderData(1, Qt.Horizontal, 'xuehao')
        self.model.setHeaderData(2, Qt.Horizontal, 'name')

    #找指定位置的数据
    def find_data(self, row, col):
        # 序号从0开始
        index = self.model.index(row, col)
        return self.model.data(index)

    #新加一行
    def add_row(self,ID,xuehao,name):
        row = self.model.rowCount()
        self.model.insertRow(row)  
        self.model.setData(self.model.index(row, 0), ID)  
        self.model.setData(self.model.index(row, 1), xuehao)  
        self.model.setData(self.model.index(row, 2), name)  
        self.model.submitAll()
        print(ID)
        print(xuehao)
        print(name)

    #删除最后一行
    def del_row(self):
        row = self.model.rowCount()-1
        self.model.removeRow(row)
        self.model.submitAll()

    def get_rows(self):
        #print(self.model.rowCount())
        return self.model.rowCount()


if __name__ == "__main__":
    mysqlite=Fle_Sqlite()
    app = QApplication(sys.argv)
    w = Fle_MainWindow()
    w.show()
    sys.exit(app.exec_())

3、运行结果:


由上面两图可以看出识别效果正确。

识别出人脸时亮绿灯。


由上图可知,显示“不认识”时亮红灯。

六、总结与体会

①本次实验采用的LBPH人脸识别模型精度欠缺,受光线影响非常严重,或许可以通过摄像头加红外滤光片外加红外补光灯解决。
②本次实验中多次遇到摄像头调用打开不了导致imread出错的情况,具体原因没有找到,猜测是摄像头的序列号改变了。
③本次实验中创建了多个界面类,而人脸识别界面类和人脸录入界面类都需要调用摄像头,导致了摄像头经常报错,所以在界面切换的时候关掉了摄像头,在界面初始化的时候又打开了摄像头,但是这样做有时也会造成摄像头来不及释放而报错。
④对于数据库的操作,卡了很长的时间才分清楚数据库和数据表的区别,最后才搞清楚读写操作都是对链接到数据库的数据表操作。

附录

代码:https://gitee.com/guoxianda/face-recognition-base-on-Raspberry-pie

有关基于树莓派的人脸识别门禁系统的更多相关文章

  1. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  2. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  3. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  4. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  5. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. ruby-on-rails - 在 heroku 的 .fonts 文件夹中包含自定义字体,似乎无法识别它们 - 2

    Heroku支持人员告诉我,为了在我的Web应用程序中使用自定义字体(未安装在系统中,您可以在bash控制台中使用fc-list查看已安装的字体)我必须部署一个包含所有字体的.fonts文件夹里面的字体。问题是我不知道该怎么做。我的意思是,我不知道文件名是否必须遵循heroku的任何特殊模式,或者我必须在我的代码中做一些事情来考虑这种字体,或者如果我将它包含在文件夹中它是自动的......事实是,我尝试以不同的方式更改字体的文件名,但根本没有使用该字体。为了提供更多详细信息,我们使用字体的过程是将PDF转换为图像,更具体地说,使用rghostgem。并且最终图像根本不使用自定义字体。在

  8. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  9. ruby-on-rails - 没有这样的文件或目录 - 用 Mini Magick 识别 - 2

    在我让另一个人重做我的前端UI之前,我的Rails应用程序运行平稳。我已经尝试解决此错误3天了。这是错误:Nosuchfileordirectory-identifyExtractedsource(aroundline#59):575859606162@post=Post.find(params[:id])authorize@postif@post.update_attributes(post_params)flash[:notice]="Postwasupdated."redirect_to[@topic,@post]else{"utf8"=>"✓","_method"=>"patc

  10. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

随机推荐