在几年前的时候在做修图APP算法的时候,

曾经一度想过对3D Lut 预设数据进行压缩,

主要用于提升用户体验。

关于3d lut算法开源的资源也挺多的,就不多做科普了。

有兴趣的朋友,可以去查阅下ffmepg项目相关实现代码。

最早接触3d lut算法是2014年逆向 VSCO Cam 胶片算法的时候,

当然一开始我也不知道它的算法是3d lut,

是反反复复编写各个版本,算法优化,

直到有一天我突然想起一个常量特别奇怪,

后来有一段时间在看3d lut算法资料的时候,觉得算法特别熟悉。

后来自然也就知道啥情况了。

那时候在做一款APP,考虑压缩预设资源的时候,

当时由于项目匆忙,采用LZ压缩算法,自然压缩比并不高。

导致预设文件偏大,有点占资源体积。

原来预想要做一下浮点类型的压缩算法的,这一拖,就没后文了。

很多人很好奇胶片滤镜算法,是怎么实现的,

网上流传了很多个版本,作为一名资深安全研究员,我就说一下大概情况。

早期绝大数APP采用了 2d lut去模拟 VSCO Cam的效果,

思路比较简单,就是做一个2D颜色映射表进行插值实现,一般是512*512*3 的颜色表,

GPUImage里面有具体实现,感兴趣的可以去看下,这里也不展开了。

我这边还保留着当年原汁原味的VSCO Cam胶片算法。

再到近些年,深度学习 神经网络大火,笔者在做手机端前向传播的时候,

再一次碰到类似的问题。

模型量化,模型压缩等等。

模型量化的思路其实也挺简单的,例如32位量化到16位,

或者量化到8位,通过降低精度获取一定的性能提升和资源压缩。

就量化一个操作,就可以做到既提升性能又压缩模型体积,

所以肯定是比较不错的方案。

当然在IOS下还可以考虑采用内存映射的方式,

将物理空间映射为内存空间,以减少内存占用之类的。

当然这种方式必须是操作系统的文件类型支持。

毫无疑问,再一次碰到浮点数据的压缩问题。

现在绝大多数深度学习模型都是采用32位浮点进行存储权重的。

但是很奇怪,好像没看到有谁针对浮点数据进行压缩处理。

难不成浮点数据真的没法压缩了?!

不然,不然,在之前做图像算法的时候,俺对频域算法特别感兴趣,

因为这种转换思维角度的处理方式,的确巧妙。

看了网上很多资料,感觉没多少人能用通俗的语言解释频域。

我就大言不惭,大话一番。

其实频域核心是频,也就是符合一定的频次规律。

有点像计数法,举个例子:八个8 可以记为88888888 也可以记为88,也可以直接记为8.

这就是频,而频域是什么?

频域或者说频率,其实就是一种描述特定频率波率概率乃至频域的表达方式。

换句更通俗的话来说,就是 基于特定表达标准进行计数。

而傅里叶变换也好,余弦变换也好,这里的变换其实就是指的一种表达方式。

有点像,你跟你女朋友约定一种暗号,例如抛个媚眼,表达的是,亲爱的,你懂的。

好了,解释暂告一段落,往下就有点儿童不宜了。

而最经典的压缩算法,莫过于jpeg,这个格式已经家喻户晓了。

虽然也有后起之秀 WebP FLIF 等格式,

但jpeg跟mp3一样成为一个时代的默认标记。

而jpeg就是一种基于dct8x8变换的压缩算法。

具体也不展开了,有兴趣可以去看看jpeg编解码相关。

例如:https://github.com/cpuimage/TinyJPEG

这铺垫有点长,所以是不是可以基于dct 8x8 对浮点数据进行有损压缩呢?

答案,没错就是这么简单粗暴。

数据长度 :8*8*8

按照顺序从 0 - 511 填充数据。

这里给出一个参考数据:

zlib 压缩:
miniz.c version: 10.0.2
Compressed from 2048 to 730 bytes
Decompressed from 730 to 2048 bytes

