基于PaddleDetection的疲劳驾驶检测系统

c++kquote>该项目针对疲劳驾驶问题,利用移动端摄像头实时监测司机状态。通过PaddleDetection等技术部署,推断驾驶员疲劳状态,本地语音提醒并同步报警给管理人员。介绍了数据集处理、模型训练导出,以及在树莓派和Windows系统的部署,还包含信息记录至数据库、可视化界面等功能实现。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

基于paddledetection的疲劳驾驶检测系统 - 创想鸟

项目简介

每一年,中国都因交通事故而造成数万人的死亡,造成了严重的损失。而其中司机疲劳驾驶,是导致事故发生的重要原因之一。但是当司机们陷入疲劳驾驶状态时,往往司机本人对此状态并不在意,甚至会陷入睡眠状态!整治疲劳驾驶行为成为了交通运输行业的首要任务。随着信息技术的日新月异,如今,我们有机会使用信息技术,消除疲劳驾驶的隐患。利用移动端设备的摄像头,我们可以实现实时的监测司机的状态。使用PaddleDetection+PaddleLite部署及Paddle2onnx转IR的OpenVINO部署,实现了通过驾驶员的眼部、嘴部动作实时推断疲劳状态,使得驾驶员能及时的被本地语音方式提醒,避免疲劳驾驶,同时后台管理人员能接收到司机疲劳报警信息。

下载完整代码在本地运行

GitHub链接: FatigueDriving

数据预处理-调整数据集

数据来源:AIStudio公开数据集:疲劳驾驶行为数据集

公开数据集为VOC格式,并划分训练集:验证集:测试集=7:2:1

硅基智能 硅基智能

基于Web3.0的元宇宙,去中心化的互联网,高质量、沉浸式元宇宙直播平台,用数字化重新定义直播

硅基智能 62 查看详情 硅基智能 数据示例:

基于PaddleDetection的疲劳驾驶检测系统 - 创想鸟

In [ ]

# 解压数据集!unzip /home/aistudio/data/data106856/fatigue.zip

In [ ]

# 调整数据集的反斜杠问题f = open('/home/aistudio/fatigue/val_list.txt')data = f.read()data2 = data.replace('', '/')

In [ ]

f1 = open('/home/aistudio/fatigue/val_list.txt','w')f1.write(data2)

In [1]

# 解压PaddleDetection!unzip /home/aistudio/data/data122193/PaddleDetection.zip

In [1]

# 安装PaddleDetection依赖环境!pip install -r PaddleDetection/requirements.txt

平台1:使用PaddleLite部署至树莓派—模型训练及导出(PaddleLite)

ARM下部署选用了ssd算法,转为nb模型后使用PaddleLite做部署

In [ ]

# 模型训练# 修改ssdlite_mobilenet_v3_small_320_coco.yml中数据集格式为'../datasets/voc.yml', 并将'../datasets/voc.yml'里的数据路径改为当前数据集目录,以适配疲劳驾驶数据集!python PaddleDetection/tools/train.py -c PaddleDetection/configs/ssd/ssdlite_mobilenet_v3_small_320_coco.yml --use_vdl=True --eval

In [ ]

!python PaddleDetection/tools/infer.py -c PaddleDetection/configs/ssd/ssdlite_mobilenet_v3_small_320_coco.yml                           -o weights=output/ssdlite_mobilenet_v3_small_320_coco/best_model.pdparams                         --infer_img= ./0.jpg #(需要检测的图片)

In [ ]

# 导出ssd模型python PaddleDetection/tools/export_model.py -c PaddleDetection/configs/ssd/ssdlite_mobilenet_v3_small_320_coco.yml                             --output_dir=./ssd_inference_model                             -o weights=output/ssdlite_mobilenet_v3_small_320_coco/best_models.pdparams

In [2]

# 安装PaddleLite!pip install paddlelite

In [3]

# 转换为PaddleLite部署需要的nb模型!paddle_lite_opt     --model_file=ssd_inference_model/ssdlite_mobilenet_v3_small_320_coco/model.pdmodel     --param_file=ssd_inference_model/ssdlite_mobilenet_v3_small_320_coco/model.pdiparams     --optimize_out=ssd_inference_model/ssdlite_mobilenet_v3_small_320_coco/model     --optimize_out_type=naive_buffer     --valid_targets=arm

PaddleLite推理部分代码

// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at////     http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.#include "paddle_api.h"#include #include #include #include #include #include #include #include #include #include //#include int WARMUP_COUNT = 0;int REPEAT_COUNT = 1;const int CPU_THREAD_NUM = 2;const paddle::lite_api::PowerMode CPU_POWER_MODE =    paddle::lite_api::PowerMode::LITE_POWER_HIGH;const std::vector INPUT_SHAPE = {1, 3, 320, 320}; //输入形状const std::vector INPUT_MEAN = {0.5f, 0.5f, 0.5f};  //输入平均值const std::vector INPUT_STD = {0.5f, 0.5f, 0.5f};   //输入标准const float SCORE_THRESHOLD = 0.5f;                        //分数阈值const std::string file_number = "434343434343";            //用户编号struct RESULT {  std::string class_name;  float score;  float left;  float top;  float right;  float bottom;};// 获取时间inline int64_t get_current_us() {  struct timeval time;  gettimeofday(&time, NULL);  return 1000000LL * (int64_t)time.tv_sec + (int64_t)time.tv_usec;}// 年月日时分秒static std::string getCurrentTimeStr(){time_t t = time(NULL);char ch[64] = {0};strftime(ch, sizeof(ch) - 1, "%Y%m%d%H%M", localtime(&t));     //年-月-日-时-分return ch;}// 加载标签std::vector load_labels(const std::string &path) {  std::ifstream file;  std::vector labels;  file.open(path);  while (file) {    std::string line;    std::getline(file, line);    labels.push_back(line);  }  file.clear();  file.close();  return labels;}// 预处理void preprocess(cv::Mat &input_image, const std::vector &input_mean,                const std::vector &input_std, int input_width,                int input_height, float *input_data) {  cv::Mat resize_image;  cv::resize(input_image, resize_image, cv::Size(input_width, input_height), 0, 0);  if (resize_image.channels() == 4) {    cv::cvtColor(resize_image, resize_image, 3);  }  cv::Mat norm_image;  resize_image.convertTo(norm_image, CV_32FC3, 1 / 255.f);  // NHWC->NCHW  int image_size = input_height * input_width;  const float *image_data = reinterpret_cast(norm_image.data);  float32x4_t vmean0 = vdupq_n_f32(input_mean[0]);  float32x4_t vmean1 = vdupq_n_f32(input_mean[1]);  float32x4_t vmean2 = vdupq_n_f32(input_mean[2]);  float32x4_t vscale0 = vdupq_n_f32(1.0f / input_std[0]);  float32x4_t vscale1 = vdupq_n_f32(1.0f / input_std[1]);  float32x4_t vscale2 = vdupq_n_f32(1.0f / input_std[2]);  float *input_data_c0 = input_data;  float *input_data_c1 = input_data + image_size;  float *input_data_c2 = input_data + image_size * 2;  int i = 0;  for (; i < image_size - 3; i += 4) {    float32x4x3_t vin3 = vld3q_f32(image_data);    float32x4_t vsub0 = vsubq_f32(vin3.val[0], vmean0);    float32x4_t vsub1 = vsubq_f32(vin3.val[1], vmean1);    float32x4_t vsub2 = vsubq_f32(vin3.val[2], vmean2);    float32x4_t vs0 = vmulq_f32(vsub0, vscale0);    float32x4_t vs1 = vmulq_f32(vsub1, vscale1);    float32x4_t vs2 = vmulq_f32(vsub2, vscale2);    vst1q_f32(input_data_c0, vs0);    vst1q_f32(input_data_c1, vs1);    vst1q_f32(input_data_c2, vs2);    image_data += 12;    input_data_c0 += 4;    input_data_c1 += 4;    input_data_c2 += 4;  }  for (; i < image_size; i++) {    *(input_data_c0++) = (*(image_data++) - input_mean[0]) / input_std[0];    *(input_data_c0++) = (*(image_data++) - input_mean[1]) / input_std[1];    *(input_data_c0++) = (*(image_data++) - input_mean[2]) / input_std[2];  }}// 后处理std::vector postprocess(const float *output_data, int64_t output_size,                                const std::vector &word_labels,                                const float score_threshold,                                cv::Mat &output_image, double time) {  std::vector results;  std::vector colors = {      cv::Scalar(237, 189, 101), cv::Scalar(0, 0, 255), cv::Scalar(102, 153, 153),      cv::Scalar(255, 0, 0), cv::Scalar(9, 255, 0), cv::Scalar(0, 0, 0),      cv::Scalar(51, 153, 51)};  for (int64_t i = 0; i < output_size; i += 6) {    if (output_data[i + 1] < score_threshold) {      continue;    }    int class_id = static_cast(output_data[i]);    float score = output_data[i + 1];    RESULT result;    std::string class_name = "Unknown";    if (word_labels.size() > 0 && class_id >= 0 &&         class_id < word_labels.size()) {      class_name = word_labels[class_id];    }    result.class_name = class_name;    result.score = score;    result.left = output_data[i + 2];    result.top = output_data[i + 3];    result.right = output_data[i + 4];    result.bottom = output_data[i + 5];    int lx = static_cast(result.left * output_image.cols);    int ly = static_cast(result.top * output_image.rows);    int w = static_cast(result.right * output_image.cols) - lx;    int h = static_cast(result.bottom * output_image.rows) - ly;    cv::Rect bounding_box = cv::Rect(lx, ly, w, h) &        cv::Rect(0, 0, output_image.cols, output_image.rows);    if (w > 0 && h > 0 && score <= 1) {      cv::Scalar color = colors[results.size() % colors.size()];      cv::rectangle(output_image, bounding_box, color);      cv::rectangle(output_image, cv::Point2d(lx, ly), cv::Point2d(lx + w, ly - 10),                    color, -1);      cv::putText(output_image,                  std::to_string(results.size()) + "." + class_name + ":" +                      std::to_string(score),                  cv::Point2d(lx, ly), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(255, 255, 255));      results.push_back(result);    }  }  return results;}//识别处理cv::Mat process(cv::Mat &input_image,                std::vector &word_labels,                std::shared_ptr &predictor) {  // 对图像进行预处理,填充输入张量数据  std::unique_ptr input_tensor(      std::move(predictor->GetInput(0)));  input_tensor->Resize(INPUT_SHAPE);  int input_width = INPUT_SHAPE[3];  int input_height = INPUT_SHAPE[2];  auto *input_data = input_tensor->mutable_data();  double preprocess_start_time = get_current_us();  preprocess(input_image, INPUT_MEAN, INPUT_STD, input_width, input_height,             input_data);  double preprocess_end_time = get_current_us();  double preprocess_time = (preprocess_end_time - preprocess_start_time) / 1000.0f;  double prediction_time;  // 运行预测  for (int i = 0; i Run();  }  double max_time_cost = 0.0f;  double min_time_cost = std::numeric_limits::max();  double total_time_cost = 0.0f;  for (int i = 0; i Run();    auto end = get_current_us();    double cur_time_cost = (end - start) / 1000.0f;    if (cur_time_cost > max_time_cost) {      max_time_cost = cur_time_cost;    }    if (cur_time_cost < min_time_cost) {      min_time_cost = cur_time_cost;    }    total_time_cost += cur_time_cost;    prediction_time = total_time_cost / REPEAT_COUNT;    printf("iter %d cost: %f ms", i, cur_time_cost);  }  printf("warmup: %d repeat: %d, average: %f ms, max: %f ms, min: %f ms",         WARMUP_COUNT, REPEAT_COUNT, prediction_time,         max_time_cost, min_time_cost);  // 获取输出张量数据,进行后处理,输出检测对象  std::unique_ptr output_tensor(      std::move(predictor->GetOutput(0)));  const float *output_data = output_tensor->mutable_data();  int64_t output_size = 1;  for (auto dim : output_tensor->shape()) {    output_size *= dim;  }  cv::Mat output_image = input_image.clone();  double postprocess_start_time = get_current_us();  std::vector results = postprocess(      output_data, output_size, word_labels, SCORE_THRESHOLD, output_image, prediction_time);  double postprocess_end_time = get_current_us();  double postprocess_time = (postprocess_end_time - postprocess_start_time) / 1000.0f;    // wiringPi   //wiringPiSetup();  std::string warning_content_0 = "closed_eye";  std::string warning_content_1 = "open_mouth";  std::string time_ymdhms = getCurrentTimeStr();    // 结果输出  printf("results: %d", results.size());  for (int i = 0; i < results.size(); i++) {  // GPIO 0 高电平输出触发蜂鸣器  // pinMode(0,OUTPUT);// 存储图像    if (results[i].class_name.c_str()==warning_content_0)    {      std::string output_path = "./fatigueDriving/screenshots/Q" + file_number + time_ymdhms + warning_content_0 + ".jpg";      cv::imwrite(output_path,output_image);    }    if (results[i].class_name.c_str()==warning_content_1){      std::string output_path = "./fatigueDriving/screenshots/Q" + file_number + time_ymdhms + warning_content_1 + ".jpg";      cv::imwrite(output_path,output_image);    }    // if (results[i].class_name.c_str()==warning_content){digitalWrite(0,HIGH);delay(20);digitalWrite(0,LOW);delay(1);cv::imwrite(output_path,output_image);delay(1);}    // else{digitalWrite(0,LOW);delay(1);}    printf("[%d] %s - %f %f,%f,%f,%f", i, results[i].class_name.c_str(),           results[i].score, results[i].left, results[i].top, results[i].right,           results[i].bottom);  }  printf("Preprocess time: %f ms", preprocess_time);  printf("Prediction time: %f ms", prediction_time);  printf("Postprocess time: %f ms", postprocess_time);  return output_image;}int main(int argc, char **argv) {  // 加载模型地址  std::string model_path = argv[1];  // 加载标签地址  std::string label_path = argv[2];  // 加载标签  std::vector word_labels = load_labels(label_path);  // 加载模型  paddle::lite_api::MobileConfig config;  config.set_model_from_file(model_path);  config.set_threads(CPU_THREAD_NUM);  config.set_power_mode(CPU_POWER_MODE);  std::shared_ptr predictor =      paddle::lite_api::CreatePaddlePredictor(config);  // 视频流预测  cv::VideoCapture cap(-1);  cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);  cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);  // 判断是否开启摄像头  if (!cap.isOpened()) {    return -1;  }  // 执行  while (1) {    cv::Mat input_image;    cap >> input_image;    cv::Mat output_image = process(input_image, word_labels, predictor);    cv::imshow("Real-Time", output_image);    if (cv::waitKey(1) == char('q')) {      break;    }  }  cap.release();  cv::destroyAllWindows();  return 0;}

平台2:使用OpenVINO部署至Windows系统—模型训练及模型导出

IntelCPU下部署选用了yolov3算法,转为IR模型后使用OpenVINO做部署,可使用核显加速

In [ ]

# 模型训练!python PaddleDetection/tools/train.py -c PaddleDetection/configs/yolov3/yolov3_mobilenet_v3_large_270e_voc.yml --use_vdl=True --eval

In [ ]

!python PaddleDetection/tools/infer.py -c PaddleDetection/configs/yolov3/yolov3_mobilenet_v3_large_270e_voc.yml                           -o weights=output/yolov3_mobilenet_v3_large_270e_voc/best_model.pdparams                         --infer_img= ./0.jpg #(需要检测的图片)

In [ ]

# 安装paddle2onnx%cd /home/aistudio/# !git clone https://github.com/PaddlePaddle/Paddle2ONNX.git!unzip Paddle2ONNX-develop.zip%cd Paddle2ONNX-develop# !git checkout release/0.9!python setup.py install%cd experimental

In [2]

!pip install onnx

In [8]

import paddle2onnximport paddlefrom openvino_ppdet import nms_mapper# 通过上面的`nms_mapper`的import来启用插件,替换了paddle2onnx原始的nms_mappermodel_prefix = "/home/aistudio/yolov3-output/yolov3_mobilenet_v3_large_270e_voc/" # 一直写到模型文件名model = paddle.jit.load(model_prefix)input_shape_dict = {    "image": [1, 3, 608, 608],    "scale_factor": [1, 2],    "im_shape": [1, 2]    }onnx_model = paddle2onnx.run_convert(model, input_shape_dict=input_shape_dict, opset_version=11)with open("./yolov3.onnx", "wb") as f:    f.write(onnx_model.SerializeToString())

到此步骤后我们可以得到XXX.onnx模型文件,再使用OpenVINO的mo命令转换为IR模型文件。具体操作请参照OpenVINO使用文档

OpenVINO推理部分代码

