0%

HDR 综述

图像融合

HDR 概念

  1. HDR 动态范围:是指图像的最大亮度和最小亮度的比值。对于一个8位的图像,能够存储的动态的范围只有 10^2量级,但是自然场景以及人眼的感知范围远远大于这个。而显示器可以显示的动态范围也只有 10^2 量级,因此直接显示 HDR 图像也会使细节丢失。
  2. HDR成像:包含两个 过程,一个是成像,相机采集生成位深度为10,12或者16位的高动态图像;另一个过程是 显示,使用特殊的显示器显示高动态范围的图像。

我理解的低光图像增强和HDR的区别:

主要就是两种场景:

  1. 一种是纯黑的图提亮;
  2. 一种是光照不均匀的图

低光照增强实际就是 8位的 图像 -> 提亮后的8位图像,整个过程图像位深度不变,只是通过算法改变了图像的效果,可以认为是伪HDR图像,因为记录的图像的动态范围并没有增加

HDR技术,则是 8位LDR图像 -> 10/14/16位HDR图像,图像的位深度变大了,图像记录信息的动态范围确确实实增加了。但是显示器能显示的动态范围有限,因此还是要经过 色调映射技术才能在一般的显示器上显示。

image-20210706200639404

方法分类

Tone Map色调映射

照相机和摄像机可以捕捉到HDR的影像,渲染过程中可以产生HDR的画面。这些内容如果需要显示到LDR的设备上,就需要一个称为tone mapping的过程,把HDR变成LDR HDR->LDR显示

Tone Map色调映射算法都可以看作 S 曲线映射 只是不同方法曲线形状不太一样

单帧HDR

单帧HDR 是输入为LDR图像,然后提升局部和全部细节输出,感觉输出也是LDR域的伪HDR图像

  1. 条件编码结构:传统的CNN在图像所有位置都采用相同的滤波器权重处理,但是对于图像中 不同区域的处理是不同的,例如图像超分中 通过预先语义分割的一个map作为条件图来使网络对不同纹理的区域有不同的处理。在HDR中也一样,因此常常会通过一个条件子网络来 做位置相关编码核图像相关编码。但是感觉这种和注意力没啥区别 都是点乘 一个 条件图 + 一个偏置
  2. 分频处理的结构:将图像分为不同的频带分别处理 低中高 分别处理融合 例如 DRBN 文中 就是全局三个频带处理,并且再每个阶段会有频带间的信息交流 就是会上采样融合作为下一阶段的输入 同时 全局和局部特征提取结构也是很常见的结构
  3. 生成多帧再融合的思路就是将 单张图像 通过 乘系数 或者 通过相机响应模型等方法 扩展为多张亮度等级的图 再采用多帧融合的思路进行 融合

多帧HDR

多帧HDR 常常是要考虑 动态场景下的 HDR 如果是对齐的 可以之间采用一个蒙板函数融合生成 HDR 图像 然后 ToneMap压缩动态范围再显示。同时多帧HDR可能有下面两种思路

  • 使用不同的曝光采集多帧图像 融合 由于不同时间采集的一定会有前景不对齐的问题
  • 采集多张相同的低曝光的图像 HDR+ 中 避免不可逆的过饱和,同时也可以采用多帧降噪方法抑制阴影区域的噪声,但是也存在不对齐的问题

网络结构

  1. 传统思路:传统思路就是 预处理 对齐 融合的 方法;早期的方法都是这样做的
  2. 多分支的网络:认为不需要认为做 对齐 CNN有自动对齐的功能,因此直接采用多分枝特征提取 + 融合 + 解码 的结构
  3. 在网络中引入 特征对齐网络,相对第二种的朴素思路自然效果更好。具体的可以是 使用可变形卷积构建的多尺度特征对齐模板,也可以是基于自适应卷积滤波器的结构,就是一个分支输出线性变换参数 ,通道数为 cxc 然后对对应位置的特征图的 c x c 领域的特征做线性变换输出。融合的话可以使用注意力机制的特征融合模块

参考链接

https://zhuanlan.zhihu.com/p/376246037

https://zhuanlan.zhihu.com/p/361596881

https://blog.csdn.net/weixin_45250844/article/details/103207184

https://mp.weixin.qq.com/s/WxHn33gIArfRlRdg0jDfdw

https://mp.weixin.qq.com/s/Tqxj7UfX8z6o3l8OfAqW0w

维纳滤波

逆滤波

用于不含噪声的图像去模糊

维纳滤波

用于含有噪声的图像去模糊 以及去噪

参考链接

基本原理

维纳滤波详解

在图像拍摄过程中由于各种原因会造成图像退化,图像退化模型如下:

image-20210630222253821

其中,⋆ 为卷积符号,f ( x , y ) 为输入图像,g ( x , y ) 为退化图像,h ( x , y ) 为退化函数,η ( x , y ) 为加性噪声,将上式进行傅里叶变换有(根据傅里叶变换的特性,空间域中的卷积相当于频率域中的乘积):

image-20210630222401374

(1) 如果不考虑退化函数,图像退化模型就简化为图像噪声模型

image-20210630222435984

图像增强问题成为单纯的图像去噪问题,可以通过空间域滤波等众多方法解决。

