用FrameBuffer实现无Highgui库的OpenCV程序的图片显示

还记得上一篇关于OpenCV移植到FPGA上的文章里,我只写了BMP图片的读写,关于如何显示,因为毕设没有需要,我也没有深入去弄。
后来几次有网友发邮件过来询问,很无奈自己也未曾尝试,更何况做毕设实验的板也早已经还给学校,这个部分就一直搁浅着。直到前一段lanying兄的来信,这个事情才有新的进展。

有个可能比较好的方案是使用GTK那些成熟的库然后整合上OpenCV来实现,这里只介绍一个初级的,也是lanying兄验证完的一个方法,就是使用FrameBuffer来显示图片

首先引用下别人的一点基础介绍

FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。
Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出 FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过 Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接 进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由 Framebuffer设备驱动来完成的。

但是呢,FrameBuffer同时也有很大的限制,比如分辨率这些在系统设置里面如果设定完了,就不能实时更改了,同理包括显示的颜色深度。相关方面的东西请自行了解,这里只给个小链接让大家看看:http://www.91linux.com/html/article/kernel/20071204/8805.html

然后就像上次提到的图片读取的编写,要读取什么格式的图片,你自己必须对那种格式的编码存储结构很了解,然后才在那基础上继续进行。因为FrameBuffer的显示图片的思路是,自己读取图片文件的像素信息,然后按照每一行每一列逐一将像素点的信息写入到FrameBuffer对应的内存空间上。

这次只说个实现思路,具体的技术细节因未亲身验证所以不妄加解说,只是与lanying兄信件往来不时了解下,下面给出一个例程给大家参考,由于没有板验证,所以诸位看代码的时候如果有小细节错误,还望见谅。

http://blog.tyreal.net/download/FrameBuffer

若你有更好的方案,请来信tyreal.han@gmail.com赐教,不胜感激

Tagged

梦至未名

距离我上一次写东西,俨然已经过去了4个月。一毕业,工作之后,就处于盲目奔波状态,却没什么新的收获。在一个南方还算有点名气的集团里弄嵌入式开发,待了一个月多久就离职了,所做的事情无非只是预备热身,没太多值得道来。

此刻的我,又回到了复习考研的路上。只有3个月的时间里,跟其他正常的统考计算机的人一样要复习3科,外加一本没有学过的离散数学。这样的成功概率有多小,其实根本就不在意,这只是年少轻狂的血液躁动之后的不安分。考研究竟有没有意义,根本不去深究,只是选自己曾经想走的路。

昨天跟Jennal聊天的时候,审视了下自己,除了只会写点小玩意,从来没弄过中等规模以上的开发。做过最拿得上台面的东西,也就只有那份毕设了,虽然可能是在FPGA上第一份OpenCV移植成功的资料,也经常有人来信问我一些事情,不过平心而论,我也只是做完毕设之后就没再深入了。对于OpenCV,其实我并不是很懂,算法这种东西对我这个电子系出身又不是很勤快研究的人来说,更是门外汉的水平。偶然间收到一个英国研究生的来信,对于3D高斯模糊优化这种问题只好汗颜回信说我并不了解。而后关于OpenCV移植的一些来信,都只是简单的说一些建议跟指导,每次都谨慎回复,深怕误导了别人。前些日子看到Jennal写的漫画下载器,还是蛮羡慕的,因为自己从来就没弄个像样的项目出来,答应帮他做的QuesLite的Flex界面也拖到最后只能道歉说没时间动手。

就是这样长期处于羡慕别人的状态,无论是写程序,还是弹吉他。读一个都已经底层到物理级别的微电子专业,编程是自学的,弹吉他也只是有了好友的吉他之后,在网上搜了下乐理看看视频自娱自乐而已。当初折腾了半天才写出了WoW的插件,看弹奏视频也是揣摩了好久才把切音练熟。每每看到很多弹唱自如的视频旁边写着“新手”二字,我都只能保持⊙﹏⊙!的表情。反正我天生节奏无能,又没有音感,唱歌的时候已经是满负载运作了,又弹又唱现在还是办不到的。至于技术,硬件方面皮毛都不懂,软件能力疲软,自己这样的单核CPU脑袋却曾经狂妄的想要并行处理,这又是多么讽刺的事情。

工作之后才意识到自己一些想法的改变,会不会以后就不会再有为自己曾经的想法而活的冲动呢?自认为辛辛苦苦才从同辈们手中抢来的工作,一个月的工资是2500,在厦门这样的地方要涨到猴年马月才能出头呢?一直认为,在分配不均的局势下,想法比努力重要,不过,这或许又是为自己的不努力找借口。什么叫“辛辛苦苦”,我这种大三之后才知道“努力”的人根本没资格谈这个词。

大三的暑假,跑去厦大找阿睿跟飞龙学东西,那段蹭吃蹭喝的日子里,晚饭后散步在环岛路走回海韵校区的路上,记得有几次跟阿睿的对话都让他对我这个偏执的小孩颇为无奈。我跟Jennal说我辞职准备考研,他很自然的认为我会报考厦大。那些在木栈道上来来回回的每一晚,舒适的海风还有那缓缓拂拭沙滩的浪涛,都未曾让自己放下焦虑的心情,那个暑假的一切,都只是为了挽回某人的盲目努力而已。然而,后来几次再回到厦大西村的时候,自己也没再试着一路走回海韵,却偶尔遐想,有朝一日若能平静地再次回到那条路上,从晚上的上弦场旁漫步而过,云淡风轻,仿佛是自我救赎的结局一样。

过后的日子,曾经每天倒数不断准备的金山面试,已无任何意义。再之后的日子,如同当初在木栈道上错乱的心情一样,背离自己内心深处的答案越来越远。此刻,只想将这一切错乱,亲手停摆。

在这还有热血去燃烧的岁月里,真庆幸曾经的梦想,一直都在。

OpenCV在基于FPGA的嵌入式系统中的移植研究

自己本科毕设的内容涉及到OpenCV
所以需要移植OpenCV到Nios平台上的嵌入式系统中
可是自己搜遍所有可能的资料
还是没有看到有人做过
即使在我毕设答辩前5天
自己依旧没有做成功

几度犹豫
曾想过腰斩题目改变毕设内容
最后忍住坚持了下来
论文末尾还都写上了移植失败分析
之后的几天
事情就像喜剧结局一样的峰回路转
原本以为不成功的移植居然在小组答辩演示的时候成功了
然后在大组答辩之前找出了不稳定的原因
于是
这篇文章就有了雏形

导师曾让我整理好自己毕设论文中的关于OpenCV的移植研究的部分
可以考虑发表出去
毕竟没人做过还是有些参考价值
自己这一段一是休息外加忙着聚会
二是不知道这样的技术含量值不值得发表
自知现在浮躁的环境发表个论文就像在刷个人数据
其实不见得文章都有什么价值
自己在查阅资料的时候也曾见过几篇文章的测试数据跟精度居然都能一模一样
只是换到自己要发表的时候
就诸多考虑
终究不想敷衍了事
只是想保持自己心中学术思想之正直
听起来多少有点自视清高的酸味
酸就酸吧
得瑟归得瑟,终究对得起自己

唠叨这么久终于开始要说正题了
感谢诸位忍到此刻的耐心
由于之前没人做过这方面的移植研究
所以本文能参考的资料不多
限于笔者的水平
若有错误之处还望见谅

移植整体步骤

  1. 配置交叉编译开发环境,移植uClinux系统
  2. 获取OpenCV源码,配置并编译,生成目标平台的静态链接库
  3. 自行编写文件读写以及图片显示函数,摒除对Highgui库的调用依赖
  4. 将改写后的程序与生成的静态链接库链接一起,下载到目标平台上测试运行

对于第一步,给个自己写的文章当参考——《uClinux在Nios II平台上的移植》,我将从第二步开始说起
值得注意的是:

  • Highgui是造成移植程序运行不稳定的一个原因,毕竟不同嵌入式硬件平台之间都有诸多区别,更何况与Pc的差异。所以要移植OpenCV程序到Nios II的嵌入式系统中,请修改源程序,摒除对Highgui库的调用依赖,自行编写文件读写以及对IplImage这种OpenCV特有数据结构的初始化。