#include "fatigueDriving.h"fatigueDriving::fatigueDriving() {};fatigueDriving::~fatigueDriving() {};int fatigueDriving::Init(std::string modelPath, bool useIntelgpu){std::string xmlPath, binPath;// 加载模型std::cout << "Loading model ... " << std::endl;xmlPath = modelPath + "/yolov3.xml";binPath = modelPath + "/yolov3.bin";network = ie.ReadNetwork(xmlPath, binPath);// 载至cpu/gputry {executableNetwork = ie.LoadNetwork(network, useIntelgpu ? "GPU" : "CPU");std::cout << "Loading model succeeded!" <getTensorDesc().getDims()[1];inputHeight = inputImage->getTensorDesc().getDims()[2];inputWidth = inputImage->getTensorDesc().getDims()[3];// 输出尺寸outputBboxesNumber = outputInfo->getTensorDesc().getDims()[0];outputSingleInfo = outputInfo->getTensorDesc().getDims()[1];return 1;}int fatigueDriving::Process(const cv::Mat img, std::vector<std::vector> &outputs, float scoreThreshold){if (!img.data)return -1;//判断是否有数据std::vector mean;mean.push_back(0.485f);mean.push_back(0.456f);mean.push_back(0.406f);std::vector std;std.push_back(0.229f);std.push_back(0.224f);std.push_back(0.225f);cv::Mat image;cv::cvtColor(img, image, cv::COLOR_BGR2RGB);cv::resize(image, image, cv::Size(inputWidth, inputHeight));// HWC =》NCHWfloat* data = static_cast(inputImage->buffer());size_t image_size = inputHeight * inputWidth;for (size_t row = 0; row < inputHeight; ++row) {for (size_t col = 0; col < inputWidth; ++col) {for (size_t ch = 0; ch < inputChannelsNumber; ++ch) {data[image_size * ch + row * inputWidth + col] = ((image.at(row, col)[ch]) / 255.0f) - float(mean[ch]) / std[ch];}}}    // 输入尺寸float* im_shape = static_cast(inputImShape->buffer());im_shape[0] = image.rows;im_shape[1] = image.cols;// 缩放因子float* scale_factor = static_cast(inputScaleFactor->buffer());scale_factor[0] = inputHeight / float(img.rows);scale_factor[1] = inputWidth / float(img.cols);// 预测clock_t time_start = clock();inferRequest.Infer();clock_t time_temp = clock();// 获取输出std::vector output;auto output_data = outputInfo->buffer().as<InferenceEngine::PrecisionTrait::value_type*>();for (int i = 0; i < outputBboxesNumber; i++) {for (int j = 0; j  scoreThreshold) {outputs.push_back(output);}output.clear();}return 1;}

c++版OpenVINO部署转python接口

#include #include #include #include #include "fatigueDriving.h"namespace py = pybind11;py::array ConvertMatToNDArray(cv::Mat& m);PYBIND11_MODULE(pyFatigueDriving, m){m.attr("__doc__") = "paddle interface";m.attr("__version__") = "v0.1.0";m.def("ConvertNDArrayToMat", [](py::array_t& input_data){cv::Mat dst = ConvertNDArrayToMat(input_data);return dst;}, py::arg("input_data"));py::class_(m, "cvMat");py::class_(m, "fatigueDriving").def(py::init()).def("Init", &fatigueDriving::Init).def("Process", [](fatigueDriving& vc, const cv::Mat& img, float scoreThreshold){std::vector<std::vector> outputs;int ret = vc.Process(img, outputs, scoreThreshold);return py::make_tuple(ret, outputs);}, py::arg("img"), py::arg("scoreThreshold"));}

实时检测驾驶员:违规时,信息写入数据库+语音提示

通过检测部分的存储违规图像动作,触发语音提示及信息记录功能

PC端 – main.py

In [ ]

import osimport _threadimport sysimport timeimport threadingimport cv2import numpy as npimport pygamefrom PyQt5 import QtWidgetsfrom PyQt5.QtWidgets import QApplication, QDialog, QMainWindowfrom watchdog.events import *from watchdog.observers import Observerfrom MySQL_Connect import MySQL_Connect# 加载openvino部署封装的dll及pydos.add_dll_directory(os.path.join(os.getcwd(), "./OpenVINO/dlls/"))sys.path.append(os.path.join(os.getcwd(), "./OpenVINO/libs/"))import pyFatigueDriving# 播放警示音频class Play_Audio():    def __init__(self, audio):        self.audio = audio    def run(self):        pygame.mixer.init()        pygame.mixer.music.load(self.audio)  # './audio/warning.mp3'        pygame.mixer.music.play()        time.sleep(7)        pygame.mixer.music.stop()class MyDirEventHandler(FileSystemEventHandler):    def __init__(self):        FileSystemEventHandler.__init__(self)        self.audio = './audio/warning.mp3'        self.warning = Play_Audio(self.audio)    def on_created(self, event):                print("file created:{0}".format(event.src_path))        a = str(event.src_path)        self.warning.run()# 转换为可存储到数据库的格式def save_received_info(user_info, iolations_time, new_filename):    user_all_info = {}    user_all_info['编号'] = user_info[0]    user_all_info['姓名'] = user_info[1]    user_all_info['性别'] = user_info[2]    user_all_info['年龄'] = user_info[3]    user_all_info['身份证号'] = user_info[4]    user_all_info['联系电话'] = user_info[5]    user_all_info['省份'] = user_info[6]    user_all_info['城市'] = user_info[7]    user_all_info['区/县'] = user_info[8]    user_all_info['车型'] = user_info[9]    user_all_info['行驶证编号'] = user_info[10]    user_all_info['车牌号'] = user_info[11]    user_all_info['违规时间'] = iolations_time    user_all_info['违规证据图像位置'] = new_filename    print(user_all_info)    return user_all_info# 信息记录至SQLdef ToSQL(a, module_name):    # 初始化SQL    MySQL = MySQL_Connect()    # 拉取用户数据    number = module_name[1:13]    iolations_time = module_name[13:17]+'-'+module_name[17:19] +         '-'+module_name[19:21]+' '+module_name[21:23]+':'+module_name[23:25]    user_info = MySQL.MC_SELECT_User_Info(local_info=number)[0]    # 生成数据列表    user_list_violation_info = save_received_info(        user_info, iolations_time, a)    MySQL.MC_Save_Violation_Info(user_list_violation_info)# 执行预测def run_detection_win_py(savepath):    file_number = "434343434343"    cap =cv2.VideoCapture(0)    while(True):        nowtime = time.strftime("%Y%m%d%H%M%S")        ret , frame =cap.read()        ret, output = fd.Process(pyFatigueDriving.ConvertNDArrayToMat(frame), 0.1)        for single_info in output:            cv2.rectangle(frame, (int(single_info[2]), int(single_info[3])), (int(single_info[4]), int(single_info[5])), (0, 0, 255), 1, 4)            label = str(int(single_info[0])) + ': ' + str(single_info[1])            cv2.putText(frame, label, (int(single_info[2]), int(single_info[3])), cv2.FONT_HERSHEY_COMPLEX, 2, (0, 0, 255), 1, 4)            if int(single_info[0]) == 0:                module_name = 'Q' + file_number + nowtime + str(single_info[0]) + '.jpg'                savefile = savepath + module_name                cv2.imwrite(savefile, frame)                ToSQL(savefile, module_name)                break            if int(single_info[0]) == 3:                if ((single_info[5]-single_info[3])/(single_info[4]-single_info[2])>=0.9):                    module_name = 'Q' + file_number + nowtime + str(single_info[0]) + '.jpg'                    savefile = savepath + module_name                    cv2.imwrite(savefile, frame)                    ToSQL(savefile, module_name)                    break        cv2.imshow("real-time", frame)        if cv2.waitKey(1) & 0xFF == ord('q'):            break    cap.release()    cv2.destroyAllWindows()# 监控文件的变化def run_scan_file(file_name):    # 创建观察者对象    observer = Observer()    # 创建事件处理对象    fileHandler = MyDirEventHandler()    # 为观察者设置观察对象与处理事件对象    observer.schedule(        fileHandler, file_name, True)    observer.start()    try:        while True:            time.sleep(2)    except KeyboardInterrupt:        observer.stop()    observer.join()if __name__ == '__main__':    fd = pyFatigueDriving.fatigueDriving()    modelPath = "./models/yolov3_ir"    useIntelGPU = False    ret = fd.Init(modelPath, useIntelGPU)    try:        # 识别线程        savepath="./screenshots/"        getDetect = threading.Thread(target=run_detection_win_py, args=(savepath, ))        getDetect.start()        # 警示线程        file_name="C:/project/FatigueDriving/screenshots"        getWarning = threading.Thread(target=run_scan_file, args=(file_name, ))        getWarning.start()    except Exception as e:        print("多线程异常,异常信息:{}".format(e))

移动端 – arm_main.py

In [ ]

import osimport _threadimport sysimport timeimport threadingimport cv2import numpy as npimport pygamefrom PyQt5 import QtWidgetsfrom PyQt5.QtWidgets import QApplication, QDialog, QMainWindowfrom watchdog.events import *from watchdog.observers import Observerfrom MySQL_Connect import MySQL_Connect# 执行预测-raspi端def run_detection_raspi(sh_name):    subprocess.run(sh_name, shell=True)# 转换为可存储到数据库的格式def save_received_info(user_info, iolations_time, new_filename):    user_all_info = {}    user_all_info['编号'] = user_info[0]    user_all_info['姓名'] = user_info[1]    user_all_info['性别'] = user_info[2]    user_all_info['年龄'] = user_info[3]    user_all_info['身份证号'] = user_info[4]    user_all_info['联系电话'] = user_info[5]    user_all_info['省份'] = user_info[6]    user_all_info['城市'] = user_info[7]    user_all_info['区/县'] = user_info[8]    user_all_info['车型'] = user_info[9]    user_all_info['行驶证编号'] = user_info[10]    user_all_info['车牌号'] = user_info[11]    user_all_info['违规时间'] = iolations_time    user_all_info['违规证据图像位置'] = new_filename    print(user_all_info)    return user_all_info# 信息记录至SQLdef ToSQL(a, module_name):    # 初始化SQL    MySQL = MySQL_Connect()    # 拉取用户数据    number = module_name[1:13]    iolations_time = module_name[13:17]+'-'+module_name[17:19] +         '-'+module_name[19:21]+' '+module_name[21:23]+':'+module_name[23:25]    user_info = MySQL.MC_SELECT_User_Info(local_info=number)[0]    # 生成数据列表    user_list_violation_info = save_received_info(        user_info, iolations_time, a)    MySQL.MC_Save_Violation_Info(user_list_violation_info)# 播放警示音频class Play_Audio():    def __init__(self, audio):        self.audio = audio    def run(self):        pygame.mixer.init()        pygame.mixer.music.load(self.audio)  # './audio/warning.mp3'        pygame.mixer.music.play()        time.sleep(3)        pygame.mixer.music.stop()class MyDirEventHandler(FileSystemEventHandler):    def __init__(self):        FileSystemEventHandler.__init__(self)        self.audio = './audio/warning.mp3'        self.warning = Play_Audio(self.audio)    def on_created(self, event):        print("file created:{0}".format(event.src_path))        a = str(event.src_path)        #        self.warning.run(self.audio)        directory, module_name = os.path.split(a)        module_name = os.path.splitext(module_name)[0]        ToSQL(a, module_name)# 监控文件的变化def run_scan_file(file_name):    # 创建观察者对象    observer = Observer()    # 创建事件处理对象    fileHandler = MyDirEventHandler()    # 为观察者设置观察对象与处理事件对象    observer.schedule(        fileHandler, file_name, True)    observer.start()    try:        while True:            time.sleep(2)    except KeyboardInterrupt:        observer.stop()    observer.join()"""多线程run_detection 执行预测程序run_scan_file 监控文件的变化"""if __name__ == '__main__':    try:        # 识别线程        command='./real_time.sh'        getDetect = threading.Thread(target=run_detection_raspi, args=(command, ))        getDetect.start()        # 警示线程        file_name="C:/project/FatigueDriving/screenshots"        getWarning = threading.Thread(target=run_scan_file, args=(file_name, ))        getWarning.start()    except Exception as e:        print("多线程异常,异常信息:{}".format(e))

数据库接入 – MySQL_Connect.py

In [ ]

class MySQL_Connect():    def __init__(self):        self.write_jud_user_info = 1        self.write_user_info = "insert into `user_info` values("        self.write_jud_violation_info = 1        self.write_violation_info = "insert into `violation_info` values("        # 加载数据库        try:            self._con = sql.Connect(                host="localhost",                user="root",                password="yours",                database="yours",                port=,                charset='utf8'                )        except Exception as e:            print("ERROR: " + e)            self._con.close()            #pass    # 编号判断    def MC_Number_Judge(self, number):        if len(number) != 12:            return False        else:            return True    # 性别判断    def MC_Sex_Judge(self, sex):        if sex != '男' and sex != '女':            return False        else:            return True    # 身份证判断    def MC_Id_Card_Judge(self, id):        if len(id) != 18:            return False        else:            return True    # 电话号判断    def MC_Phone_Judge(self, tel):        if len(tel) != 11:            return False        else:            return True    # 行驶证编号判断    def MC_Driver_Licenise_Number_Judge(self, num):        if len(num) != 12:            return False        else:            return True    # 车牌号判断    def MC_Licenise_Number_Judge(self, num):        if len(num) != 7:            return False        else:            return True    '''----------------------- 用户信息 -----------------------'''    # 写入用户信息    def MC_Save_User_Info(self, list_user_info):        self.list_user_info = list_user_info        cursor = self._con.cursor()        if self.MC_Number_Judge(self.list_user_info['编号']):            self.MC_Insert_User_Info(self.list_user_info['编号'])        self.MC_Insert_User_Info(self.list_user_info['姓名'])        if self.MC_Sex_Judge(self.list_user_info['性别']):            self.MC_Insert_User_Info(self.list_user_info['性别'])        self.MC_Insert_User_Info(self.list_user_info['年龄'])        if self.MC_Id_Card_Judge(self.list_user_info['身份证号']):            self.MC_Insert_User_Info(self.list_user_info['身份证号'])        if self.MC_Phone_Judge(self.list_user_info['联系电话']):            self.MC_Insert_User_Info(self.list_user_info['联系电话'])        self.MC_Insert_User_Info(self.list_user_info['省份'])        self.MC_Insert_User_Info(self.list_user_info['城市'])        self.MC_Insert_User_Info(self.list_user_info['区/县'])        self.MC_Insert_User_Info(self.list_user_info['车型'])        if self.MC_Driver_Licenise_Number_Judge(self.list_user_info['行驶证编号']):            self.MC_Insert_User_Info(self.list_user_info['行驶证编号'])        if self.MC_Licenise_Number_Judge(self.list_user_info['车牌号']):            self.MC_Insert_User_Info(self.list_user_info['车牌号'])        self.write_user_info += ")"        print(self.write_user_info)        cursor.execute(self.write_user_info)        self._con.commit()        cursor.close()        self.list_user_info.clear()    # 读取数据库信息    def MC_Read_User_Info(self):        try:            # 创建游标对象            cursor = self._con.cursor()            sql = 'SELECT * FROM `user_info`'            cursor.execute(sql)            result = cursor.fetchall()            cursor.close()        except Exception as e:            print("ERROR: " + e)            self._con.rollback()            cursor.close()        return result    # 数据库语言 数据插入    def MC_Insert_User_Info(self, new):        if self.write_jud_user_info != 1:            self.write_user_info += ", "        self.write_user_info += "'" + new + "'"        self.write_jud_user_info += 1        # 根据单个变量查询    def MC_SELECT_User_Info(self, local_info):        #self.local_info = '434343434343'        self.local_info = local_info        try:            # 创建游标对象            cursor = self._con.cursor()            sql = "SELECT * FROM `user_info` WHERE number=%s" % self.local_info            cursor.execute(sql)            result = cursor.fetchall()            cursor.close()        except Exception as e:            print("ERROR: " + e)            self._con.rollback()            cursor.close()        return result    '''----------------------- 违规记录 -----------------------'''    # 写入违规记录    def MC_Save_Violation_Info(self, list_violation_info):        self.list_violation_info = list_violation_info        cursor = self._con.cursor()        if self.MC_Number_Judge(self.list_violation_info['编号']):            self.MC_Insert_Violation_Info(self.list_violation_info['编号'])        self.MC_Insert_Violation_Info(self.list_violation_info['姓名'])        if self.MC_Sex_Judge(self.list_violation_info['性别']):            self.MC_Insert_Violation_Info(self.list_violation_info['性别'])        self.MC_Insert_Violation_Info(self.list_violation_info['年龄'])        if self.MC_Id_Card_Judge(self.list_violation_info['身份证号']):            self.MC_Insert_Violation_Info(self.list_violation_info['身份证号'])        if self.MC_Phone_Judge(self.list_violation_info['联系电话']):            self.MC_Insert_Violation_Info(self.list_violation_info['联系电话'])        self.MC_Insert_Violation_Info(self.list_violation_info['省份'])        self.MC_Insert_Violation_Info(self.list_violation_info['城市'])        self.MC_Insert_Violation_Info(self.list_violation_info['区/县'])        self.MC_Insert_Violation_Info(self.list_violation_info['车型'])        if self.MC_Driver_Licenise_Number_Judge(self.list_violation_info['行驶证编号']):            self.MC_Insert_Violation_Info(self.list_violation_info['行驶证编号'])        if self.MC_Licenise_Number_Judge(self.list_violation_info['车牌号']):            self.MC_Insert_Violation_Info(self.list_violation_info['车牌号'])        self.MC_Insert_Violation_Info(self.list_violation_info['违规时间'])        self.MC_Insert_Violation_Info(self.list_violation_info['违规证据图像位置'])        self.write_violation_info += ")"        print(self.write_violation_info)        cursor.execute(self.write_violation_info)        self._con.commit()        cursor.close()        self.list_violation_info.clear()    # 读取数据库信息    def MC_Read_Violation_Info(self):        try:            # 创建游标对象            cursor = self._con.cursor()            sql = 'SELECT * FROM `violation_info`'            cursor.execute(sql)            result = cursor.fetchall()            cursor.close()        except Exception as e:            print("ERROR: " + e)            self._con.rollback()            cursor.close()        return result    # 数据库语言 信息插入    def MC_Insert_Violation_Info(self, new):        if self.write_jud_violation_info != 1:            self.write_violation_info += ", "        self.write_violation_info += "'" + new + "'"        self.write_jud_violation_info += 1

可视化界面(GUI)

违规信息查询主界面 – Server_MainGUI.py

In [ ]

