コード例 #1
0
def get_roi_approval(roi_name, examination=None):
    """
    Checks if ROI named *roi_name* is approved (in any of the existing beamsets).

    Args:
        roi_name (str): the name of the ROI
        examination (RS examination object, optional): the examination (CT or StructureSet) for which the ROI is defined; if left out the currently selected examination is used.

    Returns:
        True if the ROI is approved, False if not.
    """

    patient = lib.get_current_patient()

    if examination is None:
        examination = lib.get_current_examination()

    try:
        for beamset_approved_structure_set in patient.PatientModel.StructureSets[
                examination.Name].ApprovedStructureSets:
            for beamset_approved_rois in beamset_approved_structure_set.ApprovedRoiStructures:
                if beamset_approved_rois.OfRoi.Name == roi_name:
                    logger.info('ROI %s is approved.', roi_name)
                    return True
        logger.info('ROI %s is NOT approved.', roi_name)
        return False
    except Exception as e:
        logger.exception(e)
        raise
コード例 #2
0
def auto_assign_roi_types_v2():
    """
        Attempts to automatically assign correct ROI types.

        This functions makes several assumptions and ignores case in ROI names.

        - Any ROI with a name containing *PTV* or *CTV* or *GTV* or *ITV* with the absence of "-" character is considered a *Target*.  Thus, ROIs named such as *RIBS-PTV* will not be labeled as *Target*.
        - Any ROI with a name starting with *BOLUS* will have its type changed as such.
        - All other ROIs will be set by default to *Organ* type and *OrganAtRisk* organ type.
    """
    patient = lib.get_current_patient()
    roi_names = [x.Name for x in patient.PatientModel.RegionsOfInterest]
    for name in roi_names:
        n = name.replace(' ', '').upper()
        if 'PTV' in n and '-' not in n:
            set_roi_type(name, 'Ptv', 'Target')
        elif 'CTV' in n and '-' not in n:
            set_roi_type(name, 'Ctv', 'Target')
        elif 'GTV' in n and '-' not in n:
            set_roi_type(name, 'Gtv', 'Target')
        elif 'ITV' in n and '-' not in n:
            set_roi_type(name, 'TreatedVolume', 'Target')
        elif n == 'BODY':
            set_roi_type(name, organ_type='Other')
        elif n == 'BODY+TABLE':
            set_roi_type(name, organ_type='Other')
        elif n.startswith('BOLUS'):
            set_roi_type(name, 'Bolus', 'Other')
        else:
            set_roi_type(name, 'Organ', 'OrganAtRisk')
コード例 #3
0
def set_roi_type(roi_name, roi_type=None, organ_type=None):
    """
    Sets a ROI to a given type of ROI and/or to a given organ type.

    Args:
        roi_name (str): the name of the ROI
        roi_type (str, optional): the new type of ROI.  Must be one of the supported ROI types in RayStation: *Ptv*, *Gtv*, *TreatedVolume*, *Organ*, etc.
        organ_type (str, optional): the organ type of the ROI. Must be one of the supported ROI organ types in RayStation: *OrganAtRisk*, *Target*, *Other*, etc.
    """

    if get_roi_approval(roi_name):
        logger.warning(
            'ROI "%s" is approved and therefore cannot be changed.' % roi_name)
        return

    patient = lib.get_current_patient()
    try:
        with CompositeAction('Apply ROI changes (' + roi_name + ')'):
            if roi_type:
                patient.PatientModel.RegionsOfInterest[
                    roi_name].Type = roi_type
                logger.info('Type of ROI "%s" set as %s', roi_name, roi_type)
            if organ_type:
                patient.PatientModel.RegionsOfInterest[
                    roi_name].OrganData.OrganType = organ_type
                logger.info('OrganType of ROI "%s" set as %s', roi_name,
                            organ_type)
    except Exception as e:
        logger.exception(e)
        raise
コード例 #4
0
def roi_exists(roi_name, examination=None):
    """
        Checks if a ROI exists.

        Args:
            roi_name (str): the name of the ROI to check
            examination (RayStation object, optional): the RayStation object representing the examination

        Returns:
            True if it exists, False if not.
    """

    patient = lib.get_current_patient()

    if examination is None:
        exam = lib.get_current_examination()
    else:
        exam = examination

    try:
        roi = patient.PatientModel.StructureSets[
            exam.Name].RoiGeometries[roi_name]
    except:
        return False

    if roi.PrimaryShape is None:
        # is not segmented on current examination.
        return False

    return True
コード例 #5
0
def process_ac_qa():
    patient = lib.get_current_patient()
    print '{process_ac_qa}\t\t\tPatient : %s, ID : %s' % (patient.PatientName, patient.PatientID)
    for tp in patient.TreatmentPlans:
        if (tp.Name.startswith('D2IS_')):
            create_ac_qa_plan(tp, phantom_name='QAVMAT ARCCHECK')
            compute_ac_qa_plan_dose((vp for vp in tp.VerificationPlans if (vp.ForTreatmentPlan.Name == tp.Name)).next())
    print 'done ...'
