DicomImageViewer

DicomImageViewer - библиотека для работы с медицинскими изображениями и данными в формате DICOM

Основные компоненты: ImageViewerManager, LayoutManager, ReferenceLineManager, ExportManager, AnnotationManager, FilterManager, LocalizeManager

В состав библиотеки входит тестовое приложение в котором показан вариант использования компонентов данной библиотеки

Функционал тестового приложения:

  • Набор инструментов для работы с изображениями в сериях: смещение, масштаб, яркость, контрастность, линейка, стрелка, произвольная область, прямоугольник, воспроизведение серии
  • Автоматическое размещение окон по сериям после загрузки обследования
  • Отображение DICOM атрибутов файла
  • Отображение линий пересечения связанных взаимных проекций
  • Сетка серий от 1x1 до 5x5
  • Экспорт кадров в файлы изображений, серии кадров в файл видео
  • Инструмент локализации слайсов
  • Применение фильтров

Основные компоненты библиотеки

ImageViewerManager

Главный компонент системы.

    
    ...    
    //Пример стартовой инициализация ImageViewerManager
    ImageViewerManager imageViewerManager = new ImageViewerManager(this, this.layoutPanel);
    imageViewerManager.onProcessDescriptionChanged += imageViewerManager_ProcessDescriptionChanged;
    imageViewerManager.onSeriesLoaded += ImageViewerManager_onSeriesLoaded;
    imageViewerManager.onStudyLoaded += ImageViewerManager_onStudyLoaded; 
    //Конец инициализации
    ...
    //Событие загрузка серии
    private void ImageViewerManager_onSeriesLoaded(DicomImageViewControl currentDicomImageViewControl)
    {
        //Например, генерация TreeView для переключения между папками серий
        generateTreeViewNodesForStudy();
    }
    ...
    //Генерация TreeView для переключения между папками серий
    private void generateTreeViewNodesForStudy()
    {
        treeViewDicomStudies.Nodes.Clear();
        foreach (DicomImageViewControl dicomImageViewControl in imageViewerManager.LoadedDicomImageViewControls)
        {
            TreeNode seriesNode = new TreeNode();

            seriesNode.Text = dicomImageViewControl.CurrentDicomElement.ImageSop.SeriesDescription;

            seriesNode.Tag = dicomImageViewControl.FilePaths;

            imageListDicomStudies.Images.Add(dicomImageViewControl.CurrentDicomElement.Bitmap);
            seriesNode.ImageIndex = imageListDicomStudies.Images.Count - 1;
            seriesNode.SelectedImageIndex = imageListDicomStudies.Images.Count - 1;
            treeViewDicomStudies.Nodes.Add(seriesNode);
        }            
    }
    ...
    
        

LayoutManager

Размещение окон с обследованиями, изменение сетки, изменение размеров окон

    
    ...
    //Пример реализации метода реакции на событие изменения размеров контейнера, в котором отображаются серии обследований
    void layoutPanel_SizeChanged(object sender, System.EventArgs e)
    {
        imageViewerManager.LayoutManager.Resize();
    }
    ...
    //Обработка нажатия клавиши
    void container_KeyDown(object sender, KeyEventArgs e)
    {
        imageViewerManager.LayoutManager.KeyDown(e);

        e.Handled = false;
    }
    ...
    //Изменение сетки размещения
    imageViewerManager.LayoutManager.Offset = this.Offset;
    imageViewerManager.LayoutManager.GridX = 2;
    imageViewerManager.LayoutManager.GridY = 3;
    imageViewerManager.LayoutManager.ChangeLayout();
    ...
    
        

ReferenceLineManager

Отрисовка линий взаимного пересечения различных проекций

    
    ...
    //Пример реализации метода переключения на следующее изображение в серии    
    internal void NextImage()
    {
        if (this.CurrentDicomElement != null)
        {
            if (this.number + 1 < DicomElements.Count)
            {
                this.number++;
                this.CurrentDicomElement = DicomElements[this.number];

                this.LoadImage();
            }
            else
            {
                this.number = 0;
                this.CurrentDicomElement = DicomElements[0];
                this.LoadImage();
            }
            //Вызов пересчета и перерисовки линий взаимного пересечения различных проекций во всех отображаемых на данный момент окнах
            imageViewerManager.ReferenceLineManager.Refresh();
        }
    }
    ...
    
        

ExportManager

Экспорт DICOM-файлов в jpeg, avi

    
    ...
    //Пример вызова методов для экспорта
    private void bgrwExport_DoWork(object sender, DoWorkEventArgs e)
    {
        ArgForBgrwExport argForBgrwExport = (ArgForBgrwExport)e.Argument;
        BackgroundWorker worker = sender as BackgroundWorker;
        ExportManager = new ExportManager(argForBgrwExport.folder);
        string type = argForBgrwExport.type;

        if (type == "bitmap")
        {
            ExportManager.doBitmapExporting(worker);
        }
        if (type == "video")
        {
            ExportManager.doVideoExporting(worker);
        }
        if (worker.CancellationPending)
        {
            e.Cancel = true;
        }
    }
    ...
    //Экспорт DICOM серии в видеофайл
    public void doVideoExporting(BackgroundWorker worker)
    {
        int count = 0;
        foreach (DirectoryInfo folder in folders.GetDirectories())
        {
            if (worker.CancellationPending)
            {
                break;
            }
            try
            {
                new VideoExporter().exportTo(folder);
            }
            catch (Exception e)
            {
                log.Error("Error while video exporting from folder: " + folder.Name);
                log.Error(e.StackTrace);
            }
            count++;
            worker.ReportProgress(count);
        }
    }
    ...
    
        

