jasper.yeh
 Senior Member Posts:523

 |
| 2008-06-18 11:14 AM |
|
Our code reads/writes private tags, and since these tags aren't defined in the data dictionary, they're read in as UN tags in the dataset when using implicit.
So far, my thoughts are to get the UN attribute from the attributecollection using the private DicomTag, get the values from the UN attribute as a byte[], create a Dicom.IO.ByteBuffer with this byte array and set all parameters for interpreting data (endianess and the like), use the ToXYZ methods to assign data to the values of a new XY attribute, and then set the item in attributecollection at the private tag to this new XY attribute.
And then, somewhere in there, put a huge switch on the actual VR and cover all possible cases of XY.
Any comments on this method? Or perhaps is there an easier way?
|
|
| // Jasper |
|
|
steve
 Senior Member Posts:1932
 |
| 2008-06-18 12:24 PM |
|
Hi Jasper, The method you suggest for solving this problem would work. However, I'd probably actually recommend that instead you develop a way to include private tags in the data dictionary. Specifically, if you were to implement this ticket: https://trac.clearcanvas.ca/source/ticket/937 I would suggest creating a new class, DicomPrivateTagDictionary, which could include the dictionary for private tags, and the ability to look up the tag based on the Private Creator Code, group, and element byte. You would then require a small change in the StreamReader.cs, in the Read method, where we current call DicomTagDictionary.GetDicomTag(), to do a check to see if the tag is a private attribute, and call the DicomPrivateTagDictionary instead. (you'd also have to retrieve the private creator code out of the message at this point.) To start with, the DicomPrivateTagDictionary could just be loaded at run-time with your private tags whenever your app is initialized. Let me know if you have questions. If you know your private tags are pretty static, I wouldn't mind having the DicomPrivateTagDictionary having a list of "known" private tags for which the definition is publicly available. You could just include your tags when the class is init right in the tool kit code. Steve |
|
| Real-time support available to Clinical Edition and Team Edition customers |
|
|
dblanchard
 Senior Member Posts:185
 |
| 2008-06-18 01:32 PM |
|
Hi,
I was actually thinking about implementing a Private Tag Dictionary because we actually write as ExplicitVR to get the UN issue.
I think it would be helpful to have a List of Private Dictionaries because we may have several for different companies. ie, make a IDictionary or IPrivateDictionary interface w/ the appropriate methods to get DicomTag info that each private dictionary concrete class can implement. (The DicomTagDictionary could do the same)
Regards,
Dan
|
|
|
|
|
jasper.yeh
 Senior Member Posts:523

 |
| 2008-06-18 05:13 PM |
|
Well, I had actually started writing something of the sort already. I'll post my solution when I've cleaned it up a bit. Basically, I have an implementor class that defines all the private tags and can create a collection of dicomattributes that wraps an actual dicomattributecollection so that subsequent modifications to either collection are reflected in the other. On a side note: I had a couple of problems with properly decoding some UN attributes that should be a binary type. The Dicom.IO.ByteConvertor has methods ToUInt16Array and ToInt16Array but, unlike the other ToXYZArray methods, do not seem to take into account system byte order. Additionally, ToDoubleArray has a typo in it, causing it to decode a multi-valued FD attribute using the lower dword of one double and the upper dword of the next double. |
Attachment: ByteConvertor.cs.patch
|
| // Jasper |
|
|
steve
 Senior Member Posts:1932
 |
| 2008-06-18 07:09 PM |
|
Dan, Concerning your comments, are you saying you have conflicts with the tags between different companies that you can't just include all the private tags in a single dictionary? It seems like the combination of the private creator code, the group number, and the element byte should be a unique identifier of a private tag. Because of this, I'm unsure of the need for separate dictionaries. I guess there would, however, be some value in having a dedicated interface for the dictionary for loading purposes at run time. Jasper -- concerning the patch, thanks for posting. I think we need some slightly better regression tests in this case to check for streaming. It looks like we must not have been testing streaming in out messages for all VRs, and missed this problem. I'll take a closer look at it and your patch. Thanks, Steve |
|
| Real-time support available to Clinical Edition and Team Edition customers |
|
|
dblanchard
 Senior Member Posts:185
 |