コード例 #6
0
def erase_pois_not_in_list(poi_list=None):

    patient = lib.get_current_patient()

    if poi_list is None:
        poi_list = ['ISO', 'REF SCAN']
    for poi in patient.PatientModel.PointsOfInterest:
        if not poi.Name.upper() in poi_list:
            poi.DeleteRoi()
コード例 #7
0
def double_optimization(plan=None, beamset=None):
    # Function which runs two consecutive optimizations.
    patient = lib.get_current_patient()
    if plan is None:
        plan = lib.get_current_plan()
    if beamset is None:
        beamset = lib.get_current_beamset()

    # Beamsets are numbered starting at 1 whereas Plan Optimizations are numbered starting at 0.
    # Determine what the number of the selected beamset is, then subtract one to locate the associated Plan Optimization.
    plan.PlanOptimizations[beamset.Number - 1].RunOptimization()
    plan.PlanOptimizations[beamset.Number - 1].RunOptimization()
コード例 #8
0
def optimization_90_30(plan=None, beamset=None):
    patient = lib.get_current_patient()
    if plan is None:
        plan = lib.get_current_plan()
    if beamset is None:
        beamset = lib.get_current_beamset()
       
    opt = plan.PlanOptimizations[beamset.Number - 1]
    set_optimization_parameters(plan=plan,fluence_iterations=30, max_iterations=90, compute_intermediate_dose=False)
    opt.RunOptimization()
    set_optimization_parameters(plan=plan,fluence_iterations=30, max_iterations=30, compute_intermediate_dose=False)
    opt.RunOptimization()  
コード例 #9
0
def create_expanded_ptv(ptv_name,
                        color="Yellow",
                        examination=None,
                        margeptv=0,
                        output_name=None,
                        operation='Expand'):
    """Creates an expansion of the specified PTV with a uniform margin.
       Note that the maximum margin permitted by RayStation is 5cm, so the operation only continues if the margin <=5cm.

    Args:
        ptv_name (str): The name of the PTV to use
        color (str): The color of the resulting ROI
        examination (RS Examination object): The CT on which the source and resulting geometries are contoured
        margeptv (float) : Uniform margin around PTV in cm. Default is 0.
    Returns:
        RS region of interest structure
    """

    patient = lib.get_current_patient()
    if examination is None:
        examination = lib.get_current_examination()

    if output_name is None:
        if operation == 'Expand':
            name = "PTV+%scm" % margeptv
        else:
            name = "PTV-%scm" % margeptv
    else:
        if operation == 'Expand':
            name = output_name + "+%scm" % margeptv
        else:
            name = output_name + "-%scm" % margeptv

    if margeptv <= 5:
        patient.PatientModel.CreateRoi(Name=name,
                                       Color=color,
                                       Type="Organ",
                                       TissueName=None,
                                       RoiMaterial=None)
        patient.PatientModel.RegionsOfInterest[name].SetMarginExpression(
            SourceRoiName=ptv_name,
            MarginSettings={
                'Type': operation,
                'Superior': margeptv,
                'Inferior': margeptv,
                'Anterior': margeptv,
                'Posterior': margeptv,
                'Right': margeptv,
                'Left': margeptv
            })
        patient.PatientModel.RegionsOfInterest[name].UpdateDerivedGeometry(
            Examination=examination)
コード例 #10
0
def create_expanded_roi(roi_name,
                        color="Yellow",
                        examination=None,
                        marge_lat=0,
                        marge_sup_inf=0,
                        output_name=None,
                        operation='Expand'):
    """Creates an expansion of the specified ROI with a variable margin.
       Note that the maximum margin permitted by RayStation is 5cm, so the operation only continues if the margin <=5cm.

    Args:
        roi_name (str): The name of the ROI to use
        color (str): The color of the resulting ROI
        examination (RS Examination object): The CT on which the source and resulting geometries are contoured
        marge_lat (float) : The margin to use in the LAT and A-P directions. Default is 0.
        marge_sup_inf (float) : The margin to use in the SUP-INF direction. Default is 0.
        operation(str) : Whether to expand or contract the ROI
    Returns:
        RS region of interest structure
    """

    patient = lib.get_current_patient()
    if examination is None:
        examination = lib.get_current_examination()

    if output_name is None:
        if operation == 'Expand':
            name = "%s+%scm" % (roi_name, marge_lat)
        else:
            name = "%s-%scm" % (roi_name, marge_lat)
    else:
        name = output_name

    if marge_lat <= 5 and marge_sup_inf <= 5:
        patient.PatientModel.CreateRoi(Name=name,
                                       Color=color,
                                       Type="Organ",
                                       TissueName=None,
                                       RoiMaterial=None)
        patient.PatientModel.RegionsOfInterest[name].SetMarginExpression(
            SourceRoiName=roi_name,
            MarginSettings={
                'Type': operation,
                'Superior': marge_sup_inf,
                'Inferior': marge_sup_inf,
                'Anterior': marge_lat,
                'Posterior': marge_lat,
                'Right': marge_lat,
                'Left': marge_lat
            })
        patient.PatientModel.RegionsOfInterest[name].UpdateDerivedGeometry(
            Examination=examination)
