<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muneeb Ahmad</title>
    <description>The latest articles on DEV Community by Muneeb Ahmad (@muneeb_ahmad_18a2b2f10a70).</description>
    <link>https://dev.to/muneeb_ahmad_18a2b2f10a70</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4110969%2F5a0c5804-f6df-485c-86c6-856f1fcf1ba1.png</url>
      <title>DEV Community: Muneeb Ahmad</title>
      <link>https://dev.to/muneeb_ahmad_18a2b2f10a70</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muneeb_ahmad_18a2b2f10a70"/>
    <language>en</language>
    <item>
      <title>My GSoC Journey with InVesalius: Bringing PACS Integration to InVesalius</title>
      <dc:creator>Muneeb Ahmad</dc:creator>
      <pubDate>Sat, 05 Sep 2026 10:45:34 +0000</pubDate>
      <link>https://dev.to/muneeb_ahmad_18a2b2f10a70/my-gsoc-journey-with-invesalius-bringing-pacs-integration-to-invesalius-3m39</link>
      <guid>https://dev.to/muneeb_ahmad_18a2b2f10a70/my-gsoc-journey-with-invesalius-bringing-pacs-integration-to-invesalius-3m39</guid>
      <description>&lt;h2&gt;
  
  
  My GSoC Journey with InVesalius: Bringing PACS Integration to InVesalius
&lt;/h2&gt;

&lt;p&gt;For the past three months I have been contributing at Invesalius as a GSOC contributor. It has been quite a fun summer and now that I have finally completed the project I wanted to share a sneak peak of what I did and learnt and about the org.&lt;/p&gt;

&lt;h2&gt;
  
  
  InVesalius
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3dry4k3yvy2vei9cbfob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3dry4k3yvy2vei9cbfob.png" alt="Invesalisus" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A well-established open-source medical imaging software developed in Brazil. It enables the generation of physical models through 3D printing using two-dimensional (2D) images acquired from CT or MRI. By converting these 2D images into 3D anatomical representations, healthcare professionals can utilize the resulting models for various medical purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Invesalius did not support remote connections with hospital servers (PACS) so to exchange data external tools were required, which is an overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Use libraries like pynetdicom and pydicom to build methods that support querying Hospital servers (PACS) for image search and retrieval over the network, without the manual effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick introduction to PACS and DICOM
&lt;/h2&gt;

&lt;p&gt;PACS i.e. Picture Archiving and Communication System does exactly what it sounds like, it helps us manage pictures by communicating with the systems.&lt;/p&gt;

&lt;p&gt;Which systems you say? DICOM systems.&lt;/p&gt;

&lt;p&gt;Now what are DICOMs?&lt;/p&gt;

&lt;p&gt;DICOM is the standard used for storing, communicating, querying and retrieving medical images and related information. DICOM servers are the systems that implement this standard and host/manage these images, acting as a centralized picture archive/storage for a hospital. Some examples are Orthanc and dcm4chee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk8yf0wg5ejyv9260vmun.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk8yf0wg5ejyv9260vmun.png" alt="Architecture" width="739" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the DICOM Operations Fit Together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxytoznrjsgdwe5p6v4os.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxytoznrjsgdwe5p6v4os.png" alt="Pacs Flow" width="621" height="903"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;/p&gt;

&lt;p&gt;Connectivity → Search → Retrieval → Local Storage&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;C-ECHO: to check if the server is connected&lt;/li&gt;
&lt;li&gt;C-FIND: to search patients across available nodes/servers&lt;/li&gt;
&lt;li&gt;C-MOVE: to retrieve/download images&lt;/li&gt;
&lt;li&gt;C-GET: to retrieve/download images, used as a fallback&lt;/li&gt;
&lt;li&gt;C-STORE: to store retrieved images in a pre-specified folder&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Patient → Study → Series → Image
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Felv28sw1fl5liq87c8hh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Felv28sw1fl5liq87c8hh.png" alt="DICOM Data Hierarchy" width="289" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The images in a DICOM server are organized hierarchically:&lt;br&gt;
Patient → Study → Series → Image&lt;br&gt;
This also allowed the user to download patient images at different hierarchy levels like patient, study, series.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Did
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DICOM Networking
&lt;/h3&gt;

&lt;p&gt;Implemented querying methods like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;c-echo: to check if the server is connected&lt;/li&gt;
&lt;li&gt;c-find: to search patients across available nodes/servers&lt;/li&gt;
&lt;li&gt;c-move: to retrieve/download images&lt;/li&gt;
&lt;li&gt;c-get: to retrieve/download images - used as a fallback&lt;/li&gt;
&lt;li&gt;c-store: to store retrieved images in a pre-specified folder&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  User Interface
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4wzovjeer7r0se9c8wjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4wzovjeer7r0se9c8wjp.png" alt="PACS UI" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implemented ui for user interaction that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search bar and associated dialogs for searching&lt;/li&gt;
&lt;li&gt;UI to manage nodes/servers for image retrieval.&lt;/li&gt;
&lt;li&gt;Option to download patient images at different hierarchy levels like: patient, study, series.&lt;/li&gt;
&lt;li&gt;Cancellation option to stop download, and clean up the directory.&lt;/li&gt;
&lt;li&gt;Preferences dialog for network options to set download directory, choose download method, and application configuration.&lt;/li&gt;
&lt;li&gt;Checking if the specific images are already downloaded to prevent duplicates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4yz0hvj88hw3wub7fprk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4yz0hvj88hw3wub7fprk.png" alt="Preference Dialog" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend best practices and optimizations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Graceful error handling: if c-move fails automatically falls back to c-get.&lt;/li&gt;
&lt;li&gt;Type hinting for type safety.&lt;/li&gt;
&lt;li&gt;Clear logging for easier debugging.&lt;/li&gt;
&lt;li&gt;Query methods wrapped with async methods to prevent ui-freeze.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Engineering Challenges
&lt;/h2&gt;