# 子窗口 -- 信息录入class Ui_Info_Entry_Child(Ui_Info_Entry):    """docstring for Ui_Info_Entry_Child"""    def __init__(self):        super(Ui_Info_Entry_Child, self).__init__()        self.setupUi(self)# 子窗口 -- 违规信息可视化class Ui_EvidenceIm_Child(Ui_EvidenceIm):    """docstring for Ui_EvidenceIm_Child"""    def __init__(self, impath, oneinfo):        super(Ui_EvidenceIm_Child, self).__init__(impath,oneinfo)        self.setupUi(self)        self.impath = impath        self.oneinfo = oneinfo# 主窗口 -- 数据库信息可视化class Ui_MainWindow(QtWidgets.QMainWindow):    def __init__(self):        super(Ui_MainWindow, self).__init__()        #self.line = 5        self.row_num = 1        self.MainWindow = QtWidgets.QMainWindow()        self.Info_Entry_Windows = Ui_Info_Entry_Child()        self.setupUi(self.MainWindow)    def setupUi(self, MainWindow):        MainWindow.setObjectName("MainWindow")        MainWindow.resize(1455, 969)        self.centralwidget = QtWidgets.QWidget(MainWindow)        self.centralwidget.setObjectName("centralwidget")        # 省份        self.server_label_province = QtWidgets.QLabel(self.centralwidget)        self.server_label_province.setGeometry(QtCore.QRect(0, 10, 51, 21))        self.server_label_province.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.server_label_province.setObjectName("server_label_province")        self.server_province = QtWidgets.QComboBox(self.centralwidget)        self.server_province.setGeometry(QtCore.QRect(60, 10, 111, 22))        self.server_province.setObjectName("server_province")        # 市/州        self.server_label_city = QtWidgets.QLabel(self.centralwidget)        self.server_label_city.setGeometry(QtCore.QRect(190, 10, 51, 21))        self.server_label_city.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.server_label_city.setObjectName("server_label_city")        self.server_city = QtWidgets.QComboBox(self.centralwidget)        self.server_city.setGeometry(QtCore.QRect(250, 10, 111, 22))        self.server_city.setObjectName("server_city")        # 区县        self.server_label_borough = QtWidgets.QLabel(self.centralwidget)        self.server_label_borough.setGeometry(QtCore.QRect(380, 10, 51, 21))        self.server_label_borough.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.server_label_borough.setObjectName("server_label_borough")        self.server_borough = QtWidgets.QComboBox(self.centralwidget)        self.server_borough.setGeometry(QtCore.QRect(440, 10, 111, 22))        self.server_borough.setObjectName("server_borough")        # 导入        self.pushButton_import = QtWidgets.QPushButton(self.centralwidget)        self.pushButton_import.setGeometry(QtCore.QRect(590, 10, 111, 23))        self.pushButton_import.setObjectName("pushButton_import")        # 更新        self.pushButton_update = QtWidgets.QPushButton(self.centralwidget)        self.pushButton_update.setGeometry(QtCore.QRect(740, 10, 111, 23))        self.pushButton_update.setObjectName("pushButton_update")        # 信息录入        self.pushButton_info_entry = QtWidgets.QPushButton(self.centralwidget)        self.pushButton_info_entry.setGeometry(QtCore.QRect(1300, 10, 111, 23))        self.pushButton_info_entry.setObjectName("pushButton_info_entry")        # 数据表格        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)        self.tableWidget.setEditTriggers(            QtWidgets.QAbstractItemView.NoEditTriggers)        # self.tableWidget.setSelectionBehavior(        #     QtWidgets.QAbstractItemView.SelectRows)        self.tableWidget.setGeometry(QtCore.QRect(10, 50, 1435, 901))        self.tableWidget.setObjectName("tableWidget")        self.tableWidget.setColumnCount(14)        self.tableWidget.setRowCount(self.row_num)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setVerticalHeaderItem(0, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(0, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(1, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(2, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(3, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(4, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(5, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(6, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(7, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(8, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(9, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(10, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(11, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(12, item)        item = QtWidgets.QTableWidgetItem()        self.tableWidget.setHorizontalHeaderItem(13, item)        MainWindow.setCentralWidget(self.centralwidget)        self.retranslateUi(MainWindow)        QtCore.QMetaObject.connectSlotsByName(MainWindow)        # 初始化省份数据        self.server_province.clear()        self.server_province.addItem('请选择')        for key, value in dictPorovince.items():            self.server_province.addItem(value, QVariant(key))        # 按扭框被点击事件信号        self.server_province.activated.connect(self.add_city)        self.server_city.activated.connect(self.add_borough)        self.server_borough.activated.connect(self.just_btn_enable)        self.pushButton_import.clicked.connect(self.import_ok)        self.pushButton_update.clicked.connect(self.import_ok)        self.pushButton_info_entry.clicked.connect(self.show_info_entry_windows)            def retranslateUi(self, MainWindow):        _translate = QtCore.QCoreApplication.translate        MainWindow.setWindowTitle(_translate("MainWindow", "疲劳驾驶监测数据终端平台"))        self.server_label_province.setText(_translate("MainWindow", "省份"))        self.server_label_city.setText(_translate("MainWindow", "市/州"))        self.server_label_borough.setText(_translate("MainWindow", "区/县"))        self.pushButton_import.setText(_translate("MainWindow", "导出"))        self.pushButton_update.setText(_translate("MainWindow", "刷新"))        self.pushButton_info_entry.setText(_translate("MainWindow", "信息录入"))        item = self.tableWidget.verticalHeaderItem(0)        item.setText(_translate("MainWindow", "1"))        item = self.tableWidget.horizontalHeaderItem(0)        item.setText(_translate("MainWindow", "编号"))        item = self.tableWidget.horizontalHeaderItem(1)        item.setText(_translate("MainWindow", "姓名"))        item = self.tableWidget.horizontalHeaderItem(2)        item.setText(_translate("MainWindow", "性别"))        item = self.tableWidget.horizontalHeaderItem(3)        item.setText(_translate("MainWindow", "年龄"))        item = self.tableWidget.horizontalHeaderItem(4)        item.setText(_translate("MainWindow", "身份证号"))        item = self.tableWidget.horizontalHeaderItem(5)        item.setText(_translate("MainWindow", "联系电话"))        item = self.tableWidget.horizontalHeaderItem(6)        item.setText(_translate("MainWindow", "省份"))        item = self.tableWidget.horizontalHeaderItem(7)        item.setText(_translate("MainWindow", "市/州"))        item = self.tableWidget.horizontalHeaderItem(8)        item.setText(_translate("MainWindow", "区/县"))        item = self.tableWidget.horizontalHeaderItem(9)        item.setText(_translate("MainWindow", "车型"))        item = self.tableWidget.horizontalHeaderItem(10)        item.setText(_translate("MainWindow", "行驶证编号"))        item = self.tableWidget.horizontalHeaderItem(11)        item.setText(_translate("MainWindow", "车牌号"))        item = self.tableWidget.horizontalHeaderItem(12)        item.setText(_translate("MainWindow", "违规时间"))        item = self.tableWidget.horizontalHeaderItem(13)        item.setText(_translate("MainWindow", "操作"))    # 当省份按钮被选择后添加对应的城市数据    def add_city(self, index):        pro_code = self.server_province.itemData(index)        city_data = dictCity.get(pro_code, dict())        self.server_city.clear()        self.server_city.addItem('请选择')        self.server_borough.clear()        self.server_borough.addItem('请选择')        if self.server_province.currentText() != '请选择':            for key, value in city_data.items():                self.server_city.addItem(value, QVariant(key))        self.pushButton_import.setDisabled(True)    # 当城市按钮被选择后添加对应的区县数据    def add_borough(self, index):        city_code = self.server_city.itemData(index)        borough_data = dicBorough.get(city_code, dict())        self.server_borough.clear()        self.server_borough.addItem('请选择')        if self.server_city.currentText() != '请选择':            for key, value in borough_data.items():                self.server_borough.addItem(value, QVariant(key))        self.pushButton_import.setDisabled(True)    # 导出按钮是否可用    def just_btn_enable(self, txt):        if self.server_borough.currentText() != '请选择':            self.pushButton_import.setDisabled(False)        else:            self.pushButton_import.setDisabled(True)    # 根据省市县筛选信息导出    def import_ok(self):        if self.server_province.currentText() == '请选择':            QtWidgets.QMessageBox.warning(self, "警告", "请选择省/市/县信息", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)        else:            self.MySQL_Read = MySQL_Connect()            self.info_all = self.MySQL_Read.MC_Read_Violation_Info()            # 根据省市县筛选得到的信息存放            self.info_realdata = []            # 提取筛选后的各数据对应图片地址            self.info_realimpath = []            # 按钮名称            self.btn_realname = []            # 遍历提取相应数据            for i in range(len(self.info_all)):                if self.info_all[i][6] == self.server_province.currentText() and self.info_all[i][7] == self.server_city.currentText() and self.info_all[i][8] == self.server_borough.currentText():                    self.info_realdata.append(list(self.info_all[i]))                    self.info_realimpath.append(str(self.info_all[i][-1]))                    self.btn_realname.append(str('详情:'+self.info_all[i][1]+' - '+self.info_all[i][-2]))            self.pushbutton_list()            for m in range(len(self.info_realdata)):  # m行 n列                for n in range(14):                    item = QtWidgets.QTableWidgetItem(str(self.info_realdata[m][n]))                    self.tableWidget.setItem(m, n, item)    # 创建详情按钮    def pushbutton_list(self):        self.tableWidget.setRowCount(len(self.info_realdata))        for n in range(len(self.info_realdata)):            self.btn = QtWidgets.QPushButton()            self.btn.setDown(True)            self.btn.setStyleSheet('QPushButton{margin:3px}'                                   'QPushButton{padding:1px 1px}')            self.tableWidget.setCellWidget(n, 13, self.btn)            #此处将字传入按钮            self.btn.setText(self.btn_realname[n])            #传达参数,这里sender将接收你点击的字并传入函数            self.btn.clicked.connect(                lambda: self.btn_clicked(self.btn_realname.index(self.sender().text())))    # 显示子页面 -- 信息录入    def show_info_entry_windows(self):        self.Info_Entry_Windows.show()    # 显示子页面 -- 证据图像    def btn_clicked(self,n):        #self.nn = list.index(self.line[self.n])        self.impath = self.info_realimpath[n]        self.oneinfo = self.btn_realname[n]        self.EvidenceIm_Windows = Ui_EvidenceIm_Child(            self.impath, self.oneinfo)        self.EvidenceIm_Windows.show()# 重写QtWidgets.QMainWindow类关闭事件class MainWindow(QtWidgets.QMainWindow):    def closeEvent(self, event):  # 关闭窗口触发以下事件        reply = QtWidgets.QMessageBox.question(            self, '本程序', '你确定要退出吗?', QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)        if reply == QtWidgets.QMessageBox.Yes:            event.accept()  # 接受关闭事件        else:            event.ignore()  # 忽略关闭事件

