Hiển thị các bài đăng có nhãn c#. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn c#. Hiển thị tất cả bài đăng

Thứ Tư

Lập trình với Kinect .NET SDK


Kinect đánh dấu thời kì chơi game tương tác, cho người chơi cảm giác như thật. Với những người lập trình, câu hỏi đặt ra là: "Làm sao để làm được điều đó"? Và điều này dễ dàng hơn khi Microsoft ra mắt thư viện Kinect .NET SDK. Bài viết giới thiệu các bạn làm quen với thư viện SDK.
 
I. Khởi động 
Để làm việc với Kinect .NET SDK chúng ta bắt buộc phải có môi trường làm việc như sau:

Hệ điều hành: Windows 7 (32bit hoặc 64 bit).

Phần cứng: Cấu hình máy tính với CPU dual-core, 2,66GHz trở lên; card màn hình hỗ trợ Microsoft DirectX 9.0c; RAM tối thiểu 2GB; Quan trọng nhất là thiết bị Kinect.

Phần mềm: Microsoft Visual Studio 2010 Express hoặc các phiên bản khác của Visual Studio 2010, tải tại địa chỉ http://www.microsoft.com/visualstudio/en-us; Microsoft .NET Framework 4.0 sẽ được cài đặt khi cài VS 2010; Kinect .NET SDK Beta, tải tại địa chỉ http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/download.aspx.

II. Bước 2: Nhập cuộc
Bước tiếp theo chúng ta tạo một project để bắt đầu lập trình cho thiết bị Kinect. Bạn có thể tạo project theo dạng mà bạn thích: Windows Form, WPF… Tuy nhiên chúng tôi khuyên bạn nên sử dụng WPF để xử lí hình ảnh tốt hơn. Trong bài viết này chúng tôi tạo project theo dạng WPF.

Trong bài viết này chúng tôi cũng mặc định các bạn đã biết lập trình trên nền WPF nên chúng tôi chỉ tập trung vào cách khai thác thư viện Kinect .NET SDK.

Để sử dụng thư viện Kinect .NET SDK, chúng ta Add reference đến Microsoft.Research.Kinect.Nui ở thư mục C:\Program Files (x86)\Microsoft Research KinectSDK hoặc C:\Program Files\Microsoft Research KinectSDK
Sau khi đã Add reference thành công, chúng ta bắt tay vào khởi động và tắt thiết bị Kinect.

Khởi động thiết bị

Trước hết chúng ta khai báo đối tượng Runtime, đối tượng này là đại diện cho thiết bị Kinect

Runtime kinect = new Runtime();

Đối tượng Runtime thuộc namespace Microsoft.Research.Kinect.Nui do đó trước khi sử dụng chúng ta phải khai báo:
using Microsoft.Research.Kinect.Nui;
Sau đó tiến hành khởi tạo thiết bị với các thông số khởi tạo:

kinect.Initialize(RuntimeOptions.UseColor)

Có nhiều tùy chọn khởi tạo, tùy vào mục đích sử dụng mà chúng ta lựa chọn những thông số cho phù hợp:

RuntimeOptions.UseDepth: Khởi tạo thiết bị để lấy theo độ sâu

RuntimeOptions.UseSkeletalTracking: khởi tạo thiết bị để theo dõi hoạt động cử chỉ của người

RuntimeOptions.UseDepthAndPlayerIndex: khởi tạo thiết bị cho phép nhận dạng nhiều nhân vật.

Các thông số này có thể kết hợp với nhau bằng toán tử ( | )

kinect.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseDepth | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseDepthAndPlayerIndex);
Tắt thiết bị
 
Như vậy là thiết bị đã sẵn sàng cho chúng ta khai thác. Sau khi sử dụng xong, chúng ta tắt thiết bị bằng phương thức:
kinect.Uninitialize();
Tóm lại, để sử dụng và tắt thiết bị Kinect chúng ta thực hiện các phương thức sau:
Runtime kinect;
private void InitKinectDevice()
{
kinect = new Runtime();
kinect.Initialize(RuntimeOptions.UseColor| RuntimeOptions.UseDepth|
RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseDepthAndPlayerIndex);
}
private void ShutdownKinectDevice()
{
kinect.Uninitialize();
}
III. Thao tác với camera
  
Dạng chiều sâuDạng ảnh màu bình thường
Có hai việc mà chúng ta có thể làm được với Camera:

- Lấy hình ảnh ghi nhận được

- Điều khiển hướng quay.

1. Lấy hình ảnh ghi nhận được

Camera của Kinect hỗ trợ chúng ta lấy 2 dạng hình ảnh từ camera: hình ảnh màu bình thường và hình ảnh dạng chiều sâu.

Để lấy hình ảnh chúng ta cần làm những công đoạn sau:

• Mở camera với chế độ cần lấy hình ảnh

//Mở camera với chế độ màu bình thường

kinect.VideoStream.Open(ImageStreamType.Video, 2,ImageResolution.Resolution640x480, ImageType.ColorYuv);
//Mở camera với chế độ nhìn theo chiều sâu

kinect.DepthStream.Open(ImageStreamType.Depth, 2,

ImageResolution.Resolution320x240,ImageType.DepthAndPlayerIndex);

Tùy theo mục đích sử dụng mà chúng ta mở một trong 2 phương thức trên hoặc mở cả hai chế độ.
• Hiển thị hình ảnh ghi nhận được

Để hình ảnh ghi nhận được hiển thị một cách liên tục, thư viện Microsoft .NET SDK hỗ trợ 2 hàm sự kiện để lấy hình ảnh từ camera. Sự kiện này sẽ được thực hiện khi camera ghi nhận được hình ảnh.
// sự kiện lấy hình ảnh dạng màu bình thường

kinect.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(KinectVideoFrameReady);
// sự kiện lấy hình ảnh dạng chiều sâu

kinect.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(KinectDepthFrameReady);

Hình ảnh ghi nhận được sẽ được hiển thị lên control Image có tên là videoColor và depthVideo

void KinectVideoFrameReady(object sender,ImageFrameReadyEventArgs e)
{
PlanarImage Image = e.ImageFrame.Image;
videoColor.Source = BitmapSource.Create(Image.Width, Image.Height, 96, 96, PixelFormats.Bgr32,null, Image.Bits, Image.Width * Image.BytesPerPixel);
}
void KinectDepthFrameReady(object sender, ImageFrameReadyEventArgs e)
{
PlanarImage Image = e.ImageFrame.Image;
byte[] convertedDepthFrame = convertDepthFrame( Image .Bits);
depth.Source = BitmapSource.Create(Image.Width, Image.Height,96,96,PixelFormats.Bgr32, null, convertedDepthFrame, Image.Width * 4);
}
2. Điều khiển hướng quay của thiết bị Kinect
Camera hướng lênCamera hướng xuống
Điều khiển hướng quay của thiết bị Kinect được điều khiển bởi thuộc tính ElevationAngle trong đối tượng camera.
Khởi tạo đối tượng:

Camera cam= kinect. NuiCamera

Cho camera hướng lên:

cam.ElevationAngle = camElevation Angle +5;

Cho camera hướng xuống:

cam.ElevationAngle = camElevation Angle -5;

Lưu ý là góc quay của camera chỉ là +25 và -25 thôi. Vượt ra khỏi giới hạn này thì sẽ bị lỗi.

private void btnUp_Click(object sender, RoutedEventArgs e)
{
cam.ElevationAngle += 5;
}
private void btnDown_Click(object sender, RoutedEventArgs e)
{
cam.ElevationAngle += -5;
}
IV. Bước 4: Nhận dạng cử động của con người

Để người chơi có thể chơi game dựa vào động tác thì chương trình game phải bắt được từng cử động của cơ thể. Và để làm điều này thì rất… dễ khi có sự hỗ trợ của thư viện Kinect .NET SDK. Thư viện hỗ trợ chúng ta lấy được 20 điểm khớp chuyển động trên cơ thể mình mà Microsoft gọi đó là các điểm Joint.

HipCenter, Spine, ShoulderCenter, Head, ShoulderLeft, ElbowLeft, WristLeft, HandLeft, ShoulderRight, ElbowRight, WristRight, HandRight, HipLeft, KneeLeft, AnkleLeft, FootLeft, HipRight, KneeRight, AnkleRight,FootRight, Count
Khung xương
Hiện tại, SDK chỉ mới hỗ trợ cho chúng ta nhận dạng được cử động của 2 người chơi. Hi vọng phiên bản chính thức sẽ hỗ trợ nhiều hơn.

Để làm được điều này, đơn giản bạn chỉ cần làm theo các bước sau:

