DCT变换(离散余弦变换)

  • 离散余弦变换原理
    一句话概括核心原理:频率不同的正弦波相乘,其黎曼积分总是为零。下图是网上找来的离散余弦变换公式:
  • C代码实现
    dct.h
//==============================================================================
//  Copyright (C) 2019 王小康. All rights reserved.
//
//  作者: 王小康
//  描述: 一维二维DCT变换
//  日期: 2019-05-09
//
//==============================================================================

#ifndef _DCT_H_
#define _DCT_H_

#define  DCT_LEN  8    //一维或二维矩阵边长

void dct_init       (void);
void dct_1Ddct      (float *out, float *in);
void dct_1Didct     (float *out, float *in);
void dct_1Dspectrum (float *out, float *in);
void dct_2Ddct      (float out[][DCT_LEN], float in[][DCT_LEN]);
void dct_2Didct     (float out[][DCT_LEN], float in[][DCT_LEN]);

#endif

dct.c

//==============================================================================
//  Copyright (C) 2019 王小康. All rights reserved.
//
//  作者: 王小康
//  描述: 一维二维DCT变换
//  日期: 2019-05-09
//
//==============================================================================

#include <math.h>
#include "dct.h"

static float  dctTableA[DCT_LEN][DCT_LEN];
static float  sqrt_1_N;
static float  sqrt_2_N;

////////////////////////////////////////////////////////////////////////////////

// 离散余弦变换初始化,生成变换核。一维二维同核。
void dct_init(void){
    sqrt_1_N = sqrt(1.0/DCT_LEN);
    sqrt_2_N = sqrt(2.0/DCT_LEN);
    int i, j;
    for(i=0; i<DCT_LEN; i++){
        dctTableA[0][i] = sqrt_1_N;
    }
    for(i=1; i<DCT_LEN; i++){
        for(j=0; j<DCT_LEN; j++){
            dctTableA[i][j] = sqrt_2_N * cos((3.14159265359*i*(2*j+1))/(2*DCT_LEN));
        }
    }
}

// 一维离散余弦变换
void dct_1Ddct(float *out, float *in){
    unsigned i, j;
    for(i=0; i<DCT_LEN; i++){
        out[i] = 0;
        for(j=0; j<DCT_LEN; j++) out[i] += dctTableA[i][j] * in[j];
    }
}

// 一维离散余弦变换,返回频谱图
void dct_1Dspectrum(float *out, float *in){
    unsigned i, j;
    out[0] = 0;
    for(i=0; i<DCT_LEN; i++) out[0] += in[i];
    out[0] /= DCT_LEN;
    if(out[0] < 0) out[0] = -out[0];
    for(i=1; i<DCT_LEN; i++){
        out[i] = 0;
        for(j=0; j<DCT_LEN; j++) out[i] += dctTableA[i][j] * in[j];
        out[i] *= sqrt_2_N;
        if(out[i] < 0) out[i] = -out[i];
    }
}

// 一维离散余弦逆变换
void dct_1Didct(float *out, float *in){
    unsigned i, j;
    for(i=0; i<DCT_LEN; i++){
        out[i] = 0;
        for(j=0; j<DCT_LEN; j++) out[i] += dctTableA[j][i] * in[j];
    }
}

// 二维离散余弦变换
void dct_2Ddct(float out[][DCT_LEN], float in[][DCT_LEN]){
    float tmp[DCT_LEN][DCT_LEN];
    float sum;
    int i, j, k;
    for(i=0; i<DCT_LEN; i++){
        for(j=0; j<DCT_LEN; j++){
            sum = 0;
            for(k=0; k<DCT_LEN; k++) sum += dctTableA[i][k] * in[k][j];
            tmp[i][j] = sum;
        }
    }
    for(i=0; i<DCT_LEN; i++){
        for(j=0; j<DCT_LEN; j++){
            sum = 0;
            for(k=0; k<DCT_LEN; k++) sum += dctTableA[j][k] * tmp[i][k];
            out[i][j] = sum;
        }
    }
}

// 二维离散余弦逆变换
void dct_2Didct(float out[][DCT_LEN], float in[][DCT_LEN]){
    float tmp[DCT_LEN][DCT_LEN];
    float sum;
    int i, j, k;
    for(i=0; i<DCT_LEN; i++){
        for(j=0; j<DCT_LEN; j++){
            sum = 0;
            for(k=0; k<DCT_LEN; k++) sum += dctTableA[k][i] * in[k][j];
            tmp[i][j] = sum;
        }
    }
    for(i=0; i<DCT_LEN; i++){
        for(j=0; j<DCT_LEN; j++){
            sum = 0;
            for(k=0; k<DCT_LEN; k++) sum += dctTableA[k][j] * tmp[i][k];
            out[i][j] = sum;
        }
    }
}

  • 测试代码
//==============================================================================
//
//  作者: 王小康
//  描述: 一维二维DCT变换测试程序
//  日期: 2019-05-09
//
//==============================================================================

#include <stdio.h>
#include <stdlib.h>
#include "dct.h"

static void show1D(float *x){
    int i;
    for(i=0; i<DCT_LEN; i++){
        printf("%15f", x[i]);
    }
    putchar('\n');
}

static void show2D(float x[][DCT_LEN]){
    int i, j;
    for(i=0; i<DCT_LEN; i++){
        for(j=0; j<DCT_LEN; j++){
            printf("%15f", x[i][j]);
        }
        putchar('\n');
    }
    putchar('\n');
}

int main(int argc, char *argv[]){
    
    float data1[DCT_LEN][DCT_LEN] = {
        { 89, 101, 114, 125, 126, 115, 105,  96},
        { 97, 115, 131, 147, 149, 135, 123, 113},
        {114, 134, 159, 178, 175, 164, 149, 137},
        {121, 143, 177, 196, 201, 189, 165, 150},
        {119, 141, 175, 201, 207, 186, 162, 144},
        {107, 130, 165, 189, 192, 171, 144, 125},
        { 97, 119, 149, 171, 172, 145, 117,  96},
        { 88, 107, 136, 156, 155, 129,  97,  75}
        
        // {200, 200, 200, 200, 200, 200, 200, 200},
        // {240, 220, 190,  20,  20,  60,  30, 160},
        // {200, 160, 180,  40,  40,  50,  50, 120},
        // {220, 120, 190,  60,  60,  80,  70, 100},
        // {190, 100, 100,  80,  80,  90,  99, 111},
        // {100, 150, 120, 100,  70, 100, 120, 130},
        // {111, 111, 121, 123, 110, 111, 134, 167},
        // {120, 220, 222, 222, 210, 109, 190, 100}
    };
    float data2[DCT_LEN][DCT_LEN];
    float data3[DCT_LEN][DCT_LEN];
    
    dct_init();
    
    printf("一维原始数据    :");
    show1D((float*)data1);
    dct_1Dspectrum((float*)data2, (float*)data1);
    printf("一维原始数据频谱:");
    show1D((float*)data2);
    dct_1Ddct((float*)data2, (float*)data1);
    printf("一维DCT变换后   :");
    show1D((float*)data2);
    dct_1Didct((float*)data3, (float*)data2);
    printf("一维DCT逆变换后 :");
    show1D((float*)data3);
    putchar('\n');
    
    printf("二维原始数据:\n");
    show2D(data1);
    putchar('\n');
    
    dct_2Ddct(data2, data1);
    printf("二维DCT变换后:\n");
    show2D(data2);
    putchar('\n');
    
    dct_2Didct(data3, data2);
    printf("二维DCT逆变换后:\n");
    show2D(data3);
    putchar('\n');
    
    return 0;
}

  • 运行效果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容