信息录入界面 – Input_Info_GUI.py

In [ ]

class Ui_Info_Entry(QtWidgets.QMainWindow):    def setupUi(self, Form):        Form.setObjectName("Form")        Form.resize(432, 532)        Form.setFixedSize(432, 532)        # 分隔线0        self.line_0 = QtWidgets.QLabel(Form)        self.line_0.setGeometry(QtCore.QRect(30, 40, 371, 16))        self.line_0.setObjectName("line_0")        # 分隔线1        self.line_1 = QtWidgets.QLabel(Form)        self.line_1.setGeometry(QtCore.QRect(30, 210, 371, 16))        self.line_1.setObjectName("line_1")        # 分隔线2        self.line_2 = QtWidgets.QLabel(Form)        self.line_2.setGeometry(QtCore.QRect(30, 340, 371, 16))        self.line_2.setObjectName("line_2")        # 基本信息        self.essential_information = QtWidgets.QLabel(Form)        self.essential_information.setGeometry(QtCore.QRect(30, 20, 61, 16))        self.essential_information.setObjectName("essential_information")        # 地址信息        self.address_information = QtWidgets.QLabel(Form)        self.address_information.setGeometry(QtCore.QRect(30, 190, 54, 12))        self.address_information.setObjectName("address_information")        # 车辆信息        self.vehicle_information = QtWidgets.QLabel(Form)        self.vehicle_information.setGeometry(QtCore.QRect(30, 320, 54, 12))        self.vehicle_information.setObjectName("vehicle_information")        # 省份        self.label_province = QtWidgets.QLabel(Form)        self.label_province.setGeometry(QtCore.QRect(40, 230, 51, 21))        self.label_province.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.label_province.setObjectName("label_province")        self.province = QtWidgets.QComboBox(Form)        self.province.setGeometry(QtCore.QRect(90, 230, 111, 22))        self.province.setObjectName("province")        self.province.addItem("")        # 市/州        self.label_city = QtWidgets.QLabel(Form)        self.label_city.setGeometry(QtCore.QRect(230, 230, 51, 21))        self.label_city.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.label_city.setObjectName("label_city")        self.city = QtWidgets.QComboBox(Form)        self.city.setGeometry(QtCore.QRect(280, 230, 111, 22))        self.city.setObjectName("city")        self.city.addItem("")        # 区/县        self.label_borough = QtWidgets.QLabel(Form)        self.label_borough.setGeometry(QtCore.QRect(40, 280, 51, 21))        self.label_borough.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.label_borough.setObjectName("label_borough")        self.borough = QtWidgets.QComboBox(Form)        self.borough.setGeometry(QtCore.QRect(90, 280, 111, 22))        self.borough.setObjectName("borough")        self.borough.addItem("")        # 编号        self.number = QtWidgets.QLabel(Form)        self.number.setGeometry(QtCore.QRect(40, 60, 51, 21))        self.number.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.number.setObjectName("number")        self.lineEdit_00 = QtWidgets.QLineEdit(Form)        self.lineEdit_00.setGeometry(QtCore.QRect(90, 60, 111, 20))        self.lineEdit_00.setText("")        self.lineEdit_00.setObjectName("lineEdit_00")        # 姓名        self.name = QtWidgets.QLabel(Form)        self.name.setGeometry(QtCore.QRect(230, 60, 51, 21))        self.name.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.name.setObjectName("name")        self.lineEdit_01 = QtWidgets.QLineEdit(Form)        self.lineEdit_01.setGeometry(QtCore.QRect(280, 60, 111, 20))        self.lineEdit_01.setText("")        self.lineEdit_01.setObjectName("lineEdit_01")        # 性别        self.sex = QtWidgets.QLabel(Form)        self.sex.setGeometry(QtCore.QRect(40, 110, 51, 21))        self.sex.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.sex.setObjectName("sex")        self.lineEdit_10 = QtWidgets.QLineEdit(Form)        self.lineEdit_10.setGeometry(QtCore.QRect(90, 110, 111, 20))        self.lineEdit_10.setText("")        self.lineEdit_10.setObjectName("lineEdit_10")        # 年龄        self.age = QtWidgets.QLabel(Form)        self.age.setGeometry(QtCore.QRect(230, 110, 51, 21))        self.age.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.age.setObjectName("age")        self.lineEdit_11 = QtWidgets.QLineEdit(Form)        self.lineEdit_11.setGeometry(QtCore.QRect(280, 110, 111, 20))        self.lineEdit_11.setText("")        self.lineEdit_11.setObjectName("lineEdit_11")        # 身份证号        self.id_number = QtWidgets.QLabel(Form)        self.id_number.setGeometry(QtCore.QRect(30, 150, 61, 21))        self.id_number.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.id_number.setObjectName("id_number")        self.lineEdit_20 = QtWidgets.QLineEdit(Form)        self.lineEdit_20.setGeometry(QtCore.QRect(90, 150, 111, 20))        self.lineEdit_20.setText("")        self.lineEdit_20.setObjectName("lineEdit_20")        # 联系电话        self.contact_number = QtWidgets.QLabel(Form)        self.contact_number.setGeometry(QtCore.QRect(220, 150, 61, 21))        self.contact_number.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.contact_number.setObjectName("contact_number")        self.lineEdit_21 = QtWidgets.QLineEdit(Form)        self.lineEdit_21.setGeometry(QtCore.QRect(280, 150, 111, 20))        self.lineEdit_21.setText("")        self.lineEdit_21.setObjectName("lineEdit_21")        # 车型        self.model = QtWidgets.QLabel(Form)        self.model.setGeometry(QtCore.QRect(40, 360, 51, 21))        self.model.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.model.setObjectName("model")        self.lineEdit_30 = QtWidgets.QLineEdit(Form)        self.lineEdit_30.setGeometry(QtCore.QRect(90, 360, 111, 20))        self.lineEdit_30.setText("")        self.lineEdit_30.setObjectName("lineEdit_30")        # 驾驶证编号        self.driver_license_number = QtWidgets.QLabel(Form)        self.driver_license_number.setGeometry(QtCore.QRect(200, 360, 81, 21))        self.driver_license_number.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.driver_license_number.setObjectName("driver_license_number")        self.lineEdit_31 = QtWidgets.QLineEdit(Form)        self.lineEdit_31.setGeometry(QtCore.QRect(280, 360, 111, 20))        self.lineEdit_31.setText("")        self.lineEdit_31.setObjectName("lineEdit_31")        # 车牌号        self.license_number = QtWidgets.QLabel(Form)        self.license_number.setGeometry(QtCore.QRect(40, 400, 51, 21))        self.license_number.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)        self.license_number.setObjectName("license_number")        self.lineEdit_40 = QtWidgets.QLineEdit(Form)        self.lineEdit_40.setGeometry(QtCore.QRect(90, 400, 111, 20))        self.lineEdit_40.setText("")        self.lineEdit_40.setObjectName("lineEdit_40")        # 保存        self.info_entry = QtWidgets.QPushButton(Form)        self.info_entry.setGeometry(QtCore.QRect(90, 450, 75, 23))        self.info_entry.setObjectName("info_entry")        # 取消        self.cancel = QtWidgets.QPushButton(Form)        self.cancel.setGeometry(QtCore.QRect(260, 450, 75, 23))        self.cancel.setObjectName("cancel")        # 中心窗口        self.retranslateUi(Form)        QtCore.QMetaObject.connectSlotsByName(Form)        # 初始化省份数据        self.province.clear()        self.province.addItem('请选择')        for key, value in dictPorovince.items():            self.province.addItem(value, QVariant(key))        # 按扭框被点击事件信号        self.province.activated.connect(self.add_city)        self.city.activated.connect(self.add_borough)        self.borough.activated.connect(self.just_btn_enable)        self.info_entry.clicked.connect(self.save_sql_ok)        self.cancel.clicked.connect(self.close)    def retranslateUi(self, Form):        _translate = QtCore.QCoreApplication.translate        Form.setWindowTitle(_translate("Form", "个人信息登记"))        # 基本信息        self.essential_information.setText(_translate("Form", "