コード例 #11
0
def display_external_roi(): # WIP, do not use yet
    patient = lib.get_current_patient()
    plan = lib.get_current_plan()
    
    for roi in patient.PatientModel.StructureSets[exam.Name].RoiGeometries:
        if roi.OfRoi.Type == "External":
            external_roi = roi.OfRoi.Name
        
    try:
        ui = get_current("ui")
        ui.MenuItem[1].Button_PatientModeling.Click()       
    except:
        pass
コード例 #12
0
def double_opt_save(plan=None, beamset=None):
    # Function which runs two consecutive optimizations and saves the plan afterwards (NB this removes the ability to undo the optimizations).
    patient = lib.get_current_patient()
    if plan is None:
        plan = lib.get_current_plan()
    if beamset is None:
        beamset = lib.get_current_beamset()

    # Beamset.Number starts at 1 whereas Plan Optimizations are numbered starting at 0.
    # Determine what the number of the selected beamset is, then subtract one to locate the associated Plan Optimization.
    plan.PlanOptimizations[beamset.Number - 1].RunOptimization()
    plan.PlanOptimizations[beamset.Number - 1].RunOptimization()
    patient.Save()
コード例 #13
0
def create_ring(roi_name, r2, r1=0, new_name=None):
    """
    Creates a ring ROI around another source ROI.  The ring is specified by two
    distance parameters, :math:`r_1` and :math:`r_2`.  A ring of uniform thickness
    is assumed.

    - By default, :math:`r_1`, the inward distance, is set at 0, which makes the ring ROI inner surface coincide with the source roi surface.
    - The outward distance, :math:`r_2`, must always be specified.  It corresponds to the outward distance from the source ROI surface.  If set at zero, the outer surface of the ring ROI will correspond to the source ROI surface.
    - The sum :math:`r_1 + r_2` therefore gives the ring thickness.

    Args:
        r2 (float): the outward distance in cm
        r1 (float, optional): the inward distance in cm (default 0)
        new_name (str, optional): the name of the newly created ring ROI.  If unspecified, the name will automatically be generated based on *r2* and *r1*.

    Returns:
        the ROI RayStation object corresponding to the new ring ROI
    """
    if r1 < 0 or r2 < 0:
        lib.error('Cannot have a negative distance.')

    if new_name is None:
        if r1 == 0:
            new_name = "%s_ring_%dmm" % (roi_name, r2 * 10)
        else:
            new_name = "%s_ring_%d-%dmm" % (roi_name, r1 * 10, r2 * 10)
    patient = lib.get_current_patient()
    examination = lib.get_current_examination()
    source_roi = get_roi(roi_name)
    # with CompositeAction('ROI Algebra (%s)' % roi_name):
    #     ring_roi = patient.PatientModel.CreateRoi(Name=new_name, Type="Organ", TissueName=None, RoiMaterial=None)
    #     ring_roi.SetRoiMaterial(Material=source_roi.RoiMaterial)
    #     ring_roi.SetAlgebraExpression(ExpressionA=dict(Operation="Union", SourceRoiNames=[roi_name], MarginSettings=get_margin_settings(r2)),
    #                                   ExpressionB=dict(Operation="Union", SourceRoiNames=[roi_name], MarginSettings=get_margin_settings(r1)),
    #                                   ResultOperation="Subtraction",
    #                                   ResultMarginSettings=get_margin_settings(0))
    #     ring_roi.UpdateDerivedGeometry(Examination=examination)
    with CompositeAction('Create Wall (%s, Image set: %s)' %
                         (roi_name, examination)):
        ring_roi = patient.PatientModel.CreateRoi(Name=new_name,
                                                  Color="White",
                                                  Type="Organ",
                                                  TissueName=None,
                                                  RoiMaterial=None)
        ring_roi.SetRoiMaterial(Material=source_roi.OfRoi.RoiMaterial)
        ring_roi.SetWallExpression(SourceRoiName=roi_name,
                                   OutwardDistance=r2,
                                   InwardDistance=r1)
        ring_roi.UpdateDerivedGeometry(Examination=examination)
    return ring_roi
コード例 #14
0
def show_all_poi_coordinates():
    """
        In a popup window, shows DICOM coordinates of all POIs defined to the current patient model.
    """
    import gui
    patient = lib.get_current_patient()
    poi_names = [r.Name for r in patient.PatientModel.PointsOfInterest]
    msg = ''
    for poi_name in poi_names:
        coords = get_poi_coordinates(poi_name)
        #msg += 'Les coordonnées DICOM du point "%s" sont : [x = %.2f cm, y = %.2f cm, z = %.2f cm]\n' % (poi_name, coords.x, coords.y, coords.z)
        msg += '%s: [x = %.2f cm, y = %.2f cm, z = %.2f cm]\n' % (
            poi_name, coords.x, coords.y, coords.z)
    #gui.show_log_window(msg)
    return msg
