qpwoeiru96

qpwoeiru96 查看完整档案

北京编辑  |  填写毕业院校杭州大头网络科技有限公司  |  Leader 编辑 yi-you.org 编辑
编辑

吴子棋

个人动态

qpwoeiru96 评论了文章 · 2019-05-12

实战还原PHP加密文件

先说环境:

    Ubuntu 14.04.4 LTS (GNU/Linux 3.13.0-52-generic x86_64)
    PHP 5.5.9-1ubuntu4.19 (cli)

实战过程

手头上有一个加密过的项目和一个php扩展的动态连接库(jinhou.so)。

PHP代码类似如下的样子:

<?php
/* xxxx技术有限公司版权所有: 2016-09-08 08:18:00 */
jhgo('uGpqefbDEBkqp4preQ2UaAp3RAUeJAZ5s4aERAQMkxbJTgEovHnQw6WxsA99sAhSacJCLGxZL4Q4u6zFyGveuDUoemktHGkMaB5D');?>

根据上面分析到的已知条件有:

  1. 加密方式类似 eval 的加密方式。

  2. jhgo 包含执行代码跟解密代码。

  3. jinhou.so 里面包含 jhgo 函数。

首先非常粗暴的用 VIM 打开 jinhou.so 看看有没有什么关键的信息。

jinhou.so

很不幸,明文写着解决方案的出处。https://github.com/eixom/zoee...

看了代码理论上这个扩展暴露出来有两个方法,一个执行加密文件的方法和一个解密文件的方法。但实际上只有一个方法暴露出来,那么还有一个被狡猾的提供者删除了。

当然源码我们都有了也就不计较那么多了。

试着用官方源码编译下然后decode一下发现还是不行。

再仔细研究了一下,发现

jinhou.so

里面有一串很神奇的字符串: 82dsa7dsas32112389uy7aydh8h2h2i412 心想是不是他的加密Key。再翻代码果真是。在 https://github.com/eixom/zoeeyguard/blob/master/src/guard.h 文件里面。
原来的是 28dsa7dsas12312389uy7aydh8h1h2i312。改完之后发现还是不行。

果真我还是Too Young Too Simple啊。

程序员心理角度分析下:一般没人会去改代码,基本上都是动动参数而已。是不是还有其他参数改动了?可是其他参数都是数组格式的这可头疼了。

/*  private key */
#define PRIVATE_KEY "28dsa7dsas12312389uy7aydh8h1h2i312"
#define PRIVATE_KEY_LEN sizeof(PRIVATE_KEY)

/* order */
static const unsigned char OBFUSCATED_ORDER[] = {
      13,  6,  5,  7,  1, 15, 14, 20
    ,  9, 16, 19,  4, 18, 10,  2,  8
    , 12,  3, 11,  0, 17
};
#define ORDER_SIZE sizeof(OBFUSCATED_ORDER) / sizeof(* OBFUSCATED_ORDER)

/* alphabet for base64 */
static const unsigned char OBFUSCATED_ALPHABET[] = {
      's', '4', 'N', 'E', 'k', 'X', 'c', 'u'
    , 'J', '2', 'U', 'o', 'O', 'w', 'K', 'v'
    , 'h', 'H', 'C', '/', 'D', 'q', 'l', 'R'
    , 'B', 'r', '5', 'Z', 'S', 'Q', '6', 'W'
    , '3', 'L', 'j', '8', '1', 'z', '0', 'G'
    , 'n', 'e', 'y', 'b', 'I', 'd', 'i', 'P'
    , 'A', '9', '7', '+', 'm', 'V', 'M', 'Y'
    , 'F', 'g', 'f', 'p', 'a', 'T', 't', 'x'
};
#define ALPHABET_SIZE 64

这个时候得请出大杀器了:IDA Pro v6.8,反编译神器。左边是正常版本,右边是jinhou.so。

IDA Pro v6.8

根据数据更改guard.h里面的文件。重新编译下,果真成功解密出来了。

<?php
require_cache(APP_PATH.'/Lib/Action/User/AddonAction.class.php');
?>

