- 作者:老汪软件技巧
- 发表时间:2024-08-17 15:36
- 浏览量:
需求真是前进的基础动力,前段时间遇到一个这样的情景。给你一张大脑CT图片,希望你对它进行降级(所谓降级,也就是变低其质量),你有哪些好办法呢?比较基础的有高斯模糊方式、锐化、下上采样等,这次查阅了一些资料,顺便对它进行一个整理。
降级方法加噪
加噪方式比较好理解,实现也简单,生成一堆高斯噪声往上丢就可,如图1所示。
图1 添加高斯噪声
后面代码实现都是基于torch的,加高斯噪声代码如下。
def add_gaussian_noise(img, mean= 0.0, std= 0.1):
noise= torch.randn_like(img)* std+ mean
noisy_img= img+ noise
return torch.clamp(noisy_img, 0, 1)
除了添加高斯噪声,还可以添加椒盐噪声,这是一种数字图像中常见的噪声类型,具体表现为在图像上随机出现黑色和白色的像素点,如图2所示。椒盐噪声之所以得名,是因为其表现形式与“椒”和“盐”的颜色相似。在这里,“椒”代表黑色(pepper),而“盐”代表白色(salt)。当这种噪声出现在图像上时,就像是图像上撒了黑胡椒和白盐粒一样,因此得名椒盐噪声。
图2 添加椒盐噪声
下面是添加椒盐噪声的对应代码。
def add_salt_and_pepper_noise(img, salt_prob= 0.01, pepper_prob= 0.01):
salt_mask= torch.rand_like(img)< salt_prob
pepper_mask= torch.rand_like(img)< pepper_prob
img[salt_mask]= 1.0
img[pepper_mask]= 0.0
return img
下上采样
由于下采样会损失图像细节,而普通基于插值的上采样是无法生成细节的,先下采样再上采样便成为了一种图像降级方法,如图3所示。
图3 下上采样方式
下面是相应的代码实现。
# downsample and upsample
def dusample_3d(x):
assert(x.shape[-1]% 2== 0 and x.shape[-2]% 2== 0 and x.shape[-3]% 2== 0, 'Dim should be even.')
x_ds= F.interpolate(x.unsqueeze(0), scale_factor= (0.5, 0.5, 0.5))
return F.interpolate(x_ds, scale_factor= (2, 2, 2)).squeeze(0)
锐化
图像锐化可以补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰。这不是降级操作,是增强操作,不过也一起整理在这儿吧,如图4所示。
图4 锐化操作
我们通过设置卷积核就可以对图像进行锐化操作,考虑到锐化是增强边缘与周围像素之间的差异,对于一个2d图像,其卷积核可以被设计如下,
(0−10−14−10−10).\begin{pmatrix} 0 & -1 & 0 \\ -1 & 4 & -1 \\ 0 & -1 & 0 \end{pmatrix}. 0−10−14−10−10.
后面只需要用这个卷积核去卷图像就可以,相应代码如下(注意,我们数据是3d的)。
# apply sharp on image
def sharpen_3d(img, alpha= 0.1, ratio= 0.4):
channels, depth, height, width= img.shape
# define laplacian kernel
laplacian_kernel= torch.tensor([[[[0, 0, 0],
[0, -1, 0],
[0, 0, 0]],
[[0, -1, 0],
[-1, 6, -1],
[0, -1, 0]],
[[0, 0, 0],
[0, -1, 0],
[0, 0, 0]]]], dtype= torch.float32)
# adjust kernel, [1, 1, 3, 3, 3]
laplacian_kernel= laplacian_kernel.view(1, 1, 3, 3, 3)
laplacian_kernel= laplacian_kernel.repeat(channels, 1, 1, 1, 1)
# add batch
img= img.unsqueeze(0) # [1, C, D, H, W]
# 3dconv
sharpened_img= F.conv3d(img, alpha* laplacian_kernel, padding= 1, groups= channels)
# mix by interpolate
return ratio* sharpened_img.squeeze(0)+ (1- ratio)* img.squeeze(0) # [C, D, H, W]
腐蚀
通过减少图片中高频信息,我们就可以实现对图像的腐蚀,如图5所示。
图5 腐蚀操作
理解了腐蚀操作的原理,实现就很方便,通过利用Maxpool就可。
def erosion_3d(img, kernel_size= 3):
eroded_img= -F.max_pool3d(-img.unsqueeze(0), kernel_size= kernel_size, stride= 1, padding= kernel_size//2)
return eroded_img.squeeze(0)
模糊
最常见的可能就是高斯模糊了,也称作高斯平滑或高斯滤波。它通过对图像中的每个像素点及其邻域内的像素点按照高斯函数进行加权平均处理,达到减少图像噪声和降低细节层次的效果,从而使图像看起来更加平滑。由于它丢失了高频信息,我们也视之为降级操作。在2d图像上,高斯核的公式为:
G(x,y)=12πσ2e−x2+y22σ2G(x,y)=\frac{1}{2\pi\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}} G(x,y)=2πσ21e−2σ2x2+y2
其中,xxx和yyy是像素相对于核中心的偏移,σ\sigmaσ是标准差,可以决定模糊程度。通过这个高斯核对图像进行卷积,就可以实现高斯模糊,如图6所示。
图6 高斯模糊操作
实际代码上,我们采用的归一化方式与公式有所出入,不过原理相同。
# apply gaussian blur on image
def gaussian_blur_3d(img, kernel_size= 5, sigma= 1.0):
channels, depth, height, width= img.shape
# 3d gaussian kernel
grid= torch.stack(torch.meshgrid([torch.arange(size, dtype= torch.float32) for size in [kernel_size]* 3]), dim= -1)
grid= grid- (kernel_size- 1)/ 2.0
kernel= torch.exp(-(grid** 2).sum(-1)/ (2* sigma** 2))
kernel= kernel/ kernel.sum()
kernel= kernel.view(1, 1, *kernel.shape) # [1, 1, k, k, k]
kernel= kernel.repeat(channels, 1, 1, 1, 1) # [C, 1, k, k, k]
# conv
img= img.unsqueeze(0) # [1, C, D, H, W]
blurred_img= F.conv3d(img, kernel, padding= kernel_size//2, groups= channels)
return blurred_img.squeeze(0) # [C, D, H, W]
量化
量化可以将连续值变为离散值,应用非常广泛,如图像压缩等。连续值变为离散值,自然就丢失了一些信息,因此它也是图像降级操作,如图7所示。
图7 图像量化操作
图像降级操作的实现有很多,下面是通过四舍五入方式实现的。如果quality很大,则量化因子比较小,四舍五入对其影响就比较小,图像几乎没有被量化。如果quality比较小,比如50,则图像先*2,再四舍五入,此时影响就比较大。
# quality \in [0, 100)
def compression_3d(img, quality= 99.9):
quantization_factor= (100- quality)/ 100.0
quantized_img= torch.round(img/ (quantization_factor+ 1e-10))* quantization_factor
return torch.clamp(quantized_img, 0, 1)
代码与数据
完整代码可见我的github.