Перейти к содержимому. | Перейти к навигации

УНИИТО Лучевая Диагностика

Персональные инструменты

This is SunRain Plone Theme

Navigation

Вы здесь: Главная / Категории / Документация / Сценарии / Считаем снимки в базе данных DCM4CHEE.

Считаем снимки в базе данных DCM4CHEE.

Простые скрипты на bash и awk для получения статистики по базе данных сервера DCM4CHEE за период с раскладкой по сегментам обследования и по отделениям медучреждения.

Итак, имеем работающий DICOM-сервер. Снимки ежедневно поступают с рентгеновского аппарата и "раздаются" заинтересованным сотрудникам. Все довольны.

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

Возможно, в комплекте замечательного сервера DCM4CHEE есть средства для анализа, но мы ничего не знали про это, когда начинали работать в 2013 году и решили задачу средствами Linux, в среде которого и работает наш экземпляр DCM4CHEE.

Снимки в DCM4CHEE "упрятаны" в dicom-файлы, снабжены там необходимой информацией и складываются в файловую систему в специальную структуру каталогов. Во всем этом и решено было разобраться.

Вариант с анализом базы данных MySQL, на котором работает DCM4CHEE мы сразу отбросили, т.к. "уже лет 15 как" не любим реляционные базы (а любим объектные!) и размеры реляционной структуры DCM4CHEE не радовали.

Вот эта замечательна картинка http://www.dcm4che.org/confluence/display/ee2/Database+Schema+Diagram

Свою задачу мы разбили на несколько шагов. Первым делом нужно было получить интересующую информацию из файловой структуры DCM4CHEE в виде табличного текстового файла. Затем "расправиться" с этим файлом средствами Linux, например используя awk.

Смотрим, как хранятся dicom-файлы со снимками в файловой структуре DCM4CHEE. Все лежит в каталоге

/var/lib/dcm4chee/server/default/archive, далее каталоги с годами

4chee:/var/lib/dcm4chee/server/default/archive# ls -aF
./  ../  2012/    2013/  2014/  2015/

далее каталоги месяц и день:


4chee:/var/lib/dcm4chee/server/default/archive/2015/5# ls -aF
./   12/  14/  18/  20/  22/  26/  28/    5/  7/
../  13/  15/  19/  21/  25/  27/  29/    6/  8/

смотрим что внутри дня:

4chee:/var/lib/dcm4chee/server/default/archive/2015/5# cd 12
4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12# ls -aF
./  ../  10/  11/  12/    13/  14/  15/  9/

что за цифры ? Непонятно пока, ну и ладно... Смотрим дальше:

4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12# cd 15
4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12/15# ls -aF
./  ../  1402D15F/  3426B704/  F44F38F4/

еще страньше... Смотрим, что в этих странных каталогах:

4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12/15# cd F44F38F4/
4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12/15/F44F38F4# ls -aF
./  ../  343713A0/  71CA6CB8/  95F30D48/  97E48CE2/  98658E37/

"магические цифирки" ? Далее:

4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12/15/F44F38F4# cd 97E48CE2/
4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12/15/F44F38F4/97E48CE2# ls -aF
./  ../  2CD25CB7
4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12/15/F44F38F4/97E48CE2# file 2CD25CB7
2CD25CB7: DICOM medical imaging data

А вот это уже файлик. Проверяем, что за файл и видим, что DICOM medical imaging data. Заглянем в него с помощью утилиты dcmdump из пакета dcmtk:

4chee:/var/lib/dcm4chee/server/default/archive/2015/5/12/15/F44F38F4/97E48CE2# dcmdump 2CD25CB7 | less

...
(0010,0010) PN [OSHCHEPKOVA V.V.]                       #  16, 1 PatientsName
(0010,0020) LO [421086]                                 #   6, 1 PatientID
(0010,0030) DA [19611222]                               #   8, 1 PatientsBirthDate
(0010,0040) CS [F]                                      #   2, 1 PatientsSex
(0018,0010) LO (no value available)                     #   0, 0 ContrastBolusAgent
(0018,0015) CS [KNEE]                                   #   4, 1 BodyPartExamined
...

