[ Main Page ]

Scilab / Octaveの比較 - アダマール変換

Freeverb3の開発の際に、残響演算のためのディレイライン拡散のための行列として使われている Hadamard matrix (アダマール行列) を学習するためにScilabとOctaveを使った際の記録。執筆時はScilab-6.0.0 / Octave-4.2.1で行ったが、Scilabは5系から6系になってかなり変わっているので、 一部のマクロ等では互換性がなくなっている。5系の中でも、対応するToolboxが異なり、互換性はかなり乏しいところがある。後の項目では、前の項目の関数に依存している部分もあるので、最初から順に実行する。
端的に言えば、Matlabと同じようにScilabを使おうとすると、かなりの関数を外部Toolboxでインストールする必要が出てくるため、コアのみのインストールのみでMatlabのSignal Processing Toolboxや Image Processing Toolboxと同等に使いたい人にとってはOctaveがお勧めである。Scilabの優位性は、Xcos等のGUIプログラミングができる所ということになる。

英語版Windowsで日本語ロケールの状態で起動すると文字化けすることがあり、その場合は、下記のように英語を指定すると良い。

Scilab

	  setdefaultlanguage('en_US')
	

Hadamard matrixの生成

Octaveでは標準でhadamard()関数が使えるが、Scilabでは標準では使えないようである。行列生成マクロが開発されている他、 Scilab 5系にはImage_Processing_Toolbox_2(IPT2)があり、hadamard()やfwht()が使える。ここには先述のマクロと短くした関数を載せる。 但し、このToolboxのfwht()はMatlabのfwht()と同じく、二次元の行列を入力しても列毎に演算するだけで、二次元の処理をしてくれるわけではないので、処理はできず、後述の方法で対応する。

Scilab

	  // makematrix_hadamard.sci
	  // Copyright (C) 2008-2009 - INRIA - Michael Baudin
	  //
	  // This file must be used under the terms of the CeCILL.
	  // This source file is licensed as described in the file COPYING, which
	  // you should have received as part of this distribution.  The terms
	  // are also available at
	  // http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt

	  //
	  // makematrix_hadamard --
	  //   Returns the Hadamard matrix of size n.
	  //   n is expected to be a power of 2.
	  // Arguments, input
	  //  n : the size of the matrix to return
	  // Reference
	  //  http://en.wikipedia.org/wiki/Hadamard_matrix
	  //
	  function a = makematrix_hadamard ( n )
	    pow2n = log2(n);
	    if ( n - 2^pow2n <>0 ) then
	      errmsg = msprintf(gettext("%s: n is expected to be a power of 2."), "makematrix_hadamard");
	      error(errmsg)
	    end
	    if n==1 then
	      a = ones(1,1)
	    else
	      m = int(n/2)
	      b = makematrix_hadamard(m)
	      a = zeros(n,n)
	      a(1:m,1:m) = b(1:m,1:m)
	      a(1:m,m+1:n) = b(1:m,1:m)
	      a(m+1:n,1:m) = b(1:m,1:m)
	      a(m+1:n,m+1:n) = -b(1:m,1:m)
	    end
	  endfunction

	  // hadamard.sci
	  // Copyright (C) 2008-2009 - INRIA - Michael Baudin
	  //
	  // This file must be used under the terms of the CeCILL.
	  // This source file is licensed as described in the file COPYING, which
	  // you should have received as part of this distribution.  The terms
	  // are also available at
	  // http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt

	  //
	  // hadamard --
	  //   Returns the Hadamard matrix of size n.
	  //   n is expected to be a power of 2.
	  // Arguments, input
	  //  n : the size of the matrix to return
	  // Reference
	  //  http://en.wikipedia.org/wiki/Hadamard_matrix
	  //
	  function a = hadamard ( n )
	    a = makematrix_hadamard ( n );
	  endfunction
	
	  --> hadamard(4)
	   ans  =
	  
	     1.   1.   1.   1.
	     1.  -1.   1.  -1.
	     1.   1.  -1.  -1.
	     1.  -1.  -1.   1.
	

エラーチェック等はなしでもっと簡単な定義もできる。

	  function hmatrix=hadamard(n)
	  h=[1,1;1,-1]
	  hmatrix=h;
	    for n = 1:1:(log2(n)+1)-2
	      // kron() - Kronecker product (.*.)
	      hmatrix = hmatrix.*.h;
	    end
	  endfunction
	

クロネッカー積 kron()は、Scilab、Octaveの両方で使えるが、Scilabでは、.*.という演算子が使える。

Octave

	  GNU Octave, version 4.2.1
	  Copyright (C) 2017 John W. Eaton and others.
	  This is free software; see the source code for copying conditions.
	  There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
	  FITNESS FOR A PARTICULAR PURPOSE.  For details, type 'warranty'.

	  Octave was configured for "x86_64-w64-mingw32".

	  Additional information about Octave is available at http://www.octave.org.

	  Please contribute if you find this software useful.
	  For more information, visit http://www.octave.org/get-involved.html

	  Read http://www.octave.org/bugs.html to learn how to submit bug reports.
	  For information about changes from previous versions, type 'news'.

	  >> hadamard(8)
	  ans =

	     1   1   1   1   1   1   1   1
	     1  -1   1  -1   1  -1   1  -1
	     1   1  -1  -1   1   1  -1  -1
	     1  -1  -1   1   1  -1  -1   1
	     1   1   1   1  -1  -1  -1  -1
	     1  -1   1  -1  -1   1  -1   1
	     1   1  -1  -1  -1  -1   1   1
	     1  -1  -1   1  -1   1   1  -1

	  >>
	

