[Insight-developers] DICOM UID generation

David Clunie dclunie at dclunie.com
Thu Jan 6 11:19:43 EST 2005


Hi Mathieu

The use of IP address is very problematic.

Most of us work using private IP addresses behind routers that do
network address translation, therefore the chances of collisions
based on IP address are extremely high. E.g. there are thousands
of 10.1.1.1 and 192.168.1.1 hosts in the world.

MAC address is much better, but harder to get in a platform
neutral way. Some platforms have a hostid.

For a timestamp, one needs precision well down into the millisecond
range, and something like the Unix milliseconds since epoch might do.

I would always try and include a thread and/or process number as
well, since it is quite likely that separate threads or processes
on the same machine might generate UIDs at the same time with a
granularity of milliseconds, given the speed of today's servers.

I try and throw in a random number if at all possible, just in case.

Another possibility is to use some form of cryptographic random number
generator that creates a number that is very, very likely to be unique.

You might want to take a look at the Javadoc describing java.rmi.server.UID
and java.security.SecureRandom for some ideas, even though you use a
different platform.

It also depends also on how deterministic one wants the generation process
to be, if at all.

More questions than answers perhaps, but the simplistic approach
suggested below is very likely not robust enough for generating
UIDs that will escape into a clinical PACS environment, where uniqueness
is extremely important, because of a) the use of IP address and b) the
use of perhaps insufficiently precise time. The inclusion of the "gdcm"
as octal may not help since the root that precedes it should be
unique enough to distinguish the toolkit, and all users of gdcm will be
adding this extra unhelpful component.

Discussing this makes me cringe when I think how inadequate some of
the techniques I use in my own toolkits are under various circumstances.
Time to revisit them again I suppose.

David

Mathieu Malaterre wrote:

> David,
> 
>     Could you please comment on the code bellow. This is extracted from 
> GDCM CVS tree.
> 
>     The code works as follow:
> echo "gdcm" | od -b
> 0000000 147 144 143 155 012
> 
>     Then we build the UID with:
> 
>  radical (if passed in) + 147.144.143.155 + IP + time()
> 
>     Therefore even organization without radical can create unique DICOM 
> images internal within their organization.
> 
> Mathieu
> 
> /**
>  * \ingroup Util
>  * \brief   Return the IP adress of the machine writting the DICOM image
>  */
> std::string Util::GetIPAddress()
> {
>   // This is a rip from 
> http://www.codeguru.com/Cpp/I-N/internet/network/article.php/c3445/
> #ifndef HOST_NAME_MAX
>   // SUSv2 guarantees that `Host names are limited to 255 bytes'.
>   // POSIX 1003.1-2001 guarantees that `Host names (not including the
>   // terminating NUL) are limited to HOST_NAME_MAX bytes'.
> #  define HOST_NAME_MAX 255
>   // In this case we should maybe check the string was not truncated.
>   // But I don't known how to check that...
> #if defined(_MSC_VER) || defined(__BORLANDC__)
>   // with WinSock DLL we need to initialise the WinSock before using 
> gethostname
>   WORD wVersionRequested = MAKEWORD(1,0);
>   WSADATA WSAData;
>   int err = WSAStartup(wVersionRequested,&WSAData);
>   if (err != 0) {
>       /* Tell the user that we could not find a usable */
>       /* WinSock DLL.                                  */
>       WSACleanup();
>       return "127.0.0.1";
>   }
> #endif
> 
> #endif //HOST_NAME_MAX
> 
>   std::string str;
>   char szHostName[HOST_NAME_MAX+1];
>   int r = gethostname(szHostName, HOST_NAME_MAX);
> 
>   if( r == 0 )
>   {
>     // Get host adresses
>     struct hostent * pHost = gethostbyname(szHostName);
> 
>     for( int i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ )
>     {
>       for( int j = 0; j<pHost->h_length; j++ )
>       {
>         if( j > 0 ) str += ".";
> 
>         str += Util::Format("%u",
>             (unsigned int)((unsigned char*)pHost->h_addr_list[i])[j]);
>       }
>       // str now contains one local IP address
> 
> #if defined(_MSC_VER) || defined(__BORLANDC__)
>   WSACleanup();
> #endif
> 
>     }
>   }
>   // If an error occur r == -1
>   // Most of the time it will return 127.0.0.1...
>   return str;
> }
> 
> /**
>  * \ingroup Util
>  * \brief Creates a new UID. As stipulate in the DICOM ref
>  *        each time a DICOM image is create it should have
>  *        a unique identifier (URI)
>  */
> std::string Util::CreateUniqueUID(const std::string& root)
> {
>   // The code works as follow:
>   // echo "gdcm" | od -b
>   // 0000000 147 144 143 155 012
>   // Therefore we return
>   // radical + 147.144.143.155 + IP + time()
>   std::string radical = root;
>   if( !root.size() ) //anything better ?
>   {
>     radical = "0.0."; // Is this really usefull ?
>   }
>   // else
>   // A root was specified use it to forge our new UID:
>   radical += "147.144.143.155"; // gdcm
>   radical += ".";
>   radical += Util::GetIPAddress();
>   radical += ".";
>   radical += Util::GetCurrentDate();
>   radical += ".";
>   radical += Util::GetCurrentTime();
> 
>   return radical;
> }



More information about the Insight-developers mailing list