事后总结

1、 这次破解没花费多少时间,主要感谢加密方案跟加密代码已经明文告诉我们。
2、 主要的时间是在试探加密参数上,很幸运的是.so文件也没有加壳。
3、 在破解的过程中也了解了PHP加密的缺陷性,所以才有了下一篇文章:通用PHP加密文件还原方法。

查看原文

qpwoeiru96 评论了文章 · 2019-05-12

实战还原PHP加密文件

先说环境:

    Ubuntu 14.04.4 LTS (GNU/Linux 3.13.0-52-generic x86_64)
    PHP 5.5.9-1ubuntu4.19 (cli)

实战过程

手头上有一个加密过的项目和一个php扩展的动态连接库(jinhou.so)。

PHP代码类似如下的样子:

<?php
/* xxxx技术有限公司版权所有: 2016-09-08 08:18:00 */
jhgo('uGpqefbDEBkqp4preQ2UaAp3RAUeJAZ5s4aERAQMkxbJTgEovHnQw6WxsA99sAhSacJCLGxZL4Q4u6zFyGveuDUoemktHGkMaB5D');?>

根据上面分析到的已知条件有:

  1. 加密方式类似 eval 的加密方式。

  2. jhgo 包含执行代码跟解密代码。

  3. jinhou.so 里面包含 jhgo 函数。

首先非常粗暴的用 VIM 打开 jinhou.so 看看有没有什么关键的信息。

jinhou.so

很不幸,明文写着解决方案的出处。https://github.com/eixom/zoee...

看了代码理论上这个扩展暴露出来有两个方法,一个执行加密文件的方法和一个解密文件的方法。但实际上只有一个方法暴露出来,那么还有一个被狡猾的提供者删除了。

当然源码我们都有了也就不计较那么多了。

试着用官方源码编译下然后decode一下发现还是不行。

再仔细研究了一下,发现

jinhou.so

里面有一串很神奇的字符串: 82dsa7dsas32112389uy7aydh8h2h2i412 心想是不是他的加密Key。再翻代码果真是。在 https://github.com/eixom/zoeeyguard/blob/master/src/guard.h 文件里面。
原来的是 28dsa7dsas12312389uy7aydh8h1h2i312。改完之后发现还是不行。

果真我还是Too Young Too Simple啊。

程序员心理角度分析下:一般没人会去改代码,基本上都是动动参数而已。是不是还有其他参数改动了?可是其他参数都是数组格式的这可头疼了。

/*  private key */
#define PRIVATE_KEY "28dsa7dsas12312389uy7aydh8h1h2i312"
#define PRIVATE_KEY_LEN sizeof(PRIVATE_KEY)

/* order */
static const unsigned char OBFUSCATED_ORDER[] = {
      13,  6,  5,  7,  1, 15, 14, 20
    ,  9, 16, 19,  4, 18, 10,  2,  8
    , 12,  3, 11,  0, 17
};
#define ORDER_SIZE sizeof(OBFUSCATED_ORDER) / sizeof(* OBFUSCATED_ORDER)

/* alphabet for base64 */
static const unsigned char OBFUSCATED_ALPHABET[] = {
      's', '4', 'N', 'E', 'k', 'X', 'c', 'u'
    , 'J', '2', 'U', 'o', 'O', 'w', 'K', 'v'
    , 'h', 'H', 'C', '/', 'D', 'q', 'l', 'R'
    , 'B', 'r', '5', 'Z', 'S', 'Q', '6', 'W'
    , '3', 'L', 'j', '8', '1', 'z', '0', 'G'
    , 'n', 'e', 'y', 'b', 'I', 'd', 'i', 'P'
    , 'A', '9', '7', '+', 'm', 'V', 'M', 'Y'
    , 'F', 'g', 'f', 'p', 'a', 'T', 't', 'x'
};
#define ALPHABET_SIZE 64

这个时候得请出大杀器了:IDA Pro v6.8,反编译神器。左边是正常版本,右边是jinhou.so。

IDA Pro v6.8

根据数据更改guard.h里面的文件。重新编译下,果真成功解密出来了。

