Showing posts with label cornerstone. Show all posts
Showing posts with label cornerstone. Show all posts

Tuesday, April 28, 2015

SIIM 2015 Hackathon Grand Challenge

Yesterday I spent about eight hours building out the foundation for the SIIM 2015 Hackathon Grand Challenge.  This foundation is a simple web based radiology focused EMR client that includes functionality such as patient search and the display of a patient's radiology reports with images.  By providing such a foundation, it is hoped that hackers would have a baseline system to start hacking from rather than starting from scratch.

The SIIM Hackathon committee had already deployed a Spark FHIR server and DCM4CHEE v4 server into AWS and loaded both with synchronized data sets.  A special thanks to Mark Kohli, Steve Langer, and Jason Hostettler for their work on the datasets and Mohannad Hussain for setting up the DCM4CHEE server.  I also contributed the ImagingStudy resources to the FHIR dataset which were generated by converting the response from a WADO-RS Retrieve Metadata call using this tool.

I wanted to keep the learning curve for the baseline system to be low so hackers could get started quickly.  To support this, I decided to keep the third party dependencies to a minimum.  I ended up using only jQuery and bootstrap since most web developers are familiar with both.  I ruled out other popular (and powerful) libraries such as Meteor (my favorite), Angular and KnockoutJS as these powerful libraries take some time to learn and not everyone knows them.

My first goal was to create an architecture spike to prove out that it would work and also lay down the foundational pieces that I could build on top of.  I decided to begin with displaying a list of patients that I would obtain by querying the FHIR server.  This turned out to be very easy and took about 30 minutes to get up and going.  It basically involved making a query to the Patient resource filtered by the last name of the patients in our datasets and creating rows in a table from the results:

http://fhir.hackathon.siim.org/fhir/Patient?family=SIIM

Next up was creating a radiology centric patient view.  The first thing I needed was to display the available reports for the user.  This required querying the DiagnosticReport resource filtered by the ID for the selected Patient and creating rows in a table from the results:

http://fhir.hackathon.siim.org/fhir/DiagnosticReport?subject=Patient/siimjoe

This was up and running after another 30 minutes.  After this, I needed to display the actual report the user clicked on in the list of reports.  I hooked the click event on the table row and made a request for the associated DiagnosticReport by ID:

http://fhir.hackathon.siim.org/fhir/DiagnosticReport/2257132503242682

I grabbed the human readable form for the report from the data.text.div property.  This is normally HTML so I used the jQuery parseHTML() function to parse it into DOM nodes that I could stick directly into DOM.  This took yet another 30 minutes.

The last step is to display the images for the report.  This required searching for ImagingStudy resources based on the accession number stored in selected the DiagnosticReport resource:

http://fhir.hackathon.siim.org/fhir/ImagingStudy?accession=2257132503242682

There may actually be multiple ImagingStudy resources for a given DiagnosticReport so some logic was required to pick the right one.  I decided to pick the one with the fewest number of referenced SOP Instances assuming that the smallest one would contain the key images (rather than the entire study).  Once I had the ImagingStudy, I decided to pick the first image in the first series and display it using cornerstone.  I haven't built a WADO-RS based ImageLoader for cornerstone yet so I decided to use cornerstoneWADOImageLoader and load images via WADO-URI.  30 minutes later, I had an image displayed!

The spike took about 2 1/2 hours to complete and not only proved the concept but provided a great foundation to work from.  I spent the rest of the day refactoring the code for readability and adding more functionality. If you are really interested in seeing how this was built, check out the commit log.

Saturday, March 28, 2015

Happy Birthday Cornerstone!

I just realized that cornerstone had its first birthday a little over one week ago.  The project started based on this discussion on comp.protocols.dicom where folks where folks were disappointed with the lack of open source image viewers.  While there already was two good open source viewers (Ovyiam and DICOM Web Viewer), neither of these were architected such that I could use them to build my own web based medical image viewing applications.  I was already convinced that the future of medical imaging was HTML5/JS based image viewers and being the lazy programmer that I am, didn't want to build basic image viewing functionality over and over again (I have personally coded a ww/wc algorithm at least 15 different times in various languages).  I had a personal need for a javascript SDK that made it easy to display interactive medical images in a web browser - and that is how cornerstone began.

I must admit that starting cornerstone was not easy to do.  I was starting a new business (Lury) and it seemed a bit crazy to spend my time writing code that I would be giving away for free.  This is especially true because I had figured out a number of tricks to make client side rendering possible while the industry norm was (and still is) server side rendering.  The benefits of client side rendering are compelling enough to provide the differentiation needed to make a new startup company like Lury successful in an already competitive market and giving this away for free was very hard to do.

On top of giving away some of these secrets, the code I wrote would be on display for everyone to see.  While I believe I write fairly good code, I am not perfect and what is "right" can sometimes be subjective.  Code reviews are actually quite common in closed source projects and it is one of the most vulnerable experiences you go through as a software developer.  What happens is you get in a room with a bunch of other developers and they look at your code with a magnifying glass and give you feedback.  Your "best effort" is on display and the bulk of the discussion is around how you could do better.  In safe environments, these meetings are productive and are highly educational.  The internet is not a safe place though and making it publicly available for everyone to see and criticize required a tremendous amount of courage.

Looking back on the past year, I can say that making cornerstone open source is the best thing I have ever done.  It has brought me tremendous joy to see others using cornerstone.  There are at least 50 projects that I know of using cornerstone today and probably many I don't know about.  Many of these projects are positively impacting patient outcomes and probably would not have been possible without cornerstone.  I have also made many new friends all over the world - some of which have given me an open invitation to stay with them whenever I might visit.

I want to say "thank you" to everyone who has supported the cornerstone project - it would not have been possible without your emails of encouragement, bug reports, bug fixes and new features.  The future is bright for cornerstone and everyone is welcome to be part of this!

Tuesday, May 27, 2014

Image Rendering Refactoring

While working on the faster image loading mechanism for Cornerstone, I found myself making further changes to the drawImage() function.  I already felt this function was too complex as it had to deal with at least 12 different flows due to the different types of images and caching scenarios.  I realized that adding support for the different faster image loading the function would quickly become hard to read and unmaintainable.  Clearly something had to change, but what?

After thinking about it for a bit, I had an "aha" moment and realized that the image rendering responsibility should be moved to the image loader.  The image loader design was already providing flexibility with respect to the image format and protocol used and it would also have to be aware of the various fast image loading techniques being used.  Adding the image rendering responsibility to the image loader will allow the fastest possible rendering.

The only issue with moving rendering to the image loader is code reuse.  I envision a wide variety of image loaders that pull full uncompressed pixel data from different servers in different formats.  For these types of image loaders, it doesn't make sense to have them cut and paste the rendering code from another image loader.  It would be much better to have this generic image rendering mechanism in a shared location.

Based on the above, I decided to split the drawImage() function into three new functions - renderGrayscaleImage(), renderColorImage() and renderWebImage().  This simple refactoring immediately made the code easier to understand so I knew I was on the right path.  The drawImage() function was simplified to some boilerplate logic and delegated the actual rendering to the image object returned by the image loader.  I then proceeded to modify each of the image loaders to call one of the three newly created render functions and everything was working again.

This simple refactoring not only made the existing code simpler and easier to understand, but it also prepares Cornerstone for the more complex functionality that will be needed to handle faster image loading.  You can find the commit for these changes here