コード例 #15
0
def fit_objectives_orl(plan=None, beamset=None):
    # Function which adjusts DVH and EUD values in objectives to reflect obtained values.
    patient = lib.get_current_patient()
    if plan is None:
        plan = lib.get_current_plan()
    if beamset is None:
        beamset = lib.get_current_beamset()
    
    for objective in plan.PlanOptimizations[beamset.Number - 1].Objective.ConstituentFunctions:
    
        try:
            f_type = objective.DoseFunctionParameters.FunctionType  # Dose falloff objectives do not have a FunctionType and must be skipped
        except:
            continue
        
        roi_name = objective.ForRegionOfInterest.Name
        if roi_name in ['BLOC MOELLE','MOELLE','prv5mmMOELLE']:
            continue
        else:
            if f_type == "MaxDvh":
                dose_level = objective.DoseFunctionParameters.DoseLevel
                obtained_vol = plan.TreatmentCourse.TotalDose.GetRelativeVolumeAtDoseValues(RoiName=roi_name, DoseValues=[dose_level])
                if obtained_vol[0] >= 0.02:
                    objective.DoseFunctionParameters.PercentVolume = int(obtained_vol[0] * 90)
                if obtained_vol[0] < 0.02:
                    objective.DoseFunctionParameters.DoseLevel = int(dose_level*0.9)

            elif f_type == "MaxEud":
                dose_level = objective.DoseFunctionParameters.DoseLevel
                a = objective.DoseFunctionParameters.EudParameterA
                obtained_eud = compute_eud(plan.TreatmentCourse.TotalDose, roi_name, a)
                if obtained_eud > 100:
                    objective.DoseFunctionParameters.DoseLevel = round(obtained_eud*0.99,-1)

            elif f_type == "MaxDose":
                dose_level = objective.DoseFunctionParameters.DoseLevel
                vol_roi=patient.PatientModel.StructureSets['CT 1'].RoiGeometries[roi_name].GetRoiVolume()
                if vol_roi >= 0.1:
                    vol_cc=0.1/vol_roi
                    max_dose_obtenue = plan.TreatmentCourse.TotalDose.GetDoseAtRelativeVolumes(RoiName=roi_name, RelativeVolumes=[vol_cc])
                else:
                    max_dose_obtenue = plan.TreatmentCourse.TotalDose.GetDoseAtRelativeVolumes(RoiName=roi_name, RelativeVolumes=[vol_roi])
                if max_dose_obtenue[0] < (dose_level/1.05) and max_dose_obtenue[0] > 500:
                    objective.DoseFunctionParameters.DoseLevel = int(max_dose_obtenue[0]*1.05)
                elif max_dose_obtenue[0] < (dose_level/1.05) and max_dose_obtenue[0] <= 500:
                    objective.DoseFunctionParameters.DoseLevel = 500    
コード例 #16
0
def auto_assign_poi_types():
    """
        Attempts to automatically assign correct POI types.

        Assumes localization point has string *SCAN* as part of its name.
        If no POI with *SCAN* in its name ca be found, the isocenter will
        be assumed to be the proper localization point.

        Isocenter is set to the type *isocenter*, save for the case above.

        All other POIs are set to type *marker*.
    """
    patient = lib.get_current_patient()
    poi_names = [p.Name for p in patient.PatientModel.PointsOfInterest]

    iso_name = identify_isocenter_poi()

    set_poi_type(iso_name, 'Isocenter')

    loc_name = [
        p.Name for p in patient.PatientModel.PointsOfInterest
        if 'SCAN' in p.Name.upper()
    ]
    if len(loc_name) > 1:
        logger.error(
            'More than one POI found for possible localization points.  Only one POI should exist with "SCAN" in its name.'
        )
        raise SystemError(
            'More than one POI found for possible localization points.  Only one POI should exist with "SCAN" in its name.'
        )
    elif len(loc_name) == 0:
        logger.warning(
            'No localization point could be found.  Using the isocenter point for localization.'
        )
        loc_name = iso_name
        set_poi_type(loc_name, 'LocalizationPoint')
    else:
        loc_name = loc_name[0]
        set_poi_type(loc_name, 'LocalizationPoint')

    done_pois = [iso_name, loc_name]

    for p in poi_names:
        if p not in done_pois:
            set_poi_type(p, 'Marker')
コード例 #17
0
def identify_body():
    """
        Attempts to identify which is the body ROI that should be set as external
        in RayStation.  However, it does not set this ROI as external.

        Returns:
            str: the name of the ROI identified as body. Returns `None` if it could not be identified.
    """
    patient = lib.get_current_patient()
    roi_names = [x.Name for x in patient.PatientModel.RegionsOfInterest]
    for r in roi_names:
        if r.upper().replace(' ', '') == 'BODY+TABLE':
            return r
        if r.upper().replace(' ', '') == 'BODY':
            return r
    logger.warning(
        'No ROI named in a standard way for the body could be found.')
    return None
コード例 #18
0
def delete_roi(roi_name):
    """
        Deletes an ROI based on its name.

        Args:
            roi_name (str): the name of the ROI to delete.
    """
    patient = lib.get_current_patient()

    try:
        for r in patient.PatientModel.RegionsOfInterest:
            if r.Name == roi_name:
                r.DeleteRoi()
                logger.info('Deleted ROI %s.', roi_name)
                break
        logger.warning('Could not delete ROI %s (not found).', roi_name)
    except Exception as e:
        logger.exception(e)
        raise e