(2) 如果不考虑加性噪声,图像退化模型就简化为

image-20210630222453569

这种问题可以通过逆滤波解决,即通过傅里叶变化以及阵列除法即可获得恢复后的图像频谱:

image-20210630222524519

但是对于退化模型 H 如何获取呢?数字图像处理中有 观察估计法,试验估计法和建模估计。例如建模估中可以通过运动数学模型将退化函数构造为:

image-20210630222732217

(3) 如果退化函数和加性噪声都考虑,空域滤波器无法解决图像退化问题,逆滤波效果因为噪声的存在会变得非常差,这个时候就需要用到维纳滤波,(维纳滤波的推导写在结论中)维纳滤波公式如下:

image-20210630222742064

https://blog.csdn.net/qq_33552519/article/details/108632146

https://www.codenong.com/cs105736895/

https://segmentfault.com/a/1190000021721649

https://soaringleefighting.github.io/2019/05/29/%E3%80%90CV%E7%B3%BB%E5%88%97%E3%80%91%E5%9B%BE%E5%83%8F%E5%8E%BB%E5%99%AA%E7%AE%97%E6%B3%95%E7%A0%94%E7%A9%B6%EF%BC%88%E5%8E%BB%E5%99%AA+%E9%94%90%E5%8C%96%EF%BC%89/

HDR+

Burst photography for high dynamic range and low-light imaging on mobile cameras

参考链接

主要思想

  1. 不使用多曝光。而是使用一个固定的较低的曝光时间和增益采集多帧图像。将这个曝光设置的足够低以比避免过曝溢出
  2. 直接对bayer格式的数据处理,而不是RGB。一方面采集多帧如果都用RGB格式那每张都要经过ISP,另一方面ISP会对RAW图像做个非线性化,使得去噪变得困难
  3. 使用了基于 FFT 的快速对齐算法

主要思想是,首先使用多帧低照度的 raw 格式的图像做去噪融合成一张(这是一个常用多帧去噪手段),然后使用一个自适应的曝光算法将该图像扩展到长短曝光后融合,最后将处理后的 RAW 数据 经过ISP流程得到结果。

主要内容

  1. 故意曝光不足避免过曝而不可逆
  2. 采集多帧用于抑制暗区域噪声
  3. 使用自动曝光算法 设置长短曝光参数,短曝光参数用于调整相机硬件参数来采集固定曝光的图像,长曝光参数用于对短曝光图像做tone map合成长曝光图
  4. 使用 像素级的 曝光融合方法 融合长短曝光图像
  5. 色调映射(tone map)压缩动态范围

自动曝光

这一步其实是在多帧去噪融合之后使用的,按照原文顺序先介绍了。

现在的 ISP 中都集成了 自动对焦 自动曝光 自动白平衡算法(3A),理论上我们可以直接使用ISP中自带的自动曝光算法根据场景自动设置相机曝光参数,然后再沿用这个参数采集多帧固定曝光的图像用于后续的融合去噪。但是,对于高动态范围的场景,拍摄的图像可能包含无法通过后期 HDR 色调映射恢复的高光或曝光不足的区域。(个人感觉主要是过曝不可逆,所以要避免)

因此其实最终是对 两张gamma矫正后的图像做融合,其中一个是多帧去噪后的欠曝光图,另一张是对欠曝光后的图使用数字曝光补偿后的图。因此自动曝光算法可以 表述 为两个曝光水平:

  • 合成长曝光:由短曝光图合成的长曝光图,在色调映射中用于恢复阴影区域
  • 短曝光:用于捕捉场景中亮的区域

动态范围: 图像能捕捉的场景中的光亮度的范围。自然场景中的动态场景通常特别高,人眼能看到的范围也很大。而通常相机能表示的范围只有256中。即便相机能采集到大动态范围的图像,但是一般的显示器能显示的亮度等级也有限,一方面为了弥补相机自生的动态感知范围不足的问题常常采用多曝光融合的方式来合成高动态的图像(场景亮暗细节都能保留),另一方面还要通过tone map色调映射技术将HDR图像压缩为,转换成可以在显示器上显示出更符合人眼视觉的LDR图像。动态范围

虽然说要尽量先使用低曝光的图像去采集多帧图像,从而避免饱和。但是曝光太低意味着噪声太强,需要融合更多帧来抑制噪声,这需要付出计算代价。同时如果过分的压缩动态范围(我理解为这是 Tone map 这一步的压缩动态范围,将合成的高动态图使用色调映射手段压缩为显示器可以显示的范围)会出现卡通效应(就是阴影部分被过度提亮了)文章中设置的压缩比为8(即最亮:最暗大约8比1)

基于实例的自动曝光