基本信息*

")) self.line_0.setText(_translate("Form", "--------------------------------------------------------------------")) self.number.setText(_translate("Form", "编号:")) self.name.setText(_translate("Form", "姓名:")) self.sex.setText(_translate("Form", "性别:")) self.age.setText(_translate("Form", "年龄:")) self.id_number.setText(_translate("Form", "身份证号:")) self.contact_number.setText(_translate("Form", "联系电话:")) # 地址信息 self.address_information.setText(_translate("Form", "

地址信息*

")) self.line_1.setText(_translate("Form", "--------------------------------------------------------------------")) self.label_province.setText(_translate("Form", "省份:")) self.province.setItemText(0, _translate("Form", "请选择")) self.label_city.setText(_translate("Form", "城市:")) self.city.setItemText(0, _translate("Form", "请选择")) self.label_borough.setText(_translate("Form", "区/县:")) self.borough.setItemText(0, _translate("Form", "请选择")) # 车辆信息 self.vehicle_information.setText(_translate("Form", "

车辆信息*

")) self.line_2.setText(_translate("Form", "--------------------------------------------------------------------")) self.model.setText(_translate("Form", "车型:")) self.driver_license_number.setText(_translate("Form", "行驶证编号:")) self.license_number.setText(_translate("Form", "车牌号:")) # 按钮 self.cancel.setText(_translate("Form", "取消")) self.info_entry.setText(_translate("Form", "录入")) # 当省份按钮被选择后添加对应的城市数据 def add_city(self, index): pro_code = self.province.itemData(index) city_data = dictCity.get(pro_code, dict()) self.city.clear() self.city.addItem('请选择') self.borough.clear() self.borough.addItem('请选择') if self.province.currentText() != '请选择': for key, value in city_data.items(): self.city.addItem(value, QVariant(key)) self.info_entry.setDisabled(True) # 当城市按钮被选择后添加对应的区县数据 def add_borough(self, index): city_code = self.city.itemData(index) borough_data = dicBorough.get(city_code, dict()) self.borough.clear() self.borough.addItem('请选择') if self.city.currentText() != '请选择': for key, value in borough_data.items(): self.borough.addItem(value, QVariant(key)) self.info_entry.setDisabled(True) # 导出按钮是否可用 def just_btn_enable(self, txt): if self.borough.currentText() != '请选择': self.info_entry.setDisabled(False) else: self.info_entry.setDisabled(True) # 清空信息 def clear(self): self.lineEdit_00.clear() self.lineEdit_01.clear() self.lineEdit_10.clear() self.lineEdit_11.clear() self.lineEdit_20.clear() self.lineEdit_21.clear() self.province.clear() self.province.addItem('请选择') self.city.clear() self.city.addItem('请选择') self.borough.clear() self.borough.addItem('请选择') self.lineEdit_30.clear() self.lineEdit_31.clear() self.lineEdit_40.clear() # 初始化省按钮信息 for key, value in dictPorovince.items(): self.province.addItem(value, QVariant(key)) # 点击保存后,信息确认 def save_sql_ok(self): self.all_info = {} self.all_info['编号'] = self.lineEdit_00.text() self.all_info['姓名'] = self.lineEdit_01.text() self.all_info['性别'] = self.lineEdit_10.text() self.all_info['年龄'] = self.lineEdit_11.text() self.all_info['身份证号'] = self.lineEdit_20.text() self.all_info['联系电话'] = self.lineEdit_21.text() self.all_info['省份'] = self.province.currentText() self.all_info['城市'] = self.city.currentText() self.all_info['区/县'] = self.borough.currentText() self.all_info['车型'] = self.lineEdit_30.text() self.all_info['行驶证编号'] = self.lineEdit_31.text() self.all_info['车牌号'] = self.lineEdit_40.text() print(self.all_info) self.MySQL_Save = MySQL_Connect() self.MySQL_Save.MC_Save_User_Info(list_user_info=self.all_info) QMessageBox.information(self, "请确认信息", '''您的编号:{}姓名:{}性别:{}年龄:{}身份证号:{}联系电话:{}省份:{}城市:{} 区县:{}车型:{}行驶证编号:{}车牌号:{}'''.format(self.lineEdit_00.text(), self.lineEdit_01.text(), self.lineEdit_10.text(), self.lineEdit_11.text(), self.lineEdit_20.text(), self.lineEdit_21.text(), self.province.currentText(), self.city.currentText(), self.borough.currentText(), self.lineEdit_30.text(), self.lineEdit_31.text(), self.lineEdit_40.text()), QMessageBox.Yes | QMessageBox.No) self.clear()

违规图像查看页面 – EvidenceIm_GUI.py

In [ ]

class Ui_EvidenceIm(QtWidgets.QMainWindow):    def __init__(self,impath,oneinfo):        super(Ui_EvidenceIm, self).__init__()        self.impath = impath        self.oneinfo = oneinfo    def setupUi(self, Form):        Form.setObjectName(self.oneinfo)        Form.resize(979, 584)        Form.setFixedSize(979, 584)        self.evidence_im_view = GraphicsView(Form)        self.evidence_im_view.setGeometry(QtCore.QRect(10, 40, 960, 540))        self.evidence_im_view.setObjectName("evidence_im_view")        self.evidence_im_label = QtWidgets.QLabel(Form)        self.evidence_im_label.setGeometry(QtCore.QRect(10, 10, 971, 21))        self.evidence_im_label.setObjectName("evidence_im_label")        self.retranslateUi(Form)        QtCore.QMetaObject.connectSlotsByName(Form)        try:            # 显示图片            #img = cv_imread()            img = cv2.imdecode(np.fromfile(self.impath, dtype=np.uint8), -1)            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img)            x = img.shape[1]  # 获取图像宽度            y = img.shape[0]  # 获取图像高度            frame = QtGui.QImage(img.data, x, y, x*3, QtGui.QImage.Format_RGB888)            pix = QtGui.QPixmap.fromImage(frame)            self.item = QtWidgets.QGraphicsPixmapItem(pix)  # 创建像素图元            self.scene = QtWidgets.QGraphicsScene()  # 创建场景            self.scene.clear()            self.scene.addItem(self.item)            self.scene.update()            self.evidence_im_view.setScene(self.scene)  # 将场景添加至视图        except:            pass    def retranslateUi(self, Form):        _translate = QtCore.QCoreApplication.translate        Form.setWindowTitle(_translate("Form", self.oneinfo))        self.evidence_im_label.setText(_translate(            "Form", "

-------------------------------------------------- 图像证据 --------------------------------------------------

"))# 重写GraphicsView类 可拖动、放大、缩小图像class GraphicsView(QGraphicsView): # 背景区域颜色 backgroundColor = QColor(255, 255, 255) def __init__(self, *args, **kwargs): super(GraphicsView, self).__init__(*args, **kwargs) self.resize(800, 600) # 设置背景颜色 self.setBackgroundBrush(self.backgroundColor) self.setCacheMode(self.CacheBackground) self.setDragMode(self.ScrollHandDrag) self.setOptimizationFlag(self.DontSavePainterState) self.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform) if QGLFormat.hasOpenGL(): self.setRenderHint(QPainter.HighQualityAntialiasing) self.setResizeAnchor(self.AnchorUnderMouse) self.setRubberBandSelectionMode(Qt.IntersectsItemShape) self.setTransformationAnchor(self.AnchorUnderMouse) self.setViewportUpdateMode(self.SmartViewportUpdate) # 设置场景(显示在屏幕中间) self._scene = QGraphicsScene(-180, -90, 360, 180, self) self.setScene(self._scene) def wheelEvent(self, event): # 滑轮事件 if event.modifiers() & Qt.ControlModifier: self.scaleView(math.pow(2.0, -event.angleDelta().y() / 240.0)) return event.accept() super(GraphicsView, self).wheelEvent(event) def scaleView(self, scaleFactor): factor = self.transform().scale( scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width() if factor 100: return self.scale(scaleFactor, scaleFactor)

以上就是基于PaddleDetection的疲劳驾驶检测系统的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/315947.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月5日 07:43:42
下一篇 2025年11月5日 07:44:41