AnnotationManager

Управление аннотациями к отображаемому файлу DICOM. Стрелка, область, прямоуголник, измерение, текст.

    
    ...
    //Пример добавления аннотации "Произвольная область"
    if (this.freehandToolActive)
    {
        freehanding = true;
        if (currentFreeHandAnnotation == null)
        {
            currentFreeHandAnnotation = new FreeHandAnnotation();
            currentFreeHandAnnotation.Color = this.imageViewerManager.CurrentColor;
            currentFreeHandAnnotation.Width = this.imageViewerManager.CurrentBrushSize;
            AnnotationManager.AddAnnotation(currentFreeHandAnnotation);
        }
    }
    ...
    //Пример добавления аннотации "Линейка"
    if (this.rulerToolActive)
    {
        rulling = true;
        if (currentRulerAnnotation == null)
        {
            currentRulerAnnotation = new RulerAnnotation();
            currentRulerAnnotation.Color = this.imageViewerManager.CurrentColor;
            currentRulerAnnotation.StartPoint = this.convertToOrigin(e.Location);
            currentRulerAnnotation.EndPoint = new Point(currentRulerAnnotation.StartPoint.X, currentRulerAnnotation.StartPoint.Y - 15);
            AnnotationManager.AddAnnotation(currentRulerAnnotation);
        }
        return;
    }
    ...
    //Пример добавления аннотации "Стрелка"
    if (this.arrowToolActive)
    {
        arrowing = true;
        if (currentArrowAnnotation == null)
        {
            currentArrowAnnotation = new ArrowAnnotation();
            currentArrowAnnotation.Color = this.imageViewerManager.CurrentColor;
            currentArrowAnnotation.Width = this.imageViewerManager.CurrentBrushSize;
            currentArrowAnnotation.StartPoint = this.convertToOrigin(e.Location);
            currentArrowAnnotation.EndPoint = new Point(currentArrowAnnotation.StartPoint.X, currentArrowAnnotation.StartPoint.Y - 15);
            AnnotationManager.AddAnnotation(currentArrowAnnotation);
        }
        return;
    }
    ...
    
        

FilterManager

Компонент управления накладываемыми на DICOM-изображение фильтрами.

    
    ...    
    //Пример реализации формы для управления фильтрами
    public FormFilterManager(DicomImageViewer.Dicom.FilterManager filterManager)
    {
        InitializeComponent();

        this.FilterManager = filterManager;

        //Получаем список существующих фильтров
        foreach (var item in FilterManager.FiltersNameList)
        {
            cbFiltersList.Items.Add(item);
        }
        if (cbFiltersList.Items.Count > 0)
        {
            cbFiltersList.SelectedIndex = 0;
        }
        refreshLbFilters();
    }
    ...
    private void bAddFilter_Click(object sender, EventArgs e)
    {
        //Добавляем фильтр и базовое значение
        FilterManager.AddFilter(cbFiltersList.Text, Convert.ToDouble(tbValue.Text));
        refreshLbFilters();
    }
    ...
    private void bDeleteAllFilters_Click(object sender, EventArgs e)
    {
        //Удалить все фильтры
        FilterManager.DeleteAllFilters();
        refreshLbFilters();
    }
    ...
    private void bDeleteFilter_Click(object sender, EventArgs e)
    {
        //Удаляем конкретный фильтр
        if (lbFilters.SelectedIndex != -1)
        {
            FilterManager.DeleteFilter(((Filter)lbFilters.Items[lbFilters.SelectedIndex]).Id);
            refreshLbFilters();

        }
    }
    ...
    //Актуализация ListBox со списком фильтров
    private void refreshLbFilters(int selectedIndex = -1)
    {
        //Получаем список примененных фильтров
        int currentIndex = lbFilters.SelectedIndex;

        if(selectedIndex != -1)
        {
            currentIndex = selectedIndex;
        }  

        lbFilters.Items.Clear();
        foreach (System.Collections.DictionaryEntry item in this.FilterManager.Filters)
        {
            lbFilters.Items.Add((Filter)item.Value);
        }


        if (currentIndex >= lbFilters.Items.Count)
        {
            currentIndex = lbFilters.Items.Count - 1;
        }
        if (currentIndex < -1)
        {
            currentIndex = -1;
        }

        if (lbFilters.Items.Count > 0)
        {                
            lbFilters.SelectedIndex = currentIndex;
        }
        else
        {
            tbSelectedFilterName.Tag = "";
            tbSelectedFilterName.Text = "";
            tbSelectedFilterValue.Text = "";
        }

    }
    ...
    
        

LocalizeManager

Инструмент локализации срезов(слайсов) для передачи полученной информации исполнительному оборудованию

    
    ...
    //Пример создания локализатора    
    if (toolName == "localize")
    {
        LocalizeManager.CreateLocalize(this.CurrentDicomImageViewControl);
    }
    ...
    
        

Библитека использует компоненты популярного opensource проекта ClearCanvas.