基于人体关节夹角的人体动作识别算法(代码+数据集)

为此本文提出了一个基于人体关节夹角的人体动作识别算法,主要做了以下工作:
(1)提出了一个可解释性强,耗费算力较少且鲁棒性较高的基于人体关节夹角的人体动作序列的特征抽取方法。
(2)本文所使用的分类模型是一个融合了SVM,lightgbm等6个分类模型的集成学习模型,并且使用了一个结合了Boosting和Bagging的集成学习策略。
最终本论文提出的方法在G3D数据集上的分类准确率为92.3%。该结果验证论文中方法的可行性

论文提出的算法的流程图

本文当中所提出的人体动作分类算法如上图所示,本算法主要分为人体3D关节点坐标获取,特征工程,模型集成,模型训练4个部分:
(1)人体关节点坐标获取部分:先使用Openpose的body25预训练模型结合训练集自带的深度图来提取出人体关键点3D坐标
(2)特征工程部分:先抽取人体动作特征向量,具体方法为:
①在每一个人体动作序列中等间距取50张图片
②基于得到的人体关键点3D坐标,每一张图片我们都可以得到 24 个肢体向量。然后通过24 个肢体向量两两之间的夹角的余弦值,可以得到一个长度为276的向量
③将50张图片得到的向量按照顺序拼接成长为13800的向量而后用基于方差的Filter方法和基于随机森林的RFE方法来特征选择。
(3)模型集成部分:因为logistic回归拟合能力较弱,所以先让它进行Adaboost增强拟合能力,而后再把它和SVM,KNN,随机森林,Xgboost,lightgbm一起进行基于软投票策略的Bagging方法后输出最终的分类结果。
(4)模型训练部分:按照训练集和测试集5:5的比例划分数据集,而后按照划分的数据集使用网格调参法调出每一个基分类模型的最佳参数,而后将其带入集成学习模型当中进行最终的模型分类预测。

论文方法的实验以及实验结果结果

本文先使用网格调参法调出基分类器的最佳参数,具体调参的过程如下图所示:

根据调参的结果,各个基分类器的最佳参数如下:
(1)logistic回归:“L2”惩罚项,C取0.214
(2)支持向量机:核函数取“poly”,C取0.238
(3)KNN:使用kdtree进行搜索,K值取3
(4)随机森林:estimators的最佳参数为142,criterion选择“entropy”这个选项
(5)Xgboost:最佳estimators的个数为25
(6)Lightgbm:最佳estimators的个数为67
(7)在实验当中Adaboost中的基分类器的个数设定为100
将调参结果输入到集成分类学习模型当中以后分类准确率达到了92.3%,得到的分类结果的混淆矩阵如下图所示:

一些重要东西的下载

①数据集使用的是G3D数据集:call me
②Openpose预处理以后的数据: call me 

 

代码运行

cd ./code
①集成分类模型运行结果 python proposed_method.py
②SVM画图 python SVM.py
①KNN画图 python KNN.py
④随机森林画图 python randomforest.py
⑤xgboost和Lightgbm画图 python xgboost_and_lightgbm.py
⑥logistic画图 python logistic.py
⑦特征抽取和特征选择:先把Openpose预处理以后的数据解压好以后放到code/data目录下,而后运行 python feature_extracting.py ,但是使用这个代码运行所得到的特征抽取结果来运行模型就可能得到和论文得到的结果不一样,因为RFE的运行结果每一次都会有细小的差异
⑧如果你想直接使用论文实验的数据的话就把Experimental data 文件夹里面的数据放到code文件夹下面,这样就不用再跑一遍特征抽取了.

feature_extracting.py