dct+ zlib 压缩:
miniz.c version: 10.0.2
Compressed from 2048 to 116 bytes
Decompressed from 116 to 2048 bytes

如果符合一定的DCT 规律,dct+zlib 的压缩比,极高。

如果填充的数据是没有规律的随机数据,大多数情况下zlib压缩比高一些。

而jpeg编码中有另一个技巧,就是采用颜色空间,

RGB转换为YCBCR空间,来获取更高的压缩比。

当然也会有一定的信息损失,好像说得有点多了。

打住打住。。

附上完整示例代码:

#ifdef __cplusplus
extern "C" {
#endif #include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "miniz.h"
#include "dct.h"
#include <stdint.h> int test_miniz(const unsigned char *s_pStr, uLong data_len) {
int cmp_status;
uLong src_len = data_len;
uLong cmp_len = compressBound(src_len);
uLong uncomp_len = src_len;
uint8_t *pCmp, *pUncomp;
printf("miniz.c version: %s\n", MZ_VERSION);
// Allocate buffers to hold compressed and uncompressed data.
pCmp = (mz_uint8 *) malloc((size_t) cmp_len);
pUncomp = (mz_uint8 *) malloc((size_t) src_len);
if ((!pCmp) || (!pUncomp)) {
printf("Out of memory!\n");
return EXIT_FAILURE;
}
// Compress the string.
cmp_status = compress(pCmp, &cmp_len, (const unsigned char *) s_pStr, src_len);
if (cmp_status != Z_OK) {
printf("compress() failed!\n");
free(pCmp);
free(pUncomp);
return EXIT_FAILURE;
}
printf("Compressed from %u to %u bytes\n", (mz_uint32) src_len, (mz_uint32) cmp_len);
// Decompress.
cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len);
if (cmp_status != Z_OK) {
printf("uncompress failed!\n");
free(pCmp);
free(pUncomp);
return EXIT_FAILURE;
}
printf("Decompressed from %u to %u bytes\n", (mz_uint32) cmp_len, (mz_uint32) uncomp_len);
// Ensure uncompress() returned the expected data.
if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t) src_len))) {
printf("Decompression failed!\n");
free(pCmp);
free(pUncomp);
return EXIT_FAILURE;
}
free(pCmp);
free(pUncomp);
printf("Success.\n");
return EXIT_SUCCESS; } int test_dct_miniz(float *data, uLong len) {
uLong nCount = len / ;
float *in_data = data;
for (int i = ; i < nCount; i++) {
DCT(in_data, in_data);
in_data += ;
}
test_miniz((const unsigned char *) data, len * sizeof(float));
float *out_data = data;
for (int i = ; i < nCount; i++) {
IDCT(out_data, out_data);
out_data += ;
}
} int main(int argc, char *argv[]) {
printf("float data loss compression algorithm base DCT 8X8.\n");
printf("DCT implementation by Thomas G. Lane.\n");
printf("miniz implementation by Rich Geldreich.\n");
//http://developer.download.nvidia.com/SDK/9.5/Samples/vidimaging_samples.html#gpgpu_dct
printf("blog:http://cpuimage.cnblogs.com/\n");
int is_debug_output = ;
const uLong data_len = * * ;// blocksize
float test_for_miniz[data_len];
float test_for_dct[data_len];
for (int i = ; i < data_len; ++i) {
test_for_miniz[i] = i;
}
memcpy(test_for_dct, test_for_miniz, data_len * sizeof(float));
printf("\nonly miniz:\n");
test_miniz((const unsigned char *) test_for_miniz, data_len * sizeof(float));
printf("\nwith dct:\n");
test_dct_miniz(test_for_dct, data_len); if (is_debug_output) {
for (int i = ; i < data_len; ++i) {
if (test_for_miniz[i] != test_for_dct[i]) {
printf("index %d: %f != %f \n", i, test_for_miniz[i], test_for_dct[i]);
}
}
} printf("\n press any key to exit.\n");
return EXIT_SUCCESS;
} #ifdef __cplusplus
}
#endif

项目地址:https://github.com/cpuimage/DCT_8X8