构建了一个包含5000个场景的数据集,并手动给每张图片标注 短曝光参数 和 长曝光参数,前者用于采集图像的曝光参数,后者用于后面 tone map 的参数。给定一帧raw的输入图像,提取他的特征描述符并在数据库中寻找与他对应的候选样例,然后将所有候选样例手动标注的两个参数分别做一个简单的加权融合。

  • 用于计算特征描述符的图像保证是白平衡的
  • 是在大幅下采样的版本上计算特征描述符,用于量化场景亮度分布
  • 四组64纬的特征,其中两组在两个不同的尺度空间上提取的,其中两组提取自图像 max(R,G,B) 和 avg(R,G,B);前者用于代表频率空间的曝光,后两组用于关注颜色削波 (color clipping ?????不懂)
  • 带注意力的特征提取手段,通常情况下计算特征时都是给与图像中心更多的权重,当选择中人脸时也可以给与选定区域较大的权重。
  • 选取候选实例中亮度在当前场景亮度8倍的实例,以防止昼夜颠倒的不真实情况

曝光参数分解

通过前面可以计算出 长短曝光参数。那么这一步就是要将从实例中查询计算的短曝光参数 分解为 真实的相机参数(相机曝光时间和曝光增益)然后修改相机参数再去采集图像。这一步使用一个固定的策略来 平衡噪声 和 运动模糊(如果曝光时间长就会有运动模糊)之间的关系

  • 对于亮的场景,曝光增益设置到最低,而曝光时间提高到8ms
  • 当场景变得暗时,曝光时间固定8ms不变,曝光增益x4
  • 最后曝光时间和曝光增益一起成比例的提高,曝光时间上限100ms 曝光增益上限96x 为了尽可能提高采集图像的信噪比,尽可能提高曝光增益

(这个过程不是很理解 为何要这么做 以及前面的短曝光参数在这一步做何用?)

图像数量(burst size)

在较亮的场景 使用1~2张图即可,因为噪声不多,虽然多一点图也可以抑制运动模糊啥的。在低光照环境下 或者 高动态场景下,通常要将低亮度的区域亮度拉升,会有噪声,因此需要较多帧去噪,提高信噪比。

通常作者 会限制 图像数量才 2~8 张

图像流采集

自动曝光算法运行在ISP之前。每四帧图像做一次 自动曝光的参数 计算,计算一次大概10ms,没必要每次都计算因为场景不会急速变化,但是也不能太久计算一次参数,免得场景切换时显示有延迟。

另一个问题就是,高动态场景中如果包含大量过曝的像素,对估计 低曝光参数 有影响,因此作者通过 忽略 部分过曝像素来计算 匹配参数能较好地匹配样例中相似的低光照场景 (?)

帧对齐

常用的对齐方法是光流对齐,这种方法可以对齐非刚体形变,但是计算复杂度很高。出于速度、内存和性能(毕竟是移动端)的折衷考虑,作者提出了一个简单的对齐的方法,虽然这种方法无法应对非刚性形变并且误差比较大,但是后续的多帧融合算法对这个误差比较鲁棒,所以即便是使用这个简单快速的对齐方法,整体效果也可以接受。

选参考帧

参考帧选择尽可能清晰的帧。根据基于原始输入绿色通道中梯度的简单度量,选择参考帧作为突发子集中最清晰的帧。同时为了最大限度地减少快门延迟,按下快门后连拍并从前 3 帧中选择参考帧。

处理raw图像

raw数据格式实际是一整张 RGGB 排列的图像,包含了四个颜色平面。作者直接使用 2X2 的平均池化 得到 下采样的灰度图,对该灰度图像做配准。(我觉得这里不必拘泥于是不是 严格的灰度,因为最后对齐的损失函数使用像素差异来做的,即便不是标准的 RGB->gray也不太影响对齐性能)

分层配准

作者采用了一个由粗到细的配准策略。具体的是构建一个高斯金字塔的结构,先从顶层配(粗), 将上层的配准结果作为下一层的固定偏移。每层配准的表达式

image-20210628093708522

其中 u0 v0 为上一层配准的偏移结果,p 可能是 L1/L2距离,n 是计算相似度窗口的大小。u v 即为要求的 偏移;因此求u v 即为优化D的最小值。关于窗口的大小 和 采用的损失类型会在后面讨论。另外这里没有采用归一化互相关系数类似NCC中的东西对光照对比度做归一化是因为作者认为采集的几帧图像都是在相同曝光才采集的亮度一致。

边界情况怎么处理?

根据不同的尺度层,采用了不同的超参数。在粗尺度下,使用L2损失和大搜索半径来做对齐(这里的较大搜索半径如何理解?理解为偏移吧),以提高初始对齐的准确率;在底层,使用L1损失和较小的搜索半径。按照文中的说明:

  • 最底层,使用+-1的搜索半径,L1损失,图像块为16X16
  • 接着,对上层做二倍下采样,+-4搜索半径,L2损失
  • 接着,对上层做四倍下采样,+-4搜索半径,L2损失

个人理解,最底层的L1损失精度最高,但是计算比较耗时。高层的L2损失可以做加速,优化的计算方法中这个搜索半径其实就是一个限幅的作用,并不会实际对每组偏移都计算然后选一个最优,而是最小化二次式的最优估计的方式,因此这里添加的搜索半径其实就是个限度,并不会实际影响计算速度,而这里的限度估计也是怕高层估计误差太大跑偏太远使得当前层无法对上一层的偏差纠正。由于在高层有了粗略偏移估计,所以在最低层使用+-1的搜索半径就行。