アダマール行列の性質から、ビット演算と剰余演算で生成することもできる。

	  dec2bin(0:7)-'0'
	  (dec2bin(0:7)-'0')'
	  mod((dec2bin(0:7)-'0') * (dec2bin(0:7)-'0')', 2)
	  mod((dec2bin(0:7)-'0') * (dec2bin(0:7)-'0')', 2) * (-2) + 1
	
	  ans =

	     0   0   0
	     0   0   1
	     0   1   0
	     0   1   1
	     1   0   0
	     1   0   1
	     1   1   0
	     1   1   1

	  ans =

	     0   0   0   0   1   1   1   1
	     0   0   1   1   0   0   1   1
	     0   1   0   1   0   1   0   1
	  
	  ans =
	  
	     0   0   0   0   0   0   0   0
	     0   1   0   1   0   1   0   1
	     0   0   1   1   0   0   1   1
	     0   1   1   0   0   1   1   0
	     0   0   0   0   1   1   1   1
	     0   1   0   1   1   0   1   0
	     0   0   1   1   1   1   0   0
	     0   1   1   0   1   0   0   1
	  
	  ans =

	     1   1   1   1   1   1   1   1
	     1  -1   1  -1   1  -1   1  -1
	     1   1  -1  -1   1   1  -1  -1
	     1  -1  -1   1   1  -1  -1   1
	     1   1   1   1  -1  -1  -1  -1
	     1  -1   1  -1  -1   1  -1   1
	     1   1  -1  -1  -1  -1   1   1
	     1  -1  -1   1  -1   1   1  -1
	

Hadamard transform (アダマール変換) の準備とGray code (グレイコード)

上記で生成された行列をHadamard ordered (Natural orderedまたはSylvester) Matrixと呼ぶが、信号処理や画像処理等で行うような低周波から高周波に順に変換された行列もあり、 これはSequency ordered Walsh-Hadamard Matrixと呼ぶ。この係数順序は、 グレイコードのビット反転の順になっている。その他に係数順序がグレイコード順のDyadic ordered (Paley ordered) Matrixというのもある。 最近だとFPGA実装の論文に簡潔な説明が載っている。 通常のバタフライ演算によるFast Walsh-Hadamard Transform (高速アダマール変換)を行うためには、 Hadamard ordered Matrixでなければならず、その他の行列による処理でFWHTを行うには、入力順序の入れ替えが必要となる。 ちなみに、DCTやDFTと違って係数が1か-1で、さらに計算の形から、バタフライ演算はどちらから行っても問題ないし、順序をシャッフルしても問題ない。Wikipediaの図には遠い所との演算からの方法が 例示されているが、論文によっては隣同士の演算からの方法も示してあり、実装上は隣同士の演算からの方が分かりやすいと思う。

Fig. 3 from Impulse-response and reverberation-decaymeasurements made by using pseudorandom sequence Chu, W. T.

Figure from Wikipedia - Fast Walsh Hadamard transform

Discrete Walsh-Hadamard Transform (離散ウォルシュ・アダマール変換) - MATLAB & Simulink Example - MathWorksをScilabとOctaveで実行した例。 MatlabとScilabは似ているようで細かい関数等に互換性がなく、意外に苦労することが多い。Octaveにはfwht()ifwht()があるがScilabにはない。このMatlabの例では、 Octaveではほぼ問題なく動いた。ScilabにはImage Processing Toolboxがあり、この中にWalsh transformとHadamard transformが入っているので、 これを使う場合には別途インストールが必要である。

Walsh matrixの順序のビット演算がわかりにくいが、グレイコードのビット逆転した値で行列の列を整列させている。 グレイコードは、右シフトした値とのXORだが、下記の例では、先にビット逆転してからXORしている。

Scilab

	  // Gray code
	  function list = graycode(N)
	    P=2^N;
	    list = dec2bin(bitxor(int((0:P-1)/2),0:P-1))';
	  endfunction
	  
	  graycode(3)
	  graycode(4)
	
	  --> graycode(3)
	   ans  =

	  !000  !
	  !     !
	  !001  !
	  !     !
	  !011  !
	  !     !
	  !010  !
	  !     !
	  !110  !
	  !     !
	  !111  !
	  !     !
	  !101  !
	  !     !
	  !100  !


	  --> graycode(4)
	   ans  =

	  !0000  !
	  !      !
	  !0001  !
	  !      !
	  !0011  !
	  !      !
	  !0010  !
	  !      !
	  !0110  !
	  !      !
	  !0111  !
	  !      !
	  !0101  !
	  !      !
	  !0100  !
	  !      !
	  !1100  !
	  !      !
	  !1101  !
	  !      !
	  !1111  !
	  !      !
	  !1110  !
	  !      !
	  !1010  !
	  !      !
	  !1011  !
	  !      !
	  !1001  !
	  !      !
	  !1000  !
	

Octave

	  % Gray code	  
	  function list = graycode(N)
	    P=2^N;
	    list = dec2bin(bitxor(bitshift(0:P-1,-1),0:P-1));
	  end
	  
	  graycode(3)
	  graycode(4)
	
	  >> graycode(3)
	  ans =

	  000
	  001
	  011
	  010
	  110
	  111
	  101
	  100

	  >> graycode(4)
	  ans =

	  0000
	  0001
	  0011
	  0010
	  0110
	  0111
	  0101
	  0100
	  1100
	  1101
	  1111
	  1110
	  1010
	  1011
	  1001
	  1000
	

Scilab

	  // Length of Walsh (Hadamard) functions
	  N = 8;
	  hadamardMatrix=hadamard(N)

	  // Hadamard index
	  HadIdx = 0:N-1;
	  // Number of bits to represent the index
	  M = log2(N)+1;

	  //binHadIdx = fliplr(dec2bin(HadIdx,M))-'0';
	  //関数 flipdim(A,1) は flipud(A) と同じ
	  //関数 flipdim(A,2) は関数 fliplr(A) と同じ

	  // Bit reversing of the binary index
	  binHadIdx = strrev(dec2bin(HadIdx(:),M))
	  // Pre-allocate memory
	  binSeqVdx = zeros(N,M);
	  for k = 1:1:N
	      binSeqVdx(k,:) = strtod(strsplit(binHadIdx(k)))';
	  end

	  // Pre-allocate memory
	  binSeqIdx = zeros(N,M-1);
	  //for k = M:-1:2
	  //  binSeqIdx(:,k) = xor(binHadIdx(:,k),binHadIdx(:,k-1));
	  //end
	  for k = M:-1:2
	    binSeqIdx(:,k) = bitxor(binSeqVdx(:,k),binSeqVdx(:,k-1));
	  end

	  // Binary to integer sequency index
	  //SeqIdx = binSeqIdx*(2^((M-1:-1:0)'));
	  for k = 1:1:N
	    // Binary sequency index 
	    HadIdx(k) = bin2dec(strcat(string(binSeqIdx(k,:))));
	  end

	  // 1-based indexing
	  walshMatrix = hadamardMatrix(HadIdx+1,:)
	
	   hadamardMatrix  = 
	  
	     1.   1.   1.   1.   1.   1.   1.   1.
	     1.  -1.   1.  -1.   1.  -1.   1.  -1.
	     1.   1.  -1.  -1.   1.   1.  -1.  -1.
	     1.  -1.  -1.   1.   1.  -1.  -1.   1.
	     1.   1.   1.   1.  -1.  -1.  -1.  -1.
	     1.  -1.   1.  -1.  -1.   1.  -1.   1.
	     1.   1.  -1.  -1.  -1.  -1.   1.   1.
	     1.  -1.  -1.   1.  -1.   1.   1.  -1.
	  
	   walshMatrix  = 
	  
	     1.   1.   1.   1.   1.   1.   1.   1.
	     1.   1.   1.   1.  -1.  -1.  -1.  -1.
	     1.   1.  -1.  -1.  -1.  -1.   1.   1.
	     1.   1.  -1.  -1.   1.   1.  -1.  -1.
	     1.  -1.  -1.   1.   1.  -1.  -1.   1.
	     1.  -1.  -1.   1.  -1.   1.   1.  -1.
	     1.  -1.   1.  -1.  -1.   1.  -1.   1.
	     1.  -1.   1.  -1.   1.  -1.   1.  -1.
	

Octave

	  pkg load signal

	  N = 8;  % Length of Walsh (Hadamard) functions
	  hadamardMatrix = hadamard(N)

	  % Hadamard index
	  HadIdx = 0:N-1;
	  % Number of bits to represent the index
	  M = log2(N)+1;

	  % Bit reversing of the binary index
	  binHadIdx = fliplr(dec2bin(HadIdx,M))-'0';
	  % Pre-allocate memory
	  binSeqIdx = zeros(N,M-1);
	  for k = M:-1:2
	    % Binary sequency index 
	    binSeqIdx(:,k) = xor(binHadIdx(:,k),binHadIdx(:,k-1));
	  end
	  % Binary to integer sequency index
	  SeqIdx = binSeqIdx*pow2((M-1:-1:0)');
	  % 1-based indexing
	  walshMatrix = hadamardMatrix(SeqIdx+1,:)
	
	  hadamardMatrix =

	     1   1   1   1   1   1   1   1
	     1  -1   1  -1   1  -1   1  -1
	     1   1  -1  -1   1   1  -1  -1
	     1  -1  -1   1   1  -1  -1   1
	     1   1   1   1  -1  -1  -1  -1
	     1  -1   1  -1  -1   1  -1   1
	     1   1  -1  -1  -1  -1   1   1
	     1  -1  -1   1  -1   1   1  -1

	  walshMatrix =

	     1   1   1   1   1   1   1   1
	     1   1   1   1  -1  -1  -1  -1
	     1   1  -1  -1  -1  -1   1   1
	     1   1  -1  -1   1   1  -1  -1
	     1  -1  -1   1   1  -1  -1   1
	     1  -1  -1   1  -1   1   1  -1
	     1  -1   1  -1  -1   1  -1   1
	     1  -1   1  -1   1  -1   1  -1
	
	  N = 8;
	  H = hadamard(N);
	  x = 8.*H(1,:) + 12.*H(3,:) + 18.*H(5,:) + 10.*H(8,:);
	  // fwht(x,8,'hadamard')
	  y = x * hadamardMatrix / N
	  // ifwht(y,8,'hadamard')
	  z = y * hadamardMatrix
	  norm(x-z)
	
	   y  = 
	  
	     8.   0.   12.   0.   18.   0.   0.   10.
	  
	   z  = 
	  
	     48.   28.   4.   24.  -8.   12.  -12.  -32.

	   ans  = 
	  
	     0.
	
	  % Fast Walsh-Hadamard transform
	  y1 = fwht(walshMatrix)
	  
	  N = 8;
	  H = hadamard(N);
	  x = 8.*H(1,:) + 12.*H(3,:) + 18.*H(5,:) + 10.*H(8,:);
	  y = fwht(x,N,'hadamard')
	  xHat = ifwht(y,N,'hadamard')
	  norm(x-xHat)
	
	  y1 =

	     1   0   0   0   0   0   0   0
	     0   1   0   0   0   0   0   0
	     0   0   1   0   0   0   0   0
	     0   0   0   1   0   0   0   0
	     0   0   0   0   1   0   0   0
	     0   0   0   0   0   1   0   0
	     0   0   0   0   0   0   1   0
	     0   0   0   0   0   0   0   1

	  y =

	      8    0   12    0   18    0    0   10

	  xHat =
	  
	     48   28    4   24   -8   12  -12  -32
	  
	  ans = 0
	

Sequency ordered / Dyadic ordered Walsh-Hadamard Matrix

Octaveの方がビット演算等の関数が使いやすく、上記の行列順の入れ替えはもう少し簡単に表記できる。 Octaveでは、指定しなければ、fwht()はsequencyが規定であり、これはMatlabと同じ動作である。

Scilab

	  HadIdx
	
	   HadIdx  = 
	  
	     0.   4.   6.   2.   3.   7.   5.   1.
	
	  x = [19 -1 11 -9 -7 13 -15 5];
	  // y = fwht(x)
	  y = x * walshMatrix / 8
	
	   y  = 
	  
	     2.   3.   0.   4.   0.   0.   10.   0.
	

Octave

	  % Sequency (Walsh) Ordered Walsh-Hadamard Matrix
	  pkg load signal
	  N=8;
	  bitand(bitxor(bitshift(bitrevorder(0:N-1),1),bitrevorder(0:N-1)),(N-1))
	  bitand(bitrevorder(bitxor(bitshift(0:N-1,1),0:N-1)),(N-1))

	  % Dyadic (Paley) ordered Matrix
	  bitrevorder(0:N-1)

	  paleyMatrix = hadamardMatrix(bitrevorder(0:N-1)+1,:)
	  fwht(paleyMatrix,N,'dyadic')
	
	  >> bitand(bitxor(bitshift(bitrevorder(0:N-1),1),bitrevorder(0:N-1)),(N-1))
	  ans =

	     0   4   6   2   3   7   5   1

	  >> bitand(bitrevorder(bitxor(bitshift(0:N-1,1),0:N-1)),(N-1))
	  ans =

	     0   4   6   2   3   7   5   1

	  >>
	  >> % Dyadic (Paley) ordered Matrix
	  >> bitrevorder(0:N-1)
	  ans =

	     0   4   2   6   1   5   3   7

	  >>
	  >> paleyMatrix = hadamardMatrix(bitrevorder(0:N-1)+1,:)
	  paleyMatrix =

	     1   1   1   1   1   1   1   1
	     1   1   1   1  -1  -1  -1  -1
	     1   1  -1  -1   1   1  -1  -1
	     1   1  -1  -1  -1  -1   1   1
	     1  -1   1  -1   1  -1   1  -1
	     1  -1   1  -1  -1   1  -1   1
	     1  -1  -1   1   1  -1  -1   1
	     1  -1  -1   1  -1   1   1  -1

	  >> fwht(paleyMatrix,N,'dyadic')
	  ans =

	     1   0   0   0   0   0   0   0
	     0   1   0   0   0   0   0   0
	     0   0   1   0   0   0   0   0
	     0   0   0   1   0   0   0   0
	     0   0   0   0   1   0   0   0
	     0   0   0   0   0   1   0   0
	     0   0   0   0   0   0   1   0
	     0   0   0   0   0   0   0   1
	
	  x = [19 -1 11 -9 -7 13 -15 5];
	  y = fwht(x)
	  z = fwht(x,8,'dyadic')
	  p = x*paleyMatrix/8
	  porder = bitrevorder(0:N-1);
	  fwht(x(porder+1),N,'hadamard')
	  fwht(x,N,'hadamard')(porder+1)
	
	  >> y = fwht(x)
	  y =
	  
	      2    3    0    4    0    0   10    0
	  
	  >> z = fwht(x,8,'dyadic')
	  z =
	  
	      2    3    4    0    0   10    0    0
	  
	  >> p = x*paleyMatrix/8
	  p =
	  
	      2    3    4    0    0   10    0    0
	  
	  >> porder = bitrevorder(0:N-1);
	  >> fwht(x(porder+1),N,'hadamard')
	  ans =
	  
	      2    3    4    0    0   10    0    0
	  >> fwht(x,N,'hadamard')(porder+1)
	  ans =
	  
	      2    3    4    0    0   10    0    0
	

Sequency ordered fast Hadamard transform

アダマール変換が盛んに研究されていた1970年代の本を検索すると、sequency ordered用の高速変換法も提案されていた。最近の実装を検索したところ、MATLAB等の信号処理の分野では上記のバタフライ演算と入出力の置換を実行する方法が多かったが、 Julia用のHadamard.jlパッケージではSequecy ordered用の高速アルゴリズムが実装されていた。 Walsh Functions And Their Applications K Beauchamp by K. G. Beauchampという1975年出版の本 (mirror)には図を含めてかなり詳しく述べられており、Fortranによる実装も載っているので一部転載する。 但し、OCRによる読み取りのため一部誤植の可能性があるのと、最近のコンパイラに通した場合エラーが出る可能性がある。

Fig. 3.3

Fig. 3.5

Fig. 3.6

	  Appendix I 

	  A Signal processing computer programs 192
	  B Program summary 192 
	  C Fast Walsh transform routines FWT and FFWT 193 
	  D Fast Walsh transform routines FHT and FRT 195 

	  C2 Calling sequence 

	  CALL FWT (N,X, WORK) or CALL FFWT (N,X) 

	  where N is an integer constant or variable and must be a power of 2. X is the 
	  name of a real array which on input contains the N data samples and on 
	  output contains the transformed signal. WORK is an array dimensioned 
	  N/2 and is used for temporary work space. 

	  SUBROUTINE FWT(N,X,Y) 

	  C THIS ROUTINES PERFORMS A FAST WALSH TRANSFORM ON AN INPUT SERIES X 
	  C LEAVING THE TRANSFORMED RESULTS IN X, THE ARRAY Y IS USED FOR WORKING 
	  C SPACE. X AND Y ARE DIMENSIONED N WHICH MUST BE A POWER OF 2 
	  C THE RESULTS OF THIS WALSH TRANSFORM ARE IN SEQUENCY ORDER 

	  DIMENSION X(N) ,Y(N) 
	  N2=N/2 
	  M=ALOG2 (FLOAT (N) ) 
	  DO 4 L=1,M 
	   NY=0 
	   NZ=2** (L-1)
	   NZI=2*NZ 
	   NZN=N/NZI 
	   DO 3 I=1, NZN 
	    NX=NY+1 
	    NY=NY+NZ 
	    JS=(I-1) *NZI 
	    JD=JS+NZI+1 
	    DO 1 J=NX,NY 
	     JS=JS+1 
	     J2=J+N2 
	     Y(JS)=X(J)+X(J2) 
	     JD= JD-1 
	     Y ( JD) =ABS (X ( J) -X ( J2 ) ) 
	    1 CONTINUE 
	   3 CONTINUE 
	   CALL FMOVE (Y(1) ,X(1) ,N) 
	  4 CONTINUE 
	  RETURN 
	  END 


	  SUBROUTINE FFWT(N,X) 
	  C THIS SUBROUTINES PERFORMS AN IN PLACE FAST WALSH TRANSFORM LEAVING THE 
	  C TRANSFORMED VALUES IN SEQUENCY ORDER AFTER BIT-REVERSAL 

	  DIMENSION X (N) ,INT (24) 
	  M=ALOG2 (FLOAT (N) ) 
	  DO 10 1=1, M 
	   10 INT (I) =2** (M-l) 
	   DO 4 L=1,M 
	   NZ=2** (L-1) 
	   NZI=2*NZ 
	   NZN=N/NZI 
	   NZ2=NZ/2 
	   IF (NZ2.EQ.0) NZ2=NZ2+1 
	   DO 3 1=1, NZN 
	    JS=(I-1) *NZI 
	    Z=1.0 
	    DO 2 11=1,2 
	     DO 1 J=1,NZ2 
	     JS=JS+1 
	     J2=JS+NZ 
	     HOLD=X(JS) +Z*X(J2) 
	     Z=-Z 
	     X ( J2) =X(JS) +Z*X(J2) 
	     X ( JS) =HOLD 
	     Z=-Z 
	     1 CONTINUE 
	     IF (L.EQ. 1) GO TO 3 
	     Z=-1.0 
	    2 CONTINUE 
	   3 CONTINUE 
	  4 CONTINUE 

	  C BIT-REVERSAL SECTION 
	  C THE TRANSFORMED ARRAY IS REARRANGED INTO SEQUENCY ORDER 

	  NW=0 
	  DO 50 K = 1 , N 
	    C CHOOSE CORRECT INDEX & SWITCH ELEMENTS IF NOT ALREADY SWITCHED 
	    NW1=NW+1 
	    IF (NW1-K) 55,55,60 
	    60 HOLD=X (NW1 ) 
	    X(NW1)=X(K) 
	    X (K) =HOLD 
	    55 CONTINUE 
	  C BUMP UP SERIES BY ONE 
	    DO 70 1=1, M 
	    II=I 
	    IF (NW.LT.INT (I) ) GO TO 80 
	    MW=NW/INT(I) 
	    MWl=MW/2 
	    IF (MW.GT.2*MW1) GO TO 70 
	    GO TO 80 
	    70 NW=NW- INT ( I ) 
	    80 NW=NW+INT (II) 
	    50 CONTINUE 
	  RETURN 
	  END 
	
	  D Fast Walsh transform routines FHT and FRT 

	  The program FHT forms an alternative method to that of FWT described 
	  previously and is slightly faster. A signal flow diagram was given in Fig. 3.7 
	  which indicated the arithmetic steps required for the transformation of a 
	  time series for N = 16 samples. Solid lines meeting at an intersection indicate 
	  addition, whereas dotted lines indicate subtraction. 

	  A disadvantage of the Walsh transform for some purposes is that it is not 
	  invariant to circular time shifts of the series being transformed. The particu- 
	  lar sequence of calculations, used in FHT, enables a slight modification to be 
	  carried out in order to obtain a transform, known as the R-transform (see 
	  Section III I) which is invariant to circular time shift. This modification 
	  involves the replacement of the subtractive terms obtained during the 
	  interim calculations by their absolute values. However, unlike the Walsh transform,
	  the R-transform does not permit the original series to be recovered by
	  a second transformation, i.e. it is not its own inverse.


	  D.1 Program details 

	  The programs are written in FORTRAN for the ICL 1900 computer, using
	  all single precision variables. Program length for the routines are 26 
	  statements or 134 words. 

	  The data input and output is made via a calling sequence. A working space of N values is required. 


	  D2 Calling sequence 

	  CALL FHT (N,X,WORK) or CALL FRT (N,X,WORK) 

	  where N is an integer constant or variable and X and WORK are the names of real
	  arrays holding N samples. WORK is used for temporary working 
	  space. X contains the input signal on entry to the routine and the transformed series
	  is left in X in sequency order on return from the routine. 


	  SUBROUTINE FHT(N,X,Y) 

	  C THIS ROUTINES PERFORMS A FAST WALSH TRANSFORM ON AN INPUT SERIES X
	  C LEAVING THE TRANSFORMED RESULTS IN X, THE ARRAY Y IS USED FOR WORKING
	  C SPACE. X AND Y ARE DIMENSIONED N WHICH MUST BE A POWER OF 2
	  C THE RESULTS OF THIS HADAMARD TRANSFORM ARE IN SEQUENCY ORDER

	  DIMENSION X(N),Y(N)

	  N2=N/2 
	  N=ALOG2(FLOAT (N) ) 

	  DO 4 L=1,M
	   NY=0
	   NZ=2 ** (L-1)
	   NZI=2*NZ
	   NZN=N/NZI

	   DO 3 I=1, NZN
	    NX=NY+1
	    NY=NY+NZ
	    JS=(I-1)*NZI 
	    JD=JS+NZI+1 

	    DO 1 J=NX,NY 
	     JS=JS+1 
	     J2=J+N2 
	     Y(JS)=X(J)+X(J2)
	     JD=JD-1 
	     Y(JD)=X(J)-X(J2)
	    1 CONTINUE 
	   3 CONTINUE 
	   CALL FMOVE(Y(1),X(1),N) 
	  4 CONTINUE 
	  RETURN 
	  END 


	  D FAST WALSH TRANSFORM ROUTINES FHT AND FRT 


	  SUBROUTINE FRT(N,X,Y) 

	  C THIS ROUTINE PERFORMS A FASTER TRANSFORM ON AN INPUT SERIES X 
	  C LEAVING THE TRANSFORMED RESULTS IN X, THE ARRAY Y IS USED FOR WORKING 
	  C SPACE. X IS DIMENSIONED N AND Y N/2 , WHERE N MUST BE A POWER OF 2 
	  C THE RESULTS OF THIS R TRANSFORM ARE IN SEQUENCY ORDER 

	  N2=N/2 

	  DIMENSION X (N) ,Y(N2) 

	  M=ALOG2 (FLOAT (N) ) 
	  Z=-1.0 
	  DO 4 J=l, M 
	   NL=2** (M-J+l) 
	   Jl=2** (J-l) 
	   
	   DO 3 L=1 f Jl 
	    IS= (I-1) *N1+1 
	    II=0 
	    W=2 

	    DO 1 I=IS,IS+N1-1,2 
	     A=X(I) 
	     X (IS+Il) =A+X (I+1) 
	     II = II+1 
	     Y (II) =(X(I+1) -A) *W 
	     W=W*Z 
	    1 CONTINUE 
	    CALL FMOVE (Y ( 1 ) ,X(IS+Nl/2) ,Nl/2) 
	   3 CONTINUE 
	  4 CONTINUE 
	  RETURN 
	  END 
	

ScilabとOctaveでNatural orderとSequency orderのバタフライ演算を実装してみる。Sequency orderのバタフライ演算では、Natural orderでの先述のような順序シャッフルが利かないので注意。

Scilab / Octave

fhtnat() 1D natural (Hadamard) ordered fast Hadamard transform
fhtseq() 1D sequency (Walsh) ordered fast Hadamard transform
下記はScilab、Octaveの両方で使用可能。上述の通り、fhtnat()の
for level=1:log2(N)

for level=log2(N):-1:1
のように逆順でも可。

	  function data=fhtnat(data)
	  N = length(data);
	  tmp = zeros(1,N);
	  level = 1;
	  for level=1:log2(N)
	    addbin = repmat([ones(1,2^(level-1)), zeros(1,2^(level-1))],1,2^(log2(N)-level));
	    addindex = find(addbin==1);
	    subindex = find(addbin==0);
	    tmp(addindex) = data(addindex) + data(subindex);
	    tmp(subindex) = data(addindex) - data(subindex);
	    data = tmp;
	  end
	  data = data/N;
	  endfunction
	  
	  function data=fhtseq(data)
	  N = length(data);
	  tmp = zeros(1,N);
	  addindex2 = find(repmat([1,0],1,N/2)==1);
	  subindex2 = find(repmat([1,0],1,N/2)==0);
	  for level=log2(N):-1:2
	    addbin = repmat([ones(1,2^(level-1)), zeros(1,2^(level-1))],1,2^(log2(N)-level));
	    addindex = find(addbin==1);
	    subindex = find(addbin==0);
	  
	    decbin = repmat([1,0,0,1],1,N/4);
	    addindexf = find(decbin==1);
	    subindexf = find(decbin==0);
	    
	    tmp(addindex) = data(addindex2) + data(subindex2);
	    tmp(subindex) = data(addindexf) - data(subindexf);
	    data = tmp;
	  end
	  tmp(addindex2) = data(addindex2) + data(subindex2);
	  tmp(subindex2) = data(addindex2) - data(subindex2);
	  data = tmp;
	  
	  data = data/N;
	  endfunction
	

Octave

	  pkg load signal;
	  x = [19 -1 11 -9 -7 13 -15 5];
	  y = fwht(x,length(x),'hadamard')
	  y = fhtnat(x)
	  y = fwht(x,length(x),'sequency')
	  y = fhtseq(x)
	
	  y =
	  
	      2    0    4    0    3   10    0    0
	  
	  y =
	  
	      2    0    4    0    3   10    0    0

	  y =
	  
	      2    3    0    4    0    0   10    0
	  
	  y =
	  
	      2    3    0    4    0    0   10    0
	

他のsequency ordered fast Hadamard transformの実装を見ると、少し順序を変えた方法が良く使われているようだ。TVAL3 (TV minimization by Augmented Lagrangian and ALternating direction ALgorithms)で実装されている方法と記述されているものが多いが、上のFortranのコードと同じ方法である。 もちろん、この方法も順序を逆にすることはできない。

Octave

	  function data=fhtseqv2(data)
	  N = length(data);
	  tmp = zeros(1,N);

	  addindex2 = find(repmat([1,0],1,N/2)==1);
	  subindex2 = find(repmat([1,0],1,N/2)==0);
	  tmp(addindex2) = data(addindex2) + data(subindex2);
	  tmp(subindex2) = data(addindex2) - data(subindex2);
	  data = tmp;

	  addindexf = find(repmat([1,0,0,1],1,N/4)==1);
	  subindexf = find(repmat([1,0,0,1],1,N/4)==0);

	  for level=2:1:log2(N)
	    vecbin = repmat([ones(1,2^(level-1)), zeros(1,2^(level-1))],1,2^(log2(N)-level));
	    addindex = find(vecbin==1);
	    subindex = find(vecbin==0);
	    tmp(addindexf) = data(addindex) + data(subindex);
	    tmp(subindexf) = data(addindex) - data(subindex);
	    data = tmp;
	  end
	  data = data/N;
	  endfunction

	  pkg load signal;
	  x = [19 -1 11 -9 -7 13 -15 5];
	  y = fwht(x,length(x),'sequency')
	  y = fhtseqv2(x)
	
	  y =
	  
	      2    3    0    4    0    0   10    0
	  
	  y =
	  
	      2    3    0    4    0    0   10    0
	

Python

Fast Walsh-Hadamard Transform in Pythonからの抜粋。

	  def FWHT(x):
	      """ Fast Walsh-Hadamard Transform
	      Based on mex function written by Chengbo Li@Rice Uni for his TVAL3 algorithm.
	      His code is according to the K.G. Beauchamp's book -- Applications of Walsh and Related Functions.
	      """
	      x = x.squeeze()
	      N = x.size
	      G = N/2 # Number of Groups
	      M = 2 # Number of Members in Each Group

	      # First stage
	      y = np.zeros((N/2,2))
	      y[:,0] = x[0::2] + x[1::2]
	      y[:,1] = x[0::2] - x[1::2]
	      x = y.copy()
	      # Second and further stage
	      for nStage in xrange(2,int(log(N,2))+1):
	          y = np.zeros((G/2,M*2))
	          y[0:G/2,0:M*2:4] = x[0:G:2,0:M:2] + x[1:G:2,0:M:2]
	          y[0:G/2,1:M*2:4] = x[0:G:2,0:M:2] - x[1:G:2,0:M:2]
	          y[0:G/2,2:M*2:4] = x[0:G:2,1:M:2] - x[1:G:2,1:M:2]
	          y[0:G/2,3:M*2:4] = x[0:G:2,1:M:2] + x[1:G:2,1:M:2]
	          x = y.copy()
	          G = G/2
	          M = M*2
	      x = y[0,:]
	      x = x.reshape((x.size,1))
	      return x/float(N)
	

C++ (Matlab MEX)

TVAL3からの抜粋。

	  /*=================================================================
	   *
	   * \file fWHtrans.cpp
	   *
	   *
	   * This code computes the (real) fast discrete Walsh-Hadamard transform with sequency order according to the K.G. Beauchamp's book -- Applications of Walsh and Related Functions.
	   *
	   *
	   * This file is written by Chengbo Li from Computational and Applied Mathematics Department of Rice University.
	   *
	   *
	   * This is a MEX-file for MATLAB.
	   *
	   * 02/15/2010
	   *
	   *=================================================================*/
	  #include <math.h>
	  #include "mex.h"
	  #include "matrix.h"
	  //#include <stdio.h>
	  //#include <stdlib.h>
	  // #include <malloc.h>
	  // #include <stack>


	  //! Matlab entry function
	  /*!
	   * \param nlhs number of left-hand-side output arguments
	   * \param plhs mxArray of output arguments
	   * \param nrhs number of right-hand-side input arguments
	   * \param prhs mxArray of input arguments
	   */
	  void mexFunction( int nlhs, mxArray *plhs[],
	          int nrhs, const mxArray*prhs[] )
	          
	  {
	      int p, nStage, L, clm;
	      int i, j, m, n, N, J, K, M;
	      double *v_in, *v_out, *v_ext, *v_final, *temp;
	      
	      /* Check for proper number of arguments */
	      if (nrhs != 1) {
	          mexErrMsgTxt("Only one input arguments required.");
	      }
	      else if (nlhs > 1) {
	          mexErrMsgTxt("Too many output arguments.");
	      }
	      
	      /* Get the size and pointers to input data. */
	      m  = mxGetM(prhs[0]);
	      n  = mxGetN(prhs[0]);
	      
	      
	      /* Make sure that both input and output vectors have the length with 2^p where p is some integer. */
	      p = ceil(log2(m));
	      N = pow(2, p);
	      
	      plhs[0] = mxCreateDoubleMatrix(N, n, mxREAL);
	      
	      v_final = mxGetPr(plhs[0]);
	      v_in = mxGetPr(prhs[0]);
	      
	      /* Extend the input vector if necessary. */
	      v_ext = (double*) mxCalloc(N, sizeof(double));
	      v_out = (double*) mxCalloc(N, sizeof(double));
	      
	      for (clm=0; clm<n; clm++) {
	          
	          /* C is row major while Matlab is column major */
	          for (j=0; j<m; j++){
	              v_ext[j] = (v_in+clm*m)[j];
	          }
	          for (j=m; j<N; j++){
	              v_ext[j] = 0;
	          }
	          
	          
	          for (i=0; i<N-1; i = i+2) {
	              v_ext[i] = v_ext[i] + v_ext[i+1];
	              v_ext[i+1] = v_ext[i] - v_ext[i+1] * 2;
	          }
	          L = 1;
	          
	          /* main loop */
	          for (nStage = 2; nStage<=p; ++nStage){
	              M = pow(2, L);
	              J = 0;
	              K = 0; // difference between Matlab and C++
	              while (K<N-1){
	                  for (j = J;j < J+M-1; j = j+2){
	                      /* sequency order  */
	                      v_out[K] = v_ext[j] + v_ext[j+M];
	                      v_out[K+1] = v_ext[j] - v_ext[j+M];
	                      v_out[K+2] = v_ext[j+1] - v_ext[j+1+M];
	                      v_out[K+3] = v_ext[j+1] + v_ext[j+1+M];
	                      
	                      K = K+4;
	                  }
	                  
	                  J = J+2*M;
	              }
	              
	              temp = v_ext;
	              v_ext = v_out;
	              v_out = temp;
	              
	              L = L+1;
	          }
	          
	          /* Perform scaling of coefficients. */
	          for ( i =0; i<N; ++i){
	              (v_final+clm*N)[i] = v_ext[i]/N;
	          }
	      }
	      
	      /* Set free the memory. */
	      mxFree(v_out);
	      mxFree(v_ext);
	      
	      return;
	  }
	

Python 3 Tips

Pythonでも行列演算は可能で、ScilabやOctaveやMatlab以外に何か使う言語となるとまず挙げられる。Python 2からPython 3になりかなり変更点があるが、 アダマール変換を実装した例を転載する。上記はPython 2用だったのをPython 3用に書き直した。//による整数除算や、xrangeの変換、printのブラケットが主な変更点である。

FWHT.py

	  # -*- coding: utf-8 -*-
	  """
	  Created on Thu Jun 11 15:21:43 2015
	  Fast Walsh-Hadamard Transform with Sequency Order
	  Author: Ding Luo@Fraunhofer IOSB
	  """
	  from math import log
	  import numpy as np
	  import GrayCode
	  from time import clock

	  def get_sequency_list(inputArray):
	      """ Sort input 1D array into sequency order
	      Utilizes gray code generation from a Python recipe from Internet.
	      """
	      length = inputArray.size
	      bitlength = int(log(length,2))
	      # Gray Code
	      graycodes=GrayCode.GrayCode(bitlength)
	      # Bitreverse of gray code
	      bitreverse = [int(graycodes[i][::-1],2) for i in range(length)]
	      
	      outputArray = inputArray.copy()
	      outputArray[bitreverse] = inputArray[:]

	      return outputArray

	  def SFWHT(X):
	      """ 'Slower' Fast Walsh-Hadamard Transform
	      Step#1 Get sequency-ordered input
	      Step#2 Perform Hadamard Transform
	      """
	      x=get_sequency_list(X)
	      N = x.size
	      M = int(log(N,2))
	      out = x.copy()
	      for m in range(M):
	          outtemp = out.copy()         
	          step = 2**m
	          numCalc = 2**m
	          for g in range(0,N,2*step): # number of groups
	              for c in range(numCalc):
	                  index = g + c
	                  out[index] = outtemp[index] + outtemp[index+step]
	                  out[index+step] = outtemp[index] - outtemp[index+step]
	      return out/float(N)


	  def FWHT(x):
	      """ Fast Walsh-Hadamard Transform
	      Based on mex function written by Chengbo Li@Rice Uni for his TVAL3 algorithm.
	      His code is according to the K.G. Beauchamp's book -- Applications of Walsh and Related Functions.
	      """
	      x = x.squeeze()
	      N = x.size
	      G = N // 2 # Number of Groups
	      M = 2 # Number of Members in Each Group

	      # First stage
	      y = np.zeros((G,2))
	      y[:,0] = x[0::2] + x[1::2]
	      y[:,1] = x[0::2] - x[1::2]
	      x = y.copy()
	      # Second and further stage
	      for nStage in range(2,int(log(N,2))+1):
	          y = np.zeros((G//2,M*2))
	          y[0:G//2,0:M*2:4] = x[0:G:2,0:M:2] + x[1:G:2,0:M:2]
	          y[0:G//2,1:M*2:4] = x[0:G:2,0:M:2] - x[1:G:2,0:M:2]
	          y[0:G//2,2:M*2:4] = x[0:G:2,1:M:2] - x[1:G:2,1:M:2]
	          y[0:G//2,3:M*2:4] = x[0:G:2,1:M:2] + x[1:G:2,1:M:2]
	          x = y.copy()
	          G = int(G/2)
	          M = M*2
	      x = y[0,:]
	      x = x.reshape((x.size,1))
	      return x/float(N)

	     
	  if __name__ == "__main__":
	      x = np.random.random(1024**2)
	      t1 = clock()
	      y1 = SFWHT(x)
	      t1 = clock() - t1
	      print(t1)
	      t2 = clock()
	      y2 = FWHT(x)
	      t2 = clock() - t2
	      print(t2)
	

GrayCode.py

	  def isOdd(integer):
	      #assert isinstance(integer, int)
	      return integer % 2 == 1

	  def isEven(integer):
	      #assert isinstance(integer, int)
	      return integer % 2 == 0

	  def _list_to_string(li):
	      return ''.join(map(str, li))

	  class GrayCode(object):
	      def __init__(self, nbits):
	          self._nbits = nbits
	          self._grayCode = []
	          self.__generate()

	      def __getitem__(self, i):
	          return self._grayCode[i]

	      def __str__(self):
	          return str(self._grayCode)

	      __repr__ = __str__

	      def __iter__(self):            
	          return self._grayCode.__iter__()

	      def __generate(self):
	          li = [0 for i in range(self._nbits)]
	          self._grayCode.append(_list_to_string(li))

	          for term in range(2, (1<<self._nbits)+1):
	              if isOdd(term):                
	                  for i in range(-1,-(self._nbits),-1):
	                      if li[i]==1:                        
	                          li[i-1]=li[i-1]^1                        
	                          break
	                      
	              if isEven(term):
	                  li[-1]=li[-1]^1

	              self._grayCode.append(_list_to_string(li))

	  class GrayCodeIterator(object):
	      def __init__(self, nbits):
	          self._nbits = nbits

	      def __iter__(self):
	          li = [0 for i in range(self._nbits)]
	          yield _list_to_string(li)

	          for term in range(2, (1<<self._nbits)+1):
	              if isOdd(term):                
	                  for i in range(-1,-(self._nbits),-1):
	                      if li[i]==1:                        
	                          li[i-1]=li[i-1]^1                        
	                          break
	                      
	              if isEven(term):
	                  li[-1]=li[-1]^1

	              yield _list_to_string(li)


	  if __name__=='__main__':
	      d = 4
	      codes=GrayCode(d)
	      print ('%d digits gray codes:', d)
	      print (codes)
	      print ('Using Iterator:')
	      #for c in GrayCode(20):  
	      #    print c
	      for c in GrayCodeIterator(20):
	          print (c)
	

Python 3

	  import FWHT as fwht
	  import GrayCode as gc
	  import numpy as np
	  import importlib
	  importlib.reload(fwht)
	  importlib.reload(gc)
	  
	  a = np.array([19, -1, 11, -9, -7, 13, -15, 5])
	  
	  # Secuency ordered
	  print("Secuency ordered")
	  print(fwht.SFWHT(a))
	  print(fwht.FWHT(a).T)
	  
	  from scipy.linalg import hadamard
	  print(hadamard(4, dtype=complex))
	  print(hadamard(8))
	  # Hadamard ordered
	  print(np.dot(a,hadamard(8))/8)
	  
	  # // division error example
	  print("matrix indice")
	  print(a[0:8//2])
	  print(a[0:8/2])

	  print(np.zeros((8//2,2)))
	  np.zeros(8//2,2)
	
	  Secuency ordered
	  [  2.   3.   0.   4.   0.   0.  10.   0.]
	  [[  2.   3.   0.   4.   0.   0.  10.   0.]]
	  [[ 1.+0.j  1.+0.j  1.+0.j  1.+0.j]
	   [ 1.+0.j -1.-0.j  1.+0.j -1.-0.j]
	   [ 1.+0.j  1.+0.j -1.-0.j -1.-0.j]
	   [ 1.+0.j -1.-0.j -1.-0.j  1.+0.j]]
	  [[ 1  1  1  1  1  1  1  1]
	   [ 1 -1  1 -1  1 -1  1 -1]
	   [ 1  1 -1 -1  1  1 -1 -1]
	   [ 1 -1 -1  1  1 -1 -1  1]
	   [ 1  1  1  1 -1 -1 -1 -1]
	   [ 1 -1  1 -1 -1  1 -1  1]
	   [ 1  1 -1 -1 -1 -1  1  1]
	   [ 1 -1 -1  1 -1  1  1 -1]]
	  [  2.   0.   4.   0.   3.  10.   0.   0.]
	  matrix indice
	  [19 -1 11 -9]

	  ---------------------------------------------------------------------------
	  TypeError Traceback (most recent call last)
	  <ipython-input-194-830b68925cc9> in <module>()
	       22 print("matrix indice")
	       23 print(a[0:8//2])
	  ---> 24 print(a[0:8/2])

	  TypeError: slice indices must be integers or None or have an __index__ method
	
	  print(np.zeros((8//2,2)))
	  np.zeros(8//2,2)
	  
	  np.zeros(8/2,2)
	
	  [[ 0.  0.]
	   [ 0.  0.]
	   [ 0.  0.]
	   [ 0.  0.]]
	  
	  ---------------------------------------------------------------------------
	  TypeError Traceback (most recent call last)
	  <ipython-input-205-d3ad594fc4dc> in <module>()
	        1 print(np.zeros((8//2,2)))
	  ----> 2 np.zeros(8//2,2)
	  
	  TypeError: data type not understood
	  
	  ---------------------------------------------------------------------------
	  TypeError Traceback (most recent call last)
	  <ipython-input-206-805c1441d66f> in <module>()
	  ----> 1 np.zeros(8/2,2)
	  
	  TypeError: 'float' object cannot be interpreted as an integer
	

画像読み込みや書き込みと二次元離散コサイン変換(DCT)

Matlabには、imreadやimwriteが装備されているが、Scilabでは標準状態で使えない。6.0.xでは Image Processing and Computer Vision (IPCV) Toolboxを導入すると使えるようになる。 5.x系では別のToolbox(Image_Processing_Toolbox_2 (IPT2) for 5.3.xScilab Image and Video Processing (SIVP) toolbox for 5.5.x) が存在し、バージョンによって使えるToolboxが異なるのはScilabの欠点の一つである。

まずはHadamard matrixを画像に書き出すのと、画像を読み込んで二次元離散コサイン変換を行った結果を表示するところまで。原点付近に大きな値が集まっているのがよくわかる。

画像セット

Scilab

	  atomsInstall("IPCV")

	  N=256;
	  hadamardMatrix=hadamard(N);

	  function wmat=had2wal(hmat,msize)
	  HadIdx = 0:msize-1;
	  M = log2(msize)+1;
	  binHadIdx = strrev(dec2bin(HadIdx(:),M));
	  binSeqVdx = zeros(msize,M);
	  for k = 1:1:msize
	      binSeqVdx(k,:) = strtod(strsplit(binHadIdx(k)))';
	  end
	  binSeqIdx = zeros(msize,M-1);
	  for k = M:-1:2
	      binSeqIdx(:,k) = bitxor(binSeqVdx(:,k),binSeqVdx(:,k-1));
	  end
	  for k = 1:1:msize
	      HadIdx(k) = bin2dec(strcat(string(binSeqIdx(k,:))));
	  end
	  wmat = hmat(HadIdx+1,:);
	  endfunction
	  
	  walshMatrix = had2wal(hadamardMatrix,N);
	  imwrite(hadamardMatrix,"hmat.bmp");
	  imwrite(walshMatrix,"wmat.bmp");
	  
	  img_orig = double(imread("BRIDGE.bmp"));
	  imshow(uint8(img_orig));
	  surf(imdct(img_orig))
	  surf(imdct(img_orig)(1:50,1:50))
	

hmat.bmp
Hadamard matrix
wmat.bmp
Walsh matrix

bridge.jpg
bridge.jpg
bridge.jpg

Octave

	  img_orig = double(imread("Text.bmp"));
	  imshow(uint8(img_orig));
	  img_dct = dct2(img_orig);
	  surf(img_dct);
	  surf(img_dct(1:50,1:50));
	

bridge.png

bridge.png
bridge.png

Two-Dimensional Hadamard Transform 二次元アダマール変換

二次元FFTや二次元DCTと同様、行及び列それぞれの2回直交変換すればよい。低周波から高周波に順に並べられた係数を得るには、Sequency Matrixを使うが、 その他の行列を使っても良い。高速アダマール変換のためには、Natural order以外では、上述の通り入力順の入れ替えが必要である。

アダマール変換と離散コサイン変換は、計数行列形が比較的似ているだけだが、遊びで逆変換をそれぞれ入れ替えた基底を使うと、前衛芸術のような画像が出る。

Scilab

	  function imat = fwht2d(imat,hmat)
	  N = sqrt(length(imat))
	  tmat = zeros(N,N);
	  for i = 1:N
	    tmat(i,:) = imat(i,:) * hmat / N;
	  end
	  imat = zeros(N,N);
	  for j = 1:N
	    imat(:,j) = hmat * tmat(:,j)
	  end
	  endfunction
	  
	  a2d = matrix(1:64,8,8)'

	  // Sequency order
	  fwht2d(a2d,walshMatrix)
	  walshMatrix*a2d*walshMatrix / 8
	
	   a2d  = 
	  
	     1.    2.    3.    4.    5.    6.    7.    8. 
	     9.    10.   11.   12.   13.   14.   15.   16.
	     17.   18.   19.   20.   21.   22.   23.   24.
	     25.   26.   27.   28.   29.   30.   31.   32.
	     33.   34.   35.   36.   37.   38.   39.   40.
	     41.   42.   43.   44.   45.   46.   47.   48.
	     49.   50.   51.   52.   53.   54.   55.   56.
	     57.   58.   59.   60.   61.   62.   63.   64.

	   ans  =
	  
	     260.  -16.   0.  -8.   0.   0.   0.  -4.
	    -128.   0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	    -64.    0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	    -32.    0.    0.   0.   0.   0.   0.   0.
	  
	   ans  =
	  
	     260.  -16.   0.  -8.   0.   0.   0.  -4.
	    -128.   0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	    -64.    0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	     0.     0.    0.   0.   0.   0.   0.   0.
	    -32.    0.    0.   0.   0.   0.   0.   0.
	
	  // 通常は意味のない行為
	  img_dct = imdct(img_orig);
	  img_wal = fwht2d(img_dct,walshMatrix);
	  imshow(uint8( (img_wal-min(img_wal))/(max(img_wal)-min(img_wal))*255 ));
	  
	  img_mat = fwht2d(img_orig,walshMatrix);
	  img_ict = imidct(img_mat);
	  imshow(uint8( (img_ict-min(img_ict))/(max(img_ict)-min(img_ict))*255 ));
	

bridge_dctht.png
bridge_htdct.png

   <rindolf>  "Who's the idiot that wrote this code?"
   <rindolf>  That's what many people say when looking at their old code.
 <jkauffman>  "I can't believe I used to listen to this crap"
 <jkauffman>  that's what people say when they look back at their old
              music collection
   <rindolf>  jkauffman: I don't usually.
   <rindolf>  jkauffman: I am however, a bit ashamed of some of the shows
              I liked when I was younger.
   <rindolf>  jkauffman: they seem a bit cheesy now.
 <jkauffman>  yes, you're onto such better things now that you can fully
              appreciate the gilmore girls
   <rindolf>  jkauffman: you can never really appreciate The Gilmore Girls
              until you've watched it in the original Klingon.

    -- Looking Back at Your Old Habits
    -- #perlcafe, Freenode

XSLT is what Chuck Norris has nightmares of.

    -- Shlomi Fish
    -- XSLT Facts by Shlomi Fish and Friends ( http://www.shlomifish.org/humour/bits/facts/XSLT/ )


Powered by UNIX fortune(6)
[ Main Page ]