Thursday, August 27, 2020

Sending DICOM files indexed in a DICOMDIR on a DICOM CD to PACS using PowerShell and MODALIZER-SDK

 I'm posting this PowerShell script in response to a question I got about sending DICOM files on a CD to PACS. 

This functionality is available in MODALIZER+ from the View menu simply by right clicking the Study item at the top study panel and selecting "Send to". The PACS AE title has to be configured first in the settings.

But, for the automation enthusiasts, here's a PowerShell script that do the same. Recalling on DICOMDIR, a DICOM CD or DVD has a file called DICOMDIR in the root path that references all DICOM files on the media. This script iterates through the DICOMDIR records to collect all referenced files, then it send them using DCXREQ.Send command to the PACS.

Here it is. Enjoy!

 # Send DICOMDIR Patients to PACS


param($rootFolder);

Wednesday, January 8, 2020

Indexing DICOM files in a directory into a CSV file using PowerShell and MODALIZER-SDK

This post is continues my series on PowerShell DICOM scripting. It covers a very common task that repeats itself in all kind of forms where I have a directory full of DICOM files that piled up somewhere and needs to be indexed and processed. PowerShell scripting comes handy in these cases where there's a need to do something fast and probably change it on the fly. Of course, you will need our MODALIZER-SDK for this one.

#!PowerShell ... jeje ... this doesn't work on widows ;-) 

# Well, this is something very useful (I think) for anyone managing a PACS.
# Lets say you have a directory full with DICOM files you have no idea what's in them (you do, right?) and you want to scan through them.
# So, here's a little powershell script for this:

# Ok, so first we get the directory to scan as a command line parameter
param ($ScanPath)

