Рубрики
Без рубрики

Как я написал Средство для Удаления фонового шума от Начала до конца

Как так много коммуникационных приложений, таких как Signal и Telegram, избавляются от всего статического шума и помех?.. С пометкой java, показать разработчика, учебник, noiseremoval.

Как так много коммуникационных приложений, таких как Signal и Telegram, избавляются от всего статического шума и фонового шума до того, как звук достигнет других людей во время разговора?

Эти компании включают удаление шума в свое программное обеспечение, и в недавнем проекте по кодированию я хотел сделать именно это.

В этой статье я расскажу о том, что мне было нужно, как я это сделал, и о препятствиях, с которыми я столкнулся на этом пути.

Когда я говорю “окружающий шум”, “фоновый шум” или просто “шум”, я имею в виду звук, который вы не хотите слышать в аудиофайле. Этот звук нарушает сигнал, звук, который вы действительно хотите услышать, в аудиофайле или входящем звуке.

Мой план состоял в том, чтобы каким-то образом идентифицировать шум, чтобы я мог его отфильтровать, а затем записать аудиофайл с удаленным шумом в “выходной” файл, как только программа выполнит свои основные функции, чтобы я мог сравнивать и собирать данные.

Исследовать это было трудно. Это заняло у меня некоторое время, но я нашел учебник по фильтру Калмана . Это объясняло все базовые знания, необходимые для понимания фильтра Калмана, объясняло фильтр Калмана и приводило примеры.

Фильтр Калмана бывает двух типов: одномерный и многомерный. Многомерный фильтр Калмана включает в себя понятия линейной алгебры, которые мне пришлось изучить. Итак, я начал изучать концепции линейной алгебры и погрузился в длинные исследовательские работы по ковариационной матрице и собственным векторам.

В конце концов я обнаружил, что единственными понятиями, необходимыми для фильтра Калмана в отношении линейной алгебры, были матрицы, векторы и их операции. Я также нашел полезное видео о ковариационной матрице, в котором за 10 минут объяснялось, что описывалось в исследовательских работах.

Кроме того, я изучил Java Sound API в официальном руководстве Oracle по программированию чтобы я мог использовать Java для применения фильтра Калмана к звуку. К сожалению, Java Sound API не предоставлял низкоуровневых функций, в которых я нуждался. Единственной полезной функцией были байтовые данные, которые представляют собой двоичные данные, представляющие звук аудиофайла. Затем мне пришлось использовать линейную импульсно-кодовую модуляцию для определения звуковой волны, а затем применить фильтр Калмана оттуда.

Фильтр Калмана

Фильтр Калмана – это алгоритм оценки, который позволяет вам найти значение объекта, которое вы, возможно, никогда не узнаете наверняка, называемое “скрытым состоянием”, но очень близко к нему, измеряя объект и проходя через ряд уравнений, которые позволяют вам сходиться к истинному значению.

Например, если вы хотите использовать фильтр Калмана, чтобы найти вес бронзового троянского коня, его скрытое состояние может составлять 200 кг (из-за всех людей внутри), а ваша окончательная оценка состояния , которую вы получаете, когда вы проводите измерения через кучу уравнений, составляет 198,4 кг.

В фильтре Калмана каждый раз, когда вы измеряете , вы обновляете свою оценку состояния с помощью уравнения. Затем вы прогнозируете следующую оценку состояния, которую вы рассчитываете после следующего измерения.

Фильтр Калмана использует механизм коррекции предсказания.

Фильтр Калмана следует циклу “измерение, обновление, прогнозирование”.

как это работает

Возвращаясь к примеру с троянским конем, допустим, вы установили свое первоначальное предположение на 100 кг. Единственная цель первоначального предположения – запустить цикл.

Кроме того, вам необходимо задать свое первоначальное предположение для неопределенности оценки. Другими словами, используйте число от 0 до 1, представляющее, насколько вы не уверены в том, что 100 кг являются скрытым состоянием.

