Как звучит сердцебиение: перевод бумажной кардиограммы в WAV-формат

vd8icsdtkcxv6x3i-djvtgs6hrs.jpeg
Многим неоднократно приходилось сталкиваться с обследованием сердца в медицинских учреждениях с помощью кардиографа. Данный аппарат измеряет биоэлектрическую активность сердца, регистрируя результат на бумажной ленте. Современные кардиографы записывают результат измерения не на бумагу, а в цифровую память. Однако в качестве конечного носителя записанной информации зачастую применятся бумажная лента. Визуально она представляет собой длинную миллиметровую бумагу небольшой ширины, которая скручена в рулон. На бумаге, помимо миллиметровой сетки, нарисован во всю длину некий график, который отражает закон изменения измеряемой величины во времени. Измеряемая величина, как я понимаю, это есть разность потенциалов между отведениями. Чаще всего на одной ленте представлено сразу несколько графиков, так как регистрируются разности потенциалов между множеством отведений. Однако, не вдаваясь в подробности медицины, в дальнейшем будем рассматривать один из первых основных графиков. Кроме графиков, на ленте имеется дополнительная текстовая информация: масштаб по горизонтали (мм/сек), по вертикали (мм/мВ), измеренная частота сердцебиения (уд/мин) и прочее.

Возникла идея преобразовать данный график в звуковой формат, воспроизвести результат и послушать, как это будет звучать.
По предварительному анализу можно сделать вывод, что частотный состав такой волны не очень насыщенный. Фактически это низкие частоты, включая инфразвук, который, как считается, не слышен. Однако можно будет увидеть, как диффузор НЧ динамика будет повторять колебания, похожие на «колебания» сердца. На самом же деле, ввиду наличия емкостных дифференцирующих цепей на пути распространения сигнала от звуковой карты ПК до НЧ усилителя, колебания динамической головки не будут в точности повторять колебания, представленные на кардиограмме. И это при условии, что нигде не установлены фильтры, срезающие сверхнизкие частоты. Кроме того, будут присутствовать нелинейные искажения, сопровождающиеся кратными гармониками верхних частот. Тем не менее, не принимая во внимание вышеизложенный анализ, поставим задачу так: преобразовать график на бумаге в формат WAV, чтобы при открытии этого файла в звуковом редакторе вид волны совпадал с бумажным вариантом и, кроме того, чтобы соответствовал временной масштаб.

Для начала надо прикинуть глубину квантования (разрешающая способность цифрового аудио по вертикали). Я рассматриваю один из стандартных: 8 или 16 бит. Второй вариант (16 бит) — это 65536 сэмплов по вертикали, что будет соответствовать 65536 пикселей картинки, которая представляет собой скан или фотографию кардиограммы. Это очень много, и смысла в этом нет. Если брать 8 бит — это 256 сэмплов, или 256 пикселей картинки. Вот это уже более подходящий вариант. При этом динамический диапазон аудио составит 6×8=48 дБ. Я не знаю, какой динамический диапазон у кардиограммы, но думаю, что не больше. У самого аппарата он естественно больше, но погрешность неизбежна при выводе кардиограммы на бумагу, особенно если речь идёт о прямой прорисовке пером. Кстати, насчёт последнего. Я не буду брать во внимание старые образцы, которые нарисованы «радиальным» пером. Миллиметровая бумажная лента для таких кардиограмм специфическая: по вертикали вместо прямых линий нанесены дуги окружностей. Что касается масштаба по горизонтали — частота дискретизации — будет рассчитываться исходя из масштаба кардиограммы и размера изображения. От этого параметра будет зависеть скорость воспроизведения, и она должна соответствовать реальной «скорости кардиограммы».

Бумагу с кардиограммой следует отсканировать в ч/б виде с оттенками серого в достаточном разрешении. Затем нужно изменить размер изображения таким образом, чтобы требуемая кардиограмма вписывалась в полосу шириной 256 пикселей. Я нашёл в Интернете множество изображений с кардиограммами. В качестве примера рассмотрим два из них.

