From 32a06be96dc126cbe9b28dd0e0cca78f29862a5d Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Thu, 7 May 2026 11:28:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E5=B8=A7=E8=BF=9B=E8=A1=8C=E6=8B=86?= =?UTF-8?q?=E5=88=86=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/ImageAndDoc/StudyService.cs | 211 ++++++++++++++---- 1 file changed, 172 insertions(+), 39 deletions(-) diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs index 4cc69c749..10c7fc48e 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs @@ -17,6 +17,7 @@ using Microsoft.Extensions.Hosting; using NetTopologySuite.Algorithm; using SharpCompress.Common; using SkiaSharp; +using System.Data; using System.Drawing; using ZiggyCreatures.Caching.Fusion; @@ -77,9 +78,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc } - private static async Task TryWriteMergedDicomAsync( -Func> sourceFactory, -Stream output) + private static async Task TryWriteMergedDicomAsync(Func> sourceFactory, Stream output) { try { @@ -125,6 +124,166 @@ Stream output) } } + private static async Task TryWriteSplitDicomAsync(Stream input) + { + var dicomFile = await DicomFile.OpenAsync(input); + + var numberOfFrames = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1); + + //多帧处理逻辑 + if (numberOfFrames > 1) + { + //一定要有像素数据才处理 + var pixelData = DicomPixelData.Create(dicomFile.Dataset); + + if (pixelData != null) + { + try + { + + + var syntax = pixelData.Syntax; + + // 每个 fragment 固定大小 (64KB 示例,可以自己调整) + int fragmentSize = 20 * 1024; + + + + var frag = dicomFile.Dataset.GetDicomItem(DicomTag.PixelData); + + int fragmentCount = frag?.Fragments?.Count() ?? 0; + + var originOffsetTable = frag?.OffsetTable; //有可能没有表,需要自己重建 + + var bot = new List(); + + uint botOffset = 0; + + //需要拆成固定片段的 + if (syntax.IsEncapsulated && fragmentCount == pixelData.NumberOfFrames && numberOfFrames > 1) + { + + var newFragments = new DicomOtherByteFragment(DicomTag.PixelData); + + + #region 最终使用 + + for (int n = 0; n < pixelData.NumberOfFrames; n++) + { + var frameData = pixelData.GetFrame(n); // 获取完整一帧 + var data = frameData.Data; + int offset = 0; + + bot.Add(botOffset); + + botOffset += (uint)data.Length; + + + while (offset < data.Length) + { + botOffset += 8; + + + int size = Math.Min(fragmentSize, data.Length - offset); + var buffer = new byte[size]; + Buffer.BlockCopy(data, offset, buffer, 0, size); + + newFragments.Fragments.Add(new MemoryByteBuffer(buffer)); + + offset += size; + + } + } + + //保留原始偏移表 + + if (originOffsetTable.Count == pixelData.NumberOfFrames) + { + newFragments.OffsetTable.AddRange(originOffsetTable.ToArray()); + + } + else + { + newFragments.OffsetTable.AddRange(bot.ToArray()); + + } + + #endregion + + + dicomFile.Dataset.AddOrUpdate(newFragments); + + + } + //传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败 + else if (syntax.IsEncapsulated && fragmentCount > pixelData.NumberOfFrames && originOffsetTable.Count == 0) + { + + + + uint offset = 0; + + bot.Add(offset); + + var fistSize = frag.Fragments.FirstOrDefault()?.Size ?? 0; + + //和上一个大小不一样 + var isDiffrentBefore = false; + + uint count = 0; + + // 假设你知道每帧对应的 fragment 数量 + foreach (var frameFragments in frag.Fragments) + { + count++; + + if (frameFragments.Size == fistSize) + { + isDiffrentBefore = false; + // 累加这一帧所有 fragment 的大小 + offset += (uint)frameFragments.Size; + continue; + } + else + { + offset += (uint)frameFragments.Size; + isDiffrentBefore = true; + + } + + if (isDiffrentBefore) + { + //每个Fragment 也占用字节 + offset += 8 * count; + bot.Add(offset); + count = 0; + + } + + } + + bot.RemoveAt(bot.Count - 1); + // 设置到新的 PixelData + frag.OffsetTable.AddRange(bot.ToArray()); + + + + } + + } + catch (Exception ex) + { + // 只记录,不传播 + Log.Logger.Warning($"拆分 failed: {ex.Message}"); + } + } + + } + + return dicomFile; + + } + /// /// 标注遮盖影像 路径后面加了.MaskImage 就是遮盖的新路径 /// @@ -164,7 +323,7 @@ Stream output) await using var inputStream = new MemoryStream(); - if (item.IsEncapsulated /*&& item.NumberOfFrames > 1*/) + if (item.IsEncapsulated) { var ok = await TryWriteMergedDicomAsync(() => _oSSService.GetStreamFromOSSAsync(path), inputStream); @@ -175,42 +334,10 @@ Stream output) } - - - - - //await using var ms = new MemoryStream(); - - //await inputStream.CopyToAsync(ms); - - //ms.Position = 0; - - - #region 测试废弃 - //var localPath = Path.Combine(FileStoreHelper.GetDonwnloadImageFolder(_hostEnvironment), Path.GetFileName(path)); - - //await _oSSService.DownLoadFromOSSAsync(path, localPath); - - //await using var input = File.OpenRead(localPath); - - //var outputPath = Path.Combine(FileStoreHelper.GetDonwnloadImageFolder(_hostEnvironment), "After", Path.GetFileName(path)); - - //var outputFolder = Path.GetDirectoryName(outputPath); - //if (!string.IsNullOrEmpty(outputFolder)) - //{ - // Directory.CreateDirectory(outputFolder); - //} - - //await using var output = File.Create(outputPath); - - //await DicomPixelMasker.MaskAsync(inputStream, output,inCommand.MaskRegionList); - - #endregion - - - var outPutStream = await DicomPixelMasker.MaskAsync(inputStream, inCommand.MaskRegionList); + var needUploadDicomFile = await TryWriteSplitDicomAsync(outPutStream); + var prefix = path.Substring(1, path.LastIndexOf('/') - 1); //每次都用一个新的名字 @@ -234,7 +361,13 @@ Stream output) maskFileName = $"{Path.GetFileName(path)}.MaskDicom_{batchId}"; } - await _oSSService.UploadToOSSAsync(outPutStream, prefix, maskFileName, false); + + // 直接写入内存 + await using var uploadStream = new MemoryStream(); + await needUploadDicomFile.SaveAsync(uploadStream); + uploadStream.Position = 0; + + await _oSSService.UploadToOSSAsync(uploadStream, prefix, maskFileName, false); var newPath = $"/{prefix}/{maskFileName}";