2009年9月7日 | 标签:

============
写在前面的话
============

这个程序是我的操作系统小学期做的文件管理部分,磁盘布局是抄minix的,但是内存数据结构是自己想的(越往下写越感觉自己设计的不对)。从8月24号到熄灯前的那段时间基本是每天晚上2点上床7点起床,其它时间除了吃饭上厕所基本就是在查资料写代码,整个疯狂的程序员。既然抄了GPL的东西,就遵循GPL的规定开源了。

数据结构跟vfs的出入挺大,所以写出来的东西不是很好,大牛们轻拍~

====
简介
====

文件系统用一个文件模拟,默认大小是32M(由ofs.h中的OFS_SIZE定义),默认块大小为4K(由ofs.h中的PAGE_SIZE定义)。程序由ANSI C写成,可以在Windows和Linux上运行。

模拟文件系统总共分六大部分,分别是引导块(MBR),超级块(super block),索引节点位图(inode bitmap),数据块位图(data block bitmap),索引节点表(inode table)和数据区(data area)。各个部分的大致情况如下图所示:

+————-+
| MBR | 0 –> 1k 预留块,未作处理
| |
+————-+
| super | 0 –> 1k 用来记录系统重要信息,如文件系统大小,可引用inode,
| block | 空闲数据块,创建时间等,具体见 struct ofs_super_block。
+————-+
| . |
| . | 0 –> 2k 预设块大小是4k,所以这里没使用
| . |
+————-+
| inode | 2 –> 根据inode table的大小决定,这个块创建顺序是2
| bitmap |
+————-+
| data block | 3 –> 由除了1和2的数据块块数决定,创建顺序是3
| bitmap |
+————-+
| inode | 1 –> 创建顺序为1,inode数量默认为磁盘总数据块数的1/3,所以
| table | 这个块大小是inode的数量*每个inode的大小。
+————-+
| data | 4 –> 剩下的部分全都归数据使用。
| area |
| . |
| . |
| . |
+————-+

整个程序分成3层,分别是应用程序(在cmd.h中定义),系统调用(在syscall.h中定义),文件系统接口(在ofs.h中定义):

—————-
应用程序
—————-
系统调用
—————-
虚拟文件系统(缺)
—————-
具体文件系统
—————-
…………….

在文件系统部分中又分为3部分:虚拟文件系统(vfs),内存组织方式和磁盘组织方式。内存组织方式是磁盘组织方式的延伸。本来想加上vfs的,但是由于时间关系没实现。没有vfs的一个直接后果就是有些操作本来是由vfs完成的,但是现在却要放到系统调
用层,或者放到与具体文件系统相关的地方去实现,这样就为以后的扩展带来了问题。对一些参数的合法性判断不知道该放到什么地方,有些数据结构是为了符合vfs的接口而凑出来的(例如struct file),有些函数的实现形式很奇怪,因为毕竟是模拟文件系统,没有操作系统的支持,有些操作很像真正的调用那样实现。

可用的命令:cd ls create ln mkdir rmdir mv truncate pwd read write rm fsinfo help

========
数据结构
========

——————-
超级块(super block)
——————-

在 ofs.h 中定义:

/* super block的磁盘数据,大小为 128 字节 */

struct ofs_super_block {
__u16 s_magic; /* 文件系统签名,用于决定文件系统类型 */
__u16 s_inode_size; /* inode 大小,单位是字节(Byte) */
__u16 s_block_size; /* 数据块大小,单位字节(Byte) */
__u8 s_mount_nr; /* 被挂载的次数 */
__u8 s_version; /* 文件系统版本 */
__u64 s_size; /* 文件系统所在分区大小 */
__u64 s_mkfs_time; /* 创建时间 */
__u64 s_mount_time; /* 最后一次挂载的时间 */
__u64 s_mtime; /* 最后一次修改的时间 */
__u64 s_itable_offset; /* inode 表距离分区起始位置的偏移量 */
__u64 s_data_offset; /* 数据区离分区起始位置的偏移量 */
__u64 s_inodes; /* inode 的数量 */
__u64 s_blocks; /* 数据块的数量 */
__u16 s_imap_blocks; /* inode bitmap 所占的块数 */
__u16 s_bmap_blocks; /* data bitmap 所占的块数 */
__u64 s_free_inodes; /* 可用的 inode 数量 */
__u64 s_free_blocks; /* 可用的数据块数 */
__u8 s_uuid[16]; /* 唯一标识 */
char s_name[16]; /* 分区名称 */
__u32 s_padding; /* 填充 */
};

