美文网首页iOS猿媛圈iOS进阶iOS
[iOS] 图像处理 - 一种高效裁剪图片圆角的算法

[iOS] 图像处理 - 一种高效裁剪图片圆角的算法

作者: ck2016 | 来源:发表于2016-12-13 11:25 被阅读5583次

场景

经常看到各种高效裁剪圆角的文章,正好之前做过一点数字图像处理,就打算用空域处理的办法,写个裁剪圆角的算法,一定要尽可能的快的,不然界面容易卡顿。

裁圆角很简单,对于图像上的一个点(x, y),判断其在不在圆角矩形内,在的话 alpha 是原值,不在的话 alpha 设为 0 即可。如下图

15F6A143-2704-402D-88EA-DB80B0266F80.png

我遍历所有像素,判断每个像素在不在4个圆的圆内就行了,4个角,每个角有一个四分之一的圆。

一个优化就是,我不需要遍历全部的像素就能裁出圆角,只需要考虑类似左下角三角形的区域就行了,左下,左上,右上,右下,一共4个三角形区域(另外3个图中没画出),for循环的时候,就循环这个4个三角形区域就行了。

所以对于一幅 w * h 的图像,设圆角大小为 n,n <= min(w, h) / 2,其复杂度为 O(n) = 2(n^2),最坏的情况计算量也不会超过 wh / 2

对于一个像素点(x, y),判断其在不在圆内的公式

如果  (x-cx)^2 + (y-cy)^2 <= r^2  就表示点 (x, y) 在圆内,反之不在。

理论说完了,下面看实际的测试数据。

测试结果与分析

根据上面的分析,我写了一个裁剪圆角的程序,叫为 my裁剪
还用了苹果 CoreGraphics 库的 CGContext 裁剪圆角,叫为 CGContext 裁剪
还用了 UIKit 的 UIBezierPath 裁剪圆角,叫为 贝塞尔裁剪

下面来对比三种方法,哪种最快,分别是
1. my裁剪
2. CGContext裁剪
3. 贝塞尔裁剪


实验数据:
一张 png 格式 512 * 512 的 lena 女神的标准实验图像。
圆角大小分别取 10,50,100,250,这4个值。
每次实验裁剪 10000 张图片数据,获得总耗时。

因为图片是 512 * 512 的,最大的圆角为 512 / 2 = 256,所以超过 256 的会被强制设在 256,所以实验中用了个近似 256 的 250 做为最大的测试数据。

实验前关闭所有比较耗CPU的软件。实验中不操作电脑,避免影响实验结果准确性,最好真机测试,关掉后台所有APP。最后得到了以下实验数据,并绘制成表格。

表格.jpeg

根据上图可以看出 my 裁剪运行时间看起来像指数上升的,是不是会更慢?
答:不是的,看 x 轴的坐标间距,10 到 50 到 100,然后直接跳到 250 了,不是均等分的!要是画均等分的话,图会非常非常长。
如果要 x, y 轴刻度均等分的话,画出来的图大概是这样的,如下图:

表格2.jpeg

my 裁剪时间随着圆角大小线性上升,到达 256 的时候,是最大值了。

实验过程中的具体数据。

  • 圆角为 10 的情况
裁剪方法 用时 ms
my裁剪 2085
CGContext裁剪 15270
贝塞尔裁剪 14317
圆角为 10
  • 圆角为 50 的情况
裁剪方法 用时 ms
my裁剪 2419
CGContext裁剪 14676
贝塞尔裁剪 14612
圆角为 50
  • 圆角为 100 的情况
裁剪方法 用时 ms
my裁剪 3662
CGContext裁剪 14646
贝塞尔裁剪 14597
圆角为 100
  • 圆角为 250 的情况
裁剪方法 用时 ms
my裁剪 12399
CGContext裁剪 14692
贝塞尔裁剪 14313
圆角为 250

结论与分析

从上面数据可以看出:
时间上:不管圆角大小 n 是多少,CGContext 和 UIBezierPath,耗时都在 14.6 秒左右。而 my裁剪在圆角小的时候,性能较好,耗时在 3 秒左右,随着圆角增到250,耗时也去到了 12 秒,但最坏不会超过 w * h / 2,在 n < min(w, h) / 2 时,具有较高的性能,比CGContext, UIBezierPath要快。

空间上:内存使用上,没精确测量,大致看了一下,裁剪1万张 512 * 512的图片,3种算法的内存使用都在 10MB 左右,还可以接受,但 UIBezierPath 裁剪时居然会写磁盘。

另外,在图像编码/解码中,用了 CGDataProviderRef,CGImageRef,这两个对象,它们的速度应该是很快的了,如果自己写的话,预测可以更快。

