数字图像处理(三)——高斯噪声
与椒盐噪声相似,高斯噪声(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 int
BmpPixmap::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/
写了个提取手机屏幕数据的程序 fb2bmp
我的 E680i 已经买了三年了,一直想要研究它,却苦于技术不足。网上的手机程序,大多是二进制包,想要学习也没有办法。
以前在 Windows 下,甚至可以通过一个叫做 MotoVision 的程序,把手机当摄像头用,可惜根本找不到源代码……
昨天查了查资料,发现小 i 的屏幕色深是 16 位的,也就是每个像素点的颜色值存储为两个字节,那么整个屏幕的数据就是 320*240*2/1024=150 KB,每个像素点分为 RGB 三色,存储方法为 RRRRRGGG GGGBBBBB (16 位 565 方式),那么根据最近学的一点数字图象处理的知识,很容易就可以把它转换成 BMP 格式。
小 i 的操作系统是 Linux,屏幕设备文件是 /dev/fb0,也就是 framebuffer,这也是我把程序叫做 fb2bmp 的原因。只要通过 telnet 把 /dev/fb0 拷贝到电脑上,得到的就是拷贝瞬间的屏幕数据。
fb2bmp,可以将 fb0 的数据转换成 16bit 和 24bit 的 BMP 文件,16bit 比较好办,加上个文件头即可,需要注意的是要加上 3 个 DWORD 类型的掩码,表示 RGB 三种颜色分别占哪几位。24bit 则需要对数据进行移位,然后再加上文件头。
项目已经放在 http://github.com/iven/e680_fb2bmp/ ,有兴趣可以去看看。
补上图:
使用 PolicyKit 进行身份认证(下)
PolicyKit 的配置文件
分析完了 PolicyKit 的机制,我们来看一下它的配置文件是如何书写的。
PolicyKit 的配置文件藏在哪里呢?我们来进入/usr/share/PolicyKit/policy这个目录,怎么样,是不是看到很多.policy文件?(不要告诉我你没装 PolicyKit ……)
好的,打开其中的org.freedesktop.hal.storage.policy这个文件,你可能会看到下面的内容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> <policyconfig> <action id="org.freedesktop.hal.storage.mount-fixed"> <description>Mount file systems from internal drives.</description> <message>System policy prevents mounting internal media</message> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_admin_keep_always</allow_active> </defaults> </action> <action id="org.freedesktop.hal.storage.mount-removable"> <description>Mount file systems from removable drives.</description> <message>System policy prevents mounting removable media</message> <defaults> <allow_inactive>no</allow_inactive> <allow_active>yes</allow_active> </defaults> </action> </policyconfig> 我们来依次分析一下: id:id 是对 PolicyKit 中 Action 的唯一标识,可以看到,各个域从大到小,用圆点分隔。从下图我们可以看出,这是一种树形结构。description:这个是注释,没什么好说的,让读配置文件的人知道这个选项的意义。 message:这个就是前面提到的说明字符串了,强烈推荐写上,让使用者明白他在验证什么操作,以免引发安全隐患。 defaults:关键的地方到了!这里可以写上对于请求者是否为活动状态的三种情况(allow_any、allow_inactive、allow_active)返回的值。 可以返回的值有以下几种: no
auth_self_one_shot auth_self auth_self_keep_session auth_self_keep_always auth_admin_one_shot auth_admin auth_admin_keep_session auth_admin_keep_always yes这个实在没有什么可说的,它们的作用从字面上理解就可以了……如果不懂还是好好学学英语吧……
好了,说了这么多,可能大家还是不知道怎么使用 PolicyKit 编程吧?其实最简单的办法还是看手册啊!什么,你想要例子?看下这里吧!呵呵,相信懂得原理的你,再看这个例子的时候就不会那么吃力,很快就能应用 PolicyKit 进行编程了吧?
(全文完)
使用 PolicyKit 进行身份认证(中)
PolicyKit 的运作流程
在上次的图中,我们可以看出 PolicyKit 运作过程中的几个要素:请求发出者(gnome-mount),请求目标(磁盘),请求的动作(挂载)。下面我们来看一下 PolicyKit 的运作流程。
图一:用户点击了磁盘图标,系统通过 DBus 发送请求给 HAL。
图二:HAL收到并验证请求(请求被包装在一个叫做 PolKitCaller 的对象中,包含 uid 、 pid 、会话标识符、会话是否为活动状态、是否远程、远程地址和其他可选信息),并据此构建了 PolKitCaller 对象,之后,HAL 会调用 libpolkit 中的函数 polkit_context_can_caller_do_action () ,并把以上两个结构体作为参数。
根据这个函数传递来的参数,系统会到 PolicyKit 的配置文件(关于配置文件,会在下次说明)中进行验证,根据配置文件的返回值和验证数据库中的内容决定是否允许请求的操作。
可能的返回值有三种:通过(POLKIT_RESULT_YES)、拒绝(POLKIT_RESULT_NO)、需要验证(POLKIT_RESULT_AUTH 系列)。返回值被包含在一个叫做 PolKitResult 的结构体中。
图三:当返回值不是 POLKIT_RESULT_YES 时,HAL 把这个返回值外加之前所请求的操作一起返回给请求者。
图四:如果返回值是 POLKIT_RESULT_AUTH 系列(例如 POLKIT_RESULT_AUTH_ADMIN_KEEP_ALWAYS),顺带返回的还有定义在配置文件里的说明字符串(这样可以识别你正在验证的是什么动作)。
现在文件管理器知道想要随便挂载一个磁盘是不行的了,只好向 Authentication Agent 求助,说:”快快!帮我做个验证窗口出来!“顺带把刚才 HAL 发给它的 PolKitResult 和 PolKitAction 发给它。
图五:Authentication Agent 收到请求,也跟图二中的 HAL 一样,先验证一下传来的东西是否正确,构建个 PolKitCaller 去配置文件那里验证一下,检查 PolKitResult 是否和文件管理器传过来的一样。
然后 Authentication Agent 听话地构建一个验证窗口,包含之前返回的说明字符串。
图六:如果验证成功,它会让 HAL 去重新请求一下。此时因为验证成功,验证数据库中已经允许了此程序,当然验证通过了!
下次我们来分析一下 PolicyKit 的配置文件。
使用 PolicyKit 进行身份认证(上)
(为了说明方便,文中很多地方使用了 PolicyKit 官方文档中的图片。这篇文章并不是官方文档的翻译,想要获得更加全面的说明,请参见这里。)
什么是 PolicyKit
PolicyKit 是什么呢?之前发帖子的时候并没有弄清,以为是一个可以用来获得 Root 权限的东西,经过 TualatriX 兄的指点,才发现根本不是那么回事。
PolicyKit 是一种应用程序本身或者应用程序之间验证身份的机制,验证通过,你就可以执行所请求的操作,否则没门。这样说来对于没有什么安全隐患的功能是用不到它的,比如我的 CugbFreer,需要的只是调用 libpcap 进行流量统计,实在看不出什么危险,验证反而麻烦。不过既然之前已经说了要写一篇,那还是简单说说吧。
为什么要使用 PolicyKit
这个看起来像是对大多数人没用的机制,但是事实并非这样,只要仔细想一下,就可以挖掘出它的用途。
比如说,nautilus 里挂载磁盘,不知道你是否留意过,第一次使用的时候,点击一个未挂载的磁盘,就会弹出 PolicyKit 验证的界面。
我们知道,磁盘的自动挂载是靠 HAL 控制的,在 HAL 上面还有 gnome-mount,当你点击 nautilus 里的未挂载磁盘时,就会调用 gnome-mount。可是 gnome-mount 只是一个用户程序,而非特权(privileged)程序,如何能够“请”得动 HAL 呢?
与其主动去请,还不如 HAL 给我们提供一个特权接口,只需要验证一下身份,就可以调用 HAL 的特权方法,来挂载和卸载设备,并且系统可以记住这种特权,这样下次你挂载的时候,就不需要验证了!是不是很方便?
当然,当很多程序都需要一种验证机制、又不想自己去实现的时候,一种统一的机制 PolicyKit 诞生了。
(小提示:PolicyKit 还有哪些应用呢?我们比较熟悉的有 Network Manager 、Gnome 面板的时间控件、Ubuntu-tweak 等等。通常是在界面上加一个“解锁”键,点击时就会触发验证,以执行特权功能。)