这里对基本的数据结构进行了typedef,主要是为了对齐的时候方便一点:

typedef unsigned char __u8;
typedef unsigned short int __u16;
typedef unsigned int __u32;
typedef unsigned long long int __u64;

typedef signed char __s8;
typedef signed short int __s16;
typedef signed int __s32;
typedef signed long long int __s64;

因为是模拟,所以就不分大小端了。

—————
索引节点(inode)
—————

在 ofs.h 中定义:

/* 根节点号 */
#define OFS_ROOT_INO 1

/* 直接索引块数量 */
#define OFS_DIR_BLOCKS 9

/* 间接索引块位置 */
#define OFS_IDX_BLOCK OFS_DIR_BLOCKS

/* inode中的索引数 */
#define OFS_BLOCKS_NR (OFS_IDX_BLOCK+1)

/* inode的磁盘数据结构,每个inode的大小是128 字节 */

struct ofs_inode {
__u16 i_version; /* inode 的版本号 */
__u16 i_mode; /* 文件类型和访问权限 */
__u32 i_uid; /* 所有者 id */
__u32 i_gid; /* 组 id */
__u32 i_links; /* 引用数 */
__u64 i_size; /* 文件大小,单位是字节 */
__u64 i_atime; /* 最后访问时间 */
__u64 i_mtime; /* 最后修改时间 */
__u64 i_ctime; /* 创建时间 */
__u64 i_block[OFS_BLOCKS_NR]; /* 数据保存的位置,最后一块是间接块,
其他的都是直接块,按照块大小4k来
算,最大的文件是2M+36K */
};

/* inode的内存数据结构 */

struct inode {
__u64 i_ino; /* inode number */
unsigned int i_dents; /* 如果该inode是目录就记录该目录下的文件数目,
这个是为了创建文件的时候防止超过目录大小限制
而增加的 */
unsigned char i_flags; /* 是否需要写回磁盘(dirty) */
struct ofs_inode *i_ip; /* 磁盘上的inode数据结构 */
struct dentry *i_sub_de; /* 子目录项,是一个循环双链表,如果inode是普通
文件这个就为空。在vfs中这个功能好像是属于
dentry的,后来的实现证明这样组织有很大问题,
这个是造成struct file严重变形的元凶之一。 */
};

————–
目录项(dentry)
————–

在 ofs.h 中定义:

/* dentry的磁盘数据结构,每个dentry的大小是 128 字节 */

#define OFS_MAX_NAME_LEN 119

struct ofs_dentry {
__u64 d_ino; /* inode number */
char d_name[OFS_MAX_NAME_LEN+1]; /* 目录项名字 */
};

/* dentry的内存数据结构 */

struct dentry {
__u64 d_ino; /* 对应的inode号 */
/* 因为读到内存后无法得知目录项在磁盘的位置,所以增设了这个记录位置
的项 */
struct {
unsigned int no; /* 在目录文件内的哪个数据块 */
unsigned int off; /* 在数据块内的偏移量 */
} d_pos;
unsigned char d_flags; /* 是否需要写回磁盘(dirty) */
struct inode *d_inode; /* 相关的inode */
struct inode *d_parent; /* 父目录inode */
struct dentry *d_prev,*d_next; /* 同一父目录下的目录项,一个
循环双链表,头结点由父目录
inode的i_sub_de指向 */
char d_name[OFS_MAX_NAME_LEN+1]; /* 名字 */
};

被删除了的dentry在磁盘中的inode number为0,并且这个块不会出现在内存中。当需要在目录新建文件时可以重新使用这些项。
至于查找方法在后面说明。

———————
文件相关(struct file)
———————

真不想把这个写出来。这段介绍的作用也跟这个结构体在这个程序的作用一样,只是为了完整性……

在ofs.h中定义:

/* 伪 “struct file” */

struct file {
struct dentry *f_dentry; /* 与文件相关的目录项 */
void *f_ptr; /* 遍历目录时使用的指针,主要供readdir()使用。
都是数据结构设计得不好才有这个东东 */
__u64 f_pos; /* 如果是普通文件,就表示偏移量;如果是目录……
不想说了,看ofs_readdir()的实现吧,我面壁
去了…… */
};

————
位图(bitmap)
————