6aaoveakfnrluqr-m-hybxevcly.jpeg

Первое изображение, можно сказать, уже почти подготовленное. Высота изображения — 431. Ширина — 1023. Волна по ширине полностью заполняет весь рисунок. А вот по высоте нужно оставить 256, обрезав изображение сверху и снизу так, чтобы волна располагалась приблизительно по центру.

1ypl9b8u33wmyo2xa6cdkhbx9n0.png

На втором рисунке изображено сразу несколько кардиограмм. Возьмём самую первую. После обрезки получилась картинка размерами 508 на 61.

odsrncybq1ghgyy1tmf-x7rxmh4.png

Не растягивая изображение по вертикали, сделаем картинку по высоте 256, заполнив созданную область белой пустотой. Волна также должна располагаться приблизительно по центру. Ширину 508 при вырезании я выбрал так, чтобы оставить как можно большее число целых миллиметровых клеток, которые также видны на изображении.

Оба изображения нужно преобразовать к такому виду, при котором будет виден только чёрный график на белом фоне и больше ничего. Это делается элементарно. Ввиду того, что график нарисован намного жирнее, чем миллиметровая сетка, в графическом редакторе с помощью регулировок «яркость, контрастность, насыщенность» можно добиться требуемого результата. Если не получается идеального преобразования, лишний оставшийся «мусор» над графиком необходимо стереть инструментом «ластик». Все изображения требуется сохранить в формат монохромного BMP. Таким образом, в картинке уже точно останутся только белые и чёрные цвета.

Прежде приступить к описанию алгоритма преобразования картинки в WAV, стоит оговорить некоторые нюансы, которые упростят программирование. Получившиеся картинки необходимо повернуть на 90 градусов против часовой стрелки (ширина и высота изображения при этом поменяются местами). Это нужно для того, чтобы сориентировать начало кардиограммы с началом BMP файла. Известно, что цветовые данные каждого пикселя BMP файла записываются в файл по порядку построчно, начиная с нижнего левого угла. Затем картинки необходимо открыть в редакторе «MS Paint» (у меня Windows XP) и выполнить сохранение в 8-битный BMP (256 цветов). Конечно же, произойдёт «переопределение информации», но зато при таком формате каждый пиксель изображения соответствует одному байту, что очень удобно при программировании. Байт »0» — пиксель чёрного цвета, а байт »255» — белого. В результате должно получиться примерно следующее (здесь две картинки соединены и уменьшены вдвое).

vrccnhi1qkyegtfpanecxls5c40.jpeg

Что касается выходного формата — будем выводить не в стандартный WAV файл, а в файл RAW-данных (PCM). Это также упрощает программирование, ибо при выводе в WAV необходимо ещё позаботиться о 44-байтном заголовке. В распространённом звуковом редакторе «Adobe Audition 1.5» PCM файл открывается без проблем. Более того, можно даже выводить в текстовый файл десятичные числа PCM данных в столбик, предварительно сформировав специфический текстовый заголовок. Как ни странно, но такие файлы Adobe Audition также открывает.

Опишем алгоритм преобразования. А алгоритм очень простой: нужно проанализировать каждую строчку BMP файла снизу вверх. Анализ будет заключаться в подсчёте слева направо идущих подряд белых пикселей, пока не встретится чёрный. Полученные целочисленные неотрицательные значения нужно записать в выходной PCM файл в бинарном виде. Размер выходного файла будет в точности совпадать с высотой обрабатываемого изображения. Глубина квантования 8 бит PCM формата аудиоданных подразумевает кодировку сэмплов по такому же принципу. Значение »0» — максимальное отрицательное значения сэмпла аудио, значение »255» — максимальное положительное, а »128» — нулевое значение (посередине). К примеру, файл PCM аудио данных тишины будет содержать одинаковые байты значением »128». Стоит оговорить, что в строчке может встретиться несколько подряд идущих чёрных пикселей, в зависимости от толщины линии кардиограммы. Но описанный алгоритм «ловит» верхнюю огибающую, чего вполне будет достаточно. Тем более, острые пики кардиограммы, направленные вверх, лучше будут схватываться этим способом.