如果不采用尺度金子塔的策略,最底层就要 做一个搜索半径变化范围很大的对齐,例如可能一次就要估计出 +20 的偏移,如果使用L2加速对齐策略,这里一次L2最优估计很容易陷入局部最优,采用L1做精细搜索显然计算量很大。

快速L2对齐

image-20210628111641192

其实就是前面那个 L2 损失 可以分解为为局部和、局部平方和 以及 卷积计算两部分。前者可以通过积分图加速,后者可以在频域加速。然后假设为 D2为 待估计参数偏移 (u,v)的一个二元二次表达式,如下,我们可以通过计算几个固定偏移 (u v) 下对应的 D2值来估计固定参数A ,b c

image-20210628111927519

最后全局最优值 (u , v) 即为:

image-20210628112051053

所以这是一种近似最优的L2加速方法,不需要搜索每个可能的偏移值,只需要计算几个离散的点做一个二次多项式拟合。

多帧去噪

时域去噪

这也是该文章的核心部分,通过连拍实现对同一场景做多次观察来实现降噪。但是从上面的对齐策略可以看出,前面的对齐只考虑了刚性变化,对于非刚性运动遮挡等问题,上述的对齐方法无法估计。因此这就要求这一节的多帧融合方法可以对上述的对齐误差鲁棒。

采用了 成对的时域合并方法。

首先对每帧图像 变换到频域 T0 为参考帧的频谱图

  1. 首先一个方法,如下式,简单的将每个帧的频谱图加权平均,但是这样效果最差,因为正交变换是线性的,这样加权等同于直接在空域中对多帧取平均,因此如果有旋转不对齐的情况鬼影就很严重。
image-20210704160856275
  1. 接着还有 3D 卷积的方法,类似于BM3D中的做法 参考 图像处理-传统去噪算法汇总. 将时域的图像组成3D块然后先对两维变换再在时域第三维做变换,利用白噪声正交变换分布不变的思路通过新增维度的正交变换将能量进一步聚集,拉开噪声幅值和信号幅值的差距,然后采用硬阈值或者软阈值滤波滤掉噪声。但是这种方法也要依赖相似块是否对其,否则在第三维中会引入新的频带信号和噪声混合被一同滤除,相当于还是做了模糊。

  2. 最后作者的方法是 对 1 的一个改进。如下式,其实就是 (1 - A) T + A T0 A是个系数 。相当于在参考帧 和 其他帧 的频谱之间做了个线性插值,而不是直接取平均。这个插值系数由 两帧之间的频谱差异 D 计算。但是不是简单的差异,而是拿噪声的强度做了个参考,如果差异相对噪声频谱的强度很大,表明不对齐,那么就给较小的插值系数;反之给较大的权重。系数 类似 像素级别的维纳收缩的系数只不过维纳收缩中 使用信号噪声的比 来确定收缩系数,这里使用 频谱功率差异和噪声的比来确定每对儿的插值系数。

image-20210704162829608
image-20210704162934904
image-20210704162947537

最后,作者说他们的方法 可以在 图像去噪质量 和 鬼影问题 做个较好的权衡

空间去噪

通过上一节对时域中的多帧频谱图像平均达到了 减小噪声方差的目的,如果是N张平均可以理解为噪声缩小了 sigma^2 / N,得到一张滤波后的二维频谱图。按照维纳滤波收缩的形式(即使用信噪比估计收缩系数),再在空域对频谱幅值做收缩。但是这一次根据人眼视觉特性,人眼对高频区的人造痕迹不是很敏感,所以根据这个特性 作者对不同频带的噪声强度乘了个系数,给高频区域更高的噪声强度,从而降低对高频区域的滤波程度(即尽可能保留高频区域的纹理)。其中 f(w) 是个分段线性的函数。通过此举,来尽量提高图像的视觉效果 而 不是 SNR

也正是这种 不同频段设置不同噪声水平的维纳滤波方法,使得最后在高对比度的区域噪声抑制效果不好

image-20210704170800576

融合Bayer数据

对 bayer 图像的每个颜色平面分别对齐融合。

然后就帧融合是 一个块一个块进行的,这些块之间设置一定的重叠,为了防止出现块边缘的不连续效应,使得融合更加平滑,对块进行窗函数操作。在DFT域使用升余弦函数。

问题

  1. 这种滤波方法不能有效地抑制高对比度区域(如强边缘区域)的噪声。这是由于高对比度区域在空间DFT域的分布是非稀疏的,削弱了空间降噪的有效性。

image-20210704192601394

  1. 由于算法不会拒绝一个不能完全配准的 块 ,所以有时候会出现轻微的鬼影。

image-20210704192645530

  1. 有时会出现振铃效应,作者说该现象基本可以忽略不计;(若频域滤波函数具有陡峭变化,则傅里叶逆变换得到的空域滤波函数会在外围出现震荡)

合成HDR

经过配准和融合之后,拍摄的多帧Bayer raw图像合成单张raw图像,且具有更高的bit深度和SNR。实际中,输入10bit的raw数据会合成12bit的数据。得到输出raw图像后,还需要一系列的ISP后处理流程才能得到可视化RGB图像。这些步骤主要包括:

  1. 暗电流扣除
  2. 镜头阴影校正
  3. 白平衡
  4. 去马赛克
  5. 彩色去噪
  6. 颜色校正
  7. 动态范围压缩
  8. 去雾
  9. 全局色调调整
  10. 色差校正
  11. 锐化
  12. 特定色调颜色调整
  13. dithering