コード例 #19
0
def set_poi_type(poi_name, poi_type='Marker'):
    """
    Sets a POI to a given POI type.

    Args:
        poi_name (str): the name of the POI for which to set the type
        poi_type (str, optional): the type of the POI.  By default, will set to type *Marker*.
    """

    if get_poi_approval(poi_name):
        logger.warning(
            'POI "%s" is approved and therefore cannot be changed.' % poi_name)
        return

    patient = lib.get_current_patient()
    try:
        patient.PatientModel.PointsOfInterest[poi_name].Type = poi_type
        logger.info('Type of POI "%s" set as %s', poi_name, poi_type)
    except Exception as e:
        logger.exception(e)
        raise
コード例 #20
0
def generate_BodyRS_using_threshold():
    patient = lib.get_current_patient()
    examination = lib.get_current_examination()
    structure_set = 0

    if not roi_exists("BodyRS"):
        patient.PatientModel.CreateRoi(Name="BodyRS",
                                       Color="Green",
                                       Type="Organ",
                                       TissueName=None,
                                       RoiMaterial=None)
        patient.PatientModel.RegionsOfInterest['BodyRS'].GrayLevelThreshold(
            Examination=patient.Examinations['CT 1'],
            LowThreshold=-650,
            HighThreshold=5000,
            PetUnit="",
            BoundingBox=None)
        patient.PatientModel.RegionsOfInterest['BodyRS'].Type = "External"
        patient.PatientModel.RegionsOfInterest['BodyRS'].SetAsExternal()
        patient.PatientModel.StructureSets[structure_set].SimplifyContours(
            RoiNames=["BodyRS"], RemoveHoles3D=True)
コード例 #21
0
def identify_ptvs2():
    """
    Attempts to identify PTVs.

    Ignores case.  Expects 'PTV' at the beginning of the ROI name, then
    characters which can be represented as a number (the prescription dose).
    """
    patient = lib.get_current_patient()
    roi_names = [x.Name for x in patient.PatientModel.RegionsOfInterest]
    candidates = [
        x for x in roi_names
        if (x.replace(' ', '').upper().startswith('PTV')
            and lib.is_number(x.replace(' ', '').upper()[3:]))
    ]

    if len(candidates) == 0:
        logger.warning('No ROIs could be identified as a PTV.')
        return None

    logger.info('Possible PTVs identified as %s', candidates)
    return candidates
コード例 #22
0
def create_poi(point,
               name,
               color='Green',
               poi_type='Marker',
               examination=None):
    """
    Creates a new POI.

    Args:
        point (dict or RSPoint): the (x, y, z) DICOM coordinates of the new point
        name (str): the name of the new POI
        color (str, optional): the color of the new POI (default: *Green*)
        poi_type (str, optional): the type of the new POI (default: *Marker*)

    Returns:
        the RayStation POI object corresponding to the newly created POI
    """

    # Must used dictionary representation of the point if an RSPoint
    if isinstance(point, lib.RSPoint):
        point = point.value

    patient = lib.get_current_patient()
    if examination is None:
        examination = lib.get_current_examination()

    try:
        poi = patient.PatientModel.CreatePoi(Examination=examination,
                                             Point=point,
                                             Volume=0,
                                             Name=name,
                                             Color=color,
                                             Type=poi_type)
        return poi
    except SystemError as e:
        if str(e) == 'Name not unique':
            logger.warning('A POI named %s already exists.' % name)
    except Exception as e:
        logger.exception(e)
        raise
コード例 #23
0
def create_expansion(roi_name,
                     sup,
                     inf=None,
                     ant=None,
                     post=None,
                     left=None,
                     right=None,
                     new_name=None):
    """
    Create a new ROI which is the expansion of another existing ROI.

    Args:
        roi_name (str): the source ROI name
        sup (float): the margin in cm in the superior direction
        inf (float, optional): the margin in cm in the inferior direction, equal to sup if unspecified
        ant (float, optional): the margin in cm in the anterior direction, equal to sup if unspecified
        post (float, optional): the margin in cm in the posterior direction, equal to sup if unspecified
        left (float, optional): the margin in cm in the patient left direction, equal to sup if unspecified
        right (float, optional): the margin in cm in the patient right direction, equal to sup if unspecified
        new_name (str, optional): the name of the newly created expanded ROI.  If unspecified, will default to the *<roi_name>+<sup>mm*.

    Returns:
        the ROI RayStation object corresponding to the expanded volume
    """
    if new_name is None:
        new_name = "%s+%dmm" % (roi_name, sup * 10)
    margins = get_margin_settings(sup, inf, ant, post, left, right)
    patient = lib.get_current_patient()
    examination = lib.get_current_examination()
    source_roi = get_roi(roi_name)
    with CompositeAction('Expand'):
        expanded_roi = patient.PatientModel.CreateRoi(Name=new_name,
                                                      Type="Organ",
                                                      TissueName=None,
                                                      RoiMaterial=None)
        expanded_roi.SetRoiMaterial(Material=source_roi.OfRoi.RoiMaterial)
        expanded_roi.SetMarginExpression(SourceRoiName=roi_name,
                                         MarginSettings=margins)
        expanded_roi.UpdateDerivedGeometry(Examination=examination)
    return expanded_roi
