Organizacje opieki zdrowotnej coraz bardziej potrzebują automatycznych rozwiązań do de-identyfikacji danych obrazów medycznych.W tym przewodniku dowiesz się, jak zbudować DICOM anonimowość microservice w ASP.NET Core, który otrzymuje pliki DICUM za pośrednictwem HTTP, anonymializuje je za pomocą konfigurowalnych profili, i zwraca deidentified wyniki.

Tabela zawartości

Przegląd architektury

Mikroservice śledzi warstwę architektury z wyraźnym oddzieleniem obaw. warstwa API obsługuje zapytania HTTP i odpowiedzi za pośrednictwem kontrolerów ASP.NET Core. Warstwa obsługi zawiera logikę biznesową za pomocą Aspose.Medical Anonymizer. Ścieżka konfiguracji zarządza ustawieniami profilu i opcjami aplikacji. Opcjonalne komponenty obejmują przechowywanie do logowania i kwing dla przetwarzania partii.

Ta architektura umożliwia skalowanie horyzontalne, łatwe testowanie i prostą integrację z systemami informacyjnymi szpitala i serwerami PACS.

Ustawienie projektu

Zacznij tworząc nowy projekt ASP.NET Core Web API i zainstaluj wymagany pakiet:

dotnet new webapi -n DicomAnonymizationService
cd DicomAnonymizationService
dotnet add package Aspose.Medical

Konfiguruj projekt dla dużych przesyłek plików Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Configure for large DICOM files
builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 500_000_000; // 500MB
});

builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 500_000_000;
});

// Register services
builder.Services.AddScoped<IDicomAnonymizationService, DicomAnonymizationService>();
builder.Services.AddControllers();

var app = builder.Build();

app.MapControllers();
app.Run();

Wdrażanie warstwy serwisowej

Tworzenie interfejsu i wdrożenie usługi anonimowości:

using Aspose.Medical.Dicom;
using Aspose.Medical.Dicom.Anonymization;

public interface IDicomAnonymizationService
{
    Task<byte[]> AnonymizeAsync(Stream inputStream, string? profileName = null);
    Task<byte[]> AnonymizeAsync(byte[] inputBytes, string? profileName = null);
}

public class DicomAnonymizationService : IDicomAnonymizationService
{
    private readonly ILogger<DicomAnonymizationService> _logger;
    private readonly Dictionary<string, ConfidentialityProfileOptions> _profiles;

    public DicomAnonymizationService(ILogger<DicomAnonymizationService> logger)
    {
        _logger = logger;
        _profiles = new Dictionary<string, ConfidentialityProfileOptions>
        {
            ["basic"] = ConfidentialityProfileOptions.BasicProfile,
            ["research"] = ConfidentialityProfileOptions.BasicProfile | 
                          ConfidentialityProfileOptions.RetainPatientChars,
            ["internal"] = ConfidentialityProfileOptions.RetainUIDs | 
                          ConfidentialityProfileOptions.RetainDeviceIdent,
            ["clean"] = ConfidentialityProfileOptions.CleanGraph | 
                       ConfidentialityProfileOptions.CleanDesc
        };
    }

    public async Task<byte[]> AnonymizeAsync(Stream inputStream, string? profileName = null)
    {
        using var memoryStream = new MemoryStream();
        await inputStream.CopyToAsync(memoryStream);
        return await AnonymizeAsync(memoryStream.ToArray(), profileName);
    }

    public Task<byte[]> AnonymizeAsync(byte[] inputBytes, string? profileName = null)
    {
        return Task.Run(() =>
        {
            // Create anonymizer with selected profile
            Anonymizer anonymizer = CreateAnonymizer(profileName);

            // Load DICOM from bytes
            using var inputStream = new MemoryStream(inputBytes);
            DicomFile dcm = DicomFile.Open(inputStream);

            // Anonymize
            DicomFile anonymizedDcm = anonymizer.Anonymize(dcm);

            // Write to output stream
            using var outputStream = new MemoryStream();
            anonymizedDcm.Save(outputStream);

            _logger.LogInformation("Anonymized DICOM file using profile: {Profile}", 
                profileName ?? "default");

            return outputStream.ToArray();
        });
    }

    private Anonymizer CreateAnonymizer(string? profileName)
    {
        if (string.IsNullOrEmpty(profileName) || !_profiles.ContainsKey(profileName.ToLower()))
        {
            return new Anonymizer();
        }

        var options = _profiles[profileName.ToLower()];
        var profile = ConfidentialityProfile.CreateDefault(options);
        return new Anonymizer(profile);
    }
}

Tworzenie kontrolera API