Khai báo hàm sự kiện phát sinh khi SDK phân tích được chuyển động của người chơi, hàm này thường được khai báo cùng việc InitKinectDevice
kinect.SkeletonFrameReady += new
EventHandler<SkeletonFrameReadyEventArgs>(SkeletonFrameReady);

Lấy danh sách tọa độ các khớp chuyển động của người chơi thông qua thuộc tính SkeletonFrameReadyEventArgs.SkeletonFrame.Skeletons. Như đã nói ở trên các điểm này được trả ra dưới dạng các Joint, do đó để lấy được tọa độ x, y trên màn hình ta cần phải có hàm chuyển đối.

private Point getDisplayPosition(Joint joint)
{
float depthX, depthY;
nui.SkeletonEngine.SkeletonToDep thImage(joint.Position, out depthX, out depthY);
//đổi sang hệ quy chiếu tọa độ 320x240
depthX = Math.Max(0, Math.Min( depthX * 320, 320));
depthY = Math.Max(0, Math.Min(
depthY * 240, 240));
int colorX, colorY;
ImageViewArea iv = new ImageVie wArea();

nui.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480, iv, (int)depthX, (int)depthY, (short)0, out colorX, out colorY);

return new Point((int)(skeleton.Width * colorX / 640.0), (int)(skeleton.Height * colorY / 480));
}
void kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
SkeletonFrame skeletonFrame = e.SkeletonFrame;
skeleton.Children.Clear();
foreach (SkeletonData data in skeletonFrame.Skeletons)
{
if (SkeletonTrackingState.Tracked == data.Tracking
State)
{
// Draw bones
Brush brush = new SolidColorBrush(Color.FromRgb(255, 0, 0));
skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.Spine, JointID.ShoulderCenter, JointID.Head));
skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.ShoulderCenter, JointID.ShoulderLeft, JointID.ElbowLeft, JointID.WristLeft, JointID.HandLeft));
 
skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.ShoulderCenter, JointID.ShoulderRight, JointID.ElbowRight, JointID.WristRight, JointID.HandRight));

skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.HipLeft, JointID.KneeLeft, JointID.AnkleLeft, JointID.FootLeft));
skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.HipRight, JointID.KneeRight, JointID.AnkleRight, JointID.FootRight));
//Draw joints
foreach (Joint joint in data.Joints)
{
Point jointPos = getDisplayPosition(joint);
Line jointLine = new Line();
jointLine.X1 = jointPos.X - 3;
jointLine.X2 = jointLine.X1 + 6;
jointLine.Y1 = jointLine.Y2 = jointPos.Y;
jointLine.Stroke = new SolidColorBrush(Color.FromRgb (0, 255, 0));
jointLine.StrokeThickness = 6;
skeleton.Children.Add(jointLine);
}
}
}
}
V. Kết luận
Như vậy qua bài này chúng ta đã biết cách lấy hình ảnh ghi nhận được từ camera, điều khiển góc quay, và quan trọng nhất là lấy được tọa độ các khớp chuyển động của nhân vật. Ví dụ như lấy được tọa độ của bàn tay, của đầu, chân, hông. Từ đây chúng ta có thể tạo ra những game dựa vào cử chỉ của người chơi và để điều khiển nhân vật trong game.

Hãy tưởng tưởng một ngày nào đó chúng ta chơi trò Audition thay vì gõ bàn phím, chúng ta sẽ nhảy theo điệu nhạc của nhân vật trên game. Hoặc chúng ta có thể điều khiển máy tính từ xa, giả lập màn hình cảm ứng với Kinect… Vô số ứng dụng dạng tương tác (Interactive Application) cho chúng ta khai thác với thiết bị Kinect.

Email: nguyentankhtn@gmail.com

Chủ Nhật

================================================
Trích bài của thành viên SuperAdmin trong diễn đànhttp://www.it-huflit.info
================================================

Kế thừa:
Một lớp A1 kế thừa lớp A nghĩa là lớp A1 chứa tất cả thuộc tính, phương thức là protected hoặc public của A.
Vd:

public class A {
    public int a;
    protected int b;
    private int c;
    /**
     *  constructor – Hàm khởi tạo - của lớp A.
     */
    public A() {
        a = 0;
        b = 1;
        c = 2;
    }
    public int getNextA() {
        return a+1;
    }
    public int getNextB() {
        return b+1;
    }
    public int getNextC() {
        return c+1;
    }
}
public class A1: A {
    public void setA (int a) {
        this.a = a;
    }
    public void setB (int b) {
        this.b = b;
    }
    /**
     * Hàm setC này sai (bị báo lỗi) vì không thể truy cập tới biến c.
     */
    public void setC (int c) {
        this.c = c;
    }
    /**
     * hàm main sau sẽ in ra kết quả: a = 1; b = 2; c = 3;
     */
    static void main() {
        A1 a1 = new A1();
        Console.WriteLine("a = " + a1.getNextA()
                           + "; b = " + a1.getNextB()
                           + "; c = " + a1.getNextC() + ";");
   }
}
Từ khóa abstract:
Nếu lớp A là abstract thì lớp A không thể tự khởi tạo mà nó chỉ được dùng cho việc kế thừa.
vd:

public abstract class A {
    private int a;
    public A() {
        a = 100;
    }
    public int getA() {
        return a;
    }
}
public class A1: A {
    public A1() {
    }
    static void Main() {
        A a = new A(); //--> sai
        A1 a1 = new A1();
        Console.WriteLine("a = " + a1.getA());//kết quả là: a = 100
    }
}


Nếu phương setA của lớp A là abstract thì nó không được có phần thân hàm mà phần thân hàm sẽ được viết trong lớp kế thừa A. Lớp A cũng phải là abstract.

public abstract class A {
    protected int a;
    public A() {
        a = 100;
    }
    public int getA() {
        return a;
    }
    /**
     * Nếu phương thức setA có phần thân hàm sẽ bị báo lỗi.
     */
    abstract void setA(int a);
}
public class A1: A {
    /**
     * Nếu lớp A1 không có hàm setA thì sẽ bị báo lỗi.
     */
    public void setA(int a) {
        this.a = a;
    }
    static void Main() {
        A1 a1 = new A1();
        a1.setA(1);
        Console.WriteLine("a = " + a1.getA());//kết quả là: a = 1
    }
}

Từ khóa interface:
Một lớp A là interface nghĩa là mọi phương thức, hàm đều không có phần thân hàm mà chỉ là liệt kê những phương thức, hàm cho các lớp khác implements. Nếu lớp A1 implements interface A và không có đủ các phương thức, hàm được liệt kê trong A thì sẽ bị báo lỗi.
Vd :
public interface A {
    /**
     * Trong C#, không được đưa các từ khóa public, private, protected
     * vào trước hàm.
     */
    int getNextA();
    void setA(int a);
}
public class A1 {
    private int a = 0;
    static void Main() {
        A a = new A1();
        a.setA(10);
        Console.WriteLine("a = " + a.getNextA());//kết quả là: a = 11
    }
    /**
     *  Nếu thiếu một trong 2 phương thức sau sẽ báo lỗi.
     */
    public int getNextA() {
        return a+1;
    }
    public void setA(int a) {
        this.a = a;
    }
}
Từ khóa virtual và override:
Hàm getA là virtual nghĩa là nó cho phép các lớp con của nó override lên.
Vd:
public class A {
    private int a = 0;
    public virtual int getA() {
        return a;
    }
}
public class A1 {
    private int b = 10;
    public override int getA() {
         return b;
    }
}

Hãy xem thêm ví dụ sau để hiểu rõ hơn về virtual và override:
public class A {
    public virtual string getName() {
        return "A";
    }
}
public class B: A {
    public virtual string getName() {
        return "B";
    }
}
public class C: B {
    public override string getName() {
        return "C";
    }
}
public class D: C {
    /**
     * Nếu hàm sau là override thì sẽ bị báo lỗi.
     */
    public virtual string getName() {
        return "D";
    }
    static void Main() {
        D d = new D();
        C c = d;
        B b = c;
        A a = b;
        Console.WriteLine(a.getName() + "; "
                          + b.getName() + "; "
                          + c.getName() + "; "
                          + d.getName() + "; ");
        //kết quả là: A; C; C; D;
    }
}
 
Từ khóa sealed:
Nếu lớp A là sealed thì lớp A sẽ không được kế thừa bởi bất kỳ lớp nào.
public sealed class A {
    public a;
    public A() {
    }
    //và các phương thức khác...
}
/**
 * lớp A1 sau sẽ bị báo lỗi.
 */