Затем вы завершаете “0-ю” итерацию цикла измерения, обновления, прогнозирования. Эти первоначальные предположения являются “оценками”, так что теперь вам нужно сделать прогноз. Поскольку у вас еще нет измерения, прогнозы совпадают с оценкой, и они будут использоваться на 1-й итерации.

  1. Вы кладете Троянского коня на весы и видите “195 кг. ” Это ваше измерение

  2. Рассчитайте коэффициент усиления Калмана. По мере увеличения этого коэффициента измерению придается больший вес, а по мере его уменьшения измерению придается меньший вес, а оценке придается больший вес, как показано ниже.

Уравнение усиления Калмана

Коэффициент усиления Калмана использует неопределенность оценки и неопределенность измерения в своих расчетах. Неопределенность измерения – это постоянное значение от 0 до 1, которое вы задаете в начале.

  1. Теперь, включив прогноз с 0-й итерации (прогноз предыдущего состояния) – 100 кг – и ваше текущее измерение – 195 кг – в уравнение Обновления состояния , вы получите свою оценку состояния .

Уравнение обновления состояния

  1. Затем вы обновляете неопределенность оценки с помощью другого уравнения, уравнения обновления ковариации .

  2. Наконец, вы прогнозируете следующую оценку и следующую неопределенность оценки, используя последние 2 уравнения в алгоритме фильтра Калмана.

И это цикл для одномерного фильтра Калмана. Вы можете сделать это еще проще, игнорируя неопределенность. Теперь вы делаете еще одно измерение и продолжаете повторять его до тех пор, пока не сделаете все необходимые измерения.

Когда вы отобразите оценку и скрытое состояние в виде значений y, а итерацию # в виде значения x, вы увидите, что оценка сходится к истинному значению.

Реализация фильтра Калмана

Пакет фильтров Калмана был первым модулем, над которым я работал после небольшого эксперимента с Java Sound API (и в основном неудачного). Поскольку я люблю программировать, я даже не стал сначала внедрять фильтр Калмана. Я реализовал более простые фильтры, которые также следуют циклу “измерение, обновление, прогнозирование”: Альфа-фильтр , Алфавитный фильтр и Алфавитный фильтр .

После разработки, основанной на тестировании, я протестировал их со значениями в примерах с того же сайта, который научил меня самим фильтрам: kalmanfilter.net . Я принял правильное решение при тестировании, так как сразу же столкнулся с множеством неудачных тестов из-за множества ошибок.

В дополнение к тому, что я получил опыт тестирования, которого у меня очень мало, внедрение аналогичных и более простых фильтров помогло мне устранить ошибки, которые, вероятно, появились бы в фильтре Калмана, если бы я начал с этого. Начав с более простого, позвольте мне сосредоточиться на более сложных ошибках, когда я наконец доберусь до более сложных фильтров.

Линейная импульсно-кодовая модуляция и манипуляция звуком

Звуковые файлы используют формат, называемый линейной импульсной кодовой модуляцией, для представления звука в виде чисел. При заданной частоте дискретизации байты данных представляют собой определенные амплитуды, и вместе они образуют синусоидальные волны, которые представляют звук.

Сначала я подумал, что сами байтовые данные являются образцами, которые можно напрямую преобразовать в синусоидальные волны. Когда аудиофайл был поврежден и производил неразборчивый шум.

Более чем через два месяца после начала моего проекта я обнаружил алгоритм в StackOverflow, который позволил мне абстрагировать [побитовые операции], промежуточный шаг между байтовыми данными и синусоидальными волнами, которыми я знал, как манипулировать.

И это сработало как заклинание! Увеличьте значение a (амплитуду) и его громкость, увеличьте произведение внутри Math.sin() и высота звука становится выше. Затем я мог бы объединить образцы float (тип данных для десятичных чисел) вместе, чтобы добавить синусоидальные или звуковые волны.