另外感谢5.1节假日时,某位匿名 网友的一元人民币打赏。

涓涓细流可成江河~

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是: 
gaozhihan@vip.qq.com

浮点数据有损压缩算法 附完整C代码的更多相关文章

  1. 音频降噪算法 附完整C代码

    降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...

  2. 音频自动增益 与 静音检测 算法 附完整C代码

    前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...

  3. 音频自动增益 与 静音检测 算法 附完整C代码【转】

    转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用 ...

  4. 自动曝光修复算法 附完整C代码

    众所周知, 图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了 ...

  5. Java架构师方案—多数据源开发详解及原理(二)(附完整项目代码)

    1. mybatis下数据源开发工作 2. 数据源与DAO的关系原理模型 3. 为什么要配置SqlSessionTemplate类的bean 4. 多数据源应用测试 1. mybatis下数据源开发工 ...

  6. 基于RNN的音频降噪算法 (附完整C代码)

    前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...

  7. mser 最大稳定极值区域(文字区域定位)算法 附完整C代码

    mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...

  8. 经典傅里叶算法小集合 附完整c代码

    前面写过关于傅里叶算法的应用例子. <基于傅里叶变换的音频重采样算法 (附完整c代码)> 当然也就是举个例子,主要是学习傅里叶变换. 这个重采样思路还有点瑕疵, 稍微改一下,就可以支持多通 ...

  9. 基于傅里叶变换的音频重采样算法 (附完整c代码)

    前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...

随机推荐

  1. Ext JS 6开发实例(二) :使用CMD创建应用程序

    由于Ext JS 6将原来的Ext JS和Sencha Touch合并为一个框架,因而在使用CMD来创建应用程序前,需要考虑清楚你是要创建一个通用应用程序,还是仅仅只是针对桌面或移动设备的应用程序. ...

  2. Android初级教程理论知识(第九章多媒体编程)

    多媒体概念 文字.图片.音频.视频 计算机图片大小的计算 图片大小 = 图片的总像素 * 每个像素占用的大小 单色图:每个像素占用1/8个字节 16色图:每个像素占用1/2个字节 256色图:每个像素 ...

  3. 学习笔记6-Android查看应用输出的错误信息 如何部署应用到真实手机 发布软件

    查看应用输出的错误信息 1.      通过LogCat窗口查看信息 右上角图标可以筛选不同级别的信息(比如info等). 右上角的+可以进行信息筛选 把应用部署到真实手机 1.      要把手机的 ...

  4. UNIX网络编程——TCP长连接与短连接的区别

    一.TCP短连接 我们模拟一下TCP短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接.client向server发送消息,server回应client,然后 ...

  5. Dynamics CRM2011/2013 删除个人视图

    这里以2013为例,2011同理.个人视图的功能很人性化,可以设置自己常看数据列表形式而不会去影响别人,但创建容易怎么删除还真不一定能找得到地,具体见下方截图.

  6. MySQL创建视图和Union all的使用案例

     CREATE VIEW netcheck.cpu_mp AS (SELECT  cpu.ID AS id,  cpu.chanel_name AS chanel_name,  cpu.first ...

  7. UNIX环境高级编程——管道和FIFO限制

    系统加于管道和FIFO的唯一限制为: OPEN_MAX     一个进程在任意时刻打开的最大描述符数: PIPE_BUF       可原子的写往一个管道或FIFO的最大数据量. OPEN_MAX的值 ...

  8. 【一天一道LeetCode】#92. Reverse Linked List II

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Reverse ...

  9. 批量替换数据库中所有用户数据表中字段数据类型为char和varchar到nvarchar的脚本

    解决问题:字段类型为char的总是占用指定字节长度(末尾好多空白符号),varchar数据类型长度一个汉字占2个字节,内容存储为中文的字段个人建议全部使用nvarchar. 操作说明:打开SQL Se ...

  10. 动态创建VIEW

    很多人都应该知道 global temporary table 的用法,这里也提出一个动态VIEW的用法,在实际过程中有着很好的独特之处 具体如下: /***************创建PACKAGE ...