<?php
require_cache(APP_PATH.'/Lib/Action/User/AddonAction.class.php');
?>

事后总结

1、 这次破解没花费多少时间,主要感谢加密方案跟加密代码已经明文告诉我们。
2、 主要的时间是在试探加密参数上,很幸运的是.so文件也没有加壳。
3、 在破解的过程中也了解了PHP加密的缺陷性,所以才有了下一篇文章:通用PHP加密文件还原方法。

查看原文

qpwoeiru96 评论了文章 · 2019-03-05

通用加密php文件还原方法

只是技术交流,并没有恶意,请不要滥用此技术。如果有疑问欢迎跟我交流。

前面一篇文章 实战还原PHP加密文件 说到我发现了PHP加密文件方式的不足,所以才有了这篇文章。

背景分析

众所周知 PHP 是一种脚本语言,脚本语言主要是使用解释运行而非编译运行。所以相对于编译型语言(C、C#、C++),它没有直接生成exedll的能力。所以传统型的加密方式加壳它使用不上。

所以PHP的加密方式一般有两种:混淆和加密。

混淆这种严格意义上不算加密,这种代码就像用拼音缩写命名变量的代码,只是你看起来费劲了点。这种不在我们讨论范围。

加密分为两种方式执行:一种是有扩展(Loader)。 另外一种是无扩展。

首先讲讲无扩展的加密方式。这种加密方式非常的无脑,因为他的解密过程是公开的,而且得利用已知的PHP函数。加密之后的代码一般类似下面:

<?php
eval(base64_decode("cGhwaW5mbygpOw=="));

即使看起来非常复杂的:

jm2.jpg

也不过是上面的变式而已。像这种解密形式公开的非常好破解,只要找到 eval 改成 echo 就行

另外一种有扩展方式就比较复杂了。扩展在这里的作用主要是解密跟执行代码,相比无扩展的形式他将解密跟执行的代码隐藏在扩展里,所以破解较为复杂。

这里的扩展一般分为两种类型的:1、提供解密执行函数的。2、直接覆盖 zend_compile_file 实现解析自定义的php文件功能。

第一种最典型的例子就是我上篇文章提到的 zoeeyguard,主要使用了 zend_eval_string 这个函数来执行php代码。

第二种代表的有:Zend Guard 和 松哥的 php-beast

可以说两种破解思路都差不多。可以优先尝试我上一篇文章里面的思路。

当然如果我上一篇没有解决的情况下怎么办呢?

这个时候就需要奉上大杀器了。

你会发现我在前文一直说到两个的函数 zend_compile_file 和 zend_eval_string,有些聪明的phper已经想到了,对!思路还是 只要找到 eval 改成 echo 就行

实战例子

咱们不能光打嘴炮,光说不做。来个例子:

这次咱们拿松哥的 php-beast 来下手。松哥的代码写得非常漂亮,知道 AES DES 解密耗费时间还写了个 Cache 。

1.下载 linux 的 php 源码包(版本最好在5.6跟5.5之间选择一个,7不支持,BTW:7的改动还是挺大的,很多东西都没兼容,Zend Guard 也不支持7。这里我走了弯路。)

2.找到文件 Zendzend_language_scanner.c 中的函数:zend_op_array *compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) 大概在500多行的样子,将如下的代码加在前面(代码写得烂,切勿吐槽):

ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSRMLS_DC)
{
    zend_lex_state original_lex_state;
    zend_op_array *op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
    zend_op_array *original_active_op_array = CG(active_op_array);
    zend_op_array *retval=NULL;
    int compiler_result;
    zend_bool compilation_successful=0;
    znode retval_znode;
    zend_bool original_in_compilation = CG(in_compilation);

    /** BEGIN **/
    //加在这里的含义就是每次php编译php源码的时候都把这份源码打印一份
    char *buf;
    size_t size;
    zend_stream_fixup(file_handle, &buf, &size);
    printf("\n#######\nFILE TYPE: %d  FILE NAME: %s CONTENT: %s\n#######\n", (*file_handle).type, (*file_handle).filename, buf);
    /** END **/
  1. 在php文件夹下执行:

//节省点时间
./configure --disable-ipv6 --disable-all
make
make install

4.如果顺利的话PHP已经安装成功了。

5.我们写两个测试文件:
作用是: test.php 执行它去加密 before.php 生成 after.php。

//test.php
<?php
$path = __DIR__ . '/before.php';
$newPath = __DIR__ . '/after.php';
$result = beast_encode_file($path, $newPath, 0, BEAST_ENCRYPT_TYPE_DES);
var_dump($result);
//before.php
<?php
print 'http://wx-app.com.cn/' . PHP_EOL;

7.执行以下 php test.php 接着在执行 php after.php。

下面就看截图吧:

执行加密

after.php 已经看不出源码的样子

执行after.php 成功得到源码

总结

1、没有破不了的密码。我们能做的只是增加破解时间。如果这个破解时间大于一个人的寿命,那么这个加密方式肯定是成功的。
2、对于加密的程序,就像破解者需要了解、猜测编写者的思路一样,编写者也需要去了解破解者的方法、手段。这样才能写出破解难度更高的程序。
3、相比加密我觉得混淆更适合php源代码的“加密”。
4、如果你的php代码想要加密发布出去,最好的方式还是提高收费然后直接源码提供。

广告

如果你觉得好 随便支持一点吧

如果您觉得写的好,随便支持一点吧!

http://wx-app.com.cn 是一个微信小程序的资讯站点。

查看原文

qpwoeiru96 赞了回答 · 2017-06-22

解决RegExp中的匹配

正则表达式有一个lastIndex属性,它表示下次匹配从字符串的第几个字符开始,初始值是0;
如果没有g选项,每次匹配完之后,lastIndex的值会变为0;
如果有g选项,如果匹配成功,则lastIndex变成匹配字符串后面的位置,如果没有匹配成功,则lastIndex重置为0;
lastIndex是正则对象的属性,不关心字符串是否是同一个,所以即使匹配的字符串在变,lastIndex也不会清零。

clipboard.png

更明显的效果如下截图
clipboard.png

关注 3 回答 2

qpwoeiru96 发布了文章 · 2017-06-17

合理的技术栈永远比语言来的重要

知道我的人都知道我是做在线教育,准确的应该说是高中生在线一对一辅导平台。

这个平台最核心的服务应该就是上课服务了,这个上课服务里面包含着什么呢?我来列一下:

  1. 白板互动系统(屏幕共享系统)

  2. 语音即使通讯系统

  3. 文字即时通讯系统

  4. 课件中心

  5. 题库中心

其他服务先不说,首先说说这个课件中心,其实也就是将 PPT、PPTX、PDF 之类的文件转换成图片使其可以在白板互动系统里面使用,看似非常简单的模块,我们却碰到了非常多的问题。我总结了我们才到的几个坑,一一列在下面。

网上搜索到的并非是最完美的方案,仅仅能用而已.

最初我们的解决方式是使用网上 LibreOffice 转换 Office 文档到 PDF ,然后再使用 ImageMagick(或者GhostScript)转换 PDF 到指定图片格式的方案。 这个方案非常的简单,我甚至没有上队列,仅仅使用 CRON 定时脚本就搞定了了。

然后我们很快就放弃了这个方案,因为 LibreOffcie 对于 Microsoft Office的支持实在是太有限,很多 PPT 转换出来简直是面目全非。对于内嵌对象的支持跟上非常糟糕,很多内嵌对象往往无法显示,比如数学公式。当然还有转换时间过长的问题,因为我们屌丝的服务器,基本上转换一个 50 页的文档需要5分钟,这个体验太糟糕了!

某些时候云服务也不是非常靠谱.

鉴于我们使用的七牛存储,而七牛上刚好有 亿方云文档转换服务,所以我们很快就将我们的
PPT 转换到 PDF 这个本地步骤变成了使用亿方云的服务。这种方式我们使用了非常长的一段时间。但是无法转换成功的问题还是断续存在的,虽然转换时间大大降低了。总之我们在第一个方案上碰到的问题基本上第二个方案都有除了频率小了,转换速度快了。

clipboard.png

而事实上七牛也含蓄的说明了这个服务是不靠谱的。

有些时候99%的成功概率都难以容忍

随着我们规模的扩大,课件转换问题就像一个幽灵一样环绕在我的周围,因为即使只有 1% 的转换失败概率,那么我们一周也至少会碰到 5 例失败,这个非常影响我们的工作效率。而实际上的转换失败概率远比这个大。所以我明白了很多时候为什么很多公司要把可靠性要定在5个9甚至6个9那么高了。

这个影响非常的大,以至于我们不得不出了一个奇葩的规则补救:如果你上传 PPT 转换失败,请用 WPS 或者 Windows Office 转换成 PDF 再上传。但这个措施一出无异于我们放弃了 PPT 转换的方案,这样还不如直接跟用户说:请用 PDF 上传吧,PPT 我们不支持来的直接。

商业方案也不一定靠谱

到了今年,课件转换这个模块已经可以说是如鲠在喉,不吐不快了,无论是用户还是内部都抱怨多多。

这个时候我们试用了 Aspose.TotalSpire.Office 这两套方案,但是很快就被我们排除了,因为通过不了我们的“单元测试”(一大批来自老师的疑难课件)。

而经我们事后了解亿方云使用的就是 Aspose.Total 的解决方案。

PS: 特别夸一下 Spire.Office 是国人开发的,值得推荐。

Linux并非政治正确

这个时候我不得不把目光瞄向了 Microsoft Office,我想如果 Microsoft Office 都无法转换成功,那么这个世界上也没有什么软件可以转换这个课件了。

但如果使用 Microsoft Office 那么必须得安装 Windows Server 系列的系统,这无疑是对大多数Linux系运维架构的一个挑战(我们就碰到 Windows 自动更新自己重启了!自己重启了!!!)。

我用 COM Interop + C# + Windows Office 2013 迅速撸了一个转换命令行工具。然后迅速上架这个功能,发现效果提升非常明显。

首先转换内容出错肯定不存在问题了。第二个我们发现转换速度非常快,50页的PPT只需要上传、打开PowerPoint、输出图片、上传图片、更新数据库只需要不到10秒。而这个在之前是需要至少100秒的。

这使得我们在我们的产品上不得不改口说:

clipboard.png

而这个30秒还是我们夸大了讲的。

这套方案成功的转换过 500 多页的 PPT, 而这个 500 多页的 PPT 在以前的转换方案中是 100% 失败的,基本上不是在 LibreOffice 挂了,就是在 ImageMagick 挂了。

合理的技术栈永远比语言来的重要

阿里巴巴用 Java 我们也用 Java 吧?
PHP 这东西没什么难度的?
是不是 C++ 开发的会稳定点?
微软的东西,还是算了吧用开源的吧!

以上这些不是不懂技术的人会讲吗,懂技术的人也会讲。我曾经在微博上说过我们一家对手公司因为使用 C++ 开发客户端导致大半年时间浪费在处理 Windows 系统的兼容性上。我也见过多家公司因为听信 Java 好,而把技术方向从 PHP 转向 Java 导致公司一蹶不振的。对于大多数基础技术公司来讲,这根本不是哪种语言好的问题,而是你能不能 Hold 住的问题。

我一直对外讲我们公司用 PHP + JavaScript,但实际上我们用的还有 Go、TypeScript、C#、C++、Java,每一块都实现的非常好,各居其位、各谋其政。但我们的核心目前还是 PHP + JavaScript。

微服务是未来,虚拟化是未来、接口是未来

学过 Golang 的朋友都知道 Golang 的接口机制非常的奇怪,**因为它的接口机制是你有没有继承不是看你有没有说我继承自某某,而是看你会不会某某的功能。

通俗讲就是:如果我们定义 Bird 类只有一个方法叫 Fly(),如果你能 Fly,那么你也叫 Bird 。

所以你用 PHP 实现或者用 Java 实现一个服务化的功能并不重要,重要的是你实现的完不完美。你能不能 Hold
住。

举个例子:我在13年的时候用PHP实现过一个任务队列系统(PHP Coolie),今年我用 Go 重新实现 Windows 下任务队列的 Worker 。对于我来讲不是 Go 好,而是 Windows 下没有 PHP 没有 pcntl 。

Docker 大行其道的当天,对于大多数服务来讲都只需要 docker run 一下,谁还会在乎下面一层是用什么语言?

查看原文

赞 4 收藏 3 评论 0

qpwoeiru96 回答了问题 · 2017-06-14

php导出excel 一万多条浏览器卡得半死怎么解决

10000条不算多的,只是不能用PHPExcel,因为实在太慢了而且容易爆内存。
我写过一个 https://github.com/qpwoeiru96... ,专门用来简单的Excel导出的,可以使用一下。

关注 16 回答 13

qpwoeiru96 发布了文章 · 2016-12-10

优雅的处理短信屏蔽关键词

最近业务系统中经常会报:短信无法发送成功,关键词屏蔽的错误。一个原因随着业务的发展人名重复的次数增加了。第二个自然是我们国家特殊的国情导致的。

短信屏蔽

那是怎么优雅的解决呢?

这里的优雅要保证两点:第一点是用户看到的信息无任何异常。第二点是能正常发送短信。

比较简单的是第二点,一般来讲只要在屏蔽关键词之间加入特殊字符既可以解决但是这本身是跟第一点相悖的。

要保证第一点就需要在选择特殊字符上取巧了。在Unicode字符集中恰恰有一些字符是能满足我们的需求。其中一个代表就是:零宽空格(零宽空格(ZWSP)是一种不可打印的Unicode字符,用于可能需要换行处。)。

PHP例子

function replaceWords($words, $text)
{
    foreach($words as $word) {
        $newWord = trim(preg_replace("#(\S)#u", "$1​", $word), "​");
        $text = str_replace($word, $newWord, $text);
    }
    return $text;
}

例子
图片描述

查看原文

赞 0 收藏 5 评论 0

qpwoeiru96 赞了文章 · 2016-11-30

图像技术在直播中的应用(上)——美颜技术

2016年作为视频直播元年,无论从资本层面不断高涨的估值,到平台主播各种天文数字的报酬,再到像“局座”这样的主流人士争相上直播,直播的社会热度可见一斑。而各大直播平台在经历了直播概念从无到有的阶段后,如何做出差异化,如何解决在野蛮生长期产生的各种涉黄问题,成为了几乎所有平台“成长的烦恼”。抛开政策、内容问题不说,单就技术层面,基于图像技术的创新成为解决这些难题最为可行的办法。作为Tu料的首个分享,我们首先就选择了直播这个热门话题,就图像技术在直播中的应用做一个入门的介绍。该分享系列整理自涂图CTO在架构师沙龙上的演讲内容。

一、技术框架

爱美之心,人皆有之。早期的图片美颜教育了市场,到了直播时代,美颜同样成为直播平台的标配。就目前来说,直播美颜用的主流技术是OpenGL ES。它的好处首先是直接在GPU上运行的,所以性能高、功耗小,用在直播上比较划算。第二,它是跨平台的,iOS和安卓都支持,美颜效果能够直接在这两个平台上达到跨平台的效果。另外OpenGL ES有一个优势,就是有大量现成的开源库。比如像GPUImage,谷歌的grafika,还有基于安卓的一些具有实践意义的库,都是很常用的。网上也有一些比较热心的开发者把自己的美颜的算法直接开源,包括一整套解决方案,从采集到处理到美颜处理,到最后输出一个源编码,都有相关的解决方案。

图片描述

二、美颜原理——混合不能少

市面上大部分美颜产品的一般原理都是相似的。摄像头采集画面,通过一定的方式处理,最后输出一张美颜后的图片。

具体的说,原图首先经过磨皮处理,也就是把痘、斑这些消除掉。然后把经过磨皮的图片与原图进行混合。混合这个步骤是不可缺少的,因为如果只用磨皮后的图,很容易丢失细节。而且把两张图混合,还可以通过调整两个图的混合权重,来控制磨皮的程度,达成不同级别的磨皮效果。当然最后一步也很关键,就是美肤,比如把皮肤肤色调得白一点、红嫩一点,或者一些特殊的需求都可以实现。基本上大部分的美颜,都是这样的流程。

图片描述

三、磨皮算法——去痘就是降噪

本质上说,一张图就是一个二维的数据。如果相临的两个区域灰度值相差比较大,这就意味着存在着噪点。比如脸上有个痘,这个痘自然产生一个灰度值的变化,从抽象意义上说就是一个噪点。所以美颜磨皮的算法,核心是去噪。去噪有很多的办法,网上有各种各样的算法,包括现成的论文。但是不管产用什么算法,美颜的去噪算法都要保持一个特点,那就既要是边界保持,同时还要做到平滑,也就是要滤波。

滤波算法中比较常见的是双边滤波,它的优点在于很高效,因此非常适合移动平台。还有一些其他的算法比较复杂一点,也可以达到那样的效果,但是在移动应用上效率不高。虽然GPU是并行运算的,很适合这种运算,但GPU的能力是有一定范围的,超过这个范围也很耗电。包括双边滤波在内,一种滤波算法也有各种各样实现,考虑在移动平台上运行,可以做一下特别的优化,比如在精度计算里,适当降低精度,达到效果与效率的平衡。

图片描述

四、肤色调整——检测才是难题

在做好磨皮后,最后一个流程就是肤色调整。调整肤色本身的手法已经非常成熟,所以更难的其实在于肤色检测。为什么要做肤色检测?有些早期的直播美颜是没有这个功能的,所以他们就是以美化的肤色为基础,简单粗暴的把整个图像按这个色彩全部处理掉了,这就造成了整体偏色,效果还不如不做。因此,在处理图像前,必须先进行肤色检测,在图像的所有像素点中找到对应肤色范围的像素再进行处理。

图片描述

直播中的肤色检测特殊的地方在于颜色空间的转换。因为跟图像处理相关的颜色空间主要有三种:RGB、YUV、HSV,而这三种色彩空间在直播中都要用到。

RGB是最常见的色彩空间,我们日常用的显示设备就是基于RGB空间,这里不多解释。

YUV是一种比较传统的颜色空间,最早是应用在电视信号的传播里面的,目前多用在直播的数据采样、传输的过程。这是因为人眼对亮度(Y)远比色度(U、V)更敏感,所以YUV比起RGB更容易被压缩,这样就更容易节省带宽进行传输。

而基于HSV颜色空间才是用来做肤色检测的。因为如果用RGB来做肤色检测,需要检测R、G、B三个值是否同时满足肤色的颜色范围,YUV同理。而HSV三个值:色调(H)、饱和度(S),明度(V)中,只有H是关乎肤色的,因此只需要对H进行考虑(H值在25-50之间即可判断为肤色),所需要的运算量自然比RGB少很多。

所以,在直播的不同阶段,要分别使用这三种色彩空间,要不停的把这三种色彩空间进行相互转换。

五、细节——在算法之外

美颜算法固然重要,但美更是一件很主观的事。算法写得非常优美和高效并不能保证美颜效果是最好的,所以用标准的算法处理后,还需要设计师依据自己的经验去进行调整。比如很多平台算法都大同小异,但是为什么最终出来的美颜效果让人感觉还是有差异,其实就是说里面有很多细节在,需要花时间优化,特别是用户的需求是什么,怎样更漂亮。

再举一个例子。很多平台在不同的光照条件下,比如白天、晚上,室内、室外,自然光、人工光,直播出来的美颜效果差异很大。这其中的原因可能就是算法中没有考虑光照因素,结果使很小的因素影响了效果。

所以,这就要求进行大量的测试,用技术手段结合人工去优化,才能保证最佳的美颜效果。还是那句话:细节是魔鬼。

六、性能——不服跑个分?

说到性能,iOS平台目前一般来说没什么问题,或者问题很少。比如 GPUImage是第三方的,算是iOS平台上很有历史的一个库。它实现了很多效果,比如刚才提到的一些算法,在GPUImage里面可以看到简单版的实现,包括怎么写脚本,怎么跑起来,怎么做双边滤波,里面有简单的实现,也可以有很好的效果。包括在做直播的时候,GPUImage可以作为很好的客户端扩展,唯一需要做的事情,就是加上一个推流;因为它包含的从客户端的采集、处理到每一帧的数据,无论是YUV,还是RGB,都可以输出来。所以iOS平台上相对来说问题少很多。

而安卓平台问题就比较大了。因为安卓本身的特点,厂商很多、设备很多、系统版本很多,因此相互间比较难兼容。

第一个是设备问题。比如一个美颜算法在不同机器上跑起来,即使是同一款GPU,性能也可能会差别很大。所以,为了保证一个脚本去适应不同的机器,有种办法是这样的:根据GPU的性能做了一个分级算法,如果评级比较高就采用最复杂的算法,如果性能评级比较低,就把美颜效果降低,保证在大部分环境下使用。

第二是版本的问题。比如,只有4.0以上版本才能够通过相机,直接从相机采集里面获取到一个纹理,这个叫GLTEXTUREEXTERNAL_OES,相机直接把采集到的画面转给GPU,全部通过GPU加速。4.3以后可以做什么事情?从相机采集到处理到编码,走的是全GPU,这是效果最好的也是最快的,当然这对系统的兼容要求是最高的。因为有些厂商在实现的时候没有兼容这些东西,所以GPU加速很难做。

还有输出YUV。很多直播平台都要支持输出YUV。这些YUV的数据就涉及到CPU和GPU的转换过程。因为处理可能是在GPU里面完成的,GPU没法直接输出来,就需要从GPU到CPU的转换,这个目前来说还没有比较好的方案。安卓目前一些底层的GPU还没有开放,有的时候可以通过Graphics Buffer来实现,但是安卓并没有把这个开放出来。如果要有这个东西,唯一能做的是把安卓源代码拿出来,包括把源代码link到关键码里面去,这样才能达到比较好的效果。CPU到GPU的转换,是可以毫秒级别的,如果直接从GPU转换到CPU,可能好的设备也会花费20毫秒左右,这样导致的数据,你预测的是24帧,可能就会有掉帧的情况。在主流上可能影响不大,大部分情况下都是可以接受的,当然这最后也要看用户只有的应用场景。

最后说说常被问到的一个问题: iOS平台、安卓平台都自带有人脸检测的API,为什么不用?

首先是系统频率低、速度慢。苹果可能有这样一个考虑,不要影响到相机API的正常使用,所以频率很低。检测一次可能3秒钟;不是检测一次需要3秒钟,而是3秒钟才给你一个数据,告诉你这张照片有没有人脸。而作为一个实用的产品,一秒钟假设24帧,起码要做十几次检测才可以达到实时的要求,要不然的话跟不上对帧率的要求。在安卓问题更严重,因为还要看设备,有些设备甚至就没有,厂商把这个设置就直接去掉了。另外一个特征点的问题。iOS上面有这些特征点,比如眼睛、嘴巴、鼻子这些,安卓上面是直接没有这些特征点的。

七、美颜2.0——从化妆到整容

以上的内容都属于美颜1.0的概念,而目前最新的美颜技术已经发展到了2.0概念。打个简单的比方,如果美颜1.0只是化妆的话,美颜2.0基本就能达到整容的效果——把眼睛变大,把圆脸变成瓜子脸。而实现这一效果的基础就是人脸识别。这很好理解,只有确定了有没有人脸,知道了五官在什么位置,我们才能把它们“整”的更漂亮。

关于人脸识别,就又是一个大问题了。限于篇幅,我们会在下一期详细展开这个问题说。

查看原文

赞 6 收藏 8 评论 0

认证与成就

  • 获得 48 次点赞
  • 获得 6 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 5 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2011-05-27
个人主页被 1.3k 人浏览