点击上方“CVer”,选择加"星标"置顶
重磅干货,第一时间送达
本文作者:简单控 | 来源:知乎(已授权)
https://zhuanlan.zhihu.com/p/342991714
0.前情回顾
CNN大家在计算机视觉中运用得已经非常多了,一般认为CNN提取特征是循序渐进的,开始提取 low-level的特征,比如边缘、纹理,中间提取 middle-level的特征,最后提取更加抽象的 high-level的特征。从low到high,特征需要的感受野也在不断扩大,high-level的特征需要前两层的特征进行融合,由此诞生了一系列经典的操作,比如特征融合的Densenet,扩大感受野的Non-local、ASPP,各种注意力机制的SENet、CBAM,以及等会儿会提到的FcaNet。
以上很多操作都是让网络去学习具体的卷积核参数,可以说是对频域的隐式建模。很早就有工作考虑结合传统图像处理领域的各类频率分解方法,显式去建模频域上的特征。
最近在看JPEG deartifacts的文章,因为DCT(离散余弦变换)是JPEG压缩技术中天然的一环,所以观察到很早就有工作在DCT域去解释特征,恰好最近Fcanet的工作也用到了DCT,所以自己做了一个简单的整理。
1.DCT与小波分解的简介及实践
比较常见的与CNN结合的技术主要是小波变换和DCT,也有傅里叶变换,但我还没实践过。这两者能用卷积能很容易的去实现,无缝衔接。
转化到频域的好处也很明显,首先RGB域与频域之间的相互转换是没有信息丢失的,不同的是频域的能量排布更加compact,每个channal明确代表了不同频率带的信息,可以用卷积去进一步融合,可解释性更好。
小波变换主要采用的是Haar小波变换,简单来说是把图像用四个卷积核去提取不同频率分量,分表代表直流、横向、纵向、斜向上的能量。
DCT的部分在Fcanet中有了很详细的推导,这里也不再赘述,简单说是频率带划分更加精细,可以自定义设置不同的频率带,KxK的分块DCT能划分出 K^2个从低到高的频率(channal),卷积核的可视化如下
人眼天然对低频分量更加敏感,高频的丢失并不会显著影响观感,这也是JPEG压缩的原理之一,下面有一个简单的实验来证明这一点。
class dct(nn.Module):
def __init__(self):
super(dct, self).__init__()
self.dct_conv = nn.Conv2d(3, 192, 8, 8, bias=False,
groups=3) # 3 h w -> 192 h/8 w/8
self.weight = torch.from_numpy(
np.load('models/DCTmtx.npy')).float().permute(2, 0,
1).unsqueeze(
1) # 64 1 8 8, order in Z
self.dct_conv.weight.data = torch.cat([self.weight] * 3,
dim=0) # 192 1 8 8
self.dct_conv.weight.requires_grad = False
self.Ycbcr = nn.Conv2d(3, 3, 1, 1, bias=False)
trans_matrix = np.array([[0.299, 0.587, 0.114],
[-0.169, -0.331, 0.5],
[0.5, -0.419, -0.081]]) # a simillar version, maybe be a little wrong
trans_matrix = torch.from_numpy(trans_matrix).float().unsqueeze(
2).unsqueeze(3)
self.Ycbcr.weight.data = trans_matrix
self.Ycbcr.weight.requires_grad = False
self.reYcbcr = nn.Conv2d(3, 3, 1, 1, bias=False)
re_matrix = np.linalg.pinv(np.array([[0.299, 0.587, 0.114],
[-0.169, -0.331, 0.5],
[0.5, -0.419, -0.081]]))
re_matrix = torch.from_numpy(re_matrix).float().unsqueeze(
2).unsqueeze(3)
self.reYcbcr.weight.data = re_matrix
def forward(self, x):
# jpg = (jpg * self.std) + self.mean # 0-1
ycbcr = self.Ycbcr(x) # b 3 h w
dct = self.dct_conv(ycbcr)
return ycbcr, dct
def reverse(self, x):
ycbcr = F.conv_transpose2d(x, torch.cat([self.weight] * 3, 0),
bias=None, stride=8, groups=3)
rgb = self.reYcbcr(ycbcr)
return rgb, ycbcr
主要设计把RGB转换到 YCbCr,再对YCbCr进行DCT变换,这里DCT采用8*8,系数已经经过重新排列,是标准的从低频到高频。
ycbcr, dct_ = DCT(img_tensor)
rgb2, ycbcr2 = DCT.reverse(dct_)
k = 1
for i in range(3):
dct_[0,64*i+k:64*i+64,...] = 0 # 截取64个通道的前K个低频信息
rgb3, ycbcr2 = DCT.reverse(dct_)
K=1,只有直流信息,压缩比1/64。分别代表原图,全频率还原,K频率还原
可以看出大部分信息
K=5
K=25
可以看出已经很好了,几乎不可见差异,这也是频域的作用,更加直观。
具体代码可以见这里
2.CNN与频率相结合的文章
D3: Deep Dual-Domain Based Fast Restoration of JPEG-Compressed Images
https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Wang_D3_Deep_Dual-Domain_CVPR_2016_paper.pdf
这是2016CVPR, 关于JPEG压缩 deartifacts的,解释了稀疏性和DCT
2020 cvpr,邢波老师,关于CNN对于不同频率的感知程度的
https://zhuanlan.zhihu.com/p/3156012952020 CVPR, 把频率送进去当输入
https://zhuanlan.zhihu.com/p/112751461
2018年,左旺孟老师,把小波运用到多种low-level任务中的
Multi-Level Wavelet Convolutional Neural Networks
https://ieeexplore.ieee.org/document/8732332
ECCV 2020, 可控SR,其中也用了了特征的频率分解,采用Haar小波变换
ECCV 2020 Oral | 可逆图像缩放:完美恢复降采样后的高清图片
2020,FcaNet
3.想法
运用频率的思想之前就了,如何用好还是很值得思考的,这里的可解释性比单纯CNN撘模型要强。
CV资源下载
后台回复:CVPR2020,即可下载代码开源的论文合集
后台回复:ECCV2020,即可下载代码开源的论文合集
后台回复:YOLO,即可下载YOLOv4论文和代码
后台回复:Transformer综述,即可下载两个最新的视觉Transformer综述PDF,肝起来!
重磅!CVer-细分垂直交流群成立
扫码添加CVer助手,可申请加入CVer-细分垂直方向 微信交流群,也可申请加入CVer大群,细分方向已涵盖:目标检测、图像分割、目标跟踪、人脸检测&识别、OCR、姿态估计、超分辨率、SLAM、医疗影像、Re-ID、GAN、NAS、深度估计、自动驾驶、强化学习、车道线检测、模型剪枝&压缩、去噪、去雾、去雨、风格迁移、遥感图像、行为识别、视频理解、图像融合、图像检索、论文投稿&交流、Transformer、PyTorch和TensorFlow等群。
一定要备注:研究方向+地点+学校/公司+昵称(如目标检测+上海+上交+卡卡),根据格式备注,才能通过且邀请进群
▲长按加微信群
▲长按关注CVer公众号
整理不易,请给CVer点赞和在看!