图像噪声是图像在获取或传输过程中受到随机信号干扰,妨碍人们对图像理解及分析处理的信号。噪声一般分为分为加性噪声和乘性噪声:
为图像添加噪声既可以使用C++标准库random,也可以使用opencv提供的函数。无论哪一种方法都涉及到随机数的相关知识,这里进行简要介绍,主要是涉及C++中的random标准库,具体请参考《C++primer(第五版)》。理论上计算机只能产生均匀分布,其他分布都是通过对均匀分布进行各种变换得到的。常用的变换方法有逆变换、拒绝采样等等。具体算法可以参考Luc Devroye, Non-Uniform Random Variate Generation(Springer-Verlag, New York, 1986)、左飞的《图像处理中的数学修炼》(清华大学出版社)和维基百科。
C++11新增了random库来产生随机数,通过使用随机数引擎类和随机数分布类生成符合要求的随机数。
随机数引擎是函数对象类,它们定义了一个调用运算符,该运算符不接受参数并返回一个随机无符号整数。我们可以通过调用一个随机数引擎对象来生成原始随机数。
default_random_engine e;
for(size_t i = 0; i < 10; i++)
{
cout << e() << " ";
}
标准库定义了多个随机数引擎类,区别在于性能和随机性质量不同。每个编译器都会指定其中一个作为default_random_engine类型。此类型一般具有最常用的特性。下表列出了随机数引擎操作。
操作 | 描述 |
---|---|
Engine e; | 默认构造函数:使用该引擎类型默认的种子 |
Engine e(s); | 使用整型值s作为种子 |
e.seed(s) | 使用种子s重置引擎状态 |
e.min() | 此引擎可生成的最小值和最大值 |
e.max() | |
Engine::result_type | 此引擎生成的unsigned整型类型 |
e.discard(u) | 将此引擎推进u步;u的类型为unsigned long long |
大多数情况下,随机数引擎的输出是不能直接使用的,必须要转换为合适的范围、类型和分布。
一个给定的随机数发生器一直会生成相同的随机数序列,这一特性在调试中很有用。但是,一旦我们的程序调试完毕,我们通常希望每次运行程序都会生成不同的随机结果,可以通过提供一个种子(seed)来达到这个目的。种子就是一个数值,引擎可以利用它从序列中一个新位置重新开始生成随机数。
为引擎设置种子有两种方式:在创建引擎对象时提供种子,或者调用引擎的 seed 成员:
default_random_engine e1; // 使用默认种子
default_random_engine e2(2147483646); // 使用给定的种子值
// e3 和 e4 将会生成相同的序列,因为他们使用了相同的种子
default_random_engine e3;
e3.seed(32767); //调用 seed 设置为一个新种子值
default_random_engine e4(32767); // 将种子值设置为 32767
for(size_t i = 0; i != 10; i++)
{
if (e1() == e2())
cout << "unseeded match at iteeration: " << i << endl;
if (e3() != e4())
cout << "seeded differs at itertion: " << i << endl;
}
设置种子最常用的方法是调用系统函数 time(),它返回一个特定时刻到当前经过了多少秒。函数 time 接受单个指针参数,它指向用于写入时间的数据结构。如果此指针为空,则函数简单的返回时间:
default_random_engine e1(time(0)); // 稍微随机些的种子
由于 time 返回以秒计的时间,因此这种方式只适用于生成种子的间隔为秒级或更长的应用。
类似引擎类型,分布类型也是函数对象类。分布类型定义了一个调用运算符,它接受一个随机数引擎作为参数。分布对象使用它的引擎参数生成随机数,并将其映射到指定的分布。例如,为了得到一个在指定范围内的数,我们使用一个分布类型的对象
//生成 0 到 9 之间(包含)均匀分布的随机数
uniform_int_distribution<unsigned> u(0, 9);
default_random_engine e; // 生成无符号随机整数
for (size_t i = 0; i < 10; i++)
// 将 u 作为随机数源
// 每个调用返回在指定范围内并服从均匀分布的值
cout << u(e) << " ";
上面的程序中,我们将 u 定义为 uniform_int_distribution
注意,我们传递给分布对象的是引擎对象本身,也就是 u(e)。如果我们将调用写为 u(e()),含义就变为将 e 生成的下一个值传递给 u,会导致一个编译错误。我们传递的是引擎本身,而不是它生成的下一个值,原因是某些分布可能需要调用引擎多次才能得到一个值。
分布类型都是模板,通过设置类型参数,可以得到不同类型的随机数。实际上,random库定义了20种分布类型,分布的名字与他们的数学性质相对应。在下面的描述中,我们通过将类型说明为template_name来指出分布生成浮点数。对这些模板,可以使用float、double或long double代替RealT。类似的,IntT表示要求一个内置整型类型,但不包括bool类型或任何char类型。可以用来代替IntT的类型是short、int、long、long long、unsigned short、unsigned int、unsigend long或unsigned long long。
分布模板定义了一个默认模板类型参数。整型分布的默认参数是int,生成浮点数的模板的默认参数是double。
uniform_int_distribution<IntT> u(m,n);
uniform_real_distribution<realT> u(x,y);
生成指定类型的,在给定包含范围内的值。m(或x)是可以返回的最小值;n(或y)是最大值。m默认为0;n默认为类型IntT对象可以表示的最大值。x默认为0.0,y默认为1.0。
bernoulli_distribution b(p);//以给定概率p生成true;p的默认值为0.5。
binomial_distribution<IntT> b(t, p);//分布是按采样大小为整型值t,概率为p生成的;t的默认值为1,p的默认值为0.5
geometric_distribution<Intr> g(p);//每次试验成功的概率为p;p的默认值为0.5
negative_binomial_distribution<IntT> nb(k, p);//k(整型值)次试验成功的概率为p;k的默认值为1,p的默认值为0.5
poisson_distribution<Intr>p(x);//均值为 double值x的分布
exponential_distribution<RealT> e(lam);//指数分布,参数lambda通过浮点值lam给出;lam的默认值为1.0。
gamma_distribution<Realm> g(a, b);//alpha(形状参数)为a,beta(尺度参数)为b;两者的默认值均为1.0。
weibull_distribution<RealT> w(a, b);//形状参数为a,尺度参数为b的分布;两者的默认值均为1.0。
extreme_value distribution<RealT> e(a, b);//a的默认值为0.0,b的默认值为1.0。
normal_distribution<RealT> n(m, s);//均值为m,标准差为s;m的默认值为0.0,s的默认值为1.0。
lognormal_distribution<RealT> ln(m, s);//均值为m,标准差为s;m的默认值为0.0,s的默认值为1.0。
chi_squared distribution<RealT> c(x);//自由度为x;默认值为1.0。
cauchy distribution<RealT> c(a, b);//位置参数a和尺度参数b的默认值分别为0.0和1.0。
fisher_f_distribution<RealT> f(m, n);//自由度为m和n;默认值均为1。
student_t_distribution<RealT> s(n);//自由度为n;n的默认值为1。