Дискретное косинусное преобразование 1D Matlab

Я пытаюсь реализовать DCT в MATLAB, используя умножение матрицы на вектор. В частности, я хотел бы создать матрицу коэффициентов DCT, а затем использовать ее для умножения на 1D-сигнал для вычисления 1D DCT.

Вот мой код для создания матрицы DCT:

function D=dct1d(n)
for u=0:n-1
   if u==0
       au=sqrt(1/n);
   else
       au=sqrt(2/n);
    end
   for x=0:n-1
       D(u+1,x+1)=au*cos(((2*x+1)*u*pi)/2*n); 
   end
end

После этого я попытался выполнить DCT с тестовым сигналом x = [1 2 3 4 5 6 7 8]:

x=[1 2 3 4 5 6 7 8];
y=dct1(8)*x';

Ответ, который он дает:

12.7279
18.0000
18.0000
18.0000
18.0000
18.0000
18.0000
18.0000

Однако правильный ответ:

12.7279
-6.4423
-0.0000
-0.6735
      0
-0.2009
-0.0000
-0.0507

person Mark Ben    schedule 18.02.2016    source источник


Ответы (1)


Ошибка в вашем коде очень небольшая, но фундаментальная. Строка, в которой вы вычисляете коэффициенты:

D(u+1,x+1)=au*cos(((2*x+1)*u*pi)/2*n);

Взгляните на самую последнюю часть строки:

D(u+1,x+1)=au*cos(((2*x+1)*u*pi)/2*n);
%//                              ^^^

Поскольку умножение и деление имеют одинаковый приоритет, это именно тот то же, что делать:

D(u+1,x+1)=au*cos((((2*x+1)*u*pi)/2)*n);

Таким образом, вы не делите на 2n. Вы делите на 2, а затем умножаете на n, что неверно. Вам просто нужно заключить операцию 2*n в скобки:

D(u+1,x+1)=au*cos(((2*x+1)*u*pi)/(2*n)); 

Как только вы это сделаете, мы получим правильную матрицу DCT. Кстати, один из способов проверить правильность ответа — использовать dctmtx, которая вычисляет искомую матрицу коэффициентов N x N DCT. Тем не менее, эта функция является частью набора инструментов обработки сигналов, поэтому, если у вас ее нет, вы, к сожалению, не можете использовать эту функцию, но если я могу предложить альтернативный ответ вместо использования for циклов, я бы построил 2D-сетку. координат с помощью meshgrid, а затем вычислить поэлементные произведения.

Вместо этого будет работать что-то вроде этого:

function D = dct1d(n)

[x,u] = meshgrid(0:n-1);
D = sqrt(2/n)*cos(((2*x+1).*u*pi)/(2*n)); 
D(1,:) = D(1,:) / sqrt(2);

end

Вместо использования оператора if для определения веса, который нам нужно применить к строке, мы можем просто использовать sqrt(2/n), а затем разделить на sqrt(2) для первой строки, чтобы вместо этого вы делили на sqrt(1/n). Этот код должен дать те же результаты, что и исправленный код1.


В любом случае, как только я внес эти исправления, я сравнил оба ответа между тем, что дает ваш код, и тем, что дает dctmtx, и это правильно:

>> dct1d(8)

ans =

    0.3536    0.3536    0.3536    0.3536    0.3536    0.3536    0.3536    0.3536
    0.4904    0.4157    0.2778    0.0975   -0.0975   -0.2778   -0.4157   -0.4904
    0.4619    0.1913   -0.1913   -0.4619   -0.4619   -0.1913    0.1913    0.4619
    0.4157   -0.0975   -0.4904   -0.2778    0.2778    0.4904    0.0975   -0.4157
    0.3536   -0.3536   -0.3536    0.3536    0.3536   -0.3536   -0.3536    0.3536
    0.2778   -0.4904    0.0975    0.4157   -0.4157   -0.0975    0.4904   -0.2778
    0.1913   -0.4619    0.4619   -0.1913   -0.1913    0.4619   -0.4619    0.1913
    0.0975   -0.2778    0.4157   -0.4904    0.4904   -0.4157    0.2778   -0.0975

>> dctmtx(8)

ans =

    0.3536    0.3536    0.3536    0.3536    0.3536    0.3536    0.3536    0.3536
    0.4904    0.4157    0.2778    0.0975   -0.0975   -0.2778   -0.4157   -0.4904
    0.4619    0.1913   -0.1913   -0.4619   -0.4619   -0.1913    0.1913    0.4619
    0.4157   -0.0975   -0.4904   -0.2778    0.2778    0.4904    0.0975   -0.4157
    0.3536   -0.3536   -0.3536    0.3536    0.3536   -0.3536   -0.3536    0.3536
    0.2778   -0.4904    0.0975    0.4157   -0.4157   -0.0975    0.4904   -0.2778
    0.1913   -0.4619    0.4619   -0.1913   -0.1913    0.4619   -0.4619    0.1913
    0.0975   -0.2778    0.4157   -0.4904    0.4904   -0.4157    0.2778   -0.0975

Как только мы получим скорректированную матрицу DCT, мы можем проверить фактические вычисления DCT, выполнив умножение матрицы на тестовый вектор 1:8, который вы использовали:

>> dct1d(8)*((1:8).')

ans =

   12.7279
   -6.4423
   -0.0000
   -0.6735
         0
   -0.2009
   -0.0000
   -0.0507

1. Этот код на самом деле делается в dctmtx под капотом, как только вы уберете все проверки ошибок и проверки согласованности ввода.

person rayryeng    schedule 18.02.2016
comment
Спасибо, я забыл о приоритете - person Mark Ben; 18.02.2016
comment
@MarkBen Я также создал для вас более эффективную версию dct1d. Я предпочитаю делать это с помощью векторных операций, но все, что вам нужно, чтобы понять, как это работает! - person rayryeng; 18.02.2016