Теперь можно приступить к написанию текста программы. Программа, написанная на Си, весьма простая и в подробных комментариях не нуждается.

#include  //Нужная библиотека, как обычно;

int main(){ //Типа начало программы;
    FILE *in,*out; //Входной и выходной файлы;
    unsigned long int h,i; //Высота изображения и итератор цикла;
    unsigned char px,s; //Прочитанный пиксель и насчитанный сэмпл аудио;
    in=fopen("1.bmp","rb"); //Открываем файл на чтение;
    out=fopen("1.pcm","wb"); //Открываем файл на запись;
    fseek(in,22,SEEK_SET); //Позиционируемся в то место заголовка, где записана высота изображения
    fread(&h,4,1,in); //Считываем высоту изображения (4 байта);
    for(i=0;i

После выполнения программы над файлом »1.bmp» будет создан файл »1.pcm» в том же каталоге с программой. При попытке открытия файла в Adobe Audition должно всплыть следующее окно.

swljpg5dphpocfdbnvsartz0k-8.png

Необходимо выбрать «Моно»,»8-бит», а частоту дискретизации в поле ввода напечатать исходя из расчёта: f=h/(s/v), где h — высота рисунка (оно же и число сэмплов в аудио), s — длинна кардиограммы в миллиметрах, v — масштаб кардиограммы в мм/сек. Последний параметр написан на кардиограмме. На первой кардиограмме ничего не написано, но масштаб, как правило, часто составляет 25 мм/сек. Расчёты частот дискретизации в Excel для наших примеров продемонстрированы на рисунке ниже.

tlvwjpkgjgxsh2z5bwrgkmzjuy8.png

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

hatrjtjokkoinjcox4ed9ushjiw.png

При нажатии «OK» соглашаемся с тем, что работаем с «Unsigned 8 bit» сэмплами (будет ещё одно окошко), после чего в основном поле звукового редактора развернётся вид волны нашего файла. Заметим, что данный вид будет представлен «вверх ногами», и для полного соответствия с бумажным вариантом следует выполнить инвертирование волны в соответствующем меню. В результате это будет выглядеть так.

rupfzjhna9czkxh3xifzjuzhubu.png

Вторая кардиограмма выглядит «потише», так как она изначально была не очень большая по размеру.

-uflvzhf_iswthnmhrwsdd3xvl4.png

К сожалению, не все звуковые карты позволяют воспроизводить аудио на произвольной частоте дискретизации, точнее, подавляющее большинство вовсе не умеет. Для того чтобы корректно воспроизвести файл, необходимо выполнить функцию «Конвертировать тип сэмпла». Будем преобразовывать к ближайшему стандартному значению 8000 Гц, заодно увеличим разрешающую способность по амплитуде до 16 бит. Последнее необходимо для точности на этапе интерполяции при апсэмплинге. Если оставить 8 бит, то оставшаяся область спектра будет заполнена шумом квантования. Кстати говоря, данную процедуру можно было выполнить программно на этапе преобразования BMP-PCM, даже с применением интерполяции. Но одна из целей являлась простота программного кода.

После операции апсэмплинга можно прослушивать и наслаждаться результатом. Можно сохранить результат в стандартный wav или mp3 файл. Звучит ровно так же, как я и предполагал изначально. Ниже представлено видео, звуковой дорожкой которого (после монтажа) как раз и являются те самые аудио, которые были получены из бумажной кардиограммы. Сразу приношу извинение за качество видео и за мерцание, но у меня при текущих условиях не было экранного рекордера и нормального дисплея. Видео хотелось сделать как можно быстрее.

hwdx1_no6w8ohtqjfc55r1nlngq.jpeg

© Habrahabr.ru