人生苦短,我用 Genie
这篇文章已经酝酿很久了,不过一直没时间没心情写。今天到学校开始学日语了,又看到 WowUbuntu 介绍的日语 50 音图测试软件 Kanatest 在 Arch 的 AUR 里面找不到,所以干脆自己写一个,顺便补上这篇文章。
进入正题,开发一个 Linux 下的软件,你选择的编程语言是什么呢?我想大多数人会选择 C 或者 Python 吧。像上面说的 50 音图测试软件,用 C 开发有些大材小用了,用 Python 呢,要是涉及运算的时候速度又不尽人意。
那么用什么呢?Google 的 Go 语言?库还不完善呢。Vala?有些人会想到这个 GNOME 的项目了,确实,Vala 对于熟悉 C# 的朋友可能十分顺手,可是对于更加喜欢 Python 的朋友,或许就不是那么友好了。
好的,我们的主角登场了,Genie!
既然你知道 Vala,或许你也知道,它只是对 C 语言的一个包装,运行效率与 C 相差无几。Genie 语言就是 Vala 的同胞弟弟,它也是对 C 语言的包装和翻译,能够实现 Vala 的所有特性,甚至“编译器”都是相同的——valac!唯一不同的是,Genie 是以 Boo、Python 等脚本语言为蓝本,语法上更加灵活、简洁,如果你学过 Python,Genie 的入门将十分之快!
来看看我写的这个简单的 50 音图背诵软件吧,功能很简单,就是将日语的平假名随机闪现,相信有 GTK+ 基础的朋友很容易就可以看懂。
/* Build with valac --pkg gtk+-2.0 mykana.gs */
[indent=4]
uses
Gtk
init
Gtk.init(ref args)
var window = new MainWindow()
window.show_all()
Gtk.main()
class MainWindow: Window
label: Label
const chars : array of string = {"あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ", "さ", "し", "す", "せ", "そ", "た", "ち", "ち", "て", "と", "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み", "む", "め", "も", "や", "う", "よ", "ら", "り", "る", "れ", "ろ", "わ", "を", "ん"}
init
title = "My Kana"
window_position = WindowPosition.CENTER
default_width = 250
default_height = 250
destroy.connect(Gtk.main_quit)
label = new Label(null)
add(label)
Timeout.add_seconds(1, this.change_char)
def change_char(): bool
var i = Random.int_range(0, 46)
var output = Markup.printf_escaped("%s", chars[i])
label.set_markup(output)
return true
可以看出,除了类型声明之外,从某种程度上讲,Genie 的语法甚至比 Python 更加简单。而且,通过使用 libgee,Genie 甚至可以使用等同于 Python 中的 list、dict 等类型,功能十分强大。Genie 与 Vala 共用 API,所以库的问题也不用担心。
不过,Genie 创造于 2008 年,年纪还很轻,文档还不够全。就如同 Vala 一样,Genie 的语法也在发展之中。如果你觉得这些都不是问题,那么赶快看看文档学习一下吧!
我觉得很不错的一些文档:官方指南、来自 Puppy Linux 的指南、官方代码示例。
关于 C++ 中模板类(Template Class)在多文件中的问题
最近 C++ 学到模板类了,老师要做一个模板类的题目。
一直以来我们都是保持着类声明和类实现分别写在头文件和 CPP 文件中这个不错的习惯,这次也没有例外,然而这次问题却出来了。
先是在 G++ 中提示:
undefined reference to `Array::Array(int, int const*)'
移到 VC++ 6.0 后又提示:
ex_08_1.obj : error LNK2001: unresolved external symbol "public: __thiscall Array<int>::~Array<int>(void)" (??1?$Array@H@@QAE@XZ)ex_08_1.obj : error LNK2001: unresolved external symbol "public: __thiscall Array<int>::Array<int>(int,int const *)" (??0?$Array@H@@QAE@HPBH@Z)
到底是怎么回事呢?老师说是编译器的 BUG,我却觉得没那么简单。 汗一个……VC++ 上更不用说了……
搜索了一下,才发现,C++ 中模板类(或者叫“类模板”)的声明和实现和普通类是不同的,模板类的声明有两种模式:包含编译模式和分离编译模式。
包含编译模式就是把声明和实现写在同一个文件中;分离编译模式当然就是写在不同文件中,不过用这种形式的时候,要在类的声明前加上 export 关键字。
我在 G++ 上试了一下,出现:
警告:关键字‘export’未实现,将被忽略
C++ 真是个复杂的东西,还是写在一个文件里吧……
数字图像处理(四)——均值滤波
上次提到产生高斯噪声的算法,我们知道,椒盐噪声是可以通过中值滤波来修复的,高斯噪声也可以通过均值滤波来还原。
顾名思义,均值滤波也是构建一个 n*n 的模板,然后取其平均值来替代模板中间的值。这样做出的效果明显不如用中值滤波修复椒盐噪声的效果好,不过当 n 取很大值的时候,图片看起来像是高斯模糊,不知道是不是一个原理……
看效果,n=3 时:
n=11 时:
算法更加简单,有以前的基础不成问题:
BmpPixmap &
BmpPixmap::mean_filter (int n)
{
assert (n >= 3 && n % 2);
int ii, jj, nn, sum [3];
BmpPixmap *temp = new BmpPixmap (*this);
for (i = n / 2; i < height - n / 2; i++) {
for (j = n / 2; j < width - n / 2; j++) {
for (nn = 0; nn < 3; nn++) {
sum [nn] = 0;
}
/*-----------------------------------------------------------------------------
* Calculate the average of the model.
*-----------------------------------------------------------------------------*/
for (ii = i - n / 2, nn = 0; ii <= i + n / 2; ii++) {
for (jj = j - n / 2; jj <= j + n / 2; jj++, nn++) {
sum [0] += pdata [ii][jj]->get_blue ();
sum [1] += pdata [ii][jj]->get_green ();
sum [2] += pdata [ii][jj]->get_red ();
}
}
temp->pdata [i][j]->set (sum [0] / (n * n), sum [1] / (n * n), sum [2] / (n * n));
}
}
return *temp;
} /* ----- end of method BmpPixmap::mean_filter ----- */
数字图像处理(三)——高斯噪声
与椒盐噪声相似,高斯噪声(gauss noise)也是数字图像的一个常见噪声,产生该噪声的算法也很简单。
上次说过,椒盐噪声是出现在随机位置、噪点深度基本固定的噪声,高斯噪声与其相反,是几乎每个点上都出现噪声、噪点深度随机的噪声。
该噪声效果如下:
这个算法比较简单,需要注意,颜色值不要超出范围(0-255),不然效果很可怕……
代码如下:
BmpPixmap &
BmpPixmap::gauss (int level)
{
assert (level >= 0);
BmpPixmap *temp = new BmpPixmap (*this);
int k, rand_temp, pixel [3];
/*-----------------------------------------------------------------------------
* Init the random seed with time.
*-----------------------------------------------------------------------------*/
srand (time (NULL));
/*-----------------------------------------------------------------------------
* Salt & Pepper.
*-----------------------------------------------------------------------------*/
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
rand_temp = rand () % level - level / 2;
pixel [0] = temp->pdata [i][j]->get_blue () + rand_temp;
pixel [1] = temp->pdata [i][j]->get_green () + rand_temp;
pixel [2] = temp->pdata [i][j]->get_red () + rand_temp;
for (k = 0; k < 3; k++) {
if (pixel [k] < 0) {
pixel [k] = 0;
} else if (pixel [k] > 255) {
pixel [k] = 255;
}
}
temp->pdata [i][j]->set (pixel [0], pixel [1], pixel [2]);
}
}
return *temp;
} /* ----- end of method BmpPixmap::gauss ----- */
数字图像处理(二)——中值滤波
中值滤波(median filter)是一种有效消除椒盐噪声的算法。
基本原理是,对图像中所有点进行遍历,对于每个点,取以其为中心的 n * n 的矩形区域,对矩形区域中点的灰度值进行排序,取其中间值替换当前点。
这样,如果这个点灰度值与周围点相差较大的话,就可以将其平滑化。
当然,这里要求 n 是个大于 1 的奇数。
在这里,我对算法进行了优化,使只有当前点是矩形区域灰度值的最大或者最小值的时候才进行替换,这样效果好了很多。
原始图像:
椒盐噪声:
中值滤波后:
可以看到,图像损失很小,只有边缘处有小的细节损失。同时,由于所加的噪点比较密集,少数地方仍然有噪点,这是因为矩形区域噪点太多,以至于中值本身也是个噪点了。
源代码:
BmpPixmap & BmpPixmap::median_filter (int n) { assert (n >= 3 && n % 2); int ii, jj, nn; Byte model [3][n * n]; BmpPixmap *temp = new BmpPixmap (*this); for (i = n / 2; i < height - n / 2; i++) { for (j = n / 2; j < width - n / 2; j++) { /*----------------------------------------------------------------------------- * Put n*n pixels around current pixels to the model. *-----------------------------------------------------------------------------*/ for (ii = i - n / 2, nn = 0; ii <= i + n / 2; ii++) { for (jj = j - n / 2; jj <= j + n / 2; jj++, nn++) { model [0][nn] = pdata [ii][jj]->get_blue (); model [1][nn] = pdata [ii][jj]->get_green (); model [2][nn] = pdata [ii][jj]->get_red (); } } /*----------------------------------------------------------------------------- * Sort the model. *-----------------------------------------------------------------------------*/ qsort (model [0], nn, sizeof (Byte), cmp_Byte); qsort (model [1], nn, sizeof (Byte), cmp_Byte); qsort (model [2], nn, sizeof (Byte), cmp_Byte); if (pdata [i][j]->get_blue () == model [0][nn - 1] || pdata [i][j]->get_blue () == model [0][0] || pdata [i][j]->get_green () == model [1][nn - 1] || pdata [i][j]->get_green () == model [1][0] || pdata [i][j]->get_red () == model [2][nn - 1] || pdata [i][j]->get_red () == model [2][0]) { temp->pdata [i][j]->set (model [0][nn / 2], model [1][nn / 2], model [2][nn / 2]); } } } return *temp; } /* ----- end of method BmpPixmap::median_filter ----- */
C++ 中出现“不能将成员函数声明为有静态链接“的解决方法
类中遇到快速排序,准备把排序函数声明为静态的,于是有了以下代码:
class BmpPixmap{…… static int cmp_Byte (const void *p1, const void *p2);……}; /* ----- end of class BmpPixmap ----- */
static intBmpPixmap::cmp_Byte (const void *p1, const void *p2){ return (* (Byte *) p1 - * (Byte *) p2);} /* ----- end of method BmpPixmap::cmp_Byte ----- */
结果出现了错误:
这个是因为声明的时候已经提过是静态的了,实现的时候就不能再说一遍了,把实现函数的 static 去掉即可。不能将成员函数‘static int BmpPixmap::cmp_Byte(const void*, const void*)’声明为有静态链接
数字图像处理(一)——椒盐噪声
椒盐噪声(salt & pepper noise)是数字图像的一个常见噪声,产生该噪声的算法也比较简单。
椒盐,按我的理解,椒就是黑,盐就是白,椒盐噪声就是在图像上随机出现黑色白色的像素。
那么传入两个参数,分别为黑白像素在图像上所占比例,就可以对图像进行修改。
我们可以使用 srand 函数,根据 time 产生一个随机种子(以免每次随机的结果相同),然后使用 rand 函数产生随机数,rand 产生的随机数是 0 到 RAND_MAX 之间的整数,可以通过使用 double (rand ()) / RAND_MAX 产生一个 0 到 1 之间的浮点型。
这样,当这个随机数小于 pepper 时,就把该点调黑,大于 1 - salt 时,就把该点调白,就可以产生随机的椒盐噪声了。
效果如图:
源代码:
#include #include #include "bmp_pixmap.h" /* *-------------------------------------------------------------------------------------- * Class: BmpPixmap * Method: salt_pepper * Description: Add salt and pepper noise to the pixmap. *-------------------------------------------------------------------------------------- */ void BmpPixmap::salt_pepper (double salt, double pepper) { double temp; /*----------------------------------------------------------------------------- * Salt & Pepper should be between 0 and 1. *-----------------------------------------------------------------------------*/ salt -= int (salt); pepper -= int (pepper); /*----------------------------------------------------------------------------- * Init the random seed with time. *-----------------------------------------------------------------------------*/ srand (time (NULL)); /*----------------------------------------------------------------------------- * Salt & Pepper. *-----------------------------------------------------------------------------*/ for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { temp = double (rand ()) / RAND_MAX; if (temp > 1 - salt) { pdata [i][j]->set (255, 255, 255); } else if (temp < pepper) { pdata [i][j]->set (0, 0, 0); } } } return ; } /* ----- end of method BmpPixmap::salt_pepper ----- */
CMake 生成动态链接库的方法
使用 CMake 管理程序项目真的很方便,现在感觉 CMake 已经是编程的一部分一样。
上次说的数字图象处理的实现,当然也是使用 CMake 进行管理。
然而编着编着,却觉得把源码全放在一个目录里是件很恼人的事情,同时也不利于测试和再利用,于是产生了把 BMP 相关类作成一个动态链接库的想法。
CMake 做到这一点很简单。
首先,把所有要做成库的源代码放在一个目录里,比如我这里叫做 bmp_pixmap ,然后在这个目录下建立 CMakeLists.txt,加入以下内容:
SET (SRC_LIST bmp_info_header bmp_file_header bmp_pixel bmp_pixmap bmp_pixmap_fill bmp_pixmap_noise_salt_pepper) ADD_LIBRARY (bmp_pixmap SHARED ${SRC_LIST})
其中 SRC_LIST 是源文件列表,bmp_pixmap 是库的名称,也就是 libbmp_pixmap,SHARED 表示动态库,静态的话只需要改成 STATIC 即可。
然后,对于调用动态库的源文件 main.cpp 所在的目录,比如我这里是 bmp_pixmap 目录的上级目录 src ,也要建立一个 CMakeLists.txt,加入以下内容:
ADD_SUBDIRECTORY (bmp_pixmap) LINK_DIRECTORIES (bmp_pixmap) SET (SRC_LIST main) ADD_EXECUTABLE (main ${SRC_LIST}) TARGET_LINK_LIBRARIES (main bmp_pixmap)
因为 src 是 bmp_pixmap 的父目录,所以加上 ADD_SUBDIRECTORY 指令,LINK_DIRECTORIES 则指明链接库所在位置,TARGET_LINK_LIBRARIES 表示对目标 main 链接 libbmp_pixmap 这个库。
最后,项目根目录下的 CMakeLists.txt 是这样的:
CMAKE_MINIMUM_REQUIRED (VERSION 2.6) PROJECT (DIP) ADD_SUBDIRECTORY (src bin) ADD_SUBDIRECTORY (config)
这样就可以编译了。
可见,CMake 管理项目所需要写的配置文件十分之少,常用的命令也就那么几个,相比 AutoTools ,实在可以说是极度傻瓜化了……
用 CMake ,我骄傲哇~
试试用 C++ 实现数字图像处理
看到同寝的在学数字图像处理,好像很好玩啊……
正好正在学 C++,决定用 C++ 实现一下(他原来用的是 C、C++ 混编,代码惨不忍睹……)。
C++ 真的是个很麻烦的东西啊,定义一个类要费那么长时间,读写文件也没有 C 那么轻松,真怀疑它的用处了……
还是 Python 最简洁,C 我也很喜欢,真不明白那些自己用 C++ 写程序的人,怎么受得了这种笨重的搭建速度……现在做 C++ 作业,一个没什么用的小程序都要写一下午……
不说了,代码在 http://github.com/iven/dip/
Arch Linux – 自定义 CFlags 榨干计算机的油水
提到榨干计算机的油水,人们想到的往往是 Gentoo。
会这样想,一个重要的原因就是,Gentoo 完全由源码编译,在编译的过程中,可以自定义每个包的 CFlags。我们知道,很多 CFlags 可以对本机的 CPU 进行专门的优化,去除程序的一些调试信息等等,使程序在速度、体积等方面大幅度的提升(相对于没有优化来说,事实上,大多数发行版已经设定了 CFlags ,达到一定程度的性能提升),还可以设置 MAKEFLAGS 。
那么,我们的 Arch Linux 是否也可以像 Gentoo 一样自定义 CFlags 呢?当然可以。
呃,方法当然不是 export ……
用你喜欢的编辑器打开 /etc/makepkg.conf,其他不懂的不要改,看这一段:
#-- Exclusive: will only run on -march=x86-64 # -march (or -mcpu) builds exclusively for an architecture# -mtune optimizes for an architecture, but builds for whole processor familyCFLAGS="-march=native -O2 -pipe -fomit-frame-pointer"CXXFLAGS="-march=native -O2 -pipe -fomit-frame-pointer"#-- Make Flags: change this for DistCC/SMP systemsMAKEFLAGS="-j3"
这是我修改过的,其中 MAKEFLAGS 原来是被注释掉的,你可以根据自己的需要修改。
当然,这些 FLAGS 只对 AUR 中需要编译的软件包或者用 makepkg 编译的软件包(其实是一个意思)才有用,如果想要全部使用这些 FLAGS 编译,你可以使用 ABS 哦,要知道源里的软件包也都是 makepkg 起来的,呵呵,编译狂人动起来吧~







