XIM images#

Images ending in .xim are generally produced by a Varian TrueBeam or newer linac. They are images with additional tags. Unfortunately, they are written in binary into a custom format so using a typical image library will not work.

The binary file specification appears to be unofficial, but it does work. You can find the spec here which comes from this repo: https://bitbucket.org/dmoderesearchtools/ximreader/src/master/

Warning

Rant ahead.

The XIM images used a custom compression format. Why they chose to use a custom format is beyond me. Moreso, the format they chose was that of a PNG algorithm, but not as good. So, XIM images are just PNG images but with a custom lookup table and property tags. Everyday PNG format would’ve worked just as well. It’s possible this is security by obscurity or NIH syndrome.

Loading an XIM image#

To load an XIM images use the XIM class:

from pylinac.core.image import XIM


my_xim_file = r"C:\TDS\H12345\QA\image.xim"
xim_img = XIM(my_xim_file)

# plot the image
xim_img.plot()

# see the XIM properties
print(xim_img.properties)

Reconstructing the image pixels is relatively slow (~1s for AS1200 image) thanks to the custom compression format, so if you are only searching through the properties you can skip reconstructing the pixels. Skipping the pixels and only reading the properties is relatively fast (order of milliseconds):

from pylinac.core.image import XIM


my_xim_files = [r"C:\TDS\H12345\QA\image.xim", ...]
files_to_analyze = []
for file in my_xim_files:
    # will load relatively fast
    xim_img = XIM(file, read_pixels=False)
    if xim_img.properties["AcquisitionMode"] == "Highres":
        files_to_analyze.append(file)

# now load the pixel data only for the files we're interested in
for file in files_to_analyze:
    xim_img = XIM(file)
    # image is available, do what you want
    xim_img.plot()

An XIM image has all the utility methods other pylinac images do, so use this to your advantage:

from pylinac.core.image import XIM


my_xim_file = r"C:\TDS\H12345\QA\image.xim"
xim_img = XIM(my_xim_file)

# process
xim_img.crop(pixels=30)
xim_img.filter()
xim_img.fliplr()
...

Exporting images#

Exporting *.xim images is easy. The PNG format is recommended because its ~1/2 the size of the original xim image and will also include the properties. PNG is also lossless, so all information is retained. PNG images can usually be viewed easily across many devices and OSs and also loads very fast.

from pylinac.core.image import XIM


my_xim_file = r"C:\TDS\H12345\QA\image.xim"
xim_img = XIM(my_xim_file)

xim_img.save_as("myxim.png")
# saved to PNG!

Reading exported images#

To load the image in python you can use any library that reads PNG. Pillow is recommended. Opening these files are usually very fast (order of milliseconds), so if you plan on doing research or analysis of a large number of .xim images, it may be worth it to export to PNG en masse and then perform the analysis.

import numpy as np
import PIL.Image
import matplotlib.pyplot as plt

xim_img = PIL.Image.open("myxim.png")

# numpy array of the pixels
xim_array = np.asarray(xim_img)

# plot it
plt.imshow(xim_array)
plt.show()

To read the properties of an XIM file that was saved to PNG we may to have to load from strings. PNG tags are all strings, and some xim properties are arrays or numbers. In order to easily save it, we convert them all to strings. In order to get the native datatype for non-string types we cast to the inferred type. For numbers, use float and for lists use json:

import json
import PIL.Image

xim_img = PIL.Image.open("myxim.png")

system_version = xim_img.info["AcquisitionSystemVersion"]
# "2.7.304.16" already a string so no change needed

couch_lat = xim_img.info["CouchLat"]
# '100.39021332'  it's a string even though it looks like a number
# convert to the original type:
couch_lat_num = float(couch_lat)

# MLCs are a list; we need json
mlc_a_string = xim_img.info["MLCLeafsA"]
# '[20.6643, 20.6992, ...]'
mlc_a_list = json.loads(mlc_a_string)
# now it's a normal list: [20.6643, 20.6992, ...]