Медицинске апликације за сликање често треба да претражују и траже метадане ДИЦОМ-а ефикасно. Док се датотеке ДиКОМ складиште богате метаданте, екстракција у базе података омогућава брзе потраге, анализе и интеграцију са болничким информационим системима. Овај водич показује како да се диктоматски метадати чувају у СКЛ и НоСКЛ базама користећи Аспасе.Медикал за .НЕТ.

Zašto DICOM metadata pohranjuje u bazama podataka?

ДИЦОМ датотеке садрже велике метадане, али претраживање преко хиљада фајлова је споро.

  • Fast queries: Pronađite studije po pacijentu, datumu, modalitetu ili bilo kom atributu odmah
  • Аналитика: Агрегирани подаци за извештавање и статистику
  • Интеграција: повезивање података слике са ЕМР/ЕХР системима
  • Скалабилност: Управљање милионима студија са правилним индексирањем
  • Полна текстуална претрага: Пронађите студије описом кључних речи

Фондација за екстракцију метаданих

Прво, креирајте екстрактор метадата који конвертује ДИЦОМ у објекте спремне за базу података:

using Aspose.Medical.Dicom;

public class DicomMetadataExtractor
{
    public DicomStudyRecord ExtractStudyRecord(string filePath)
    {
        DicomFile dicomFile = DicomFile.Open(filePath);
        var dataset = dicomFile.Dataset;
        
        return new DicomStudyRecord
        {
            // Identifiers
            StudyInstanceUID = dataset.GetString(DicomTag.StudyInstanceUID),
            SeriesInstanceUID = dataset.GetString(DicomTag.SeriesInstanceUID),
            SOPInstanceUID = dataset.GetString(DicomTag.SOPInstanceUID),
            AccessionNumber = dataset.GetString(DicomTag.AccessionNumber),
            
            // Patient
            PatientID = dataset.GetString(DicomTag.PatientID),
            PatientName = dataset.GetString(DicomTag.PatientName),
            PatientBirthDate = ParseDicomDate(dataset.GetString(DicomTag.PatientBirthDate)),
            PatientSex = dataset.GetString(DicomTag.PatientSex),
            
            // Study
            StudyDate = ParseDicomDate(dataset.GetString(DicomTag.StudyDate)),
            StudyTime = dataset.GetString(DicomTag.StudyTime),
            StudyDescription = dataset.GetString(DicomTag.StudyDescription),
            ReferringPhysicianName = dataset.GetString(DicomTag.ReferringPhysicianName),
            
            // Series
            Modality = dataset.GetString(DicomTag.Modality),
            SeriesDescription = dataset.GetString(DicomTag.SeriesDescription),
            SeriesNumber = ParseInt(dataset.GetString(DicomTag.SeriesNumber)),
            BodyPartExamined = dataset.GetString(DicomTag.BodyPartExamined),
            
            // Image
            InstanceNumber = ParseInt(dataset.GetString(DicomTag.InstanceNumber)),
            Rows = ParseInt(dataset.GetString(DicomTag.Rows)),
            Columns = ParseInt(dataset.GetString(DicomTag.Columns)),
            
            // Equipment
            Manufacturer = dataset.GetString(DicomTag.Manufacturer),
            InstitutionName = dataset.GetString(DicomTag.InstitutionName),
            StationName = dataset.GetString(DicomTag.StationName),
            
            // File Info
            FilePath = filePath,
            FileSize = new FileInfo(filePath).Length,
            IndexedAt = DateTime.UtcNow
        };
    }

    private DateTime? ParseDicomDate(string dicomDate)
    {
        if (string.IsNullOrEmpty(dicomDate) || dicomDate.Length != 8)
            return null;
        
        if (DateTime.TryParseExact(dicomDate, "yyyyMMdd", null,
            System.Globalization.DateTimeStyles.None, out var date))
        {
            return date;
        }
        return null;
    }

    private int? ParseInt(string value)
    {
        if (int.TryParse(value, out var result))
            return result;
        return null;
    }
}