コード例 #24
0
def identify_isocenter_poi():
    """ Attempts to identify isocenter POI.

        The expected name of the isocenter POI is either *ISO* or *ISO SCAN*.
        If either of the two is found, it is returned, with priority to *ISO*.
        If not, the shortest string in the name candidates is returned.

        Returns:
            str: Name of most probable isocenter POI if found, `None` if not.
    """
    patient = lib.get_current_patient()
    poi_names = [r.Name for r in patient.PatientModel.PointsOfInterest]
    iso_candidates = [x for x in poi_names if x.upper().startswith('ISO')]

    # No candidates
    if len(iso_candidates) == 0:
        logger.warning('No isocenter POI could be found.')
        return None

    # Multiple candidates
    if len(iso_candidates) > 1:
        for i in sorted(iso_candidates, key=lambda x: len(x)):
            if i.upper() == 'ISO':
                logger.info('Isocenter POI identified as "%s"', i)
                return i
            if i.upper() == 'ISO SCAN':
                logger.info('Isocenter POI identified as "%s"', i)
                return i

        # If all else fails, return shortest string.
        guess = sorted(iso_candidates, key=lambda x: len(x))[0]
        logger.warning('Best isocenter POI candidate is "%s"', guess)
        return guess

    # Case where there is only 1 candidates
    logger.info('Isocenter POI identified as "%s"', iso_candidates[0])
    return iso_candidates[0]
コード例 #25
0
def get_roi(roi_name, examination=None):
    """
    Returns ROI object from an ROI name for the current structure set.

    Args:
        roi_name (str): the name of the ROI to be returned
        examination (RayStation object, optional): the RayStation object representing the examination

    Returns:
        The ROI RayStation object.
    """
    patient = lib.get_current_patient()

    if examination is None:
        exam = lib.get_current_examination()
    else:
        exam = examination

    try:
        roi = patient.PatientModel.StructureSets[
            exam.Name].RoiGeometries[roi_name]
        if roi.PrimaryShape is None:
            lib.error(
                'ROI %s is not segmented on currently selected examination.  Select correct CT and try again.'
                % roi_name)
    except Exception as e:
        logger.exception(e)
        raise

    # Could have returned roi.OfRoi, but returning roi lets once access the
    # DicomImportHistory, PrimaryShape and GetCenterOfRoi() and GetRoiVolume()
    # functions.
    #
    # Under roi.OfRoi, we have among others Name, Type, Color, RoiMaterial and
    # several functions.  Launch StateTree to see.
    return roi
コード例 #26
0
def get_poi_approval(poi_name, examination=None):
    """
    Checks if POI named *poi_name* is approved (in any of the existing beamsets).

    TODO: handle the case where no beamsets or no plans exists, implying that
    the structures cannot be approved ?

    Args:
        poi_name (str): the name of the POI
        examination (str, optional): the name of the examination (CT or StructureSet) for which the poi is defined; if left out the currently selected examination is used.

    Returns:
        True if the POI is approved, False if not.
    """

    patient = lib.get_current_patient()

    if examination is None:
        examination = lib.get_current_examination()

    # print 'before'

    try:
        for beamset_approved_structure_set in patient.PatientModel.StructureSets[
                examination.Name].ApprovedStructureSets:
            for beamset_approved_pois in beamset_approved_structure_set.ApprovedPoiStructures:
                if beamset_approved_pois.OfPoi.Name == poi_name:
                    # print 'after True'
                    logger.info('POI %s is approved.', poi_name)
                    return True
        # print 'after False'
        logger.info('POI %s is NOT approved.', poi_name)
        return False
    except Exception as e:
        logger.exception(e)
        raise
コード例 #27
0
def get_poi(poi_name, examination=None):
    """
    Returns POI object from a POI name.

    Args:
        poi_name (str): the name of the POI
        examination (str, optional): the name of the examination (CT or StructureSet) for which the poi is defined; if left out the currently selected examination is used.

    Returns:
        the RayStation POI object
    """
    patient = lib.get_current_patient()

    if examination is None:
        examination = lib.get_current_examination()

    try:
        poi = [
            x for x in patient.PatientModel.StructureSets[
                examination.Name].PoiGeometries if x.OfPoi.Name == poi_name
        ][0]

        if abs(poi.Point.x) == sys.float_info.max or abs(
                poi.Point.x) == sys.float_info.min:
            lib.error(
                'POI %s is not segmented on current examination.  Select correct CT and try again.'
                % poi_name)

        return poi
    except IndexError as e:
        logger.error('No POI found named %s', poi_name)
        logger.exception(e)
        raise
    except Exception as e:
        logger.exception(e)
        raise
