• CentOS安装git

    CentOS默认源是没有git的,所以希望通过yum install来安装的童鞋们,不要在错误的道路上越走越远……

    所以,要从源安装,对应的命令:

    yum -y install zlib-devel openssl-devel perl cpio expat-devel gettext-devel

    wget http://git-core.googlecode.com/files/git-1.7.7.5.tar.gz

    ./configure –prefix=/usr/local/git

    make

    make install

    然后对执行文件进行链接,当然,如果安装在/usr/local目录的话,就不用这步了。

    ln -s /usr/local/git/bin/git /usr/local/bin/git

    ln -s /usr/local/git/bin/gitk /usr/local/bin/gitk

    ln -s /usr/local/git/bin/git-shell /usr/local/bin/git-shell

    ln -s /usr/local/git/bin/git-upload-pack /usr/local/bin/git-upload-pack

    ln -s /usr/local/git/bin/git-cvsserver /usr/local/bin/git-cvsserver

    ln -s /usr/local/git/bin/git-receive-pack /usr/local/bin/git-receive-pack

    ln -s /usr/local/git/bin/git-upload-archive /usr/local/bin/git-upload-archive

    常见问题

    1、如果出现“git: error while loading shared libraries: libiconv.so.2: cannot open shared object file: No such file or directory”

    在确认已经安装iconv库的情况下,执行以下命令:

    echo "/usr/local/lib" > /etc/ld.so.conf.d/git.conf

    /sbin/ldconfig

    2、如果在“git clone https://***”时出现“error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed while accessing”

    在使用git前加上“GIT_SSL_NO_VERIFY=true”的环境变量,即:

    env GIT_SSL_NO_VERIFY=true git clone https://***

    2012.01.09 / 1 条评论 / 分类: 所谓技术

  • firefox蛋疼升级到9.0.1后提示“附加组件不兼容”解决办法

    firefox蛋疼升级到9.0.1后提示“附加组件不兼容”解决办法

    访问about:config->我保证我会小心->右击“新建”->“布尔值”->名称中填写“extensions.checkCompatibility.9.0”->值的内容为“false”。重启firefox即可屏蔽兼容性检查,直接启用插件。 将来升级到10的话,将上面的“extensions.checkCompatibility.9.0”改为“extensions.checkCompatibility.10.0”即可。

    参考: http://kb.mozillazine.org/Extensions.checkCompatibility

    需要注意的是,在“附加组件管理器”中搜索某个插件,可能会提示“找不到任何匹配的附加组件”,这是因为,即使设置了安装插件是不检查兼容性,但搜索时自动过滤了Firefox认为不兼容的插件。这时,需要移步去https://addons.mozilla.org/搜索对应插件。

    在about:config怎么删除一个首选项名称:如果是自己添加的,在键上点右键→ 重置,重新启动firefox之后就没有了。

    2011.12.24 / 2 条评论 / 分类: 所谓技术

  • PHP的curl/libcurl连接https/SSL网站

    在PHP中使用libcurl连接https/SSL网站,可以用以下代码测试是否连接畅通:

    <?
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://passport.baidu.com/?login');
    curl_setopt($curl, CURLOPT_POST, false);
    $curl_res = curl_exec($curl);
    echo $curl_res;
    if (curl_errno($curl)) {
    	echo 'Error: ' . curl_error($curl);
    }
    curl_close($curl);
    ?>

    如果显示正常,则证明连接畅通。

    如果显示“Error: couldn’t connect to host”,则证明出错啦!

    首先,进入phpinfo页面查看libcurl,看看curl字段是否有OpenSSL支持。如果没有,那就自己折腾PHP的安装吧!

    然后,将libeay32.dll和ssleay32.dll拷贝到windows目录、system32目录;或者如果是命令行运行PHP,则拷贝到当前运行目录;或运行Apache运行PHP,则拷贝到当前运行Apache的目录。

    重启Apache或者PHP,再次执行代码查看是否正常。

    2011.12.09 / 暂无评论 / 分类: 所谓技术

  • Windows下编译安装Privoxy

    首先,要安装MingW。在此不赘述。当然,用cygwin也可以,但是这样编译出来的Privoxy没有GUI图形界面。PS:Privoxy在jcc.c那里已经自动识别了MingW,并且嵌入了Windows GUI的API,因此编译出来就可以自己采用图形界面了。

    然后,在这里下载Privoxy的源代码,本文以privoxy-3.0.18-stable为例。

    进入privoxy-3.0.18-stable源码目录,执行以下命令,进入bash:

    bash

    执行以下命令,进行配置:

    autoheader
    autoconf
    ./configure –prefix=/r/privoxy –enable-mingw32 –disable-pthread

    稍微解释以下configure的各个参数。–prefix参数指明privoxy的安装目录,/r/privoxy是mingw表示windows文件系统的方式,即R盘的privoxy文件夹。–enable-mingw32指明了为mingw环境配置,其实不指明配置程序也会自动检测得到。–disable-pthread是禁用POSIX的pthread,privoxy会自动使用windows API的thread来实现线程,具体参照errlog.c的部分代码:

    /*********************************************************************
     *
     * Function    :  get_thread_id
     *
     * Description :  Returns a number that is different for each thread.
     *
     *                XXX: Should be moved elsewhere (miscutil.c?)
     *
     * Parameters  :  None
     *
     * Returns     :  thread_id
     *
     *********************************************************************/
    static long get_thread_id(void)
    {
       long this_thread = 1;  /* was: pthread_t this_thread;*/
    
    #ifdef __OS2__
       PTIB     ptib;
       APIRET   ulrc; /* XXX: I have no clue what this does */
    #endif /* __OS2__ */
    
       /* FIXME get current thread id */
    #ifdef FEATURE_PTHREAD
       this_thread = (long)pthread_self();
    #ifdef __MACH__
       /*
        * Mac OSX (and perhaps other Mach instances) doesn't have a debuggable
        * value at the first 4 bytes of pthread_self()'s return value, a pthread_t.
        * pthread_t is supposed to be opaque... but it's fairly random, though, so
        * we make it mostly presentable.
        */
       this_thread = abs(this_thread % 1000);
    #endif /* def __MACH__ */
    #elif defined(_WIN32)
       this_thread = GetCurrentThreadId();
    #elif defined(__OS2__)
       ulrc = DosGetInfoBlocks(&ptib, NULL);
       if (ulrc == 0)
         this_thread = ptib -> tib_ptib2 -> tib2_ultid;
    #endif /* def FEATURE_PTHREAD */
    
       return this_thread;
    }

    配置成功后,编译安装:

    make

    make install

    其实,make后已经有privoxy.exe,可以下载官方的Win32版本的ZIP包,替换里面的privoxy.exe即可使用,无需再make install。

     

    一些注意的问题:

    1、编译时提示未定义“NI_MAXSERV”

    编译时出现如下错误:

    jbsockets.c: In function ‘get_host_information’:

    jbsockets.c:979:22: error: ‘NI_MAXSERV’ undeclared (first use in this function)

    jbsockets.c:979:22: note: each undeclared identifier is reported only once for e

    ach function it appears in

    make: *** [jbsockets.o] Error 1

    修改jbsockets.c,在

    const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION;

    前面加上:

    /* MOD BY Creke START */

    #ifndef NI_MAXSERV

    #define NI_MAXSERV    32

    #endif

    /* MOD BY Creke END */

    说实话,这是configure没有识别getnameinfo和getaddrinfo,因此config.h中没有定义HAVE_RFC2553所致。这些依赖于官方修复,再次仅拷贝ws2tcpip.h中的相关值进来,作临时修补。

    2、MingW默认安装的话,需要额外的库吗?

    需要zlib库,在mingw中又称为libz,可以在这里下载。当然,如果需要将privoxy拷贝到其它电脑运行,需要将libz-1.dll拷贝到privoxy程序目录中。

    3、额外的DLL?

    libgcc_s_dw2-1.dll

    4、编译好如何发行和安装?

    推荐下载官方发行的win32的zip包,将编译好的privoxy.exe覆盖。同时别忘记拷贝依赖的dll。

    2011.12.07 / 8 条评论 / 分类: 所谓技术

  • 使用plowshare命令行下自动下载国内外网盘资源

    下载plowshare:

    git clone https://code.google.com/p/plowshare/

    安装:

    PREFIX=/usr/local/plowshare make install

    执行链接:

    ln -s /usr/local/plowshare/bin/plowdown /usr/bin/plowdown
    ln -s /usr/local/plowshare/bin/plowup /usr/bin/plowup
    ln -s /usr/local/plowshare/bin/plowlist /usr/bin/plowlist
    ln -s /usr/local/plowshare/bin/plowdel /usr/bin/plowdel

    下载国内外网盘的东西,如网盘下载链接为“http://xxx/xxx”只需执行一条命令

    plowdown http://xxx/xxx

    如果plowshare出现“Can’t locate HTML/Entities.pm in @INC”,执行以下命令:

    cpan

    (如果是第一次使用cpan,根据自己情况配置好)

    当看到:

    cpan>

    输入:

    install HTML::Entities

    这样就会修复上述问题啦!

    2011.11.29 / 暂无评论 / 分类: 所谓技术

  • CentOS5安装后常见问题解决方案

    教育网用户怎么设置更新源?

    用163的更新源吧:

    mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.old
    wget http://mirrors.163.com/.help/CentOS5-Base-163.repo
    mv CentOS5-Base-163.repo /etc/yum.repos.d/CentOS5-Base-163.repo

    用了163更新源,yum install时出现“GPG key retrieval failed: [Errno 4] IOError: <urlopen error (111, ‘Connection refused’)>”咋办?

    那是你的教育网不能连国外网站,获取不了官网镜像上的GPG公钥。解决方法:

    编辑/etc/yum.repos.d/CentOS5-Base-163.repo

    把其中的“gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-5”

    改为“gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-5”

    SSH在机子里可以连接,在机子外不能?

    确认是不是防火墙iptables挡住了吧!解决办法:

    iptables -I INPUT 1 -p tcp –dport 22 -j ACCEPT
    iptables-save
    service iptables save

    好啦,差不多就这么多,最后附一个dev环境的给力批量安装脚本。保存为xxoo.sh使用。

    #! /bin/bash
    for packages in patch make gcc gcc-c++ gcc-g77 flex bison file libtool libtool-libs autoconf kernel-devel libjpeg libjpeg-devel libpng libpng-devel libpng10 libpng10-devel gd gd-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glib2 glib2-devel bzip2 bzip2-devel libevent libevent-devel ncurses ncurses-devel curl curl-devel e2fsprogs e2fsprogs-devel krb5 krb5-devel libidn libidn-devel openssl openssl-devel fonts-chinese gettext gettext-devel ncurses-devel gmp-devel pspell-devel unzip screen;
    do yum -y install $packages; done

    2011.11.20 / 2 条评论 / 分类: 所谓技术

  • Eclipse支持PHP使用Zend Debugger调试

    一、安装Eclipse

    下载地址为http://www.eclipse.org/downloads/

    上面有好多个版本,通过Compare Packages科研看到其中的区别。开发PHP的话,Eclipse IDE for JavaScript Web Developers就行。但我考虑到以后可能要用它来折腾JAVA EE,所以下了Eclipse IDE for Java EE Developers。

    下载下来的是ZIP包,解压即可用。

    二、安装Eclipse for PHP集成开发环境

    参考http://wiki.eclipse.org/PDT/Installation#Installation_Flow

    选择“Help”——“Install New Software…”——Work With下拉菜单处选择对应版本的release,如我的3.7选“Indigo – http://download.eclipse.org/releases/indigo”——展开“Programming Languages”——选择“PHP Development Tools”。

    然后确认条款,等待安装,重启Eclipse即可支持PHP开发。

    三、PHP配置Zend Debugger

    这里,点击“Studio Web Debugger”那个链接,下载ZendDebugger,解压缩,如我解压缩到W:\ZendDebugger文件夹中。

    我是使用PHP5.2,则讲ZendDebugger文件夹中的5.2.x_comp改名为php-5.2.x。

    然后在php.ini的[Zend]段末加上以下内容:

    ; zend_debugger
    zend_extension_manager.debug_server_ts="W:\ZendDebugger"
    ;zend_debugger.allow_hosts=127.0.0.1,192.168.0.0/24
    zend_debugger.expose_remotely=always

    注意,如果是有安装Zend Optimizer的话,要在Zend Optimizer的配置后加入Zend Debugger的配置语句。

    四、联合Eclipse和Zend Debugger

    首先,开启WWW服务器,确认Zend Debugger成功配置。

    然后,开启Eclipse。

    在Eclipse中,选择“Window”——“Preferences”——“PHP”——“PHP executables”。在里面设置php的各项参数。我把SAPI设为“CGI”。

    在Eclipse中,选择“Window”——“Preferences”——“PHP”——“Debug”——“Installed Debuggers”。在里面设置php的各项参数。把Zend Debugger的“Debug Port”设为“10137”,默认的端口为10137,需要与php.ini设置的一致。

    将W:\ZendDebugger中的dummy.php拷贝至Web服务器的根目录中。

    在Eclipse中,选择“Run”——“Debug Configuration…”。在PHP Web Page中右击,选“new”,新建一个server配置,并设置好其中各项内容。点击“Test Debugger”,如果成功了就恭喜恭喜。如果没通过,则按照出错提示修正配置错误。

     

    OK,大功告成。

    2011.11.18 / 3 条评论 / 分类: 所谓技术

  • 让SSH/SOCKS成为全局代理的软件们(Windows+Linux)

    Windows下的有:

    1. Proxifier 下载地址
    2. ProxyCap 下载地址

    前者比较好用,可控规则较多。我正在使用。

    Linux下的有:

    1. proxychains 下载地址
    2. redsocks 下载地址
    3. tsocks 下载地址

    proxychain功能较多,支持多个代理轮询等;redsocks据说支持android;tsocks配置简单。

     

    proxychains教程

    假设代理为127.0.0.1,端口为7070。我在Ubuntu下安装。

    安装很简单:

    sudo apt-get install proxychains

    配置:

    sudo vi /etc/proxychains.conf

    把最后的“[ProxyList]”部分配置为自己的代理即可:

    socks4  127.0.0.1 7070

    使用方法:

    proxychains <程序名>

    即可让程序使用代理。

     

    redsocks教程

    严格意义上来说,proxychains不算自动的全局代理,有没有像Proxifier这样,开了之后自动让所有启动的程序都走系统代理呢?答案就是redsocks。

    首先安装Ubuntu编译环境和必要的库:

    sudo apt-get install autoconf automake libtool libevent-dev g++

    下载源代码,然后编译安装:

    ./mkauto.sh

    cp redsocks /usr/local/bin/

    配置文件为:

    base {
    // debug: connection progress & client list on SIGUSR1
    log_debug = off;

    // info: start and end of client session
    log_info = off;

    /* possible `log’ values are:
    * stderr
    * file:/path/to/file
    * syslog:FACILITY facility is any of "daemon", "local0"…"local7"
    */
    log = "file:/dev/null";
    // log = stderr;
    // log = "file:/path/to/file";
    // log = "syslog:local7";

    // detach from console
    daemon = on;

    /* Change uid, gid and root directory, these options require root
    * privilegies on startup.
    * Note, your chroot may requre /etc/localtime if you write log to syslog.
    * Log is opened before chroot & uid changing.
    */
    // user = nobody;
    // group = nobody;
    // chroot = "/var/chroot";

    /* possible `redirector’ values are:
    * iptables – for Linux
    * ipf – for FreeBSD
    * pf – for OpenBSD
    * generic – some generic redirector that MAY work
    */
    redirector = iptables;
    }

    redsocks {
    /* `local_ip’ defaults to 127.0.0.1 for security reasons,
    * use 0.0.0.0 if you want to listen on every interface.
    * `local_*’ are used as port to redirect to.
    */
    local_ip = 127.0.0.1;
    local_port = 12345;

    // `ip’ and `port’ are IP and tcp-port of proxy-server
    ip = 127.0.0.1;
    port = 7070;

    // known types: socks4, socks5, http-connect, http-relay
    type = socks5;

    // login = "foobar";
    // password = "baz";
    }

    redudp {
    // `local_ip’ should not be 0.0.0.0 as it’s also used for outgoing
    // packets that are sent as replies – and it should be fixed
    // if we want NAT to work properly.
    local_ip = 127.0.0.1;
    local_port = 10053;

    // `ip’ and `port’ of socks5 proxy server.
    ip = 10.0.0.1;
    port = 1080;
    login = username;
    password = pazzw0rd;

    // kernel does not give us this information, so we have to duplicate it
    // in both iptables rules and configuration file. By the way, you can
    // set `local_ip’ to 127.45.67.89 if you need more than 65535 ports to
    // forward ;-)
    // This limitation may be relaxed in future versions using contrack-tools.
    dest_ip = 8.8.8.8;
    dest_port = 53;

    udp_timeout = 30;
    udp_timeout_stream = 180;
    }

    dnstc {
    // fake and really dumb DNS server that returns "truncated answer" to
    // every query via UDP, RFC-compliant resolver should repeat same query
    // via TCP in this case.
    local_ip = 127.0.0.1;
    local_port = 5300;
    }

    // you can add more `redsocks’ and `redudp’ sections if you need.

    这里的配置没有配置udp的代理部分,只是配置了tcp即redsocks部分。监听端口是12345。日志关闭了,因为好像我下载的当前版本无论怎么样都产生一堆调试日志,不知道以后会不会修复这点。

    启动关闭脚本redsocks.sh为(via):

    #! /bin/bash

    SSHHOST=creke
    SSHPORT=22
    SSHUSR=creke
    SSHPWD=creke

    SSHDAEMON=/usr/local/bin/plink
    SSHPIDFILE=/var/run/sshtunnel.pid

    start_ssh()
    {
        echo "Start SSH Tunnel Daemon: "
        start-stop-daemon -b -q -m -p $SSHPIDFILE –exec $SSHDAEMON -S \
        — -N -D 127.0.0.1:7070 -P $SSHPORT -pw $SSHPWD $SSHUSR@$SSHHOST
        echo "SSH Tunnel Daemon Started."
    }

    stop_ssh()
    {
        #ps aux|grep "ssh -NfD 1234"|awk ‘{print $2}’|xargs kill
        if [ -f $SSHPIDFILE ]; then
        PID=$(cat $SSHPIDFILE)
        kill $PID
        while [ -d /proc/$PID ];
        do
        sleep 1
        done
        fi
        rm -rf $SSHPIDFILE
        echo "SSH Tunnel Daemon Stoped."
    }

    case "$1" in
      start)
        start_ssh
        cd /usr/local/redsocks
        if [ -e redsocks.log ] ; then
          rm redsocks.log
        fi
        ./redsocks -p /usr/local/redsocks/redsocks.pid #set daemon = on in config file
        # start redirection
        # iptables -t nat -A OUTPUT -p tcp –dport 80 -j REDIRECT –to 12345
        # iptables -t nat -A OUTPUT -p tcp –dport 443 -j REDIRECT –to 12345
        # Create new chain
        iptables -t nat -N REDSOCKS

        # Ignore LANs and some other reserved addresses.
        iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN
        iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN
        iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN
        iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN
        iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN
        iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN
        iptables -t nat -A REDSOCKS -d 224.0.0.0/4 -j RETURN
        iptables -t nat -A REDSOCKS -d 240.0.0.0/4 -j RETURN

        # Anything else should be redirected to port 12345
        iptables -t nat -A REDSOCKS -p tcp -j REDIRECT –to-ports 12345
        # Any tcp connection should be redirected.
        iptables -t nat -A OUTPUT -p tcp -j REDSOCKS
        ;;

      stop)
        stop_ssh
        cd /usr/local/redsocks
        if [ -e redsocks.pid ]; then
          kill `cat redsocks.pid`
          rm redsocks.pid
        else
          echo already killed, anyway, I will try killall
          killall -9 redsocks
        fi
        # stop redirection
        iptables -t nat -F OUTPUT
        iptables -t nat -F REDSOCKS
        iptables -t nat -X REDSOCKS
        ;;

      start_ssh)
        start_ssh
        ;;

      stop_ssh)
        stop_ssh
        ;;

      clean_dns)
        # iptables -A INPUT -p udp –sport 53 -m state –state ESTABLISHED -m you-know-who -j DROP -m comment –comment "drop you-know-who dns hijacks"
        echo this function not finished
        ;;

      *)
        echo "Usage: redsocks start|stop|start_ssh|stop_ssh|clean_dns" >&2
        exit 3
        ;;
    esac

    iptables的规则是让所有的TCP包都发送到redsocks监听的端口12345。本脚本还整合了ssh的daemon启动,使用start-stop-daemon来实现。

    启动和关闭:

    将启动关闭脚本中的开头的几个变量配置好

    启动命令:sudo ./redsocks.sh start

    关闭命令:sudo ./redsocks.sh stop

    2011.11.09 / 17 条评论 / 分类: 所谓技术

  • Kinect驱动Microsoft官方与OpenNI对比

    RGB:

    RGB对比就不用了,因为在普通应用中,没有什么好对比的。

    结论:都能捕捉RBG彩色图像

    骨骼捕捉:

    微软:当人完整出现在Kinect感应范围内时,不需要做特定动作,即可以准确地捕捉全身骨骼。但不能捕捉半身骨骼。

    OpenNI:当深度图像中识别到用户时,需要手臂和肘呈90度,作投降姿势,才能识别骨骼。可以捕捉半身骨骼。

    深度数据:

    微软:默认情况下会出现两个重影(因为有两个红外摄像头的缘故)。而且深度信息精确度较低。

    MS Depth.jpg

    OpenNI:深度信息处理较好,精确度较高。 这些对自己实现的手指捕捉,手势识别等很重要。

    OpenNI Depth.jpg

    2011.11.02 / 2 条评论 / 分类: 所谓技术

  • Kinect SDK中SkeletalViewer sample的学习笔记

    运行时初始化

    VideoStream必须在Runtime初始化时指定UseColor;分辨率有两种格式:Resolution1280×1024和Resolution640×480;图像类型有三种:Color,ColorYUV和ColorYUVRaw。

    DepthStream必须在Runtime初始化时指定UseDepthAndPlayerIndex;的分辨率有两种格式:Resolution320×240和Resolution80×60;图像类型只有一种:DepthAndPlayerIndex。

    运行的时候

    当一个视频帧准备好时,runtime发出VideoFrameReady信号,并调用nui_ColorFrameReady。其余的DepthFrameReady和SkeletonFrameReady,与其相关的EventHandler函数类似。

    nui_ColorFrameReady函数的内容是,更新对应控件(即video)的图像内容。

    处理Depth数据

            void nui_DepthFrameReady(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);
    
                ++totalFrames;
    
                DateTime cur = DateTime.Now;
                if (cur.Subtract(lastTime) > TimeSpan.FromSeconds(1))
                {
                    int frameDiff = totalFrames - lastFrames;
                    lastFrames = totalFrames;
                    lastTime = cur;
                    frameRate.Text = frameDiff.ToString() + " fps";
                }
            }

    函数大概意思:首先,从事件中获得图片信息Image;然后,将原始的16位景深图转为32位图像;接着,设置depth控件的图像为convertedDepthFrame,即转换后的32位景深图;最后,将总帧数加1,并判断,如果统计事件超过一秒,则刷新fps的记录。

    原始的16位景深图描述如下(这里第8个问题为更详细的解释):

    • 最低3位为skeleton ID
    • 剩下的13位中的低12位是深度值,单位为毫米

    如果景深数据以raw或gray-scale显示,用户很难分清场景中的人像。所以,程序将不同的用户渲染一种不同的颜色。先看代码:

            // Converts a 16-bit grayscale depth frame which includes player indexes into a 32-bit frame
            // that displays different players in different colors
            byte[] convertDepthFrame(byte[] depthFrame16)
            {
                for (int i16 = 0, i32 = 0; i16 < depthFrame16.Length && i32 < depthFrame32.Length; i16 += 2, i32 += 4)
                {
                    int player = depthFrame16[i16] & 0x07;
                    int realDepth = (depthFrame16[i16+1] << 5) | (depthFrame16[i16] >> 3);
                    // transform 13-bit depth information into an 8-bit intensity appropriate
                    // for display (we disregard information in most significant bit)
                    byte intensity = (byte)(255 - (255 * realDepth / 0x0fff));
    
                    depthFrame32[i32 + RED_IDX] = 0;
                    depthFrame32[i32 + GREEN_IDX] = 0;
                    depthFrame32[i32 + BLUE_IDX] = 0;
    
                    // choose different display colors based on player
                    switch (player)
                    {
                        case 0:
                            depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2);
                            depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2);
                            depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 2);
                            break;
                        case 1:
                            depthFrame32[i32 + RED_IDX] = intensity;
                            break;
                        case 2:
                            depthFrame32[i32 + GREEN_IDX] = intensity;
                            break;
                        case 3:
                            depthFrame32[i32 + RED_IDX] = (byte)(intensity / 4);
                            depthFrame32[i32 + GREEN_IDX] = (byte)(intensity);
                            depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
                            break;
                        case 4:
                            depthFrame32[i32 + RED_IDX] = (byte)(intensity);
                            depthFrame32[i32 + GREEN_IDX] = (byte)(intensity);
                            depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 4);
                            break;
                        case 5:
                            depthFrame32[i32 + RED_IDX] = (byte)(intensity);
                            depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 4);
                            depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
                            break;
                        case 6:
                            depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2);
                            depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2);
                            depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
                            break;
                        case 7:
                            depthFrame32[i32 + RED_IDX] = (byte)(255 - intensity);
                            depthFrame32[i32 + GREEN_IDX] = (byte)(255 - intensity);
                            depthFrame32[i32 + BLUE_IDX] = (byte)(255 - intensity);
                            break;
                    }
                }
                return depthFrame32;
            }

    解释一下具体步骤:

    1. “player = depthFrame16[i16] & 0×07”说的是,将用户信息从最低3位中提取。值从0到6(Kinect理论上最多支持7个用户,但官方文档称SDK最多只支持6个),如果值为0,则表示无用户。
    2. “realDepth = (depthFrame16[i16+1] << 5) | (depthFrame16[i16] >> 3)”的意思是,因为depthFrame16只有8位,所以从第一个byte得到高5位(depthFrame16[i16] >> 3),以及与第二个byte中拼接(depthFrame16[i16+1] << 5)。注:上面那里官方文档有误!
    3. “intensity = (byte)(255 – (255 * realDepth / 0×0fff))”这句话,是将12位的景深数据转化为8位。注:官方文档也不准确!其实就是:255*(1 – realDepth/0×0fff)
    4. “depthFrame32[i32 + *_IDX] = 0;”几条语句将32位的图像置0。其实,从初始化语句“byte[] depthFrame32 = new byte[320 * 240 * 4]”可以看到,这是一个具有4个通道,320*240的视频帧。前三个通道分别为蓝、绿、红的颜色值,第4个通道为保留字段。
    5. “case”语句基于用户序号信息,将玩家所在的像素设置为不同的颜色。当然,当前Kinect最多支持6个玩家,所以“case 7”我认为只是理论上有用。

    处理骨架数据

            void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
            {
                SkeletonFrame skeletonFrame = e.SkeletonFrame;
                int iSkeleton = 0;
                Brush[] brushes = new Brush[6];
                brushes[0] = new SolidColorBrush(Color.FromRgb(255, 0, 0));
                brushes[1] = new SolidColorBrush(Color.FromRgb(0, 255, 0));
                brushes[2] = new SolidColorBrush(Color.FromRgb(64, 255, 255));
                brushes[3] = new SolidColorBrush(Color.FromRgb(255, 255, 64));
                brushes[4] = new SolidColorBrush(Color.FromRgb(255, 64, 255));
                brushes[5] = new SolidColorBrush(Color.FromRgb(128, 128, 255));
    
                skeleton.Children.Clear();
                foreach (SkeletonData data in skeletonFrame.Skeletons)
                {
                    if (SkeletonTrackingState.Tracked == data.TrackingState)
                    {
                        // Draw bones
                        Brush brush = brushes[iSkeleton % brushes.Length];
                        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 = jointColors[joint.ID];
                            jointLine.StrokeThickness = 6;
                            skeleton.Children.Add(jointLine);
                        }
                    }
                    iSkeleton++;
                } // for each skeleton
            }

    iSkeleton标明当前是第几个骨架,从0到5。brush数组标明了每个骨架的骨头绘制颜色。然后,就开始for循环,获得每个骨架的信息,然后判断,如果已经捕捉到(tracked),则开始绘制骨头、绘制关节点。

    getBodySegment()函数在文档中解释道,此函数有3个参数:

    1. 骨架的关节点的集合,Microsoft.Research.Kinect.Nui.JointsCollection对象
    2. 绘制线条的brush
    3. JointID值,多个,每一个都标识一个特定的骨架关节

    getBodySegment()返回Polyline,连接各个JointID。函数原型为:

            Polyline getBodySegment(Microsoft.Research.Kinect.Nui.JointsCollection joints, Brush brush, params JointID[] ids)
            {
                PointCollection points = new PointCollection(ids.Length);
                for (int i = 0; i < ids.Length; ++i )
                {
                    points.Add(getDisplayPosition(joints[ids[i]]));
                }
    
                Polyline polyline = new Polyline();
                polyline.Points = points;
                polyline.Stroke = brush;
                polyline.StrokeThickness = 5;
                return polyline;
            }

    里面有个叫的getDisplayPosition()函数,将关节点从原始数据转化为程序界面显示区域的坐标。解释如下:

    骨架的数据、颜色图像数据和深度数据都是基于不同坐标的。为了从三个流中显示一致的数据,程序就要将各个坐标转换啦!转换步骤如下:

    1. 将骨架坐标从[-1.0, 1.0]转化为深度数据,调用的函数是SkeletonEngine.SkeletonToDepthImage。该函数返回x和y坐标,浮点(不是螺纹>_<),从0.0到1.0。
    2. 将浮点坐标转化为320*240的深度坐标空间,这是NuiCamera.GetColorPixelCoordinatesFromDepthPixel要求的格式。
    3. 将深度坐标转化为彩色图像坐标,调用NuiCamera.GetColorPixelCoordinatesFromDepthPixel,函数的要求参考第三点,返回一个彩色图像,坐标是640*480的彩色图像空间。(各种蛋疼)
    4. 把图像转化为程序界面可以显示的图像,方法是,将x坐标和y坐标分别除以640和480,然后再乘以程序界面显示区域的高和宽。

    函数原型为:

            private Point getDisplayPosition(Joint joint)
            {
                float depthX, depthY;
                nui.SkeletonEngine.SkeletonToDepthImage(joint.Position, out depthX, out depthY);
                depthX = depthX * 320; //convert to 320, 240 space
                depthY = depthY * 240; //convert to 320, 240 space
                int colorX, colorY;
                ImageViewArea iv = new ImageViewArea();
                // only ImageResolution.Resolution640x480 is supported at this point
                nui.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480, iv, (int)depthX, (int)depthY, (short)0, out colorX, out colorY);
    
                // map back to skeleton.Width & skeleton.Height
                return new Point((int)(skeleton.Width * colorX / 640.0), (int)(skeleton.Height * colorY / 480));
            }

    好了,说完了。小朋友们,下次节目,再见吧~

     

    注:文章所述之“文档”皆指:http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/docs/SkeletalViewer_Walkthrough.pdf

    2011.10.31 / 2 条评论 / 分类: 所谓技术