Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • 22:41
    AJohnson2ACR labeled #1280
  • 22:41
    AJohnson2ACR opened #1280
  • 14:28
    amoerie commented #1037
  • 14:26
    JoakimRaySearch commented #1037
  • 14:10
    amoerie commented #1037
  • 14:05
    JoakimRaySearch commented #1037
  • 13:51
    amoerie commented #1037
  • 10:39
    JoakimRaySearch commented #1037
  • 10:09
    amoerie commented #1273
  • 09:41
    codecov[bot] commented #1273
  • 09:31
    codecov[bot] commented #1273
  • 09:31
    amoerie synchronize #1273
  • 09:31

    amoerie on ImproveMemory

    Mark test as flaky, statically … (compare)

  • 08:39
    codecov[bot] commented #1273
  • 08:30
    codecov[bot] commented #1273
  • 08:30
    amoerie synchronize #1273
  • 08:30

    amoerie on ImproveMemory

    Add safety nets for PDataTF and… (compare)

  • Dec 06 16:46
    codecov[bot] commented #1144
  • Dec 06 16:38
    codecov[bot] commented #1144
  • Dec 06 16:37
    amoerie synchronize #1144
1009058470
@1009058470
Hi, is there anyone know the different with Bits Allocated(0028,0100) And Bits Stored(0028,0101)?
Why there code can be Gray or color when i only losing fill the Bits Allocated?
DicomDataset dataset = new DicomDataset();
            dataset.Add(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Rgb.Value);
            dataset.Add(DicomTag.Rows, (ushort)bitmap.Height);
            dataset.Add(DicomTag.Columns, (ushort)bitmap.Width);
            dataset.Add(DicomTag.BitsAllocated, (ushort)8);
            dataset.Add(DicomTag.SOPClassUID, "1.2.840.10008.5.1.4.1.1.2");
            dataset.Add(DicomTag.SOPInstanceUID, "1.2.840.10008.5.1.4.1.1.2.20181120090837121314");
            DicomPixelData pixelData = DicomPixelData.Create(dataset, true);
            pixelData.BitsStored = 8;
            //pixelData.BitsAllocated = 8;
            pixelData.SamplesPerPixel = 3;
            pixelData.HighBit = 7;
            pixelData.PixelRepresentation = 0;
            pixelData.PlanarConfiguration = 0;
            pixelData.AddFrame(buffer);
mrbean-bremen
@mrbean-bremen
Bits Allocated is a mandatory tag, without this the size of the pixel data is not known, and most DICOM software will just not process such data. Bits Stored are the bits that are actually used opposed to the number of bits allocated in memory/on disk.
1009058470
@1009058470

Bits Allocated is a mandatory tag, without this the size of the pixel data is not known, and most DICOM software will just not process such data. Bits Stored are the bits that are actually used opposed to the number of bits allocated in memory/on disk.

Sorry ,i do not understand why without Bits Allocated will make the size of pixel data unknow ?can not use bit stored to caic that?

1009058470
@1009058470
for example
if i have a pixel data=0x01, bitsallocated=16, bitsstored=12,is that means that it will stored in memory or disk with each bit for 0001 0000 0000 or is this 0001 0000 0000 0000
mrbean-bremen
@mrbean-bremen
The latter. With the exception of bit data (where BitsAllocated is 1) the data is is always on a byte boundary (e.g. BitsAllocated is a multiple of 8). Mostly this is the next multiple (like in BitsStored 12, BitsAllocated 16), but for example BitsStored 8, BitsAllocated 16 is also allowed (and I have seen this). So you need the tag. Apart from that, it is a mandatory tag, meaning that a DICOM file without it is considered invalid.
Dash-Uttam
@Dash-Uttam

Hello @ALL

I am creating dicom file from video. File was created successfully but it is not showing up in Osmisis web viewer or stone web viewer. Apart from that when i am uploading that created dicom file manually in orthanc server it is playing fine. So can any one tell me how i can get out from this problem i am attaching my code here. Please guys help me into this.