Tworzenie sterownika, który obsługuje przesyłki plików i zwraca anonimowe pliki DICOM:

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class AnonymizeController : ControllerBase
{
    private readonly IDicomAnonymizationService _anonymizationService;
    private readonly ILogger<AnonymizeController> _logger;

    public AnonymizeController(
        IDicomAnonymizationService anonymizationService,
        ILogger<AnonymizeController> logger)
    {
        _anonymizationService = anonymizationService;
        _logger = logger;
    }

    /// <summary>
    /// Anonymize a single DICOM file
    /// </summary>
    [HttpPost]
    [RequestSizeLimit(500_000_000)]
    public async Task<IActionResult> AnonymizeFile(
        IFormFile file,
        [FromQuery] string? profile = null)
    {
        if (file == null || file.Length == 0)
        {
            return BadRequest("No file uploaded");
        }

        try
        {
            _logger.LogInformation("Processing file: {FileName}, Size: {Size}", 
                file.FileName, file.Length);

            using var stream = file.OpenReadStream();
            byte[] anonymizedBytes = await _anonymizationService.AnonymizeAsync(stream, profile);

            string outputFileName = $"anonymized_{file.FileName}";
            return File(anonymizedBytes, "application/dicom", outputFileName);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error anonymizing file: {FileName}", file.FileName);
            return StatusCode(500, $"Anonymization failed: {ex.Message}");
        }
    }

    /// <summary>
    /// Anonymize DICOM data from request body
    /// </summary>
    [HttpPost("stream")]
    [RequestSizeLimit(500_000_000)]
    public async Task<IActionResult> AnonymizeStream([FromQuery] string? profile = null)
    {
        try
        {
            byte[] anonymizedBytes = await _anonymizationService.AnonymizeAsync(
                Request.Body, profile);

            return File(anonymizedBytes, "application/dicom", "anonymized.dcm");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error anonymizing stream");
            return StatusCode(500, $"Anonymization failed: {ex.Message}");
        }
    }

    /// <summary>
    /// List available anonymization profiles
    /// </summary>
    [HttpGet("profiles")]
    public IActionResult GetProfiles()
    {
        var profiles = new[]
        {
            new { name = "basic", description = "Standard anonymization removing most identifiers" },
            new { name = "research", description = "Retains patient characteristics for research" },
            new { name = "internal", description = "Retains UIDs for internal tracking" },
            new { name = "clean", description = "Removes burned-in graphics and descriptions" }
        };

        return Ok(profiles);
    }
}

Konfigurowane profily

Umożliwia konfigurowanie profili poprzez appsettings.json:

{
  "AnonymizationProfiles": {
    "default": "basic",
    "profiles": {
      "basic": ["BasicProfile"],
      "research": ["BasicProfile", "RetainPatientChars"],
      "internal": ["RetainUIDs", "RetainDeviceIdent"],
      "clean": ["CleanGraph", "CleanDesc"],
      "maximum": ["BasicProfile", "CleanGraph", "CleanDesc"]
    }
  }
}

Tworzyć klasę konfiguracji i zaktualizować usługę do odczytu z ustawień:

public class AnonymizationProfileOptions
{
    public string Default { get; set; } = "basic";
    public Dictionary<string, string[]> Profiles { get; set; } = new();
}

// In Program.cs
builder.Services.Configure<AnonymizationProfileOptions>(
    builder.Configuration.GetSection("AnonymizationProfiles"));

Rozważania dotyczące bezpieczeństwa

Podczas korzystania z tej usługi wdrażamy te środki bezpieczeństwa.

Umożliwia HTTPS, aby upewnić się, że wszystkie komunikacje są szyfrowane w czasie tranzytu. skonfigurować TLS 1.2 lub wyższy i przekierować Http do https.

Wdrożenie uwierzytelniania za pomocą tokenów JWT lub kluczy API w celu ograniczenia dostępu do upoważnionych klientów:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class AnonymizeController : ControllerBase
{
    // Controller code...
}

** Unikaj przechowywania oryginalnych plików** bez szyfrowania na dysku. przetwarzanie pliki w pamięci, gdy jest to możliwe, a jeśli potrzebne jest tymczasowe zapisywanie, użyj zaszyfrowanych objętości.

Umożliwia rejestrację audytu do śledzenia wszystkich operacji anonimowości, w tym tych, którzy ich poprosili i kiedy:

_logger.LogInformation(
    "Anonymization completed - User: {User}, Profile: {Profile}, FileSize: {Size}",
    User.Identity?.Name, profile, file.Length);

Optymalizacja wydajności

Kilka technik może poprawić wydajność dla scenariuszy o wysokiej mocy.

Użyj anonimowości w miejscu, gdy nie musisz przechowywać oryginalnych danych w pamięci:

anonymizer.AnonymizeInPlace(dcm);

Umożliwia równoległe przetwarzanie dla operacji zbiornika:

[HttpPost("batch")]
public async Task<IActionResult> AnonymizeBatch(List<IFormFile> files)
{
    var results = await Parallel.ForEachAsync(
        files,
        new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
        async (file, ct) =>
        {
            using var stream = file.OpenReadStream();
            return await _anonymizationService.AnonymizeAsync(stream);
        });

    // Return results...
}

Wdrożenie żądania kwing dla bardzo dużych partii za pomocą kwingu wiadomości, takich jak RabbitMQ lub Azure Service Bus do obsługi spików ładowania bez przesadzenia usługi.

Konkluzja

Teraz masz kompletny plakat do budowy DICOM anonimizacji microservice w ASP.NET Core. Usługa akceptuje pliki DICUM za pośrednictwem HTTP, stosuje konfigurowalne profily anonimizacji za pomocą Aspose.Medical i zwraca de-identyfikowane pliky gotowe do badań lub udostępniania.

Kluczowe zadania obejmują wykorzystanie warstwy architektury w celu utrzymania, wdrażanie konfigurowalnych profilów dla elastyczności, zapewnienie usługi z autentycznością i szyfrowaniem oraz optymalizację wydajności za pomocą równoległego przetwarzania.

Aby uzyskać pełny kod źródłowy i dodatkowe przykłady, odwiedź Aspose.Dokumentacja medycznaAby spróbować ognia, Zdobądź darmową licencję tymczasową.

More in this category