查找

Article
· May 2 3m read

Creating a DICOM file and adding a JPG to it

One of the challenges of creating a DICOM message is how to implement putting data in the correct place. Part of it is by inserting the data in the specific DICOM tags, while the other is to insert binary data such as a picture - In this article I will explain both.

To create a DICOM message, you can either use the  EnsLib.DICOM.File class (to create a DICOM file) or the  EnsLib.DICOM.Document class (to create a message that can be sent to PACS directly). In either case, the SetValueAt method will allow you to add your data to the DICOM tags.

A DICOM message consists of two constituent parts, CommandSet and the DataSet.
The CommandSet contains DICOM elements which contain details about the characteristics of the DataSet, while the DataSet contains the data itself - patient's demographic, image etc.

To update the tags in the CommandSet or the DataSet, simply state the value and the name of the property you wish to update using the SetValueAt method:

set tstatus=tDoc.SetValueAt("1.2.840.10008.5.1.4.1.1.7","CommandSet.MediaStorageSOPClassUID")
set tstatus=tDoc.SetValueAt("1.2.392.200059.1.11.11084587.3.35820032317.2.1.56","CommandSet.MediaStorageSOPInstanceUID") 
set tstatus=tDoc.SetValueAt("1.2.276.0.7230010.3.0.3.6.4","CommandSet.ImplementationClassUID") 
set tstatus=tDoc.SetValueAt("OFFIS_DCMTK_364","CommandSet.ImplementationVersionName") 
set tstatus=tDoc.SetValueAt("Morgan^Gina^G","DataSet.PatientName") 
set tstatus=tDoc.SetValueAt("2751","DataSet.PatientID")
set tstatus=tDoc.SetValueAt("19810816","DataSet.PatientBirthDate")	
set tstatus=tDoc.SetValueAt("F","DataSet.PatientSex") 
you can either use the property name or the property tag. For example, those 2 commands are updating the same tag:
	set tstatus=tDoc.SetValueAt("Olympus","DataSet.Manufacturer")		
	set tstatus=tDoc.SetValueAt("Olympus","DataSet.(0008,0070)") 

Once the message is created and transferred to PACS as a document, you can see its data as part of the trace (note that binary data cannot be seen):

In order to add the binary data for the image, it is more complicated that just putting the data in a specific tag, because it needs to be structured in a specific way and measured appropriately. This is why after updating the tags and saving the document, we need to open it as a simple binary file and add the image data at the end of it in a specific manner.

The image is part of the PixelData property in tag (7FE0,0010).

This tag is a sequence - DICOM allows a DataSet to contain other nested DataSets, which are encoded as “sequences”. The point of this structure is to allow repeating groups of data, so whilst such sequences often only contain a single DataSet, the format is defined such that each sequence consists of a set of DataSets.

This structure can be used in recursion, and some DICOM scenarios might use sequences nested 5 or 6 deep.

 

The demo shows a sample of creating a DICOM document with an image in it. The patient's demographic and other details are just for the sake of teh sample. To run this demo, simply put a JPG file in a directory, configure the directory name in the 'FileStorageDirectory' property in the business operation's settings:

 

and run the Business Process. After its completion, you'll see a new dcm file in the same directory where your JPG file was. open it in a DICOM viewer and you'll see the DICOM tags as well as the image in it:

 

Here is a quick demo showing the whole process:

Look for the demo files and instruction in Open Exchange:

https://openexchange.intersystems.com/package/DICOM--Image-Demo

Keren.

Discussion (0)0
Log in or sign up to continue
Discussion
· May 2

Vamos discutir a nova IU para Interoperabilidade e editor de Transformações de Dados

Já faz um tempo desde que a nova interface de usuário para Produções e DTL foi publicada como uma prévia e eu gostaria de saber suas opiniões sobre ela.

AVISO: Esta é uma opinião pessoal, totalmente pessoal e não relacionada com a InterSystems Corporation.

Vou começar com a tela de Interoperabilidade:

Tela de Produção:

O estilo é sóbrio e sem adornos, seguindo a linha do design de serviços de nuvem, eu gosto.

Mas, sempre um mas... ou talvez dois:

Na minha opinião, há informação demais, o menu esquerdo é supérfluo. É verdade que você pode recolhê-lo, mas não quero fazer isso cada vez que uso a tela. Não preciso ver o tempo todo todas as produções no meu NAMESPACE, os Itens de Produção, Conjuntos de Regras e Transformações de Dados. Sinto que os designers sofreram um "horror vacui"

Sobre esses menus, parece que as opções estão muito próximas:

E exibir os editores de Regras e Transformações de Dados na mesma tela para telas pequenas como a de um laptop é um pesadelo de rolagem:

Uma janela pop-up com os editores seria mais "limpa" para o usuário comum. Um ponto positivo é que podemos selecionar como queremos abrir os editores.

Mas talvez tenhamos opções demais.

Editor de DTL:

Bem, eu gosto, o design é simples e claro, talvez, como na tela de Produção, as linhas estejam muito próximas e perdemos o arrastar e soltar para ligar os campos.

Conclusão:

O design foi modernizado e parece agradável, mas na minha opinião como um ex-desenvolvedor web "menos é mais". Eu gostaria de trabalhar com telas mais simples, com um comportamento bem definido, não preciso acessar todas as funcionalidades de interoperabilidade na mesma tela.

Minha modesta opinião, precisamos equilibrar funcionalidades e uma interface de usuário amigável e moderna, o novo design parece ir nessa direção. Obrigado a toda a equipe envolvida no desenvolvimento!

[OBS.: o texto reflete a opinião pessoal do AUTOR, não do TRADUTOR.]

Discussion (0)1
Log in or sign up to continue
Article
· May 2 3m read

Minify XML in IRIS

In a project I'm working on we need to store some arbitrary XML in the database. This XML does not have any corresponding class in IRIS, we just need to store it as a string (it's relatively small and can fit in a string).
Since there are MANY (millions!) of records in the database I decided to reduce as much as possible the size without compressing. I know that some XML to be stored is indented, some not, it varies.
To reduce the size I decided to minify  the XML, but how do I minify an XML document in IRIS?
I searched across all the classes/utilities and I could not find a ready made code/method, so I had to implement it and it turned out to be fairly simple in IRIS using %XML.TextReader class, frankly simpler than I expected.

Since this can be useful in some other context, I decided to share this little utility with the Developer Community.
I've tested with some fairly complex XML documents and works fine, here is the code.

