九. 滤波与卷积
边界外推和边界处理
自定义边框
自定义外推
阈值化操作
cv::threshold函数
double cv::threshold(
cv::InputArray src,
cv::OutputArray dst,
double thresh, //阈值值
double maxValue, //最大值
int thresholdType //阈值处理类型
);
每种阈值化操作类型对应于第i个源像素(srci) 和阈值thresh之间的比较运算方式, 根据源像素和阈值之间的关系, 目标像素dsti可以被赋值为0, srci或给定的最大值maxValue.
cv::threshold()中thresholdType的可选项:
阈值类型 | 操作 |
---|---|
cv::THRESH_BINARY | DSTi = (SRCi > thresh) ? MAXVALUE : 0 |
cv::THRESH_BINARY)INV | DSTi = (SRCi > thresh) ? 0 : MAXVALUE |
cv::THRESH_TRUNC | DSTi = (SRCi > thresh) ? THRESH : SRCi |
cv::THRESH_T0ZERO | DSTi = (SRCi > thresh) ? SRCi : 0 |
cv::THRESH_T0ZERO_INV | DSTi = (SRCi > thresh) ? 0 : SRCi |
Otus算法
函数cv::threshold()也可以自动决定最优的阈值, 只需要对参数thresh传递值cv::THRESH_OTUS即可.
Otus算法遍历所有可能的阈值, 然后对每个阈值结果的两类像素计算方差
σ
i
2
{\sigma_i}^2
σi2(即低于阈值和高于阈值的两类像素).Otus算法计算方差使下列表达式最小:
σ
i
2
=
w
1
(
t
)
⋅
σ
1
2
+
w
2
(
t
)
⋅
σ
2
2
{\sigma_i}^2 = w_1(t) \cdot {\sigma_1}^2 + w_2(t) \cdot {\sigma_2}^2
σi2=w1(t)⋅σ12+w2(t)⋅σ22
式中的
w
1
(
t
)
w_1(t)
w1(t)和
w
2
(
t
)
w_2(t)
w2(t)是根据两类像素的数量计算而来的权值,
σ
1
2
{\sigma_1}^2
σ12和
σ
2
2
{\sigma_2}^2
σ22表示两类像素的方差. 实际上, 这种方法不是一个相对高效的过程.
自适应阈值
阈值可以在整个过程中自动产生变化:
void cv::adaptiveThreshold(
cv::InputArray src, //输入图像
cv::OutputArray ds, //输出图像
double maxValue, //最大值
int adaptiveMethod, //方法或者高斯分布
int thresholdType, //阈值类型
int blockSize, //块的大小
double C //常数
);
根据adaptiveMethod的设置, 可以使用两种不同的自适应阈值方法. 两种方法都是逐个像素地计算自适应阈值T(x, y), 方法是通过计算每个像素位置周围的b x b区域的加权平均值然后减去常数C, 其中b由blockSize给定.
当图像中出现较大的明暗差异时, 自适应阈值非常有效. 这个函数仅醋栗单通道8位或浮点型图像, 并且要求源图像和目标图像不同.
平滑
平滑也成为"模糊", 平滑图像的目的有很多, 但通常都是为了减少噪声和伪影. 在降低分辨率的时候, 平滑也十分重要.
简单模糊和方框型滤波器
void cv::blur(
cv::InputArray src,
cv::OutputArray dst,
cv::Size ksize, //核大小
cv::Point anchor = cv::Point(-1, -1), //Location of anchor point
int borderType = cv::BORDER_DEFAULT //border extrapolation to use(边界插值)
);
图像中的每个值都是源图像中相应位置一个窗口(核)中像素的平均值(窗口尺寸通过ksize声明),
anchor指定计算时核与源图像的对齐方式, 默认情况下anchor位cv::Point(-1, -1), 表示核相对于滤波器剧中.
简单模糊是方框型滤波器(Box Filter)的一种特殊形式. 方框型滤波器中所有值 k i , j k_{i,j} ki,j全部相等(为1或者1/A, 其中A为滤波器的面积).
void cv::boxFilter(
cv::InputArray src,
cv::OutputArray dst,
int ddepth, //输出深度(e.g. CV_8U)
cv::Size ksize,
cv::Point anchor = cv::Point(-1, -1),
bool normalize = true, //ture: divide by box area
int borderType = cv::BORDER_DEFAULt
);
变量ddepth的值设为-1则目标图像的深度与源图像保持一致, 否则可以设置为其他任何一种常用别名.
中值滤波器
将每个像素围绕这个像素的矩形邻域内的中值或者"中值"像素(相对于平均像素).
少量具有较大偏差的点也会严重影响到均值滤波, 中值滤波可以采用取中间点的方式来消除异常值.
void cv::medianBlut(
cv::InputArray src,
cv::OutputArray dst,
cv::Size ksize
);
高斯滤波器
void cv::GaussianBlur(
cv::InputArray src,
cv::OutputArray dst,
cv::Size ksize,
double sigmaX, //Gaussian half-width in x-direction
double sigmaY = 0.0,
int borderType = cv::BORDER_DEFAULT //border extrapllation to use
);
参数sigmaX表示高斯核在x方向上的sigma值(最大值的半宽); 第四个参数表示y方向上的sigam值.
若两者都设为0, 则高斯参数根据以下公式确定:
σ
x
=
(
n
x
−
1
2
)
⋅
0.30
+
0.80
,
n
x
=
k
s
i
z
e
.
w
i
d
t
h
−
1
\sigma_x = (\frac{n_x-1}{2})\cdot0.30 + 0.80, n_x = ksize.width-1
σx=(2nx−1)⋅0.30+0.80,nx=ksize.width−1
σ y = ( n y − 1 2 ) ⋅ 0.30 + 0.80 , n y = k s i z e . h e i g h t = 1 \sigma_y = (\frac{n_y-1}{2})\cdot0.30+0.80, n_y=ksize.height=1 σy=(2ny−1)⋅0.30+0.80,ny=ksize.height=1
双边滤波器
void cv::bilateraFilter(
cv::InputArray src,
cv::OutputArray dst,
int d, //像素领域大小(最大距离)
double sigmaColor, //Width param for color weight function
double sigmaSpace, //width param for spatial weight function
int borderType = cv::BORDER_DEFAULT
);
第三个参数是像素邻域的直径d, 第四个参数是颜色空间滤波器的sigma值sigmaColor, 第五个参数是坐标空间中滤波器的sigma值sigmaSpace. 第三个参数越大, 平滑时所包括的强度(色彩)越大(因此图像的不连续性将会更显著).
双边滤波器是一种比较大的图像分析算子, 也就是边缘保持平滑.
tips:
- 滤波器的大小d对算法的效率有很大影响, 通常在视频处理时不大于5, 但在非实时应用是这个值可以放大到9, 你也可以在调用这个函数时将其设为-1, 函数将自动为图像计算sigmaSpcae变量的值.
- 实际情况中, 小的sigmaSpace值比如10会带来一个轻微的但也明显的效果; 而大的sigmaSpace值比如150会对图像产生非常显著的影响, 使图像有一种卡通的效果.
- 高斯模糊的过程是减缓像素在空间上的变化, 因此与邻域的关系紧密, 高斯平滑很好地减弱了噪声并且保留了小信号, 但破坏了边缘信息, 最终是高斯模糊把边缘也模糊了.
- 可以把双边滤波当作是高斯平滑, 只是相似程度更高的像素权值更高, 边缘更明显, 对比度更高. 双边滤波的效果就是将源图像变成一幅水彩画, 这种效果在多次迭代后效果显著, 因此这种方法在图像分割领域十分有用.
导数和梯度
索贝尔导数
void cv::Sobel(
cv::InputArray src,
cv::OutputArray dst,
int ddepth, //输出的图像深度(e.g. CV_8U)
int xorder, //x中相应导数的阶
int yorder, //y中相应导数的阶
cv::Size ksize = 3
);
xorder和yorder是求导顺序, 其取值范围为0, 1和2. 0表示在这个方向上不进行求导(xorder和yorder不能同时取0).
Scharr滤波器
拉普拉斯变换
图像形态学
膨胀和腐蚀
最基础的形态学变换是膨胀和腐蚀, 常应用于: 消除噪声, 元素分割和连接等. 基于这两种操作, 可以实现更复杂的形态学操作, 用来定位强度峰值或孔洞, 另一种形式的图像梯度等.
膨胀是一种卷积操作, 它将目标像素的值替换为卷积核覆盖区域的局部最大值. 膨胀的作用是使填充区域生长.
与膨胀相反, 腐蚀操作计算的是核覆盖范围内的局部最小值.
tips:
图像的形态学操作通常在阈值化操作后的布尔图像上进行.
总的来说, 膨胀扩张了明亮区域, 腐蚀缩减了明亮区域. 另外, 膨胀填充凹面, 腐蚀消除突起.
//腐蚀
void cv::erode(
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray element, //Structuring, a cv::Mat()
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1, //迭代次数
int borderType = cv::BORDER_CONSTANT,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
//膨胀
void cv::dilate(
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray element,
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1,
int borderType = cv::BORDER_CONSTAN,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
腐蚀和膨胀支持原地调用(源图像和目标图像是同一副图像).
第三个元素是核, 可以向它传递一个未被初始化的cv::Mat().
通用形态学操作
void cv::morphologEx(
cv::InputArray src,
cv::OutputArray dst,
int op,
cv::InputArray element,
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1,
int borderType = cv::BORDER_DEFUALT,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
参数op:
操作值 | 形态学操作 | 是否需要临时图像 |
---|---|---|
cv::MOP_OPEN | 开操作 | 否 |
cv::MOP_CLOSE | 闭操作 | 否 |
cv::MOP_GRADIENT | 形态学梯度 | 总是需要 |
cv::MOP_TOPHAT | 顶帽操作 | 就地调用需要(src = dst) |
cv::MOP_BLACKHAT | 底帽操作 | 就地调用需要(src = dst) |
开操作和闭操作
- 开操作先将图像腐蚀,然后对腐蚀的结果膨胀(常用于对二值图像中的区域进行计数)(放大裂缝和局部小洞)
- 闭操作先将图像进行膨胀, 然后对膨胀的结果进行腐蚀(常用于减少无用或噪声驱动的片段)
形态学梯度
顶帽和黑帽
分别用于显示与其邻域相比更亮或更暗的部分.
T
o
p
H
a
t
(
s
r
c
)
=
s
r
c
−
o
p
e
n
(
s
r
c
)
TopHat(src) = src - open(src)
TopHat(src)=src−open(src)
B l a c k H a t ( s r c ) = c l o s e ( s r c ) − s r c BlackHat(src) = close(src) - src BlackHat(src)=close(src)−src
顶帽操作用源图像减去对其开操作后的图像
黑帽操作用闭操作后的图像减去源图像
自定义核
自定义形态学核的函数:
cv::Mat cv::getStructuringElement(
int shapce, //Element shape
cv::Size ksize, //size fo structuring element(odd num!)
cv::Point anchor = cv::Point(-1, -1) //默认锚点在元素中心
);
第一个参数控制构造元素的基本形状, 第二第三个参数确定元素的大小和锚点位置.
cv::getStructuringElement()的元素形状:
形状值 | 元素 | 描述 |
---|---|---|
cv::MORPH_RECT | 矩形 | E i , j = 1 , ∀ i , j E_{i,j}=1,\forall{i,j} Ei,j=1,∀i,j |
cv::MORPH_ELLIPSE | 椭圆形 | 以ksize, width和ksize,height为 两个半径做椭圆 |
cv::MORPH_CROSS | 交叉 | E i , j = 1 E_{i,j}=1 Ei,j=1当 i = = a n c h o r . y i==anchor.y i==anchor.y或 j = = a n c h o r . x j==anchor.x j==anchor.x |