其中,动态范围压缩步骤中,利用获取的短曝光图像以及合成的长曝光图像来 融合。融合方法采用了 Exposure Fusion 文章中的方法融合。但是这里作者只用了亮度这一个评判因素,计算两张图的亮度权重,加权融合(这样节省计算量)合后做gamma校正并将原来的颜色赋值上去。

问题

在极端动态范围场景,可能存在过曝光区域。在有快速运动的低光场景,为了避免运动模糊使用较短的曝光时间,可能会残留比较剧烈的噪声。在高对比度场景中,由于使用了曝光融合,可能会存在轻微的中频halos。

实验结果

有一个无限小数,小数点后前两位是a和b,a和b均不为0,从第三位起,是前两位之和取模10 这个小数是无限循环,还是不循环,还是不一定

由于当前位数只由前两位决定,因此如果 第一次出现了 ab 然后后面又出现了ab 那么必然就循环了。而要对10取模,因此a b 的范围都0~9 两个连续字取值就10 x 10种情况,所以一定会循环。

1- n中所有的数里面1出现的次数

剑指 Offer 43. 1~n 整数中 1 出现的次数

100个石子,每次只能取1-8个。问第一次取多少个能保证自己必赢

将两个人取的总数看作一轮,每一轮先手最多拿走8个,因此后手总是可以保证这一轮两个人一共拿走9个。所以这样如果有99个石子,99/9=11个回合后,后手正好拿完所有的式子,因此先手拿一个一定可以赢

斗地主中,地主拿到王炸的概率是多少

这个就是很简单的排列组合问题。54张牌,可以考虑这种分配模型,将54张牌放一排,前17张农民1的,后17张农名2的,最后20张地主的,然后就是算大小王都在后20张的概率,可以看作把大小王放到 54 个位置中的两个,大小王不同,有序,所以A54/2 同理 A20/2

如何精确测量(计算)不规则物体的表面积?

热缩膜覆盖后撕开铺在格子纸上数格子

海盗分金

经济学上有个“海盗分金”模型,是说5个海盗抢得100枚金币,他们按抽签的顺序依次提方案:首先由1号提出分配方案,然后5人表决,超过半数同意方案才被通过,否则他将被扔入大海喂鲨鱼,依此类推. 假定“每人海盗都是绝顶聪明且很理智”,那么“第一个海盗提出怎样的分配方案才能够使自己的收益最大化?”

1,2,3,4,5 从后往前推,如果1-3号都死了,只有4,5号,那么5号一定不会给4号投票让4号也死这样自己就能占有所有硬币。因此4号直到5号的想法就会杜绝这种情况发生,因此它要无条件支持3号,避免只剩4,5两人的情况。3号也直到4号会无条件支持他,因此它只用 100 0 0 即可。2号也知道3号这么想的,所以3号必定不会支持2二号,因此它必须讨好2号让他俩同意它的方法,所以就 98 0 1 1 这样根据2号给出的方案4号5号都能拿到1块,比把2号淘汰后3的方案好,所以这样4和5号就会接受2号的方案。同理1号又知道2号这么想的,所以应该放弃2号,并且给3号4号和5号更多的利益,首先3号如果不投1号,到2号手里就只能得到0个硬币,所以1号只需给3号1个硬币即可让3号支持它,而4和5号如果让1号死了他们最多从中获得1个硬币,因此给他们两个硬币比之前多就可以。由于1号只需3 4 5 中取两个拿到票即可,因此4号和五号挑一个给两个硬币拉拢即可。

综上 一号的方案 97 0 1 2 0 或 97 0 1 0 2

机器人挖矿问题

一个机器人每天可以挖到8个硬币; 50个硬币可以换得一个机器人; 问:给您5个机器人,50个硬币,10天后,最多拥有多少硬币?

参考链接

struct和union的区别

memcpy实现

要 考虑函数的鲁棒性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void mymemcpy(void* src, void* dst, int len){
// 多字节复制 要判断两个地址是不是有重叠,如果有重叠要反向赋值
if(!dst || !src) return;
int m = len / sizeof(int);
int n = len % sizeof(int);

//倒序
if(src + len > dst && dst > src){
char* p1 = (char*)(src + len - 1);
char* p2 = (char*)(dst + len - 1);
while(n--){
*p2-- = *p1--;
}
p1++; p2++; // 注意内存覆盖
int* p3 = (int*)p1;
int* p4 = (int*)p2;
p3--; p4--;
while(m--){
*((char*)p4++) = *((char*)p3++);
}
}else{
// 正向拷贝
}
}

手写一下智能指针

weak_ptr 内部实现, 可以实现观察者模式

这是因为 weak_ptr 只会增加弱引用计数,它不会影响指向的对象的生命周期,多以可以在被观察者中使用weak_ptr去指向观察者,观察者对象的释放与否都不会影响被观察者的运行结果,当被观察者需要向观察者传递消息时只需要将弱指针提升为智能指针再去访问。