import  numpy as np
import pandas as pd
import os
import sklearn as sk
import matplotlib.pyplot as plt
import json
import math
import os
from PIL import Image
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.feature_selection import RFE
from sklearn.feature_selection import VarianceThreshold
def fun(x):return x >> 3
def depth2xyz(depth_map, depth_cam_matrix, flatten=False, depth_scale=1000):fx, fy = depth_cam_matrix[0, 0], depth_cam_matrix[1, 1]cx, cy = depth_cam_matrix[0, 2], depth_cam_matrix[1, 2]h, w = np.mgrid[0:depth_map.shape[0], 0:depth_map.shape[1]]z = depth_map/518x = wy = hxyz = np.dstack((x, y, z))return xyzdef getdata(filename1,filename2):jsonname = filename1picurename = filename2picurename = picurename.replace('Colour', 'Depth')with open(jsonname) as f:keypoints = json.load(f)body25 = keypoints['people'][0]['pose_keypoints_2d']img = Image.open(picurename)img = np.array(img)img = fun(img)img = np.where(img > 8192, img - 8192, img)depth_map = img.Tdepth_cam_matrix = np.array([[518, 0, 325.5],[0, 519, 253.5],[0, 0, 1]])targrt = depth2xyz(depth_map, depth_cam_matrix)x = []y = []z = []for i in range(0, 74, 3):D = targrt[int(body25[i])-1][int(body25[i + 1])-1]x.append(body25[i])y.append(body25[i + 1])z.append(D[2])return x, y, zdef VectorCosine(x, y):a = 0b = 0c = 0for i in range(0, len(x)):a += x[i] * y[i]b += x[i] * x[i]c += y[i] * y[i]c += 0.00001b += 0.00001result = a / (math.sqrt(b) * math.sqrt(c))return resultdef cosvalues(filename1,filename2):x, y, z = getdata(filename1,filename2)result = []bodys = [[15, 17], [15, 0], [16, 0], [16, 18], [0, 1], [2, 1], [8, 1], [5, 1],[2, 3], [3, 4], [5, 6], [6, 7], [8, 9], [8, 12], [9, 10], [12, 13],[10, 11], [13, 14], [11, 24], [14, 21], [11, 22], [14, 19], [22, 23], [19, 20]]i = 0bodys1 = bodyslengths = []for body in bodys:for body1 in bodys1[i + 1:24:1]:vector1 = np.array([x[body[0]] - x[body[1]], y[body[0]] - y[body[1]], z[body[0]] - z[body[1]]])vector2 = np.array([x[body1[0]] - x[body1[1]], y[body1[0]] - y[body1[1]], z[body1[0]] - z[body1[1]]])array = np.array([np.linalg.norm(vector1), np.linalg.norm(vector2)])cosine = VectorCosine(vector1, vector2)result.append(cosine)i = i + 1return result
Root="data/feature-extracting-result"
Sports=os.listdir(Root)
print(Sports)for Sport in Sports:sportpath=Root+'/'+SportActors=os.listdir(sportpath)for Actor in Actors:Actionspath=sportpath+'/'+ActorActions=os.listdir(Actionspath)for Action in Actions:Finalpath = Actionspath + '/' + Actionfor i in np.linspace(1, 1, 1):i = int(i)jsonpath = Finalpath+ '/output/' + str(i) + '_keypoints.json'depthpath = Finalpath+ '/depth/' + str(i) + '.png'Matrix = np.array(cosvalues(jsonpath, depthpath))for i in np.linspace(2, 50, 49):i = int(i)jsonpath = Finalpath+ '/output/' + str(i) + '_keypoints.json'depthpath = Finalpath+ '/depth/' + str(i) + '.png'Vector = np.array(cosvalues(jsonpath, depthpath))Matrix = np.vstack((Matrix, Vector))Matrixname = Finalpath + '/' + "Matrix.csv"Matrix.tofile(Matrixname, sep=',', format='%1.5f')print(Sport,"is Done")
Root="data/feature-extracting-result"
sports=os.listdir(Root)
Class=0
'''Bowling是,0,Drving是1.Fighting是2,FPS是3,Golf是4,Misc是5,Tennis是6'''
result=pd.read_csv("data/feature-extracting-result/Bowling/Actor_1/1/Matrix.csv",header=None)
result["class"]=1
data=pd.DataFrame(columns=result.columns)
for sport in sports:Actors=os.listdir(Root+"/"+sport)for Actor in Actors:bianshus=os.listdir(Root+"/"+sport+"/"+Actor)for bianshu in bianshus:filename=Root + "/" + sport + "/" + Actor + "/" + bianshu+"/Matrix.csv"data1=pd.read_csv(filename,header = None)data1["class"]=Classdata=data.append(data1)Class=Class+1
data.to_csv("future_extracting_result.csv")
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=data.drop(["class"],axis=1)
sel = VarianceThreshold(threshold=0.16)
###选取0.16的原因:若特征是伯努利随机变量,假设p=0.8,即二分类特征中某种分类占到80%以上的时候删除特征
#因为有80%都是相似数据,对于数据的整体的特征已经没有太多的区分意义
traindata = sel.fit_transform(traindata)
print("variance filtler is Done")
RFC_ = RFC(n_estimators =25,random_state=0)
traindata = RFE(RFC_,n_features_to_select=500).fit_transform(traindata,target)
print("RFE via randomforest is Done")
selectdata=pd.DataFrame(traindata)
selectdata.to_csv("future_selection_result.csv",index=False)
print("Feature extraction is Done")

 logistic.py