就是一长串01。只有3个操作:(1)查找第一个不为0的位置;(2)把该位置为1;(3)把该位清零。

查找的方法很土,每次按一个字节处理。如果小于255就说明在这8位中有0,再用一个for循环找到不为零的位置然后返回偏移量。置1和清零的方法就是根据给出的偏移量直接跳到该偏移量所在字节的前一个字节,然后移位处理即可。

格式化文件系统时并没有对inode table和数据区进行填充操作,只是把inode bitmap和data block bitmap都清零。分配inode或
者数据块的时候从左往右找到第一个不为0的位,然后置1,返回偏移量,就是inode number或者block number。

——–
全局变量
——–

因为不想全局变量满天飞,所以将各种缓冲区都放在struct fs_info中。但是还是有漏网之鱼,就是指向当前目录的inode指针,
这也是这个程序之中唯一一个真正意义上的全局变量。

在ofs.h中定义:

struct fs_info {
FILE *dev; /* 指向打开的用来模拟这个文件系统的文件指针
名字起得还不错~ */
struct ofs_super_block *sb; /* 超级块 */
unsigned char *imap; /* inode bitmap */
unsigned char *bmap; /* data block bitmap */
struct inode *root; /* 根节点 */
struct rbtree icache; /* 读入内存的根结点都放在这棵红黑树中 */
struct dentry *dcache; /* 没使用的dentry项的一个单链表,优点
是实现简单,不过很后悔没有用块缓冲 */
/* int sync_ival; */ /* 定时更新,没实现 */
};

这些缓冲区由ofs_mount()初始化,由ofs_umount()最后更新到磁盘并释放空间。

============
内存组织方式
============

ofs_mount()函数初始化struct fs_info的各个项,并且把根结点放到红黑树中。当需要查找目录时先调用ofs_read_entry()从磁
盘上读取所有的目录项并且按顺序链接成一个双链表。注意一定要按顺序,这个关系到磁盘上被删除的块可以重用并且更新的时候可以把同一个块需要更新的目录项都写到缓冲区中再写回磁盘。

磁盘上的inode出现在内存中有两种途径:新建一个文件时由ofs_alloc_inode()创建一个新的inode并插到红黑树中,还有使用ofs_lookup()查找到某个目录项后把该目录项的相关inode读到内存,这个也插入到红黑树中。这样inode的管理就很清楚了,就是创建和删除都要经过红黑树,查找的结果也是指向红黑树中的inode。这个好处就是保证了inode的信息是实时的,最后umount只要在删除树的时候释放树的每个结点就可以了。

目录项出现在内存中也有两种方式:新建一个目录项时由ofs_add_entry()插入到相应inode的以i_sub_de为头结点的循环链表中,或者由ofs_read_entry()读到相应inode的i_sub_de中。这样目录项的更新和释放就由它的父目录inode决定了:当一个目录项存在时,它的父目录inode必存在;但反之不一定,这个好像正好跟内核相反。当删除红黑树里的某个inode的时候要把该inode的
子目录都释放掉。这样,当一个目录项存在时,它的d_inode和d_parent都不会为空。

当要往某个目录下增加一个文件时,先根据块号查询。如果在同一块内并且两者的偏移量相差大于sizeof(struct ofs_dentry),
就说明这两个目录项之间至少有一个可用的目录项(前面已经说过,被删除的目录项不会出现在内存中);如果在不同的块内,并且另一块的起始偏移量大于0,就说明在另一块的起始位置至少有一个可用的目录项;如果另一块的起始偏移量为0但是前一块的偏移量小于block_size-sizeof(struct ofs_dentry),就说明在前一块的末尾至少有一个可用的目录项;还有其它的情况就不详细说了,参考ofs_add_entry()。这样做虽然可以重复利用目录项,但是每一次都得查找,特别是像这个程序中的目录项组织方式
,当一个目录下有很多文件时效率将会非常低。

删除一个文件时,要把磁盘里的目录项inode number清零以便以后使用,并且把被删除的目录项从其父目录的子目录双链表中移到全局变量struct fs_info中的dcache链表中。以后要创建目录项时先到这个链表中看有没有可用的项,没有的话再申请。

==========
接口和功能
==========

已经实现了的接口和功能如下(接口并不是都和vfs相应的函数功能或形式相同):

/* 根据名字找到对应的inode */
int vfs_namei(struct fs_info *fsi,char *path,struct dentry *de);