Dash-Uttam
@Dash-Uttam
code
StringBuilder uid = new StringBuilder();
uid.Append("1.08.1982.10121984.2.0.07").Append(".z").Append(DateTime.UtcNow.Ticks);
        ds.Add(DicomTag.SOPInstanceUID, DicomUID.Generate());
        ds.Add(DicomTag.PatientName, patientDetailsModelForDicom.PatientName);
        ds.Add(DicomTag.PatientSex, patientDetailsModelForDicom.Gender);
        ds.Add(DicomTag.PatientBirthDate, patientDetailsModelForDicom.PatientBirthDate);
        ds.Add(DicomTag.StudyDate, DateTime.Now);
        ds.Add(DicomTag.StudyTime, DateTime.Now);

        DicomPixelData pixelData = DicomPixelData.Create(ds, true);
        pixelData.Width = Convert.ToUInt16(width);
        pixelData.Height = Convert.ToUInt16(height);
        pixelData.NumberOfFrames = frameCount;

        pixelData.BitsStored = 8;
        pixelData.SamplesPerPixel = 3;
        pixelData.HighBit = 7;
        pixelData.PixelRepresentation = 0;
        pixelData.PlanarConfiguration = 0;
        pixelData.PhotometricInterpretation = PhotometricInterpretation.YbrPartial420;



        ds.AddOrUpdate(DicomTag.NumberOfFrames, frameCount);
        ds.AddOrUpdate(DicomTag.Columns, pixelData.Width);
        ds.AddOrUpdate(DicomTag.Rows, pixelData.Height);


        ds.Add(DicomTag.SpecificCharacterSet, "ISO_IR 100");
        ds.Add(DicomTag.ConversionType, "SI");

        ds.AddOrUpdate(DicomTag.BitsAllocated, (ushort)8);
        ds.AddOrUpdate(DicomTag.InstanceNumber, "1");



        DicomDataset sqContent = new DicomDataset
        {
            { DicomTag.Modality, "OT" },
            { DicomTag.ScheduledProcedureStepStartDate, DateTime.Now.Date }
        }; //Content of the sequence

        DicomSequence sq = new DicomSequence(DicomTag.ScheduledProcedureStepSequence, sqContent); // Create sequence, add content
        ds.AddOrUpdate(sq);
        byte[] videoBytes = File.ReadAllBytes(model.Files[0].FileName);
        MemoryByteBuffer buffer = new MemoryByteBuffer(videoBytes);
        pixelData.AddFrame(buffer);
        DicomFile dicomfile = new DicomFile(ds);
        //  dicomfile.FileMetaInfo.TransferSyntax = DicomTransferSyntax.JPEGProcess1;
        dicomfile.FileMetaInfo.TransferSyntax = DicomTransferSyntax.Lookup(DicomUID.MPEG4HP41);
        //    dicomfile.FileMetaInfo.TransferSyntax = DicomTransferSyntax.Lookup(DicomUID.MPEG2MPHL);
        dicomfile.Save(fileName);



        await client.AddRequestAsync(new DicomCStoreRequest(dicomfile));
        await client.SendAsync();
Thomas Boby
@tboby

This is perhaps a silly question: Has anyone managed to use fo-dicom to stream ultrasound image frames directly and in real-time?
I'm using 4.0.8 with WPF and can't seem to get things fast enough.

  • fo-dicom seems to do a lot of byte array copying, and then I make it worse by copying a final time for WPF
  • Efferent might be writing and reading to disk on every frame render

When I try and stream it's very bursty, presumably due to the high GC pressure and dependence on IO, even though the dicom files are in memory.

Is there anything obvious I might be missing? I'm just holding the dicom file in memory and calling RenderImage. I feel like I'm going to have to resort to writing some kind of run-ahead buffer.

1 reply
Dan Nudelman
@dnoodles
Can you change DICOM tag values when doing a cmove? Or you would have to store the image change the data then send it?
Barni
@barna21

Hello,

How can I convert multi fram DICOM file to standard single frame DICOM files?

any help is appreciated
thanks

