離散コサイン変換について

このQ&Aのポイント
  • 離散コサイン変換(DCT)についてのサンプルソースコードと書籍の解説を比較しています。
  • 書籍ではデータをクリップして2N点の離散フーリエ変換(DFT)を行う方法を紹介していますが、サンプルソースではDCTの計算式に基づいています。
  • DCTとDFTは違う変換方法であり、サンプルソースの計算はDCTになっています。
回答を見る
  • ベストアンサー

離散コサイン変換について

Webにあるサンプルソースや書籍のソースを見て、 感じの違うソースなのですが、 以下のソースはDCTになっているのでしょうか? 書籍には、 Data[N]=1.2.3[.3.2.1<-付け足] して、2NでDFTすればよいとありました。 ですが、式を見てもそういう記述にはなっていません。 よろしくお願いします。 #include<stdio.h> #define _USE_MATH_DEFINES #include<math.h> int main() { double *Data; double *DataAfter; double *DataConvert; int DataLen; DataLen=16; Data=new double[DataLen*2]; DataAfter=new double[DataLen*2]; DataConvert=new double[DataLen*2]; for(int i=0;i<DataLen;i++) { Data[i]=1.5*i; } for(int i=0;i<DataLen;i++) { Data[DataLen+i]=Data[DataLen-1-i]; } for(int i=0;i<DataLen*2;i++) { DataAfter[i]=0; for(int j=0;j<DataLen*2;j++) { DataAfter[i]+=1.0*Data[i]*cos(M_PI*i*j/(DataLen*2)); } DataAfter[i]/=DataLen; } for(int i=0;i<DataLen*2;i++) { DataConvert[i]=0; for(int j=0;j<DataLen*2;j++) { DataConvert[i]+=1.0*DataAfter[i]*cos(-M_PI*i*j/(DataLen*2)); } DataConvert[i]*=DataLen; } for(int i=0;i<DataLen*2;i++) { printf("%d %f\t%d %f\t%d %f\n",i,Data[i],i,DataAfter[i],i,DataConvert[i]); } delete[] Data; delete[] DataAfter; delete[] DataConvert; getchar(); return 0; }

noname#16581
noname#16581

質問者が選んだベストアンサー

  • ベストアンサー
  • bulgaris
  • ベストアンサー率50% (8/16)
回答No.1

元データから生成した偶対称な信号に対するDFT。 これがDCTの正体(の1つ)です(だったと思います^^;)。 この事は偶対称な信号に対するフーリエ変換式はsin項が消えるという性質が係わってます。 元データ(N個)から生成した偶対称な信号(2N個)に対してDFTを行った結果(2N個)は、偶対称な結果になります。 そのため片方半分の結果(N個)を保持すれば、もう片方の結果が得られるため、変換前<->変換後の相互の変換が可能になります。 この半分の結果は元データに対してDCTを行った結果(N個)と等価なものになります。 なぜならDCTの式は、偶対称な信号に対するフーリエ変換式はsin項が消えるという性質を基に作ることが出来るからです。 質問には答えていませんが、2NのDFTとDCTの関係について述べてみました。 自分自身は大雑把な理解しかしていないので、真実は下記の資料を参考にして下さい。 http://momonga.t.u-tokyo.ac.jp/~ooura/fftman/index.html http://laputa.cs.shinshu-u.ac.jp/~yizawa/InfSys1/basic/index.htm http://laputa.cs.shinshu-u.ac.jp/~yizawa/InfSys1/advanced/index.htm http://www.amazon.co.jp/exec/obidos/ASIN/4789836797/249-4541087-5530767

noname#16581
質問者

お礼

補足の内容が2*i+1にすると、 偶関数(データを倍にする)の意味がわからなくなりました。 忘れてください。 ありがとうございました。

noname#16581
質問者

補足