И когда я добавил амплитуду и ее отрицание, я получил ожидаемую тишину. Это называется разрушительной интерференцией – когда вы складываете звуковую синусоидальную волну и обратную синусоидальную волну вместе. В математических терминах у вас есть горизонтальная линия, а в звуковых терминах это полная тишина.

Моделирование Матриц и векторов

Многомерный фильтр Калмана использует матрицы , которые представляют собой прямоугольные массивы чисел, и векторы, которые являются одномерными массивами чисел. Мне было очень весело создавать это, потому что я работал с концепцией, с которой меня уже познакомили в школе, и я использовал синтаксис Java, с которым я был знаком.

Я создал класс Matrix, который содержал двумерный массив для матрицы, и класс Vector, который расширяет класс Matrix и содержит одномерный массив.

После разработки, основанной на тестировании, я сначала создал класс Matrix test и класс VectorTest.

Для теста на сложение матриц я вызвал свой метод Matrix#add() для первой матрицы, проходящей во второй матрице. Затем я проверил, является ли матрица суммы правильным значением, создав третий объект Matrix .

@Test
public void testMatrixAddition() {
    Matrix addend = new Matrix(new double[][] { { 1, 2, 3 }, { 4, 5, 6 } });
    Matrix augend = new Matrix(new double[][] { { 10, 14, -8 }, { 3, 1, 5 } });

    Matrix sum = addend.plus(augend);

    assertArrayEquals(new double[][] { { 11, 16, -5 }, { 7, 6, 11 } }, sum.getMatrixElements());
}
public Matrix plus(Matrix augend) {
    if (!(getRows() == augend.getRows() && getColumns() == augend.getColumns()))
        throw new IllegalArgumentException("Cannot add matrices of different dimensions");
     Matrix toReturn = new Matrix(new double[getRows()][getColumns()]);

    for (int i = 0; i < getRows(); i++) {
        for (int j = 0; j < getColumns(); j++) {
            toReturn.set(i, j, matrixElements[i][j] + augend.getMatrixElements()[i][j]);
        }
    }

    return toReturn;
}

После реализации Matrix #add я продолжил операции вычитания, умножения, инверсии (которые были настолько сложными, что в итоге я извлек их в свой собственный класс) и транспонирования, каждый раз проверяя перед реализацией. Я также включил различные методы, касающиеся свойств матрицы, такие как

  • getДетерминант()
  • является квадратным()
  • является идентификационной матрицей()
  • является обратным (матрица обратная)

Наконец, я реализовал статические фабричные методы для универсальных типов матриц, которые сейчас предназначены только для матрицы идентичности, где 1 охватывает большую диагональ, а все остальное в матрице равно нулю, и это квадратная матрица. Например, идентификационная матрица 3×3 представляет собой [[1,0,0],[0,1,0],[0,0,1]]

Результаты

Когда я избавился от всех Math.random() s, Исключение NullPointerException s и Исключение UnsupportedOperationException s, которое остановило полную работу моей программы, Я не нашел никаких изменений в аудиофайле.

Вывод

Удаление окружающего шума было огромной проблемой, и я еще не успешно удалил фоновый шум. Однако я многому научился на этом опыте – от звукорежиссуры до линейной алгебры и Java-API.

Вот ссылка на код (с открытым исходным кодом!): Вот ссылка на код (с открытым исходным кодом!):

Он также доступен в виде файла JAR, просто убедитесь, что вы установили Java JDK перед загрузкой моей программы!

Если вы хотите подробно изучить фильтр Калмана, я предлагаю посетить обучающий веб-сайт ( https://kalmanfilter.net ), так как это объясняет все очень просто.

Спасибо, что прочитали мою статью! У вас есть проект, который включает сложную математическую тему, такую как линейная алгебра? Оставьте ссылку на него в комментариях!

Оригинал: “https://dev.to/varuns924/how-i-wrote-a-background-noise-remover-from-start-to-finish-3h9m”