This article will cover writing a storage server that is able to receive images, store them on your hard drive, create thumbnails of the images, parse out important header information, and store that header information in a database.

In my continuing effort to show off how handy my PHP DICOM class and the OFFIS DCMTK are, I’ve decided to write a series of articles on how to implement a PACS in PHP.

These articles will be focused on Linux, though I may amend them in the future to cover Windows. They will assume that you have some knowledge of PHP, mySQL, and the Linux command line.

You will need my PHP DICOM class installed. You will also need mySQL installed and have access to add users and databases.

One of the things I dislike about programming books is that they’ll spend pages upon pages showing snippets of code and explaining everything line by line. I’m not going to use that approach, I’m going to walk you through getting the example code running on your computer, testing it, and explaining how it works in a high level fashion. I tried to write the code plainly, simply, and use verbose comments through out explaining what is going on.

Lets get the sample code up and running!

Step 1:

tar xvf PACS_Tutorial.tar.gz
cd PACS_Tutorial/part1
chmod 755 import.php store_server.php send_test_image.php
chmod -R 777 received_images temp

Download all of the sample code and set permissions.

Step 2:

mysql -uroot -p < db.sql

We need to create a database and user for our PACS. The file db.sql will create a user, ‘pacs’, with password ‘pacspassword’, and grant it all privileges to a new database called ‘pacs’. It will then create two tables, studies and images.

If at all possible, don’t change any of this, it’ll make the tutorial a lot less of a headache. If you do change it, open up import.php, and look for mysql_connect. Change the values there as needed.

Step 3:


With that command we’ve launched our storage server on port 1104. Notice that the script didn’t launch in the background? Don’t worry, I’ll cover that later on.

If you don’t want your server running on port 1104, edit store_server.php and its easy enough to change.

The storage server as is doesn’t care about AE titles, its promiscuous and will accept images from anyone.

Step 4:

Since our storage server is using our terminal, leave it running and open a new terminal for the next steps.

store_server.php is going to be receiving your images, after an image is received it will run another script, import.php. import.php will be responsible for moving the image to a safe place on the hard drive, parsing the image’s header information, placing that information into a database, and creating a JPEG thumbnail of the image for later use.

You can test to see if all of this works by sending an image to your computer’s IP address using port 1104. You can make something up for the AE title.

If you don’t have a way to send yourself images, I’ve included a small script that will send your new server one image.


That will send dean.dcm from class_dicom.php’s examples over to your new server.

Once you have sent an image you can switch back to your terminal running store_server.php and see the log entries regarding the image transmission. Among many things the log will show you where the image was saved.

Step 5:

mysql -uroot -p < view_db.sql

Let’s take a look at the database. If all went well the command above should show you the contents of the database; information about the images sent to the server.

There are two tables.

The first table is studies. This table contains information obtained from the image pertaining to the study. Each study can have many images, in example, a chest study could have both a PA and lateral view; one study, two images.

The second table is images. This table contains information specific to an image.

You’ll see there isn’t a field for storing the file name of the image, this is because the file name is easily inferred from database information.

Take a look at this snippet from import.php:

$store_dir = "./received_images/$sent_from_ae/" . $img['year'] . "/" . $img['month'] . "/" . $img['day'] . "/" .

The image is stored in a directory based on the AE title it came from, the appointment date, and the study uid. The image itself uses the SOP instance plus ‘.dcm’ for its file name. Since all of this information is in the database, we can find the file. Later tutorials will go into more detail on this.

Step 6:

Right now, the storage server is stuck running in its own terminal window, if you close the window the storage server stops running, not cool. You’ll want to get it running in the background and you’ll want it to start at boot.

This gets tricky as different flavors of Linux, and even different versions of those flavors, handle this differently. I’ll discuss the three most common approaches. If you have not done so already, control-c in the storage_server.php terminal to stop it.

CentOS 5.6+, Fedora, Red Hat, Ubuntu:

You should have a /etc/init directory. You can place script in this directory that init will use to start/stop and keep running your programs.

start on runlevel [3]
stop on runlevel [!$RUNLEVEL]
console output
exec /home/dean/PACS_Tutorial/part1/store_server.php

Create /init/store_server.conf and add all of that. Be sure to change the path to store_server.php to match your own setup.

‘initctl start store_server’ to start your server


If you don’t have a /etc/init directory, you can always fall back to editing the inittab itself.


Edit /etc/inittab and add this line to the bottom of the file. Be sure to change the path so it points to your copy of store_server.php. Once you’re back at the command line, type ‘init q’. This will reload the inittab and start up store_server.php.

All else fails:

nohup store_server.php

nohup will start store_server.php in the background for you. It will not restart the process if it crashes as the methods above will.

The End?

Now you have a multi-threaded DICOM storage server capable of receiving thousands of studies worth of images everyday. The information it receives is placed into a database for easy access by other programs.

Stay tuned for part two where in I’ll show you how to write a user interface to your PACS.