public class DicomStudyRecord
{
    public string StudyInstanceUID { get; set; }
    public string SeriesInstanceUID { get; set; }
    public string SOPInstanceUID { get; set; }
    public string AccessionNumber { get; set; }
    
    public string PatientID { get; set; }
    public string PatientName { get; set; }
    public DateTime? PatientBirthDate { get; set; }
    public string PatientSex { get; set; }
    
    public DateTime? StudyDate { get; set; }
    public string StudyTime { get; set; }
    public string StudyDescription { get; set; }
    public string ReferringPhysicianName { get; set; }
    
    public string Modality { get; set; }
    public string SeriesDescription { get; set; }
    public int? SeriesNumber { get; set; }
    public string BodyPartExamined { get; set; }
    
    public int? InstanceNumber { get; set; }
    public int? Rows { get; set; }
    public int? Columns { get; set; }
    
    public string Manufacturer { get; set; }
    public string InstitutionName { get; set; }
    public string StationName { get; set; }
    
    public string FilePath { get; set; }
    public long FileSize { get; set; }
    public DateTime IndexedAt { get; set; }
}

Интеграција СКЛ сервера

Складиштење ДИЦОМ метада у СКЛ серверу са ентитетским оквиром:

using Microsoft.EntityFrameworkCore;

public class DicomDbContext : DbContext
{
    public DbSet<DicomStudyEntity> Studies { get; set; }
    public DbSet<DicomSeriesEntity> Series { get; set; }
    public DbSet<DicomInstanceEntity> Instances { get; set; }

    public DicomDbContext(DbContextOptions<DicomDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Study entity
        modelBuilder.Entity<DicomStudyEntity>(entity =>
        {
            entity.HasKey(e => e.StudyInstanceUID);
            entity.HasIndex(e => e.PatientID);
            entity.HasIndex(e => e.StudyDate);
            entity.HasIndex(e => e.AccessionNumber);
            entity.HasIndex(e => e.Modality);
        });

        // Series entity
        modelBuilder.Entity<DicomSeriesEntity>(entity =>
        {
            entity.HasKey(e => e.SeriesInstanceUID);
            entity.HasIndex(e => e.StudyInstanceUID);
            entity.HasOne(e => e.Study)
                  .WithMany(s => s.SeriesList)
                  .HasForeignKey(e => e.StudyInstanceUID);
        });

        // Instance entity
        modelBuilder.Entity<DicomInstanceEntity>(entity =>
        {
            entity.HasKey(e => e.SOPInstanceUID);
            entity.HasIndex(e => e.SeriesInstanceUID);
            entity.HasOne(e => e.Series)
                  .WithMany(s => s.Instances)
                  .HasForeignKey(e => e.SeriesInstanceUID);
        });
    }
}

public class DicomStudyEntity
{
    public string StudyInstanceUID { get; set; }
    public string AccessionNumber { get; set; }
    public string PatientID { get; set; }
    public string PatientName { get; set; }
    public DateTime? PatientBirthDate { get; set; }
    public string PatientSex { get; set; }
    public DateTime? StudyDate { get; set; }
    public string StudyDescription { get; set; }
    public string Modality { get; set; }
    public string ReferringPhysicianName { get; set; }
    public string InstitutionName { get; set; }
    public int ImageCount { get; set; }
    public DateTime IndexedAt { get; set; }
    
    public List<DicomSeriesEntity> SeriesList { get; set; }
}

public class DicomSeriesEntity
{
    public string SeriesInstanceUID { get; set; }
    public string StudyInstanceUID { get; set; }
    public string Modality { get; set; }
    public string SeriesDescription { get; set; }
    public int? SeriesNumber { get; set; }
    public string BodyPartExamined { get; set; }
    public int ImageCount { get; set; }
    
    public DicomStudyEntity Study { get; set; }
    public List<DicomInstanceEntity> Instances { get; set; }
}