то что надо! Но почему, так обозваны каталоги, куда прячутся файлы ? Пока не будем вдаваться и напишем скрипт

для bash, который устроит такая структура. Назовем его gettab.sh:

 

#! /bin/bash


ARPATH=/var/lib/dcm4chee/server/default/archive
DPATT="no_string"


# If need to get paths to dcm-files set 1, for dose.sh set 0
EMPTYID=0


MCOUNTER=$2
DCOUNTER=$3

NEXTMO=`expr $5 + 1`
ind=0

while [ $MCOUNTER -le $NEXTMO ]
do
        ARRAY[$ind]=$DPATT
#       echo ==$MCOUNTER
        if [ $MCOUNTER -lt $5 ]; then
                while [ $DCOUNTER -le 31 ]
                do
#                       echo ==$MCOUNTER/$DCOUNTER
                        ARRAY[$ind]="${ARRAY[$ind]}|/$1/$MCOUNTER/$DCOUNTER/"
                        DCOUNTER=`expr $DCOUNTER + 1`
                done
                DCOUNTER=1
        else
                while [ $DCOUNTER -le $6 ]
                do
#                       echo ==$MCOUNTER/$DCOUNTER
                        ARRAY[$ind]="${ARRAY[$ind]}|/$1/$MCOUNTER/$DCOUNTER/"
                        DCOUNTER=`expr $DCOUNTER + 1`
                done
        fi

        MCOUNTER=`expr $MCOUNTER + 1`
        ind=`expr $ind + 1`
done


for element in "${ARRAY[@]}"
do
        if [ "$element" != "$DPATT" ];
        then
#               for fname in `find $ARPATH -type f -print | grep -E "$element"`
                for fname in `find $ARPATH/$1/$2 -type f -print | grep -E "$element"`
                do