/* super operations */

/* mount: 初始化fs_info结构 */
struct fs_info* ofs_mount(const char*);

/* umount: 更新磁盘文件信息并且释放内存 */
void ofs_umount(struct fs_info*);

/* 将超级块内容写回磁盘 */
void ofs_write_super(struct fs_info *fsi);

/* 分配一个新的inode并且初始化 */
struct inode* ofs_alloc_inode(struct fs_info *fsi);

/* 释放内存中的inode */
void ofs_destroy_inode(struct fs_info *fsi,struct inode *inode);

/* 从磁盘中删除给定的inode */
void ofs_delete_inode(struct fs_info *fsi,struct inode *inode);

/* 把磁盘上inode的内容读到内存中 */
void ofs_read_inode(struct fs_info *fsi,struct inode *inode);

/* 把inode的内容写回磁盘 */
void ofs_write_inode(struct fs_info *fsi,struct inode *inode,int wait);

/* inode operations */

/* 在dir中查找给定名字的dentry,如果找到就返回,找不到返回NULL */
struct dentry* ofs_lookup(struct fs_info *fsi,struct inode *dir,
struct dentry *de);

/* 在dir中创建一个新文件,名字由de给定 */
int ofs_create(struct fs_info *fsi,struct inode *dir,
struct dentry *de,int mode);

/* 在dir中创建一个指向old_de的inode的 硬 链接 */
int ofs_link(struct fs_info *fsi,struct dentry *old_de,
struct inode *dir,struct dentry *de);

/* 在dir中删除一个名字由de指定的目录项 */
int ofs_unlink(struct fs_info *fsi,struct inode *dir,
struct dentry *de);

/* 把文件大小变为newsize。如果newsize大于原来的文件,扩充部分填充0 */
int ofs_truncate(struct fs_info *fsi,struct inode *inode,
unsigned long long newsize);

/* 在dir中创建一个名字由de指定的目录 */
int ofs_mkdir(struct fs_info *fsi,struct inode *dir,
struct dentry *de,int mode);

/* 在dir中删除一个名字由de指定的 空 目录 */
int ofs_rmdir(struct fs_info *fsi,struct inode *dir,
struct dentry *de);

/* 将old_dir下的old_de移到new_dir下,并且新名字为new_de */
int ofs_rename(struct fs_info *fsi,
struct inode *old_dir,struct dentry *old_de,
struct inode *new_dir,struct dentry *new_de);

/* file operations */

/* 从文件filp偏移为offset的位置开始读取count字节到buf中 */
unsigned long long ofs_read(struct fs_info *fsi,struct file *filp,
char *buf,unsigned long long count,unsigned long long *offset);

/* 从filp的偏移为offset的位置开始写入buf中的count字节 */
unsigned long long ofs_write(struct fs_info *fsi,struct file *filp,
char *buf,unsigned long long count,unsigned long long *offset);

typedef int (*filldir_t)(void *dirent,const char *name,int namelen,
__u64 offset,__u64 ino,unsigned dentry_type);

/* 每次读取一个目录到dirent中,使用filldir_t的格式填充 */
int ofs_readdir(struct fs_info *fsi,struct file *filp,void *dirent,
filldir_t filldir);

更多接口见ofs.h。应用程序和系统调用的内容分别见cmd.*和syscall.*。

2009年7月31日 | 标签:

F 我的图像为什么是颠倒的

A IplImage结构中有一个参数是用来设置在调用cvImageShow时图像的显示的,有两种模式:顶左结构和底座结构.指定IplImage->origin的值来设置显示方式.0为顶左结构,1为底左结构.

F 图像的灰度和色彩问题.

A opencv中IplImage支持单通道的灰度图象和3通道(4通道)的彩色图像等.图像的深度为每一个像素值的存储位数.如普通的单通道IPL_DEPTH_8U灰度图就是图像由单通道的矩阵表示,每一个点用8位共256个像素值来表示.彩色图像一般用3通道(或者4通道)来表示,存储方式有两种:用打他Order来设置,0为交叉存储方式,对于三通道的RGB图像为BGR BGR…,对于4通道的RGBA图像来说为BGRA BGRA…;1为分开的颜色通道,对于三通道的RGB图像来说,为RRR…R GGG…G BBB…B,对于4通道的来说为RRR…R GGG…G BBB… B AAA… A.其中A为alpha通道,一般是用来表示图像的透明度的。