コード例 #28
0
def check_eccentricity(poi_name):

    #Script to check if a current point can be used as an isocenter without a collision occurring between the gantry and the patient.
    #Simply creates a large cylinder centered on the point and sets the window/level to Lung so the planner can confirm visually that the clearance is adequate.
    #Currently assumes a safe radius of 40cm for all types of linac, this number may change if further measurements are made.

    patient = lib.get_current_patient()
    exam = lib.get_current_examination()

    lung_dict = dict(x=-600, y=1600)
    exam.Series[0].LevelWindow = lung_dict

    if roi.roi_exists("verif_excentricite", exam):
        patient.PatientModel.RegionsOfInterest["verif_excentricite"].DeleteRoi(
        )

    center = get_poi_coordinates(poi_name, exam)

    patient.PatientModel.CreateRoi(Name="verif_ex_temp1",
                                   Color="Green",
                                   Type="Organ",
                                   TissueName=None,
                                   RoiMaterial=None)
    patient.PatientModel.RegionsOfInterest[
        "verif_ex_temp1"].CreateCylinderGeometry(Radius=30,
                                                 Axis={
                                                     'x': 0,
                                                     'y': 0,
                                                     'z': 1
                                                 },
                                                 Length=50,
                                                 Examination=exam,
                                                 Center={
                                                     'x': center.x,
                                                     'y': center.y,
                                                     'z': center.z
                                                 })

    patient.PatientModel.CreateRoi(Name="verif_ex_temp2",
                                   Color="Pink",
                                   Type="Organ",
                                   TissueName=None,
                                   RoiMaterial=None)
    patient.PatientModel.RegionsOfInterest[
        "verif_ex_temp2"].SetMarginExpression(SourceRoiName="verif_ex_temp1",
                                              MarginSettings={
                                                  'Type': "Expand",
                                                  'Superior': 0,
                                                  'Inferior': 0,
                                                  'Anterior': 5,
                                                  'Posterior': 5,
                                                  'Right': 5,
                                                  'Left': 5
                                              })
    patient.PatientModel.RegionsOfInterest[
        "verif_ex_temp2"].UpdateDerivedGeometry(Examination=exam)

    patient.PatientModel.CreateRoi(Name="verif_excentricite",
                                   Color="Red",
                                   Type="Organ",
                                   TissueName=None,
                                   RoiMaterial=None)
    patient.PatientModel.RegionsOfInterest[
        "verif_excentricite"].SetMarginExpression(
            SourceRoiName="verif_ex_temp2",
            MarginSettings={
                'Type': "Expand",
                'Superior': 0,
                'Inferior': 0,
                'Anterior': 5,
                'Posterior': 5,
                'Right': 5,
                'Left': 5
            })
    patient.PatientModel.RegionsOfInterest[
        "verif_excentricite"].UpdateDerivedGeometry(Examination=exam)

    patient.PatientModel.RegionsOfInterest["verif_ex_temp1"].DeleteRoi()
    patient.PatientModel.RegionsOfInterest["verif_ex_temp2"].DeleteRoi()
