图像算子
点算子
点算子仅根据单点信息计算新图像的每个点。常见的对比度、亮度调节均可视为点算子。
y = [x – 127.5 * (1 – B)] * k + 127.5 * (1 + B);
k = tan( (45 + 44 * c) / 180 * pi );
此处B, c为常见的亮度、对比度的调整参数,取值[-1,1]。注意这里的亮度、对比度是相对关系。尽管可以通过取均值或直方图统计的方法计算整幅图的亮度、对比度情况,但两者意义不同。
还有常见的伽马变换y=x^gamma (0<=x,y<=1),gamma参数为对数坐标,gamma=1时不变。伽马的特点是压缩一侧亮度区域,并扩展另一侧亮度区域。常见于相机动态光度感知不足时的调整。
至于饱和度、明度、色相的调整,需要在HSV/HSL色彩空间上进行。
线性滤波,卷积
如果变换依赖当前点周围的信息,则可借助(二维)卷积运算。该操作也称为线性滤波。
性质
根据卷积的一些性质,可以提高运算效率。例如,卷积的结合性和分配律使得做多次滤波等效于先对这些卷积核做卷积。卷积具有反交换性,因此交换次序问题也不大。
计算机用朴素的方式计算卷积时,需要(图像大小*卷积核大小)次乘法。该操作非常简单,很适合SIMD指令或GPU运算,这也是把各种滤波采用卷积表示的原因。
更进一步的,很多常见算子具有行列分离性,包括常见的高斯算子、Sobel算子等,这意味着可以把二维卷积分解为两次一维卷积。 行列独立性要求两个非零特征值。即使一些算子没有行列独立性,有时仍可以使用奇异值分解的方式,取前几个特征值做叠加。
而当卷积核比较大时,可使用离散快速傅里叶变换来进一步降低计算次数。
模糊,低通滤波
高斯、平均、tent函数(线性)等均可用于低通滤波。其中高斯滤波效果较好,具有旋转不变性,相比平均滤波中心权值比周围高。
滤波器可以通过傅里叶变换从频域来理解,则窗Sinc是理想的低通滤波器,而高斯滤波、线性滤波是其较平滑的近似。
锐化,高通滤波,边缘检测
从函数上看,锐化需要凸显图片的高频细节,也就是梯度大的地方。对图片可使用离散差分来代替导数,可以用卷积实现。最常见的是x方向和y方向的梯度算子,可以检测图片水平和垂直方向的边缘。拉普拉斯算子则使用二阶导数,它是梯度的散度,表示梯度的模方,比一阶导的梯度算子检测能力更强,且旋转不变。
然而,对现实中有噪声的图像做梯度会明显突出其噪声。更常用的方法是求梯度前先做一次模糊。对x,y方向偏导会采样周围的几个像素,这就是著名的Sobel算子。使用拉普拉斯算子前进行高斯滤波,称为高斯拉普拉斯算子。
Harris角点检测中,就使用了Sobel算子计算梯度的模值和角度,以分析该点是否是角度。
从频域来看,图像可视为低频+高频部分的组合,如果直接使用原图像减去低通图像(比如高斯模糊过的),同样进行了高频滤波。事实上,该操作称为高斯差分算子,它的卷积核与高斯拉普拉斯非常相似,是一种很好的近似。
通过对图像的下采样,每次将图像尺度减半,得到图像的高斯金字塔。高斯金字塔能够便捷的处理不同尺度下的图像情况。对金字塔的两层做差,得到相应的拉普拉斯金字塔,也就是图像的带通。
高斯金字塔的一个简单应用是高斯金字塔混合,它把两张图片不同尺度下的细节分别混合,效果比直接alpha混合好很多。
高斯下采样、上采样的范例实现:
def pyrdown_impl(image):
kernel = np.array(((1.0, 4.0, 6.0, 4.0, 1.0), ))
kernel /= kernel.sum()
image = cv2.filter2D(image, -1, kernel.T,
borderType=cv2.BORDER_REFLECT_101)
image = cv2.filter2D(image, -1, kernel, borderType=cv2.BORDER_REFLECT_101)
return image[::2, ::2]
def pyrup_impl(image):
upscaled_shape = list(image.shape)
upscaled_shape[0] *= 2
upscaled_shape[1] *= 2
kernel = np.array(((1.0, 4.0, 6.0, 4.0, 1.0), ))
kernel /= kernel.sum()
kernel *= 2
upscaled_image = np.zeros(upscaled_shape, dtype=np.float32)
upscaled_image[::2, ::2] = image
upscaled_image = cv2.filter2D(
upscaled_image, -1, kernel.T, borderType=cv2.BORDER_REFLECT_101)
upscaled_image = cv2.filter2D(
upscaled_image, -1, kernel, borderType=cv2.BORDER_REFLECT_101)
return upscaled_image
非线性滤波
非线性滤波需要一些特殊的计算,无法用卷积表示。常见有如中值滤波、双边滤波等。中值滤波和双边滤波用于滤除比例不高但非常明显的噪点。
形态学算子也属于非线性滤波。形态学算子处理的是阈值化后的二值图像,包括膨胀、腐蚀、过半、开运算、闭运算这几种。这里结构元素常见为3*3的窗口,有时它可以是更复杂的结构。
膨胀:结构元素内有0(黑色)则当前块取黑色。效果为使二值图像增大一圈。
腐蚀:同理,使图像减小一圈。
过半:结构元素内超过有一半为0则取0。可以平滑边界而不显著改变面积。
开运算:先腐蚀后膨胀。效果为消除小物体,使纤细链接分离,平滑大物体但不显著改变面积。
闭运算:先膨胀后腐蚀。效果为填补空洞、链接邻近等。
几何变换
二维图像变换时,将向量扩充为三维(x, y, 1),称为齐次坐标,使平移也可使用矩阵表达。使用齐次坐标时注意计算完投影变换后要将第三维归一。
平移 | [I | t] 2*3 | 自由度2,方向 |
刚性(欧氏) | [R | t] 2*3 | 自由度3,长度 |
相似 | [sR | t] 2*3 | 自由度4,夹角 |
仿射 | [A] 2*3 | 自由度6,平行性 |
投影(单应变换/同态) | [H] 3*3 | 自由度8,直线性,H整体数乘不影响结果 |
如表所示,虽然比投影简单的变换做一次变换时只需2*3矩阵,但计算它们的复合时需要扩充成3*3以便矩阵乘法。
对三维的点变换时,同样可以使用(x,y,z,1)的齐次坐标。但有个在二维中不会出现的问题是用三个参量表示的三维旋转无法被平滑插值,为解决这个问题需要用到四元数。