回答ありがとうございます。 マルチメディア技術の基礎は途中までですが読みました。 やり直しのための信号数学も一通り読みました。 参考URLも一通り知っています。ですが、少し難解です。 後、 http://www5.airnet.ne.jp/tomy/cpro/etc19.htm http://d.hatena.ne.jp/ryoko_komachi/20000601 このあたりでしょうか。 もう少し質問させてもらってよいですか? 若干省略しますが、 以下のようにひとつおきにデータが0となるので、 2*i+1とし、0項目の周波数をスキップしました。 IDCTも同様です。 それとは別に、 for(int j=0;j<DataLen*2;j++)のところで、 *2→*1になりそうなのですが、なりそうでしょうか? 半分の計算で済ませるためには、 重み係数?みたいなsqrt(2.0/N)をかけてやるのでしょうか?たぶん、変換式の導出と関係ありますか? >>そのため片方半分の結果(N個)を保持すれば、 >>もう片方の結果が得られるため、変換前<->変換後の >>相互の変換が可能になります。 と関係ありそうですが、、、。 何度もすみませんがよろしくお願いします。 ////////////////////////////////////////////////////////////////////// // DCT? for(int i=0;i<DataLen;i++) { DataAfter[i]=0; for(int j=0;j<DataLen*2;j++) { DataAfter[i]+=1.0*Data[i]*cos(M_PI*(2*i+1)*j/(DataLen*2)); } DataAfter[i]/=DataLen; } ////////////////////////////////////////////////////////////////////// // IDCT? for(int i=0;i<DataLen;i++) { DataConvert[i]=0; for(int j=0;j<DataLen*2;j++) { DataConvert[i]+=1.0*DataAfter[i]*cos(-M_PI*(2*i+1)*j/(DataLen*2)); } DataConvert[i]*=DataLen; } //////////////////////////////////////////////////////////////////////