未完待续

2009年7月31日 | 标签:

计算机:Thinkpad T61

操作系统:windowsXP

使用vs2005 Team suite

下载gpucv from http://www-public.intedu.eu/~allusse/gpucv/release//0.4/0.4.1.rev.175/GPUCV_v0.4.1_rev175_bin_win32.exe

下载svn from http://subversion.tigris.org/files/documents/15/35379/svn-1.4.2-setup.exesugoitools,也可使用别的方法直接下载。)

下载glew from http://jaist.dl.sourceforge.net/sourceforge/glew/glew-1.5.0-win32.zip

下载glut from http://www.opengl.org/resources/libraries/glut/glut37.zip

下载premake from http://nchc.dl.sourceforge.net/sourceforge/premake/premake-win32-3.6.zip

下载dirent.h和dirent.lib from http://softagalleria.net/download/dirent/dirent-1.8.zip and http://opensvn.csie.org/Pnuts/1.2/launcher/win32/dirent/lib/dirent.lib

把dirent.h 和dirent.lib拷贝到vs2005的include和lib中使用svn获得SugoiTools(在命令行里)

svn co https://sugoitools.svn.sourceforge.net/svnroot/sugoitools sugoitools

运行sugoitools中的CreateSugoiProjects.bat和make.VS2005.bat

可获得lib文件夹现在开始添加lib和include…N多…

首先是gpucv的 /lib /include

注意:删除/bin下的有关opencv,opengl,SugoiTools的dll文件,容易和系统中的这些文件混淆,没有安装并且不需要用opencv的话就不用管了,其余两个只是project中所要用到的。

把glew和glut中的include和lib \VC\PlatformSDK\include和 \VC\PlatformSDK\lib

添加sugoitools的include和lib \include \lib\vs2005

并将gpucv,sugoitools的bin目录加到系统的path下面(opencv,opengl的我已经加入,若是没有加如的话请自行添加)(需重启)

安装过程完毕。

测试:打开\projects\vs2005中的gpuCV-vs2005.sln

更改以下三句: SG_Assert(GpuImageManager()->Remove(src1), “Could not find corresponding matrix”); SG_Assert(GpuImageManager()->Remove(src2), “Could not find corresponding matrix”); if(src3) SG_Assert(GpuImageManager()->Remove(src3), “Could not find corresponding matrix”); 为 SG_Assert(GpuImageManager()->Delete(src1), “Could not find corresponding matrix”); SG_Assert(GpuImageManager()->Delete(src2), “Could not find corresponding matrix”); if(src3) SG_Assert(GpuImageManager()->Delete(src3), “Could not find corresponding matrix”);

使用批编译,成功后将得到

GPUCVConsoled.exe

GPUCVCamDemo.exe

GPUCVCamDemod.exe

GPUCVConsole.exe 和

gpucv的dlls,libs.

OK 运行(CamDemo需摄像头),看见画面即OK vs6的安装方法类似。

2009年7月31日 | 标签:

OpenCv版本:1.0
Os:window XP
Vs2005版本:Visual Studio 2005 Team Suite(RTM.050727-4200)
cvaux.h中(cvaux/include/external/cvaux.h)第1137行的?/改为*/
在工具->选项->项目于解决方案->vc++目录->包含文件,中加入
\cxcore\include
\cv\include
\cvaux\include
\ml\include
\otherlibs\highgui\include
\otherlibs\cvcam\include
在工具->选项->项目于解决方案->vc++目录->库文件,中加入
\OPENCV\LIB 点完成即可
使用批生成,选择win32的cv,cvaux,cxcore,highgui,ml的debug和release版本。进行编译。
若遇见问题,请参照如下方法解决,有时会出现有时不会… cxcore.lib cv.lib highgui.lib ml.lib cvaux.lib等等文件无法找到,一种可能是没有配置好lib文件,一种可能是没有这些文件。
请参照上面的方法进行配置,这些文件都在opencv\lib中 找不到streams.h,编译highgui或是cxcore时ms出现这个问题,原因是没有directshow sdk支持 opencv中视频输入输出很大程度上是依靠directshow.
安装directshow sdk吧,包含在directx sdk中, directx 9.0b之前的版本都包含directshow,但是9.0c之后将directshow单独拿出发行.可以去微软的网站搜索,本次测试使用的是directx 9.0b 安装directx 9.0b
在工具->选项->项目于解决方案->vc++目录->包含文件,中加入
\samples\Multimedia\DirectShow\BaseClasses
\samples\Multimedia\Common\include
\include
在工具->选项->项目于解决方案->vc++目录->库文件,中加入
\lib
C:\Program Files\DXSDK\Samples\C++\DirectShow\BaseClasses 可能出现的问题:
(1)若出现:语法错误 : 缺少“;”(在标识符“PVOID64”的前面) directx中的包含文件和原有的winnt.h有冲突造成的,将\include移动到最后一位即可.
(2)或者在其前面加上#define POINTER_64 __ptr64 若出现:operator=(LONG)的错误,适用于vs2005要求严格的语法,词句没有明确的返回值,在vs6中是可以的,但vs2005不支持默认的返回值. 将其改为:LONG operator=(LONG)
(3)还会出现很多的变量没有定义,如Count,idone没有定义等,
此类主要是vs2005默认使用强制for循环,
在项目->属性->配置属性->C/C++->语言中将强制For循环中的一致性后面的”是”改为否即可. Ok,
到此为止,我的Baseclasses顺利编译通过了,产生了两个文件,strmbasd.lib和STRMBASE.lib,将这两个文件考到\lib中,再次批生成opencv即可. 在opencv的编译过程中可能遇到的一些问题是由于文件和系统本身自带的文件冲突造成的,可以将opencv的包含文件设置到顶部。

2009年7月30日 | 标签:

如何将论文挂在网上呢?转成html吧!
我安装的是CTex2.7.0,更高版本的应该也可以。
配置起来很简单
1,将\[installpath]\MiKTeX\scripts\tex4ht\bat\htlatex.bat拷贝到\[installpath]\WinEdt\Bin\TeX\目录下并更名为TtH.bat(将原有的TtH.bat备份即可)。
2,下载并安装ImageMagick。
OK,重启一下就可以了。
打开WinEdit,点击Accessories->HTML->Tth(TeX->HTML)就可以了。
有个IE的图标键,打开就可以看到Html版本的文章了,有些图还不是很好,自己手动调一下。

2009年7月30日 | 标签:

对所见即所得的word和编译的tex的一种整合,感觉做的还不到位。Lyx和latex兼容性还很差。

2009年7月28日 | 标签:

最近一直在写文章,好不容易赶完了,发现Opencv论坛很久都没去。在这里把最近这几个月来使用Opencv的感想和一些Opencv文档之外的东西分享给大家,希望有用。

首先还是说说如何学Opencv吧,记得最开始进实验室的时候师兄让看Opencv,当时的学术基础很差,编程能力也很一般,所以学Opencv时感到很无助,像是陷入了一个死循环。想通过算法看代码,发现算法不会;通过代码学算法,又发现自己读代码的能力很弱。后来通过不断的读文章(下面会推荐几篇文章供算法入门)和加强实际的编程能力,其实就是通过Opencv把一些经典的算法实现,来学习。慢慢的就会发现,Opencv和matlab一样好用。不过,Opencv是C/C++混合+模板编程+N多指针,看代码的确很费劲(很多模板类的代码和函数指针的调用函数我到现在还是没怎么看明白)。慢慢来就好,读代码读的多了,会有三个好处:1,更深入和实际的了解算法,深入的了解算法是指通过看代码可以获得算法在实际实践的时候很多细节方面的工作是如何做的,实际是指算法本身在实现的时候会有一些小的窍门,有的算法的实践并不是完完全全将算法翻译过来的,编程实现的时候有实践的一些小窍门。2,Opencv中有很多没有被列到文档里的很实用的代码(很多函数都是_icv开头是,其实很值得一看)。3,发现Opencv的bug,及时的修正保证自己程序的健壮。我的Opencv和刚安装的已经有很大的区别了,就是偶尔发现某处的一些问题,然后修改了,再直接编译。不过我没有注意存记录,弄的现在都不知道什么地方改过。
首先先推荐几篇供大家学习Opencv的文章吧,这些文章是一些经典的算法,它们在Opencv上已经有了具体的实践。这些算法原理比较简单,可以用来作为学习Opencv的算法(通过算法学代码)。这里所说的读代码是要深入函数的内部,了解代码运行的步骤,而不仅仅是会用个别函数:
1,Adaptive background mixture models for real-time tracking —> 对应Opencv代码:CvGaussBGModel等。
2,Computer vision face tracking as a component of a perceptual user interface —> cvCamShift等。
3,EM cluster —>CvEM 这个算法很简单,网上到处都有算法介绍。
4,计算机视觉那本书 —> cvFindHomography, 根据多对点计算图像的投影变换矩阵。计算机标定和三维重建经常用到。
5,An Introduction To the Kalman Filter —> CvKalman等
6,SIFT —> Opencv算法版置顶里Rob Hess的SIFT代码,很值得一看,个人感觉非常好的SIFT代码。
先写这么多,把这些算法的Opencv实现完全看懂了Opencv大体上就没问题了。