&lt;p&gt;This is the section I would give the most attention to. These are actual problems you encountered rather than simply features you implemented.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Falling back from C-MOVE to C-GET
&lt;/h3&gt;

&lt;p&gt;One of the challenges was to use c-get as fallback if c-move was to fail.&lt;br&gt;
I had to think of a creative way, so I came up with a less creative simple solution and that was to handle it as an error and also support the user to choose his preffered method.&lt;br&gt;
So what I did was to create one orchestrator for both methods and handle error gracefully like if the choice was cmove and an error occurs then move to orchestrate c-get, pretty simple i know but i was happy when i came up with it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkehu7jre3xnuojek8ypt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkehu7jre3xnuojek8ypt.png" alt="Fall Back" width="345" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cancellation, Cleanup and Progress Tracking
&lt;/h3&gt;

&lt;p&gt;Second challenge would be to handle the actual cancelling the c-move, cleaning the downloaded ones and not fallback to c-get and also deal with the download dialog without ui freeze.&lt;br&gt;
I used wx.Callafter functions for this, raised error “CANCELLED”.. and tracked at orchestrator, removing all the downloaded images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0bqwtsa6tb2wguk8pwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0bqwtsa6tb2wguk8pwu.png" alt="Cancellation Flow" width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2m24cxhe6reschbs9wig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2m24cxhe6reschbs9wig.png" alt="Cancellation Dialog" width="702" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main problem was the progress dialog.&lt;br&gt;
I used initially a progress dialog with exact downloaded count and boy that was tough to continually update it after each image, and then there was another problem was that orthanc sent one image per response so tracking was easier, but when i tried dcm4chee it sent all the images at once, so i had to track it in the cg-store.&lt;br&gt;
So I went with an easier solution and that was to fallback to a simpler progress dialog without the exact image count.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdyyb03z6byg0bss6d995.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdyyb03z6byg0bss6d995.png" alt="Previous Progress Dialog" width="702" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7z1df1hm7yi50hs79u1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7z1df1hm7yi50hs79u1.png" alt="Current Progress Dialog" width="702" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Preventing UI Freeze
&lt;/h3&gt;

&lt;p&gt;The third issue was actually the ui-freeze problem for these c-queries.&lt;br&gt;
The solution for that was rather simple too and that was to wrap these sync functions into functions which would then call these in the background(as async) using thread executors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;For initial queries I added manual tests, but then with UI - inclusion etc. We stuck with manual verification / testing.&lt;br&gt;
Checking multiple combinations of steps etc. And paulo was a big help here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Language: Python&lt;/li&gt;
&lt;li&gt;Libraries: pynetdicom, pydicom, wx, logging&lt;/li&gt;
&lt;li&gt;Tools: docker, Orthanc, dcm4chee, git&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Learned the docker yml structure and docker commands.&lt;/li&gt;
&lt;li&gt;Learned how medical networks work with the top layer using application entities along with ip: ports and how under the hood tcp is used.&lt;/li&gt;
&lt;li&gt;Learned how different DICOM servers work and how they are configured.&lt;/li&gt;
&lt;li&gt;Learned how DICOM servers are queried for different needs and what context does each require.&lt;/li&gt;
&lt;li&gt;Learned how to skim through documentation to understand new technology and to understand the error codes etc.&lt;/li&gt;
&lt;li&gt;Learned how important logs are for documentation.&lt;/li&gt;
&lt;li&gt;Learned how to better handle the errors.&lt;/li&gt;
&lt;li&gt;And a lot more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open Source
&lt;/h2&gt;

&lt;p&gt;So my journey consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being confused&lt;/li&gt;
&lt;li&gt;Starting from an existing stale PR (#644).&lt;/li&gt;
&lt;li&gt;Being confused&lt;/li&gt;
&lt;li&gt;understanding the previous implementation and the existing codebase before continuing.&lt;/li&gt;
&lt;li&gt;Adapted/extended the work to fit the current codebase.&lt;/li&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Debugging again&lt;/li&gt;
&lt;li&gt;going through mentor/code review and iteration.&lt;/li&gt;
&lt;li&gt;Getting my PR merged (oh the joy of it)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Querying multiple nodes/servers at once.&lt;/li&gt;
&lt;li&gt;Editing nodes information&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;So this project took me from being completely unaware of what DICOM was to implementing PACS integration for InVesalius, handling edge cases and cancellation flows, understanding status codes from different servers, reading documentation, debugging issues, and a lot more along the way. It was a great experience and I would say everyone should contribute to OSS to the software they use and enjoy.&lt;/p&gt;

&lt;p&gt;Well, if you reached this point consider commenting :)&lt;/p&gt;

&lt;h2&gt;
  
  
  PR
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhzcmpdav5ejmvm0bben3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhzcmpdav5ejmvm0bben3.png" alt="Merged PR" width="795" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PR Link: &lt;a href="https://github.com/invesalius/invesalius3/pull/1261" rel="noopener noreferrer"&gt;https://github.com/invesalius/invesalius3/pull/1261&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Special Mentions
&lt;/h2&gt;

&lt;p&gt;Special thanks to my mentor &lt;a href="https://www.linkedin.com/in/paulojamorim/" rel="noopener noreferrer"&gt;Paulo&lt;/a&gt; for his continued guidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Socials
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/muneeb-ahmad-se/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/muneeb-Ahmad-404/" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>dicom</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
