Archive for 2012年3月

Beta 发布 Adaptive Wide Angle

2012/03/29

3 月 21 日 Photoshop CS6 beta 开始公开下载。随之和用户见面的包括 Adobe 中国 Photoshop 团队开发的首个 Photoshop 主要功能 [1] —— Adaptive Wide Angle。这是我们工作一年半的成果。从用户的反应来看结果和前景都令人兴奋。

Adaptive Wide Angle 不仅是 Photoshop CS6 的主要功能,在图像处理方面也是新的突破。Photoshop 一般用来弥补照片的瑕疵或者制作各种超现实效果。Adaptive Wide Angle 面对的是一个较少为人了解的领域 —— 摄影与人主观视觉的差异。

除了人眼的双视立体效果外,普通人对摄影和主观视觉的差异了解不多。Adaptive Wide Angle 处理的问题正是双视立体之外的主观视觉(可以简单视为闭上一只眼之后感受到的视觉效果)和摄影的差异。所以 Adaptive Wide Angle 面临双重挑战:除了提供强大 UI,还需要向用户传达新领域的概念。另一方面,我从一开始就认为传达新概念这个任务并不全属于 UI 设计本身,因为新概念很快会成为被广为接受的普通概念,而 UI 演进要求一定的稳定性。所以,在总结工作心得的同时,这篇 blog 也是对这个新概念的简单介绍。

人的单眼视觉可以达到 100 度左右的视角(和视线中心轴成 50 度夹角的锥形)。这 100 度的视角投射到视网膜上,经过视神经和大脑的处理,最终具备下面近乎完美的特性:

  1. 所有直线仍然是「直线」;
  2. 局部不变形。物体的大小变化只和物体离开人眼的距离有关,在以人为球心的球面上移动物体,观察到的大小不会发生变化。

摄影技术无法完全还原单眼视觉。3D 全息技术固然可以,但在无需双视立体效果的情况下并无必要采用这种复杂昂贵的技术。完全还原单眼视觉的最简技术是把场景投影在以人为球心的球面上(称为视面球,view-sphere)。这种方法的关键在于直线并不真正投影为直线,但是也不像 fisheye 之类的镜头投影为平面上的曲线,而是投影为球面上的「侧地线」[2]。所以既可以保证主观感受的直线不弯曲,也能保证局部不变形。这正是大幅电影采用弧面银幕(部分球面或者部分柱面)的原因。但是这种做法代价仍然很大,以人为球心并且半径不能太小的球面不可能便于携带,也难于在建筑物平面上安置。

画家们在 16 世纪发明了灵活变通技术:对于包含明显直线的大尺度物体,比如背景建筑物,采用保证直线的透视投影 (perspective projection,等价于 pin-hole projection);对于本身不包含明显直线的小尺度物体,采用它们在视面球上的形式(由于球面的小部分可视为近似于平面)。比如这幅油画 [3]。这种技术称为「自由透视」或者「移动视角」[4],是即能避免球面投影又接近主观视觉效果的最佳折衷。但是没有经过透视画法和艺术创意训练的普通人显然和这种技术无缘。美国的图形学家找到了一种可以由计算机实现的数学方法。这篇论文的作者中的 Aseem Agarwala 和 Robert Caroll 和中国团队合作完成了 Photoshop CS6 Adaptive Wide Angle。中国团队从两位图形学家手中得到了质量很高的偏微分方程的线性方程逼近算法代码。

我们的第一个目标是让 Adaptive Wide Angle 和整个 Photoshop 平台风格高度一致。Photoshop 在过去的几十年中不断加入的新功能以及与 Filter 的交互方式,Adaptive Wide Angle 都给予了支持,主要包括:

  • Smart Object(同时也是对 action 的支持);
  • LAB,CMYK 等色彩模式;
  • 每通道 16 位色;
  • 透明通道。

Adaptive Wide Angle 还克隆了一些较新的 Filter 辅助功能,比如 Liquify 按住/放开 X 可以将 Preview 面板的放大系数增加四倍/恢复原状,同时不打断任何正在进行的 on-canvas 元素编辑。

