1. 程式人生 > >浮點數據有損壓縮算法 附完整C代碼

浮點數據有損壓縮算法 附完整C代碼

com ldr dct vip deb from stdio.h 也好 提升

在幾年前的時候在做修圖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 / 64;
    float *in_data = data;
    for (int i = 0; i < nCount; i++) {
        DCT(in_data, in_data);
        in_data += 64;
    }
    test_miniz((const unsigned char *) data, len * sizeof(float));
    float *out_data = data;
    for (int i = 0; i < nCount; i++) {
        IDCT(out_data, out_data);
        out_data += 64;
    }
}

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 = 1;
    const uLong data_len = 8 * 8* 8;// blocksize
    float test_for_miniz[data_len];
    float test_for_dct[data_len];
    for (int i = 0; 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 = 0; 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節假日時,某位匿名 網友的一元人民幣打賞。

涓涓細流可成江河~

若有其他相關問題或者需求也可以郵件聯系俺探討。

郵箱地址是:
[email protected]

浮點數據有損壓縮算法 附完整C代碼