Jussi Mattila
@jussimattila
:point_up_2: I want to do the same (convert from multi-frame to single frame DICOMs), did you find anything yet?
private static IEnumerable<DicomDataset> GetDicomInstances(DicomFile inputDicomFile)
{
    var numberOfFrames = inputDicomFile.Dataset.GetValueOrDefault(DicomTag.NumberOfFrames, 0, 1);
    if (numberOfFrames < 2)
    {
        yield break;
    }

    DicomPixelData multiFramePixelData = DicomPixelData.Create(inputDicomFile.Dataset);
    for (var frameIndex = 0; frameIndex < numberOfFrames; frameIndex++)
    {
        var image = new DicomImage(inputDicomFile.Dataset, frameIndex);
        var dataset = inputDicomFile.Dataset.Clone();
        dataset.AddOrUpdate(DicomTag.SOPInstanceUID, DicomUIDGenerator.GenerateDerivedFromUUID());
        dataset.AddOrUpdate(DicomTag.NumberOfFrames, 1);
        dataset.AddOrUpdate(DicomTag.InstanceNumber, frameIndex + 1);

        var frame = multiFramePixelData.GetFrame(frameIndex);
        var  framePixelData = DicomPixelData.Create(dataset, true);
        framePixelData.AddFrame(frame);
        yield return dataset;
    }
}
This works but the problem is that it is missing the instance specific DICOM tags, like PixelSpacing, SliceThickness, etc. Pixel data for a frame was easy to extract, but how do you get the dataset of a single frame?
Jussi Mattila
@jussimattila
I know the data exists in sequences in the original dataset, but I was of hoping there would be a built in way to get a dataset per frame easily
Reinhard Gruber
@gofal
@barna21 @jussimattila A similar question has been asked recently on github. See here: #1211
This issue also contains some code example. And it mentions the most important part: if the multiframe image is some kind of EnhancedCT or EnhancedMR, then a lot of important per-frame information is stored in sequences and would have to be extracted and stored into each single file then.
This is not implemented so far, but if you are implementing this anyway, then it would be a great enhancement and worth a pull request.
Jussi Mattila
@jussimattila
I've been looking into this today. For us, a big issue is that tags normally found in MR DICOMs are not available in the multi-frame DICOMs we've seen. E.g., InversionTime, EchoTime and ContrastBolusAgent, which we use for selecting an image processing algorithm to run, are stored under different tags deep in the sequence hierarchy of a multi-frame DICOM. Thus, there needs to be a mapping between tags for this conversion to work (for us), perhaps even scanner-specific.
I could contribute a simple solution that only flattens sequence data corresponding to pixel data frames, but I'm not sure if this is useful or not. I did not implement any kind of tag mapping, so most likely the resulting single frame DICOMs are missing relevant data (for other users as well).
mrbean-bremen
@mrbean-bremen
There is actually the related issue #742, so it would certainly be helpful. Actually there is no mapping needed - the functional groups are there for convenience only and have no meaning apart from grouping tags. You could just iterate over all the groups and collect the contained tags. All tags in the SharedFunctionalGroupsSequence item will go into each new dataset, and the tags from each item in the PerFrameFunctionalGroupsSequence will go into the respective dataset.
Jussi Mattila
@jussimattila
We identify MR sequences using e.g. EchoTime (0018,0081) and InversionTime (0018,0082). In the multi-frame MRs I have, these tags do not exist at all. Instead, there is EffectiveEchoTime (0018,9082) for each frame and a single InversionTimes (0018,9079) for the whole stack. Without mapping, the resulting DICOMs will not be according to spec, I think. I'm not sure if there are other fields like this, but even lacking these would break our current DICOM processing pipelines. So for us, we clearly need to do mapping or handle different (non-standard) tags, if we go from multi-frame DICOM to single frame DICOMs.
mrbean-bremen
@mrbean-bremen
Ah ok, you are right - there are some tags that are specific for multi-frames, so you either have to do the mapping, or include these tags in your logic.
If fo-dicom were to add handling of multi-frames, it would probably only be generic (by flattening the attributes from the groups), the SOPClass-specific part shall be done by the applications IMO
Jussi Mattila
@jussimattila
Yeah, I agree
In our case, I'm leaning towards supporting multi-frame DICOMs natively, instead of trying to convert them, now that I have realized it is not going to be 100% compatible without some additional logic
mrbean-bremen
@mrbean-bremen
Makes sense
Dan Nudelman
@dnoodles
Can you change DICOM tag values when doing a cmove? Or you would have to store the image change the data then send it?
pretty sure I cant do this but want to confirm
Reinhard Gruber
@gofal
@dnoodles i'm not sure what you are asking exactly. But I understand that within your CMoveRequest-Handler of the QRSCP you want to load the file from disk, change some values and then send it via network to the client.
Sure you can do that technically, but be carefull what you are changing. DicomDatasets are meant to be immutable. if you generate new data, you should also generate new InstanceUIDs for those data
Jussi Mattila
@jussimattila
I'm implementing C-STORE SCP. Is OnCStoreRequest "thread-safe", or can we get multiple C-STORE requests in parallel? I'm doing some very quick work when the first instance of a study is received but I would like to confirm that the DICOM files come sequentially, one after another. Or is it possible that during one DICOM transfer association multiple C-STORE requests are being handled in parallel?
HFDornier
@HFDornier

