Les organitzacions sanitàries necessiten cada vegada més solucions automatitzades per desidentificar les dades d’imatges mèdiques.En aquest guia, aprendreu com construir un microservice de anònimització de DICOM a ASP.NET Core** que rep arxius Dicom a través de HTTP, els anonimitza utilitzant perfils configurables, i torna els resultats desidentificats.Al final, tindreu una arquitectura de servei preparada per a la producció amb codi font de mostra.

Taula de continguts

Revisió de l’arquitectura

El microservice segueix una arquitectura capaç amb una clara separació de preocupacions. La capa API gestiona les sol·licituds HTTP i les respostes a través dels controladors d’ASP.NET Core. la capa de servei conté la lògica de negoci utilitzant l’Aspose.Medical Anonymizer. L’escala de configuració governa les configuracions de perfil i opcions d’aplicació. Els components opcionals inclouen emmagatzematge per al logging i queuing per a el processament de batx.

Aquesta arquitectura permet escalar horitzontal, fàcil de provar i integració senzilla amb els sistemes d’informació hospitalària i els servidors PACS.

Establir el projecte

Comença creant un nou projecte ASP.NET Core Web API i instal·lant el paquet requerit:

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

Configura el projecte per a grans carregaments de fitxers en 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();

Implementació de la capa de servei

Crear una interfície i implementació per al servei d’anonimització:

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

Construcció del controlador API

Crea un controlador que gestiona les carregades de fitxers i torna els arxius DICOM anònims:

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

Profils configurables

Permet configurar els perfils a través de appsettings.json:

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

Crea una classe de configuració i actualitza el servei per llegir des de les configuracions:

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

Consideracions de seguretat

En la implementació d’aquest servei, s’implementen aquestes mesures de seguretat.

Activa HTTPS per assegurar que totes les comunicacions estan encryptades en el transit. Configureu TLS 1.2 o superior i redirigeu l’HTTP a Https.

Authenticació d’implementació utilitzant tokens JWT o clau API per restringir l’accés a clients autoritzats:

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

** Evitar emmagatzemar els arxius originals** no encriptats en el disc. Processar els fitxers en la memòria quan sigui possible, i si es requereix un enregistrament temporal, utilitzeu volums encritats.

Aplicar el registre d’auditoria per rastrejar totes les operacions d’anonimització, incloent qui les va sol·licitar i quan:

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

Optimització de les prestacions

Diverses tècniques poden millorar el rendiment per a escenaris d’alta velocitat.

Use anonimització en lloc quan no cal guardar les dades originals en la memòria:

anonymizer.AnonymizeInPlace(dcm);

Activa el processament paral·lel per a les operacions de batxillerat:

[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...
}

**Implementació de sol·licituds per a batxes molt grans utilitzant una batxa de missatges com ara RabbitMQ o Azure Service Bus per gestionar els pics de càrrega sense sobrepassar el servei.

La conclusió

El servei accepta els fitxers DICOM a través de HTTP, aplica els perfils d’anonimització configurables utilitzant Aspose.Medical i torna els arxius desidentificats preparats per a la investigació o compartir.

Les tasques clau inclouen l’ús d’arquitectura capaç per al manteniment, la implementació de perfils configurables per a la flexibilitat, assegurar el servei amb autenticació i xifració, i optimitzar el rendiment amb processament paral·lel.

Per obtenir el codi font complet i exemples addicionals, visiteu el Aspose.Documentació mèdicaPer provar el foc, Obtenir una llicència temporal gratuïta.

More in this category