下面介绍一个改动Opencv代码实现更有用的功能的方法


图像的直方图均匀化在做光照均衡时十分有用,但实际上直方图规定话其实更有用,Opencv里面只提供了直方图均匀化的代码cvEqualizeHist
在这个函数的基础上稍加改动,就能得到直方图规定化的代码:

?View Code LANGUAGE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void cvMatchHist( const CvArr* src, CvArr* dst, CvHistogram * histdst )
{
   CvHistogram* hist = 0;
   CvMat* lut = 0, *lutG = 0, *lutZ = 0;
   CvHistogram * histeq = NULL;
 
   CV_FUNCNAME( "cvMatchHist" );
 
   __BEGIN__;
 
   cvCopyHist( histdst, &histeq );
   cvNormalizeHist(histeq, 1.0);
 
   int i, hist_sz = 256;
   CvSize img_sz;
   float scale;
   float* h, *hG;
   int sum = 0;
   int type;
 
   CV_CALL( type = cvGetElemType( src ));
   if( type != CV_8UC1 )
      CV_ERROR( CV_StsUnsupportedFormat, "Only 8uC1 images are supported" );
 
   CV_CALL( hist = cvCreateHist( 1, &hist_sz, CV_HIST_ARRAY ));
   CV_CALL( lut = cvCreateMat( 1, 256, CV_8UC1 ));
   CV_CALL( lutG = cvCreateMat( 1, hist_sz, CV_8UC1 ));
   CV_CALL( lutZ = cvCreateMat( 1, hist_sz, CV_8UC1 ));
   CV_CALL( cvCalcArrHist( (CvArr**)&src, hist ));
   CV_CALL( img_sz = cvGetSize( src ));
   scale = 255.f/(img_sz.width*img_sz.height);
   h = (float*)cvPtr1D( hist->bins, 0 );
   hG = (float*)cvPtr1D( histeq->bins, 0 );
   for(i = 0; i < 256; i++)
   {
      sum += cvRound(hG[i]*255);
      lutG->data.ptr[i] = (uchar)cvRound(sum);
   }
   sum = 0;
   for( i = 0; i < hist_sz; i++ )
   {
      sum += cvRound(h[i]);
      lut->data.ptr[i] = (uchar)cvRound(sum*scale);
   }
   for (i = 0; i < 256; i++)
   {
      int k = 0;
      while(lutG-&gt;data.ptr[k] &lt; i || k < 256)
      {
         k++;
      }
      lutZ-&gt;data.ptr[i] = k;
   }
   lut-&gt;data.ptr[0] = 0;
   CV_CALL( cvLUT( src, dst, lut ));
   CV_CALL( cvLUT( dst, dst, lutZ ));
 
   __END__;
 
   cvReleaseHist(&amp;hist);
   cvReleaseMat(&amp;lut);
   cvReleaseMat(&amp;lutG);
   cvReleaseMat(&amp;lutZ);
   cvReleaseHist(&amp;histeq);
}

一个bug:
CvBoost里的一个内存泄漏问题:
CvBoost在clear()函数中有一个成员变量没有被释放:
将clear函数改为:

?View Code LANGUAGE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    if( weak )
    {
        prune( CV_WHOLE_SEQ );
        cvReleaseMemStorage( &amp;weak-&gt;storage );
    }
    if( data )
        delete data;
    weak = 0;
    data = 0;
    cvReleaseMat( &amp;orig_response );
    cvReleaseMat( &amp;sum_response );
    cvReleaseMat( &amp;weak_eval );
    cvReleaseMat( &amp;subsample_mask );
    cvReleaseMat( &amp;weights );
   cvReleaseMat(&amp;subtree_weights);
    have_subsample = false;

2009年7月28日 | 标签:

This is my new homepage, welcome!