本文使用nios2-gcc-3.4.6 + OpenCV 1.1 pre测试,OpenCV的1.0、2.0版本我也尝试过编译,均告失败,只有1.1测试通过。

配置编译OpenCV

获取OpenCV源码

你可以从http://sourceforge.net/projects/opencvlibrary/files/这里下载OpenCV的源码
点击View all files那个按钮,然后下载opencv-unix 1.1pre1,在终端输入

tar -zxvf opencv-1.1pre1.tar.gz

解压供后面使用
如果你还尝试了别的版本并且也成功了,请不吝告诉在下

配置OpenCV编译信息

进入刚才解压出来的源码文件夹,输入配置命令

$ ./configure --host=nios2-linux --without-gtk --without-carbon --without-quicktime --without-1394libs --without-ffmpeg --without-python --without-swig --enable-static --disable-shared --disable-apps CXX=nios2-linux-g++ --prefix=/home/lee/uccv

–host 指定要编译的目标平台

–witout-* 禁用各种函数库,一是uClinux本来就不支持这里的大部分库,除了gtk,其他的其实你不填也是一样不会编译进去;二是出于精简的需要,说不定日后真支持某个库了,这些我们不需要的东西还是明令去除的好

–enable-static –disable-shared 启动静态链接库,禁用动态链接,因为嵌入式系统对内核的大小要求很看重,不适合将OpenCV库编译成动态链接库,因为这样生成的库都是完整且较为庞大,所以在移植程序数量较少的情况下,通过静态链接库,只将程序用到的部分与程序编译链接到一起,是相对比较理想的选择。另外一个原因是我曾经想使用动态链接库来运行,可是编译出错,暂时无解。

–disable-apps 禁止编译范例程序,因为要编译可以在FPGA上的uClinux系统运行的OpenCV程序,需要在编译的时候额外添加参数,而且我们等会还要自行测试编译,这里一定得禁用掉,否则会导致之后make出错。

CXX 指定编译器。其实这个可以不加,只要host里面指定的平台系统能识别,最后生成的makefile里面的编译器配置信息都是一样的,之所以还保留是因为之前参考ARM下的移植文章,在此留着给大家之后编译若有错误当做一个切入点。

–prefix 可以设置OpenCV编译结果想要安装的路径,这里设置为笔者Fedora中用户所在的个人文件夹。

然后经过一堆输出信息刷新,你可以看到如下信息:

v4l那是因为我毕设要做双目测距程序,需要用到摄像头,所以我留着(不过最后还是失败了,西奈……uClinux下摄像头驱动的移植又是个头痛的问题)。

一切正常的话,我们就开始编译

$ make

如果make没有出错,就开始安装目标平台上的OpenCV函数库吧

$ make install

然后在我们设置的路径下找到lib文件夹,进入就可以看到编译好的静态链接库了

摒除Highgui的依赖

由于不能再用Highgui库了,如果你的程序有用到Highgui,我们就得自己重写文件读写以及图片显示函数,这里着重讲下如何对图片文件读取以及之后如何使用自己读取的图片数据来初始化IplImage,以此为例,其他部分按照这样的思路同样去修改。

  • 删除所有Highgui的头文件引用

为了对原来写好的程序进行最低程度的修改,我们只删除Highgui的头文件引用,然后自行添加个头文件与源文件,在其中加入的图片读取函数的名称,跟原来一样为IplImage* cvLoadImage(char* filename, int iscolor = 1)

  • 读取图片函数的编写,将图像数据存在BYTE*指针指向的内存空间

在自行编写cvLoadImage()函数中,根据要读取的图片格式不同,对应自己编写读取算法,唯一相同的步骤是,最后将图片数据读取到一个BYTE指针指向的空间

  • 使用读取的图像数据初始化OpenCV的IplImage数据结构

读取好图片,可以使用OpenCV的cxcore中的两个函数来初始化IplImage

IplImage* cvLoadImage(char* fileName, int iscolor = 1)
{
	/*...省略其他代码...*/

	//创建IplImage变量的信息头
	IplImage* img = cvCreateImageHeader(cvSize(width,height), depth, channel);
	//利用BYTE指针数据初始化IplImage
	cvSetData(img, (void *)blocks, lineByte);

	/*...省略其他代码...*/
	return img;
}

width、height是要创建IplImage图像的宽度与高度
depth是图像的位深
channel是图像的通道数
blocks是读取来的图像数据的BYTE*指针

lineByte是OpenCV中图像每一行的数据宽度,OpenCV规定图像的每一行数据宽度都必须是4的倍数,但是图像不同位数、不同宽度,不一定满足这个条件,所以这个变量之前需要额外进行一个简单的计算处理

lineByte = (width * biBitCount/8)/4*4;
//biBitCount是图像一个像素占用的位宽大小

以上width、height、biBitCount等int变量的值都可以从图片的文件头读取得知,具体方法请查阅不同图片格式的资料说明。

需要说清楚,我们只是自己简单的写了一个读取特定图片格式的函数,比不上原来的highgui原来的API那么好用,各种格式都支持,不过一般应用的时候,格式都比较固定,这点相对不是什么大问题。

  • 最后对IplImage的释放就不像原来一句cvReleaseImage()就搞定,要分2次完成
delete[] img->imageDataOrigin;
cvReleaseImageHeader(&img);

至此,我们就完成了脱离Highgui自行完成图片文件读取函数,至于文件写入乃至图片显示,限于篇幅原因就不详细讲解,基本思想掌握,剩下的就是自由发挥了。
等我工作定下之后,闲暇再找机会补写看看。

这里留下一个自己写的BMP读取的源码,供大家参考下,写的很搓很随意,而且只读取彩色图像,随意瞥下了解就好。
<—传送门—>

链接整合程序

弄到这里,有了OpenCV的静态函数库,也有脱离Highgui的源码,就可以开始编译链接目标平台的程序了

#先编译文件读取程序
$ nios2-linux-g++ -c bmpDecoder.cpp bmpDecoder.o -I/home/lee/uccv/include/opencv

#再编译测试OpenCV程序
$ nios2-linux-g++ -c test.cpp test.o -I/home/lee/uccv/include/opencv

#最后与函数库链接在一起
$ nios2-linux-g++ -o test bmpDecoder.o test.o -L/home/lee/uccv/lib -lcv -lcvaux -lcxcore -lml -lpthread -elf2flt

-I 指定源码中的头文件引用库位置

-L 指定静态链接函数库位置

-lcv -lcvaux -lcxcore -lml 是需要链接的OpenCV的几个静态链接库,顺序很重要,更改顺序很可能导致undefined reference,需要根据自己编写的程序微调这个参数列表的顺序。

-lpthread 支持多线程程序,我是根据错误提示信息加上这个参数,更深入的理由我并未深究。

-elf2flt 编译为flt格式程序,uClinux默认没有MMU,只支持绝对内存地址的二进制格式,这个记得一定要加上

假如没有什么意外的话,你就会获得最后编译出来的目标平台的OpenCV程序文件

最后唠叨

编译好了,链接成功了,按照我之前给的那篇文章的说明,再烧写下载程序到目标板上测试运行。

虽然我觉得以Cyclone II级别的FPGA器件运行OpenCV的效率,还不能到实际应用的程度,不过以现在FPGA的发展势头,多尝试新的SoPC应用开发,待硬件资源足够之时,也未必不是一大优势。

至于一些目前其他高端的FPGA的运行表现,由于自己没有条件测试,在此就不妄加评价了。

Tagged , , , ,

电信你敢再无耻点么

最近上网点击链接后,地址栏老重定向,出现一堆类似
http://59.59.58.22/req.php?str1=112774803450039633&t=00312613659330290855001277480345&str2=http://www.ccb.cn/portal/cn/home/
的地址,网页也因此刷不出来

稍微查了一下,据说是万恶的电信的DNS劫持想推广告导致的

 

根据大家的提示,我也把DNS更改了google的免费DNS服务器
8.8.8.8
8.8.4.4

结果还是个悲剧……

最后查到有个人提示说,要清除本机的dns缓存
在windows的控制台终端输入

C:\>ipconfig /flushdns

最后再上网浏览网页,终于可以不用老是被电信恶心了

——————————————————————————————
最近发现,这样的方法还是解决不掉

除了打电话去投诉估计还真没办法了

uClinux在Nios II平台上的移植

