一、引子

话说前阵子想把一张照片转换成素描,然后发个微博。结果发现 mac 上没找到能直接转换素描的软件(PS 不算,可要好几步呢),坑爹啊~~google 了下,Web 上竟然也是没有直接把照片转换成素描的东西,连让我包含期望的美图秀秀(Web 版)竟然都没有素描功能,T_T。

手机上是有很多这类 app,但是我只是想一键转换下,发个微博嗟,至于这么折腾么……

所以自己动手整一个在线版的吧,没怎么用过 canvas,正好可以顺道熟悉下。等不及的童鞋可以先到这里看看效果(http://appx.imatlas.com/sketching/)。

二、怎么转换

刚冒出这个想法的时候,简直是一头雾水诶~数学不行、PS 不懂、图形学忘光了……

还好有万能的 google,翻了几页,找到一个 ps 制作素描图片的步骤——虽然我不懂,但是如果按照这个步骤用 PS 能做成素描,我用代码也一定可以的。嗯,一定是的。

PS 里面最简单的一个转换素描的步骤为:

  1. 去色(黑白化)
  2. 复制一份,反相
  3. 把复制后的图层叠加方式设为颜色减淡
  4. 高斯模糊

PS 里面的具体步骤我就不详说了,可以看这篇文章。既然知道了实现步骤,我只要用 JS 把这些算法都实现了就行啦,哇哈哈哈~

三、原理什么的

去色:把图片变成黑白图,只要把每个像素的 R、G、B 设为亮度(Y)的值就行了。关于 R、G、B、Y 的关系可以看到这里看看,这里只要记住这条公式:Y = 0.299R + 0.587G + 0.114B。

反相:就是将一个颜色换成它的补色。补色就是用 255(8 位通道模式下,255 即 2 的 8 次方,16 位要用 65535 去减,即 2 的 16 次方)减去它本身得到的值:R(补) = 255 - R。

颜色减淡:其计算公式是:结果色 = 基色 + (混合色 * 基色) / (255 - 混合色),在这里找到的这条公式,原理我就不多说了,因为我也不大懂(^_^,图形学睡过去了……)。

高斯模糊:嗯,这个是最让我抓头摸脑的。一开始没怎么理解到这个算法,纠结了两天。最后终于灵光一闪,想通了(还好没晕过去大睡三天~.~)!网上有很多 C++的实现,但是基本没找到 JS 的。一开始不想去理解高斯模糊,就尝试把 C++代码改成 JS 的,改了半天,终于放弃了~想明白之后,自己照原理写了个,想不到还挺容易的,呃……具体的高斯模糊原理,就在这里这里这里看吧,老衲就不误人子弟了。

本项目已经托管到了 Github(https://github.com/iazrael/sketching),这几个方法的源码可以到上面查看。稍微提下实现素描的一个注意事项:去色之后需要拷贝一份像素数组备用,开始是用数组的 slice 方法来拷贝像素数组的,结果经常需要 800ms 左右的时间;后来尝试了直接用 canvas,putImageData 之后再调用 getImageData 来 “曲线救国”,结果只用 10 几毫秒就可完成,简直让老衲老泪纵横诶~其代码如下:

四、怎么用

说起用法啊,那你可以问对人了,哈哈。狠狠的敲入 app 的网址:http://appx.imatlas.com/sketching/(注意只能用现代浏览器(Chrome,Firefox,Opera,Safari 等)打开哦,IE9 以前的老古董就甭来啦),然后拖拽一张图片到画布区(就是下面打开的灰色地带~),然后……就没有然后啦,最多 2 秒之后自动生成素描画。点击 download 按钮可以下载生成的图片。

如果感觉效果不太好,可以改下取样的半径(Sample size),为正整数,最小为 1。如果你一定要填负数、小数,也会被取正取整(抠鼻)。之后点下 action 按钮,生成新的素描图。

如果你还不明白,下面来看图说明(点击图片可以查看大图)。

sketching

sketching 图示

斋说都没益啦,实牙实齿效果才是王道,看看下面的原图:

原图

转换后的素描图:

素描

怎么样,效果是不是还不错咧,嘎嘎嘎。当然,这个算法未必是最好的,欢迎各位童鞋踊跃拍砖,^_^

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2012/07/convert-picture-to-sketch-by-canvas/

  1. jener.luo 2012 年 9 月 12 日

    厉害。

  2. HTML5技术到底为我们带来了什么之图像处理 | HTML5工作室 2012 年 9 月 10 日

    […] 技术实现详解(内含 Github 开源地址):http://www.alloyteam.com/2012/07/convert-picture-to-sketch-by-canvas/ […]

  3. 袁源 2012 年 8 月 21 日

    我想转来做微博头像,结果太白了啥都看不清 http://photo.weibo.com/1085945040/talbum/detail/photo_id/3481091673455886?from=profile&profilephoto=3&wvr=3.6#

    • TAT.Kinvix

      TAT.Kinvix 2012 年 8 月 22 日

      呵呵,我们再调一下效果,后续也会再推出几种效果来给 MM 你做微博头像^_^

  4. zany 2012 年 7 月 30 日

    0_0!厉害呐!!!

  5. HTML5技术到底为我们带来了什么之图像处理 | 115q-我的互联网路 2012 年 7 月 29 日

    […] 技术实现详解(内含 Github 开源地址):http://www.alloyteam.com/2012/07/convert-picture-to-sketch-by-canvas/ […]

  6. alex 2012 年 7 月 27 日

    lz 牛 x 啊!!!解决问题的方法很值得学习!!

  7. FirstBlood 2012 年 7 月 27 日

    V5

  8. Canvas photos into Sketches | HTML5 Game Development 2012 年 7 月 27 日

    […] http://www.alloyteam.com/2012/07/convert-picture-to-sketch-by-canvas/ RedditBufferShareEmailPrintFacebookDiggStumbleUpon « Preparing Yourself for Modern JavaScript Development […]

  9. Hatsuame 2012 年 7 月 26 日

    提个小建议把高斯模糊,做成 3×3 模板可能更好点,毕竟制造这个描边的感觉不需要半径很大。或是用换成 sobel 描边。

    • TAT.iAzrael

      azrael 2012 年 7 月 27 日

      嗯,默认用了 5×5 的,感觉这个出来的效果好点,可以改的

  10. 【转载】HTML5技术到底为我们带来了什么之图像处理_HTML5研究小组_HTML5教程_HTML5资源_HTML5游戏 2012 年 7 月 26 日

    […] 技术实现详解(内含 Github 开源地址):http://www.alloyteam.com/2012/07/convert-picture-to-sketch-by-canvas/ […]

  11. louxiaxz 2012 年 7 月 25 日

    留个名嘞

  12. HTML5技术到底为我们带来了什么之图像处理_互联网资讯最新报道_野火集 2012 年 7 月 25 日

    […] 技术实现详解(内含 Github 开源地址):http://www.alloyteam.com/2012/07/convert-picture-to-sketch-by-canvas/ 图片处理在线体验:http://apps.imatlas.com/sketching/ 使用指引 拖拽一张图片到画布区(就是下面打开的灰色地带~),然后……就没有然后啦,因为在 0.01 秒的时间内,照片已经处理完毕,点击 download 按钮可以下载生成的图片。 […]

  13. svenzeng 2012 年 7 月 20 日

    强帖留名, 小龙厉害

  14. ivy1107 2012 年 7 月 20 日

    copyPixes = ctx.getImageData(0, 0, width, height).data;
    不需要重新获取 ImageData 或者通过 Slice 拷贝,ImageData.data 类型是 Uint8ClampedArray,它有一个 subarray 方法,参数为 (start, end),start 必填,end 不填表示到末尾。所以 subarray(0) 就是拷贝该数组。

    • TAT.iAzrael

      azrael 2012 年 7 月 20 日

      强~我看了 ImageData, 没发现什么方法, 就没继续看 ImageData.data 了, 谢谢你的分享, ^_^

    • TAT.iAzrael

      azrael 2012 年 7 月 20 日

      刚试了下, subarray 得到的数组, 里面的元素还是引用了原来的, 拷贝的只是引用…

    • TAT.iAzrael

      azrael 2012 年 7 月 20 日

      查了文档, 也是这么说的.
      subarray(begin : Number, [end : Number]) : Uint8ClampedArray

      Returns a new Uint8ClampedArray that is a view on top of this containing items this[begin], this[begin + 1], …, this[end – 1]. The 0th item in the returned array in the same memory location as this[begin]. If end is not specified, this.length is used.

      • ivy1107 2012 年 7 月 20 日

        嗯,是的。试着新建一个。
        var copy = new Uint8ClampedArray(pixels.length );
        copy.set(pixels, 0);

  15. 使用CSS3绘制网格线 | Tencent AlloyTeam 2012 年 7 月 19 日

    […] Post navigation ← Previous […]

  16. iphone5 2012 年 7 月 19 日

    又熬夜到凌晨 3 点,准备睡觉……

  17. TAT.Cson

    csonlai1989 2012 年 7 月 18 日

    大赞~

  18. 匿名 2012 年 7 月 18 日

    1111111

  19. 81% 2012 年 7 月 18 日

    漂亮

  20. 自由国度 2012 年 7 月 18 日

    这等于实现了一个 PS 的滤镜啊

发表评论