Again a C-FIND challenge paired with characters like "äüö" in the response.dataset. So this already describes my problem well. My response.Dataset contains the "?" character instead of the expected "Umlaute":
string patname = response.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);

DicomPersonName person = new DicomPersonName(DicomTag.PatientName, DicomEncoding.GetEncoding("ISO_IR 100"), patname);

Either solution does not work. The response dataset does NOT contain the specificCharacterSet Tag. It comes from Conquest. But the strange thing is this: If i run the same query from the WeasisViewer against conquest, Weasis displays the patient name correctly.

My query is super simple "^". So that it does not contain the "äöü" in my C-FIND dataset nor a SpecialCharacterSet Tag.

What's it that i am doing wrong?

Thomas Boby
@tboby
I'm trying to reconcile fo-dicom 5 MONOCHROME2 with fo-dicom 4 and my own manually calculated LINEAR VOI LUT function.
GrayscalePixelDataS16 (and all the pixel data implmentations?) seem to cast the final double value to int, which is equivalent to flooring the values.
Does the spec specify somewhere that values should be floored not rounded to the nearest integer?
Looking at DCMTK source it appears to cast, which I believe is truncation in C++ as well, but I haven't been able to confirm.
mrbean-bremen
@mrbean-bremen
In the standard this is not really specified, but the implementations I have seen seem to truncate/cast (have seen the same behavior in proprietary code of a big vendor recently)
Thomas Boby
@tboby
Great, thanks.
Another reconciliation question, trying to ensure fo-dicom doesn't make changes simply by loading and saving (unless necessary).
I have an input file with a private tag of VR SQ using undefined length.
FO-DICOM ignores the setting for explicit sequence length on private tags. Is there a reason for this? I've been combing the standard but I don't see anything obvious, and this file is from a big vendor.
This decision seems to date back a very long way.
fo-dicom/fo-dicom@b2b9441
Masoud
@Mardanshahi

@gofal Guys, I want to add an MP4 file to dicom file and I think I must add my this MP4 file to the Pixel Data tag (7FE0,0010) . So My code is like this:

string mp4 = @"C:\Users\arya.m\Desktop\MP4Dicoms\test_720\20211108-064551.mp4";
var dataset = new DicomDataset();
var rows = 720;
var columns = 1280;
var numFrames = 1080;

dataset.Add(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Rgb.Value);     dataset.Add(DicomTag.Rows, (ushort)rows);
dataset.Add(DicomTag.Columns, (ushort)columns);     dataset.AddOrUpdate(DicomTag.BitsAllocated, (ushort)8);
dataset.AddOrUpdate(DicomTag.TransferSyntaxUID, DicomTransferSyntax.MPEG4AVCH264HighProfileLevel41.UID.UID);
dataset.Add( DicomTag.SOPClassUID, "Video Photographic Image Storage");
dataset.Add(DicomTag.SOPInstanceUID, "1.3.51.5146.1682.20140505.1225531.197");     dataset.AddOrUpdate(DicomTag.PatientName, "Arya pic1");
DicomPixelData pixelData = DicomPixelData.Create(dataset, true);

And at this point I probably add my file to the tag pixeldata like this since I don't know how to write it:

pixelData.Dataset.Add( My mp4 file);
DicomFile dicomfile = new DicomFile(pixelData.Dataset);                             dicomfile.Save(dicom_mp4);

Anyone can help me complete my code?

Reinhard Gruber
@gofal
@tboby I am not aware of any reason for this. explicit and implicit length are both options how to store data. When reading then fo-dicom knows both of them, when writing then fo-dicom can decide which to use, i guess. Is there any reason why you prefer one way over the other? Or is it simply that you feel unconformable that the file changed after you read and stored it?
5 replies
@Mardanshahi See here for a full sample: #976
Ecliptiga
@ecliptiga
Have anybody has full code of implement fo-dicom cmove to download the dicom file from PACS?
Kevin Stephen Biswas
@BluerGost