最后口说无凭,我发个代码可以自己下载运行看看结果(下载地址
使用方法都在代码的 ViewController.m 文件里,就这么一个文件,很好找,没有其他了。请在 iphone6,6 plus 的屏幕下运行。本实验重点在于3个算法,无关紧要的东西没做太多处理。

相关文章

网友评论

  • 请叫我喵_喵:不说赞一个,我都不好意思用了~~~ 真棒!
    请叫我喵_喵:@请叫我喵_喵 遍历时内存偏移存在越界的危险.
    请叫我喵_喵:po *p
    error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory
    ck2016:@请叫我喵_喵 :+1:
  • 马克吐温Coder:裁剪出来的图形有些问题。直接调用UIBezierPathClip 参数填写200. 裁剪是圆形,是正确的。dealImage调用参数填写200 裁剪的出的并不是圆形。楼主的算法应该有些误差吧。如果这样的话,那3个方法对比出的结果也就有些问题了
    ck2016:@马克吐温_ 我试试
  • Hesse_Huang:http://www.jianshu.com/p/0778bee3794f
    我参考了你的文章,写了一份Swift版本。(在Swift中操作C指针真的挺绕。。)
    Hesse_Huang:@ck2016 CocoaChina微信号
    ck2016:@ynboh 问一下,你在哪里看我的文章的
    ck2016:@ynboh 我去看看,牛逼啊
  • 北漂少年郎:我一直用self.view.layer.cornerRadius 这个切,这个切都有什么弊端那,请教楼主
    ck2016:@逍遥若水 不在屏幕缓存区画图
    北漂少年郎:@ck2016 什么是离屏渲染
    ck2016:@逍遥若水 会发生离屏渲染,数量多了会掉帧
  • d6b305902a5e:楼主,我平时一般都是用CornerRadius直接切圆角 。这是属于你说的哪种方式???还是单独的属于另一种方式切割???这种方式速度几何???
    ck2016:@蚂蚁偷钻石 行,界面不卡就行了,卡了就想办法优化
  • KevinTing:卤煮啊,你这个测试得在真机上测试的数据才有说服力吧
    ck2016:@KevinTing 是啊,我忘了啊,电脑上干扰多
  • ff0f6b05526c:因为图像是对称的,所以4个角其实可以变成1个角
    ck2016:@流年如秋 啥意思
    _涼城:@ck2016 那在遍历一个的情况下处理相对应的另外3个可行么
    ck2016:@ff0f6b05526c 不行,像素还是要遍历那么多个的
  • RemisKrlet: iphone 6测试,10000次,radius 256像素,自定义处理,CG,UIBezier分别是 44.448s, 29.132, 29.157s。次数少,radius小的情况下CG比UIBezier慢
    ck2016:@RemisKrlet 这个真不好测,有些网友可能是后台程序影响,或者一次按了好多个按钮导致的,按下button后,不动其他的操作,给他算完,次数调到1000试试
    RemisKrlet:@ck2016 不清楚,你这边是多少
    ck2016:@RemisKrlet 这不科学,应该是哪里错了,居然44秒,
  • li_礼光:测试了一下Demo,算法是比原生的要快.以我个人的观点,有些问题吧.
    1.[self dealImage:image cornerRadius:100]这个方法与self.imageV2.layer.cornerRadius = 100;self.imageV2.layer.masksToBounds = YES; 这样的方式处理圆角. 作者自己创建的方法的圆角处理并不到位.
    2.假如忽略上面的内容来说,圆角处理并不圆滑.满足性能再丢失了图像的裁剪效果,不太明智.
    3.处理图片少的,其实系统提供的方法的时间也差不多.
    ck2016:@靓模袭地球 嗯,边缘一圈的像素点没与背景衔接(双线性插值能很好的平滑边缘),数据量小时间差距就小了
  • 从来吃不胖:方法的确挺好的!
    但是发现有些问题,“my裁剪”产出的图像的圆角不够平滑,而另外两种方法产出的圆角是十分平滑的。
    是不是性能上的差距主要就是在处理上舍去了平滑处理,所以效率更高呢?
    ck2016:@从来吃不胖 圆角100的圆,周长是600多个像素,扩3圈就是1800个像素左右吧,就是for循环多算1800次
    从来吃不胖:@ck2016 边缘一圈的这一圈,甚至是往外扩散的2~3圈,都需要做过渡平滑处理,也就是alpha值过渡,这个如果要做的精确,计算量要比现在的DEMO复杂很多吧?:disappointed_relieved:
    如果把边缘平滑算法加上,再测测效率,可能更加有说服力:+1:
    现在的算法已经很棒啦!加油
    ck2016:@从来吃不胖 不是,要平滑一点的话,可以把边缘那一圈的像素调低alpha值,与背景衔接,几乎不会影响性能
  • 丷笑者:支持楼主,不过测试了一下13张图片以下的情况,11-13张之间3种方式耗时差别不大,只有在13张以上并且圆角裁剪的时候,裁剪才能优于其他2种方式
  • NateLam:楼主, 问一个比较弱智的问题, 参数CornerRadius在切圆的时候要填多少, 你demo里面原图边长是200, 参数填100, 结果点击"计算"出来的图不是圆形...
    ck2016:图有长和宽,长宽都是200的话,参数就写100,就是圆了
  • 诸子百家谁的天下:请求加入专题!
    ck2016:@诸子百家谁的天下 好的
    ck2016:@诸子百家谁的天下 怎么加
    诸子百家谁的天下:@诸子百家谁的天下 另模拟器实在是不太准确的,应该使用真机测试!
  • 丶大明:LZ,我count=10000,radius=250 用模拟器iphone6 :cold_sweat: 测试:
    2016-12-19 14:49:37.680 ClipCorner[98213:2837474] ---start
    2016-12-19 14:50:10.371 ClipCorner[98213:2837474] my裁剪用时:32.545 秒
    2016-12-19 14:50:13.918 ClipCorner[98213:2837474] ---start
    2016-12-19 14:50:28.565 ClipCorner[98213:2837474] CGContext裁剪用时:14.611 秒
    2016-12-19 14:50:34.532 ClipCorner[98213:2837474] ---start
    2016-12-19 14:50:49.696 ClipCorner[98213:2837474] 贝塞尔裁剪用时:15.125 秒
    丶大明:@ck2016 恩,好
    ck2016:@丶大明 亲关掉一些耗CPU的软件,再测一遍看看
  • iOS_愛OS:你demo 中的图好熟悉,毕设搞图像算法用的全是这张图
    ck2016:@iOS_愛OS 有几张标准实验图像来的
  • littlewish:mark 找时间试试demo
    ck2016:@littlewish :relaxed:
  • e220c343b18f:试了一下你这个方法, 发现在圆角小于100时,剪切后图片获取不到。
    把自定义的剪切算法里的 释放内存方法更改了一下,发现解决问题了。
    希望作者看下一是否存在这样的问题。

    更改内容是这样的
    CGDataProviderRelease(pv);
    CGImageRelease(content);
    // free(imgData); // 释放空间
    ck2016:@林98 内存释放得好
    ck2016:@林98 明天我看看 :+1:
  • Zhengjun_Sun:这算法好🤔🤔🤔
    ck2016:@Satinettes :blush:
  • 不留名的黄子嘉:一般不是生成一个遮罩盖上去就好吗? 生成一个CAShapeLayer 如果是cell 也就是几个遮罩的事情而已。直接盖在控件上面 传入什么图片都只能显示遮罩内容?
    ck2016:@不留名的黄子嘉 谢谢
    不留名的黄子嘉:作者确实厉害 :smile: 走drawInRect方法都会消耗性能 研究下用其它方法能不能超过作者的demo。=。=
  • 口袋海贼王_:感觉一个屏幕的圆角数量不会太多,100个都属于多的了,数量少的时候露珠你的处理时间明显要长、
    口袋海贼王_:@ck2016 不对啊,你应该是姿势不对吧,露珠,哈哈哈。我的测试效果。2016-12-19 13:00:53.940 ClipCorner[7686:224485] ---start
    2016-12-19 13:00:54.136 ClipCorner[7686:224485] my裁剪用时:0.193 秒
    2016-12-19 13:00:55.370 ClipCorner[7686:224485] ---start
    2016-12-19 13:00:55.514 ClipCorner[7686:224485] CGContext裁剪用时:0.140 秒
    2016-12-19 13:00:56.723 ClipCorner[7686:224485] ---start
    2016-12-19 13:00:56.876 ClipCorner[7686:224485] 贝塞尔裁剪用时:0.149 秒
    ck2016:@口袋海贼王_ 测试了10000, 100, 10 的情况,没有你说的情况发生
    ck2016:@口袋海贼王_ 也就是说把 10000张,改为 100 张测试吗,我待会试试
  • 白云心城:谢谢分享!向作者学习! :+1:
    ck2016:@白云心城 谢谢
  • 蓝天大海:作者好样的,棒棒的
    ck2016:@广杭小子 谢谢
  • 难却却:牛🐂
    ck2016:@难却却 谢谢
  • f680dfb8c555:厉害
    ck2016:@_silenceZ :relaxed:
  • YungFan:拼算法的时候
    ck2016:@YungFan :relaxed: :relaxed: :relaxed:
  • 80c9a4518c6f:支持原创
    ck2016:@SkyerWalker :relaxed: :relaxed: :relaxed:

本文标题:[iOS] 图像处理 - 一种高效裁剪图片圆角的算法

本文链接:https://www.haomeiwen.com/subject/abmmmttx.html