{"id":13654508,"url":"https://github.com/fo-dicom/fo-dicom","last_synced_at":"2025-05-12T13:20:27.260Z","repository":{"id":31763531,"uuid":"35329761","full_name":"fo-dicom/fo-dicom","owner":"fo-dicom","description":"Fellow Oak DICOM for .NET, .NET Core, Universal Windows, Android, iOS, Mono and Unity","archived":false,"fork":false,"pushed_at":"2025-05-12T08:43:05.000Z","size":188443,"stargazers_count":1113,"open_issues_count":61,"forks_count":644,"subscribers_count":97,"default_branch":"development","last_synced_at":"2025-05-12T13:19:46.112Z","etag":null,"topics":["c-sharp","dicom","dot-net","fo-dicom","jpeg","medical","medical-imaging","netcore","netstandard","nuget"],"latest_commit_sha":null,"homepage":"https://fo-dicom.github.io/stable/v5/index.html","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fo-dicom.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"License.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-05-09T13:35:00.000Z","updated_at":"2025-05-12T08:43:07.000Z","dependencies_parsed_at":"2025-04-23T07:03:39.736Z","dependency_job_id":null,"html_url":"https://github.com/fo-dicom/fo-dicom","commit_stats":{"total_commits":3211,"total_committers":126,"mean_commits":"25.484126984126984","dds":0.747430706944877,"last_synced_commit":"1b92c5980a06a1906f4fbb362b4234b9003949de"},"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fo-dicom%2Ffo-dicom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fo-dicom%2Ffo-dicom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fo-dicom%2Ffo-dicom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fo-dicom%2Ffo-dicom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fo-dicom","download_url":"https://codeload.github.com/fo-dicom/fo-dicom/tar.gz/refs/heads/development","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253745197,"owners_count":21957320,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-sharp","dicom","dot-net","fo-dicom","jpeg","medical","medical-imaging","netcore","netstandard","nuget"],"created_at":"2024-08-02T03:00:36.280Z","updated_at":"2025-05-12T13:20:22.227Z","avatar_url":"https://github.com/fo-dicom.png","language":"C#","funding_links":[],"categories":["Frameworks","Libraries"],"sub_categories":["C#"],"readme":"\u003cimg src=\"https://lh3.googleusercontent.com/-Fq3nigRUo7U/VfaIPuJMjfI/AAAAAAAAALo/7oaLrrTBhnw/s1600/Fellow%2BOak%2BSquare%2BTransp.png\" alt=\"fo-dicom logo\" height=\"80\" /\u003e\n\n# Fellow Oak DICOM\n\n[![NuGet](https://img.shields.io/nuget/v/fo-dicom.svg)](https://www.nuget.org/packages/fo-dicom/)\n![build development](https://github.com/fo-dicom/fo-dicom/workflows/build/badge.svg?branch=development)\n[![codecov](https://codecov.io/gh/fo-dicom/fo-dicom/branch/development/graph/badge.svg)](https://codecov.io/gh/fo-dicom/fo-dicom)\n\n### License\nThis library is licensed under the [Microsoft Public License (MS-PL)](http://opensource.org/licenses/MS-PL). See [License.txt](License.txt) for more information.\n\n### Features\n* Targets .NET Standard 2.0\n* DICOM dictionary version 2024d\n* High-performance, fully asynchronous `async`/`await` API\n* JPEG (including lossless), JPEG-LS, JPEG2000, HTJPEG2000, and RLE image compression (via additional package)\n* Supports very large datasets with content loading on demand\n* Image rendering to System.Drawing.Bitmap or SixLabors.ImageSharp\n* JSON and XML export/import\n* Anonymization\n* DICOM services\n* Customize components via DI container \n\n### Supported Runtimes\n\nFellow Oak DICOM officially supports the following runtimes:\n\n* .NET Core 8.0\n* .NET Core 6.0\n* .NET Framework 4.6.2\n\nOther runtimes that implement .NET Standard 2.0 may work, but be aware that our CI pipeline only tests these platforms (and only on Windows)\n\n### Installation\nEasiest is to obtain *fo-dicom* binaries from [NuGet](https://www.nuget.org/packages/fo-dicom/). This package reference the core *fo-dicom* assemblies for all Microsoft and Xamarin platforms.\n\n### NuGet Packages\n*Valid for version 5.0.0 and later*\n\nPackage | Description\n------- | -----------\n[fo-dicom](https://www.nuget.org/packages/fo-dicom/) | Core package containing parser, services and tools.\n[fo-dicom.Imaging.Desktop](https://www.nuget.org/packages/fo-dicom.Imaging.Desktop/) | Library with referencte to System.Drawing, required for rendering into Bitmaps\n[fo-dicom.Imaging.ImageSharp](https://www.nuget.org/packages/fo-dicom.Imaging.ImageSharp/) | Library with reference to ImageSharp, can be used for platform independent rendering\n[fo-dicom.Codecs](https://www.nuget.org/packages/fo-dicom.Codecs/) | Cross-platform Dicom codecs for fo-dicom, developed by Efferent Health (https://github.com/Efferent-Health/fo-dicom.Codecs)\n\n\n### Documentation\nDocumentation, including API documentation, is available via GitHub pages:\n- documentation for the latest release for [fo-dicom 4](https://fo-dicom.github.io/stable/v4/index.html) and\n  [fo-dicom 5](https://fo-dicom.github.io/stable/v5/index.html)\n- documentation for the development version for [fo-dicom 4](https://fo-dicom.github.io/dev/v4/index.html) and\n  [fo-dicom 5](https://fo-dicom.github.io/dev/v5/index.html)\n\n### Usage Notes\n\n#### Getting started in modern .NET\n\nIf you are using the web application builder:\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddFellowOakDicom();\nvar app = builder.Build();\n// This is still necessary for now until fo-dicom has first-class AspNetCore integration\nDicomSetupBuilder.UseServiceProvider(app.Services);\n```\n\nIf you are using the host builder:\n\n```csharp\nvar host = Host.CreateDefaultBuilder(args)\n    .ConfigureServices(services =\u003e\n    {\n        services.AddFellowOakDicom();\n    })\n    .Build();\n\n// This is still necessary for now until fo-dicom has first-class AspNetCore integration\nDicomSetupBuilder.UseServiceProvider(host.Services);\n```\n\nIf you are not using the host builder, you'll need to make your own service collection:\n\n```csharp\nvar services = new ServiceCollection();\nservices.AddFellowOakDicom();\nvar serviceProvider = services.BuildServiceProvider();\nDicomSetupBuilder.UseServiceProvider(serviceProvider);\n```\n\n#### Getting started in .NET Framework\n\nUse `DicomSetupBuilder` to configure the internals of Fellow Oak DICOM:\n\n```csharp\nnew DicomSetupBuilder()\n    .RegisterServices(s =\u003e s.AddFellowOakDicom())\n.Build();\n```\n\n#### Dependency injection support\n\nWhenever you use APIs of Fellow Oak DICOM such as `DicomFile.Open`, `DicomServerFactory.Create`, the global statically registered service provider (`DicomSetupBuilder.UseServiceProvider`) will be used to resolve dependencies.  \nPlease note that using dependency injection is generally preferred over the static APIs, if it is available.\n\n| Use case                                      | Static API                                                 | Can use dependency injection ?                   |\n| --------------------------------------------- |------------------------------------------------------------| ------------------------------------------------ |\n| Creating a DICOM server                       | `DicomServerFactory.Create`                                | Yes, use `IDicomServerFactory`                   |\n| Creating a DICOM client                       | `DicomClientFactory.Create`                                | Yes, use `IDicomClientFactory`                   |\n| Creating an advanced DICOM client connection  | `AdvancedDicomClientConnectionFactory.OpenConnectionAsync` | Yes, use `IAdvancedDicomClientConnectionFactory` |\n| Opening a DICOM file                          | `DicomFile.OpenAsync(..)`                                  | No                                               |\n| Rendering a DICOM file                        | `new DicomImage(..).RenderImage(..)`                       | No                                               |\n\n#### Injecting custom dependencies into DICOM services\n\nIt is possible to inject custom dependencies into your DICOM services, but with one important requirement: **you must have these exact three constructor parameters: `INetworkStream stream, Encoding fallbackEncoding, ILogger logger`**. \nThe names and their order don't matter, but the types do.\nYes, `ILogger` is a non-generic typed logger. If you want a `Logger\u003cT\u003e`, you can **add** an extra constructor parameter and use it however you see fit.\n\nHere is a full standalone example:\n\n```csharp\nusing System.Text;\nusing FellowOakDicom;\nusing FellowOakDicom.Network;\nusing FellowOakDicom.Network.Client;\n\nvar host = Host.CreateDefaultBuilder(args)\n    .ConfigureLogging(logging =\u003e\n    {\n        logging.ClearProviders();\n        logging.AddConsole();\n        logging.SetMinimumLevel(LogLevel.Information);\n    })\n    .ConfigureServices(services =\u003e\n    {\n        services.AddFellowOakDicom();\n        services.AddHostedService\u003cWorker\u003e();\n        // This will be injected into the DICOM service\n        services.AddSingleton\u003cCustomDependency\u003e();\n    })\n    .Build();\n\nDicomSetupBuilder.UseServiceProvider(host.Services);\n\nhost.Run();\n\npublic class CustomDependency\n{\n    \n}\n\npublic class Worker : IHostedService\n{\n    private readonly ILogger\u003cWorker\u003e _logger;\n    private readonly IDicomServerFactory _dicomServerFactory;\n    private readonly IDicomClientFactory _dicomClientFactory;\n    private IDicomServer? _server;\n\n    public Worker(ILogger\u003cWorker\u003e logger, IDicomServerFactory dicomServerFactory, IDicomClientFactory dicomClientFactory)\n    {\n        _logger = logger ?? throw new ArgumentNullException(nameof(logger));\n        _dicomServerFactory = dicomServerFactory ?? throw new ArgumentNullException(nameof(dicomServerFactory));\n        _dicomClientFactory = dicomClientFactory ?? throw new ArgumentNullException(nameof(dicomClientFactory));\n    }\n\n    public async Task StartAsync(CancellationToken cancellationToken)\n    {\n        _logger.LogInformation(\"Starting DICOM server\");\n        _server = _dicomServerFactory.Create\u003cEchoService\u003e(104);\n        _logger.LogInformation(\"DICOM server is running\");\n\n        var client = _dicomClientFactory.Create(\"127.0.0.1\", 104, false, \"AnySCU\", \"AnySCP\");\n\n        _logger.LogInformation(\"Sending C-ECHO request\");\n        DicomCEchoResponse? response = null;\n        await client.AddRequestAsync(new DicomCEchoRequest { OnResponseReceived = (_, r) =\u003e response = r});\n        await client.SendAsync(cancellationToken);\n        if (response != null)\n        {\n            _logger.LogInformation(\"C-ECHO response received\");\n        }\n        else\n        {\n            _logger.LogError(\"No C-ECHO response received\");\n        }\n    }\n\n    public Task StopAsync(CancellationToken cancellationToken)\n    {\n        if (_server != null)\n        {\n            _server.Stop();\n            _server.Dispose();\n            _server = null;\n        }\n        return Task.CompletedTask;\n    }\n}\n\npublic class EchoService : DicomService, IDicomServiceProvider, IDicomCEchoProvider\n{\n    private readonly ILogger _logger;\n    private readonly CustomDependency _customDependency;\n\n    public EchoService(INetworkStream stream,\n        Encoding fallbackEncoding, \n        ILogger logger,\n        DicomServiceDependencies dependencies,\n        CustomDependency customDependency) : base(stream, fallbackEncoding, logger, dependencies)\n    {\n        _logger = logger ?? throw new ArgumentNullException(nameof(logger));\n        _customDependency = customDependency ?? throw new ArgumentNullException(nameof(customDependency));\n    }\n\n    public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason) =\u003e _logger.LogInformation(\"Received abort\");\n    public void OnConnectionClosed(Exception exception) =\u003e _logger.LogInformation(\"Connection closed\");\n\n    public Task OnReceiveAssociationRequestAsync(DicomAssociation association)\n    {\n        foreach (DicomPresentationContext presentationContext in association.PresentationContexts)\n            presentationContext.SetResult(DicomPresentationContextResult.Accept);\n        return SendAssociationAcceptAsync(association);\n    }\n\n    public Task OnReceiveAssociationReleaseRequestAsync()\n    {\n        _logger.LogInformation(\"Received association release\");\n        return Task.CompletedTask;\n    }\n\n    public Task\u003cDicomCEchoResponse\u003e OnCEchoRequestAsync(DicomCEchoRequest request) =\u003e Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));\n}\n```\n\n\n#### Image rendering configuration\nOut-of-the-box, *fo-dicom* defaults to an internal class *FellowOakDicom.Imaging.IImage*-style image rendering. To switch to Desktop-style or ImageSharp-style image rendering, you first have to add the nuget package you desire and then call:\n\n```csharp\nnew DicomSetupBuilder()\n    .RegisterServices(s =\u003e s.AddFellowOakDicom().AddImageManager\u003cWinFormsImageManager\u003e())\n.Build();\n```\n\nor\n\n```csharp\nnew DicomSetupBuilder()\n    .RegisterServices(s =\u003e s.AddFellowOakDicom().AddImageManager\u003cImageSharpImageManager\u003e())\n.Build();\n```\n\nThen when rendering you can cast the IImage to the type by\n\n```csharp\nvar image = new DicomImage(\"filename.dcm\");\nvar bitmap = image.RenderImage().As\u003cBitmap\u003e();\n```\n\nor\n\n```csharp\nvar image = new DicomImage(\"filename.dcm\");\nvar sharpimage = image.RenderImage().AsSharpImage();\n```\n    \n#### Logging configuration\nFellow Oak DICOM uses `Microsoft.Extensions.Logging`, so if you are already using that, Fellow Oak DICOM logging will show up automatically.\n\nIn the past, Fellow Oak DICOM had a custom abstraction for logging: ILogger and ILogManager.\nFor backwards compatibility purposes, this is still supported but not recommended for new applications.\n\n```csharp\nservices.AddLogManager\u003cMyLogManager\u003e();\n```\n\nwhere MyLogManager looks like this:\n\n```\nusing FellowOakDicom.Log;\n\npublic class MyLogManager: ILogManager {\n    public ILogger GetLogger(string name) {\n        ...\n    }\n}\n```\n\n\n### Sample applications\nThere are a number of simple sample applications that use *fo-dicom* available in separate repository [here](https://github.com/fo-dicom/fo-dicom-samples). These also include the samples\nthat were previously included in the *Examples* sub-folder of the VS solutions.\n\n### Examples\n\n#### File Operations\n```csharp\nvar file = DicomFile.Open(@\"test.dcm\");             // Alt 1\nvar file = await DicomFile.OpenAsync(@\"test.dcm\");  // Alt 2\n\nvar patientid = file.Dataset.GetString(DicomTag.PatientID);\n\nfile.Dataset.AddOrUpdate(DicomTag.PatientName, \"DOE^JOHN\");\n\n// creates a new instance of DicomFile\nvar newFile = file.Clone(DicomTransferSyntax.JPEGProcess14SV1);\n\nfile.Save(@\"output.dcm\");             // Alt 1\nawait file.SaveAsync(@\"output.dcm\");  // Alt 2\n```\n\n#### Render Image to JPEG\n```csharp\nvar image = new DicomImage(@\"test.dcm\");\nimage.RenderImage().AsBitmap().Save(@\"test.jpg\");                     // Windows Forms\n\n```\n\n#### C-Store SCU\n```csharp\nvar client = DicomClientFactory.Create(\"127.0.0.1\", 12345, false, \"SCU\", \"ANY-SCP\");\nawait client.AddRequestAsync(new DicomCStoreRequest(@\"test.dcm\"));\nawait client.SendAsync();\n```\n\n#### C-Echo SCU/SCP\n```csharp\nvar server = DicomServerFactory.Create\u003cDicomCEchoProvider\u003e(12345);\n\nvar client = DicomClientFactory.Create(\"127.0.0.1\", 12345, false, \"SCU\", \"ANY-SCP\");\nclient.NegotiateAsyncOps();\n// Optionally negotiate user identity\nclient.NegotiateUserIdentity(new DicomUserIdentityNegotiation\n{\n    UserIdentityType = DicomUserIdentityType.Jwt,\n    PositiveResponseRequested = true,\n    PrimaryField = \"JWT_TOKEN\"\n});\nfor (int i = 0; i \u003c 10; i++)\n    await client.AddRequestAsync(new DicomCEchoRequest());\nawait client.SendAsync();\n```\n\n#### C-Find SCU\n```csharp\nvar cfind = DicomCFindRequest.CreateStudyQuery(patientId: \"12345\");\ncfind.OnResponseReceived = (DicomCFindRequest rq, DicomCFindResponse rp) =\u003e {\n\tConsole.WriteLine(\"Study UID: {0}\", rp.Dataset.GetString(DicomTag.StudyInstanceUID));\n};\n\nvar client = DicomClientFactory.Create(\"127.0.0.1\", 11112, false, \"SCU-AE\", \"SCP-AE\");\nawait client.AddRequestAsync(cfind);\nawait client.SendAsync();\n```\n\n#### C-Move SCU\n```csharp\nvar cmove = new DicomCMoveRequest(\"DEST-AE\", studyInstanceUid);\n\nvar client = DicomClientFactory.Create(\"127.0.0.1\", 11112, false, \"SCU-AE\", \"SCP-AE\");\nawait client.AddRequestAsync(cmove);\nawait client.SendAsync(); \n```\n\n#### N-Action SCU\n```csharp\n// It is better to increase 'associationLingerTimeoutInMs' default is 50 ms, which may not be\n// be sufficient\nvar dicomClient = DicomClientFactory.Create(\"127.0.0.1\", 12345, false, \"SCU-AE\", \"SCP-AE\",\nDicomClientDefaults.DefaultAssociationRequestTimeoutInMs, DicomClientDefaults.DefaultAssociationReleaseTimeoutInMs,5000);\nvar txnUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID;\nvar nActionDicomDataSet = new DicomDataset\n{\n    { DicomTag.TransactionUID,  txnUid }\n};\nvar dicomRefSopSequence = new DicomSequence(DicomTag.ReferencedSOPSequence);\nvar seqItem = new DicomDataset()\n{\n    { DicomTag.ReferencedSOPClassUID, \"1.2.840.10008.5.1.4.1.1.1\" },\n    { DicomTag.ReferencedSOPInstanceUID, \"1.3.46.670589.30.2273540226.4.54\" }\n};\ndicomRefSopSequence.Items.Add(seqItem);\nnActionDicomDataSet.Add(dicomRefSopSequence);\nvar nActionRequest = new DicomNActionRequest(DicomUID.StorageCommitmentPushModelSOPClass,\n                DicomUID.StorageCommitmentPushModelSOPInstance, 1)\n{\n    Dataset = nActionDicomDataSet,\n    OnResponseReceived = (DicomNActionRequest request, DicomNActionResponse response) =\u003e \n    {\n        Console.WriteLine(\"NActionResponseHandler, response status:{0}\", response.Status);\n    },\n};\nawait dicomClient.AddRequestAsync(nActionRequest);\ndicomClient.OnNEventReportRequest = OnNEventReportRequest;\nawait dicomClient.SendAsync();\n\nprivate static Task\u003cDicomNEventReportResponse\u003e OnNEventReportRequest(DicomNEventReportRequest request)\n{\n    var refSopSequence = request.Dataset.GetSequence(DicomTag.ReferencedSOPSequence);\n    foreach(var item in refSopSequence.Items)\n    {\n        Console.WriteLine(\"SOP Class UID: {0}\", item.GetString(DicomTag.ReferencedSOPClassUID));\n        Console.WriteLine(\"SOP Instance UID: {0}\", item.GetString(DicomTag.ReferencedSOPInstanceUID));\n    }\n    return Task.FromResult(new DicomNEventReportResponse(request, DicomStatus.Success));\n}\n```\n\n#### C-ECHO with advanced DICOM client connection: manual control over TCP connection and DICOM association\n```csharp\nvar cancellationToken = CancellationToken.None;\n// Alternatively, inject IDicomServerFactory via dependency injection instead of using this static method\nusing var server = DicomServerFactory.Create\u003cDicomCEchoProvider\u003e(12345); \n\nvar connectionRequest = new AdvancedDicomClientConnectionRequest\n{\n    NetworkStreamCreationOptions = new NetworkStreamCreationOptions\n    {\n        Host = \"127.0.0.1\",\n        Port = server.Port,\n    }\n};\n\n// Alternatively, inject IAdvancedDicomClientConnectionFactory via dependency injection instead of using this static method\nusing var connection = await AdvancedDicomClientConnectionFactory.OpenConnectionAsync(connectionRequest, cancellationToken);\n\nvar associationRequest = new AdvancedDicomClientAssociationRequest\n{\n    CallingAE = \"EchoSCU\",\n    CalledAE = \"EchoSCP\",\n    // Optionally negotiate user identity\n    UserIdentityNegotiation = new DicomUserIdentityNegotiation\n    {\n        UserIdentityType = DicomUserIdentityType.UsernameAndPasscode,\n        PositiveResponseRequested = true,\n        PrimaryField = \"USERNAME\",\n        SecondaryField = \"PASSCODE\",\n    }\n};\n\nvar cEchoRequest = new DicomCEchoRequest();\n\nusing var association = await connection.OpenAssociationAsync(associationRequest, cancellationToken);\ntry\n{\n    DicomCEchoResponse cEchoResponse = await association.SendCEchoRequestAsync(cEchoRequest, cancellationToken).ConfigureAwait(false);\n    \n    Console.WriteLine(cEchoResponse.Status);\n}\nfinally\n{\n    await association.ReleaseAsync(cancellationToken);\n}\n```\n\n### New to DICOM?\n\nIf you are new to DICOM, then take a look at the DICOM tutorial of Saravanan Subramanian:\nhttps://saravanansubramanian.com/dicomtutorials/\nThe author is also using fo-dicom in some code samples.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffo-dicom%2Ffo-dicom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffo-dicom%2Ffo-dicom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffo-dicom%2Ffo-dicom/lists"}