解决内存泄漏….

智能指针是线程安全的嘛

引用计数是原子的,线程安全的,对共享内存的操作不是,多线程写会出现问题

Hash 怎么做成线程安全

Hash表本质是一个数组,可以直接全表上锁,但是这样锁冲突的概率很大。可以分段上锁,因为本质上 就是一个线程修改数组中的某一个元素,同时另一个线程可以修改数组的其他元素。只有当要hash扩容的时候才需要全表上锁

菱形继承

主要总结了三块儿知识点

  1. 普通的菱形继承的内存分布,比如 D: public B1, public B2, 内存模型可以看作 [B1:B 组成的一块儿内存] + [B2:B组成的一块儿内存] + [D 自己定义的一块儿内存],就是三段儿。但是这样会有二义性,比如B1里面的变量b 和 B2里面的变量b,访问就要家类型限定,来指定你要访问的是 B1 还是 B2 里面的。
  2. 虚继承的菱形继承问题:首先 虚继承就是说 子类如果定义了父类中存在的变量,那么就不再覆写,将父类的变量放在内存最后,同时在首地址需要存放一个 vbptr ,vbptr第一项指示相对起始虚表指针的偏移,第二项来指示基类变量的偏移,这样通过 vbptr可以直接跳转到基类变量上。那么菱形继承的内存布局就是 [B1:覆写B的变量和B1独有的变量] + [B2:B2覆写B的变量和B2独有的] + [D自己定义的变量] + [基类B的一份变量];也就是说 可以指定 B1/B2/B 来选择要访问谁的变量
  3. 带虚函数的菱形继承(不考虑虚继承):这种情况下 首先很重要的- 一个 D对象只能用 B1 或者 B2类型的指针去指向,而不能用 B的指针指向D会报错。会有两个虚函数表 B1的虚函数表 + B2 的虚函数表; D自己单独写的新的虚函数被放在了第一个虚表中的最后。B1虚表中有完备的 B1+B类的所有虚函数,如果D重写了就替换,同理 B2 虚表中有完备的B2+B的所有虚函数 ,这样无论是取B1指针指向D对象 还是 B2指针指向D对象,都能调用B1中所有的虚函数 或者 B2中的所有虚函数
  4. 带虚函数的菱形继承(考虑虚继承)….

参考链接 参考链接

设计模式:解释一下proactor。

const 的作用,作用于函数 的特点

  • const 作用于局部变量它存放在栈中 可以通过地址间接修改变量值 作用于全局变量 存在只读数据存储区,如果修改它的值会产生运行错误
  • const 作用于成员函数 那么 代表它不会修改所有非 static 的成员变量(实际就是 const this 指针实现);因此const 对象也就只能调用 const 修饰的成员函数

野指针,空指针的区别;内存泄漏问题;如何解决

在不知道头节点的情况下删除某个节点

C++能否在有参构造函数中调用无参构造函数,无参构造函数中如果有修改类成员会不会对当前正在构造的类产生影响,这种调用方式有什么优势或者缺点。

可以 会修改 代码复用

面向过程和面向对象各自的优缺点

内存屏障,volatile作用,是否具有原子性,使用volatile会对编译器有什么影响

可执行文件的文件格式(ELF文件格式)

C++的构造析构顺序

怎么用一个指向子类的基类指针调用基类的虚函数

(强制转换或者指明作用域d->Base::fun())

写一个字符串类的移动构造,拷贝构造,赋值构造并模拟这几个过程

快排的时间复杂度,什么时候最坏,什么时候最好

什么情况下会出现段错误,为什么段错误程序会直接终止

大文件排序问题

题目:有一个大文件,里面记录了若干数字,把这些数字进行排序。文件大小远大于内存大小

基本思想都是要归并 将大文件分成多个小文件 分别排序,然后合并的。最后合并的时候每个小文件只读入一部分,同时留一个输出缓冲区用于存储已经排好的数据。等输出缓冲区满了之后就把他存到外存然后清空输出缓冲区。

topK的问题类似,一部分一部分的读入 用堆选择topK大的数据

参考

image-20210619101337401
image-20210619101325346
image-20210619101347480
image-20210619102250262
image-20210619103938330
image-20210619105006188
image-20210619105210585
image-20210619105741668
image-20210619105924199
image-20210619111011948

系统调用

概述

系统调用就是内核提供给用户的一组接口,通过这组接口,用户可以受限地访问硬件设备或者操作系统的其他资源。通过系统调用,应用程序可以主动地从用户态 陷入 内核态,进而获得更高地权限,访问操作系统更多的资源。用户态转换为内核态的方法为:

  • 外部中断(外部输入设备中断,例如硬盘的读写)
  • 异常(缺页异常,操作异常如除0)
  • 系统调用 (后面会详细介绍)

为了保证系统的稳定和安全性,操作系统将程序运行状态分为 用户态 和 内核态。

内核态

大多数系统交互式操作需求在内核态执行。如设备IO操作或者进程间通信。(特权指令:一类只能在核心态下运行而不能在用户态下运行的特殊指令)。不同的操作系统特权指令会有所差异,但是一般来说主要是和硬件相关的一些指令。