Hi, I am trying to save(store) modality images. I did a bit of digging but I have not found a straight answer. I feel this is how it might work and would be happy if someone could confirmed it and fix any mistake in my understanding.
1) I think medical images are transferred using PACS. So, I probably have to implement PACS.
2) I found out there are 2 type of file operations Classical-Dicom(DIMSE) and DicomWeb(P3.18).
3) I thought both did the same thing. So, either implementation works but after reading a paper(http://otechimg.com/publications/pdf/dicomweb_white_paper.pdf) I thought maybe the Classical-DIMSE is for communicating with Modality and DimcomWeb is for
any online device(mobile) to access/store the Server image.
4) I still do not know wither the modality directly sends the iamges to server(I am guessing its PACS)「CT(C-STORE SCU)->PACS(C-STORE SCP)」. or it first sends to workstations and then sends to PACS「CT(C-STORE SCU)-> Workstation(C-STORE SCP)/Workstation(C-STORE SCU) -> PACS(C-STORE SCP)」.

PS: I kind of felt guilty asking this conceptual question here. But I do not know any Dicom group where I could ask questions related to dicom's concept. If someone knows kindly let me know.
Thank You.

mrbean-bremen
@mrbean-bremen
@BluerGost - You may want to read some introduction to DICOM first, though I guess you have already searched for that... There is also a Google group, but I'm not sure if this would help you.
First, it is not clear what do you want to do: where your images come from, and where do you want to save them? As for your questions:
1) medical images are transferred using the DICOM protocol; a PACS is an archive that stores DICOM images by implementing the SOP Classes needed for that (e.g. C-FIND SCP, C-MOVE SCP, ...) - in DICOM language, it is just a DICOM node
2, 3) You should not be concerned with DIMSE - this is the protocol uses for service implementations which you get by using frameworks like fo-dicom; DicomWeb is indeed a newer means to transfer DICOM over web - what you need depends on your actual use case
4) This depends on the clinical workflow; images are sent from the modality to one or more configured DICOM nodes. In many cases, the images are indeed sent to a PACS, often they are additionally or instead sent to some workstation that acts as a DICOM node, which may process them and could send them to a PACS afterwards; all this is a matter of configuration
Kevin Stephen Biswas
@BluerGost
@mrbean-bremen Thank you for the replay. I was only given instruction to build something that can save image from Modality. All the other requirements are still unclear and also the actual workflow. What I understood from your replay is that, both DIMSE and DicomWeb can be used for the storage purpose and the workflow is what actually decides what I need. Since I currently do not know the workflow that means I do not know which of the 2 I need to implement.
mrbean-bremen
@mrbean-bremen
@BluerGost - "store images from a modalilty" sounds like you need to implement a C-STORE SCP application, though that is only a guess.
adilt
@adilt
@BluerGost You will find a sample project of DICOM Store SCP at https://github.com/fo-dicom/fo-dicom-samples.
Kevin Stephen Biswas
@BluerGost
Yes, I am following the sample codes. I am implementing the samples. Does dicom specify how a Database should be structured or does dicom only uses DICOMDIR to keep track of the files.
mrbean-bremen
@mrbean-bremen
@BluerGost - no, DICOM does not specify that. DICOMDIR is generally used for export to DVD or file system, not for internal storage - this is usually done by applications using a separate database.
Kevin Stephen Biswas
@BluerGost
Ok, thanks for the information. Another thing I was wondering is how does dicom application ensures authentication and authorization. I am thinking that in DicomWeb does the verification related information and on top of that it sends it's dicom specification (STORE,RETRIVE,etc.) related information. So, If I require UserId and Password to be sent with the STOW request(transaction) then it will attach UserId and Password in the header section. But what happens when I do DIMSE. How do I sent those information. Do I sent then inside Dicom File itself under a specific tag. At the moment I am kind of very poorly reading through PS3.15. So, my understanding could be(should be) wrong. https://dicom.nema.org/medical/dicom/current/output/html/part15.html#sect_A.5.2.1
Mr. Johnson
@Kuroiyatsu

When getting an image using the image.RenderImage().AsSharpImage() method, I get the following error: Decoding dataset with transfer syntax: JPEG 2000 Image Compression (Lossless Only) is not supported.

I only get this when running in an Ubuntu container. I don't have this error when running on Windows.

Any clue for a way around this?

Mr. Johnson
@Kuroiyatsu
Using fo-dicom 5.0.1, fo-dicom.codecs 5.0.2, fo-dicom.imaging.imagesharp 5.0.1
Mr. Johnson
@Kuroiyatsu

Attempting to run image.RenderImage().as<Bitmap>() with the Efferent NativeTranscodeManager throws an exception of: Cannot cast to "Bitmap"; type must be assignable from 'Byte[]

So this method doesn't seem to even work on Windows.