/// Minify an XML document passed in the XmlIn Stream, the minified XML is returned in XmlOut Stream
/// If XmlOut Stream is passed, then the minified XML is stored in the passed Stream, otherwise a %Stream.TmpCharacter in returned in XmlOut.
/// Collapse = 1 (default), empty elements are collapsed, e.g. <tag></tag> is returned as <tag/>
/// ExcludeComments = 1 (default), comments are not returned in the minified XML
ClassMethod MinifyXML(XmlIn As %Stream, ByRef XmlOut As %Stream = "", Collapse As %Boolean = 1, ExcludeComments As %Boolean = 1) As %Status
{
	#Include %occSAX
	Set sc=$$$OK
	Try {
		Set Mask=$$$SAXSTARTELEMENT+$$$SAXENDELEMENT+$$$SAXCHARACTERS+$$$SAXCOMMENT
		Set sc=##class(%XML.TextReader).ParseStream(XmlIn,.reader,,$$$SAXNOVALIDATION,Mask)
		#dim reader as %XML.TextReader
		If $$$ISERR(sc) Quit
		If '$IsObject(XmlOut) {
			Set XmlOut=##class(%Stream.TmpCharacter).%New()
		}
		While reader.Read() {
			Set type=reader.NodeType
			If ((type="error")||(type="fatalerror")) {
				Set sc=$$$ERROR($$$GeneralError,"Error loading XML "_type_"-"_reader.Value)
				Quit
			}
			If type="element" {
				Do XmlOut.Write("<"_reader.Name)
				If Collapse && reader.IsEmptyElement {
					; collapse empty element
					Do XmlOut.Write("/>")
					Set ElementEnded=1
				} Else {
					; add attributes
					For k=1:1:reader.AttributeCount {
						Do reader.MoveToAttributeIndex(k)
						Do XmlOut.Write(" "_reader.Name_"="""_reader.Value_"""")
					}
					Do XmlOut.Write(">")
				}
			} ElseIf type="chars" {
				Set val=reader.Value
				Do XmlOut.Write($select((val["<")||(val[">")||(val["&"):"<![CDATA["_$replace(val,"]]>","]]]]><![CDATA[>")_"]]>",1:val))
			} ElseIf type="endelement" {
				If $g(ElementEnded) {
					; ended by collapsing
					Set ElementEnded=0
				} Else {
					Do XmlOut.Write("</"_reader.Name_">")
				}
			} ElseIf 'ExcludeComments && (type="comment") {
				Do XmlOut.Write("<!--"_reader.Value_"-->")
			}
		}
	} Catch CatchError {
		#dim CatchError as %Exception.SystemException
		Set sc=CatchError.AsStatus()
	}
	Quit sc
}

P.S.: anyone know if there is other/simpler way to minify XML in IRIS?

3 Comments
Discussion (3)1
Log in or sign up to continue
Question
· May 2

I am getting date as 00010101 in CCDA if we use $ZDATEH function it is giving value out of range error

Can anyone please help me which function i can use to handle this error.

5 Comments
Discussion (5)3
Log in or sign up to continue
Article
· May 2 3m read

Mini-astuce du jour : Préchargement de la licence dans l'image Docker IRIS

Qui n'a jamais développé un bel exemple avec une image IRIS Docker et vu la génération de l'image échouer dans le Dockerfile parce que la licence sous laquelle l'image a été créée ne comportait pas certains privilèges ?

Dans mon cas, je déployais dans Docker une petite application utilisant le type de données Vector. Avec la version Community, ce n'est pas un problème, car elle inclut déjà la recherche et le stockage vectoriels. Cependant, lorsque j'ai remplacé l'image IRIS par une image IRIS classique (latest-cd), j'ai constaté que la compilation de l'image, y compris des classes générées, renvoyait l'erreur suivante :

9.505 ERROR #15806: Vector Search not permitted with current license
9.505   > ERROR #5030: An error occurred while compiling class 'Inquisidor.Object.LicitacionOS'
9.505 Compiling class Inquisidor.Object.Licitacion
9.505 ERROR #15806: Vector Search not permitted with current license
9.505   > ERROR #5030: An error occurred while compiling class 'Inquisidor.Object.Licitacion'
9.538 Compiling class Inquisidor.Message.LicitacionResponse

Cette erreur m'a laissé perplexe, car moi, en tant que personne obéissante, j'avais défini dans mon docker-compose.yml le paramètre qui indique où se trouve ma licence valide :

  iris:
    init: true
    container_name: iris
    build:
      context: .
      dockerfile: iris/Dockerfile
    ports:
      - 52774:52773
      - 51774:1972
    volumes:
    - ./iris/shared:/iris-shared
    environment:
    - ISC_DATA_DIRECTORY=/iris-shared/durable
    command: --check-caps false --ISCAgent false --key /iris-shared/iris.key

Il m'a fallu un certain temps pour comprendre que le problème venait de l'image originale que j'utilisais, et non de la licence. Comme vous pouvez le constater, je ne suis pas très doué en la matière.

Le problème est survenu au moment où j'ai importé mes classes dans l'image IRIS par défaut :

RUN \
zn "%SYS" \
do ##class(SYS.Container).QuiesceForBundling() \
do ##class(Security.Users).UnExpireUserPasswords("*") \
set sc=##class(%SYSTEM.OBJ).Load("/opt/irisapp/DemoSetup.Utilities.cls","ck") \
set helper=##class(DemoSetup.Utilities).%New() \ 
do helper.EnableSSLSuperServer() \
do ##class(Security.Applications).Import("/ApplicationInquisidor.xml",.n) \
zn "INQUISIDOR" \
set sc = $SYSTEM.OBJ.LoadDir("/opt/irisapp/src/Inquisidor", "ck", , 1) \
set production = "Inquisidor.Production" \
set ^Ens.Configuration("csp","LastProduction") = production \
do ##class(Ens.Director).SetAutoStart(production) \

La compilation du code renvoyait l'erreur précédente. Que faire pour la corriger ? C'était très simple : j'ai dû envoyer la nouvelle licence à l'image IRIS initiale et lui demander de la mettre à jour dès la première ligne des commandes que j'utilisais.

La première étape consiste à déplacer la nouvelle licence vers le répertoire /mgr de l'installation, ce que j'ai fait avec ce code :

COPY --chown=$ISC_PACKAGE_MGRUSER:$ISC_PACKAGE_IRISGROUP /iris/iris.key /usr/irissys/mgr
RUN chmod +x /usr/irissys/mgr/iris.key

Le chemin d'installation d'IRIS sur notre image est /usr/irissys/mgr, et le chemin /iris/iris.key correspond à mon répertoire local. Avec la licence dans l'image IRIS, il me suffisait de demander à IRIS de la mettre à jour. J'ai donc modifié les commandes précédentes en ajoutant l'instruction suivante :

RUN \
zn "%SYS" \
do ##class(%SYSTEM.License).Upgrade() \

Et voilà ! J'ai maintenant mon image IRIS avec ma licence chargée avant l'importation et la compilation de mes classes. Plus d'erreurs de compilation.

J'espère que cela vous sera utile !

Discussion (0)1
Log in or sign up to continue