| 2008-06-18 10:54 PM |
|
Hi Steve,
Perhaps it is not necessary to be able to have several private dictionaries, but it could be helpful to have a different private dictionary for each vender, not because of tag conflict but just for code organizational purposes. But I could live without it... or, are you just proposing having a way to add tags at runtime that the stream reader and writer will be able to read? I could live with that too...
Dan
|
|
|
|
|
steve
 Senior Member Posts:1932
 |
| 2008-06-19 12:19 AM |
|
Dan & Jasper, Upon further review and looking at the DicomTag class, it looks like there's some changes that would need to be done to support this. Specifically, we don't have a way to store the "Private Creator Code" in the DicomTag object. The group and element byte could just be encoded in the tag value itself. There needs to be a new constructor added to DicomTag to be used when defining a private tag. Concerning the dictionary, i was thinking of something like this: public static class DicomPrivateTagDictionary { static DicomTagDictionary(); public static IList GetDicomPrivateTagList() public static DicomTag GetDicomPrivateTag(string creatorCode, ushort group, byte elementByte) public static void AddPrivateTag(DicomTag tag) public static void AddPrivateTagList(IEnumerable list) } At init time, you would add the private tags individually or as a list. Steve |
|
| Real-time support available to Clinical Edition and Team Edition customers |
|
|
dblanchard
 Senior Member Posts:185
 |
| 2008-06-19 11:16 AM |
|
Steve,
I think we would need a few other methods to retrieve private tags, or would this be available via methods in the standard dictionary? ie,
public static DicomTag GetDicomTag(uint tag, bool checkDicomStandardTags); public static DicomTag GetDicomTag(string name, bool checkDicomStandardTags);
note, the checkDicomStandardTags param may not be necessary, I just had it my private dictionary for convenience.
Regards,
Dan
|
|
|
|
|
jasper.yeh
 Senior Member Posts:523

 |
| 2008-06-19 02:12 PM |
|
Hi Guys,
Attached is my solution for supporting private tags in my application, abstracted to be extensible for general purposes.
The abstract class DicomPrivateTag is extended to define private tags using only a byte. The abstract class DicomPrivateImplementor is extended to define the creator code, the private group, and the list of DicomPrivateTags alllowed. To access private tags in a dataset, the GetPrivateAttributes method of an instance of the implementor is called, passing in the source dataset. The return value is a DicomPrivateAttributeCollection, which is similar to DicomAttributeCollection except it is indexed on DicomPrivateTags. The private collection is only a wrapper, so changes in either collection are reflected in the other.
There is sample code in the included help files.
What do you think?
Edit 2011-08-04: Removed attachment because I no longer hold rights to distribute and relicense it. |
|
| // Jasper |
|
|
echan
 Basic Member Posts:28
 |
| 2008-08-12 04:13 PM |
|
Hi Steve,
I also need to handle private tags. At the point where the reader encounters the private tag for creator code, how do I extract the creator code from the message?
Thanks in advance,
Eleanor |
|
|
|
|
steve
 Senior Member Posts:1932
 |
| 2008-08-12 04:37 PM |
|
Eleanor, With the current implementation, the best way to access private tags in a message would be to use the iterator associated with the DicomAttributeCollection to step through all the tags in the message until you find the private creator code you are looking for. From that you should be able to navigate to the actual private tags you're looking for... Steve |
|
| Real-time support available to Clinical Edition and Team Edition customers |
|
|
jasper.yeh
 Senior Member Posts:523

 |
| 2008-08-12 04:55 PM |
|
Hi Eleanor, If you just need to read the data in a private tag creator code attribute, then just access the dataset like with any other tag and then call a GetString(0, "") or ToString() on the attribute. So, something like this: DicomFile dcf = ...; string creatorCode = dcf.DataSet[(uint)0x07570010].GetString(0, ""); //0x07570010 is the creator code tag |
|
| // Jasper |
|
|
echan
 Basic Member Posts:28
 |