用户态

用户程序只在用户态下运行,有时需要访问系统核心功能,这时通过系统调用接口使用系统调用。应用程序有时会需要一些危险的、权限很高的指令,如果把这些权限放心地交给用户程序是很危险的(比如一个进程可能修改另一个进程的内存区,导致其不能运行),但是又不能完全不给这些权限。

于是有了系统调用,危险的指令被包装成系统调用,用户程序只能调用而无权自己运行那些危险的指令。另外,计算机硬件的资源是有限的,为了更好的管理这些资源,所有的资源都由操作系统控制,进程只能向操作系统请求这些资源。操作系统是这些资源的唯一入口,这个入口就是系统调用。

库函数

系统调用是一组 api 但是他和库函数不同,系统调用是操作系统提供,不同的操作系统系统调用不同,而库函数是语言提供,例如C语言库函数,或者是系统库。语言库可以屏蔽不同操作系统的差异,封装出统一的接口,使得程序具有可移植性。

库函数主要由两方面提供:一是操作系统提供的;另一类是由第三方提供的(语言库函数)。

  • 系统提供的这些函数把系统调用进行封装或者组合,可以实现更多的功能,这样的库函数能够实现一些对于内核来说比较复杂的操作。比如read函数根据参数,直接就能读文件,而背后隐藏的文件比如在那个磁道,那个扇区,加载到那个内存,是程序员不必关心的问题。这些操作里面也包含了系统调用。比如write()这个系统函数,会调用同名的系统调用,来完成写入操作。

  • 对于第三方库,其实和系统库一样,只是他直接利用系统调用的可能性要小一些,而是系统提供的 API 接口来是实现。比如printf,实际上调用了write()这个系统函数。 第三方库函数大部分是对系统函数的封装。

  • image-20210614203714441

系统调用号

在 Linux 系统中每个系统调用都被赋予一个系统调用号。这样通过这个独一无二的号就可以关联到系统调用。当用户空间执行一个系统调用的时候,这个系统调用号指明到底是要执行哪个系统调用,进程不会提及系统调用的函数名称(这个在后续会用)

一个系统调用号相当重要,一旦确定,以后都不能更改,否则编译好的程序就会崩溃。如果一个系统调用被删除,它所占用的系统调用号也不能被回收利用,否则以前编译过的代码会调用这个系统调用而实际上它掉用的却是另一个。Linux有一个 sys_ni_syscall()用于作为未实现的系统调用号的默认函数,填补空缺,它只返回一个 -ENOSYS 不做任何操作。

内核通过 sys_call_table 记录所有已经注册过的系统调用列表。这个表为每个有效的系统调用指定了唯一的系统调用号。

原理

用户空间无法直接执行内核代码,他们不能直接调用内核空间的函数,更不能直接操作内核地址空间,这都是出于系统稳定全的考虑。所以应用程序应该以某种方式告诉内核自己要执行一个系统调用,希望系统切换到内核态,这样内核就可以代替应用程序在内核空间执行系统调用。

通知机制是靠软中断实现的。前面说了用户态->内核态切换的三种方式,外中断 异常 和系统调用,系统调用是一种特殊的中断类型(软中断)

在x86的机器中,用一个8bit的数字(0~255)来区分各种中断,这个数字被称为中断向量(vector)。其中一个中断向量,即128 (0x80),专门被用于执行系统调用。在Linux系统中,存有一个系统表,叫做Interrupt DescriptorTable (中断向量表),简称IDT。IDT表共有256项,存放了从中断向量到相应处理例程(interrupt or exceptionhandler)的映射关系。当某个中断发生时,CPU从IDT表中查找到相应的处理例程的地址来执行。而第128号中断处理程序 即 system_call()

确定系统调用

通过上述可以看出,所有的系统调用都是使用相同的方式陷入内核,即触发 128 软中断进入内核,根据中断向量表找到 system_call 函数来处理具体的系统调用。

但是仅仅是这不够,用户还得告诉内核调用的是那个系统调用函数,因此,还要将系统调用号写入 exa 寄存器,即通过exa寄存器告诉具体的系统调用号。system_call()函数通过将给定的系统调用号与 NR_syscalls 做比较检查有效性,如果有效 就通过 系统调用号 找到对应的 系统调用函数的地址。

参数传递

找到了具体的系统调用函数,通常系统调用函数还有输入输出参数。所以,在发生陷入的时候,应该把这些参数从用户空间传给内核,简单的方法是和系统调用号一样,把这些参数也存放在寄存器中。在x84-32系统上,ebx ecx edx 和 edi 寄存器存放前五个参数,需要六个以上的参数,应该通过一个单独的寄存器存放指向所有这些参数在用户空间的地址。

给用户的返回值也通过寄存器传递,在x86系统上,它存放在 eax 寄存器中

image-20210614221021493

系统调用流程

当系统调用发生时,通过中断机制,系统调用例程system_call被调用。它的执行过程大概分为4个步骤:

  1. 从寄存器中取出系统调用号和输入参数,然后将这些寄存器的值压入kernel栈中。
  2. 根据系统调用号查找系统调用分派表(system call dispatch table),找到系统调用服务例程(一个内核函数)
  3. 调用查到的系统调用服务例程。
  4. 将系统调用服务例程的返回值出栈,重新保存在寄存器中。