第二个目标是在渲染能力上的扩展。Adaptive Wide Angle 既使用显卡加速渲染,又避免渲染的图片大小受到显存和内存大小的限制。

第三个目标也是中国团队投入精力最多的环节是界面的可用性。Aaptive Wide Angle 是目前 Photoshop 中界面最复杂的 Filter。我们尽量让它的界面简洁直观。Adaptive Wide Angle 的界面上先后存在过 30 多个不同控件,在不减少功能和灵活性的前提下减少到现在的十几个。更多的任务通过在 Preview 面板上编辑 on-canvas 元素来完成。

Adaptive Wide Angle 面临的最大的可用性挑战是如何让用户准确的输入照片的焦距 (focal length) 和投影类型 (比如 perspective, fisheye) 。在这方面我们体会到了 Photoshop 平台的强大威力。Adaptive Wide Angle 充分利用了 Photoshop 的资源:

  • 对于 Photoshop 支持的镜头/机身组合,Adaptive Wide Angle 自动根据 Lens Profile 数据计算焦距(同时弥补镜头的光学畸变);
  • 对于 meta-data 中同时提供 focal length 和 crop factor 的照片直接采用这些数据;
  • 通过增强 Auto-Align Layer 功能,把 Photomerge 生成的全景图的虚拟焦距存入 PSD 文档(或者其它任何支持 Adobe XMP 标准的文件);
  • 对于没有任何 meta-data 的 fisheye 镜头,中国团队发明了在 Preview 面板上一次成功的估测方法。

经过这些努力,大多数用户根本不必了解焦距和投影类型这两个概念。克服这个可用性障碍是中国团队的努力和 Photoshop 平台的巨大资源积累的合力。

最后,我们如愿以偿地看到很多用户通过 UI 本身和简单介绍能理解这个工具所实现的新概念。甚至于有些用户评价,由于这个工具直观的 UI 和结果的可预测性,一些理论上能够由 non-adaptive 工具 [5] 完成的任务,也可以通过 Adaptive Wide Angle 来完成。

脚注:

  1. Photoshop 中国团队之前开发过 Adobe Lens Profile Creator 和 Adobe Lens Profile Downloader。都是配合 Photoshop 使用的独立工具应用。
  2. Great circle。球面上连接两点最短的弧。可以看作两点和球心所在的平面与球面的部分交线。
  3. 注意此画两侧和中央的人物大小相同,而桌子的透视灭点又说明观察者距离场景并不远(不是长焦效果),这在相同视角的普通照片上是不可能的。
  4. 这个功能中的 adaptive 就是表示根据图像的内容自适应的进行自由透视投影。
  5. Non-adaptive 表示所进行的图像变形是有解析形式的统一数学变换,和 adaptive 算法进行的自由透视方法相对(后者是由用户输入和图像性质共同决定的偏微分方程逼近结果,没有解析形式)。Lens Correction 和 Perspective Transform 是常用的 non-adaptive 工具。

软件质量之路

2012/03/20

上个月有段时间修正 bug 忙得要死,最后缓口气的时候统计了一下,发现这段时间修正了近 20 个 bug。这个数字大大出乎我的意料。

这些修正 bug 的工作并非是目前我开发的产品,而是一个两年前维护过的现在已经交给其它 team 维护的系统。即便两年前,对此系统的诸多细节也不甚了解。如今已两年没有接触它的 code base,只是对其大体构架还有印象。最近维护该产品的人手紧张,调我临时支援一下,主要处理一些罕见的 random crash。这些 random crash 很难在测试环境下复现。产品发布给数千的 pre-release 用户后才会在不断的日常使用中偶尔产生。遭遇 crash 的用户可以通过自动工具把 crash report 提交给服务器,report 中的信息不是很多,最主要的是所有 thread 在 crash 时刻的 call-stack,后端系统会根据导致 crash 的 thread call-stack 自动进行分类。这就是这些 bug 的背景:一个不熟悉的产品,没有复现 bug 的情景,只有少量线索。因为还要保证目前负责的产品的进度,每天只能花一半工作时间研究这些 crash report,如此历经三周。修正近 20 个 bug 的成绩令人颇感意外。