| 2008-08-12 05:43 PM |
|
Actually, I'm following Steve's recommendation in the second reply and trying to make changes in StreamReader.
I can get it from the DicomAttributeCollection now.
Thanks,
Eleanor |
|
|
|
|
dblanchard
 Senior Member Posts:185
 |
| 2008-08-12 09:50 PM |
|
Hi
You are welcome to use my private dictionary as a sample. See attached file.
Here is an example how to use it:
// this sets my PatientUid private tag in the dicom file's dataset dicomFile.DataSet[PrivateDictionary.GetPrivateDicomTag(PrivateTag.PatientUid)]. SetStringValue(myPatientUid);
I also have a helper function to get either my private tag or the standard dicom tag:
GetDicomTag(uint tag, bool checkDicomStandardTags);
hope this helps.
dan
|
Attachment: PrivateDictionary.cs
|
|
|
|
echan
 Basic Member Posts:28
 |
| 2008-08-13 10:30 AM |
|
Thanks Dan for the code sample.
Eleanor |
|
|
|
|
awei
 New Member Posts:2
 |
| 2011-03-21 01:00 PM |
|
Hi All, Now is 2011. Do we have any new easy solution to create private tags? Thanks. Anthony |
|
|
|
|
mtl
 Basic Member Posts:21
 |
| 2011-03-21 02:44 PM |
|
Anthony,
I haven't yet had a need to create new private tags, but have been successful reading private tag data from Signa MR images using the RadFiler.Dicom.PrivateTags code posted by Jasper earlier in this discussion thread. I expect it shouldn't be too difficult to write private tags instead of reading them.
-Matt |
|
|
|
|
awei
 New Member Posts:2
 |
| 2011-03-21 09:23 PM |
|
Hi Matt, Thanks for your response. Actually I want to add some private tags into a DicomFile instance and those tags need to be saved after DicomFile.Save(). I had "Invalid tag: xxxx" problem when using DicomFile.DataSet[privateTag].SetInt16(), because the private tag does not exist in DicomTagDictionary. I have figured out how to add a private tag. It is just 4 lines code: DicomTag tag = new DicomTag(privateTag, ..); DicomAttribute dicomAttribute = tag.CreateDicomAttribute(); dicomFile.DataSet[privateTag] = dicomAttribute.Copy(); dicomFile.DataSet[privateTag].SetInt16(); -Anthony
|
|
|
|
|
mtl
 Basic Member Posts:21
 |
| 2011-08-03 04:04 PM |
|
Jasper,
Is the code included in your RadFiler.Dicom.PrivateTags zip archive file provided for use and redistribution under the ClearCanvas license and/or the Open Software License v3.0 as described in http://www.clearcanvas.ca/dnn/About...fault.aspx? (I presume that was likely your intent, but I'd like to explicitly receive your permission before redistributing your work.)
-Matt |
|
|
|
|
jasper.yeh
 Senior Member Posts:523

 |
| 2011-08-04 05:45 PM |
|
Hi Matt, Sorry, I actually had to remove that code now. It was written before I joined ClearCanvas, and its ownership remains with my employer at the time. I conferred with the current rights holder, who declined to authorize any further use of the code. It was the intent at the time that the code be integrated into the framework as a third party contribution, and thus become available under the ClearCanvas license (and the subsequent OSLv3 license). However, that did not happen and now the rights holder has clarified the licensing terms. Aside: I probably wouldn't design a generic private tag mechanism like that anymore, because it requires you to subclass too many things, resulting in a bunch of new DicomPrivateAttributeCollection instances that duplicate normal DicomAttributeCollection. A better design would be for you as the private tag creator to create a specialized lightweight wrapper for a DicomAttributeCollection with properties of the appropriate types, and the wrapper would handle creating the DicomTag objects and actually reading and writing to the dataset. This is how the current IOD implementations work (see the Iod/Modules folder in the DICOM project) - they're all just wrappers that provide a specialized view onto the dataset. You wouldn't have any duplicated functionality anywhere - simply create a new wrapper when needed. Hope this helps, |
|
| // Jasper |
|
|