c的优化

step1 对c++ 代码的优化

  1. 从算法逻辑上优化,整体调用逻辑,何时准备什么类型的数据。还有算法层面:比如滑动窗口和等。

  2. 对数据精度进行优化,例如double能变成float 的,float能拉伸成整数的。必要时标明数据的实际范围(0~10bit)这种。

  3. 对除法能被乘法代替的,乘法能用加法替代的,乘法能用位移替代的运算进行优化。

  4. 对kn ,(k+1)n这类的运算也可以优化。

  5. 减少if else分支语句内的内容,能合并的合并

step2 c写汇编

  1. 首先原则是 利用函数指针,当运行c的时候就指向c,当运行汇编时就指向汇编

  2. 汇编加速的原理是利用单指令多数据的方式进行加速

  3. 确定输入输出的尺寸,是固定还是任意,是否是偶数,8对齐 16对齐等

  4. 先把c转化成利用flag标识if else的语句。

  5. 确定每一个变量在其生命周期中,数据范围,可并行范围,能并行运算并行,不能时再拆开

  6. 根据运算的并行度,将常数都定义好 是u8 u16 还是u32 还是有符号的s。

  7. 如果大部分操作都可以在int16x8上操作 那就一次读8个 如果大部分操作都能在uint8上操作 那就一次读16个 ,以此类推。

step3 汇编注意事项

  1. 有些运算涉及负数,需要用有符号运算s

  2. 有些数据必须用uint 才能装下

  3. 作为if 条件标识的flag 再需要扩占位数时,需要用s有符号,才能补全高位

ARM Neon Intrinsics 指令相关文档:

https://blog.csdn.net/u012385733/article/details/121086734

https://developer.arm.com/architectures/instruction-sets/intrinsics/https://developer.arm.com/architectures/instruction-sets/intrinsics/

函数与汇编

X86 X64 arm64

arm64 精简指令集

X86 复杂指令集 32位

X64 复杂指令集 64位

寄存器

向下兼容

应用程序地址空间

操作系统通过虚拟内存的方式为所有应用程序提供了统一的内存映射地址。如图3所示,从上到下分别是用户栈、共享库内存、运行时堆和代码段。当然这个是一个大概的分段,实际分段比这个可能稍微复杂一些,但整个格局没有大变化。

函数调用及汇编指令

函数调用主要是call和ret。汇编语言的call指令相当于执行了2步操作,分别是,1)将当前的IP或CS和IP压入栈中; 2)跳转,类似与jmp指令。同样,ret指令也分2步,分别是,1)将栈中的地址弹出到IP寄存器;2)跳转执行后续指令。这个基本上就是函数调用的原理。

除了在代码间的跳动外,函数的调用往往还需要传递一个参数,而处理完成后还可能有返回值。这些数据的传递都是通过寄存器进行的。在函数调用之前通过上文介绍的寄存器存储参数,函数返回之前通过RAX寄存器(32位系统为EAX)存储返回结果。

函数的参数是从右往左传递的。

另外一个比较重要的知识点是函数调用过程中与堆栈相关的寄存器RSP和RBP