import numpy
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
from sklearn.svm import SVC
import numpy as np
import pandas as pd
from lightgbm import LGBMClassifier
from sklearn import svm
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from xgboost import XGBClassifier
from sklearn.neighbors import  KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import RFE
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']='SimHei' #设置字体
plt.rcParams["font.size"]=10 #设置字号
plt.rcParams["axes.unicode_minus"]=False #正常显示负号
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=pd.read_csv("future_selection_result.csv")
X_train,X_test, y_train, y_test=train_test_split(traindata,target,test_size=0.5,random_state=2)
model1=LogisticRegression(random_state=2)
C=np.linspace(0.0001,1,20)
A=[]
B=[]
for i in C:model=LogisticRegression(C=i,random_state=2)model.fit(X_train, y_train)y_pred=model.predict(X_test)A.append(i)B.append(accuracy_score(y_test,y_pred))
print(A)
print(B)
plt.ylabel("分类准确率")#横坐标名字
plt.xlabel("C")#纵坐标名字
plt.plot(A, B)
plt.show()

proposed_method.py

import numpy
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
from sklearn.svm import SVC
import numpy as np
import pandas as pd
from lightgbm import LGBMClassifier
from sklearn import svm
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from xgboost import XGBClassifier
from sklearn.neighbors import  KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import RFE
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import VotingClassifier
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']='SimHei' #设置字体
plt.rcParams["font.size"]=10 #设置字号
plt.rcParams["axes.unicode_minus"]=False #正常显示负号
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=pd.read_csv("future_selection_result.csv")
print(traindata.shape)
X_train,X_test, y_train, y_test=train_test_split(traindata,target,test_size=0.5,random_state=2)
#--------------------------------(1)logistic回归------------------------------------------
logistic=LogisticRegression(random_state=2,penalty="l2",C=0.214)
model1=AdaBoostClassifier(estimator=logistic,n_estimators=100,random_state=1)
model1.fit(X_train,y_train)
y_pred1=model1.predict(X_test)
print("构建的logistic+Adaboost分类模型的准确率为:",accuracy_score(y_test,y_pred1))
#---------------------------------(2)LGBT-----------------------------------------------
lgbt=LGBMClassifier(n_estimators=67,booster="gbtree",random_state=2)
lgbt.fit(X_train,y_train)
y_pred2=lgbt.predict(X_test)
print("构建的LGBT分类模型的准确率为:",accuracy_score(y_test,y_pred2))
#-----------------------------------(3)XGboost---------------------------------------------
xgb=XGBClassifier(n_estimators=25,booster="gbtree",random_state=2)
xgb.fit(X_train,y_train)
y_pred3=xgb.predict(X_test)
print("构建的XGboost分类模型的准确率为:",accuracy_score(y_test,y_pred3))
#-----------------------------------(4)KNN---------------------------------------------
KNN=KNeighborsClassifier(n_neighbors=3)
KNN.fit(X_train,y_train)
y_pred4=KNN.predict(X_test)
print("构建的KNN分类模型的准确率为:",accuracy_score(y_test,y_pred4))
#-----------------------------------(5)RandomForest---------------------------------------------
RF=RFC(n_estimators=142,criterion="entropy",random_state=1)
RF.fit(X_train,y_train)
y_pred5=RF.predict(X_test)
print("构建的RandomFores分类模型的准确率为:",accuracy_score(y_test,y_pred5))
#-----------------------------------(6)SVM多分类器---------------------------------------------------
svm=SVC(probability=True)
svm.fit(X_train,y_train)
y_pred6=svm.predict(X_test)
print("构建的SVM分类模型的准确率为:",accuracy_score(y_test,y_pred6))
#------------------------------Bagging集成模型一-----------------------------------------------------
voting=VotingClassifier(estimators=[('log',model1),("lgbt",lgbt),('KNN',KNN),('RF',RF),('svc',svm),('xgb',xgb)],voting='soft',weights=[1,1,1,1,1,1])
voting.fit(X_train,y_train)
y_pred=voting.predict(X_test)
print("构建的Bagging分类模型的准确率为:",accuracy_score(y_test,y_pred))
confusion = confusion_matrix(y_test, y_pred)
plt.imshow(confusion, cmap=plt.cm.Blues)
indices = range(len(confusion))plt.colorbar()plt.xlabel("真实标签")plt.ylabel("预测的标签")plt.title("混淆矩阵")
plt.xticks(indices, ['打保龄球','驾驶',"搏击",'射击','高尔夫','混合动作',"网球"])
plt.yticks(indices, ['打保龄球','驾驶',"搏击",'射击','高尔夫','混合动作',"网球"])
for first_index in range(len(confusion)):for second_index in range(len(confusion)):plt.text(first_index, second_index, confusion[first_index][second_index])plt.show()