public class DicomInstanceEntity
{
    public string SOPInstanceUID { get; set; }
    public string SeriesInstanceUID { get; set; }
    public int? InstanceNumber { get; set; }
    public int? Rows { get; set; }
    public int? Columns { get; set; }
    public string FilePath { get; set; }
    public long FileSize { get; set; }
    
    public DicomSeriesEntity Series { get; set; }
}

Репозиторијум СКЛ сервера за ЦРУД операције:

public class SqlDicomRepository
{
    private readonly DicomDbContext _context;
    private readonly DicomMetadataExtractor _extractor;

    public SqlDicomRepository(DicomDbContext context)
    {
        _context = context;
        _extractor = new DicomMetadataExtractor();
    }

    public async Task IndexDicomFileAsync(string filePath)
    {
        var record = _extractor.ExtractStudyRecord(filePath);

        // Upsert Study
        var study = await _context.Studies.FindAsync(record.StudyInstanceUID);
        if (study == null)
        {
            study = new DicomStudyEntity
            {
                StudyInstanceUID = record.StudyInstanceUID,
                AccessionNumber = record.AccessionNumber,
                PatientID = record.PatientID,
                PatientName = record.PatientName,
                PatientBirthDate = record.PatientBirthDate,
                PatientSex = record.PatientSex,
                StudyDate = record.StudyDate,
                StudyDescription = record.StudyDescription,
                Modality = record.Modality,
                ReferringPhysicianName = record.ReferringPhysicianName,
                InstitutionName = record.InstitutionName,
                IndexedAt = DateTime.UtcNow
            };
            _context.Studies.Add(study);
        }

        // Upsert Series
        var series = await _context.Series.FindAsync(record.SeriesInstanceUID);
        if (series == null)
        {
            series = new DicomSeriesEntity
            {
                SeriesInstanceUID = record.SeriesInstanceUID,
                StudyInstanceUID = record.StudyInstanceUID,
                Modality = record.Modality,
                SeriesDescription = record.SeriesDescription,
                SeriesNumber = record.SeriesNumber,
                BodyPartExamined = record.BodyPartExamined
            };
            _context.Series.Add(series);
        }

        // Upsert Instance
        var instance = await _context.Instances.FindAsync(record.SOPInstanceUID);
        if (instance == null)
        {
            instance = new DicomInstanceEntity
            {
                SOPInstanceUID = record.SOPInstanceUID,
                SeriesInstanceUID = record.SeriesInstanceUID,
                InstanceNumber = record.InstanceNumber,
                Rows = record.Rows,
                Columns = record.Columns,
                FilePath = record.FilePath,
                FileSize = record.FileSize
            };
            _context.Instances.Add(instance);
        }

        await _context.SaveChangesAsync();
    }

    public async Task<List<DicomStudyEntity>> SearchStudiesAsync(
        string patientId = null,
        string patientName = null,
        DateTime? startDate = null,
        DateTime? endDate = null,
        string modality = null)
    {
        var query = _context.Studies.AsQueryable();

        if (!string.IsNullOrEmpty(patientId))
            query = query.Where(s => s.PatientID == patientId);

        if (!string.IsNullOrEmpty(patientName))
            query = query.Where(s => s.PatientName.Contains(patientName));

        if (startDate.HasValue)
            query = query.Where(s => s.StudyDate >= startDate);

        if (endDate.HasValue)
            query = query.Where(s => s.StudyDate <= endDate);

        if (!string.IsNullOrEmpty(modality))
            query = query.Where(s => s.Modality == modality);

        return await query
            .OrderByDescending(s => s.StudyDate)
            .Take(100)
            .ToListAsync();
    }
}

Интеграција МонгоДБ

Складиштење ДИКОМ метада као ЈСОН докумената у МонгоДБ:

using MongoDB.Driver;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

public class MongoDicomDocument
{
    [BsonId]
    public string SOPInstanceUID { get; set; }
    
    public string StudyInstanceUID { get; set; }
    public string SeriesInstanceUID { get; set; }
    public string AccessionNumber { get; set; }
    
    public PatientInfo Patient { get; set; }
    public StudyInfo Study { get; set; }
    public SeriesInfo Series { get; set; }
    public ImageInfo Image { get; set; }
    public EquipmentInfo Equipment { get; set; }
    public FileInfo FileInfo { get; set; }
    
    [BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
    public DateTime IndexedAt { get; set; }
}

public class PatientInfo
{
    public string ID { get; set; }
    public string Name { get; set; }
    public DateTime? BirthDate { get; set; }
    public string Sex { get; set; }
}

public class StudyInfo
{
    public DateTime? Date { get; set; }
    public string Time { get; set; }
    public string Description { get; set; }
    public string ReferringPhysician { get; set; }
}

public class SeriesInfo
{
    public string Modality { get; set; }
    public string Description { get; set; }
    public int? Number { get; set; }
    public string BodyPart { get; set; }
}

public class ImageInfo
{
    public int? InstanceNumber { get; set; }
    public int? Rows { get; set; }
    public int? Columns { get; set; }
}

public class EquipmentInfo
{
    public string Manufacturer { get; set; }
    public string Institution { get; set; }
    public string Station { get; set; }
}

public class FileInfo
{
    public string Path { get; set; }
    public long Size { get; set; }
}

public class MongoDicomRepository
{
    private readonly IMongoCollection<MongoDicomDocument> _collection;
    private readonly DicomMetadataExtractor _extractor;

    public MongoDicomRepository(string connectionString, string databaseName)
    {
        var client = new MongoClient(connectionString);
        var database = client.GetDatabase(databaseName);
        _collection = database.GetCollection<MongoDicomDocument>("dicom_instances");
        _extractor = new DicomMetadataExtractor();
        
        CreateIndexes();
    }

    private void CreateIndexes()
    {
        var indexKeys = Builders<MongoDicomDocument>.IndexKeys;
        
        _collection.Indexes.CreateMany(new[]
        {
            new CreateIndexModel<MongoDicomDocument>(indexKeys.Ascending(x => x.StudyInstanceUID)),
            new CreateIndexModel<MongoDicomDocument>(indexKeys.Ascending(x => x.Patient.ID)),
            new CreateIndexModel<MongoDicomDocument>(indexKeys.Ascending(x => x.Study.Date)),
            new CreateIndexModel<MongoDicomDocument>(indexKeys.Ascending(x => x.Series.Modality)),
            new CreateIndexModel<MongoDicomDocument>(
                indexKeys.Text(x => x.Study.Description)
                         .Text(x => x.Series.Description)
                         .Text(x => x.Patient.Name))
        });
    }

    public async Task IndexDicomFileAsync(string filePath)
    {
        var record = _extractor.ExtractStudyRecord(filePath);
        
        var document = new MongoDicomDocument
        {
            SOPInstanceUID = record.SOPInstanceUID,
            StudyInstanceUID = record.StudyInstanceUID,
            SeriesInstanceUID = record.SeriesInstanceUID,
            AccessionNumber = record.AccessionNumber,
            Patient = new PatientInfo
            {
                ID = record.PatientID,
                Name = record.PatientName,
                BirthDate = record.PatientBirthDate,
                Sex = record.PatientSex
            },
            Study = new StudyInfo
            {
                Date = record.StudyDate,
                Time = record.StudyTime,
                Description = record.StudyDescription,
                ReferringPhysician = record.ReferringPhysicianName
            },
            Series = new SeriesInfo
            {
                Modality = record.Modality,
                Description = record.SeriesDescription,
                Number = record.SeriesNumber,
                BodyPart = record.BodyPartExamined
            },
            Image = new ImageInfo
            {
                InstanceNumber = record.InstanceNumber,
                Rows = record.Rows,
                Columns = record.Columns
            },
            Equipment = new EquipmentInfo
            {
                Manufacturer = record.Manufacturer,
                Institution = record.InstitutionName,
                Station = record.StationName
            },
            FileInfo = new FileInfo
            {
                Path = record.FilePath,
                Size = record.FileSize
            },
            IndexedAt = DateTime.UtcNow
        };

        await _collection.ReplaceOneAsync(
            x => x.SOPInstanceUID == document.SOPInstanceUID,
            document,
            new ReplaceOptions { IsUpsert = true });
    }

    public async Task<List<MongoDicomDocument>> FullTextSearchAsync(string searchText)
    {
        var filter = Builders<MongoDicomDocument>.Filter.Text(searchText);
        return await _collection.Find(filter).Limit(100).ToListAsync();
    }

    public async Task<List<BsonDocument>> GetStudyAggregationAsync()
    {
        var pipeline = new[]
        {
            new BsonDocument("$group", new BsonDocument
            {
                { "_id", "$StudyInstanceUID" },
                { "patientId", new BsonDocument("$first", "$Patient.ID") },
                { "patientName", new BsonDocument("$first", "$Patient.Name") },
                { "studyDate", new BsonDocument("$first", "$Study.Date") },
                { "modality", new BsonDocument("$first", "$Series.Modality") },
                { "description", new BsonDocument("$first", "$Study.Description") },
                { "imageCount", new BsonDocument("$sum", 1) }
            }),
            new BsonDocument("$sort", new BsonDocument("studyDate", -1)),
            new BsonDocument("$limit", 100)
        };

        return await _collection.Aggregate<BsonDocument>(pipeline).ToListAsync();
    }
}

Еластична интеграција

Омогућава моћно претраживање пуног текста са Elasticsearch:

using Elasticsearch.Net;
using Nest;

[ElasticsearchType(IdProperty = nameof(SOPInstanceUID))]
public class ElasticDicomDocument
{
    public string SOPInstanceUID { get; set; }
    public string StudyInstanceUID { get; set; }
    public string SeriesInstanceUID { get; set; }
    public string AccessionNumber { get; set; }
    
    [Text(Analyzer = "standard")]
    public string PatientID { get; set; }
    
    [Text(Analyzer = "standard")]
    public string PatientName { get; set; }
    
    public DateTime? PatientBirthDate { get; set; }
    
    [Keyword]
    public string PatientSex { get; set; }
    
    public DateTime? StudyDate { get; set; }
    
    [Text(Analyzer = "english")]
    public string StudyDescription { get; set; }
    
    [Keyword]
    public string Modality { get; set; }
    
    [Text(Analyzer = "english")]
    public string SeriesDescription { get; set; }
    
    [Keyword]
    public string BodyPartExamined { get; set; }
    
    [Text(Analyzer = "standard")]
    public string InstitutionName { get; set; }
    
    public string FilePath { get; set; }
    public long FileSize { get; set; }
    public DateTime IndexedAt { get; set; }
}

public class ElasticDicomRepository
{
    private readonly ElasticClient _client;
    private readonly DicomMetadataExtractor _extractor;
    private const string IndexName = "dicom-metadata";

    public ElasticDicomRepository(string elasticsearchUrl)
    {
        var settings = new ConnectionSettings(new Uri(elasticsearchUrl))
            .DefaultIndex(IndexName);
        
        _client = new ElasticClient(settings);
        _extractor = new DicomMetadataExtractor();
        
        CreateIndex();
    }

    private void CreateIndex()
    {
        var existsResponse = _client.Indices.Exists(IndexName);
        if (!existsResponse.Exists)
        {
            _client.Indices.Create(IndexName, c => c
                .Map<ElasticDicomDocument>(m => m.AutoMap())
                .Settings(s => s
                    .NumberOfShards(1)
                    .NumberOfReplicas(0)));
        }
    }

    public async Task IndexDicomFileAsync(string filePath)
    {
        var record = _extractor.ExtractStudyRecord(filePath);
        
        var document = new ElasticDicomDocument
        {
            SOPInstanceUID = record.SOPInstanceUID,
            StudyInstanceUID = record.StudyInstanceUID,
            SeriesInstanceUID = record.SeriesInstanceUID,
            AccessionNumber = record.AccessionNumber,
            PatientID = record.PatientID,
            PatientName = record.PatientName,
            PatientBirthDate = record.PatientBirthDate,
            PatientSex = record.PatientSex,
            StudyDate = record.StudyDate,
            StudyDescription = record.StudyDescription,
            Modality = record.Modality,
            SeriesDescription = record.SeriesDescription,
            BodyPartExamined = record.BodyPartExamined,
            InstitutionName = record.InstitutionName,
            FilePath = record.FilePath,
            FileSize = record.FileSize,
            IndexedAt = DateTime.UtcNow
        };

        await _client.IndexDocumentAsync(document);
    }

    public async Task<List<ElasticDicomDocument>> SearchAsync(string query)
    {
        var response = await _client.SearchAsync<ElasticDicomDocument>(s => s
            .Query(q => q
                .MultiMatch(mm => mm
                    .Query(query)
                    .Fields(f => f
                        .Field(d => d.PatientName, 2.0)
                        .Field(d => d.PatientID, 2.0)
                        .Field(d => d.StudyDescription)
                        .Field(d => d.SeriesDescription)
                        .Field(d => d.BodyPartExamined))
                    .Fuzziness(Fuzziness.Auto)))
            .Size(100));

        return response.Documents.ToList();
    }

    public async Task<List<ElasticDicomDocument>> AdvancedSearchAsync(
        string patientName = null,
        string modality = null,
        DateTime? startDate = null,
        DateTime? endDate = null,
        string bodyPart = null)
    {
        var response = await _client.SearchAsync<ElasticDicomDocument>(s => s
            .Query(q => q
                .Bool(b =>
                {
                    var must = new List<Func<QueryContainerDescriptor<ElasticDicomDocument>, QueryContainer>>();
                    
                    if (!string.IsNullOrEmpty(patientName))
                        must.Add(m => m.Match(mt => mt.Field(f => f.PatientName).Query(patientName)));
                    
                    if (!string.IsNullOrEmpty(modality))
                        must.Add(m => m.Term(t => t.Field(f => f.Modality).Value(modality)));
                    
                    if (startDate.HasValue || endDate.HasValue)
                    {
                        must.Add(m => m.DateRange(dr =>
                        {
                            dr = dr.Field(f => f.StudyDate);
                            if (startDate.HasValue) dr = dr.GreaterThanOrEquals(startDate.Value);
                            if (endDate.HasValue) dr = dr.LessThanOrEquals(endDate.Value);
                            return dr;
                        }));
                    }
                    
                    if (!string.IsNullOrEmpty(bodyPart))
                        must.Add(m => m.Term(t => t.Field(f => f.BodyPartExamined).Value(bodyPart)));
                    
                    return b.Must(must.ToArray());
                }))
            .Sort(sort => sort.Descending(d => d.StudyDate))
            .Size(100));

        return response.Documents.ToList();
    }

    public async Task<Dictionary<string, long>> GetModalityStatisticsAsync()
    {
        var response = await _client.SearchAsync<ElasticDicomDocument>(s => s
            .Size(0)
            .Aggregations(a => a
                .Terms("modalities", t => t
                    .Field(f => f.Modality)
                    .Size(50))));

        var buckets = response.Aggregations.Terms("modalities").Buckets;
        return buckets.ToDictionary(b => b.Key, b => b.DocCount ?? 0);
    }
}

Батцх Индексинг сервис

Индекс цео ДИЦОМ архивира ефикасно:

public class DicomIndexingService
{
    private readonly SqlDicomRepository _sqlRepo;
    private readonly MongoDicomRepository _mongoRepo;
    private readonly ElasticDicomRepository _elasticRepo;

    public DicomIndexingService(
        SqlDicomRepository sqlRepo = null,
        MongoDicomRepository mongoRepo = null,
        ElasticDicomRepository elasticRepo = null)
    {
        _sqlRepo = sqlRepo;
        _mongoRepo = mongoRepo;
        _elasticRepo = elasticRepo;
    }

    public async Task IndexDirectoryAsync(
        string directory,
        int maxParallelism = 4,
        IProgress<IndexingProgress> progress = null)
    {
        var files = Directory.GetFiles(directory, "*.dcm", SearchOption.AllDirectories);
        var total = files.Length;
        var processed = 0;
        var errors = 0;

        var semaphore = new SemaphoreSlim(maxParallelism);
        var tasks = new List<Task>();

        foreach (var file in files)
        {
            await semaphore.WaitAsync();

            tasks.Add(Task.Run(async () =>
            {
                try
                {
                    if (_sqlRepo != null)
                        await _sqlRepo.IndexDicomFileAsync(file);
                    
                    if (_mongoRepo != null)
                        await _mongoRepo.IndexDicomFileAsync(file);
                    
                    if (_elasticRepo != null)
                        await _elasticRepo.IndexDicomFileAsync(file);

                    Interlocked.Increment(ref processed);
                }
                catch (Exception ex)
                {
                    Interlocked.Increment(ref errors);
                    Console.WriteLine($"Error indexing {file}: {ex.Message}");
                }
                finally
                {
                    semaphore.Release();
                    
                    progress?.Report(new IndexingProgress
                    {
                        Total = total,
                        Processed = processed,
                        Errors = errors
                    });
                }
            }));
        }

        await Task.WhenAll(tasks);
    }
}

public class IndexingProgress
{
    public int Total { get; set; }
    public int Processed { get; set; }
    public int Errors { get; set; }
    public double PercentComplete => Total > 0 ? (double)Processed / Total * 100 : 0;
}

Коришћење примера

class Program
{
    static async Task Main(string[] args)
    {
        // Initialize license
        Metered metered = new Metered();
        metered.SetMeteredKey("your-public-key", "your-private-key");

        // SQL Server
        var optionsBuilder = new DbContextOptionsBuilder<DicomDbContext>();
        optionsBuilder.UseSqlServer("Server=localhost;Database=DicomDB;Trusted_Connection=True;");
        var sqlRepo = new SqlDicomRepository(new DicomDbContext(optionsBuilder.Options));

        // MongoDB
        var mongoRepo = new MongoDicomRepository("mongodb://localhost:27017", "dicom_archive");

        // Elasticsearch
        var elasticRepo = new ElasticDicomRepository("http://localhost:9200");

        // Index a directory
        var indexer = new DicomIndexingService(sqlRepo, mongoRepo, elasticRepo);
        
        var progress = new Progress<IndexingProgress>(p =>
        {
            Console.WriteLine($"Progress: {p.PercentComplete:F1}% ({p.Processed}/{p.Total})");
        });

        await indexer.IndexDirectoryAsync(@"C:\DICOM\Archive", maxParallelism: 8, progress);

        // Search examples
        var sqlResults = await sqlRepo.SearchStudiesAsync(modality: "CT", startDate: DateTime.Today.AddMonths(-1));
        var mongoResults = await mongoRepo.FullTextSearchAsync("chest pain");
        var elasticResults = await elasticRepo.SearchAsync("lung nodule");

        Console.WriteLine($"SQL results: {sqlResults.Count}");
        Console.WriteLine($"MongoDB results: {mongoResults.Count}");
        Console.WriteLine($"Elasticsearch results: {elasticResults.Count}");
    }
}

Закључак

Складиштење ДИЦОМ метада у базама података трансформише медицинске слике претраге и аналитичке способности. СКЛ базе се одликују на структурираним питањима и извештавањем, МонгоДБ пружа флексибилан сачување докумената идеалан за различите схеме метадата, а Еластицсеарх омогућава моћну пуну текстуалну потрагу кроз описе студије.

За више информација о извлачењу ДИЦОМ метадата, посетите Апсос.медицинска документација.

More in this category