public class A1: A {
}

Nếu phương thức getA là sealed thì phương thức này sẽ không thể bị override bởi các lớp con.
vd:
public class A {
    public int a;
    public A() {
        a = 100;
    }
    public virtual int getA() {
        return a;
    }
}
public class A1: A {
    public sealed override int getA() {
        return a-1;
    }
}
public class A2: A {
    /**
     * vì không thể override hàm getA --> bị báo lỗi.
     */
    public override int getA() {
        return a-1;
    }
}
Từ khóa static:
Dùng để định nghĩa một biến của một lớp. Đây có thể coi như là một biến toàn cục, vì dù lớp chứa biến static được khởi tao bao nhiêu lần thì nó chỉ có duy nhất một biến static (nằm ở cùng một vùng nhớ). Biến static cũng được dùng để khai báo một phương thức.
Một phương thức static chỉ có thể truy cập tới biến static của class mà thôi.
Điều khác biệt của biến static trong C# và JAVA là C# không cho truy cập vào biến static ở một instance.
Vd :
public class Math {
    public static double s_pi = 3.14;
    public double m_pi = 3.14;
    public static double calculateCircle(double r) {
        return r*r*s_pi; //nếu dùng m_pi thì sẽ sai.
    }
    public double calculateNonStaticCircle(double r) {
        return r*r*s_pi;
    }
}
public class MathExecute {
    static void Main() {
        Console.WriteLine("pi = " + Math.s_pi); 
        Math.s_pi = 3.1412;
        Math m = new Math();
        Console.WriteLine("pi = " + m.calculateNonStaticCircle(10));
        m.s_pi = 3.141; //sai
        Console.WriteLine("pi = " + m.calculateCircle(10));//sai
    }
}
Từ khóa const và readonly:
Cả 2 từ khóa đều được dùng để chỉ định một giá trị là hằng số. readonly chỉ được phép dùng cho giá trị của lớp, const có thể dùng cho cả giá trị của lớp và giá trị của hàm. Bất cứ giá trị nào là const phải được khởi tạo lúc khai báo. Giá trị readonly là toàn cục cũng phải được khởi tạo lúc khai báo. Giá trị readonly không toàn cục có thể được khởi tạo trong constructor của lớp.
public class Math {
    public static const double S_PI = 3.141;//nếu không gán giá trị sẽ bị báo lỗi
    public readonly double M_PI;
    public static readonly double S_MATH_PI = 3.14;//nếu không gán giá trị sẽ sai
    /**
     * Constructor.
     */
    public Math() {
        M_PI = 3.14;
        S_PI = 3.14128;//sai
        int const r = 10;//nếu không gán giá trị sẽ sai
        int readonly r = 20;//sai
    }
}
Từ khóa const và readonly:
Cả 2 từ khóa đều được dùng để chỉ định một giá trị là hằng số. readonly chỉ được phép dùng cho giá trị của lớp, const có thể dùng cho cả giá trị của lớp và giá trị của hàm. Bất cứ giá trị nào là const phải được khởi tạo lúc khai báo. Giá trị readonly là toàn cục cũng phải được khởi tạo lúc khai báo. Giá trị readonly không toàn cục có thể được khởi tạo trong constructor của lớp.

Xin bổ sung thêm như sau:
- const và readonly chỉ có thể sử dụng cho các kiểu dữ liệu giá trị (int, string, double...).
- readonly không cần phải khởi tạo giá trị ngay khi khai báo mà có thể khởi tạo giá trị trong hàm constructor (hàm khởi tạo) của lớp.
using System;
public class ReadOnlyTest {
   class MyClass 
   {
      public int x;
      public readonly int y = 25; // Initialize a readonly field
      public readonly int z;

      public MyClass() 
      {
         z = 24;   // Initialize a readonly instance field
      }

      public MyClass(int p1, int p2, int p3) 
      {
         x = p1; 
         y = p2; 
         z = p3;
      }
   }

   public static void Main() 
   {
      MyClass p1= new MyClass(11, 21, 32);   // OK
      Console.WriteLine("p1: x={0}, y={1}, z={2}" , p1.x, p1.y, p1.z);   
      MyClass p2 = new MyClass();
      p2.x = 55;   // OK
      Console.WriteLine("p2: x={0}, y={1}, z={2}" , p2.x, p2.y, p2.z);
   }
}


 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Blogger Templates