相关推荐

  • CSS mask属性无法获取图片:为什么我的图片不见了?

    CSS mask属性无法获取图片 在使用CSS mask属性时,可能会遇到无法获取指定照片的情况。这个问题通常表现为: 网络面板中没有请求图片:尽管CSS代码中指定了图片地址,但网络面板中却找不到图片的请求记录。 问题原因: 此问题的可能原因是浏览器的兼容性问题。某些较旧版本的浏览器可能不支持CSS…

    2025年12月24日
    900
  • 如何用dom2img解决网页打印样式不显示的问题?

    用dom2img解决网页打印样式不显示的问题 想将网页以所见即打印的的效果呈现,需要采取一些措施,特别是在使用了bootstrap等大量采用外部css样式的框架时。 问题根源 在常规打印操作中,浏览器通常会忽略css样式等非必要的页面元素,导致打印出的结果与网页显示效果不一致。这是因为打印机制只识别…

    2025年12月24日
    800
  • 如何用 CSS 模拟不影响其他元素的链接移入效果?

    如何模拟 css 中链接的移入效果 在 css 中,模拟移入到指定链接的效果尤为复杂,因为链接的移入效果不影响其他元素。要实现这种效果,最简单的方法是利用放大,例如使用 scale 或 transform 元素的 scale 属性。下面提供两种方法: scale 属性: .goods-item:ho…

    2025年12月24日
    700
  • Uniapp 中如何不拉伸不裁剪地展示图片?

    灵活展示图片:如何不拉伸不裁剪 在界面设计中,常常需要以原尺寸展示用户上传的图片。本文将介绍一种在 uniapp 框架中实现该功能的简单方法。 对于不同尺寸的图片,可以采用以下处理方式: 极端宽高比:撑满屏幕宽度或高度,再等比缩放居中。非极端宽高比:居中显示,若能撑满则撑满。 然而,如果需要不拉伸不…

    2025年12月24日
    400
  • PC端H5项目如何实现适配:流式布局、响应式设计和两套样式?

    PC端的适配方案及PC与H5兼顾的实现方案探讨 在开发H5项目时,常用的屏幕适配方案是postcss-pxtorem或postcss-px-to-viewport,通常基于iPhone 6标准作为设计稿。但对于PC端网项目,处理不同屏幕大小需要其他方案。 PC端屏幕适配方案 PC端屏幕适配一般采用流…

    2025年12月24日
    300
  • CSS 元素设置 10em 和 transition 后为何没有放大效果?

    CSS 元素设置 10em 和 transition 后为何无放大效果? 你尝试设置了一个 .box 类,其中包含字体大小为 10em 和过渡持续时间为 2 秒的文本。当你载入到页面时,它没有像 YouTube 视频中那样产生放大效果。 原因可能在于你将 CSS 直接写在页面中 在你的代码示例中,C…

    2025年12月24日
    400
  • 如何实现类似横向U型步骤条的组件?

    横向U型步骤条寻求替代品 希望找到类似横向U型步骤条的组件或 CSS 实现。 潜在解决方案 根据给出的参考图片,类似的组件有: 图片所示组件:图片提供了组件的外观,但没有提供具体的实现方式。参考链接:提供的链接指向了 SegmentFault 上的另一个问题,其中可能包含相关的讨论或解决方案建议。 …

    2025年12月24日
    800
  • 如何让小说网站控制台显示乱码,同时网页内容正常显示?

    如何在不影响用户界面的情况下实现控制台乱码? 当在小说网站上下载小说时,大家可能会遇到一个问题:网站上的文本在网页内正常显示,但是在控制台中却是乱码。如何实现此类操作,从而在不影响用户界面(UI)的情况下保持控制台乱码呢? 答案在于使用自定义字体。网站可以通过在服务器端配置自定义字体,并通过在客户端…

    2025年12月24日
    800
  • 如何优化CSS Grid布局中子元素排列和宽度问题?

    css grid布局中的优化问题 在使用css grid布局时可能会遇到以下问题: 问题1:无法控制box1中li的布局 box1设置了grid-template-columns: repeat(auto-fill, 20%),这意味着容器将自动填充尽可能多的20%宽度的列。当li数量大于5时,它们…

    2025年12月24日
    800
  • SASS 中的 Mixins

    mixin 是 css 预处理器提供的工具,虽然它们不是可以被理解的函数,但它们的主要用途是重用代码。 不止一次,我们需要创建多个类来执行相同的操作,但更改单个值,例如字体大小的多个类。 .fs-10 { font-size: 10px;}.fs-20 { font-size: 20px;}.fs-…

    2025年12月24日
    000
  • 如何在地图上轻松创建气泡信息框?

    地图上气泡信息框的巧妙生成 地图上气泡信息框是一种常用的交互功能,它简便易用,能够为用户提供额外信息。本文将探讨如何借助地图库的功能轻松创建这一功能。 利用地图库的原生功能 大多数地图库,如高德地图,都提供了现成的信息窗体和右键菜单功能。这些功能可以通过以下途径实现: 高德地图 JS API 参考文…

    2025年12月24日
    400
  • 如何使用 scroll-behavior 属性实现元素scrollLeft变化时的平滑动画?

    如何实现元素scrollleft变化时的平滑动画效果? 在许多网页应用中,滚动容器的水平滚动条(scrollleft)需要频繁使用。为了让滚动动作更加自然,你希望给scrollleft的变化添加动画效果。 解决方案:scroll-behavior 属性 要实现scrollleft变化时的平滑动画效果…

    2025年12月24日
    000
  • CSS mask 属性无法加载图片:浏览器问题还是代码错误?

    CSS mask 属性请求图片失败 在使用 CSS mask 属性时,您遇到了一个问题,即图片没有被请求获取。这可能是由于以下原因: 浏览器问题:某些浏览器可能在处理 mask 属性时存在 bug。尝试更新到浏览器的最新版本。代码示例中的其他信息:您提供的代码示例中还包含其他 HTML 和 CSS …

    2025年12月24日
    000
  • 如何为滚动元素添加平滑过渡,使滚动条滑动时更自然流畅?

    给滚动元素平滑过渡 如何在滚动条属性(scrollleft)发生改变时为元素添加平滑的过渡效果? 解决方案:scroll-behavior 属性 为滚动容器设置 scroll-behavior 属性可以实现平滑滚动。 html 代码: click the button to slide right!…

    2025年12月24日
    500
  • 如何用 CSS 实现链接移入效果?

    css 中实现链接移入效果的技巧 在 css 中模拟链接的移入效果可能并不容易,因为它们不会影响周围元素。但是,有几个方法可以实现类似的效果: 1. 缩放 最简单的方法是使用 scale 属性,它会放大元素。以下是一个示例: 立即学习“前端免费学习笔记(深入)”; .goods-item:hover…

    2025年12月24日
    000
  • 网页使用本地字体:为什么 CSS 代码中明明指定了“荆南麦圆体”,页面却仍然显示“微软雅黑”?

    网页中使用本地字体 本文将解答如何将本地安装字体应用到网页中,避免使用 src 属性直接引入字体文件。 问题: 想要在网页上使用已安装的“荆南麦圆体”字体,但 css 代码中将其置于第一位的“font-family”属性,页面仍显示“微软雅黑”字体。 立即学习“前端免费学习笔记(深入)”; 答案: …

    2025年12月24日
    000
  • 如何选择元素个数不固定的指定类名子元素?

    灵活选择元素个数不固定的指定类名子元素 在网页布局中,有时需要选择特定类名的子元素,但这些元素的数量并不固定。例如,下面这段 html 代码中,activebar 和 item 元素的数量均不固定: *n *n 如果需要选择第一个 item元素,可以使用 css 选择器 :nth-child()。该…

    2025年12月24日
    200
  • 如何用 CSS 实现类似卡券的缺口效果?

    类似卡券的布局如何实现 想要实现类似卡券的布局,可以使用遮罩(mask)来实现缺口效果。 示例代码: .card { -webkit-mask: radial-gradient(circle at 20px, #0000 20px, red 0) -20px;} 效果: 立即学习“前端免费学习笔记(…

    2025年12月24日
    000
  • 使用 SVG 如何实现自定义宽度、间距和半径的虚线边框?

    使用 svg 实现自定义虚线边框 如何实现一个具有自定义宽度、间距和半径的虚线边框是一个常见的前端开发问题。传统的解决方案通常涉及使用 border-image 引入切片图片,但是这种方法存在引入外部资源、性能低下的缺点。 为了避免上述问题,可以使用 svg(可缩放矢量图形)来创建纯代码实现。一种方…

    2025年12月24日
    100
  • 如何用纯代码实现自定义宽度和间距的虚线边框?

    自定义宽度和间距的虚线边框 提问: 如何创建一个自定义宽度和间距的虚线边框,如下图所示: 元素宽度:8px元素高度:1px间距:2px圆角:4px 解答: 传统的解决方案通常涉及使用 border-image 引入切片的图片来实现。但是,这需要引入外部资源。本解答将提供一种纯代码的方法,使用 svg…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信