randomforest.py

import numpy
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
from sklearn.svm import SVC
import numpy as np
import pandas as pd
from lightgbm import LGBMClassifier
from sklearn import svm
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from xgboost import XGBClassifier
from sklearn.neighbors import  KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import RFE
from matplotlib import pyplot as plt
from matplotlib import font_manager
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']='SimHei' #设置字体
plt.rcParams["font.size"]=10 #设置字号
plt.rcParams["axes.unicode_minus"]=False #正常显示负号
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=pd.read_csv("future_selection_result.csv")
print(traindata)
X_train,X_test, y_train, y_test=train_test_split(traindata,target,test_size=0.5,random_state=2)
estimators=np.linspace(1,200,50).astype(int)
A=[]
B=[]
A1=[]
A2=[]
A3=[]
for i in estimators:model=RFC(n_estimators=i,criterion="gini",random_state=2)model.fit(X_train, y_train)y_pred=model.predict(X_test)A.append(i)B.append(accuracy_score(y_test,y_pred))
for i in estimators:model=RFC(n_estimators=i,criterion="entropy",random_state=2)model.fit(X_train, y_train)y_pred=model.predict(X_test)A2.append(accuracy_score(y_test,y_pred))
for i in estimators:model=RFC(n_estimators=i,criterion="log_loss",random_state=2)model.fit(X_train, y_train)y_pred=model.predict(X_test)A3.append(accuracy_score(y_test,y_pred))
print(estimators)
print(A2)
print(B)
plt.ylabel("分类准确率")#横坐标名字
plt.xlabel("基学习器个数")#纵坐标名字
plt.plot(A, B, c='green',  label="基尼",markersize = 2,alpha=0.7)
plt.plot(A,A2,c='blue', linestyle='--' ,label="熵",markersize = 2,alpha=0.5)
plt.grid(True, linestyle='--', alpha=0.5)
plt.legend(loc='lower right')
plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/51999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PCL 移动立方体三维重建——RBF算法【2024最新版】

目录 一、算法原理1、算法概述2、参考文献二、代码实现三、结果展示四、相关链接本文由CSDN点云侠原创,原文链接,首发于:2024年9月1日。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、算法概述 该算法实现的是Reconstruction a…

Java并发线程 共享模型之管程 5