这件事情给我的启示是多方面的。Eric Steven Raymond 在《The Cathedral and The Bazaar》中针对 open source 模式总结出「Linus Law」,后来又在《The Art of UNIX Programming》中详细阐述:

Given enough eyeballs, all bugs are shallow.

有人质疑「Linus Law」(比如这篇),开放 source code 是否等同于有更多的人发现并修正 bug。因为不管一个 open source 软件多么流行,真正积极参与开发,能深入了解 code 的总是少部分人。这段时间修正 bug 的经历却证明修正软件稳定性方面的 bug 无需深入了解特定产品的 source code,特别需要的是与产品无关的通用软件开发知识,比如 C/C++ 的语言和 runtime,操作系统,二进制接口,静态和动态链接,汇编,多线程多核等等。影响和推动某个 open source 软件产品的通常是小团队,但能够参与提高产品稳定性质量的可以是很大的开发者群体。因为所需技能并非与某个甚至某类产品相关,一个高水平的程序员甚至可以在业余时间帮助提高多个 open source 产品的质量,即使有些产品的类型超出他熟悉的范围。

第二方面的启示是关于在软件中寻找问题。这些 crash 的代码乍看起来是非常「无辜」的。甚至于我每次都会惊呼一定是 C runtime 的 malloc() 有 bug 或者宇宙射线导致的内存错误。而且没有任何动态分析(比如使用 debugger,输出 log)的手段。不过只要定下神,告诉自己确信这个地方一定会导致 crash,经过几个小时的静态分析和冥思苦想之后几乎总是能找出一些问题。根据这些模模糊糊的推测完成的修复没法即时验证,只能先 submit 再说,称为 blind-fix。同样出乎意料同时也颇有成就感的是 crash 统计系统中相当多的 crash 数量在相关的 blind-fix 之后陡然下降为零。

在软件系统日益复杂的时候,程序员却失去了一些 postmotern debugging 的信息(例如,large sparse 进程空间让 full core dump 在用户生产系统上无法接受),但是这些真实的 blind-fix 实例说明:第一,call-stack 是最重要的调试信息;第二,修正问题最重要的一步是能 100% 的确信一定存在问题。这两点的重要程度之高远远超乎我之前的预料。关于第一点,我曾经很多次提到过,对待破坏 call-stack 可见性(比如,由于增加动态语言的虚拟机而导致两层 call-stack)和完整性(比如 message-posting,provider-consumer,Reactor 模式)的技术要非常慎重。

第三方面是关于工具。一个简单的自动提交 crash report 的工具和一个 crash 分类数据库,不算什么高深的技术。但是,如果没有这套系统,发现和研究 random crash 与验证 blind-fix 就是不可能的。在软件工业里,再不起眼的小工具都是加速整个行业进程的一部分。一些老程序员有个共同的经历:开始编程是因为能直接操控一台复杂的机器快感;经过一段时间之后,发现这机器比预想的要难驾驭,产生很强的挫败感;经过一段迷茫之后又发现,自己每一点努力的成果,并不是限于直接操控机器,而是增加整个群体的驾驭系统的能力,同时自己的能力也在这个体系上加速提高;如此重新找到编程的热情。

十几年前,「软件危机 (software crisis) 」还算个值得一提的名词,而且《人月神话》里「no silver bullet」的预言至今也没有被打破。不过,虽然没有数量级的生产力提高,软件开发还是在工具与协作模式上持续地稳步提升生产力,甚至还有些许的加速度。我们已经安然度过了「软件危机」。知乎上有一个问题 —— 是否能用十年前的技术造出今天的软件,我说的是,这十年软件业并没有空过,和十年前相比,我们毕竟拥有了很多无法或缺的东西。