RSP(32位叫ESP):栈指针寄存器(reextended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。

RBP(32位叫EBP):基址指针寄存器(reextended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

可以看出函数内,局部变量的地址是越来越低的。rsp栈顶

到运行到call Blur时,先将main函数的栈底(高地址)push rbp。

并且将main函数的栈顶rsp作为新调用函数blur的栈底 mov rbp  <- rsp。

然后把传进来的参数放在新的栈顶(rbp - 112) mov DWORD PTR [rbp-112], r9d

把函数内的局部变量放在栈底并且向上生长(rbp-8)int* pA1 = pSrc - strideS*2; mov QWORD PTR [rbp-8], rax

函数语句以及汇编指令

基本指令
指令 指令 耗时latency Rthroughput
sub eax, 5 寄存器 - 常数 1 0.25
mov eax, dword ptr [rax] 读 32位 5 0.5
mov rax, qword ptr [rbp - 8] 读64位 5 0.5
mov qword ptr [rbp - 24], rax 写64位 1 1
jmp .L3 1 0.5
add     rax, rdx 寄存器相加 1 0.25
neg rax 取反 1 0.25
shl rax, 2 左移 1 0.5
cdqe 它用于将一个32位有符号整数(存储在 EAX 寄存器中)扩展为一个64位有符号整数(扩展到 RAX 寄存器中) 1 0.25
lea   rdx, [4*rax] “Load Effective Address”  lea destination, source 1 0.5
C++ 语句对应指令
int f = pA1[w]; mov eax, DWORD PTR [rbp-48] 读w的地址 5
cdqe 32eax 64位rax 1
lea rdx, [0+rax*4] 读w地址对应的数据w 1
mov rax, QWORD PTR [rbp-8] 读pA1的地址 5
add rax, rdx 地址偏移w 1
mov eax, DWORD PTR [rax] 读pA1[w] 地址对应的数据 5
mov DWORD PTR [rbp-68], eax 写到f里 1
int c =pA1[w] + pA1[w - 1]; mov eax, DWORD PTR [rbp-48] 读w的地址 5
cdqe 32eax 64位rax 1
lea rdx, [0+rax*4] 读w地址对应的数据w 1
mov rax, QWORD PTR [rbp-8] 读pA1的地址 5
add rax, rdx 地址偏移w 1
mov edx, DWORD PTR [rax] 读pA1[w] 地址对应的数据 5
mov eax, DWORD PTR [rbp-48] 读w的地址 5
cdqe 32eax 64位rax 1
add rax, 1 地址w+1 1
lea rcx, [0+rax*4] 读w+1地址对应的数据 1
mov rax, QWORD PTR [rbp-8] 读pA1的地址 5
add rax, rcx 地址偏移w 1
mov eax, DWORD PTR [rax] 读pA1[w] 地址对应的数据 5
add eax, edx add两个数字 1
mov DWORD PTR [rbp-56], eax 1
sum =
(sum * 327)>>13;
mov eax, DWORD PTR [rbp-92]
imul eax, eax, 327
sar eax, 13
mov DWORD PTR [rbp-92], eax
sum = sum / 25; mov eax, DWORD PTR [rbp-92]
(在sum最大范围数据类型最大精度下优化算法) movsx rdx, eax
imul rdx, rdx, 1374389535
shr rdx, 32
mov ecx, edx
sar ecx, 3
cdq
mov eax, ecx
sub eax, edx
mov DWORD PTR [rbp-92], eax
pBlur[w] = static_cast(sum); mov eax, DWORD PTR [rbp-48]
cdqe
lea rdx, [0+rax*4]
mov rax, QWORD PTR [rbp-104]
add rdx, rax
mov eax, DWORD PTR [rbp-92]
mov DWORD PTR [rdx], eax
for (int h = 0; h < height; h++) mov DWORD PTR [rbp-44], 0
jmp .L2
L2:
mov eax, DWORD PTR [rbp-44]
cmp eax, DWORD PTR [rbp-128]
jl .L5

https://godbolt.org/

积分图和滑动窗口

d*d 模糊 filter

对原图做r*r 的均值模糊 以循环方式书写运算次数为 (d * d) * (HW),优化目标:运算次数和半径无关

积分图

step 0 该padding还都得padding,d = 2r + 1。那原图需要padding 成 (h+2r)* (w+2r)
step1 计算积分图 积分图在padding 的结果上还需要再加一行一列 (h+2r+1)* (w+2r+1),因为积分图每一点xy表示的是x之前 y之前那个矩形的和。如下图 绿色面积表示padding后的图像,蓝色为积分图增加的内存,xy点的值就是蓝绿色面积内的和
step2 filter 对一个点(m,n) 进行3*3的boxfilter为例,需要在积分图上进行 1 + 4 - 2 - 3操作

滑动窗口

step 0 该padding还都得padding,d = 2r + 1。那原图需要padding 成 (h+2r)* (w+2r)
step1 计算d行和,申请1*w+2r 大小的空间,存储当前位置d行和。

step2 由行和先计算第一个窗口和,代表00点的窗口和, 随后每次减去最左边的d行和 加上最右边的d行和
step3 继续向下滑动,先通过减第一行加下面一行的原图更新d行和,再重复计算窗口和步骤

blur和sharpen的探索

boxfilter and gaussian 产生的模糊和锐化核

1 模糊核对比 boxfilter and gaussian

结论1

相同尺度的boxfilter比gaussian更加平均

2 boxfilter关于sharpen的探索

boxfilter and gaussian关于sharpen的探索

结论2

假设3*3 的模糊核执行一次在最高频,第二次重复执行作用于次高频,每次重复执行作用于更低频段。

•sharpen核包含的频段越多,sharpen kernel范围越大,中心区域的正权重越多

•sharpen核截取的频段越高,中心的权重越大,视觉看起来越明显

•sharpen的最高频层决定了其kernel中心正权重的范围(1x1 or 3x3)

•boxfilter产生的模糊核比gaussian更模糊一些

•boxfilter产生的sharpen核比gaussian更锐利一些

3 将sharpen kernel用于图片

结论3

•锐化核中心正权重的范围大小反应了detail 层的粗细

翻译《The Retinex Theory of Color Vision》

The Retinex Theory of Color Vision

读后感

文章主要声明的关键点是在不同的,多种多样的环境下,我们看到的世界颜色是恒定的,亮度也是恒定的(总能认出白色和灰色和黑色,总能认出红色黄色蓝色,哪怕环境光很暗,哪怕环境光很红)

因此文章先证明,在没有颜色的情况下,人眼对白色灰色黑色亮度的认知不取决于到达人眼的绝对辐射能量,而是取决于反射率。lightness

随后文章证明人眼对颜色的认知取决于三种颜色的反射率的比值,也不取决于绝对腐蚀能量。

这是理想模型,其实人眼看到的亮度本来就受不均匀光照影响,在黄色灯光环境下看东西就是有点偏黄。相机拍摄到的rgb数值是绝对能量(白平衡过后的)。所以说绝对能量有一定价值,如果色偏或者亮度差异严重可以用反射率(与周围环境对比)这个思路矫正。

翻译方法:

尽量忠于原文的翻译,再加一些理解的理解

正文

简化实验条件的科学传统让我们直到最近,都没能得出一个人眼怎样感知世界的合理结论。矛盾的是,现代摄影技术强化了:被牛顿分解到光谱上的色彩和真实世界的色彩差不多是同样的,这个观念。例如,我们知道,如果我们使用日光色胶片,在普通钨丝灯的光线下拍摄照片,照片会出现强烈的偏红现象。我们说,这是因为钨丝灯的光线太 “红 “了。我们从来没有问过,为什么我们自己可以不断地在钨丝灯和外界下自由移动,而不会感受到熟悉的物体颜色的变化:苹果、柠檬、草莓、面包、人脸(这些色调在电视屏幕上很难得到正确达)。

那么,眼睛是如何处理钨丝灯房间里过多的 “红色 “的呢?正如我希望在这篇文章中所证明的那样,眼睛在判断颜色时,从来没有觉察到额外的红色,**因为它不依赖于到达它的绝对辐射能量(flux of radiant energy)**。眼睛已经进化到,在各种不可预测,不均匀,移动的光线环境下, 看到世界上不变的颜色,眼睛如何实现这一非凡的壮举,多年来一直让我着迷。

1959年,我在这里讲述过 ’红白实验‘。 这个实验使用两个黑白胶片叠加投影,一个胶片经过红色滤镜投影,另一个经过透明滤镜投影。而两个色彩叠加后,我们几乎能看出原本的所有色彩。为了产生‘红白图片’ 需要两张胶片,其中胶片A是通过红色滤镜拍摄下来的,这张胶片被照亮后,经过红色滤镜,剩下的光只有红色部分。并且这个红色的强度也代表了本身场景下红色分量的强度。而另一张胶片B是通过绿色滤镜拍下来的。被照亮后,经过透明滤镜,剩下的白光代表了真实世界里绿色分量的强度。理论上,在叠加投影后,我们应该只能看到红色,白色,和粉色。但实际上,我们可以看到几乎有全部色彩的图像。在红白照片的投影中,辣椒是绿色的,萝卜和草莓是红色的,橙子是橙色的,柠檬和香蕉是淡黄色的,木制砧板和刀柄是棕色的,盘子上的图案是蓝色的。(是不是 红色的地方现实红色是因为它有A本身的红色分量,也就是比周围的点多红色分量,绿色的地方现实绿色,是因为有B的绿色分量,蓝色的地方显示蓝色是因为它即没有多反射红色,也没有多反射绿色,甚至纯蓝和黑色一样(为什么不是黑色 或者是蓝绿色波长接近?可是也不接近啊))

早期的黑白实验带来的挑战使我们在20年的时间里逐步解释了视觉系统如何能够从我们周围的世界中提取可靠的颜色信息。在这个世界上,几乎每个场景的照明都是不均匀的,落在场景上的辐射的光谱组成变化很大,而且像闪电一样短暂的照明就足以准确识别颜色。如果就像我们在学校里学的,眼睛的光度感受器是通过感受三原色不同的绝对峰值来感受颜色的话,那我们就无法准确识别颜色。例如一个点在某个环境中的RGB可能是10,5,5,但是实际上这个点不一定是红色,换一个环境这个点的RGB可能就变成10,10,10了。实际上,物体在各种环境下都保持了色彩一致性,这是一个认识色彩的本质。

视觉色素是对广泛的光频段有反应的感光分子。视网膜三种色素锥体细胞(cone cells)可以感受到宽广、且有重叠的可见光谱。在波长440纳米处具有峰值灵敏度的色素在某种程度上对整个可见光谱的低频部分作出反应。另外两种色素对几乎三分之二的可见光谱作出反应,这两种色素的峰值几乎偏离30纳米,其峰值灵敏度位于535和565纳米。(下图中,实线表示人眼感觉,虚线表示胶片模拟的感光性)

只有我们的眼睛能对物体的颜色进行分类;分光光度计则不能。这一点十分重要,因为许多人在第一次观看我们的一些实验时,会将一些东西识别为红色或绿色,但随后会问,好像他们的眼睛被愚弄了一样。”这到底是什么颜色?”答案是,眼睛没有被愚弄。答案是,眼睛并没有被愚弄,它的功能完全是它必须以非自愿的在一个被不断变化和不可预测的辐射能量流照亮的世界中看到恒定的颜色。也就是我们看到什么颜色,它就是什么颜色,不用怀疑。

我认为彩色图片的实验应该从简单的无色彩的图像开始,让我们从细节说起这个实验。视网膜上超敏系统中:杆状rod 细胞比锥状cone细胞,能在光照水平上弱1000倍的环境下工作,因此有可能回答一个有趣的问题:如果只有杆状细胞被激活,那我们能看到什么颜色。一个实验办法就是戴上减光眼镜,降低30000倍的入射光强度。当一个人戴了一个半小时后,房间里20英尺烛光水平的物体就会变得可见。此时房间内的光照水平是1/1500 foot-candle. 当我们环顾四周,可以看到黑白的物品。很像是用一个绿色的滤镜拍下的黑白照片那样。换言之,原本红色的物体看起来很暗,绿色的稍微亮一些(rod敏感段接近绿色?),蓝色的很暗,白色的物品亮,黑色的物品很暗。

这些黑白图像是由对不同波长敏感的胶片拍得的。左上角的图像是模拟cones细胞对长波观察到的图像,右上角是中波激活图像,左下角是cone对短波的观察。右下角是rod细胞看到的世界。

在这个没有色彩的世界里,物体的nature不是由到达眼睛的入射流量决定的。一个一直看起来很暗的区域,可以被安排更多的flux,但依旧看起来很暗。无论该区域是真实3d世界还是二维图像。都会被视觉重新分配亮度。也就是一张纸,无论它处于比较暗还是比较亮的环境区域里,它看起来本身的亮度变得不多。如果纸片本身比较亮那么无论在什么环境它都看起来比较亮,如果纸片本身比较暗,那么在什么环境它看起来也都比较暗。哪怕在这两种情况下,实际到达我们眼镜的光照流量都相同。

此外,在一个由不同亮度大小和形状的区域组成的错综复杂的拼贴画中,一个特定元素的亮度不会因为它被重新安置在拼贴画的任何部分而发生明显改变。并且在不同的环境下亮度也不会显著变化。当一个小区域被一个大区域完全包围时,小区域的亮度会有一些变化,这取决于大区域是比小区域更暗还是更亮。然而,一般来说,令人印象深刻的事实是,一个特定区域的亮度不会被紧邻的区域明显改变,也不会被周围更大的区域所改变。

虽然上面的实验都是在rod工作下的世界,但是在正常光线下也有同样的效果。例如,如果在蒙太奇的一侧放置一张哑光表面的黑纸,或者更好的是黑色天鹅绒,而在几十米外的另一侧放置一张白纸,中间散布着各种各样的明暗纸。我们可以在靠近黑色方块的地方放置一个强大的光源,使它比远离光线的白色方块向眼睛发出更多的辐射能量:但黑色方块将继续看起来是黑色的,而白色方块则是白色。事实上,在蒙太奇仍然从一侧强烈照射的情况下,无论是黑色方块还是白色方块,都可以被移到蒙太奇的任何其他部分,而不会对其外观产生明显的变化。

人眼可以不基于绝对flux流量识别亮度的能力在以上实验里被很好的证明了,同样color vision也是基于此建立的。视觉系统对于外界的第一个反应是吸收落在视网膜上的光。虽然接收器对于光强的第一反应是与入射强度成线性关系的,但最后视觉系统的理解是‘lightness’,这个lightness和直接的光强关系不大。

处理光通量以产生亮度的过程可能发生在视网膜或大脑皮层,或部分发生在两者之中。由于我们不确定调解这些过程的机制的位置。我创造了一个术语retinex(视网膜和大脑皮层的组合)来描述将流量转换为光感模式的生物机制的集合。因此,我将在本文中使用这个术语来指代这些生物机制。我还将保留lightness这个术语,以指由生物系统产生的感觉。虽然棒状体可以在低于锥体阈值的光强度下受到刺激,但如果不刺激棒状体,就不能刺激锥体。对于锥体,我们必须用视网膜摄影法研究每一组受体产生的亮度图像,或者从基于光谱测量的模型计算中了解亮度图像的特性。

上图中视网膜感受器的阈值反应有很大的不同。杆状超感光系统在比激活锥体系统所需的光线水平弱1000倍左右的情况下提供视觉。在作者的实验室里已经证明,当多色光在调整到只有杆状系统和一个锥状系统(长波系统)有反应的光照水平下观看时,多色光几乎表现出它们的正常颜色范围。

到目前为止论文声明的观点有

  • 亮度比色彩需要的光照(flux radiance)更少;
  • 物品的亮度和所处环境关系不大。虽然如果一个小块被另一个亮度的大块完全包围了,那么小块的亮度还是会受一定影响。一个灰色块在暗一点的环境下还是灰色的,亮一点的环境下还是灰的。但是如果被黑色包围,可能会显得亮一点,如果被白色包围可能会显得暗一点。;
  • 物品的色彩也和所处环境关系不大,在钨丝灯环境下,世界也不会看起来太红。在只有红色和白色光组成的图像里,我们仍占能识别出不同颜色。

P4尾部继续翻译

现在,我们知道了当极低光照下,一个独立的杆状系统为我们提供黑白视觉,那么这个杆状系统和一个cone锥状系统一起工作过会是怎么样的呢?

McCann和Benton用550纳米的窄波段光点亮了一个彩色显示器(显示器主动发射光线)。然后,他们在656纳米处添加了第二个窄波段的照明剂,并调整其水平,使之刚好足以激活长波受体系统,而不是中波系统。在这些条件下,只有两个受体系统,即杆状体和长波锥状体,接受了足够的光来发挥作用。

在以上限制的实验中,图像显示出了多种范围的颜色,观测者可以给出每一个区域的正确色彩名称(虽然感官和正常光照下有所不同)。这个结果让人想起了红白系统产生的多色图像。这个结果让人确信了我们之前的猜想:由两个波段不同接收器接收的lightness 信息不会被平均。我们知道杆状细胞和单独一个长波接收器具不会产生色彩感知,但是当它们结合在一起,我们可以分辨出很多种颜色,比如红色黄色棕色等。

那么到底是什么导致了颜色呢?颜色的出现是由于两个不同波段接收器,感知的lightness光线的对比。上述实验让我们合理猜想:我们先独立感知短波中波长波产生的lightness,然后这三种波段lightness的对比产生了颜色。也就是说,因为颜色取决的每个波段感知到的lightness,而不是flux。因此一张图的色彩和ratio of the three fluxes 是无关的。(核心意思就是一个点RGB通亮是1,3,1,但它不一定是绿色,因为颜色和直接通亮比是无关的,是和lightness有关的。而lightness不是绝对通量,而是物体反射能力)

到目前为止有了猜想

正如我们所看到的,视觉色素的光谱敏感度有很大的重叠。如果我们用单一视觉色素敏感的全部波长范围来照亮一个场景,我们会看到大量的颜色,因为不止一个视网膜系统会做出反应。然而,在滤镜和适当的胶片乳剂的帮助下,我们可以分离出lightness,而不是色彩。我们把为此目的制作的黑白照片称为视网膜记录。

这种摄影技术利用银色乳剂,发挥了两种功能。首先,该系统提供的光谱敏感度与视觉色素的敏感度相同。其次,它产生黑白照片供人类观察者检查。正是人类的视觉系统将沉积在银中的摄影图案转换为亮度。理想情况下,我们应该让观察者只用一组感光锥体cone,来看黑白图案,并报告适合这组锥体的亮度。然而,在黑白图案的任何一点上,该点对可见光谱不同波段的反射率基本上是相同的。因此,对于黑白照片,我们用同样的信息来刺激所有的受体(三种cones和rod),也就是说,用单一视觉色素所吸收的能量来刺激它们。如果我们假设所有的反射系统都以相同的方式处理信息,我们可以提出,将这些相同的信息发送到几组受体上,就等于只将其发送到一个受体上,从而使我们能够看到如果有可能将其分离出来的图像是什么样子。(翻译成人话就是,理想情况下,我们想看一看如果人只长了一种cones,看到的世界是什么样的。于是我们就用改种波段敏感的胶片拍摄出银离子底片,由于1:这个银离子底片对自然界各个波段的光的反射率都差不多,2: 假设不同cones处理信息的逻辑相同,因此以同样信息将三个都激活了和只激活一个效果一样。毕竟真实人都长了三种cones。因此 银离子底片本身的密度反应了被拍摄物体该波段范围的反射值,再统一由肉眼直接看到的黑白图像就可以代表单独接收器看到的图像了)

描述了为什么可以用胶片模拟视网膜单一cone

在第3页,读者将看到三张黑白照片,这些照片是通过模拟三种锥状色素反应的retinex滤镜拍摄的。例如,草莓和萝卜在长波记录中是亮的,在中波记录中是暗的,在短波记录中是最暗的。尽管橙子和柠檬在短波记录上与草莓和萝卜一样深,但它们在中波记录上几乎与长波记录一样浅。在印刷品上,这些区别是微妙的。对于观看实际全彩场景的眼睛来说,这些微妙的区别提供了区分各种颜色的无数色调所需的所有信息。

当一个区域的三种亮度被三个视网膜系统确定后,就不需要进一步的信息来描述视野中任何物体的颜色。任何特定的颜色都是对三种特定亮度的报告。对于每一个光亮度的三重奏,都有一个具体而独特的颜色。

由于彩色摄影的局限性,我们不可能向读者展示在我们的实验室里很容易完成的演示,而这些实验极大地揭示了感知的颜色与到达眼睛的通量的独立性。读者将看到的是两块四英尺半见方的木板,上面覆盖着大约100张不同颜色和形状的纸。为了尽量减少镜面反射的作用,这些纸的表面是哑光的,除了黑色以外,在可视光谱的任何部分都有至少10%的反射率。在这些展示中,我们称之为 “彩色蒙德里安”(以荷兰画家的作品命名),纸张的排列方式是,每张纸周围至少有五到六张不同颜色的纸张如下图。

“彩色蒙太奇 “实验采用了两张相同的彩纸,安装在四分之一英尺见方的板上。这些彩纸有一个哑光的表面,以减少镜面反射。每个 “蒙德里安 “都用它自己的三台投影仪照明,配备了带通滤波器和独立的亮度控制,这样长波(”红”)、中波(”绿”)和短波(”蓝”)照明就可以按任何需要的比例混合。一个望远镜光度计可以对准任何区域,测量来自该区域的通量,一次一个波段。光度计的读数被投射到两个显示器上面的刻度上。在一个典型的实验中,可以调整照明器,使左边Mondrian中的白色区域和右边Mondrian中的绿色区域(或其他区域)都向眼睛发送相同的三联辐射能量。由于色彩复制的局限性,实际的辐射能量通量无法在此重现。在实际观看条件下,白色区域看起来是白色的,绿色区域看起来是绿色的,即使眼睛从这两个区域接收到相同的三联体辐射能量。

在蒙德里安实验中,眼睛上相同的能量流提供了不同的色彩感觉。在这个例子中,来自长波、中波和短波照明器的照明物按指示进行了调整,一个看起来是红色的区域继续看起来是红色(左边),一个看起来是蓝色的区域继续看起来是蓝色(中间),一个看起来是绿色的区域继续看起来是绿色(右边),即使所有三个弧线都向眼睛发送相同的长、中、短波能量的三联体。同样的三联体可以来自任何其他区域:如果该区域是白色的,它仍然是白色的;如果该区域是灰色的,它仍然是灰色的;如果它是黄色的,它仍然是黄色的,以此类推。

每一个相同的Mondrians都被它自己的三台投影仪所照亮,这些投影仪配备了锐利的切割带通滤波器(不是视网膜滤波器):一台在670纳米,包含一个长波段,一台在540纳米,包含一个中波段,一台在450纳米,包含一个短波段。每个照明投影仪的光量是由一个单独的可变变压器控制的。此外,照明投射器有同步的螺线管激活的开关,以控制照明的持续时间。有一个伸缩式光度计,可以精确地瞄准蒙德里安的任何区域,以测量从任何一点反射的辐射量,从而测量到达眼睛的通量。光度计的输出被投射在蒙德里安上方的刻度上,参加演示的人可以看到它。

演示开始时,三台照明投影仪在左边的蒙德里安上打开;右边的蒙德里安保持黑暗。变换器被设置成使左边的蒙德里安的整个纸张阵列都是深色的,同时白色是良好的白色。这个设置并不关键。然后,每次使用一台投影仪,因此每次只使用一个波段,我们用望远镜光度计测量从某个特定区域,例如一个白色矩形区域到达眼睛的能量。来自白色区域的读数(以每立体平方米毫瓦为单位)是65单位的长波光。30个单位的中波光和5个单位的短波光。我们现在已经确定了与白色感觉有关的三种能量。

我们关闭照亮左边蒙德里安颜色的三台投影仪。在右边我们只打开长波投影仪。我们选择一个未知颜色的不同区域,调整长波光,直到从所选区域进入眼睛的长波能量与刚才从左边蒙德里安的白纸上传来的长波能量相同,即65units。我们关闭长波投影仪,并分别调整控制中波和短波投影仪的变压器,一个接一个,以便从这些选定的区域发送到眼睛的能量也与来自左边白色区域的能量相同。我们还没有同时打开所有三个光源,但我们知道,当我们这样做的时候,从选定区域到达眼睛的仍是未知颜色的三组能量将与之前产生白色感觉的三组能量是一样的。

当我们打开三个照明灯,我们发现右边的蒙德里安的区域是绿色的。我们现在将左边的蒙德里安以其照明物的原始设置进行照明,这样两个蒙德里安就可以同时被观看。左边的白色区域看起来仍然是白色的,右边的绿色区域看起来仍然是绿色的,但两者都向眼睛发出了相同的三倍能量: 65. 30,5.我们关闭两个蒙德里安的照明,在左边的蒙德里安选择一些其他区域,并依次调整从那里到达眼睛的能量,使它们与最初引起白色感觉的能量相同,也引起了右边蒙德里安的绿色感觉。当我们打开照亮左蒙德里安的所有三台投影仪时,我们看到这一次所选择的区域是黄色。到达我们眼睛的三组能量就是之前产生白色和绿色感觉的那组能量。同样,如果我们愿意,黄色和绿色可以同时观看,黄色在左边,绿色在右边

我们可以用其他区域继续演示,如蓝色、灰色、红色等等。戏剧性地证明,颜色的感觉与反射率与光照的乘积,即能量无关,尽管该乘积似乎是蒙德里安人的各种区域到达眼睛的唯一信息。为了证明这些实验中的色觉并不涉及视网膜色素的广泛色度适应(extensive chromatic adaptation),投影仪配备了同步快门,以便可以在短暂的闪光中观看Mondrians,即十分之一秒或更短的饱和度。无论观察的时间有多短,演示的结果都不会被改变。因此,我们可以说,无论是色度适应还是眼球运动,都没有参与产生观察到的颜色。最后,蒙德里安颜色设计的本质是为了避免周围区域的形状和大小、物体的熟悉度和颜色的记忆的重要性。奇怪的是,人们不时地试图引用所谓的颜色恒定性来解释这些演示。显然,颜色恒定性只是本文主题的显著能力的一个紧凑的名称。(省略一些)

现在让我们用三台投影仪中的一台,例如提供长波光的投影仪,来照亮彩色的蒙德里安阵列,并观察增加和减少光通量的效果。我们观察到,各个区域保持着恒定的亮度顺序。然而,如果我们将照明切换到一个不同的波段,例如中波段,许多区域的亮度将发生变化:100个左右的区域中的许多区域将占据一个从最亮到最暗的不同等级。在短波波段的照明下,将有第三个等级顺序。具体来说,一张红色的纸在长波光下会被看作是亮的,在中波光下是暗的,在短波光下是非常暗的。另一方面,蓝色的纸张在短波光下会很亮,在中波和长波光下都很暗。其他颜色的纸张会表现出不同的三组亮度。当我们在20年前进行这样的实验时,我们不可避免地得出了这样的结论:triplets of lightnesses. 提供了我们所需要的一组恒定值,成为识别颜色的因素,与本身流量无关。

到目前为止论文声明的观点有

  • 人眼感知的亮度我们成为lightness,辐射的能量成为flux
  • 物体的颜色和三种波长flux到达眼睛的比例无关,而是以每种颜色被转换成lightness后,混合的比例有关。
  • 在两块MONDRIAN” 中,即使人眼收到的triplets of flux 相同,认知到的颜色仍然是不同的

接下来开始探索怎么感知到lightness

很明显,一张纸片的lightness和它反射到我们眼睛里光的总流量是有关的。我们现在通过实验验证,这个反射的光流量flux是怎么一步步被我们感受成lightness的,再最终变成色彩的。

我们对观察者用带有17种不同颜色区域的简化颜色蒙德里安Mondrian的反应,进行了系统研究。我们让受试者在不同的光线下,将17种颜色的板子的每一块与标准光照下标准参考书“Munsell Book of Color” 去对应。如下图所示

Mondrian的lightness被调节了五组光照用来保证五种不同的颜色(灰色,红色,黄色,蓝色,绿色)反射到眼睛的flux是一致的。一开始,观察者在Munsell书里找各种小片,与某种光照下的Mondrian 中17中颜色对应。之后,调节对Mondrian的光线,使其红色部分的反射flux与刚刚情况下灰色块的反射flux相同。这时再次让受试者在Munsell书里寻找与之对应的17块色彩。之后黄色蓝色绿色依次进行。

观察Munsell书时使用的恒定照明是三种波长的照明剂,观察者认为它们能产生 “最佳 “白色。从Munsell书中最白的纸张到达眼睛的实际三倍波长是11。5个单位的长波光。7. 8个单位的中波光和3。3个单位的短波光。这些照明剂提供的能量是在630纳米、530纳米和450纳米处有峰值的窄带。类似的三组窄带照明剂以不同的比例混合,以照亮蒙德里安。

在这一点上,读者可能会问:如果周围的纸张反射了光谱成分差别很大的光,那么单一的灰色区域会不会表现出明显的颜色变化?这些颜色的变化能不能解释蒙德里安实验的结果?对这些问题的回答是,在蒙德里安实验中,对周围纸张的任何操作都无法使灰色纸张与观察者在蒙德里安实验中选择的红色、黄色、蓝色和绿色Munsell纸张匹配。(也就是问,周围颜色会不会很大影响观察颜色的色彩,答不会太大影响)

McCann, John A. Hall和我通过重复Mondrian-Munsell实验进一步研究了这个问题,以便从Mondrian及其周围到达眼睛的光线的平均光谱组成保持不变,而不考虑从一个区域到另一个区域建立一个恒定的三联体所需的光谱组成。在一个案例中,我们通过用鲜艳的纸张包围整个蒙德里安,选择的方式是它们完全抵消了来自蒙德里安本身的平均混合波段,更引人注目的是,将蒙德里安的17个区域切割开来,将它们很好地分开放在抵消颜色的背景上。这两种安排都没有对选择与Mondrian的不同区域相匹配的Munsell chip产生任何重大影响。

上图展示了为了上五种(灰色,红色,黄色,蓝色,绿色)块到达眼睛的flux相同,需要让照亮器发射多大强度的光(第一排)

上图展示了与上上图匹配的在munesll 书里选出的五个色块以及其到达眼睛的流量

回到原问题

McCann, McKee和Taylor接下来通过使用光电倍增器和视网膜过滤器来测量各种Mondrian区域和匹配的Munsell芯片的辐射 到眼睛的能量。由于视网膜滤光片和光电倍增管的组合在一个广泛的波长带上整合了辐射能量的流量,该仪器提供了一个我们称之为综合辐射度的数值。McCann和他的同事们随后从一张大白纸上获得了综合辐射度,如果Mondrian区域的综合辐射度被用作分数的分子,而白纸的综合辐射度被用作分母,就可以得到综合反射率的数值,它可以被表示为百分比。

在恒定的 “白色 “照明下,各种Munsell芯片的综合反射率是以同样的方式确定的。这相当于使用与视觉色素具有相同光谱敏感度的探测器来测量反射率的百分比。结果表明,眼睛选择的Munsell芯片与给定的Mondri¬an区域相匹配,将具有与该区域大致相同的三个综合反射率。例如,蒙德里安的蓝色区域有三组综合反射率(长波、中波和短波),分别为27.3%、35. 9%和60.7%。匹配的Munsell芯片的可比值为34.6%. 38. 5%和57。1%

上图表现了物理计算反射率的方法

反射率 x 环境光线 = 到达眼睛的光线

最后,综合反射率被 “缩放”,使其等距与亮度感觉的等距相一致( 按照log curve去进行缩放)。这种转换的曲线显示在下面的插图中。使用这条曲线,我们看到蒙德里安的蓝色区域的综合反射率,分别是5.8. 6. 5和8.1。而匹配的Munsell芯片的相应数值是6. 4. 6. 7和7.9。 如果我们研究那五个成功地将相同的三组能量送入眼睛的区域,并将它们的标度综合反射率与它们匹配的Munsell芯片的反射率进行比较,我们发现所有的数值都非常一致。这些数据落在描述完美相关位置的45度线上[见下面的图示]。也就是说,颜色相同的块拥有相同的反射率。这个反射率 :该块反射flux/大白纸反射flux

目前为止,我们发现由flux计算的反射率和感受的亮度lightness有关。那么现在的问题是人眼怎么得到这个反射率的。这个用白纸和机器测量反射率与人眼在没白纸不确定光照下确定反射率是挺不一样的两件事。我在上面描述了一个孤立的受体系统–杆状视觉的超感系统–根据其固有的反射率对物体进行正确分类的能力,而不管这些物体是在视觉空间的亮光还是暗光区域。一个受体系统以这种方式工作的能力使人相信,正常日间视觉的其他三个系统也具有同样的能力,每个系统通过光谱中一个广泛但有限的区域观察世界。每个系统都对世界形成一个单独的亮度图像。这些图像不是混合的,而是比较的。每个区域的亮度比较产生了我们所知道的颜色感觉的范围。(也就是目前为止,我们知道了是反射率决定lightness,三种反射率comparison 决定了颜色。

让我首先指出边缘在定义场景中的物体或区域方面的重要性。如果一张白纸从一侧受到强烈的光线照射,我们就会看到从一侧到另一侧的颜色不连续。现在让我们想象一下,两个光探测器被放置在纸上的两个不同的地方来测量亮度。如果照明是不均匀的,那么这两个地方的亮度当然会不同。当两个探测器靠近时,亮度会接近相同的值,两个输出的比率也会接近统一。然而,如果两个探测器跨越了两个反射率突然不同的区域之间的边界,如白纸上的淡灰色方块,两个探测器的输出比率将接近两个反射率的比率。因此,取两个相邻点之间的比率的单一程序既能检测到边缘,又能消除不均匀光照的影响。如果我们用紧密相邻点的亮度比来处理整个图像,我们可以生成与照明无关的无尺寸数字。这些数字给出了相邻区域之间的边缘反射率:反射率本身尚未确定。(也就说,在很临近的点,它们的环境光是一样的,如果测得的反射量一样,那么它们的反射量就是一样的,如果测得反射量不同,那么这个比例就是它们反射量之间的比例,但是无法知道标准的反射量是什么)

为了确定反射率,我们需要将所有这些反射率联系起来,而不仅仅是在反射率最高的地区。任何序列中的第一个值都被任意假定为100%。由于这种特意采用的虚构,当路径到达一个反射率高于起始区域的区域时,该区域反射率就会大于单位1。获得大于单位1的反射率表明这些序列应该重新开始,新的高反射率区域被视为100%。这个过程是寻找路径中最高反射率的技术的核心。在路径达到场景中的最高反射率后,此后计算的每个序列产品都成为最高值的一部分。我们设计了一个令人满意的计算机程序来研究路径的数量、它们的长度和卷积、识别边缘的阈值以及可能最重要的是如何利用所有区域内的所有路径。

而这个过程在人大脑中是十分十分快的。这个程序的生物对应部分是在视网膜和大脑皮层之间的路径的未确定部分进行的。

在正常的图像中,白光的感觉将由任何被所有三个视网膜系统置于亮度标尺顶端的区域产生。另一方面,一个区域如果只处于三个亮度标尺中的两个标尺的顶端,就会被视为其他颜色。

眼睛计算放射率的路径

更早期一个实验,假设白光在长波中波短波区域能量都是100,长波发射器在长波处能量100,中波处50,短波处5。然后两台发射器,用手挡住长波发射器给阴影。这时候手掌内短波反射率100/105;中波反射率101/150;长波反射率101/200。这三个反射率的比值确定了手是蓝色的。

同理推论一下这个实验,红色高频光线包含100的红色区域,50的绿色能量,和5的蓝色能量。白光包含绿色部分的100点红色能量,100的绿色能量,和100的蓝色能量。其他颜色40的能量。全图的最红点是红绿分量都为100%的点(200),纯红色区域是R:140/200;G:90/150;B:45/105,纯绿区域是R:150/200;G:150/150;B105/105;纯蓝区域是R:45/200;G:90/150;B:45/105 ;.不过我这个假设能量有点不真实,但是还是可以看出能看到很多种颜色。

论文后续一些观点

正是长波和中波系统之间的lightness差距让人们感受到生动的红色和绿色的差

不明白的点:

spot of light in a void 是想说明啥,人眼是根据各个系统的最亮点确定白点的,那么百分百暗点也缩放对齐么。如果这样spot of light in a void 不应该是白色亮点么。

后续略

补充材料

红白实验:

http://land.t-a-y-l-o-r.com/

https://www.youtube.com/watch?v=YG4bzGNc1E0

有GPG签名的GitLab通信过程

非对称加密

对称加密

解密密钥和加密密钥是相同的。也正因为同一密钥既用于加密又用于解密,所以这个密钥是不能公开的。也不能在非安全的网络上传输

非对称加密

唯一的公私钥对,公钥可以明文传输,用来加密。而私钥只有自己保存,用来解密

理解公钥与私钥 | 神奕的博客

SSH通信协议

基于非对称加密,对数据进行加密传输

SSH 协议原理、组成、认证方式和过程 - 简书

基于非对称加密,对用户进行身份验证

什么是SSH?SSH是如何工作的? - 华为

GPG签名

基于非对称加密,对文件的加密和签名协议,同时对应在git commit上就是对每一次commit 签名。加密签名完,再经过SSH通信协议传输。

GPG入门教程 - 阮一峰的网络日志

服务器上的python指令

  • ctrl+c: 前台进程终止 ,直接终止当前正在运行的进程。
  • ctrl+z :中断任务执行,类似于暂停执行的意思,执行该操作意味着当前的进程被挂起。
  • 被挂起的进程只有在当下窗口,可以用jobs查看
  • jobs :查看在后台执行的进程。注意:如果退出当前终端,则无法再次进入终端查看到后台进程。jobs -l 看到pid
  • ps -u查看隶属于自己的进程,
  • linux 中查看python后台运行的程序:ps -ef | grep python

Linux后台运行python程序_linux train.py恢复进程_等待戈多。的博客-CSDN博客

常用loss合集

TV loss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class TVLoss(nn.Module):
def __init__(self,TVLoss_weight=1):
super(TVLoss,self).__init__()
self.TVLoss_weight = TVLoss_weight

def forward(self,x):
batch_size = x.size()[0]
h_x = x.size()[2]
w_x = x.size()[3]
count_h = self._tensor_size(x[:,:,1:,:]) #channel * (h-1) * w
count_w = self._tensor_size(x[:,:,:,1:]) #channel * h * (w-1)
h_tv = torch.pow((x[:,:,1:,:]-x[:,:,:h_x-1,:]),2).sum()
w_tv = torch.pow((x[:,:,:,1:]-x[:,:,:,:w_x-1]),2).sum()
return self.TVLoss_weight*2*(h_tv/count_h+w_tv/count_w)/batch_size

def _tensor_size(self,t):
return t.size()[1]*t.size()[2]*t.size()[3]

个人理解 *2 的意思是TV是四个方向的,所以两个方向计算下来需要 * 2

将测试图片网页展示

安装与准备

pip install dominate

html.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import dominate
from dominate.tags import meta, h3, table, tr, td, p, a, img, br
import os


class HTML:
"""This HTML class allows us to save images and write texts into a single HTML file.

It consists of functions such as <add_header> (add a text header to the HTML file),
<add_images> (add a row of images to the HTML file), and <save> (save the HTML to the disk).
It is based on Python library 'dominate', a Python library for creating and manipulating HTML documents using a DOM API.
"""

def __init__(self, web_dir, title, refresh=0):
"""Initialize the HTML classes

Parameters:
web_dir (str) -- a directory that stores the webpage. HTML file will be created at <web_dir>/index.html; images will be saved at <web_dir/images/
title (str) -- the webpage name
refresh (int) -- how often the website refresh itself; if 0; no refreshing
"""
self.title = title
self.web_dir = web_dir
self.img_dir = os.path.join(self.web_dir, 'images')
if not os.path.exists(self.web_dir):
os.makedirs(self.web_dir)
if not os.path.exists(self.img_dir):
os.makedirs(self.img_dir)

self.doc = dominate.document(title=title)
if refresh > 0:
with self.doc.head:
meta(http_equiv="refresh", content=str(refresh))

def get_image_dir(self):
"""Return the directory that stores images"""
return self.img_dir

def add_header(self, text):
"""Insert a header to the HTML file

Parameters:
text (str) -- the header text
"""
with self.doc:
h3(text)

def add_images(self, ims, txts, links, width=400):
"""add images to the HTML file

Parameters:
ims (str list) -- a list of image paths
txts (str list) -- a list of image names shown on the website
links (str list) -- a list of hyperref links; when you click an image, it will redirect you to a new page
"""
self.t = table(border=1, style="table-layout: fixed;") # Insert a table
self.doc.add(self.t)
with self.t:
with tr():
for im, txt, link in zip(ims, txts, links):
with td(style="word-wrap: break-word;", halign="center", valign="top"):
with p():
with a(href=os.path.join('images', link)):
img(style="width:%dpx" % width, src=os.path.join('images', im))
br()
p(txt)

def save(self):
"""save the current content to the HMTL file"""
html_file = '%s/index.html' % self.web_dir
f = open(html_file, 'wt')
f.write(self.doc.render())
f.close()


if __name__ == '__main__': # we show an example usage here.
html = HTML('web/', 'test_html')
html.add_header('hello world')

ims, txts, links = [], [], []
for n in range(4):
ims.append('image_%d.png' % n)
txts.append('text_%d' % n)
links.append('image_%d.png' % n)
html.add_images(ims, txts, links)
html.save()

测试时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from utils import html
def save_web_images(self, webpage, data, images, epoch):
if self.cfg.model.type == "Baseline":
img_path_relative = (data["image_A_path"][0]).split("/")[-1]
save_path = self.results_path / img_path_relative
save_path.parent.mkdir(exist_ok=True, parents=True)
webpage.add_header(save_path.stem)
txts = ['input','enhanced','GT','changemap','mask']
ims, links = [], []
width = 450
str_epoch = '_'+str(epoch)
root = str(save_path.parent)+'/'+str(save_path.stem)+str_epoch+".png"
self.to_pil(self.denorm(data["image_A"][0])).save(root)
self.to_pil(self.denorm(images["fake_B"][0])).save(save_path.parent / f"{save_path.stem}_enhanced.png")
self.to_pil(self.denorm(images["latent"][0]/3)).save(save_path.parent / f"{save_path.stem}_latent.png")
self.to_pil(self.denorm(data["image_B"][0])).save(save_path.parent / f"{save_path.stem}_gt.png")
self.to_pil((data["input_mask"])[0]).save(save_path.parent / f"{save_path.stem}_mask.png")
ims.append(root)
ims.append(save_path.parent / f"{save_path.stem}_enhanced.png")
ims.append(save_path.parent / f"{save_path.stem}_gt.png")
ims.append(save_path.parent / f"{save_path.stem}_latent.png")
ims.append(save_path.parent / f"{save_path.stem}_mask.png")
webpage.add_images(ims, txts, ims, width=width)
else:
raise "Unknown visualisation config"
web_dir = 'where images folder'
webpage = html.HTML(web_dir, 'Experiment = %s, Epoch = %s' % (cfg.model.type, cfg.model.load_epoch))

for images in results
    visualizer.save_web_images(webpage, data, images, epoch)

优雅的python深度学习

根目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
conf
    train.yaml
    test.yaml
data
    __init__.py           //最外层 get_dataset(dataset_name)
    datasets.py //各种个样dataset(paired unpaired combaind..)
    transform.py //制作dataset的时候,用到的数据变化 比如norm crop等
    utils.py //读取 加载 图片类型转化 padding等data用到的工具

model
    vgg19.weight

models
    auxiliary.py //作为特征提取或者vggloss 辅助网络
    discriminators.py //组成鉴别器的不同组件 以及不同鉴别器
    generators.py      //组成生成器的不同组件 以及不同生成器
    losses.py //比较复杂的loss 例如GAN perceptual loss
    engan.py //实验的完整网络和对比网络 由G和D和loss共同构成
    utils.py //初始化网络 初始化权重等 models部分用到的工具
outputs
    实验输出
utils
    metrics.py //一些评价指标
    utils.py              //系统时间啊 get device啊
    visualize.py //图片可视化和保存

eval.py
train.py
test.py
README.md