说起来网上关于如何在FPGA开发板上,对基于NIOS II软核的平台移植uClinux系统的资料真的很多,写下这篇文章,一是提到点自己测试的经验;二是整理资料,希望能有个更清晰明了的教程。

上次写魔兽插件的教程,越到最后觉得写得越仓促,主要是因为追求细节足够详细导致精力不够,毕竟写写东西也只是业余消遣。保证初学者有迹可循又简明扼要,的确不是容易办到的事情。这样冗长的技术教程,我会在给初学者留条小路的基础上尽量一针见血的说明事情。

对于开发板的选择,我也买错了,红色飓风这种板高不成低不就的,700块大洋花了真的一点都不值,至少在这次应用上,它的片上资源已经捉襟见肘。如果有可能,买块DE或者DE2的板,一块可以解决日后很多应用,我就是借用了实验室的DE2才得以继续进行下去的。

废话说完了,下面开始说正事。

移植实现思路

  1. 建立交叉编译开发环境
  2. SoPC Builder配置硬件资源
  3. 配置、编译内核
  4. 下载运行测试

这里采用Fedora 12为基础建立交叉编译环境,我是在Vmware虚拟机上安装的Fedora,如果可以推荐直接装在物理机上,装Ubuntu、CentOS之类的系统都可以。初学者请搜索“Vmware 安装linux”,自行学习基础,如果Vmware没有注册使用不了,为了版权的考虑,可以选用Virtual Box这样的开源虚拟机软件。

SoPC Builer是Quartus开发工具中的一部分,我使用的是Quartus 7.2版本,安装在物理机的Windows XP下

熟悉基本的Linux命令,以便后面解压源码等步骤使用

下载到板上测试,我使用的是NIOS Shell Command,所以要安装与Quartus同版本的NIOS开发工具,一样装在XP下。可以自行下载Linux版本的开发工具,这只是我懒得换版本而已。

搭建交叉编译环境

安装并配置linux系统

假设已经安装Fedora 12,要进行下一步开发,需要安装一些开发库。

在终端输入如下指令,可以一次性安装好本次开发需要的所有库:

$ sudo rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm

$sudo yum install git-all git-gui make gcc ncurses-devel bison \
byacc flex gawk gettext ccache zlib-devel gtk2-devel lzo-devel \
pax-utils libglade2-devel

如果是Ubuntu之类的其他Linux系统,请看NiosWiki的安装说明:<—猛击我—>

获取nios2-linux源码

要进行操作系统的移植,一要移植bootloader,二要移植内核,三是根文件系统,移植的编译需要交叉编译工具,这里可以下一个源码包,里面已经整合了我们需要的各种东西

在终端输入下载


$wget http://www.niosftp.com/pub/uclinux/nios2-linux-20090730.tar

把nios2-linux-20090730.tar解压到当前目录:


$tar nios2-linux-20090730.tar

切换到nios2-linux 目录,验证文件


$./checkout

下载交叉编译工具

获得nios2-linux源码之后,可以在toolchain-build目录下自行编译生成交叉编译工具,也可以从http://www.nioswiki.com/OperatingSystems/UClinux/BinaryToolchain这里下载编译好的BinaryToolchain。笔者试过以nios2-linux-20090730版本的源码加上Fedora的gcc 4.4.3尝试自行编译,但是提示出错,最后选用现成的交叉编译工具,节省研究时间。


$wget http://www.niosftp.com/pub/gnutools/nios2gcc-20080203.tar.bz2

之后解压该文档,交叉编译工具在/opt/nios2/bin下,Fedora 12默认建立用户一开始无法使用sudo请自行搜索“Fedora 无法使用sudo”


$sudo tar -jxf nios2gcc-20080203.tar.bz2 –C /

然后设一下Linux用户的PATH变量,这样才可以等会编译的时候才找的到交叉编译器


$ vi ~/.bashrc

根据ToolChain所在位置在.bashrc中添加一行并保存,其中的路径为刚才下载的交叉编译器的解压位置


PATH=$PATH:/opt/nios2/bin

重新登录linux用户后,查看nios2-linux-gcc 版本,验证编译器是否可用


$nios2-linux-gcc -v

SoPC Builder配置

根据NiosWiki的说明,要在NIOS上移植uClinux系统,硬件需要的最低标准如下:

  • Nios II f/s级别的核,必须要有硬件乘法器(推荐f级别,s级别是最低要求)
  • sdram(至少需要8M)
  • 一个full featured的计时器(IRQ为0)
  • 一个jtag/serial uart

假设你已经会使用SoPC Builder,然后根据上面的要求,建立一个最小系统。在移植uClinux之前你可以使用NIOS建立一些HelloWorld的程序测试一下,是否可以运行,测试通过了就可以跳过继续下一阶段了。

对于使用DE2或者一些资料比较齐全的开发板的童鞋,在第一次进行移植工作的时候,可以先使用附带的范例中现成的SoPC工程项目,可以简化操作,提高成功率,避免因为Sopc Builder中配置硬件不正确,导致后期移植系统出错却误以为是配置编译过程中操作不当。

  • 按照nioswiki上的资料表明,对nios平台移植uClinux系统,需要将一个Full Featured的Timer设置为IRQ 0,但是笔者试验证明,即使使用Custom模式的Timer,而且IRQ不设置为0,依然可以移植成功。但是IRQ 0是linux默认自动检测的一个中断号,为了避免不必要的麻烦,对于那些附带的范例中IRQ不为0的,笔者推荐还是手动稍微修改一下设置比较好。

在SoPC Builder工程的目录下找到如下2个文件:

  • {SoPC项目名称}.ptf
  • {Quartus项目名称}.sof

将其复制保存以供后面使用,前者用来配置uClinux目标板的基本信息,后者用来烧写FPGA。

配置编译内核

配置内核

切换到nios2-linux下的uClinux-dist,执行


$ make menuconfig

如果你已经安装了ncurses库,就可以进入内核配置的图形界面

然后分别对Vendor以及Kernel的下级菜单进行如下设置:

Vendor/Product Selection —>
— Select the Vendor you wish to target
Vendor (Altera)  —>

— Select the Product you wish to target
Altera Products (nios2)   —>

Kernel/Library/Defaults Selection —>
— Kernel is linux-2.6.x

Libc Version (None)  —>
[*] Default all settings (lose changes)
[ ] Customize Kernel Settings
[ ] Customize Vendor/User Settings
[ ] Update Default Vendor Settings

按Y为选中项目,N为取消,M是动态加载,按2次ESC为退出到上一级菜单,最后退出的时候会提示保存设置,根据上面设置并保存退出。

本文这里使用内核的默认设置,并为进行过多剪裁。实际上笔者发现该内核开发包可以很完善的支持DE2等一些官方认证的开发板,默认的设置都可以开始并使用网卡等设备,若无特殊要求,默认设置就可以胜任。

你可以选择Customize Kernel Setting,保存退出,然后又会进入新的配置界面,里面有详细的内核剪裁、配置选项,可以尝试着自己修改看看。东西都是折腾中学习出来了。

配置硬件信息

将之前SoPC Builder建立的ptf文件复制到某一文件夹下面,然后根据自己保存的路径与文件名输入


$ make vendor_hwselect SYSPTF=/home/lee/nios2-linux/system_0.ptf

之后选择对应的cpu以及供内核运行的存储器,cpu只有altera_nios2一个,但是板上的存储器类型很多,这里选择使用SDRAM来运行内核。

如果是物理机上的Linux系统,复制粘贴这个文件我就不说了,如果是Vmware的虚拟机安装Linux,Quartus又装在物理机的Windows下,有几种方法:

  • 使用虚拟网卡与主机相连,用Samba之类的共享文件,这种设置折腾折腾之后,你的Linux基本操作设置应该也都还可以了
  • 给虚拟机的Linux系统安装Vmware Tools,然后用虚拟机的共享文件夹功能来复制文件,不过Fedora 12这种新系统,Tools是不好安装成功的,如果是版本比较低的安装就很顺畅
  • 直接用优盘当中转,物理机把文件复制到优盘上,然后挂载到虚拟机中复制出来……很傻很简单很实在

这里配置这一步的作用,可以看NiosWiki-LinuxHWselect,说明的很详细,主要就是生成板上IO口以及存储器资源

编译

上面的步骤都没有错的话,直接输入


$make