コード例 #29
0
def place_prescription_point(target_fraction_dose,
                             ptv_name,
                             poi_name,
                             beamset=None,
                             exam=None):
    """
    Attempts to place automatically a POI on a target per-fraction isodose line.

    .. rubric::
      PRE-REQUISITES

    - Existence of an identifiable PTV contour.
    - Dose is calculated.

    .. rubric::
      Algorithm description

    - A point on the PTV contour on a slice near the PTV center is found.
    - The dose is evaluated at that point.
    - An approximate unit vector towards the PTV center is calculated from that position.
        + If the evaluated dose is smaller than the prescription dose, the evaluation point is moved a short distance towards the PTV center.
        + If the evaluated dose is greater than the prescription dose, the evaluation point is moved a short distance away from the PTV center.
    - If the prescription dose is overshot by this procedure, the direction of movement is reversed and the move distance halved.
    - This process is repeated until evaluated dose equals the prescription dose to 3 decimal figures or 100 iterations are reached.

    Finally,

    - The specified POI is placed at the found coordinates.

    .. seealso::
      function `hmrlib.hmrlib.auto_place_prescription_point`

    """

    patient = lib.get_current_patient()
    if exam is None:
        exam = lib.get_current_examination()

    if beamset is None:
        beamset = lib.get_current_beamset()

    try:
        # Get PTV center
        ptv_center = lib.RSPoint(point=patient.PatientModel.StructureSets[
            exam.Name].RoiGeometries[ptv_name].GetCenterOfRoi())

        # Get slice thickness from CT
        slice_width = abs(exam.Series[0].ImageStack.SlicePositions[1] -
                          exam.Series[0].ImageStack.SlicePositions[0])
        logger.info('CT slice width = %s cm', slice_width)

        initial_point = None
        # Find contour point on a slice close to PTV center
        for c in patient.PatientModel.StructureSets[
                exam.Name].RoiGeometries[ptv_name].PrimaryShape.Contours:
            #if lib.check_version(4.7):
            if c[0].z < ptv_center.z + slice_width and c[
                    0].z > ptv_center.z - slice_width:
                initial_point = lib.RSPoint(c[0].x, c[0].y, c[0].z)
                break
            #else:
            #    if c.ContourData[0].z < ptv_center.z + slice_width and c.ContourData[0].z > ptv_center.z - slice_width:
            #        initial_point = lib.RSPoint(c.ContourData[0].x, c.ContourData[0].y, c.ContourData[0].z)
            #        break

        if initial_point is None:
            logger.info(
                'Could not find a point on the same slice as the ROi center. Disjoint/noncoplanar PTV?'
            )
            logger.info('Trying with first point in contour shape.')
            c = patient.PatientModel.StructureSets[
                exam.Name].RoiGeometries[ptv_name].PrimaryShape.Contours[0]
            #if lib.check_version(4.7):
            initial_point = lib.RSPoint(c[0].x, c[0].y, c[0].z)
            #else:
            #    initial_point = lib.RSPoint(c.ContourData[0].x, c.ContourData[0].y, c.ContourData[0].z)

        logger.info('Initial point = %s cm', initial_point)

        u = lib.RSPoint(point=lib.unit_vector(ptv_center - initial_point))
        logger.info('Unit vector towards PTV center = %s cm', u)

        # Change unit vector so that we stay on the same transverse slice
        u.z = 0
        logger.info(
            'Approximate unit vector towards PTV center on single CT slice = %s cm',
            u)

        def move_point(point,
                       direction,
                       target_fraction_dose,
                       initial_dose,
                       step=0.05,
                       iteration=0):
            point = point + step * direction
            dose = beamset.FractionDose.InterpolateDoseInPoint(
                Point=point.value)
            logger.info('Dose at %s = %.3f cGy', point, dose)
            if round(dose, 3) == round(target_fraction_dose,
                                       3) or iteration > 100:
                # Found a suitable point or reached max iterations
                return point
            elif (initial_dose < target_fraction_dose and dose >
                  target_fraction_dose) or (initial_dose > target_fraction_dose
                                            and dose < target_fraction_dose):
                # We overshot the point, so inverse direction and reduce step
                return move_point(point,
                                  -direction,
                                  target_fraction_dose,
                                  dose,
                                  step=0.5 * step,
                                  iteration=iteration + 1)
            else:
                # Keep going in the same direction with same step
                return move_point(point,
                                  direction,
                                  target_fraction_dose,
                                  dose,
                                  step=step,
                                  iteration=iteration + 1)

        dose = beamset.FractionDose.InterpolateDoseInPoint(
            Point=initial_point.value)

        if math.isnan(dose):
            lib.error('No dose value available.  Check if dose is calculated.')

        logger.info('Dose per fraction at initial point = %.3f cGy', dose)

        # Assume dose rises when moving towards center of PTV
        if dose < target_fraction_dose:
            point = move_point(initial_point, u, target_fraction_dose, dose)
        else:
            point = move_point(initial_point, -u, target_fraction_dose, dose)

        logger.info('Final point = %s cm', point)

    except Exception as e:
        logger.exception(e)
        raise

    if get_poi_approval(poi_name):
        logger.warning('POI %s is approved; cannot continue.', poi_name)
        lib.error(
            'POI %s exists and is approved; cannot continue.  Unapprove %s before running script.'
            % (poi_name, poi_name))

    set_poi_coordinates(poi_name, point, examination=exam)

    return point
コード例 #30
0
def subtract_rois(big_roi_name,
                  small_roi_name,
                  color="Yellow",
                  examination=None,
                  output_name=None):
    """Creates ROI that is the subtraction of the two ROIs (useful for creating rings from PTV expansions).

    Args:
        big_roi_name (str): The name of the larger ROI
        small_roi_name (str): The name of the smaller ROI
        color (str): The color of the resulting ROI
        examination (RS Examination object): The CT on which the source and resulting geometries are contoured
        output_name (str): The name that the new contour will have
    Returns:
        RS region of interest structure
    """

    patient = lib.get_current_patient()
    if examination is None:
        examination = lib.get_current_examination()

    if output_name is None:
        newname = "%s-%s" % (big_roi_name, small_roi_name)
    else:
        newname = output_name

    patient.PatientModel.CreateRoi(Name=newname,
                                   Color=color,
                                   Type="Organ",
                                   TissueName=None,
                                   RoiMaterial=None)
    patient.PatientModel.RegionsOfInterest[newname].SetAlgebraExpression(
        ExpressionA={
            'Operation': "Union",
            'SourceRoiNames': [big_roi_name],
            'MarginSettings': {
                'Type': "Expand",
                'Superior': 0,
                'Inferior': 0,
                'Anterior': 0,
                'Posterior': 0,
                'Right': 0,
                'Left': 0
            }
        },
        ExpressionB={
            'Operation': "Union",
            'SourceRoiNames': [small_roi_name],
            'MarginSettings': {
                'Type': "Expand",
                'Superior': 0,
                'Inferior': 0,
                'Anterior': 0,
                'Posterior': 0,
                'Right': 0,
                'Left': 0
            }
        },
        ResultOperation="Subtraction",
        ResultMarginSettings={
            'Type': "Expand",
            'Superior': 0,
            'Inferior': 0,
            'Anterior': 0,
            'Posterior': 0,
            'Right': 0,
            'Left': 0
        })
    patient.PatientModel.RegionsOfInterest[newname].UpdateDerivedGeometry(
        Examination=examination)

    return patient.PatientModel.RegionsOfInterest[newname]