Распознавание образов для программистов

ВНИМАНИЕ! БЛОГ ПЕРЕЕХАЛ ПО АДРЕСУ
RECOG.RU

30 Январь 2010

RR1. Распознавание речевых команд

написано в рубрике: Распознавание речи — Кручинин Александр @ 8:07 ДП

Сегодня речь пойдёт о распознавании речевых команд. Теоретических подходов, патентов на изобретения и алгоритмы достаточно много. Ниже будет описано как программисту, без каких-либо речевых библиотек, разработать систему дикторозависимого распознавания речевых команд.

Прежде всего, вам нужен интерфейс для чтения/записи звуковых файлов и принятия информации с микрофона. Вы можете пользоваться готовыми библиотеками ил создать что-то своё, например, в листинге 1 показана простейшая функция для чтения звукового файла из одного старого престарого класса.

Листинг 1. Чтение несжатого WAV-файла

int WAVREAD::OpenFile(LPSTR FileName)

{

      WAVEFORMATEX wfx;

      char *p;

      DWORD *uk,size,readed;

      char buf[4],buf1[256],buf2[256];

      unsigned char buffer[1000];

      int i,k;

 

      HANDLE hFile=INVALID_HANDLE_VALUE;

      hFile=CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,0,

            OPEN_EXISTING,0,0);

      if (hFile==INVALID_HANDLE_VALUE) {

            return 1;

      }

      ReadFile(hFile,(void*)buf,4,&readed,0);

      if (buf[0]!=’R’ || buf[1]!=’I’ || buf[2]!=’F’ || buf[3]!=’F')

      {

            return 2;

      }

      ReadFile(hFile,(void*)buf,4,&readed,0);

      ReadFile(hFile,(void*)buf,4,&readed,0);

      if (buf[0]!=’W’ || buf[1]!=’A’ || buf[2]!=’V’ || buf[3]!=’E')

      {

            return 3;

      }

      ReadFile(hFile,(void*)buf,4,&readed,0);

      if (buf[0]!=’f’ || buf[1]!=’m’ || buf[2]!=’t’ || buf[3]!=’ ‘)

      {

            return 4;

      }

      ReadFile(hFile,(void*)buf,4,&readed,0);

      uk=(DWORD*)buf;

      size=uk[0];

      if (size>18) size=18;

      ReadFile(hFile,(void*)buffer,size,&readed,0);

      wfx=CreateWfx(*((WORD*)buffer),*((WORD*)buffer+1),

            *((DWORD*)buffer+1),*((DWORD*)buffer+2),

            *((WORD*)buffer+6),*((WORD*)buffer+7) );

      i=0;

      this->wfx=wfx;//Create wfx

      if (wfx.cbSize!=0)

            do{

                  ReadFile(hFile,(void*)buffer,1,&readed,0);

                  i++;

                  if (i==wfx.cbSize) break;

            }while(true);

      //Read Data from file

      k=0;

      do{

            k=ReadFile(hFile,(void*)buf,1,&readed,0);

            if (k==0) return 5;

            if (buf[0]==’d'){

                  k=ReadFile(hFile,(void*)buf,1,&readed,0);

                  if (k==0) return 5;

                if (buf[0]==’a')

                  {

                        k=ReadFile(hFile,(void*)buf,1,&readed,0);

                        if (k==0) return 5;

                        if (buf[0]==’t')

                        {

                             k=ReadFile(hFile,(void*)buf,1,&readed,0);

                             if (k==0) return 5;

                             if (buf[0]==’a')

                                   break;

                        }

                  }

            }

      }while(true);

      ReadFile(hFile,(void*)buf,4,&readed,0);

      size=*((DWORD*)buf);

      this->Size=size;

      this->Data= new BYTE[size];

      ReadFile(hFile,(void*)this->Data,size,&readed,0);

 

      if (hFile!=INVALID_HANDLE_VALUE)

             CloseHandle(hFile);

 

      return 0;

}

 

Для анализа команды обычно проводят спектральный анализ. Поэтому вам необходимо или самому написать функцию быстрого преобразования Фурье (БПФ) или воспользоваться готовыми библиотеками, например этой:

 http://www.fftw.org/

 Пользоваться её довольно просто – подключите lib-файл к проекту, выделите память для данных и вызовите БПФ функцию (Листинг 2).

 Листинг 2. Использование функций fftw

      fftw_complex *in, *out;

      fftw_plan pp;

      in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * Signal_All1);

      out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * Signal_All1);

      long o;

      for(o=0;o<Signal_All1;o++) {

            in[o][0]=Signal_ptr1[o];

            in[o][1]=0;

      }

      pp = fftw_plan_dft_1d(Signal_All1, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

      fftw_execute(pp);

       fftw_destroy_plan(pp);

      fftw_free(in); fftw_free(out);

 

Далее, для удобства тестирования у вас должны быть функции вывода на экран (или в графический файл) содержимого WAV-файла и его спектра. Пример и того и другого для команды «Вставить» показаны на рисунках 1-2.

Рис. 1. Содержимое WAV-файла

Рис. 1. Содержимое WAV-файла

Рис.2. Спектр сигнала

Рис.2. Спектр сигнала

 

В качестве эталонов при распознавании у вас будут спектры команд, с которыми будут сравниваться неизвестные образы, поступающие например с микрофона. Для сравнения можно использовать корреляционный анализ. Но предварительно надо усреднить сигнал, построив гистограмму с меньшим количеством элементов, чем на рисунке 2. На рисунке 3 показано как строится гистограмма для 8 элементов. Значения внутри столбцов гистограммы усредняются, а диапазон частот выбирается в диапазоне человеческого голоса (до 8кГц).

Рис. 3. Гистограмма для 8 элементов

Рис. 3. Гистограмма для 8 элементов

Можно подобрать общее количество элементов гистограммы, при которых распознавание наилучшее. Например, 70.

Затем необходимо сравнивать неизвестный сигнал из 70 элементов с известным сигналом из 70 элементов по коэффициенту парной корреляции:

rr1.4                 (1)

где Mx  и My – оценки матожидания для переменных  x и y, Kxy – коэффициент  ковариации, sx  и  sy  – средние квадратичные отклонения

К сожалению, данный подход даёт невысокую достоверность, Так для набора из 5 команд: «Копировать», «Печать», «Вставить», «Команда», «Сохранить», достоверность распознавания составила чуть больше 90% на тестовой выборке из 135 команд.

Работает на WordPress