Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Roll detection for "cheese" phantoms + additional dataset #525

Open
mitch-1211 opened this issue Nov 12, 2024 · 1 comment
Open

Roll detection for "cheese" phantoms + additional dataset #525

mitch-1211 opened this issue Nov 12, 2024 · 1 comment

Comments

@mitch-1211
Copy link

mitch-1211 commented Nov 12, 2024

Great to see some additional functionality added to the cheese module in f3ea44c, looking forward to 3.29!

I am looking at implementing a Gammex RMI 465 electron density phantom in Radmachine, initial testing being performed with pylinac directly. I have submitted a phantom dataset through the google form for your reference.

I have the below code currently, which includes ROI definition:

from pylinac.cheese import CheeseModule, CheesePhantomBase
from pylinac.ct import CatPhanBase
class GammexModule(CheeseModule):
    common_name = "RMI Gammex Phantom"
    inner_roi_dist_mm = 55
    outer_roi_dist_mm = 105
    periphary_roi_dist_mm = 142.5
    roi_radius_mm = 10
    roi_settings = {
        "1": {
            "angle": -90,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "2": {
            "angle": -45,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "3": {
            "angle": 0,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "4": {
            "angle": 45,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "5": {
            "angle": 90,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "6": {
            "angle": 135,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "7": {
            "angle": 180,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "8": {
            "angle": -135,
            "distance": inner_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "9": {
            "angle": -67.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "10": {
            "angle": -22.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "11": {
            "angle": 22.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "12": {
            "angle": 67.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "13": {
            "angle": 112.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "14": {
            "angle": 157.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "15": {
            "angle": -157.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "16": {
            "angle": -112.5,
            "distance": outer_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "17": {
            "angle": -90,
            "distance": periphary_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "18": {
            "angle": 0,
            "distance": periphary_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "19": {
            "angle": 90,
            "distance": periphary_roi_dist_mm,
            "radius": roi_radius_mm,
        },
        "20": {
            "angle": 180,
            "distance": periphary_roi_dist_mm,
            "radius": roi_radius_mm,
        }
    }

class GammexPhantom(CheesePhantomBase):
    model = "RMI Gammex Phantom"
    air_bubble_radius_mm = 10
    localization_radius = 105
    min_num_images = 2
    catphan_radius_mm = 165
    module_class = GammexModule
    module: GammexModule

densities = {
    "1": {"density": 1.081},
    "2": {"density": 1.707},
    "3": {"density": 1.039},
    "4": {"density": 0.292},
    "5": {"density": 1.000},
    "6": {"density": 1.000},
    "7": {"density": 1.142},
    "8": {"density": 0.945},
    "9": {"density": 1.116},
    "10": {"density": 1.099},
    "11": {"density": 0.895},
    "12": {"density": 1.473},
    "13": {"density": 1.000},
    "14": {"density": 0.438},
    "15": {"density": 1.285},
    "16": {"density": 1.000},
    "17": {"density": 0.980},
    "18": {"density": 1.050},
    "20": {"density": 1.147}
    }

image_folder = r"K:\ISS\MEDICAL PHYSICS SERVICES\Medical Physics\Radformation\Radmachine\Implementation\CT QA\Gammex images on foam block"
gammex = GammexPhantom(image_folder)


gammex.analyze(roi_config=densities)
print(gammex.results_data(as_dict=True)["phantom_roll"])
print(gammex.results())
gammex.plot_analyzed_image()
gammex.plot_density_curve()

Note I use the terminology inner_roi_dist_mm for the inner most ring of inserts, outer_roi_dist_mm for the next ring moving outwards and periphary_roi_dist_mm for the 4 HU plugs at the periphery.

We get a successful analysis using the above:

image

Something to note however:
I've had to set localization_radius to be the distance corresponding to outer_roi_dist_mm i.e not the inner most ring. Using the inner most ring results in find_origin_slice raising ValueError("No slices were found that resembled the HU linearity module")

It's not necessarily and issue per-se, it's just something that people will probably run into. Is is possible there are too many inserts with HU close to background in the inner ring? We have three solid water inserts that are essentially background in that ring.

@jrkerns
Copy link
Owner

jrkerns commented Nov 18, 2024

Yes, the localization of the phantom depends on there being both high and low HU ROIs. This was started back in the catphan days and has been extended (beyond its foreseen uses) to all the CT-like phantoms. You can also adjust the hu_origin_slice_variance parameter as well: https://pylinac.readthedocs.io/en/latest/cbct.html#localization-variance

A feature will be added to a future release that lets the user pass the slice number as an alternative to the localization as a fallback method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants