Medicinos vaizdavimo programos dažnai reikia ieškoti ir užklausyti DICOM metaduomenis efektyviai. Nors DIKOM failai saugo turtingus metadatus, jo ekstrakcija į duomenų bazes leidžia greitai užklausti, analizuoti ir integruotis su ligoninės informacinėmis sistemomis. Šis vadovas parodo, kaip saugoti DIBO metadatis SQL ir NoSQL duomenų baziuose naudojant Aspose.Medical .NET.

Kodėl DICOM metadata saugoma duomenų bazėse?

DICOM failai turi platų metadata, tačiau paieškos per tūkstančius failų yra lėtas.

  • ** Greitas užklausas**: iš karto suraskite tyrimus pagal pacientą, dieną, būseną ar bet kokį atributą
  • Analytikai: duomenų rinkimas ataskaitoms ir statistikai
  • Integracija: Prisijunkite vaizdo duomenis prie EMR/EHR sistemų
  • Scalability: valdyti milijonus tyrimų su tinkamu indeksavimu
  • Visi teksto paieškos: Rasti tyrimus pagal aprašymus raktinius žodžius

DICOM Metadata ekstrakcijos fondas

Pirmiausia sukurkite metadata ekstraktorių, kuris konvertuoja DICOM į duomenų bazės paruoštus objektus:

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; }
}

SQL serverio integracija

DICOM metaduomenų saugojimas SQL serveryje su Entity Framework Core:

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; }
}

SQL Server repositorius CRUD operacijoms:

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();
    }
}

MongoDB integracija

Išsaugokite DICOM metaduomenis kaip JSON dokumentus MongoDB:

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();
    }
}

Elastinė integracija

Galimas pilnas paieškos tekstas su 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);
    }
}

Batch indeksavimo paslaugos

Indeksas visą DICOM archyvuoja efektyviai:

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;
}

Naudojimo pavyzdys

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}");
    }
}

Conclusion

DICOM metaduomenų saugojimas duomenų bazėse keičia medicininio vaizdo paieškos ir analizės gebėjimus. SQL duomenų bazius puikiai tinka struktūrizuotoms užklausoms ir ataskaitoms, MongoDB suteikia lankstų dokumentų sandėliavimą idealiai skirtingiems metadatų schemams, o Elasticsearch leidžia galingą pilną tekstą ieškoti per tyrimo aprašymus. Aspose.Medical for .NET supaprastina ištraukimo procesą, leidžiant jums sutelkti dėmesį į tvirtų vaizdų duomenų platformų kūrimą.

Norėdami gauti daugiau informacijos apie DICOM metadata ekstrakciją, apsilankykite Aspose.Medicininė dokumentacija.

More in this category