上面描述的系统调用例程system_call在kernel空间中执行。在执行前,系统调用号和输入参数已经存入了寄存器,这个存入过程由user空间的代码完成。实际上,如同第一节所讲,每个真正的系统调用基本上都有一个封装它的库函数,一般是在这个库函数中完成系统调用号和输入参数的保存动作。当系统调用例程system_call执行完毕后,返回值通过寄存器再传回user空间的库函数。

新增一个系统调用

….

字节视频算法实习

知道的分类网络

mobile-net-v3的结构

GAN有没有采用特殊的训练技巧

知道的注意力机制

了解过transformer嘛

根据mask的分割,还问了分割算法

知道的图像去噪方法

知道的图像模糊的方法

双边滤波的原理

代码C++实现的时候用了什么

开闭操作

对图像增强这项任务的理解

和其他 low-level 网络结构有啥区别 在训练数据重组的情况下 有什么区别

包含正数和负数的数组,求乘积最大连续子数组

丢鸡蛋问题

腾讯后端实习

重载和继承

知道哪些智能指针

线程和进程区别

知道进程通信的方法嘛,线程通信呢

了解linux哪些锁

TP 图像优化岗

怎么解决过拟合

预编译的作用

图像处理流程了解嘛

回答 ISP

用了什么设计模式

红外细节增强项目候遇到过什么问题

我完成这个任务的流程是,先在Matlab上验证算法的效果,然后确是可行后再转为C++实现,因此遇到的比较难搞的问题还是Matlab算法调试阶段时候效果出不来。比如前面细节层拉伸后光晕问题,产生分析和解决,还有比如细节层调整,之前都是设置固定参数,但是很难用一套参数处理好,才想到用图像熵做个参数自动搜索。最后落地实现,都是一些代码编写的问题印象不深了。

拉普拉斯金字塔存储的是什么

对于目标检测识别深度学习能都替代传统方法

不太了解检测识别 直接围绕回答的增强

项目里的提亮效果如何评价

因为前景不对齐,所以主要还是在测试集上定性观察。其次在测试集上手工将运动区域过滤掉然后定量对比背景的相似度,对于运动的前景由于不对齐可以采用类似NIQE LOE这种无参考图像质量评价的标准,但是最后一个评估方法当时并没有尝试,因为虽然一些常规得指标是普适于大规模的自然数据集 但是应用于这个任务中这个指标的评价效果是否可靠也不一定。

怎么组织团队合作的

不同人的代码风格不一样怎么办?

深度学习方法,怎么确定一个模块的效果?

当时说了个啥奇奇怪怪的词没听懂

概率题 红蓝车撞人,目击正确的概率

验证毒药的问题

总结

要重新组织一下语言 项目详细介绍的时候脱稿 不然回答的逻辑很混乱,方法介绍要简化一点。

觉得自己做的比较好的项目

怎么做研究

怎么较好的完成项目

研究问题的思路

红外图像和RGB图像的特性

红外成像原理 红外成像设备是通过探测周围环境的热辐射特性,将它以图像的形式显示出来。特点:

  1. 灰度集中 对比度差
  2. 噪声多样性,一方面是动态噪声 如 热噪声 散粒噪声,光子噪声 形成机理复杂;另一方面是各探测元对相同辐射的反应具有不一致性,在红外图像上会产生固定的模式噪声,这种噪声是稳定的。现阶段高动态范围的红外设备也越来越多,这类红外设备有更高的灵敏度可以探测更小的热量差异,每个像元有2^14 2^16个灰度级,而现阶段普通设备….需要压缩动态范围

YUV 图像格式和用处

图像HDR

为什么选NLTV算法

https深入

怎么和他人合作

印象最深刻的事儿

形态学滤波

腐蚀

操作过程:构建一个核,并指定核的锚点,用该核与图像卷积,计算核覆盖区域的像素点最小值,将其作为图像在锚点位置的像素值 效果:使暗的更暗

image-20210609200614322

膨胀

操作过程:构建一个核,并指定核的锚点,用该核与图像卷积,计算核覆盖区域的像素点最大值,将其作为图像在锚点位置的像素值 效果:使亮的更亮

image-20210609200712018

开运算

先腐蚀后膨胀,消除小物体和小连接点

闭运算

先膨胀后腐蚀,连接缝隙,填充小黑洞

形态学梯度

基本梯度:膨胀的图 - 腐蚀的图

内部梯度:原图 - 腐蚀的图

外部梯度:膨胀的图 - 原图

效果:保留物体边缘轮廓

顶帽

原图 - 开运算 (这个内部梯度的区别,内部梯度只是腐蚀,因此物体的轮廓会小一圈,而开运算是去掉了小的点,原本物体的大小不会变化因为他是腐蚀+膨胀)

获取:获取图像小区域,例如比较小的背景,会被开运算去掉,再用原图一减提取的便是背景

image-20210609201359570

黑帽

闭运算 - 原图 闭运算突出了原本暗的区域,或者连接的空区域,那么黑帽 就是 凸显了暗区域

image-20210609201609775