# This is a function. It extract one element from a DICOM object and return an empty string if the element doesn't exist or doesn't have a value. We use it later on.
function Get-Value($obj, $tag
{
  $e = $obj.GetElement($tag)
  if ($e -ne $null)
  {
    if ($e.Value -ne $null)
    { 
      return $e.Value
    }
    else
    {
      return ""
    }
  }
  else
  {
    return "";
  }
}

# Modify this array to add more tags. The pair items are used for headers (and readabity).
$tags = @(
    0x00080016, "sop class uid",
    0x00080018, "sop instance uid",
    0x00080020, "study date",
    0x00080030, "study time",
    0x00080050, "accession number",
    0x00100010, "patient name",
    0x00100020, "patient id",
    0x00100030, "patient birth date",
    0x00100040, "patient sex",
    0x0020000d, "study Instance UID",
    0x0020000e, "$series Instance UID")

# This prints a nice CSV header line. 
# Note all the '","' to enclose values in double quotes and separate them with commas. 
function Print-Header
{
    $line = '"Filename","Status'
    for (($i=1); ($i -lt $tags.Count); ($i+=2))
    {
        $line += '","'
        $line += $tags[$i]
    }
    $line += '"'
    $line
}

# This is where the element values are extracted and one line is returned. 
# The filename is in the first column and a status is in the second. Then the values from the tags array follow.
# The status  can be "OK" or "ERROR". You will also see errors in stderr.
function Parse-DicomFile($filename)
{
    $line = '"'
    $line += $filename
    $line += '","OK'
    try {
        $obj = New-Object -ComObject rzdcx.DCXOBJ
        $obj.openFile($filename)
        for (($i=0); ($i -lt $tags.Count); ($i+=2))
        {
            $line += '","'
            $line += Get-Value $obj $tags[$i]
        }
        $line+='"'
    } catch { $line = '"' + $filename+ '","ERROR"' }
    return $line
}

# Here it starts. We first print the header and then scan through the folder and extract the data from every DICOM file. 
# Note ignoring directories
Print-Header
Get-ChildItem -Recurse -Path $ScanPath | Foreach-Object {    
    if ($_.Attributes -ne "Directory") {
        Parse-DicomFile $_.FullName
    }

# Enjoy!!!



Wednesday, May 8, 2019

More PowerShell DICOM

# PowerShell DICOM

# This little script becomes handy for example for a daily test of your PACS
# It generates a 1000x1000 image with date and key attributes (Study, Series and Instance UID) burned in
# This script uses HRZ's RZDCX.DLL.

# Create DICOM Object
$DCM = New-Object -com rzdcx.DCXOBJ

# UID Generator
$UID = New-Object -com rzdcx.DCXUID

# Create new Key UID's
$studyInstanceUID = $UID.CreateUID(1)
$seriesInstanceUID = $UID.CreateUID(2)
$sopInstanceUID = $UID.CreateUID(3)

# Add minimal elements to the dataset
$tagValues = @(
0x00080016, "1.2.840.10008.5.1.4.1.1.7", 
0x00080018, $sopInstanceUID,
0x00080020, ($studyDate = Get-Date -UFormat "%Y%m%d"),
0x00080030, "",
0x00080050, "ACCNUMHRZTEST",
0x00080060, "SC",
0x00080064, "WSD",
0x00080090, "HRZ",
0x00100010, "HRZ^TEST",
0x00100020, "HRZTEST",
0x00100030, "20100101",
0x00100040, "O",
0x0020000d, $studyInstanceUID,
0x0020000e, $seriesInstanceUID,
0x00200010, "123",
0x00200011, "1",
0x00200013, "1")

$E = New-Object -com rzdcx.DCXELM
for (($i=0); ($i -lt $tagValues.Count); ($i+=2))
{
    $E.Init($tagValues[$i])
    $E.Value = $tagValues[$i+1]
    $DCM.insertElement($E)
}

# Create 1000x1000 Image with text printed on it
$rows = 1000
$cols = 1000

# jpeg image
Add-Type -AssemblyName System.Drawing
$imageFormat = "System.Drawing.Imaging.ImageFormat" -as [type]
$format = $imageFormat::jpeg


$uidType = "rzdcx.UID_TYPE" -as [type]

$createDate = Get-Date
$bmp = new-object System.Drawing.Bitmap $cols,$rows
$font = new-object System.Drawing.Font Consolas,24 
$brushBg = [System.Drawing.Brushes]::Yellow 
$brushFg = [System.Drawing.Brushes]::Black 
$graphics = [System.Drawing.Graphics]::FromImage($bmp
$graphics.FillRectangle($brushBg,0,0,$bmp.Width,$bmp.Height) 
$graphics.DrawString($createDate, $font,$brushFg,10,10
$font = new-object System.Drawing.Font Consolas,8 
$graphics.DrawString("StudyInstance UID: " + $studyInstanceUID, $font,$brushFg, 10,110)  
$graphics.DrawString("Series Instance UID: " + $studyInstanceUID, $font,$brushFg,10,210)  
$graphics.DrawString("SOP Instance UID: " + $studyInstanceUID, $font,$brushFg,10,310)  
$graphics.DrawString("TARGET AET: " + "PACS-A", $font, $brushFg, 10, 410)  

$graphics.Dispose() 

# Save the JPEG image
$filename = "$home\HRZTEST.jpg"
$bmp.Save($filename, $format

# Insert it to the DICOM object
$DCM.SetJpegFrames($filename);

# Save the DICOM file
$dcmfile = $filename + ".dcm"
$DCM.saveFile($dcmfile)

# Send it to the PACS
$REQ = New-Object -com rzdcx.DCXREQ

$succeeded = ""
$failed = ""

# Start logging for network comunication and save to log file

$APP = New-Object -com rzdcx.DCXAPP
$APP.LogLevel = 5 # Debug

$logFile = "$home\SendTestImage.log"

$APP.StartLogging($logFile)

$REQ.Echo("HRZ-TEST", "PACS-A", "localhost", 104)
$REQ.Send("HRZ-TEST", "PACS-A", "localhost", 104, $dcmfile, [ref] $succeeded, [ref] $failed)

# Print results
$filename
$dcmfile
"Succeeded: " + $succeeded
"Failed: " + $failed

# Stop logging
$App.StopLogging()

"Done!"






Tuesday, January 15, 2019

PowerShell DICOM Scripting

Disclosure: This post is purely technical!
Assumption: you know your way around PowerShell or how to get there and a bit of DICOM and our RZDCX API.

If your system is x64 (probably) than make sure to regsvr32 radix.dll the x64 version.

Let's PowerShell!!!

# download rzdcx
Invoke-WebRequest -Uri http://downloads.roniza.com/rzdcx/2.0.8.7/RZDCX_2087.zip -OutFile ./rzdcx.zip

# unzip it
Expand-Archive ./rzdcx.zip -DestinationPath ./rzdcx

# regsvr32 win32 version
$rzdcx32 = Resolve-Path .\rzdcx\win32\rzdcx.dll
Start-Process regsvr32 -verb runAs -argumentlist $rzdcx32

$rzdcx64 = Resolve-Path .\rzdcx\x64\rzdcx.dll
Start-Process regsvr32 -verb runAs -argumentlist $rzdcx64

# Create DICOM Object
$DCM = New-Object -com rzdcx.DCXOBJ

# I assume you have a directory called DICOM with DICOM files in it
$files = @(Get-ChildItem ".\DICOM\*")

# Write headers
"filename, Patient Name, Patient ID"

# For each file extract and print patient name and patient id
foreach ($file in $files) {
  $DCM.openFile($file)
  $patname = $DCM.GetElement([int32]::Parse(00100010,'HexNumber')).Value
  $patid   = $DCM.GetElement([int32]::Parse(00100020,'HexNumber')).Value
  Write-Host $file "," $patname "," $patid
}

Try it!


Thursday, November 1, 2018

HL7Kit 2018 - Integrated DICOM and HL7 Solution

Yo! It's been a while ... no posts since June 2016 :-§

Maybe its because when you do there's less time to talk, ah? Anyway, I finally got some time for it and that's because HRZ have finally officially released HL7Kit 2018. Yeah! Check this beautiful little web site we've published for the occasion. Let me tell you about this product that my team and I worked so hard to push through the door.

For those of you that worked with the kit before, the first thing you will notice is the new UX. We've invested in usability and graphic design to make the kit nicer and simpler to use.

HL7Kit desktop applications shortcuts and the control panel


Another improvement is that now the installation and evaluation is much simpler because after installation (which is just a series of next-next-nexts) everything works with an internal SQLite database and by everything I mean everything! DICOM Storage, Q/R, Modality Worklist MPPS, and HL7 Orders ORM^O01 and Schedules SIU^S12 that are automatically mapped to Modality Worklist entries. Apart for the fact that it makes evaluation a snap, it also makes it really attractive for

Saturday, June 25, 2016

Reflection

Back in the old days, C++ templates were the highest level of code abstraction for me. I loved creating template classes. On the other hand, Reflection, when I first learned about it, it didn't raise such an emotional feeling in me. It seamed like a complicated way to do simple things. About a year ago I was lucky to work with John Volkar from Bayer Healthcare. His project used Reflection to transform between C# data members and DICOM data elements. Recently, while working on another project, this technique came in handy. It's a nice example of how to use the Attribute class to link between a data member and a DICOM tag. Here it is. Thanks John.

First, how to you use it. It's very simple. Use the Tag attribute above the class member like this:

[Tag(0x0010,0x0010)]
public string patient_name;


This just mapped the patient_name member to a DICOM Patient Name data element by using the tag (0010,0010).

Sunday, January 24, 2016

A ready-made DICOM Software that fits your needs - MODALIZER!

Its here, its ready, its published, MODALIZER, Take it for a ride! This DICOM Application is really a game changer! We took our DICOM Toolkit, combined it with our examples, mixed in our stand alone software and created the MODALIZER. Its an amazing software waiting for your imaging device to turn it into something big.

Lets say you're developing a new imaging technology and now you want to integrate it into the PACS environment. Take MODALIZER, configure your image capture application as the EXAP and that's it! MODALIZER takes care of all the rest.

Look at this DIAGRAM:
MODALIZER is a stand-alone DICOM application that implements an Imaging Modality according to the IHE Scheduled Workflow (SWF) Technical Framework. MODALIZER provides all the UI and the DICOM integration. The imaging part is performed by your technology that MODALIZER launches to the front after collection the patient and study details. Your image capture program acquire the images and then gives back the control to MODALIZER to complete the study. So simple. 
And it comes with DICOM Conformance Statement and even more you can license its source code! Check it our on our web site.