1. 生产者消费者 package cn.itcast.testcopy;import cn.itcast.n2copy.util.Sleeper; import lombok.extern.slf4j.Slf4j;import java.util.LinkedList;/*** ClassName: Test21* Package: cn.itcast.testcopy* Description: 生产者消费者** Author: 1043* Create: 2024/9/4 - …

软件测试之压力测试知识总结

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 压力测试 压力测试是一种软件测试,用于验证软件应用程序的稳定性和可靠性。压力测试的目标是在极其沉重的负载条件下测量软件的健壮性和错误处理能力&…

LNMP环境搭建(Linux+nginx+Mysql+PHP)超详细攻略

目录 一.LNMP简介 1.1LNMP架构的特点 二.详细安装步骤 2.1MySQL安装 2.1-1Yum安装 2.1-2 编译安装 2.1-3二进制安装 2.1-4 RPM安装 2.2Nginx安装 2.2-1编译安装nginx 2.2-2yum安装nginx 2.3验证Nginx安装 2.4PHP安装 2.4-1编译安装PHP 2.4-2yum安装PHP 2.5 Nginx 配…

PMP–一、二、三模、冲刺、必刷–分类–14.敏捷–技巧--累积流图

文章目录 技巧一模二模三模14.敏捷–敏捷团队的衡量结果–累积流图:1、 敏捷项目的项目经理担心团队在最近的迭代中失去了动力。项目经理应该使用哪两种工具来分析团队绩效?(选择两个) 冲刺必刷7.成本管理--挣值分析燃尽图仅能了解…

From Man vs Machine to Man + Machine

From Man vs. Machine to Man Machine: The Art and AI of Stock Analyses 论文阅读 文章目录 From Man vs. Machine to Man Machine: The Art and AI of Stock Analyses 论文阅读 AbstractConstruction and Performance of the AI AnalystMethodologyThe Performance of Ana…

一款用于分析java socket连接问题的工具

network-tools 介绍 network-tools基于sun jdk、Oracle jdk开发,拦截基于java socket请求,它包括 ​ http 客户端 ​ jdbc 客户端 ​ mq 客户端 ​ redis 客户端 目前提供如下功能: ​ 最近端点连接情况 ​ 最近与远程端点连接情况&am…

计算机网络 第1章 概述

文章目录 计算机网络概念计算机网络的组成计算机网络的功能三种数据交换技术电路交换(Circuit Switching)报文交换(message)分组交换 三种交换方式性能对比计算机网络的分类计算机网络的性能指标性能指标1:速率性能指标…

【优化】Nginx 配置页面请求不走缓存 浏览器页面禁用缓存

【优化】Nginx 配置页面请求不走缓存 禁用缓存 目录 【优化】Nginx 配置页面请求不走缓存 禁用缓存 对所有请求禁用缓存 对特定location禁用缓存 注意事项 全局禁用缓存 要配置Nginx使其不缓存内容,通常是指禁止浏览器缓存响应的内容,或者是在代理…

单片机-串口通信(二)

目录 一、串口概念 1.相关概念: 按数据传输方式分类: 按时钟分类 二、STM32F103ZET6中串口 USART特性: NRZ数据格式: 三、配置串口通信 查看硬件原理图 软件配置流程 USART相关的寄存器 新建my_usart1.c和my_usart1.h …

将Google Chrome或Microsoft Edge浏览器的地址栏隐藏的方法

将Google Chrome或Microsoft Edge浏览器的地址栏隐藏的方法 目标效果示范 我们以百度首页为例,普通模式启动的页面通常会显示地址栏,如下图所示: 而本文要实现的效果是隐去地址栏和书签栏(如果有的话),无…

JDBC |封装JDBCUtils|PreparedStatement|事务|批处理|数据库连接池| Blob类型数据的读写|Apache—DBUtils简介

一.概述 在Java中,数据库存取技术可分为如下几类: JDBC直接访问数据库JDO技术(Java Data Object)第三方O/R工具,如Hibernate, Mybatis 等 JDBC是java访问数据库的基石,JDO, Hibernate等只是更好的封装了J…

交流耦合同相放大电路设计

1 简介 该电路在单电源供电时,可放大交流信号,并可对输出信号进行电平抬升,以使其集中于电源电压信号的二分之一处。 2 设计目标 2.1 输入 2.2 输出 2.3 电源 2.4 截止频率下限() 2.5 截止频率上限(&…

elementUI根据列表id进行列合并@莫成尘

本文章提供了elementUI根据列表id进行列合并的demo&#xff0c;效果如图&#xff08;可直接复制代码粘贴&#xff09; <template><div id"app"><el-table border :data"tableList" style"width: 100%" :span-method"objectS…

物联网之硬件元器件基础知识介绍、集成电路、电阻器、电容器、电感器、二极管、三极管、晶体管、连接器、传感器、开关、电源

MENU 前言电子元件采购网址三极管持续更新中 前言 序言 硬件元器件是电子设备和系统的基本构成部分&#xff0c;它们在电子产品的设计、制造和功能实现中起着至关重要的作用。 电阻器(Resistor) 功能&#xff1a;电阻器用于限制电流流过电路的流动&#xff0c;并分配电压。它们…

【数据结构】-----哈希

目录 一、哈希表概念 二、哈希函数 三、哈希冲突 Ⅰ、定义 Ⅱ、解决 ①闭散列--开放定址法 线性探测 二次线性探测 ②开散列--链地址法&#xff08;哈希桶&#xff09; 问题&#xff1a;哈希表何时扩容&#xff1f; 一、哈希表概念 哈希表又称散列表&#xff0c;它是一…

python网络爬虫(四)——实战练习

0.为什么要学习网络爬虫 深度学习一般过程:   收集数据&#xff0c;尤其是有标签、高质量的数据是一件昂贵的工作。   爬虫的过程&#xff0c;就是模仿浏览器的行为&#xff0c;往目标站点发送请求&#xff0c;接收服务器的响应数据&#xff0c;提取需要的信息&#xff0c…

【408DS算法题】034进阶-22年真题_判断顺序存储二叉树是否是BST

Index 真题题目分析实现总结 真题题目 已知非空二叉树T的结点值均为正整数&#xff0c;采用顺序存储方式保存&#xff0c;数据结构定义如下: typedef struct { // MAX_STZE为已定义常量int SqBiTNode[MAX_SIZE]; // 保存二叉树结点值的数组int ElemNum; …

Python简易IDE工作界面制作

、 休闲一下&#xff0c;学习编程还是要学习一些界面编程&#xff0c;能够根据需要制作图形操作界面&#xff0c;这样我们开发的程序才能方便操作和使用&#xff0c;同时获得更友好的人机交互体验。下面是一个用PyQt5制作的简易界面&#xff0c;供大学参考。如下图所示&a…