関連するQ&A

  • 離散フーリエ変換(DFT)のプログラム

    タイトルの通りのレポートを出されたのですが、その問題に似たサンプルソースすら何をいっているのかわからない状態です。ひとまず、サンプルソースが何をいっているのか理解したいので、いくつか教えてください。 ソースです。質問はその後に書かせてもらいました。 #include<stdio.h> #include<math.h> #define N 10 #define F 0.1 #define PI 3.14151692 #define SQR(x) ((x)*(x)) void func() { int n; FILE *fp; fp=fopen("temporal.data","w"); for(n=0;n<N;n++) fprintf(fp,"%lf\n",cos(2.0*PI*F*(double)n)); fclose(fp); } void get_data(double x[]) { int n; FILE *fp; fp=fopen("temporal.data","r"); for(n=0;n<N;n++) fscanf(fp,"%lf",&x[n]); fclose(fp); } void dft(double x[],double X_r[],double X_i[]) { int k,n; for(k=0;k<N;k++){ X_r[k]=X_i[k]=0.0; for(n=0;n<N;n++){ X_r[k]+=x[n]*cos(2.0*PI*(double)n*(double)k/(double)N); X_i[k]-=x[n]*sin(2.0*PI*(double)n*(double)k/(double)N); } } for(k=0;k<N;k++) printf("X[%d]=%lf+j%lf\n",k,X_r[k],X_i[k]); } void amplitude(double X_r[],double X_i[]) { int k; FILE *fp; double amp; fp=fopen("amp.data","w"); for(k=0;k<N;k++){ amp=sqrt(SQR(X_r[k])+SQR(X_i[k])); fprintf(fp,"%lf\n",amp); } fclose(fp); } main() { double x[N],X_r[N],X_i[N]; func(); get_data(x); dft(x,X_r,X_i); amplitude(X_r,X_i); } 文字数の制限があるみたいなので、質問を別にさせてもらいます。

  • jpeg の DCT 変換がうまくいきません。

    jpeg の DCT 変換がうまくいきません。 jpegを作りたいのですが、DCT変換で詰まってしまいました。 よろしければ、ご教授いただけませんでしょうか? http://www.ann.hi-ho.ne.jp/jiro/assyuku2.htm のDCT変換の例を見て、プログラムを作ってみましたが、 変換例と同じ値に変換されませんでした。 作ってみたプログラムです。 #include <iostream> #include <stdio.h> #define _USE_MATH_DEFINES #include <math.h> using namespace std; void main(int argc, char** argv) {   double cu = 1.0;   double cv = 1.0;     double long sum = 0.0;      int before[8][8] ={     {57,49,44,39,33,28,23,22},     {55,45,39,33,28,21,21,19},     {55,44,37,31,20,17,17,16},     {58,44,37,29,17,15,16,14},     {66,46,31,22,16,14,12,10},     {71,49,32,24,14,12,21,19},     {69,50,30,22,12,4,-1,-2},     {62,42,25,17,7,1,-16,-16}      };   int after[8][8];   ZeroMemory(after,sizeof(int)*64);   for(int v =0; v < 8; v++)   {     if(v) cv =1.0;     if(!v) cv = 1 / sqrt(2.0);     for(int u = 0; u < 8; u++)     {       if(u) cu =1.0;       if(!u) cu = 1 / sqrt(2.0);       sum = 0.0;       for(int y = 0; y < 8; y++)       {         for(int x = 0; x < 8; x++)         {           double long a = cos((2 * x + 1)*u*M_PI/16) * cos(( 2 * y + 1)*v*M_PI/16);           double long d = before[x][y];           sum += d * a;         }       }       after[u][v] = int(cu*cv*sum/4);     }   }   for(int j = 0; j < 8; j++)   {     for(int i = 0; i < 8; i++)     {       cout << after[i][j] << ",";     }     cout << endl;   }   int b;   cin >> b ; }

  • 逆離散フーリエ変換

    逆フーリエ変換でもとの波形を作りたいです。 http://www.geocities.jp/supermisosan/fourier.html を参考にしました。 ///参考URLの逆フーリエ変換プログラム #include <stdio.h> #include <math.h> #define max 10000 //Nの最大限度数 #define pi 3.1415926535 //円周率 int main() { int i,k,n,N; double Ref,Imf; double ReF[max+1],ImF[max+1]; FILE *fourier; FILE *inversef; //逆フーリエ変換したいデータをあらかじめfourier.dataに保存しておく fourier=fopen("fourier.data","r"); inversef=fopen("inversef.data","w"); //データの読み込み。 for(N=0;N<max;N++) { if(fscanf(fourier,"%d %lf %lf",&i,&ReF[N],&ImF[N]) == EOF) { N--; break; } } //フーリエ逆変換 for(k=0;k<N;k++) { Ref=Imf=0.0; for(n=0;n<N;n++) { Ref+=(ReF[n]*cos(2*pi*k*n/N)-ImF[n]*sin(2*pi*k*n/N))/N; Imf+=(ReF[n]*sin(2*pi*k*n/N)+ReF[n]*sin(2*pi*k*n/N))/N; } fprintf(inversef,"%d %f %f\n",k,Ref,Imf); } fclose(fourier); fclose(inversef); return 0; } ////// Refが実数部分の値で、Imfが虚数部分の値になるのですが、 どうしたら元の波形データが得られるのかがわかりません。 教えて頂けたら幸いです。

  • 先に計算したほうがいいのでしょうか?

    下記のプログラムを作ったのですが、 Math.PI / 180 の部分は先に計算しておいたほうが処理が 早くなると言われたのですがそうなのでしょうか? 先に掛け算をしないといけないような気がするのですが。 import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import java.lang.Math; public class Test9 { public static void main(String[] args) { int r = (args.length > 0)? Integer.parseInt(args[0]):100; int n = (args.length > 1)? Integer.parseInt(args[1]):16; int x, y, x1, y1; try { BufferedImage image=new BufferedImage(r*2+10,r*2+10,BufferedImage.TYPE_INT_RGB); Graphics2D g2d=image.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0,0,r*2+10,r*2+10); g2d.setColor(Color.BLACK); for ( double i = 0.0; i < 360.0; i += 360.0 / n ) { x1 = (int) ( r * Math.cos( i * Math.PI / 180 ) ); y1 = (int) ( r * Math.sin( i * Math.PI / 180 ) ); for( double j = i + 360 / n; j < 360.0; j += 360.0 / n ) { x = (int) ( r * Math.cos( j * Math.PI / 180 ) ); y = (int) ( r * Math.sin( j * Math.PI / 180 ) ); g2d.drawLine( x1 + r + 5, y1 * (-1) + r + 5, x + r + 5, y * (-1) + r + 5 ); } } ImageIO.write(image, "JPEG", new File("c:\\test9.jpg")); } catch(Exception e) { e.printStackTrace(); } } }

    • ベストアンサー
    • Java
  • 大学でTurbo C++ 4.0の講義を受けています。その課題で困って

    大学でTurbo C++ 4.0の講義を受けています。その課題で困っています。 問1 画面上に描かれた長方形がキー入力によって原点を中心に回転するアニメーションを実現するプログラムを作成せよ。キーの「1」と「3」で回転角度が増減するようにせよ。 問2 前問のプログラムの図形を画面の中心に平行移動をしてから表示するように改造せよ。結果として画面の中央(320, 240)を中心とした回転移動の角度をキー入力によって増減するようにせよ。 #include <graphics.h> #include <stdio.h> #include <conio.h> #include <math.h> // SIN, COSを使うので必要 #define VN 4 // 頂点数 #define OX 10.0 // 長方形の左上頂点のX #define OY 20.0 // 長方形の左上頂点のY #define WIDTH 50.0 // 長方形の幅 #define HEIGHT 100.0 // 長方形の高さ static double rect_data[VN][2] = { // 長方形頂点配列 { OX, OY }, { OX+WIDTH, OY }, { OX+WIDTH, OY +HEIGHT}, { OX, OY +HEIGHT} }; int main() { int GraphDriver = DETECT, GraphMode; int i, j, k,c,d=0; double new_data[VN][2]; // 変換の結果を入れる配列 double matrix[2][2]; // 回転変換の行列 double theta = M_PI / 6.0; // 回転角度θ=π / 6 registerbgidriver(DOSVGA_driver); // 設定を読み込む initgraph( &GraphDriver, &GraphMode, "") ;  // 画面切替 while(1){ c=getch(); d=d+c; if(c == '1'){ theta = M_PI / d; matrix[0][0] = cos(theta); matrix[0][1] = sin(theta); matrix[1][0] = -sin(theta); matrix[1][1] = cos(theta); for (i = 0; i < VN; i++) { // すべての頂点について for (j = 0; j < 2; j++) { // ここから行列のかけ算 new_data[i][j] = 0.0; for (k = 0; k < 2; k++) { new_data[i][j] += rect_data[i][k] * matrix[k][j]; } } } // これ以後は変換した長方形の表示 moveto((int)(new_data[VN-1][0]), (int)(new_data[VN-1][1])); for (i = 0; i < VN; i++) { lineto((int)(new_data[i][0]), (int)(new_data[i][1])); } cleardevice(); } else if (c == '3') { theta = M_PI / (-d);

  • javaで三角波を合成

    javaでbyte配列を使って for(int i=0;i<triangle_wave.length;i++){ double s=0; for(int j=0;j<=2;j++){ double a = (2*j+1)*i*F0*Math.PI*2/Fs;(Math.sin((2*j+1)*i*F0*Math.PI*2/Fs)/(Math.pow((2*j+1),2)))); s += (double)(Math.pow((-1),j)*(Math.sin(a)/(Math.pow((2*j+1),2)))); } triangle_wave[i]= (byte)(110*8*s/(Math.pow(Math.PI,2))); } AudioFormat format = new AudioFormat((float)Fs,16,1,true,false); InputStream bytefile = new ByteArrayInputStream(triangle_wave); File file = new File("test.wav"); AudioInputStream inputstreem = new AudioInputStream(bytefile,format,wave.length); AudioSystem.write(inputstreem,AudioFileFormat.Type.WAVE,file); のような感じで三角波を作ったのですが、これをwavファイルに出力してSonicVisualiserでスペクトルを見ると基本周波数が出てきません。これはどうしてでしょうか? また、量子化ビット数を16から8にするとスペクトルに基本周波数が出てきます。量子化ビット数を2倍にすると周波数も2倍になるということなのでしょうか?

  • java プログラム 範囲を指定した乱数

    正規乱数をボックスミューらー法で発生させて、 範囲を指定して出力したいと思ってます。 プログラムを作成してみたのですが・・・ 平均50で範囲を48から52にしたいのですが たまに範囲外というか「0.0」が出力されてしまいます。 アドバイスをください import java.util.*; public class test2{ public static void main(String args[]){ double R,S; double r[]=new double[200];  double s[]=new double[200]; double s1[]=new double[200]; Random ran=new Random();    for(int i=0;i<200;i++){     R=ran.nextDouble(); S=50+Math.sqrt(-2*Math.log(ran.nextDouble()))*Math.cos(2*Math.PI*(ran.nextDouble())); r[i]=R; s1[i]=S; if(50-2<s1[i]){ if(50+2>s1[i]){ s[i]=s1[i]; } } } for(int j=0;j<150;j++){ System.out.println(s[j]); } } } お願いします

    • ベストアンサー
    • Java
  • 幾何学変換

    Windowsアプリケーションでボタン一つで画像を45度回転させるプログラムを作りたいのですが、プログラムがよく分かりません。どなたか教えていただけないでしょうか。あと、回転変化後の画像には線形補間法(バイリニア法)を使っての補間処理をして表示させたいのですがよろしいでしょうか。言語はC#です。 自分で色々プログラム組んでるのですがなかなか出来ないです。今組んでるプログラムを実行すると変な風に実行されます。 組んでる途中のプログラム↓↓ private Color[,] SpinImage(Color[,] colImage) { int iHeight = colImage.GetLength(0); int iWidth = colImage.GetLength(1); //Console.WriteLine(iHeight + "\t" + iWidth); double X = 45.0 * Math.PI / 180.0; int iHeight2 = (int)(iWidth * Math.Cos(X) + iHeight * Math.Sin(X)); int iWidth2 = (int)(iWidth * Math.Sin(X) + iHeight * Math.Cos(X)); Color[,] colImage2 = new Color[iHeight2, iWidth2]; //Console.WriteLine(iHeight +"\t" +iWidth); for (int j = 0; j < iHeight-1; j++) { for (int i = 0; i < iWidth-1; i++) { int m = (int)(i * Math.Cos(X) + j * Math.Sin(X)); int n = (int)(-1 * i * Math.Sin(X) + j * Math.Cos(X)) + 100; int iRed = 0; int iGreen = 0; int iBlue = 0; // Console.WriteLine("y:"+ j +"\t i:"+i+"\t m:" +m + "\tn:" + n); iRed = colImage[j, i].R; iGreen = colImage[j, i].G; iBlue = colImage[j, i].B; #region a //if ((j >= 0) && (j < iWidth) && (i >= 0) && (i < iHeight)) //{ // iRed = colImage[i, j].R; // iGreen = colImage[i, j].G; // iBlue = colImage[i, j].B; // colImage[m, n] = Color.FromArgb(iRed, iGreen, iBlue); //} //else //{ // colImage[m, n] = Color.FromArgb(0, 0, 0); //} #endregion if (m < iHeight && n >= 0) { colImage2[m, n] = Color.FromArgb(iRed, iGreen, iBlue); } } } return colImage2; }

  • 実フーリエ係数an,bnをC言語を使って求める問題について質問です。

    実フーリエ係数an,bnをC言語を使って求める問題について質問です。 以前カテゴリを数学として同じ質問を致しました。 今回はプログラムの方面からアドバイスをいただけたらと思い、質問します。 実フーリエ係数を求める式は、 an = 1/π ∫[0..2π]f(x)cos(nx)dx ...1式 bn = 1/π ∫[0..2π]f(x)cos(nx)dx ...2式 扱うデータ数をN、データとデータの間隔をΔxとし、上の式を離散化すると、 an = 1/π シグマ[x=0..N-1]f(x)cos(n*2π*x/N)Δx ...3式 bn = 1/π シグマ[x=0..N-1]f(x)sin(n*2π*x/N)Δx ...4式 ここからは、3,4式を用いて、実フーリエ係数an,bnを求めるプログラムを作成。 f(x)=cosxをフーリエ変換する場合を考えます。 データ数N=4としたとき、Δx=π/2 ----------------------------------------------------------------------------- #include <stdio.h> #include <math.h> #define PI 3.1414926434897//円周率 int main(){ int i,j; int num=4;//データ数 double an[4],bn[4];//フーリエ実係数an,bn double data[4]={1,0,-1,0};//扱うデータ //初期化 for(i=0; i<num;i++){ an[i]=0; bn[i]=0; } //フーリエ変換 for(i=0; i<num; i++){ for(j=0; j<num; j++){ an[i] += data[j]*cos((2*PI*i*j)/num); bn[i] += data[j]*sin((2*PI*i*j)/num); } an[i] /= 2; bn[i] /= 2; } //実フーリエ係数anを表示 for(i=0; i<num; i++){ printf("a[%d]%lf\n",i,an[i]); } return 0; } ----------------------------------------------------------------------------- 実行結果は、以下のようにanだけを表示します。 a[0]0.000000 a[1]1.000000 a[2]0.000000 a[3]1.000000 プログラムは合っていますでしょうか? (自分の予想ではcosxの成分だけが残るのでa[3]も0になると思いました。)

  • 二次元空間におけるフーリエサイン変換のプログラミング(C)について

    二次元のフーリエサイン変換を考えています。 正方形の中心にだけ高い数値を持つ状況をつくり、その波数分布を示したい(最終目標は違いますが・・・)のですがうまくいきません。 下記のように書いてみましたが、コンパイルは通っても、変換した値が一様に0となってしまいます。 よろしければ、見てやってください。 #include<stdio.h> #include<math.h> #define max 100 #define pi 3.141592 main() { //変数i,jが位置、ki,kjが波数に当たります int i,j,ki,kj; //Nはデータ点の数 int N=30; double a[max][max]; double A[max][max]; //aの分布をデルタ関数的なものとします。 for(i=0;i<N;i++){ for(j=0;j<N;j++){ a[i][j]=0; }} a[N/2+1][N/2+1]=100; for(ki=0;ki<N;ki++){ for(kj=0;kj<N;kj++){A[ki][kj]=0.0; for(i=0;i<N;i++){ for(j=0;j<N;j++){ A[ki][kj] += (2/N)*(2/N)*a[i][j]*sin((2*pi*ki*i)/N)*sin((2*pi*kj*j)/N); }}}} return(0); }

専門家に質問してみよう