I have an XMBC Live (XBMCbuntu) set up at home that includes a Denon 5.1 surround sound receiver. Out of the box XBMC will play the wrong sound channel out of the wrong speaker. Most noticeable is that the center channel will come out of the rear left speaker.

Finding a solution for this proved difficult, it’s not an XBMC problem so much as a problem with Alsa. The solution below has worked with XBMC 9, 10, and 11.

Here is what you do:

ssh into the XBMC box and create a /etc/asound.conf file. Cut and paste the text below into it.

pcm.!hdmi-remap {
  type asym
  playback.pcm {
    type plug
    slave.pcm "remap-surround71"

pcm.!remap-surround71 {
  type route
  slave.pcm "hw:0,3"
  ttable {
    0.0= 1
    1.1= 1
    2.4= 1
    3.5= 1
    4.2= 1
    5.3= 1
    6.6= 1
    7.7= 1

Then reboot.

Once XBMC comes back up, go to System->Audio and set the audio output to hdmi-remap. Then play a video with 5.1 sound, it should all work out.

Torrents are not safe. Your IP address is visible to all. Eventually you will be caught and copyright infringement notices will be sent to your ISP. Using an Usenet Provider will not only protect you but allow you to download faster. I recommend NewsgroupDirect.

Here’s the setup:

uTorrent is running on a Windows 7 machine. uTorrent keeps incomplete files in X:\torrent_scratch. It moves completed files to X:\SHARED. X:\SHARED is shared off to the network for everyone and has it’s permissions set to full control by everyone. XBMC on another PC has X:\SHARED mapped so it can play videos out of it.

Here’s the problem:

When you try to play a video from the share in XBMC, it asks for a password. Problem is, there is no password set  to use. The Windows 7 PC doesn’t have users with a password. The share is shared with ‘Everyone’, making the need for a password irrelevant anyway.

When you check the permissions on your shared folder, they are set correctly. The files/folders uTorrent is moving into the shared folder do not have the correct permissions.

The Solution:

Since uTorrent is moving the files into X:\SHARED you would assume the files would inherit the permission from that folder. Unfortunately it does not, it inherits permissions from the folder they were created in, X:\torrent_scratch.

The solution is to give X:\torrent_scratch the permission you want your files in X:\SHARED to have, in this case, full control by everyone.

Torrents are not safe. Your IP address is visible to all. Eventually you will be caught and copyright infringement notices will be sent to your ISP. Using an Usenet Provider will not only protect you but allow you to download faster. I recommend NewsgroupDirect.


I develop primarily on Linux and wrote class_dicom.php with Linux in mind. This tutorial will be geared towards CentOS or Fedora as that is what I use on a day to day basis. I’ll try to include notes for Ubuntu/Debian when possible.

I’m going to assume you have at minimum, Linux installed on a PC and you can access the command line as root.

1. Let’s get your environment ready

We’re going to install all of the needed development tools, a web server, and MySQL. A web server and MySQL is not needed by class_dicom.php, but since most folks are going to want to use PHP from a web server, we might as well get it installed.


yum -y groupinstall "Web Server" 'Development Tools' "MySQL Database"

yum -y install wget php php-gd ImageMagick libjpeg zlib libpng libtiff libxml2 libpng-devel zlib-devel libjpeg-devel libtiff-devel libxml-devel libxml2-devel libwrap-devel libpng10-devel  php-mysql


sudo apt-get install lamp-server^

sudo apt-get install build-essential wget ImageMagick libjpeg zlib libpng libtiff libxml2 libpng-devel zlib-devel libjpeg-devel libtiff-devel libxml-devel libxml2-devel libwrap-devel libpng10-devel

All of the above will probably take a while to finish out. Note that the ^ in the Ubuntu command is not a typo and needs to be present.

2. Build and install DCMTK

DCMTK is what class_dicom.php uses to actually work with DICOM files. Among many other things, it is a collect of command line tools for working with DICOM files.

The process of building and installing is essentially the same between versions of Linux.

tar zxvf dcmtk-3.6.0.tar.gz
cd dcmtk-3.6.0
./configure;make;make install

Depending on your computer, the DCMTK can take a long time to build.

3. Install class_dicom.php

Download a copy:

wget --no-check-certificate
unzip master
mv vedicveko-class_dicom.php* class_dicom_php

and…. you’re done.

Lets make sure it works!

cd class_dicom_php/examples
./get_tags.php dean.dcm

You should see a header dump of dean.dcm printed to the screen.

The script below will look in the directory you specify in $temp_dir and send any images it finds to a DICOM host. When it is done sending it will move the DICOM file to a back up directory.



$temp_dir = '../temp';

$target_host = 'some IP address';
$target_port = '105';
$target_ae = 'BK';
$my_ae = 'BK';

if(!file_exists('bk')) {

$d = new dicom_net;

if($handle = opendir($temp_dir)) {
  while(false !== ($file = readdir($handle))) {
    if($file != "." &#038;& $file != "..") {

      print "Sending $file...\n";

      $d--->file = "$temp_dir/$file";
      $ret = $d-&gt;send_dcm($target_host, $target_port, $my_ae, $target_ae);
      if($ret) {
        print "Send Error: $ret\n";
      else {
        print "Good Send\n";
        print "Moving $temp_dir/$file\n";
        rename("$temp_dir/$file", "bk/$file");



I had the opportunity today to help someone get class_dicom.php working under windows. I was pretty surprised to learn that you do not have to modify the class much, but it does take some time to install the other prerequisite software.

If you’re using IIS, you can skip step #1. You will need to get a copy of PHP installed under IIS. PHP on IIS ( will allow you to do that easily. If you are using IIS, change the file locations below as appropriate for your enviroment.

1. You’ll need a copy of PHP.

Since you’ll probably want to run your PHP scripts under a web server you might as well kill two (three) birds with one stone.  The Wamp (  package is an easy as pie installer that will install and configure Apache, mySQL, PHP, and several other handy tools at once.

Once installed, be sure to find it in the system tray, left click it, and then select Put Online to start it up.

You can then go to http://localhost to test your install out.

The files you’re serving are all under C:\wamp\www\

2. You’ll need the Windows binaries of the DCMTK.

Head on over to the DCMTK website ( and under executable binaries, grab the zip file for Windows.

Unzip it an copy the result to C:\dcmtk so it’s easy to find.

3. Get a copy class_dicom.php.

You can download a zipped version from GitHub ( Once downloaded unzip it to C:\wamp\www\class_dicom.

You should be able to visit your web server at http://localhost/class_dicom and see a directory listing.

We’re going to need to make some modifications to the class. Open C:/wamp/www/class_dicom/class_dicom.php in an editor.

Near the top of a file is a line that looks like this:

define('TOOLKIT_DIR', '/usr/local/bin');

We need to change that to point to where we installed DCMTK. Change the line to look like this:

define('TOOLKIT_DIR', 'C:/dcmtk/bin');

Save class_dicom.php and you’re done with it.

4. Finishing up

All of the scripts in the examples directory for class_dicom.php are meant to run on the UNIX command line. This won’t work out too well when you try to run them under a web server on Windows. We need to change up one of the examples to run in this environment so you can see that it works. Take the code below and copy it into the C:\wamp\www\class_dicom\examples\get_tags.php file.

# Prints out the DICOM tags in a file specified on the command line


$file = 'dean.dcm';

if(!file_exists($file)) {
  print "$file: does not exist\n";

$d = new dicom_tag;
$d->file = $file;
print "TEST: " . $d->load_tags();

print "<pre>";


$name = $d->get_tag('0010', '0010');
print "Name: $name\n";


Once that is done go to http://localhost/class_dicom/examples/get_tags.php. You’ll see a header dump of the dean.dcm sample DICOM file.

(Or skip all of that and open get_tags_webbased.php in your browser instead.)

You now have a functional install of class_dicom.php to work with!

For the first time ten months I’ve decided to update my DICOM PHP class (class_dicom.php).

I’ve added two new functions:

is_dcm(): This function will return true if the provided file is a DICOM file, false otherwise.

multiframe_to_video():  Convert a DICOM multi-frame file into a video (.mp4, .avi, .mpg, ect)

multiframe_to_video() is the big one. This functionality is driven by ffmpeg and is easily extendable and modifiable. Think of it as DICOM to AVI or DICOM to MPG.

Check it out!


The Mud Emporium was a website I ran for several years dedicated to archiving MUD areas and MUD code. I eventually shut it down, but I did save all of the content.

All of the content for the Mud Emporium is now on Github, seperated out into two repositories.

Mud Emporium Areas on Github

The area repositories contains hundreds of areas for many different code bases, ROM, Diku, ect.

Mud Emporium Code on Github

The code repositories is full of code snippets to add classes, skills, spells, NPC classes, and many more behaviors to a MUD.

Dibrova was a MUD (Multi-user Dungeon) I worked on, first as a builder and then as a coder from 2000-2005. Myself and many others put a lot of time into Dibrova and I feel it really shows.

A MUD is text based MMORPG. They were incredibly popular until graphical MMORPGs began to show up.

We had features that were revolutionary for their time, a strong clan/guild system, unlimited levels, automated questing, mob classes, and so on. These are features you see touted in modern MMORPGs that were not available in other games during the same the time period.

Here are some of the features at a glance:

–    243 Zones
–    19105 Rooms
–    6417 Objects
–    5687 Mobiles
–    Limited/Unlimited Levels.
–    Clans
–    Clan run stores, zones, clan halls, tollbooths, mini-bars, ect
–    Automated quest system
–    More classes and races than you can shake a stick at
–    Multi-classing
–    Immortal classing
–    Mob classes, races, skills, semi-intelligent combat

Honestly, Dibrova has been around for so long and has had so many people contributing to it that we do not have a complete feature list.

Through some turn of events I have become the keeper of the source code for Dibrova. For a long time I had it hosted on my own site, the Mud Emporium, but general laziness forced me to close the site down several years ago.

The source is currently hosted on Github.

Dibrova on Guthub

Direct link to download Dibrova