第一次编译成功之后,可以在uClinux-dist的目录看到一个带有内核编号的文件夹,这里是linux-2.6.x,进去之后可以看到生成的一些相关文件与文件夹,在linux-2.6.x/arch/nios2/boot下,会有一个zImage的内核映像文件,这就是我们要的,按照上面的方法复制保存它,以供等会烧写使用。

这个uClinux-dist的开发包,默认设置就已经帮你把bootloader以及根文件系统配置好了,使用make的时候会对应一起生成。bootloader默认是u-boot,根文件系统是romfs。

下载运行测试

经过如上的操作,此刻我们会有2个文件,一个sof文件用于烧写FPGA芯片,一个zImage用于烧写uClinux内核。

打开NIOS II Command Shell,切换到文件保存的路径下,这里是优盘的盘符下,然后输入如下指令:


$ nios2-configure-sof DE2_NET.sof

看到下面的信息说明烧写成功

接着输入如下命令,-g是指下载后复位目标板,让内核立即运行,记得不要漏掉


$ nios2-download –g zImage

若成功会看到如下提示:

最后,输入


$ nios2-terminal

启动调试,就可以看到uClinux启动的画面

至此,最简单的一次移植我们已经都成功了,可以运行基本的linux命令,测试一下移植的uClinux系统。

我们这里只是在SDRAM上下载了内核映像,测试移植的话,这几个命令就可以了,但是每次断电之后,所有信息都会丢失。若你要真正烧写到flash之类的存储器上,你需要先将FPGA的下载方式改为AS,烧写FPGA芯片,然后接着才是烧写系统内核到flash上,对于第二步在这里转载gewanyong的文章,他已经说的很详细了,步骤如下:

(1)先把内核映像转换成可以直接烧到flash的文件,用以下命令:


$elf2flash --input=zImage --output=xx.flash --base=0x..... --reset=0x..... --end=0x.... --boot=xxxx.srec

其中:

  • zImage就是编译好的uClinux内核映像,
  • xx.flash是这条命令输出的文件,
  • –base和–end分别对应flash芯片的基地址和结束地址.–reset是CPU复位地址,一般和–base相同.
  • xxxx.srec是bootloader,可以从安装目录下的ip/altera_nios2下拷贝,名字是:boot_loader_cfi.srec,若不知具体位置可以搜索一下.

(2)把上面生成的xx.flash文件下载到目标板 flash中:


$nios2-flash-programmer -g -c usb-blaster --base=0x... --program xx.flash

usb- blaster是用到的下载电缆,-g表示下载后复位目标板,启动内核.

那几个地址可以从.ptf文件中得到.

添加简单程序

在第一次生成内核映像时,输入make之后会在uClinux-dist生成romfs这个文件夹,是对应根文件系统的编译内容文件夹,我们复制自己编写程序到其中,重新make一次,新产生的内核映像文件就包含了我们自己写的程序。

在这里笔者写了一个最简单的C语言的Hello World程序,测试自己编写程序移植的可能性。


#include <stdio.h>

int main(void)

{

printf("Hello,world!\n");

return 0;

}

将如上代码保存为.c文件,这里取名为test.c,然后输入编译命令


$ nios2-linux-gcc test.c –o test –elf2flt

由于uClinux没有MMU,所以不支持虚拟内存以及程序的相对地址运行,程序文件只能使用绝对物理地址的FLT格式,所以一定得加上-elf2flt参数,否则编译完的程序无法再uClinux上运行。

编译好可以使用如下命令验证是否是FLT格式


$ nios2-linux-flthdr test

检验成功后,在romfs下建立一个名为test的文件夹,把生成的test程序复制到其中,然后回到uClinux-dist下再次编译内核


$ make

之后重复之前的烧写步骤,直到进入uClinux系统,开始测试


$ cd test

$ ./test

到这里,我们也可以自行添加简单的程序了。当然你也可以配置好网卡跟ftp,使用ftp把编译好的程序传到板上运行,可以不用每次都重复一遍烧写过程,这个再google几下,就大概知道如何做了,甚至可以考虑改成NFS根文件系统,但是相对改动就大些,初学的时候,为了简单容易成功,麻烦就麻烦吧。

至于要再此基础上开发什么应用,那就任君选择了。

之前由于没有人在Nios平台上移植OpenCV,所以我自己尝试移植了一下,这几天有空我会整理出来,不过由于要发表到学术期刊上,所以可能会晚几天。

如果想深入学习,bootloader的自行修改,内核的剪裁以及驱动的编写都可以去了解下。

这次就说这么多吧,白白~~

Tagged , , ,

OpenCV学习笔记之一——图像亮度对比度变换

由于毕设的关系,开始折腾计算机视觉
双目测距的具体实现,现在刚有点眉目而已
看了下图像亮度/对比度的变换的资料
结果又发现漫山遍野的都是同一份代码
而且几乎没有什么解释
原本决定等Harris角点那些弄完再写篇文章
现在想想
还是先记下这份简单的笔记并加以讲解下

那些计算机科班出身的人就不用看这篇了
这只是给我们这些物理/电子系中没有学过”数字图像处理“这门课的人看的

术语解释

亮度(Brightness)

也叫亮度,中文翻译不同而已,你可以看Wikipedia
RGB 色彩空间中,明度可以被认为是R(红色),G(绿色)和B(蓝色)座标的算术平均 μ(尽管这三个成分中的某个要比其他看起来更明亮,但这可以被某些显示系统自动补偿):

 \mu = {R + G + B \over 3 }

对比度(Contrast)

我实在google不到满意的解释
大概上来说
灰度图像的对比度指的是图像中的最黑与最白的点,他们灰度值的比值关系
彩色图像由于有3种通道,我的理解是各个通道中的灰度值的比值关系,确切的定义还望有人指教

实现思路

知道明度跟对比度的定义,接下来
要修改亮度,就把图像的通道的灰度值一起增加或者减少就可以
要修改对比度,需要把图像的通道的灰度值以一个值为临界点,往相反两个方向变化
比如,取128为阈值,要增强对比度,低于128的值修改得比原来更小,高于128的值再修改得更大,要降低对比度就反向操作

下面给出我自己写的一份代码,演示明度以及对比度的调整
对比度的公式参考自Photoshop的公式:
nRGB = RGB + (RGB – Threshold) * Contrast / 255

源代码

#include "highgui.h"
#pragma comment(lib,"cv200d.lib")
#pragma comment(lib,"cxcore200d.lib")
#pragma comment(lib,"highgui200d.lib")

int BrightnessAdjust(const IplImage* srcImg,
					 IplImage* dstImg,
					 float brightness)
{
	assert(srcImg != NULL);
	assert(dstImg != NULL);

	int x,y,i;
	float val;
	for (i = 0; i < 3; i++)//彩色图像需要处理3个通道,灰度图像这里可以删掉
	{
		for (y = 0; y < srcImg->height; y++)
		{
			for (x = 0; x < srcImg->width; x++)
			{

				val = ((uchar*)(srcImg->imageData + srcImg->widthStep*y))[x*3+i];
				val += brightness;
				//对灰度值的可能溢出进行处理
				if(val>255)	val=255;
				if(val<0) val=0;
				((uchar*)(dstImg->imageData + dstImg->widthStep*y))[x*3+i] = (uchar)val;
			}
		}
	}

	return 0;
}

int ContrastAdjust(const IplImage* srcImg,
				   IplImage* dstImg,
				   float nPercent)
{
	assert(srcImg != NULL);
	assert(dstImg != NULL);

	int x,y,i;
	float val;
	for (i = 0; i < 3; i++)//彩色图像需要处理3个通道,灰度图像这里可以删掉
	{
		for (y = 0; y < srcImg->height; y++)
		{
			for (x = 0; x < srcImg->width; x++)
			{

				val = ((uchar*)(srcImg->imageData + srcImg->widthStep*y))[x*3+i];
				val = 128 + (val - 128) * nPercent;
				//对灰度值的可能溢出进行处理
				if(val>255) val=255;
				if(val<0) val=0;
				((uchar*)(dstImg->imageData + dstImg->widthStep*y))[x*3+i] = (uchar)val;
			}
		}
	}
	return 0;
}

