基于Dlib的人脸识别客户端(UI界面)

简介: 基于Dlib的人脸识别客户端(UI界面)

需要源码的朋友可私信我!!!!

客户端界面

代码思路

1、开发环境

  • 开发平台:win10
  • 开发软件:PyCharm
  • 界面开发:PyQt5

2、文档说明

  • face_lib文件夹
  1. align_dlib.py文件:主要进行人脸对齐。
  2. face_recg.py文件: 进行人脸识别,其中阈值为0.4,可根据相应情况进行修改。
  3. my.api.py文件: 自己写的各种函数方法。
  4. udp_recv.py文件:包含进行udp协议传输视频的类。
  • faces文件夹 每一个文件夹名字必须是英文字母,代表一个类,其每一个类别中可以有多张图片,但数量过多,识别过慢。图片必须是96*96大小的经过对齐的jpg格式图片。
  • model文件夹 存放你训练的模型。
  • gui.py文件 一些界面相关的函数。
  • inference.py文件 神经网络函数。
  • main.py文件

使用说明

  1. 点击“打开本地摄像头”按钮,在“摄像头采集信息”栏目中即会显示摄像头采集到的信息。
  2. 点击“打开网络摄像头”按钮,在“摄像头采集信息”栏目中即会显示通过WIFI传输到的视频信息。传输协议是UDP,目的是显示树莓派采集到的视频。
  3. 在打开摄像头后,点击获取人脸,左边会显示对齐后的人脸图片,大小为96*96.第一次使用时,数据集为空,在获得人脸图片后,点击新建人脸数据按钮,输入姓名,此时人脸图片保存在faces文件夹下。
  4. 当人脸图片上出现图片时,才可点击“人脸识别”按钮,点击后会在人脸图片下方显示预测的姓名和欧式距离。欧式距离,值越小,表示越相似。当距离大于一定阈值(默认为0.4),其姓名会显示为unknown。
  5. 点击“报错”功能按钮,把误识的人脸存入对应的人脸数据集中。例如:数据集中有5种人脸,分别标号为1、2、3、4、5,采集人脸其实为5号,却被误识为1、2、3、4或者unknown,这时点击“报错”按钮,把采集人脸存为5号人脸文件夹中即可。
  6. 阈值是影响人脸识别的关键因素,默认为0.4,可自行调整。
  7. 有2种模型可供下载,在model文件夹中可得下载链接
  • 此种模型在微软MS-Celeb-1M数据集上训练,在LFW上测试的准确率可达87%以上。
  • 此种模型在模型1的基础上,在LFW上继续训练,在LFW上测试的准确率可达91%以上。
  1. 可以自己训练模型

部分源码

main.py主函数文件:

import sys
import time
import cv2
import os
import numpy as np
from PyQt5 import QtWidgets, QtCore, QtGui
from face_lib import align_dlib, face_recg
from face_lib.udp_recv import UdpGetVideo
# import helpers
from gui import Ui_widget
class MyDesignerShow(QtWidgets.QWidget, Ui_widget):
    _signal = QtCore.pyqtSignal(int)
    def __init__(self):
        super(MyDesignerShow, self).__init__()
        self.timer_camera = QtCore.QTimer()   # 本地摄像头定时器
        self.timer_udp_video = QtCore.QTimer()  # UDP获取视频定时器
        self.cap = cv2.VideoCapture()         # 获得摄像头对象
        self.CAM_NUM = 0                      # 获取摄像头编号
        self.time = time                      # 获取时间对象
        self.PREDICTOR_PATH = './face_lib/shape_predictor_68_face_landmarks.dat'  # 关键点提取模型路径
        self.my_align = align_dlib.AlignDlib(self.PREDICTOR_PATH)     # 获取人脸对齐对象
        self.pix = QtGui.QPixmap()           # 获取QPixmap对象
        self.pic_show = None
        self.face_photo = None               # 人脸图片
        self.face_recog = face_recg.Recognize()  # 获取人脸识别对象
        self.setupUi(self)                          # 加载窗体
        self.btn_close.clicked.connect(self.close)   # 关闭程序
        self.btn_local_camera.clicked.connect(self.get_local_camera)   # 打开本地相机
        self.btn_web_camera.clicked.connect(self.get_udp_video)       # 打开UDP视频数据
        self.btn_get_face.clicked.connect(self.get_face)              # 得到人脸图像
        self.btn_debug.clicked.connect(self.debug)                    # 报错
        self.btn_new_face.clicked.connect(self.new_face)                # 新建人脸数据
        self.btn_face_recognize.clicked.connect(self.face_recognize)           # 人脸识别
        self.timer_camera.timeout.connect(self.show_local_camera)  # 计时结束调用show_camera()方法
        self.timer_udp_video.timeout.connect(self.show_udp_video)  # 计时结束调用show_udp_video()方法
    # 获取本地摄像头视频
    def get_local_camera(self):
        if self.timer_udp_video.isActive():      # 查询网络摄像头是否打开
            QtWidgets.QMessageBox.warning(self, u"Warning", u"请先关闭网络摄像头", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
        elif not self.timer_camera.isActive():
            flag = self.cap.open(self.CAM_NUM)
            if not flag:
                QtWidgets.QMessageBox.warning(self, u"Warning", u"请检测相机与电脑是否连接正确", buttons=QtWidgets.QMessageBox.Ok,
                                                defaultButton=QtWidgets.QMessageBox.Ok)
            else:
                self.timer_camera.start(30)     # 30ms刷新一次
                self.btn_local_camera.setText(u'关闭本地摄像头')
        else:
            self.timer_camera.stop()    # 定时器关闭
            self.cap.release()          # 摄像头释放
            self.label_camera.clear()   # 视频显示区域清屏
            self.graphicsView.show()
            self.btn_local_camera.setText(u'打开本地摄像头')
    def show_local_camera(self):
        flag, image = self.cap.read()
        self.pic_show = cv2.resize(image, (640, 480))
        self.pic_show = cv2.cvtColor(self.pic_show, cv2.COLOR_BGR2RGB)
        showimage = QtGui.QImage(self.pic_show.data, self.pic_show.shape[1], self.pic_show.shape[0], QtGui.QImage.Format_RGB888)
        self.graphicsView.close()
        self.label_camera.setPixmap(self.pix.fromImage(showimage))
    def get_udp_video(self):
        if self.timer_camera.isActive():      # 查询本地摄像头
            QtWidgets.QMessageBox.warning(self, u"Warning", u"请先关闭本地摄像头", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
        elif not self.timer_udp_video.isActive():
            self.time.sleep(1)
            self.udp_video = UdpGetVideo()  # 获取udp视频对象
            self.timer_udp_video.start(30)     # 10ms刷新一次
            self.btn_web_camera.setText(u'关闭网络摄像头')
        else:
            self.timer_udp_video.stop()    # 定时器关闭
            self.udp_video.close()         # udp视频接受关闭
            self.label_camera.clear()      # 视频显示区域清屏
            self.graphicsView.show()
            self.btn_web_camera.setText(u'打开网络摄像头')
    def show_udp_video(self):
        image = self.udp_video.receive()
        # 从内存缓存区中读取图像
        decimg = cv2.imdecode(image, 1)
        self.pic_show = cv2.resize(decimg, (640, 480))
        self.pic_show = cv2.cvtColor(self.pic_show, cv2.COLOR_BGR2RGB)
        showimage = QtGui.QImage(self.pic_show.data, self.pic_show.shape[1], self.pic_show.shape[0], QtGui.QImage.Format_RGB888)
        self.graphicsView.close()
        self.label_camera.setPixmap(self.pix.fromImage(showimage))
    def get_face(self):
        flag_cam = True
        if not self.timer_camera.isActive() and not self.timer_udp_video.isActive():      # 查询摄像头
            QtWidgets.QMessageBox.warning(self, u"Warning", u"请先打开摄像头", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
            flag_cam = False
        if flag_cam:
            pic = self.pic_show
            if pic is not None:
                # 使用dlib自带的frontal_face_detector作为我们的特征提取器
                face_align = self.my_align.align(96, pic)
                if face_align is None:
                    QtWidgets.QMessageBox.warning(self, u"Warning", u"没有检测到人脸", buttons=QtWidgets.QMessageBox.Ok,
                                                  defaultButton=QtWidgets.QMessageBox.Ok)
                else:
                    face_align = cv2.cvtColor(face_align, cv2.COLOR_RGB2BGR)  # 转为BGR图片
                    self.face_photo = face_align
                    face_align = cv2.cvtColor(face_align, cv2.COLOR_BGR2RGB)  # 转为RGB图片
                    showimage = QtGui.QImage(face_align.data, face_align.shape[1], face_align.shape[0],
                                             QtGui.QImage.Format_RGB888)
                    self.label_face.setPixmap(QtGui.QPixmap.fromImage(showimage))
            else:
                QtWidgets.QMessageBox.warning(self, u"Warning", u"没有检测到图片", buttons=QtWidgets.QMessageBox.Ok,
                                              defaultButton=QtWidgets.QMessageBox.Ok)
    def face_recognize(self):
        if self.face_photo is None:
            QtWidgets.QMessageBox.warning(self, u"Warning", u"请先获取人脸图片", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
        else:
            self.face_recog.reload_data()         # 重载人脸数据集
            names = self.face_recog.names
            if len(names) < 1:
                QtWidgets.QMessageBox.warning(self, u"Warning", u"数据集为空!", buttons=QtWidgets.QMessageBox.Ok,
                                              defaultButton=QtWidgets.QMessageBox.Ok)
            else:
                image_data = np.array(self.face_photo)
                image_data = image_data.astype('float32') / 255.0
                face_like = self.face_recog.whose_face(image_data)                    # 识别人脸
                for i in range(len(names)):
                    print('姓名:%s   欧式距离: %s' % (names[i], face_like[i]))
                    self.textEdit.append("姓名:" + str(names[i]) + "    欧式距离: " + str(face_like[i]))
                face_id, distance = self.face_recog.get_face_id(face_like)
                if face_id is not None:
                    self.label_name.setText(str(names[face_id]))
                else:
                    self.label_name.setText('unknown')
                self.label_look.setText(str(distance))
    def new_face(self):
        text, ok = QtWidgets.QInputDialog.getText(self, '英文字符!', '请输入你的英文名字:')
        if ok:
            print(text)
            if self.face_photo is not None:
                # 创建文件夹
                paths = './faces/' + text + '/'
                if not os.path.exists(paths):
                    os.makedirs(paths)
                    # 保存图片
                    s_time = time.ctime().replace(' ', '_').replace(':', '_')
                    cv2.imwrite(str(paths) + str(s_time) + '.jpg', self.face_photo)
                    self.textEdit.append("人脸已存放在 " + paths + ' 文件夹中!!')
                else:
                    QtWidgets.QMessageBox.warning(self, u"Warning", u"数据集中已有相同人名!",
                                                  buttons=QtWidgets.QMessageBox.Ok,
                                                  defaultButton=QtWidgets.QMessageBox.Ok)
        else:
            QtWidgets.QMessageBox.warning(self, u"Warning", u"输入错误", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
    def debug(self):
        self.face_recog.reload_data()  # 重载人脸数据集
        num = self.face_recog.max_num
        file_names = self.face_recog.names
        print(file_names)
        if num > 0:
            result, ok = QtWidgets.QInputDialog.getItem(self, u"人脸数据校验", u"把人脸数据存入对应的文件夹中,可增加人脸识别的准确性。确定把图片存放在以下文件夹中吗?",
                                                        file_names, 1, False)
            if ok:
                if self.face_photo is not None:
                    # 保存图片
                    s_time = time.ctime().replace(' ', '_').replace(':', '_')
                    cv2.imwrite('./faces/' + result + '/' + str(s_time) + '.jpg', self.face_photo)
                    self.textEdit.append("已保存在./faces/" + result + '文件夹下!!')
        else:
            QtWidgets.QMessageBox.warning(self, u"Warning", u"数据集为空,请新建人脸数据!", buttons=QtWidgets.QMessageBox.Ok,
                                          defaultButton=QtWidgets.QMessageBox.Ok)
    def closeEvent(self, event):
        ok = QtWidgets.QPushButton()
        cacel = QtWidgets.QPushButton()
        msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, u"关闭", u"是否关闭!")
        msg.addButton(ok, QtWidgets.QMessageBox.ActionRole)
        msg.addButton(cacel, QtWidgets.QMessageBox.RejectRole)
        ok.setText(u'确定')
        cacel.setText(u'取消')
        if msg.exec_() == QtWidgets.QMessageBox.RejectRole:
            event.ignore()
        else:
            if self.cap.isOpened():
                self.cap.release()
            if self.timer_camera.isActive():
                self.timer_camera.stop()
            if self.timer_udp_video.isActive():
                self.timer_udp_video.stop()
            event.accept()
if __name__ == "__main__":
    if not os.path.exists("./faces"):
        os.makedirs("./faces")
    app = QtWidgets.QApplication(sys.argv)
    myshow = MyDesignerShow()    # 创建实例
    myshow.show()           # 使用Qidget的show()方法
    sys.exit(app.exec_())

UI界面文件:

# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'gui.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_widget(object):
    def setupUi(self, widget):
        widget.setObjectName("widget")
        widget.resize(1024, 768)
        widget.setMinimumSize(QtCore.QSize(1024, 768))
        widget.setMaximumSize(QtCore.QSize(1024, 768))
        self.label_camera = QtWidgets.QLabel(widget)
        self.label_camera.setEnabled(True)
        self.label_camera.setGeometry(QtCore.QRect(360, 40, 640, 480))
        self.label_camera.setMinimumSize(QtCore.QSize(640, 480))
        self.label_camera.setMaximumSize(QtCore.QSize(640, 480))
        self.label_camera.setText("")
        self.label_camera.setObjectName("label_camera")
        self.textEdit = QtWidgets.QTextEdit(widget)
        self.textEdit.setGeometry(QtCore.QRect(360, 550, 641, 181))
        self.textEdit.setObjectName("textEdit")
        self.graphicsView = QtWidgets.QGraphicsView(widget)
        self.graphicsView.setGeometry(QtCore.QRect(360, 40, 640, 480))
        self.graphicsView.setMinimumSize(QtCore.QSize(640, 480))
        self.graphicsView.setMaximumSize(QtCore.QSize(640, 480))
        self.graphicsView.setObjectName("graphicsView")
        self.label_2 = QtWidgets.QLabel(widget)
        self.label_2.setGeometry(QtCore.QRect(640, 20, 91, 16))
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(widget)
        self.label_3.setGeometry(QtCore.QRect(630, 530, 111, 20))
        self.label_3.setObjectName("label_3")
        self.layoutWidget = QtWidgets.QWidget(widget)
        self.layoutWidget.setGeometry(QtCore.QRect(40, 430, 281, 101))
        self.layoutWidget.setObjectName("layoutWidget")
        self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        self.label_name = QtWidgets.QLabel(self.layoutWidget)
        self.label_name.setText("")
        self.label_name.setObjectName("label_name")
        self.gridLayout.addWidget(self.label_name, 0, 1, 1, 1)
        self.label_look = QtWidgets.QLabel(self.layoutWidget)
        self.label_look.setText("")
        self.label_look.setObjectName("label_look")
        self.gridLayout.addWidget(self.label_look, 2, 1, 1, 1)
        self.label_5 = QtWidgets.QLabel(self.layoutWidget)
        self.label_5.setObjectName("label_5")
        self.gridLayout.addWidget(self.label_5, 2, 0, 1, 1)
        self.label = QtWidgets.QLabel(self.layoutWidget)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        self.label_face = QtWidgets.QLabel(widget)
        self.label_face.setEnabled(True)
        self.label_face.setGeometry(QtCore.QRect(50, 210, 96, 96))
        self.label_face.setMinimumSize(QtCore.QSize(96, 96))
        self.label_face.setMaximumSize(QtCore.QSize(96, 96))
        self.label_face.setObjectName("label_face")
        self.textEdit_2 = QtWidgets.QTextEdit(widget)
        self.textEdit_2.setGeometry(QtCore.QRect(40, 550, 281, 181))
        self.textEdit_2.setObjectName("textEdit_2")
        self.layoutWidget1 = QtWidgets.QWidget(widget)
        self.layoutWidget1.setGeometry(QtCore.QRect(40, 70, 281, 121))
        self.layoutWidget1.setObjectName("layoutWidget1")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.layoutWidget1)
        self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.btn_local_camera = QtWidgets.QPushButton(self.layoutWidget1)
        self.btn_local_camera.setObjectName("btn_local_camera")
        self.gridLayout_2.addWidget(self.btn_local_camera, 0, 0, 1, 1)
        self.btn_web_camera = QtWidgets.QPushButton(self.layoutWidget1)
        self.btn_web_camera.setObjectName("btn_web_camera")
        self.gridLayout_2.addWidget(self.btn_web_camera, 1, 0, 1, 1)
        self.btn_close = QtWidgets.QPushButton(self.layoutWidget1)
        self.btn_close.setObjectName("btn_close")
        self.gridLayout_2.addWidget(self.btn_close, 2, 0, 1, 1)
        self.btn_get_face = QtWidgets.QPushButton(widget)
        self.btn_get_face.setEnabled(True)
        self.btn_get_face.setGeometry(QtCore.QRect(230, 220, 91, 41))
        self.btn_get_face.setMinimumSize(QtCore.QSize(0, 0))
        self.btn_get_face.setMaximumSize(QtCore.QSize(100, 60))
        self.btn_get_face.setObjectName("btn_get_face")
        self.btn_face_recognize = QtWidgets.QPushButton(widget)
        self.btn_face_recognize.setGeometry(QtCore.QRect(230, 280, 91, 41))
        self.btn_face_recognize.setMinimumSize(QtCore.QSize(30, 30))
        self.btn_face_recognize.setMaximumSize(QtCore.QSize(16777215, 100))
        self.btn_face_recognize.setObjectName("btn_face_recognize")
        self.btn_new_face = QtWidgets.QPushButton(widget)
        self.btn_new_face.setGeometry(QtCore.QRect(50, 350, 80, 31))
        self.btn_new_face.setMaximumSize(QtCore.QSize(100, 100))
        self.btn_new_face.setObjectName("btn_new_face")
        self.btn_debug = QtWidgets.QPushButton(widget)
        self.btn_debug.setGeometry(QtCore.QRect(230, 350, 91, 31))
        self.btn_debug.setMaximumSize(QtCore.QSize(100, 100))
        self.btn_debug.setObjectName("btn_debug")
        self.retranslateUi(widget)
        QtCore.QMetaObject.connectSlotsByName(widget)
    def retranslateUi(self, widget):
        _translate = QtCore.QCoreApplication.translate
        widget.setWindowTitle(_translate("widget", "Form"))
        self.label_2.setText(_translate("widget", "摄像头采集信息"))
        self.label_3.setText(_translate("widget", "调试信息显示窗口"))
        self.label_5.setText(_translate("widget", "    欧式距离:"))
        self.label.setText(_translate("widget", "    预测姓名:"))
        self.label_face.setText(_translate("widget", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; vertical-align:sub;\">人脸图片</span></p></body></html>"))
        self.textEdit_2.setHtml(_translate("widget", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt; font-weight:600;\">说明:</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">1.初次使用软件,请确保model文件夹存有训练好的权重</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">2.初次使用请新建人脸数据,名字必须为英文字符</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">3.欧式距离越大,代表越不相似,越小代表越相似</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">4.每次点击</span><span style=\" font-size:8pt; font-weight:600; text-decoration: underline;\">人脸识别</span><span style=\" font-size:8pt;\">前都必须先</span><span style=\" font-size:8pt; font-weight:600; text-decoration: underline;\">获取人脸</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt; font-weight:600;\">步骤:</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">1.打开本地摄像头或者网络摄像头</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">2.点击获取人脸</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">3.点击人脸识别</span></p></body></html>"))
        self.btn_local_camera.setText(_translate("widget", "打开本地摄像头"))
        self.btn_web_camera.setText(_translate("widget", "打开网络摄像头"))
        self.btn_close.setText(_translate("widget", "关闭程序"))
        self.btn_get_face.setText(_translate("widget", "获取人脸"))
        self.btn_face_recognize.setText(_translate("widget", "人脸识别"))
        self.btn_new_face.setText(_translate("widget", "新建人脸数据"))
        self.btn_debug.setText(_translate("widget", "报错"))


目录
相关文章
|
1天前
|
机器学习/深度学习 数据可视化 计算机视觉
基于opencv的车牌识别系统(UI界面采用tkinter设计)
基于opencv的车牌识别系统(UI界面采用tkinter设计)
5 0
|
1月前
|
XML Android开发 UED
💥Android UI设计新风尚!掌握Material Design精髓,让你的界面颜值爆表!🎨
随着移动应用市场的蓬勃发展,用户对界面设计的要求日益提高。为此,掌握由Google推出的Material Design设计语言成为提升应用颜值和用户体验的关键。本文将带你深入了解Material Design的核心原则,如真实感、统一性和创新性,并通过丰富的组件库及示例代码,助你轻松打造美观且一致的应用界面。无论是色彩搭配还是动画效果,Material Design都能为你的Android应用增添无限魅力。
48 1
|
2月前
|
编解码 前端开发 vr&ar
从零开始的PICO教程(4)--- UI界面绘制与响应事件
这篇文章是PICO开发系列教程的第四部分,主要介绍了如何在PICO 4 VR环境中创建UI界面,包括Canvas和Panel的配置、UI元素的绘制、以及Button和Slider的事件响应绑定,并通过示例展示了数字增减和滑块功能的具体实现。
从零开始的PICO教程(4)--- UI界面绘制与响应事件
|
2月前
|
容器 iOS开发 Linux
震惊!Uno Platform 响应式 UI 构建秘籍大公开!从布局容器到自适应设计,带你轻松打造跨平台完美界面
【8月更文挑战第31天】Uno Platform 是一款强大的跨平台应用开发框架,支持 Web、桌面(Windows、macOS、Linux)及移动(iOS、Android)等平台,仅需单一代码库。本文分享了四个构建响应式用户界面的最佳实践:利用布局容器(如 Grid)适配不同屏幕尺寸;采用自适应布局调整 UI;使用媒体查询定制样式;遵循响应式设计原则确保 UI 元素自适应调整。通过这些方法,开发者可以为用户提供一致且优秀的多设备体验。
71 0
|
3月前
|
XML Android开发 UED
💥Android UI设计新风尚!掌握Material Design精髓,让你的界面颜值爆表!🎨
【7月更文挑战第28天】随着移动应用市场的发展,用户对界面设计的要求不断提高。Material Design是由Google推出的设计语言,强调真实感、统一性和创新性,通过模拟纸张和墨水的物理属性创造沉浸式体验。它注重色彩、排版、图标和布局的一致性,确保跨设备的统一视觉风格。Android Studio提供了丰富的Material Design组件库,如按钮、卡片等,易于使用且美观。
118 1
|
3月前
|
异构计算 Python
30行代码实现一个带UI界面的图片背景移除工具:并附带web网页
人工智能技术正处于蓬勃发展中,移除图片背景的方法众多,涵盖了各式各样的实现途径和模型。然而,这些方法往往在安装和配置环境方面稍显复杂。今天,介绍一种极其简便的方法——大约30行代码,就能实现这一功能。虽然相比之下可能稍显简单,但对于不太苛刻的需求来说,这种方法颇为方便实用。
Pycharm主题切换(禁用)导致UI界面显示异常解决
问题记录 UI显示异常 安装多个主题时,当禁用某些主题,切换回one dark theme时,发现代码编辑窗口背景变成白色,菜单栏其他地方背景为黑色 问题原因 查看Settings>Editor>Color Scheme>General,发现方案被改为-Classic Light
|
4月前
|
XML Android开发 数据格式
【Android UI】使用RelativeLayout与TableLayout实现登录界面
【Android UI】使用RelativeLayout与TableLayout实现登录界面
50 5
|
4月前
|
前端开发 API 开发工具
视觉智能开放平台产品使用合集之人脸识别客户端如何直接访问服务器进行人脸识别并传递视频流
视觉智能开放平台是指提供一系列基于视觉识别技术的API和服务的平台,这些服务通常包括图像识别、人脸识别、物体检测、文字识别、场景理解等。企业或开发者可以通过调用这些API,快速将视觉智能功能集成到自己的应用或服务中,而无需从零开始研发相关算法和技术。以下是一些常见的视觉智能开放平台产品及其应用场景的概览。
|
5月前
|
JavaScript API
鸿蒙开发接口UI界面:【@ohos.mediaquery (媒体查询)】
鸿蒙开发接口UI界面:【@ohos.mediaquery (媒体查询)】
60 1