




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、译Kinect for Windows SDK开发入门(十一):手势识别下:基本手势识别上文简要介绍了手势识别的基本概念和手势识别的基本方法,并以八种手势中的挥手(wave)为例讲解了如何使用算法对手势进行识别,本文接上文,继续介绍如何建立一个手部追踪类库,并以此为基础,对剩余 7中常用的手势进行识别做一些介绍。1. 基本的手势追踪手部追踪在技术上和手势识别不同,但是它和手势识别中用到的一些基本方法是一 样的。在开发一个具体的手势控件之前,我们先建立一个可重用的追踪手部运动的类库以方便我们后续开发。这个手部追踪类库包含一个以动态光标显示的可视化反馈机制。手部追踪和手势控件之间的交互高度松耦合。
2、首先在 Visual Studio中创建一个WPF控件类库项目。然后添加四个类:Kin ectCursorEve ntArgs.cs,Kinectln put.cs, CusrorAdorner.cs禾口Kin ectCursorMa nager.cs这四个类之间通过相互调用来基于用户手所在的位置来完成光标位置的管理。Kin ectI nput类包含了一些事件,这些事件可以在Kin ectCursorMa nager 和一些控件之间共享。Kin ectCursorEve ntArgs提供了一个属性集合,能够用来在事件触发者和监听者之间传递数据。KinectCursorManager用来管理从K
3、inect传感器中获取的骨骼数据 流,然后将其转换到 WPF坐标系统,提供关于转换到屏幕位置的可视化反馈,并寻找屏幕上的控件,将事件传递到这些控件上。最后CursorAdorner.cs类包含了代表手的图标的可视化元素。KinectCursorEventArgs 继承自 RoutedEventArgs 类,它包含四个属性: X、Y、Z 和Cursor X、Y、Z是一个小数,代表待转换的用户手所在位置的宽度,高度和深度值。Cursor用来存储 CursorAdorner 类的实例,后面将会讨论,下面的代码展示了 Kin ectCursorEve ntArgs 类的基本结构,其中包含了一些重载的构
4、造器。:RoutedEventArgspublic classKi nectCursorEve ntArgspublic doublepublic doublepublic doubleX get; set ; 丫 get; set ; Z get; set ; get; set ; public CursorAdor ner Cursor publicKinectCursorEventArgs(double x, double y)X = x;丫 = y;public Kin ectCursorEve ntArgs( Point point)X = poi nt.X;丫 = poi nt.Y
5、;RoutedEventArgs 基类有一个构造函数能够接收RoutedEvent作为参数。这是一个有点特别的签名,WPF中的UIEleme nt使用这种特殊的语法触发事件。下面的代码是Kin ectCursorEve ntArgs类对这一签名的实现,以及其他一些重载方法。publicKi nectCursorEve ntArgs(RoutedEvent routedEvent)base (routedEve nt) publicKi nectCursorEve ntArgs(RoutedEvent routedEvent,double x, double y, double z)base (
6、routedEve nt) X =x; Y = y; Z =乙publicKi nectCursorEve ntArgs(RoutedEvent routedEvent,Point point)base (routedEve nt) X =poi nt.X; Y = poi nt.Y; publicKi nectCursorEve ntArgs(RoutedEvent routedEvent,Point point, double z)base (routedEve nt) X =point.X; Y = point.Y; Z = z; publicKi nectCursorEve ntArg
7、s(RoutedEvent routedEvent,object source)base(routedEve nt, source) publicKi nectCursorEve ntArgs(RoutedEvent routedEvent,object source,double x, double y,double z)base(routedEve nt, source) X = x; Y = y; Z = z; publicKi nectCursorEve ntArgs(RoutedEvent routedEvent,object source,Point point)base(rout
8、edEve nt,source) X =poi nt.X;Y =poi nt.Y;publicKi nectCursorEve ntArgs(RoutedEvent routedEvent,object source,Point point, double z)base(routedEve nt, source) X = poin t.X; Y = poin t.Y; Z = z; 接下来,要在Kin ect In put类中创建事件来将消息从Kin ectCursorMa nager 中传递到可视化控件中去。这些事件传递的数据类型为Kin ectCursorEve ntArgs类型。在 Ki
9、n ect In put类中添加一个 Kin ectCursorEve ntHa ndler的代理类型:(1)添加一个静态的 routed event 声明。(2) 添力口 KinectCursorEnter, KinectCursorLeave,KinectCursorMove , KinectCursorActive 禾口 KinectCursorDeactivated 事件的 add 禾口 remove方法。下面的代码展示了三个和cursor相关的事件,其他的如KinectCursorActivated和Kin ectCursorDeactivated事件和这个结构相同:public d
10、elegate void KinectCursorEventHandler( object sender, KinectCursorEventArgse);public static classKin ect In putpublicstaticread onlyRoutedEve ntKinectCursorEnterEvent=EventManager .RegisterRoutedEvent(KinectCursorEnter , RoutingStrategy .Bubble,typeof (KinectCursorEventHandler), typeof (KinectInput
11、);public staticvoidAddK in ectCursorE nterHa ndler(Depe nden cyObjecto,Ki nectCursorEve ntHa ndlerhan dler)( UIElement )o).AddHandler(KinectCursorEnterEvent, handler);public static void RemoveKi nectCursorE nterHa ndler( Depe nden cyObject o,Ki nectCursorEve ntHa ndlerhan dler)( UIElement )o).Remove
12、Handler(KinectCursorEnterEvent, handler);publicstaticread onlyRoutedEve ntKinectCursorLeaveEvent=EventManager .RegisterRoutedEvent(KinectCursorLeave , RoutingStrategy .Bubble,typeof (KinectCursorEventHandler), typeof (KinectInput );public static voidAddK in ectCursorLeaveHa ndler(Depe nden cyObjecto
13、,Ki nectCursorEve ntHa ndlerhan dler)( UIElement )o).AddHandler(KinectCursorEnterEvent,handler);public static voidRemoveKi nectCursorLeaveHa ndler(Depe nden cyObject o,Ki nectCursorEve ntHa ndlerhan dler)( UIElement )o).RemoveHandler(KinectCursorEnterEvent, handler);注意到以上代码中没有声明任何GUI编程中的Click事件。这是因为
14、在设计控件类库时,Kinect中并没有点击事件,相反Kinect中两个重要的行为是enter和leave。手势图标可能会移入和移出某一个可视化控件的有效区域。如果要实现普通GUI控件的点击效果的话,必须在Kinect中对这一事件进行模拟,因为Kinect原生并不支持点击这一行为。CursorAdorner类用来保存用户手势图标可视化元素,它继承自WPF的Adorner类型。之所以使用这个类型是因为它有一个特点就是总是在其他元素之上绘制,这在我们的项目中非常有用,因为我们不希望我们的光标会被其他元素遮挡住。代码如下所示,我们默认的adorner对象将绘制一个默认的可视化元素来代表光标,当然也可以
15、传递一个自定义的可视化元素。public class CursorAdor ner : Adorner private read only UlEleme nt _ador nin gEleme nt;privateVisualCollectio n_visualChildre n;privateCan vas _cursorCa nvas;protected FrameworkElement _cursor;StroyBoard _gradie ntStopA ni mati on Storyboard;read only staticColor _backColor = Colors .W
16、hite;read only staticColor _foreColor = Colors .Gray;public CursorAdor ner( FrameworkEleme nt ador nin gEleme nt) base(ador nin gEleme nt)this ._adorningElement = adorningElement;CreateCursorAdor ner();thissHitTestVisible = false ;publicCursorAdor ner( FrameworkEleme ntador nin gEleme nt,FrameworkEl
17、eme ntinn erCursor)base(ador nin gEleme nt)this ._adorningElement = adorningElement;CreateCursorAdorner(i nn erCursor);thissHitTestVisible = false ;public FrameworkEleme nt CursorVisualget return _cursor; public voidCreateCursorAdor ner()var innerCursor = CreateCursor();CreateCursorAdorner(i nn erCu
18、rsor);protectedFrameworkEleme nt CreateCursor()var brush = new LinearGradientBrush();brush.EndPoint =new Point (0,1);brush.StartPoint =new Point (0, 0);brush.Gradie ntStops.Add(new Gradie ntStop (_backColor, 1);brush.Gradie ntStops.Add(new Gradie ntStop (_foreColor, 1);var cursor = new Ellipse ()Wid
19、th=50,Height=50,Fill=brush;retur n cursor;public voidCreateCursorAdor ner(FrameworkEleme nt inn erCursor)visualChildre n =new VisualCollection(this );cursorCa nvas =newCan vas();_cursor = inn erCursor;_cursorCa nvas.Childre n.Add( _visualChildre n.Add(Adorn erLayer layer = layer.Add(this );this ._cu
20、rsorCanvas);this ._cursorCanvas);AdornerLayer .GetAdornerLayer(_adorningElement);因为继承自Adorner基类,我们需要重写某些基类的方法,下面的代码展示了基类中的方法如何和 CreateCursorAdorner 方法中实例化的 _visualChildren和cursorCanvas字段进行绑定。protected override intVisualChildre nCou ntgetretur n_visualChildre n. Cou nt;protected override Visual GetVi
21、sualChild( int in dex)retur n_visualChildre ni ndex;protected override Size MeasureOverride( Size constraint) this ._cursorCanvas.Measure(constraint);return this ._cursorCa nvas.DesiredSize;protected overrideSize ArrangeOverride(Size finalSize)this ._cursorCanvas.Arrange(new Rect (finalSize);returnf
22、in alSize;CursorAdorner 对象也负责找到手所在的正确的位置,该对象的UpdateCursor方法如下,方法接受X,Y坐标位置作为参数。 然后方法在X,Y上加一个偏移量以使得图像的中 心在X, Y之上,而不是在图像的边上。另外,我们提供了该方法的一个重载,该重载告诉 光标对象一个特殊的坐标会传进去,所有的普通方法调用UpdateCursor将会被忽略。当我们在磁性按钮中想忽略基本的手部追踪给用户更好的手势体验时很有用。public void UpdateCursor( Point position,bool isOverride)_isOverride n = isOver
23、ride;_cursor.SetValue(Can vas.LeftProperty,positi on. X-(_cursor.ActualWidth/2);_cursor.SetValue( Canvas.LeftProperty,position.Y- (_cursor.ActualHeight / 2); public void UpdateCursor( Point position) return ;Can vas.LeftProperty,Can vas.LeftProperty,position.X- (_cursor.ActualWidth / 2);position.Y -
24、 (_cursor.ActualHeight / 2);if (_isOverriden)_cursor.SetValue(_cursor.SetValue(最后,添加光标对象动画效果。 当Kinect控件需要悬浮于一个元素之上, 在用户等 待的时候,给用户反馈一些信息告知正在发生的事情,这一点很有好处。下面了的代码展示了如何使用代码实现动画效果 :public virtual voidAnimateCursor( double milliSeconds) CreateGradie ntStopA nimatio n(milliSec on ds);if (_gradientStopAnima
25、tionStoryboard !=null )this , true );_gradie ntStopA nimatio nStoryboard.Begi n(public virtual voidStopCursorA nimatio n(double milliSec on ds)if (_gradientStopAnimationStoryboard !=null )_gradientStopAnimationStoryboard.Stop(this );public virtual voidCreateGradie ntStopA ni mati on(double milliSec
26、on ds) NameScopeSetNameScope( this , newNameScopeQ);var cursor = _cursor asShape;if (cursor =null )return ;var brush = cursor.FillasLinearGradientBrushvar stop1 = brush.GradientStopsO;var stop2 = brush.GradientStops1;this .RegisterName( GradientStop1 , stop1); this .RegisterName( GradientStop2 , sto
27、p2);DoubleA ni mati on offsetA ni mati on =n ewDoubleA ni matio n ();offsetA ni mati on. From = 1.0; offsetA ni mati on.To = 0.0;offsetA ni mati on .Durati on =TimeSpa n. FromMillisecon ds(milliSec on ds);Storyboard .SetTargetName(offsetAnimation,GradientStop1);Storyboard .SetTargetProperty(offsetAn
28、imation,n ewPropertyPath (Gradie ntStop .OffsetProperty);DoubleA ni matio n offsetA ni mati on2 =n ewDoubleA ni matio n ();offsetA ni mati on 2.From = 1.0;offsetA nimatio n2.To = 0.0;offsetA nimatio n2.Durati on =TimeSpa n.FromMillisec on ds(milliSec on ds);Storyboard .SetTargetName(offsetAnimation2
29、,GradientStop2);Storyboard .SetTargetProperty(offsetAnimation2,n ewPropertyPath (Gradie ntStop .OffsetProperty);_gradientStopAnimationStoryboard =newStoryboard ();_gradie ntStopA ni matio nStoryboard.Childre n.Add(offsetA nimatio n);_gradie ntStopA nimatio nStoryboard.Childre n.Add(offsetA nimatio n
30、2);_gradie ntStopA ni matio nStoryboard.Completed+=delegate _gradientStopAnimationStoryboard.Stop(this ); ;为了实现KinectCursorManager类,我们需要几个帮助方法,代码如下,GetElementAtScreenPoint方法告诉我们哪个 WPF对象位于X,Y坐标下面,在这个高度松散的结构中,GetEleme ntAtScree nPoi nt方法是主要的引擎,用来从Kin ectCurosrMa nager 传递消息到自定义控件,并接受这些事件。另外,我们使用两个方法来确定
31、我们想要追踪的骨 骼数据以及我们想要追踪的手。private staticUIEleme nt GetEleme ntAtScree nPoint(Poi nt point, Win dow wi ndow)if (!wi ndow .1 sVisible)return n ull;Poi nt win dowPo int = wi ndow.Poi ntFromScree n(poi nt);lln putEleme nteleme nt = wi ndow .In putHitTest(wi ndowPoi nt);if (elementis UIElement )return ( UIE
32、lement )element;elsereturn n ull;private staticSkeletonGetPrimarySkeleton( IEnumerable skeletons)Skeleton primarySkeleton = null ;foreach(Skelet onskelet onin skelet ons)if (skelet on. Track in gState !=Skelet on Track in gState.Tracked)continue ;if (primarySkeleton = null )primarySkelet on = skelet
33、 on;else if(primarySkeleto n.Positio n.Z skelet on .Positi on.Z)primarySkelet on = skelet on;returnprimarySkelet on;private static Joint ? GetPrimaryHand( Skeletonskeleton) JointType .HandLeft;JointType .HandRight;Joi ntTrack in gState.Tracked)Joint leftHa nd=skelet on.Join tsJoint rightHa nd=skelet
34、 on.Joints if (rightHa nd.Tracki ngState =if (leftHa nd.Tracki ngState !=Joi ntTracki ngState .Tracked)retur nrightHa nd;else if(leftHa nd.Positi on.Z rightHa nd.Positi on.Z)retur nrightHa nd;elsereturnleftHa nd;if (leftHa nd.Tracki ngState =Joi ntTracki ngState.Tracked)return leftHa nd;elsereturn n
35、 ull;Kin ectCursorMa nager应该是一个单例类。这样设计是能够使得代码实例化起来简单。任何和KinectCursorManager 工作的控件在KinectCursorManager 没有实例化的情况下 可以独立的进行 KinectCursorManager的实例化。这意味着任何开发者使用这些控件不需要 了解KinectCursorManager对象本身。相反,开发者能够简单的将控件拖动到应用程序中, 控件负责实例化 KinectCursorManager对象。为了使得这种自服务功能能和KinectCursorMange类一起使用,我们需要创建一个重载的Create方法来
36、将应用程序的主窗体类传进来。下面的代码展示了重载的构造函数以及特殊的单例模式的实现方法。public classKi nectCursorMa nagerprivateKi nectSe nsorkin ectSe nsor;privateCursorAdor nercursorAdor ner;private read only Window win dow;private UIEleme nt lastEleme ntOver;private boolisSkelet on Track in gActivated;private static boolisln itialized;priv
37、ate staticKin ectCursorMa nagerin sta nee;Create( Window window)new Ki nectCursorMa nager (win dow); true ;public static voidif (!is In itialized)in sta nee =isl nitialized =public static voidCreate( Window window, FrameworkElement cursor)if (!is In itialized)in sta nee =new Ki nectCursorMa nager (w
38、i ndow,cursor);islnitialized =true ;public static voidif (!is In itialized)in sta nee =isI nitialized =Create( Window window, KinectSensorsensor)new Ki nectCursorMa nager (win dow, sen sor); true ;public static void cursor)if (!is In itialized)in sta nee =isl nitialized =Create( Window window, Kinec
39、tSensornew Ki nectCursorMa nager true ;sen sor,FrameworkEleme nt(win dow, sen sor, cursor);public staticKi nectCursorMa nagerIn sta neeget returninstanee; privateKin ectCursorMa nager(Win dowwi ndow)this(wi ndow,Ki nectSe nsor.KinectSensors0) privateKi nectCursorMa nager(Win dow win dow,FrameworkEle
40、ment cursor):this(wi ndow,Ki nectSe nsor.Kin ectSe nsors0, cursor) privateKi nectCursorMa nager(Win dow wi ndow,Kin ectSe nsorsen sor):this(wi ndow,sen sor,n ull) privateKi nectCursorMa nager(Win dow win dow,Ki nectSe nsorsen sor, FrameworkEleme ntcursor)this .window = window;if (KinectSensor .Kinec
41、tSensors.Count 0)wi ndow.U nloaded +=if (this this ;delegate.ki nectSe nsor.Skeleto nStrea m.lsEn abled).kin ectSe nsor.Skelet on Stream.Disable();win dow.Loaded +=delegateif (cursor = null )cursorAdor ner=newCursorAdorner ( FrameworkElement )window.Content);elsecursorAdor ner=newCursorAdorner ( Fra
42、meworkElement )window.Content, cursor);this .kinectSensor = sensor;this .kinectSensor.SkeletonFrameReady += SkeletonFrameReady;this .kinectSensor.SkeletonStream.Enable(newTran sformSmoothParameters ();this .kinectSensor.Start();下面的代码展示了Kin ectCursorMa nager 如何和窗体上的可视化元素进行交互。当用户的手位于应用程序可视化元素之上时,Kin e
43、ctCursorMa nager对象始终保持对当前手所在的可视化元素以及之前手所在的可视化元素的追踪。当这一点发生改变时, KinectCursorManager会触发之前控件的leave事件和当前控件的enter事件。我们也保持对KinectSensor 对象的追踪,并触发 activated 和deactivated 事件。private voidSetSkeleto nTracki ngActivated()if (lastElementOver !=null & isSkeletonTrackingActivated =false )lastEleme ntOver.RaiseEve
44、nt(newRoutedEve ntArgs ( Kin ect In put .Ki nectCursorActivatedEve nt);isSkelet on Track in gActivated =true ;private voidSetSkeleto nTracki ngDeactivated()if (lastElementOver !=null & isSkeletonTrackingActivated =false )lastEleme ntOver.RaiseEve nt(newRoutedEve ntArgs ( Kin ect In put .Kin ectCurso
45、rDeactivatedEve nt);isSkelet on Tracki ngActivated =false ; private void Han dleCursorEve nts( Point point, double z) UIElement element = GetElementAtScreenPoint(point, window); if (element != null )eleme nt.RaiseEve nt(newpoi nt,z)point, z) Cursor =point, z) Cursor =Kin ectCursorEve ntArgs(Kin ect
46、In put .Kin ectCursorMoveEve nt,Cursor=cursorAdor ner );if (eleme nt != lastEleme ntOver)if (lastElementOver != null )lastEleme ntOver.RaiseEve nt(newKin ectCursorEve ntArgs(Kin ect In put .Kin ectCursorLeaveEve nt,cursorAdor ner );eleme nt.RaiseEve nt(newKin ectCursorEve ntArgs(Kin ect In put .Ki n
47、ectCursorE nterEve nt,cursorAdor ner );lastEleme ntOver = eleme nt;最后需要两个核心的方法来管理KinectCursorManger 类。SkeletonFrameReady 方法与之前一样,用来从Kinect获取骨骼数据帧时触发的事件。在这个项目中, SkeletonFrameReady方法负责获取合适的骨骼数据,然后获取合适的手部关节点数据。然 后将手部关节点数据传到UpdateCusror方法中,UpdateCursor方法执行一系列方法将Kinect骨骼空间坐标系转化到WPF的坐标系统中,Kinect SDK中MapSk
48、eletonPointToDepth方法提供了这一功能。SkeletonToDepthlmage方法返回的X,Y值,然后转换到应用程序中 实际的宽和高。和 X,Y不一样,Z值进行了不同的缩放操作。简单的从Kinect深度摄像机中获取的毫米数据。代码如下,一旦这些坐标系定义好了之后,将他们传递到 HandleCursorEvents 方法然后 CursorAdorner对象将会给用户以反馈。相关代码如下:private voidSkeletonFrameReady( object sender, SkeletonFrameReadyEventArgs e)using (SkeletonFrame
49、 frame = e.OpenSkeletonFrame()if (frame = null | frame.SkeletonArrayLength = 0)return ;Skeleton skeletons =newSkeleton frame.SkeletonArrayLength;frame.CopySkelet on DataTo(skelet on s);Skeleton skeleton = GetPrimarySkeleton(skeletons);if (skeleton = null )SetHa ndTrack in gDeactivated();elseJoint ?
50、primaryHand = GetPrimaryHand(skeleton);if (primaryHand.HasValue)UpdateCursor(primaryHa nd.Value);elseSetHa ndTrack in gDeactivated();private voidSetHa ndTrack in gDeactivated()cursorAdorner.SetVisibility(false );if (lastElementOver !=null & isHandTrackingActivated = true )lastEleme ntOver.RaiseEve n
51、t(n ewRoutedEve ntArgs (Kin ect In put.Ki nectCursorDeactivatedEve nt); ;isHandTrackingActivated =false ;private void UpdateCursor( Joint hand)var point=kinectSensor.MapSkeletonPointToDepth(hand.Position,ki nectSe nsor.DepthStream.Format);float x = point.X;float y = point.Y;float z = point.Depth;x =
52、 ( float )(x * window.ActualWidth / kinectSensor.DepthStream.FrameWidth);y = ( float )(y * window.ActualHeight / kinectSensor.DepthStream.FrameHeight);Point cursorPoint =newPoint (x, y);Han dleCursorEve nts(cursorPo int, z); cursorAdor ner.UpdateCursor(cursorPoi nt);至此,我们已经简单实现了一些基础结构,这些仅仅是实现了将用户手部的
53、运动显示在屏幕上。现在我们要创建一个基类来监听光标对象的事件,首先创建一个Kin ectButt on 对象,该对象继承自WPFButton类型。定义三个之前在Kin ectI nput中定义好的事件,同时创建这些事件的添加删除方法,代码如下:public classKin ectButt on: Butt onpublicstaticread only RoutedEve nt Ki nectCursorE nterEve ntKin ect In put .Kin ectCursorE nterEve nt.AddOw ner(typeof (Kin ectButt on );publicstaticread only RoutedEve nt Kin ectCursorLeaveEve ntKin ect In put .Ki nectCursorLeaveEve nt.AddOw ner(typeof (Ki nectButt on );publicstaticread only RoutedEve nt Kin ectCursorMoveEve ntKin ectI nput .Kin ectCursorMoveEve nt.AddOw ner(typeof ( Kin ec
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025至2030年中国直接加热式滚筒干燥机行业投资前景及策略咨询报告
- 健康管理咨询服务合同
- 团队协作与生产效率提升计划
- 护士职业素养提升的措施与总结计划
- 个人职业发展的规划计划
- 如何保持长期的财务稳定计划
- 年度数字化转型与进度计划
- 2024年中国稀土集团有限公司招聘考试真题
- 保安工作中的科技应用与创新计划
- 2024年济南水务集团有限公司招聘考试真题
- 光伏发电站项目安全技术交底资料
- JJF(京) 63-2018 微差压表校准规范
- 富血小板血浆(PRP)临床实践与病例分享课件
- EHS(环境健康安全)管理制度
- GB/T 32124-2024磷石膏的处理处置规范
- 装配钳工试题及答案
- 煤矿安全风险分级管控与隐患排查治理双重预防机制建设指南
- 农业推广学复习要点
- 人员素质测评理论与方法
- 【人教版】《劳动教育》六上 劳动项目六《制造手工肥皂》课件
- 部编版四年级语文下册期中试卷+ 答题卡(含答案)
评论
0/150
提交评论