int main(int argc, char** argv)
{
	IplImage* srcImg = cvLoadImage("lena.jpg");
	assert( srcImg != NULL );

	IplImage* brightnessImg = cvCloneImage(srcImg);
	//亮度变换,最后数值取值为正时变亮,负则变暗
	BrightnessAdjust(srcImg, brightnessImg, 80.0f);

	IplImage* contrastImg = cvCloneImage(srcImg);
	//对比度变换,数值小于1降低对比度,大于1增强对比度
	ContrastAdjust(srcImg, contrastImg, 1.3f);

	cvNamedWindow("Source",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("BrightnessAdjust",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("ContrastAdjust",CV_WINDOW_AUTOSIZE);
	cvShowImage("Source",srcImg);
	cvShowImage("BrightnessAdjust",brightnessImg);
	cvShowImage("ContrastAdjust",contrastImg);
	cvWaitKey(0);
	cvReleaseImage(&srcImg);
	cvReleaseImage(&brightnessImg);
	cvReleaseImage(&contrastImg);
	cvDestroyWindow("Source");
	cvDestroyWindow("BrightnessAdjust");
	cvDestroyWindow("ContrastAdjustrast");

	return 0;
}

对新手提示几句,运行这个代码请在工程文件的目录下放一个图片,名字是lena.jpg
另外,对彩色通道的处理,循环只有3次,这种循环最好放在最外围,因为图像的长宽一般都远大于这个值
如果你把小循环放最里面,频繁的循环切换,效率会低不少
这只是程序编写技巧上的小提示

本代码在Visual Studio 2008+OpenCV 2.0下运行通过,效果如下


并且与photoshop的调整效果对比过
亮度变换与ps旧版效果一致,貌似ps对亮度变换的公式进行过调整,新版不是这么单纯的加减灰度值
对比度就几乎都差不多了

更多资料

关于OpenCV的安装、配置以及基础学习,可以在OpenCV中文官网查得
官网的入门说明异常详细,各种平台各种IDE都有介绍,我就不多废话了

下次再说说Harris角点的事情吧

Tagged

魔兽世界插件开发入门教程——3

一周就这么过去了
DTP居然没有面试
不知道出了什么问题
毕设也还没开始弄
此刻这个教程估计要真挖出大坑了
我尽量整体介绍完
细节部分如果你想了解
留言或者给我Email
我看到会尽早回复

本节目标

前两节我们已经实现了这个插件的最基本界面
这一节我们开始将插件功能的核心实现——Lua文件的编写
请建立一个QuestRepeat.lua文件,并加入到第一节的Toc文件最后的文件列表中

本节的代码你可以在此获得
[—-请猛击我—-]

代码讲解

 

QuestRepeat.lua整体结构

function initialize()
--插件一些变量的初始化
end

function QuestRepeat_OnLoad()
--插件框体加载成功事件的响应处理函数
end

function QuestRepeat_OnEvent()
--插件注册事件的处理函数
end

function Item_Check(name)
--计算角色包里以及货币栏里物品的数量
end

function QuestRepeatAllButton_OnClick()
--全部完成按钮的响应函数
end

function QuestRepeatCompleteButton_OnClick()
--完成一次按钮的响应函数
end

function QuestRepeatDecrement_OnClick()
--文本框数值减少按钮的响应函数
end

function QuestRepeatIncrement_OnClick()
--文本框数值减少按钮的处理函数
end

function QuestRepeatInputBox_OnTextChanged()
--文本框数值越界处理
end

上面我们可以了解到整个Lua的大致结构
各个函数的具体作用注释都说的很清楚

回忆下这个软件的功能
是要可以快速完成重复上缴物品的任务

那Lua文件至少要干三件事情:

1.检测NPC给的任务是不是可重复完成任务
2.如果是重复任务,检查我们的包裹里是否有足够的物品可以重复上缴,假如只能交一次,我们就没使用插件的必要
3.满足重复上缴的条件,我们怎样才能快速完成任务

实际上,即使上述3个事情我们都考虑好了,细节上还是有些麻烦的地方,比如:

4.任务上缴的过程中,如何中断
5.任务最后要完成或者用户选择中断的时候,如何初始化插件,恢复最初的状态
6.要是任务要求的不是我们包裹里的东西,而是货币面板上的虚拟代币我们要如何判断数量呢

在此基础上,考虑与上两节的界面的操作处理:

7.我们如何处理各种按钮响应,文本框的数值超过我们最大能上缴任务的次数时候要如何处理

也就是说,解决了这7个问题,最初级版本的QuestRepeat就完成了。
不过,这是考虑的时候才会分解成这样的几个问题,实际编写的时候,有时候一个代码块就可以一起解决掉几个问题
我们对相关联的问题放一起进行解答

问题1/2/6

function QuestRepeat_OnLoad()
	initialize();
	this:RegisterEvent("GOSSIP_CLOSED");
	this:RegisterEvent("GOSSIP_SHOW");
	this:RegisterEvent("QUEST_COMPLETE");
	this:RegisterEvent("QUEST_PROGRESS");
	--将下面这句注释取消可以测试进入游戏时插件是否加载成功
	--DEFAULT_CHAT_FRAME:AddMessage("QuestRepeat Enabled(Debugged by Tyreal)");
end

function QuestRepeat_OnEvent()
	--[[..这里省略与问题无关部分,请看源文件..--]]
	elseif (event == "QUEST_PROGRESS") then
		if (IsQuestCompletable() and iterator == nil) then
			n = GetNumQuestItems();
			i = 0;
			if (n ~= nil) then
				if (n ~= 0) then
					MAX_VALUE = 999;
					while i < n do
						i = i + 1;
						local name, texture, numItems, quality, isUsable = GetQuestItemInfo("required",i);
						value = floor(Item_Check(name)/numItems)
						if (value < MAX_VALUE) then 							MAX_VALUE = value; 						end 					end 				end 			end 			this:RegisterEvent("PLAYER_TARGET_CHANGED")--为上缴中断做准备,注册角色目标切换事件,后面解释 --以下代码是问题3的一部分 		elseif(iterator) then 			if (iterator > 0) then
				QuestProgressCompleteButton_OnClick();
			end
		end
--[[...--]]
end

--计算包里物品的数量
function Item_Check(name)
	Count = 0;
	for bag = 4, 0, -1 do
        local size = GetContainerNumSlots(bag);
        if (size > 0) then
		--遍历包裹的东西
		for slot = 1, size do
                local texture, itemCount = GetContainerItemInfo(bag, slot);
                	if (itemCount) then
		    	local itemLink = GetContainerItemLink(bag,slot)
		    	local _, _, itemCode = strfind(itemLink, "(%d+):")
		    	local itemName, _, _, _, _, _ = GetItemInfo(itemCode)
 		    	--判断是否包含该物品
	 	    	if (itemName ~= "" and itemName ~= nil) then
                        	if (itemName == name) then
                            		Count = Count + itemCount;
                        	end
                    	end
                	end
            	end
        end
    	end

	--TBC之后有些任务上缴需要货币,此处查询角色的货币数量
	local scrollFrame = TokenFrameContainer;
	local offset = HybridScrollFrame_GetOffset(scrollFrame);
	local buttons = scrollFrame.buttons;
	local numButtons = #buttons;
	local itemName, itemCount;
	local index;
	for i=1, numButtons do
		index = offset+i;
		itemName, _, _, _, _, itemCount = GetCurrencyListInfo(index);
		if (itemName == name) then
                	Count = Count + itemCount;
            	end
	end
    return Count;
end
  • 当我们选择了一个任务,进入任务说明页面,可以看见任务要求物品列表的时候,WoW会抛出一个QUEST_PROGRESS事件
    所以我们需要注册这个事件,可以在QuestRepeat_OnLoad()看到
this:RegisterEvent("QUEST_PROGRESS");--注册捕获QUEST_PROGRESS事件
  • 然后看QuestRepeat_OnEvent()中间的部分,我们会判断捕获的事件是不是QUEST_PROGRESS,是就进行处理
    最开始是判断我们是否有足够的物品,这是靠Item_Check(name)来完成的,后面的一点代码是给后面用
  • Item_Check(name)这是自己写的函数
    前半部分是检查包裹里物品数量
    后半部分是检查货币面板,这是对问题6的解答
    最后返回符合查询物品的数量,如果用户含有的数量大于任务要求数量,就可以重复上缴,这样问题2就完成了
  • 那问题1呢?其实WoW中的任务,只能交一次的任务,你几乎是不会拥有要求物品的数量达到可以交2次的时候,所以,当我们问题2帮我们算好了数量,知道可以交超过2次以上,我们就知道这是个可重复任务,问题1也就搞定了

实际上WoW也有类似的API实现这样的功能,你可以去搜索WowProgramming和WowWiki,不同的方法,一样的结果
WoW更新版本的时候,有可能会开放新的API或者封掉一些不合理的API,所以写插件的时候请留意自己用的方法会不会太bug,免得到时候要重新修改,虽然有时候循规蹈矩的,WoW大改的时候几乎大家都得改,比如2.0时代。

请学会查看WowProgramming和WowWiki这样的网站上的API说明,返回值,参数列表,作用,这些都是日后的基本功

DEFAULT_CHAT_FRAME:AddMessage(“”)
可以在WoW的聊天窗口输出字符
我们后面会用到这个来输出一些变量的值,用来调试插件
不要怀疑,就是这么原始
若想要高端开发环境,去看In-Game Addon Studio,不过这个跟我此刻讲解的开发流程就是两回事了,你可以了解看看

问题3/5

要快速完成任务
我最早的设想,设定完成的次数,点击一次“全部完成”,插件就不停的自动点击完成
可是这样的设计是违反BLZ的插件开发原则的
因为这样等于是类似外挂自动完成的功能
这么搞是不太安全的
何况BLZ根本就没开放自动选择NPC这个API(我已经翻烂了相关的手册,真没有,有的是BLZ自己官方插件才可以使用的API)
所以这是无法实现的

那退一步,NPC让玩家手工选择
但是任务的各步骤中的点击按钮插件来自动完成
查查API后,发现这是可行的
我也是基于这个思想来完成的

一起来看下代码

function QuestRepeat_OnEvent()
--[[..老规矩,省略问题无关部分..--]]
		elseif(iterator) then
			if (iterator > 0) then
				QuestProgressCompleteButton_OnClick();
			end
		end
	elseif (event == "QUEST_DETAIL") then
		if (iterator) then
			if (iterator > 0 ) then
				QuestDetailAcceptButton_OnClick();
			end
		end
	elseif (event == "QUEST_COMPLETE") then
		if (MAX_VALUE > 1) then
			QuestFrameCompleteQuestButton:Hide();
			QuestRepeat:Show();
			if(GetTitleText() == questName) then
				if(iterator) then
					if(iterator > 0)then
						QuestRepeatInputBox:SetNumber(iterator);
						QuestRepeatCompleteButton_OnClick();
					else
						initialize();
					end
				end
			else
				QuestRepeatInputBox:SetNumber(1);
				questName = GetTitleText();
			end
		else
			initialize()
			QuestFrameCompleteQuestButton:Show()
			QuestRepeat:Hide()
		end
	elseif (event == "GOSSIP_SHOW") then
		if (iterator) then
			if (iterator > 0) then
				if GetGossipAvailableQuests() then
					i = 1;
					for index, value in pairs({GetGossipAvailableQuests()}) do
--测试输出数据
--[[
DEFAULT_CHAT_FRAME:AddMessage("i="..i);
DEFAULT_CHAT_FRAME:AddMessage("index="..index);
DEFAULT_CHAT_FRAME:AddMessage("value="..value);
DEFAULT_CHAT_FRAME:AddMessage("questName="..questName);
DEFAULT_CHAT_FRAME:AddMessage("-------------------");--]]
						if( mod(index-1, 3) == 0 ) then
							if (value == questName) then
								SelectGossipAvailableQuest(i);
								break;
							end
						i = i + 1;
						end
					end
				end
			end
		end
	end
end

这段代码中,有个很重要的变量iterator,它控制着是否进行不间断的任务自动上缴,大于1时重复上缴,小于1停止

  • 刚最开始跟NPC对话的时候,会有个GOSSIP_SHOW的事件,我们判断是否进行QuestRepeatCompleteButton_OnClick()重复点击按钮进入下一步,如果是的话我们在这事件之后获取NPC存在多少个任务GetGossipAvailableQuests()并根据以前存储的questName自动选择对应的任务,不是就什么都不干,玩家手工选择任务
  • 接着QUEST_DETAIL事件,依旧判断iterator的值来进行自动按按钮
  • 然后是QUEST_PROGRESS事件,这主要是问题1/2/6的部分,但是最后面一直延续对iterator的判断
  • 最后是QUEST_COMPLETE事件,判断可完成的最大次数是否为1,为1就回复WoW默认完成界面并initialize()初始化插件,这里就要解决问题5;如果不是就判断是不是还未启动自动上缴过程,还未启动就记录任务名称,已经启动就继续显示本插件,并自动点击按钮。至此,问题3也解决了。

initialize()的代码很简单,就初始化3个变量

function initialize()
	MAX_VALUE = 1;--玩家可以完成任务的最高次数
	iterator = nil;--是否自动上缴任务控制变量
	questName = "";--自动进行的任务的名称
end

问题4

如果你认真研究完以上代码,差不多对整个流程有个印象了
接下来要解决的就是中断自动完成的问题
因为有时候我们交到一半就不想交了
有个中断的方法是必要

我后面的QuestRepeat有新的面板可以方便控制任务中断过程
关掉那个面板就等于退出本插件
但是现在这个最初级的版本没有那个面板
所以设置玩家目标改变的时候就取消自动上缴过程

function QuestRepeat_OnEvent()
--[[...--]]
	elseif (event == "PLAYER_TARGET_CHANGED") then
		initialize();
		this:UnregisterEvent("PLAYER_TARGET_CHANGED")
--[[...--]]

QuestRepeat_OnEvent()里面完成了很多事情,中断处理也是在这里完成的
由于我们一开始有注册了PLAYER_TARGET_CHANGED事件
所以这里响应之后我们可以调用initialize()进行插件状态初始化
然后注销事件PLAYER_TARGET_CHANGED,因为此时我们已经不需要它了

当然留着也不是不可以
只是这样游戏过程中你每切换一次目标,这里都会来初始化一次插件
没什么影响
丑陋代码,不严谨就是了

最后的问题7,相信你忍着看到这里,也可以自己看源代码解决了

最后唠叨

读完这三节,基本上你就可以使用Blizzard给的官方API加上Windows自带的记事本来编写插件了,至于有些插件用ACE之类的那些库编写的,都是在此基础上的衍生,具体资料可以自己查找

最近写这个教程的时候,终于知道什么叫心有余而力不足,事说多不多,但是时间总是感觉流失得太快,没有太多的耐心把这最后一节好好写仔细,只是草草做个了概括,而且这份代码是我最早写插件的时候从原作上修改的,并不是太规范,所以本教程如果说得不好的地方还请见谅。

有问题请留言,日后若有空,再重新翻新续写也不一定。

Tagged , , ,

如何为SyntaxHighlighter编写自己的Verilog Brush

这几天为了找个合适自己的代码高亮插件

由于自己过于龟毛

所以试用了绝大部分网上推荐的插件

一一尝试了之后

最后决定试用SyntaxHighlighter Evolved

至于之前WP-Syntax

老实说,这个的确很棒,代码支持列表也完全足够绝大部分开发者,包括电子开发人员,默认自带verilog等,只是颜色搭配很一般,加上不知道是不是我自己没设置好,我装了这个之后,

<pre>标签里面的内容都不能居中对齐。

还有另外一个……我忘记名字了,换掉原因多是因为颜色搭配以及排版问题

虽然SyntaxHighlighter Evolved也不见得就是完美

我装了TinyACE Advanced,并选择保留不删除<br />等标签,因为自己习惯手工打回车空行

但是这个时候贴在Evolved里的代码

这些标签就都不会删掉,每行代码都会多出一个这个<br />……

只好最后又禁用掉那个选项

求完美解决方案,或者告诉我怎么胡乱打空行不被wordpress吃掉就可以……

既然要使用SyntaxHighlighter Evolved

这个插件外观漂亮是漂亮,但是支持的语言实在不够

想着日后我需要贴Verilog HDL代码

网上又遍寻不到

于是就自己写了个

以下附上此次编写过程并给点资料方便大家自己写个

准备工作

虽然我们要写个JavaScript的脚本文件,但是实际上,你现在不必去学它就可以写出这个东西了

你唯一可能需要了解的一个东西就是Regular Expression,也就是正则表达式

这个讨厌又很神奇的东西,花个30分钟学学吧,基本的应用蛮快学的

以下是各种推荐资料,先记下,后面需要再回来看吧

 

  • 中文正则表达式教程http://deerchao.net/tutorials/regex/regex.htmhttp://www.cyut.edu.tw/~ckhung/b/gnu/regexp.php
  • 英文教程http://www.regular-expressions.info/
  • 你可以在这里进行在线测试,看看你的正则表达式到底合不合用http://www.gskinner.com/RegExr/

从零开始

请新建一个js文件,我这里取名为shBrushVerilog.js,只是为了跟插件的其他笔刷有个相同的命名规则,整齐而已

1.编写shBrushVerilog.js文件

SyntaxHighlighter.brushes.Verilog = function()
{
	var datatypes = '';//这里的具体代码请看源文件
	var keywords = '';//每种不同语言的关键字都不一样,需要参考语言手册
 	this.regexList = [
	{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	css: 'color1' },			// one line comments
	{ regex: SyntaxHighlighter.regexLib.multiLineCComments,		css: 'color1' },			// multiline comments
	{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },			// strings
	{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },			// strings
	{ regex: /^ *`.*/gm,						css: 'preprocessor' }
	{ regex: /\b\d+\'?[oObBdDhH]?[0-9xXzZA-Fa-f]*/g,		css: 'value' }
	{ regex: /\$\w+/g,						css: 'functions bold' }
	{ regex: new RegExp(this.getKeywords(datatypes), 'gm'),		css: 'variable bold' }
	{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword bold' }
	];
}
SyntaxHighlighter.brushes.Verilog.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.Verilog.aliases = ['verilog'];

其实结构没什么好讲解的

你偷懒的话可以复制粘帖这一份代码然后进行修改

需要注意的地方是this.regexList之后的那些正则表达式对象需要根据语言的特性进行修改

默认风格的颜色参考请看这里:

  • comments, keyword, string, preprocessor, variable, value, functions, constants, script, color1, color2, color3

我的代码中

7-8行的注释因为verilog与c相同,所以可以直接这么套用原有的

9-10的string类型也有可以直接复用的代码

关键的是第11-13行

这里我只解释第13行的预处理字符中的正则表达式

regex: /^ *`.*/gm

  • ^ 意思是开头有可能含有0-无数个空格(记住^之后有空格哦)
  • 然后接着一个`字符,这是verilog中的预处理的字符
  • 之后的.*说明预处理字符之后含有0-无数个字符
  • g的意思是递归整个文件的匹配搜索
  • m的意思是多行匹配,如果不加上这个,比如:
    `timescale
    `timescale
    

    第二行是无法被匹配的

更多知识请自学正则表达式

我们写好这个文件之后,就可以把它加入到我们的插件中去

请编辑你的SyntaxHighlighter Evolved,在WordPress插件安装列表那里点击它的Edit选项

2.在一堆wp_register_script中间插入以下这句来注册我们新弄好的笔刷

wp_register_script( 'syntaxhighlighter-brush-verilog',
	plugins_url('syntaxhighlighter/third-party-brushes/shBrushVerilog.js'),
	array('syntaxhighlighter-core'),
	'20100420'
);
  • 函数的第一个参数是我们注册的笔刷名称,请保持“syntaxhighlighter-brush-你的笔刷名称”的格式
  • 第二个参数是我们笔刷的存放路径,请相应放个插件可以搜索得到的位置,最好就放在插件下
  • 照抄吧,我没去查这个的含义,现在快凌晨6点了,好困啊
  • 你的版本号,给自己个参考免得下次修改不知道哪个比较新

3.然后我们需要对我们的笔刷映射一个名称来代表它

请找到这个函数并在它下面添加

$this->brushes = (array) apply_filters( 'syntaxhighlighter_brushes', array(
/* ... */
'verilog' => 'verilog', //左边是日后我们要试用的标签名称,右边是笔刷名称
) );

4.最后,上传新笔刷文件到我们之前定义的目录下,然后贴段代码来试看看

怎么样,这样的效果满意不

你可以自己调整颜色搭配,考虑剔除掉某些关键字不着色都可以

Tagged

Creative Commons的意义

晚上与飞龙聊天
解决了几个长久以来含糊纠结的盲点
谈到自己的博客的时候
飞龙建议我自己所写的文章都加上Creative Commons申明
细想一下觉得此举可行

并非限制文章转载修改
只是每次在google搜索一些中文资料的时候
经常看到一整个页面都是重复转载的内容
那是何等的无奈
而且最头痛的是不知道原作者是谁
无法取得直接的联系
更谈不上有些进一步的交流

 

每个文章都留着那么一块链接以及作者说明
虽然有碍美观
却总好过这样的信息丢失以及资源同质化
如果你觉得申明内容太长,大部分都可以删掉,但至少保留原帖链接
只是方便大家共享中文的技术资料

当然你也可以转载我的文章的时候把这些申明信息删掉
文章的版权本来就是很难保护的
我也没有这个意图
写这些都只是为了分享交流而已
很多事情虽然做了未必有效果
但是大家都不做
那就更没希望了
所以我会在自己所写文章的末尾都加上这些申明
欢迎转载除了开头的唠叨话之外的所有完整内容

如果你想知道更多的关于Creative Commons的信息
请挪步CC中文官网
http://cn.creativecommons.org/

魔兽世界插件开发入门教程——2

自己开始写教程才知道有多麻烦
此刻再也不敢乱评价说某些书某些教程有多糟糕
干这个事情连排个版都像体力活
何况最近心里惦记着新加坡公司的面试
不是太有心思搞这个
可能会有挖个大坑出来
但是至少应该会讲完QuestRepeat最初级的版本功能实现。

本节目标

上次我们讲完了开发插件需要的3种文件中的必不可少的toc文件
这次,我们来看下开发操作界面用的XML文件
首先先看下QuestRepeat的界面
看到这个烦人的交布换声望任务了吧

    • 我们要用XML实现任务对话框最底下的那几个按钮

比如“全部完成”,“完成一次”,“取消”还有完成任务的次数的文本输入框等
至于右边的那两个窗口,我是用lua代码动态创建的,我只是尝试了用两种不同的方法来开发界面
在这里,我们只说XML,剩下的,日后再教

    • 这时候有人会问了,WoW原本的任务对话框的不是有个“完成任务”的按钮呢,那个哪里去了?

答案是我们把它隐藏了
因为我们要开发个插件,可以让我们点击一次“全部完成”,之后只需要一直右击NPC,插件就会自动不停的帮我们一直上缴物品完成任务,所以我们不需要那个游戏自带的完成按钮。

    • 可能有人又要问了,那日后如果是跟非重复任务的NPC对话时候怎么办,难道也显示这个重复任务的插件按钮?

所以,我们需要对NPC给的任务是不是重复任务进行判断
是的话,就隐藏默认按钮,然后显示我们的插件
不是重复任务的话,就把我们的插件隐藏,一切照旧

这里是一个只实现界面,不实现任何功能的插件代码,以供你参考温习这2节的内容。
[—-请猛击这里—-]

请解压到你的魔兽世界的Interface/AddOns下面
然后随便找个可以完成任务的NPC对话,看下那几个按钮是否有出现
忽视附加的那两个lua文件,我也几乎清空了所有代码,没有什么功能,这只是个框架
关键是学习第一节的Toc文件的编写,以及怎么用XML文件编写出一个界面出来

学习完, 请把这个测试学习的插件删掉,否则你永远都交不了任务……那就是活生生的杯具啊~

因为我们没有在那些lua文件中实现操作处理
系统不知道怎么响应按钮被按下等动作
你怎么点“全部完成”之类的按钮,也都是无法完成任务的

从零开始

自己编写,请新建一个XML文件,名字随意,只要记得在上一章你写的toc文件中加入即可
XML里的代码内容请参照给的范例

要至少写出以下这些结构了再放进WoW运行查看结果
没写完是看不到什么玩意的

下面我们来讲解下其中的各个标签的用途。

XML文件结构

QuestRepeat.xml代码如下:

<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/\FrameXML\UI.xsd">
	<Script file="localisation.lua"/>
	<Frame name="QuestRepeat" parent="QuestFrameRewardPanel" hidden="false">
		<Scripts>
			<OnLoad>
				QuestRepeat_OnLoad();
			</OnLoad>
			<OnEvent>
				QuestRepeat_OnEvent();
			</OnEvent>
		</Scripts>
		<Frames>
			<Button name="QuestRepeatOnceButton" inherits="UIPanelButtonTemplate" text="COMPLETE_ONCE">
				<Size>
					<AbsDimension x="90" y="22"/><!--x是按钮的宽度,y是高度-->
				</Size>
				<Anchors><!--该框体的对齐设置-->
					<Anchor point="RIGHT" relativeTo="QuestFrameCancelButton" relativePoint="LEFT">
						<Offset>
							<AbsDimension x="0" y="0"/>
						</Offset>
					</Anchor>
				</Anchors>
				<Scripts>
					<OnClick>
						QuestRepeatOnceButton_OnClick();
					</OnClick>
				</Scripts>
			</Button>

			<!--这里还有一堆别的按钮的实现代码,只讲解一个,其他就依葫芦画瓢,自己试着写看看-->

		</Frames>
	</Frame>
</Ui>
<Ui>标签

<ui>是最上级的标签

如果要用XML文件描述界面,这个标签一定要有,其中xmlns是命名空间,后面的xsi那些是用xsd文件来检查XML文档的结构,这几样东西是XML技术中的术语,不是太熟悉的童鞋可以去google温习下

需要指出的是,这个标签你可以直接这么复制粘帖到任意一次插件编写当中,你可以将这一行当约定好的规则使用

我对schema这个讨厌的东西不是太了解,还望有高手能指点下

<Script>标签

<script file=”localization.lua”/>

这个标签在我的插件源代码中是没有的,这里只是为了说明一些主要标签特地这么写而已,它的作用只是表示在此处加载一些lua文件而已

如果记性好的童鞋应该记得toc文件的最后也有文件加载列表,是的,我就是在那里加载的,所以这就意味着你可以不写这个标签,把加载文件的描述放在toc文件那里

<Frame>标签

<Frame>标签就是我们最重要的用来描述你插件界面的东西

    • name属性 —— 是我们插件要创建的图形界面那个窗体的名称
    • parent属性 —— 表示我们的插件要依附关系

这里表示依附在QuestFrameRewardPanel也就是任务说明窗口下,这样任务说明窗口显示跟隐藏的时候,我们的插件也会跟着一起显示跟隐藏

那我们怎么才能知道要继承什么窗体,或者游戏中的那个窗体叫什么名字?
呃……请下载个WinMPQ以及一个BLP图片格式的查看器,自己去搜索如何查看WoW的MPQ文件下的资源以及安装文件夹下的几个MPQ文件的作用,之后按照英文名,意识流般的第六感查找吧……

不要怀疑,我真就是这么干的
哪位大神知道人性化一点的方法,务必务必告知在下……

    • hidden属性 —— 表示我们的插件是默认要显示还是隐藏

true是隐藏,false是显示,日后可以根据不同情况更改状态,并不是一定就是一直显示或隐藏。

<Frame>底下的<Scripts>标签

包含的是插件的界面窗体在游戏系统触发的一些事件下的处理函数声明

    • <OnLoad> —— 描述的是框体只在被WoW成功创建之后触发的事件的处理函数

我们在它下面写下QuestRepeat_OnLoad(),意思是我们日后要用这个函数来响应该事件并进行一些处理。

    • <OnEvent> —— 描述该框体用来处理那些我们注册过要监控的事件的函数

跟上面的OnLoad事件不一样的是,这个是对我们注册过的所有想要监控的事件一起进行分类处理,OnLoad是一个窗体创建的时候一定会触发的一个事件,但是我们往往想要监控更多的东西,比如自己是否脱离战斗,是否可以完成某个任务,所以,一般需要独立写一个函数对这些进行分类处理,小插件的大体结构实现一般是靠这个函数来主导的。

上面说的有点绕,其实翻译的时候handler之类的名词就直接叫handler算了,不知道我这样翻译解释的更多,是否又对新手造成更多的困扰,所以给一个相同的文章给你们一起当参考:CWDG Wiki

我知道看到这里有些没基础的童鞋会不明白什么是事件,为什么要注册,为什么要使用这种机制来控制插件运作

容我在此废话多说几句小解释一下,更多的东西请自己另外google

其实现在的软件,包括操作系统,很多都在使用这种消息响应机制来设计。整个WoW的运作也是如此,比如一个插件加载会出现一个插件加载成功事件,你 跟一个NPC对话,游戏会抛出一个叫GOSSIP_SHOW的对话触发事件。

可是游戏触发了这个事件对我们有什么意义呢,比如我想监控自己的战斗记录,不停的记录我曾经出现的最高伤害数值,首先就要向WoW注册要捕获事件 CHAT_MSG_COMBAT_FACTION_CHANGE,这是战斗记录变化时会触发的事件,然后在去对应写个处理这个事件的函数,这样每次打出新 的伤害的时候,插件就会不停的比较、更新数值记录,这个日后我们写Lua文件的时候会慢慢再教怎么做。

此刻你只需要明白,事件及其响应机制就像你告诉WoW你要监控什么情况,WoW在那些情况发生的时候,会第一时间告诉你:“事情见鬼的来啦,赶紧干活啊!”, 这样你就可以在准确的时间开始干对应的活了

若想知道所有的事件处理函数的名称及其对应的框体,请猛击:WowProgramming-Script Listing

<Frame>底下的<Frames>标签

不要把两者搞混了哦,使用<Frames>可以使我们能在<Frame>下内嵌更多的框架,也就是各种界面资源。

我在<Frames>的下面可以开始写各种我们要建立的界面的东西,上述代码只贴了一个“完成一次”的按钮的实现部分,由于不好单独剥离解释,我直接在源代码中打了注释,直接看注释跟一些属性的英文名基本就知道它的用途。

我就不一行一行的解释代码,很多时候,这种看源代码自学的能力是要培养的。

问题1:我们怎么知道可以创建多少种框体?

请看下表

要创建某种框体,在<Frames>下添加对应的<框体名称>的封闭标签
不知道什么是封闭标签?请回去了解XML基础知识

Button —— 按钮
EditBox —— 可编辑文本框
GameTooltip —— 鼠标提示信息框
ColorSelect —— 颜色选择器
MessageFrame —— 信息框架
Minimap —— 迷你地图,小地图、战场地图等
Model —— 模型,用于显示3D动画,人物模型,冷却都是Model
MovieFrame —— 从来就没见过哪个插件用着这玩意,我不懂
ScrollFrame —— 带滚动条的框架
ScrollingMessageFrame —— 滚动信息框架,例如聊天窗口、战斗信息窗口
SimpleHTML —— 简单超文本标记语言,没用过,不懂
Slider —— 滑块,像系统设置中的UI缩放那个滑快
StatusBar —— 各种进度条

问题2:我们怎么知道框体有多少种可以设置的属性,比如长宽、或者是否可以有显示的文字?

请狠狠的抽这里:WowWiki-XML elements

这个WowWiki整理的非常详细,我第一次觉得它比WowProgramming好用,主要当初我在学校的时候,WowWiki经常被墙……很讨厌在这里找资料。

发现现在它不怎么被墙了,不过,翻墙工具还是要准备个的,例如Tor什么的,翻墙去看看youtube……

如果你觉得这么干做界面太辛苦了,可以给你介绍一个软件,能图形化的设计一个插件的界面
不过我并不爱用,因为我曾经试过,这个开发环境容易崩溃。现在过了这么久,说不定已经改善了
想当白老鼠的,请猛击这里:WoW UI Designer

总结一下

这一节我们讲解了如果使用XML文件来编写自己的插件界面
请和上一节的toc文件放在一起
自己验证看看效果

当你第一次编写完整,在魔兽世界里面看到你的插件界面的时候,你可以切换到桌面,一边修改XML文件,保存它,然后回到WoW,用我们上节曾经提到的

/console reloadui

刷新一下,再跟npc对话就可以查看新的结果。没错,我就是这么修改一次,刷新一次的……我这5年前的老爷机,重新载入一次那是何其漫长而销魂的等待……
就先说这些,那下次见吧~

Tagged , ,