#                       echo "$element"
#                       echo ""
                        if [ $EMPTYID = "0" ];
                        then
                                STUDYDATE=`dcmdump $fname | grep " StudyDate" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                PATISEX=`dcmdump $fname | grep " PatientsSex" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                PATIBDATE=`dcmdump $fname | grep " PatientsBirthDate" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                SERIDESCR=`dcmdump $fname | grep " SeriesDescription" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                BODYPART=`dcmdump $fname | grep " BodyPartExamined" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
#                               REFERPHIS=`dcmdump $fname | grep " ReferringPhysicinsName" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
#                               RH=`dcmdump $fname | grep "Ref" | awk '{print $1}' `
                                PH=`dcmdump $fname | grep "ReferringPhys" | awk '{print $3}' | sed 's/\[//g' | sed 's/\]//g'`
                                DE=`dcmdump $fname | grep "SeriesDescription" | awk '{printf "%s_%s", $3, $4}' | sed 's/\[//g' | sed 's/\]//g'`

                                echo $STUDYDATE $PATISEX $PATIBDATE $SERIDESCR $BODYPART $PH $DE
                        else
                                STUDYDATE=`dcmdump $fname | grep " StudyDate" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                PATISEX=`dcmdump $fname | grep " PatientsSex" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                PATIBDATE=`dcmdump $fname | grep " PatientsBirthDate" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                PATINAME=`dcmdump $fname | grep " PatientsName" | awk '{printf "%s %s\n", $3, $4}' | sed 's/\[//g' | sed  's/\]//g' |  sed 's/\^/_/g' | sed 's/ /_/g'`
                                PATIENTID=`dcmdump $fname | grep " PatientID" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`

                                PH=`dcmdump $fname | grep "ReferringPhys" | awk '{print $3}' | sed 's/\[//g' | sed 's/\]//g'`

                                STUDYTIME=`dcmdump $fname | grep " StudyTime" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                MODALITY=`dcmdump $fname | grep " Modality" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                ACCESSNO=`dcmdump $fname | grep " AccessionNumber" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                BODYPART=`dcmdump $fname | grep " BodyPartExamined" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
                                STUDYUID=`dcmdump $fname | grep " StudyInstanceUID" | awk '{print $3}' | sed 's/\[//g' | sed  's/\]//g'`
#                               

#                               echo  $PATINAME $PATIBDATE  $PATIENTID $STUDYDATE $fname
                                echo  $PATINAME $PATIBDATE  $PATIENTID $STUDYDATE $PH $PATISEX $STUDYTIME $MODALITY $ACCESSNO $STUDYUID
                        fi
                done
        fi
done

 Теперь несколько пояснений. Пример вызова скрипта - получить данные за август 2014 года:

./gettab.sh 2014 8 1 2014 8 31 | tee Aug.rep

Параметры это "год месяц день (начала периода) год месяц день (конца периода)". Результаты будут выводиться на stdout и дублироваться в файл Aug.rep. Вот несколько строк из такого файла:

...
20140811 F 19610918 Taz PELVIS ORT Taz_AP
20140811 M 19991113 Full KNEE (no Full_Leg
20140811 M 19860901 Taz PELVIS TOO1 Taz_AP
20140811 M 19860901 Taz PELVIS TOO1 Taz_AP
20140811 M 19860901 Pyatochnaya FOOT TOO1 Pyatochnaya_AX
20140811 M 19860901 Taz PELVIS TOO1 Taz_AP
20140811 M 19860901 Poyasnichnyi LSPINE TOO1 Poyasnichnyi_AP
20140811 M 19860901 Taz PELVIS TOO1 Taz_AP
...

Как видим есть информация для подсчета: колонка с областью обследования и колонка с названием отделения где лечился/наблюдался пациент (ORT, TOO1,...).

Ограничение: программа не работает в Новый Год! Т.е. не справится с переходом через 1 января. Нельзя корректно снять данные таким заросом:

gettab.sh 2014 12 1 2015 3 31 | tee lastmo.rep

придется клеить две таблички:

gettab.sh 2014 12 1 2014 12 31 | tee Dec14.rep
gettab.sh 2015 1 1 2013 3 31 | tee beg15.rep

 

Несколько слов о работе этой bash-программки. В программе два цикла while и for. В первом мы пробегаем по файловой системе и запоминаем каталоги-даты (см.выше), которые удовлетворяют искомому диапазону дат. Запоминание происходит в массиве ARRAY  в виде регулярного выражения для последующего поиска командой find. Каждый элемент массива содержит примерно такую строчку:

no_string|/2014/8/1/|/2014/8/2/|/2014/8/3/|/2014/8/4/|/2014/8/5/|/2014/8/6/|/2014/8/7/|/2014/8/8/|/2014/8/9/|/2014/8/10/|/2014/8/11/|/2014/8/12/|/2014/8/13/|/2014/8/14/|/2014/8/15/|/2014/8/16/|/2014/8/17/|/2014/8/18/|/2014/8/19/|/2014/8/20/|/2014/8/21/|/2014/8/22/|/2014/8/23/|/2014/8/24/|/2014/8/25/|/2014/8/26/|/2014/8/27/|/2014/8/28/|/2014/8/29/|/2014/8/30/|/2014/8/31/

Это даты (в том числе не существующие!) в виде подстроки каталога, разделенные оператором "или". Массив может быть большим, но bash умеет работать с виртуальной памятью. У вас же есть место на HDD :)

Второй цикл (for) пробегается по этому массиву и находит командой find все файлы со сниками и с каждым из них

идет работа с помошью команды dcmdump (пакет dcmtk должен быть установлен) и стандартных утилит Linux. Команда echo выводит параметры dicom-файла. Не очень изящьно, но задача будет решена.

Итак, файл с табличкой имеем, осталось подсчитать снимки и разложить их по локализаиям и отделениям. Тут на помощь приходит awk со своими ассоциативными массивами. Это наша программа обсчета tot.awk, в своей вы можете подправить названия отделений:

 

BEGIN { tot = 0; }
{

    if (substr($6,0,3) == "AM") {
        list_amb[$4]++;
        tot_amb++;
    } else {
        if (substr($6,0,3) == "DO") {
              list_doo[$4]++;
              tot_doo++;
        } else {
            if (substr($6,0,3) == "OR") {
                 list_ort[$4]++;
                 tot_ort++;
            } else {

                if (substr($6,0,3) == "TO") {
                     list_too[$4]++;
                     tot_too++;
                } else {

                    if (substr($6,0,3) == "MO") {
                         list_mosk[$4]++;
                         tot_mosk++;
                    } else {

                        if (substr($6,0,3) == "ST") {
                             list_stud[$4]++;
                             tot_stud++;
                        } else {
                             list_etc[$4]++;
                             tot_etc++;
                        }
                    }
                }
            }
        }
    }

    list[$4]++;
#    list[$5]++;
    tot++;
}
END {
    printf "------------------- AMB -- DOO -- ORT -- TOO -- MOS -- STU -- etc-\n"

    for (i in list)   {
        printf "%-10s: %5s :%5s :%5s :%5s :%5s :%5s :%5s :%5s\n", i, list[i], list_amb[i], list_doo[i], list_ort[i], list_too[i], list_mosk[i], list_stud[i]
     
    }
    printf "------------------------------------------------------------------\n"

И еще, один момент. Часто классификатор локализаций в компьтере лаборанта и список локализаций, которые хотят видеть в отчете, мягко говоря, не совпадают. Кроме того, в файлах бывают ошибки - например, не указана локализация и т.д. Для этого надо считать наборы локализаций, как одну, например, Predplechye, Zapiastye, Loktevoi считать как LEG, а Glaznizy, Zub, Sinus как SKULL. Для реализации такого "мэппинга" мы используем возможности утилиты sed вот наш скрипт для обработки табличного файла перед обсчетом:

 

#! /bin/bash

cat $1 | sed 's/Berzovie/LEG/' | sed 's/Stopa/LEG/' | \
         sed 's/Predplechye/LEG/' |sed 's/Kolennyi/LEG/' |sed 's/Klyuchiza/LEG/' | \
         sed 's/Zapastie/LEG/' |sed 's/Golenostop/LEG/' | sed 's/Acromio-klyucichnoe/LEG/' | \
         sed 's/Nadkolennik/LEG/' |sed 's/Zapiastye/LEG/' | sed 's/Predplechie/LEG/' | \
         sed 's/Kisti/LEG/' |sed 's/Plechevaya/LEG/' | sed 's/Plechevoi/LEG/' | \
         sed 's/Bolshoi/LEG/' |sed 's/Palez/LEG/' | sed 's/Loktevoi/LEG/' | \
         sed 's/Kist/LEG/' |sed 's/Pyatochnaya/LEG/' | sed 's/Kolenniy/LEG/' |  \
         sed 's/Sheyniyl/CSPINE/' |sed 's/Sheiniy/CSPINE/' | sed 's/Sheyniy/LEG/' | \
         sed 's/Sheinyi/CSPINE/' |sed 's/Grudnaya/CHEST/' | sed 's/Bedrennaya/HIP/' | \
         sed 's/Tazo-bedrennyi/HIP/' |sed 's/Taz/HIP/' | sed 's/Bedro/HIP/' | \
         sed 's/Berzovye/HIP/' |sed 's/Grudnoy/TSPINE/' | sed 's/Krestez\/kopchik/LSPINE/' | \
         sed 's/Grudnoi/TSPINE/' |  sed 's/Cherep/SKULL/' | sed 's/Full/FLFS/' | \
         sed 's/Poyasnichniy/LSPINE/' |sed 's/Poyasnichnyi/LSPINE/' | sed 's/L5-S1/LSPINE/'  | \
         sed 's/Loktevoy/LEG/' |sed 's/Lopatka/CHEST/' | sed 's/Bryushnaya/HIP/'  | \
         sed 's/Pelvis/HIP/' |sed 's/Glaznizy/SKULL/' | sed 's/Scapula/CHEST/'  | \
         sed 's/Knee/LEG/' | sed 's/Krestez\/Kopchik/HIP/' | \
         sed 's/PLechevaya/LEG/' | sed 's/Zub/SKULL/' | sed 's/Sinus/SKULL/' | awk -f tot.awk

 

Исправляйте "под себя", счиатйте сники, получайте статотчет !

Удачи!