Example #1
0
def livesearch_study(series):
    if series != "FirstLineEmptyLineFromJavascriptPatient":
        # Function to get studies and return them to the interface
        data = RestToolbox.GetPatientData(config.ORTHANC_URL, [series])
        studies = data[0]["Studies"]
        data_s = RestToolbox.GetStudies(config.ORTHANC_URL, studies)
        study_names = []

        for p in data_s:
            try:
                name_id = p["MainDicomTags"]["StudyID"]
            except:
                name_id = ""
            try:
                name_desc = " (" + p["MainDicomTags"]["StudyDescription"] + ")"
            except:
                name_desc = ""
            if name_desc == "" and name_id == "":
                name_id = "Undefined"
            try:
                study_date = " - " + datetime.datetime.strptime(
                    p["MainDicomTags"]["StudyDate"],
                    "%Y%m%d").strftime("%Y-%m-%d")
            except:
                study_date = ""
            study_names.append(name_id + name_desc + study_date)
        return json.dumps((["FirstLineEmptyLineFromJavascriptStudy"] + studies,
                           ["-----------"] + study_names))
    else:
        return json.dumps(
            (["FirstLineEmptyLineFromJavascriptStudy"], ["-----------"]))
Example #2
0
def livesearch_series_tags(s):
    '''Used to find some data for a large series with multiple instances'''
    if s != "FirstLineEmptyLineFromJavascriptSeries":
        p = RestToolbox.GetSeries(config.ORTHANC_URL, [s])[0]
        instances = p["Instances"]
        num_instances = len(instances)
        tags = p["MainDicomTags"]

        try:
            ser_datetime = RestToolbox.GetInstances(config.ORTHANC_URL,
                                                    [p["Instances"][0]])
            datetime_var = RestToolbox.get_datetime(ser_datetime[0])
            tt = datetime_var[0] + datetime_var[1]
            #try:
            #    milisec = "."+tt[15:17]
            #except:
            #    milisec = ".00"
            series_datetime = datetime.datetime.strptime(
                tt[0:14],
                "%Y%m%d%H%M%S").strftime("%Y-%m-%d %H:%M:%S")  #+milisec
        except:
            series_datetime = "Unknown"

        try:
            manufact = tags["Manufacturer"]
        except:
            manufact = "Unknown"
        try:
            modality = tags["Modality"]
        except:
            modality = "Unknown"
        try:
            protocol = tags["ProtocolName"]
        except:
            protocol = "Unknown"
        try:
            station = tags["StationName"]
        except:
            station = "Unknown"

        return json.dumps({
            "1": series_datetime,
            "2": num_instances,
            "3": manufact,
            "4": modality,
            "5": protocol,
            "6": station
        })
    else:
        return json.dumps({
            "1": "",
            "2": "",
            "3": "",
            "4": "",
            "5": "",
            "6": ""
        })
Example #3
0
def getinstanceimagedescription(s):
    # Function to get image description. Used only for some modules (StarShot etc)
    try:
        img_description = RestToolbox.GetImageDescription(
            config.ORTHANC_URL, s, interpretAsJson=False)
    except:
        img_description = "Not available"
    if img_description == None:
        img_description = "Not available"
    return img_description
Example #4
0
def add_referenceimage_catphan():
    machine = request.forms.Machine
    beam = request.forms.Beam
    phantom = request.forms.Phantom
    orthanc_series = request.forms.Series

    new_name = os.path.join(phantom, machine, beam)
    new_folder_name = datetime.datetime.now().strftime("%d-%m-%Y-%H-%M-%S")
    new_path = os.path.join(config.REFERENCE_IMAGES_FOLDER, new_name,
                            new_folder_name)

    # If the sql database already references machine/beam/phantom, then do nothing.
    if general_functions.has_referenceimages_catphan(machine, beam,
                                                     phantom)[0] == 1:
        return "Reference already exists. Remove the reference and retry."

    # Get the dicom images to a temp folder, then copy it to database folder
    try:
        temp_folder = RestToolbox.GetSeries2Folder2(config.ORTHANC_URL,
                                                    orthanc_series)
    except Exception as e:
        return "Could not download files. Check Orthanc settings. " + str(e)

    # Now copy the file to reference_images folder
    # First create new directory for this image
    try:
        if not os.path.isdir(new_path):
            os.makedirs(new_path)
    except Exception as e:
        general_functions.delete_files_in_subfolders([temp_folder])
        return "Could not create room for the images in the repository. " + str(
            e)

    try:
        list_of_files = os.listdir(temp_folder)
        for old_file_name in list_of_files:
            shutil.copyfile(os.path.join(temp_folder, old_file_name),
                            os.path.join(new_path, old_file_name))
    except Exception as e:
        general_functions.delete_files_in_subfolders([temp_folder])
        return "Could not copy downloaded files to repository. " + str(e)

    general_functions.delete_files_in_subfolders([temp_folder])

    # Now save the path to database
    try:
        general_functions.add_referenceimage_catphan(
            machine, beam, phantom, os.path.join(new_name, new_folder_name))
        return "Done!"
    except sql.IntegrityError:
        return "Unit already exists!"
    else:
        return "Failed"
Example #5
0
def livesearch_series(s):
    # Function to get series and return to the interface
    if s != "FirstLineEmptyLineFromJavascriptStudy":
        data = RestToolbox.GetStudies(config.ORTHANC_URL, [s])
        series = data[0]["Series"]
        data_s = RestToolbox.GetSeries(config.ORTHANC_URL, series)
        series_names = []

        for p in data_s:
            try:
                ser_num = " (" + p["MainDicomTags"]["SeriesNumber"] + ")"
            except:
                ser_num = ""
            try:
                ser_desc = p["MainDicomTags"]["SeriesDescription"]
            except:
                ser_desc = ""
            if ser_num == "" and ser_desc == "":
                ser_num = "Undefined"

            ser_datetime = RestToolbox.GetInstances(config.ORTHANC_URL,
                                                    [p["Instances"][0]])

            date_var = RestToolbox.get_datetime(ser_datetime[0])

            if date_var[0] == "Unknown":
                date_str = "Unknown date"
            else:
                date_str = datetime.datetime.strptime(
                    date_var[0], "%Y%m%d").strftime("%Y-%m-%d")  # To string
            series_names.append(ser_desc + ser_num + " - " + date_str)
        return json.dumps((["FirstLineEmptyLineFromJavascriptSeries"] + series,
                           ["-----------"] + series_names))
    else:
        return json.dumps(
            (["FirstLineEmptyLineFromJavascriptSeries"], ["-----------"]))
Example #6
0
def starshot_calculate(imgtype, w):
    # w is the image

    clip_box = float(request.forms.hidden_clipbox)*10.0
    radius = float(request.forms.hidden_radius)
    min_peak_height = float(request.forms.hidden_mph)
    start_x = int(request.forms.hidden_px)
    start_y = int(request.forms.hidden_py)
    dpi = int(request.forms.hidden_dpi)
    sid = float(request.forms.hidden_sid)
    imgdescription = request.forms.hidden_imgdescription
    station = request.forms.hidden_station
    displayname = request.forms.hidden_displayname
    acquisition_datetime = request.forms.hidden_datetime
    
    fwhm = True if request.forms.hidden_fwhm=="true" else False
    recursive = True if request.forms.hidden_recursive=="true" else False
    invert = True if request.forms.hidden_invert=="true" else False
    # Get either dicom or non-dicom file
    if imgtype == "dicom":
        temp_folder, file_path = RestToolbox.GetSingleDcm(config.ORTHANC_URL, w)
    else:
        if request.files.get("input_nondicom_file") is not None:
            upload = request.files.get("input_nondicom_file")
            if os.path.splitext(upload.filename)[1] == ".tif":
                temp_folder = tempfile.mkdtemp(prefix=os.path.splitext(upload.filename)[0]+"_", dir=config.TEMP_NONDCM_FOLDER)
                file_path = os.path.join(temp_folder, upload.filename)
                with open(file_path, "wb") as dst:
                    upload.save(dst, overwrite=False)
                dst.close()
            else:
                return template("error_template", {"error_message": "Please load a valid image file."})
        else:
            return template("error_template", {"error_message": "Please load a valid image file."})

    args = {"imgtype": imgtype, "w": w, "clip_box": clip_box, "radius":radius, "min_peak_height":min_peak_height,
            "start_x": start_x, "start_y":start_y, "dpi":dpi, "sid":sid, "fwhm":fwhm,"recursive":recursive,  "invert":invert, 
            "temp_folder": temp_folder, "file_path":file_path, "imgdescription": imgdescription,
            "station": station, "displayname": displayname, "acquisition_datetime": acquisition_datetime,
            "config": general_functions.get_configuration()}
    p = Pool(1)
    data = p.map(starshot_helperf_catch_error, [args])
    p.close()
    p.join()
    return data
Example #7
0
def picket_fence_calculate(w):
    # w is the image, m is the mlc type

    temp_folder, file_path = RestToolbox.GetSingleDcm(config.ORTHANC_URL, w)
    clip_box = float(request.forms.hidden_clipbox) * 10.0
    py_filter = int(request.forms.hidden_filter)
    py_filter = None if py_filter == 0 else py_filter
    num_pickets = int(request.forms.hidden_peaks)
    num_pickets = None if num_pickets == 0 else num_pickets
    sag = float(request.forms.hidden_sag)
    mlc = request.forms.hidden_mlc
    invert = True if request.forms.hidden_invert == "true" else False
    orientation = request.forms.hidden_orientation
    orientation = None if orientation == "Automatic" else orientation
    imgdescription = request.forms.hidden_imgdescription
    station = request.forms.hidden_station
    displayname = request.forms.hidden_displayname
    acquisition_datetime = request.forms.hidden_datetime

    args = {
        "temp_folder": temp_folder,
        "file_path": file_path,
        "clip_box": clip_box,
        "py_filter": py_filter,
        "num_pickets": num_pickets,
        "sag": sag,
        "mlc": mlc,
        "invert": invert,
        "orientation": orientation,
        "w": w,
        "imgdescription": imgdescription,
        "station": station,
        "displayname": displayname,
        "acquisition_datetime": acquisition_datetime,
        "config": general_functions.get_configuration()
    }
    p = Pool(1)
    data = p.map(picket_fence_helperf_catch_error, [args])
    p.close()
    p.join()
    return data
Example #8
0
def getinstanceimage(s):
    # Function to get png image from the instance and return it to user
    image = RestToolbox.DoGet_image(config.ORTHANC_URL + "/instances/" + s +
                                    "/preview")
    return base64.b64encode(image)
Example #9
0
def livesearch_instances(s):

    if "FirstLineEmptyLineFromJavascriptSeries" not in s:
        # Function to get single dicom files in order of time stamp
        series = s.split("/")
        instances_final_num = []
        num_instances_final = []
        instance_datetime_final = []
        instances_final = []

        inst_temp = []
        # First collect all instances, then order them!
        for ss in series:
            p = RestToolbox.GetSeries(config.ORTHANC_URL, [ss])[0]
            instances = p["Instances"]
            num_instances_final.append(len(instances))
            # GEt all instances for ss series:
            instances_final.append(instances)
            inst_temp.append(
                RestToolbox.GetInstances(config.ORTHANC_URL, instances))

        inst_temp = [val for sublist in inst_temp for val in sublist]
        instances_final = [
            val for sublist in instances_final for val in sublist
        ]

        # Order all instances by date time
        instance_datetime, instance_datetime_order, instance_num = RestToolbox.order_instance_datetime(
            inst_temp)
        order = np.argsort(instance_datetime_order)

        instances_final_num = (np.asarray(instance_num)[order]).tolist()
        instance_datetime_final = (
            np.asarray(instance_datetime)[order]).tolist()
        instances_final = (np.asarray(instances_final)[order]).tolist()

        # Take stationname from last element of series
        try:
            station_name = p["MainDicomTags"]["StationName"]
        except:
            station_name = "Unknown"
        if station_name == None:
            station_name = "Unknown"

        # Take image description tag from last element of instances (MUST DO THIS BETTER!)
        try:
            img_description = inst_temp[-1]["RTImageDescription"]
        except:
            img_description = "Not available"

        if img_description == None:
            try:
                img_description = RestToolbox.GetImageDescription(
                    config.ORTHANC_URL, instances[-1], interpretAsJson=False)
            except:
                img_description = "Not available"
            if img_description == None:
                img_description = "Not available"

        return json.dumps({
            "1": instances_final_num,
            "2": num_instances_final,
            "3": instance_datetime_final,
            "4": instances_final,
            "5": station_name,
            "6": img_description
        })
    else:
        return json.dumps({
            "1": [],
            "2": [],
            "3": [],
            "4": [],
            "5": "",
            "6": ""
        })
Example #10
0
def flatsym_helper(args):
    calc_definition = args["calc_definition"]
    center_definition = args["center_definition"]
    center_x = args["center_x"]
    center_y = args["center_y"]
    invert = args["invert"]
    w = args["instance"]
    station = args["station"]
    imgdescription = args["imgdescription"]
    displayname = args["displayname"]
    acquisition_datetime = args["acquisition_datetime"]

    general_functions.set_configuration(
        args["config"])  # Transfer to this process

    # Collect data for "save results"
    dicomenergy = general_functions.get_energy_from_imgdescription(
        imgdescription)
    user_machine, user_energy = general_functions.get_user_machine_and_energy(
        station, dicomenergy)
    machines_and_energies = general_functions.get_machines_and_energies(
        general_functions.get_treatmentunits_flatsym())
    tolerances = general_functions.get_tolerance_user_machine_flatsym(
        user_machine)  # If user_machne has specific tolerance
    if not tolerances:
        tolerance_flat, tolerance_sym, pdf_report_enable = general_functions.get_settings_flatsym(
        )
    else:
        tolerance_flat, tolerance_sym, pdf_report_enable = tolerances[0]

    tolerance_flat = float(tolerance_flat)
    tolerance_sym = float(tolerance_sym)

    save_results = {
        "user_machine": user_machine,
        "user_energy": user_energy,
        "machines_and_energies": machines_and_energies,
        "displayname": displayname
    }

    temp_folder, file_path = RestToolbox.GetSingleDcm(config.ORTHANC_URL, w)

    try:
        flatsym = FlatSym(file_path)
    except Exception as e:
        return template("error_template", {
            "error_message":
            "The FlatSym module cannot calculate. " + str(e)
        })

    # Define the center pixel where to get profiles:
    def find_field_centroid(img):
        '''taken from pylinac WL module'''
        min, max = np.percentile(img.image.array, [5, 99.9])
        # min, max = np.amin(img.array), np.max(img.array)

        threshold_img = img.image.as_binary((max - min) / 2 + min)
        # clean single-pixel noise from outside field
        coords = ndimage.measurements.center_of_mass(threshold_img)
        return (int(coords[-1]), int(coords[0]))

    flatsym.image.crop(pixels=2)
    flatsym.image.check_inversion()
    if invert:
        flatsym.image.invert()
    flatsym.image.array = np.flipud(flatsym.image.array)
    vmax = flatsym.image.array.max()

    if center_definition == "Automatic":
        center_int = [
            int(flatsym.image.array.shape[1] / 2),
            int(flatsym.image.array.shape[0] / 2)
        ]
        center = [0.5, 0.5]
    elif center_definition == "CAX":
        center_int = find_field_centroid(
            flatsym)  # Define as mechanical isocenter
        center = [
            int(center_int[0]) / flatsym.image.array.shape[1],
            int(center_int[1]) / flatsym.image.array.shape[0]
        ]
    else:
        center_int = [int(center_x), int(center_y)]
        center = [
            int(center_x) / flatsym.image.array.shape[1],
            int(center_y) / flatsym.image.array.shape[0]
        ]

    fig = Figure(figsize=(9, 9), tight_layout={"w_pad": 1})
    ax = fig.add_subplot(1, 1, 1)
    ax.imshow(flatsym.image.array,
              cmap=matplotlib.cm.jet,
              interpolation="none",
              vmin=0.9 * vmax,
              vmax=vmax,
              aspect="equal",
              origin='lower')
    ax.autoscale(tight=True)

    # Plot lines along which the profiles are calculated:
    ax.plot([0, flatsym.image.array.shape[1]], [center_int[1], center_int[1]],
            "b-",
            linewidth=2)
    ax.plot([center_int[0], center_int[0]], [0, flatsym.image.array.shape[0]],
            c="darkgreen",
            linestyle="-",
            linewidth=2)
    ax.set_xlim([0, flatsym.image.array.shape[1]])
    ax.set_ylim([0, flatsym.image.array.shape[0]])

    # Get profiles
    crossplane = PylinacSingleProfile(flatsym.image.array[center_int[1], :])
    inplane = PylinacSingleProfile(flatsym.image.array[:, center_int[0]])

    # Do some filtering:
    crossplane.filter(kind='median', size=0.01)
    inplane.filter(kind='median', size=0.01)

    # Normalize profiles
    norm_val_crossplane = crossplane.values[center_int[0]]
    norm_val_inplane = inplane.values[center_int[1]]
    crossplane.normalize(norm_val=norm_val_crossplane)
    inplane.normalize(norm_val=norm_val_inplane)

    # Get index of CAX of both profiles(different than center) to be used for mirroring:
    fwhm_crossplane = crossplane.fwxm_center(interpolate=True)
    fwhm_inplane = inplane.fwxm_center(interpolate=True)

    # Plot profiles
    divider = make_axes_locatable(ax)

    ax_crossplane = divider.append_axes("bottom",
                                        size="40%",
                                        pad=0.25,
                                        sharex=ax)
    ax_crossplane.set_xlim([0, flatsym.image.array.shape[1]])
    ax_crossplane.set_xticks([])
    ax_crossplane.set_title("Crossplane")

    ax_inplane = divider.append_axes("right", size="40%", pad=0.25, sharey=ax)
    ax_inplane.set_ylim([0, flatsym.image.array.shape[0]])
    ax_inplane.set_yticks([])
    ax_inplane.set_title("Inplane")

    ax_crossplane.plot(crossplane._indices, crossplane, "b-")
    ax_crossplane.plot(2 * fwhm_crossplane - crossplane._indices, crossplane,
                       "b--")
    ax_inplane.plot(inplane, inplane._indices, c="darkgreen", linestyle="-")
    ax_inplane.plot(inplane,
                    2 * fwhm_inplane - inplane._indices,
                    c="darkgreen",
                    linestyle="--")

    ax_inplane.grid(alpha=0.5)
    ax_crossplane.grid(alpha=0.5)
    mpld3.plugins.connect(fig,
                          mpld3.plugins.MousePosition(fontsize=14, fmt=".2f"))

    script = mpld3.fig_to_html(fig, d3_url=D3_URL, mpld3_url=MPLD3_URL)

    if calc_definition == "Elekta":
        method = "elekta"
    else:
        method = "varian"

    try:
        flatsym.analyze(flatness_method=method,
                        symmetry_method=method,
                        vert_position=center[0],
                        horiz_position=center[1])
    except Exception as e:
        return template("error_template",
                        {"error_message": "Analysis failed. " + str(e)})

    symmetry_hor = round(flatsym.symmetry['horizontal']['value'], 2)
    symmetry_vrt = round(flatsym.symmetry['vertical']['value'], 2)
    flatness_hor = round(flatsym.flatness['horizontal']['value'], 2)
    flatness_vrt = round(flatsym.flatness['vertical']['value'], 2)
    horizontal_width = round(
        flatsym.symmetry['horizontal']['profile'].fwxm(interpolate=True) /
        flatsym.image.dpmm, 2)
    vertical_width = round(
        flatsym.symmetry['vertical']['profile'].fwxm(interpolate=True) /
        flatsym.image.dpmm, 2)
    horizontal_penumbra_width = round(
        flatsym.symmetry['horizontal']['profile'].penumbra_width(
            interpolate=True) / flatsym.image.dpmm, 2)
    vertical_penumbra_width = round(
        flatsym.symmetry['vertical']['profile'].penumbra_width(
            interpolate=True) / flatsym.image.dpmm, 2)

    # Check if passed
    if method == "varian":
        if (flatness_hor <
                tolerance_flat) and (flatness_vrt < tolerance_flat) and (
                    symmetry_hor < tolerance_sym) and (symmetry_vrt <
                                                       tolerance_sym):
            passed = True
        else:
            passed = False
    else:
        if (abs(flatness_hor - 100) < tolerance_flat) and (
                abs(flatness_vrt - 100) < tolerance_flat) and (
                    abs(symmetry_hor - 100) < tolerance_sym) and (
                        abs(symmetry_vrt - 100) < tolerance_sym):
            passed = True
        else:
            passed = False

    variables = {
        "symmetry_hor": symmetry_hor,
        "symmetry_vrt": symmetry_vrt,
        "flatness_hor": flatness_hor,
        "flatness_vrt": flatness_vrt,
        "horizontal_width": horizontal_width,
        "vertical_width": vertical_width,
        "horizontal_penumbra_width": horizontal_penumbra_width,
        "vertical_penumbra_width": vertical_penumbra_width,
        "passed": passed,
        "pdf_report_enable": pdf_report_enable,
        "script": script,
        "save_results": save_results,
        "acquisition_datetime": acquisition_datetime,
        "calc_definition": calc_definition
    }

    # Generate pylinac report:
    if pdf_report_enable == "True":
        pdf_file = tempfile.NamedTemporaryFile(delete=False,
                                               prefix="FlatSym",
                                               suffix=".pdf",
                                               dir=config.PDF_REPORT_FOLDER)
        flatsym.publish_pdf(pdf_file)
        variables["pdf_report_filename"] = os.path.basename(pdf_file.name)

    general_functions.delete_files_in_subfolders([temp_folder])  # Delete image

    return template("flatsym_results", variables)
Example #11
0
def fieldsize_helperf(args):
    mlc_type = args["mlc_type"]
    iso_method = args["iso_method"]
    mlc_direction = args["mlc_direction"]
    mlc_points = args["mlc_points"]
    jaw_points = args["jaw_points"]
    mmpd = args["mmpd"]
    cax_x = args["cax_x"]
    cax_y = args["cax_y"]
    clipbox = args["clipbox"]
    invert = args["invert"]
    imgdescription = args["imgdescription"]
    station = args["station"]
    displayname = args["displayname"]
    acquisition_datetime = args["acquisition_datetime"]
    w1 = args["w1"]
    w2 = args["w2"]
    general_functions.set_configuration(args["config"])
    high_contrast = args["high_contrast"]
    filter_size = args["filter_size"]

    # Collect data for "save results"
    dicomenergy = general_functions.get_energy_from_imgdescription(
        imgdescription)
    user_machine, user_energy = general_functions.get_user_machine_and_energy(
        station, dicomenergy)
    machines_and_energies = general_functions.get_machines_and_energies(
        general_functions.get_treatmentunits_fieldsize())
    tolerances = general_functions.get_tolerance_user_machine_fieldsize(
        user_machine)  # If user_machne has specific tolerance
    if not tolerances:
        tt = general_functions.get_settings_fieldsize()
    else:
        tt = tolerances[0]
    (small_nominal, medium_nominal, large_nominal, small_exp_mlc,
     medium_exp_mlc, large_exp_mlc, small_exp_jaw, medium_exp_jaw,
     large_exp_jaw, tolerance_small_mlc, tolerance_medium_mlc,
     tolerance_large_mlc, tolerance_small_jaw, tolerance_medium_jaw,
     tolerance_large_jaw, tolerance_iso) = tt

    small_exp_mlc = float(small_exp_mlc)
    medium_exp_mlc = float(medium_exp_mlc)
    large_exp_mlc = float(large_exp_mlc)
    small_exp_jaw = float(small_exp_jaw)
    medium_exp_jaw = float(medium_exp_jaw)
    large_exp_jaw = float(large_exp_jaw)
    tolerance_small_mlc = float(tolerance_small_mlc)
    tolerance_medium_mlc = float(tolerance_medium_mlc)
    tolerance_large_mlc = float(tolerance_large_mlc)
    tolerance_small_jaw = float(tolerance_small_jaw)
    tolerance_medium_jaw = float(tolerance_medium_jaw)
    tolerance_large_jaw = float(tolerance_large_jaw)
    tolerance_iso = float(tolerance_iso)

    save_results = {
        "user_machine": user_machine,
        "user_energy": user_energy,
        "machines_and_energies": machines_and_energies,
        "displayname": displayname,
        "testtype": ["MLC and Jaws", "Jaws only", "MLC only"]
    }

    try:
        temp_folder1, file_path1 = RestToolbox.GetSingleDcm(
            config.ORTHANC_URL, w1)
        temp_folder2, file_path2 = RestToolbox.GetSingleDcm(
            config.ORTHANC_URL, w2)
    except:
        return template("error_template",
                        {"error_message": "Cannot read images."})

    # Load first image
    try:
        img1 = pylinac_image.DicomImage(file_path1)
        # Here we force pixels to background outside of box:
        if clipbox != 0:
            try:
                img1.check_inversion_by_histogram(percentiles=[
                    4, 50, 96
                ])  # Check inversion otherwise this might not work
                general_functions.clip_around_image(img1, clipbox)
            except Exception as e:
                return template(
                    "error_template",
                    {"error_message": "Unable to apply clipbox. " + str(e)})
        else:
            img1.remove_edges(pixels=2)
        if invert:
            img1.invert()
        else:
            img1.check_inversion()
        img1.flipud()
        if filter_size != 0:
            img1.filter(filter_size)

    except:
        return template("error_template",
                        {"error_message": "Cannot read image."})

    try:
        img2 = pylinac_image.DicomImage(file_path2)
        if clipbox != 0:
            try:
                img2.check_inversion_by_histogram(percentiles=[
                    4, 50, 96
                ])  # Check inversion otherwise this might not work
                general_functions.clip_around_image(img2, clipbox)
            except Exception as e:
                return template(
                    "error_template",
                    {"error_message": "Unable to apply clipbox. " + str(e)})
        else:
            img2.remove_edges(pixels=2)
        if invert:
            img2.invert()
        else:
            img2.check_inversion()
        img2.flipud()
        if filter_size != 0:
            img1.filter(filter_size)
    except:
        return template("error_template",
                        {"error_message": "Cannot read image."})

    # FIRST IMAGE (to get isocenter):
    if iso_method == "Manual":
        center = (float(cax_x), float(cax_y))

    elif iso_method == "Plate (Elekta)":
        img1_copy = copy.copy(
            img1)  # Copy because you will need the original later

        reg = MLC_fieldsize._get_canny_regions(img1)
        size = [r.area for r in reg]
        sort = np.argsort(size)[::-1]
        # Get the region with the right area:
        for s in range(0, len(reg), 1):
            if 0.9 < (reg[sort[s]].image.size /
                      (img1.dpmm * img1.dpmm) / 31420) < 1.1:
                break
        max_area_index = sort[s]
        bb_box = reg[
            max_area_index].bbox  # (min_row, min_col, max_row, max_col)
        margin = 15  # pixels

        img1.array = img1.array[bb_box[0] + margin:bb_box[2] - margin,
                                bb_box[1] + margin:bb_box[3] - margin]
        center = MLC_fieldsize._find_plate_center(
            img1)  # This is chosen as the mechanical center!!!

        filter_size = 0.05  # Don't go higher!
        sample_length = 15  # mm to both sides from the center!
        sample_box = 5  # Half the number of lines per averaged profile minus one(must be odd number)
        hor_margin = 50
        vrt_margin = 30

        # Vertical profiles (two regions):
        samples_vertical = np.rint(center[0] - sample_length * img1.dpmm +
                                   np.arange(1, 2 * sample_length * img1.dpmm +
                                             1, 2 * sample_box +
                                             1)).astype(int)
        up_end = np.rint(center[1] - vrt_margin * img1.dpmm).astype(int)
        down_start = np.rint(center[1] + vrt_margin * img1.dpmm).astype(int)
        width_vert_up, fwhm_center_vert_up, width_vert_down, fwhm_center_vert_down = MLC_fieldsize.plate_ud_width(
            img1, samples_vertical, filter_size, up_end, down_start,
            sample_box)

        # Horizontal profiles (two regions):
        samples_horizontal = np.rint(
            center[1] - sample_length * img1.dpmm +
            np.arange(1, 2 * sample_length * img1.dpmm + 1, 2 * sample_box +
                      1)).astype(int)
        left_end = np.rint(center[0] - hor_margin * img1.dpmm).astype(int)
        right_start = np.rint(center[0] + hor_margin * img1.dpmm).astype(int)
        width_hor_left, fwhm_center_hor_left, width_hor_right, fwhm_center_hor_right = MLC_fieldsize.plate_lr_width(
            img1, samples_horizontal, filter_size, left_end, right_start,
            sample_box)
        vrt_px = (np.average(width_vert_up) + np.average(width_vert_down)) / 2
        hor_px = (np.average(width_hor_left) + np.average(width_hor_right)) / 2
        center_fwhm_x = (np.average(fwhm_center_hor_left) + right_start +
                         np.average(fwhm_center_hor_right)) / 2
        center_fwhm_y = (np.average(fwhm_center_vert_up) + down_start +
                         np.average(fwhm_center_vert_down)) / 2

        # Redefine center to the original image:
        center_plate = [center_fwhm_x, center_fwhm_y]
        center = (bb_box[1] + margin + center_plate[0],
                  bb_box[0] + margin + center_plate[1])

    elif iso_method == "BB":
        center, bb_box = MLC_fieldsize._find_field_centroid(img1)
        if high_contrast:
            bb_coord, bw_bb_im = MLC_fieldsize._find_bb2(
                img1, bb_box)  # Define as mechanical isocenter
        else:
            bb_coord, bw_bb_im = MLC_fieldsize._find_bb(
                img1, bb_box)  # Define as mechanical isocenter
        center = (bb_coord[0], bb_coord[1])

    elif iso_method == "CAX":
        center, bb_box = MLC_fieldsize._find_field_centroid(
            img1)  # Define as mechanical isocenter

    elif iso_method == "Opposing coll angle":
        # Two fields with opposing collimator angles can be used to define the coll axis
        center, bb_box = MLC_fieldsize._find_field_centroid(
            img1)  # Same as any field but now:
        # Go to line below where center is redefined to account for image 2 CAX.

    # Now calculate field size from second image
    # Calculate mlc and jaw positions
    # Define pixel size. Use either that from the dcm file or calculate it via Elekta plate:
    if iso_method == "Plate (Elekta)":
        dpmm = (vrt_px / 20.0 + hor_px / 20.0) / 2
    else:
        if mmpd != 0:
            dpmm = 1.0 / mmpd
        else:
            dpmm = img2.dpmm

    center_rad, bb_box2 = MLC_fieldsize._find_field_centroid(img2)

    # If Opposing coll angle is used for isocenter definition, redefine center:
    if iso_method == "Opposing coll angle":
        center[0], center[1] = (center[0] + center_rad[0]) / 2.0, (
            center[1] + center_rad[1]) / 2.0
    marg_bb = 10  # This additional margin is included in the caluclation of bb_box2. Subtract it when needed!

    nr_leaf_sample_points = int(mlc_points)
    nr_jaw_sample_points = int(jaw_points)
    leaf_scaling = dpmm
    mlc_points = MLC_fieldsize.sample_points_mlc(nr_leaf_sample_points,
                                                 leaf_type=mlc_type)
    # Sample mlc-s according o mlc_points, sample jaws equidistanly with center at rad center (derived from bb_box2)

    if mlc_direction == "X":
        # MLCS are horizontal
        center_pixel_mlc = center[0]
        center_pixel_jaws = center[1]
        mlc_pixels = MLC_fieldsize.points_to_pixels_mlc(
            leaf_scaling, mlc_points, bb_box2[0] + marg_bb,
            bb_box2[1] - marg_bb, center_pixel_jaws)
        jaw_pixels = MLC_fieldsize.sample_pixels_jaws(nr_jaw_sample_points,
                                                      bb_box2[2] + marg_bb,
                                                      bb_box2[3] - marg_bb)
    else:
        # MLCs are vertical
        center_pixel_mlc = center[1]
        center_pixel_jaws = center[0]
        mlc_pixels = MLC_fieldsize.points_to_pixels_mlc(
            leaf_scaling, mlc_points, bb_box2[2] + marg_bb,
            bb_box2[3] - marg_bb, center_pixel_jaws)
        jaw_pixels = MLC_fieldsize.sample_pixels_jaws(nr_jaw_sample_points,
                                                      bb_box2[0] + marg_bb,
                                                      bb_box2[1] - marg_bb)

    penL, penR = MLC_fieldsize.calculate_penumbra_pixels_mlc(
        img2, mlc_pixels, mlc_direction)
    penL_abs = np.abs(penL - center_pixel_mlc)
    penL_abs_avg = np.average(penL_abs, axis=1)
    penR_abs = np.abs(penR - center_pixel_mlc)
    penR_abs_avg = np.average(penR_abs, axis=1)
    widths_mlc = np.abs(penL - penR)
    widths_mlc_avg = np.average(widths_mlc, axis=1)

    penL_jaw, penR_jaw = MLC_fieldsize.calculate_penumbra_pixels_jaws(
        img2, jaw_pixels, mlc_direction)
    penL_jaw_abs = np.abs(penL_jaw - center_pixel_jaws)
    penR_jaw_abs = np.abs(penR_jaw - center_pixel_jaws)
    widths_jaw = np.abs(penL_jaw - penR_jaw)

    # Skewness of the rectangle (a measure of collimator angle):
    # linear regression of mlc/jaw points (except the first and the last) y=mx+c
    first_leaf = np.rint(
        np.searchsorted((mlc_pixels - center_pixel_jaws).flatten(), 0) /
        mlc_pixels.shape[1]).astype(int)
    leaf_numbers = np.hstack(
        (-np.arange(1, first_leaf + 1, 1)[::-1],
         np.arange(1, mlc_pixels.shape[0] - first_leaf + 1, 1)))

    temp_mlc = np.vstack([
        (mlc_pixels[1:-1, :].flatten() - center_pixel_mlc) / dpmm,
        np.ones(len(mlc_pixels[1:-1, :].flatten()))
    ]).T
    m_mlcL, c_mlcL = np.linalg.lstsq(temp_mlc,
                                     penL_abs[1:-1, :].flatten() / dpmm,
                                     rcond=None)[0]
    m_mlcR, c_mlcR = np.linalg.lstsq(temp_mlc,
                                     penR_abs[1:-1, :].flatten() / dpmm,
                                     rcond=None)[0]
    temp_jaws = np.vstack([(jaw_pixels[1:-1] - center_pixel_jaws) / dpmm,
                           np.ones(len(jaw_pixels[1:-1]))]).T
    m_jawL, c_jawL = np.linalg.lstsq(temp_jaws,
                                     penL_jaw_abs[1:-1] / dpmm,
                                     rcond=None)[0]
    m_jawR, c_jawR = np.linalg.lstsq(temp_jaws,
                                     penR_jaw_abs[1:-1] / dpmm,
                                     rcond=None)[0]

    # Not do some heavy plotting
    size = 7
    fig1 = Figure(figsize=(size, size), tight_layout={"w_pad": 1, "pad": 1})
    ax1 = fig1.add_subplot(1, 1, 1)

    if iso_method == "BB":
        ax1.imshow(img1.array,
                   cmap=matplotlib.cm.Greys,
                   interpolation="none",
                   origin="lower",
                   extent=[0, img1.shape[1], 0, img1.shape[0]])
        ax1.plot(center[0],
                 center[1],
                 'b+',
                 markersize=24,
                 markeredgewidth=3,
                 zorder=2)
        border = np.average(np.percentile(bw_bb_im, [5, 99.9]))
        ax1.contour(bw_bb_im, levels=[border], colors=["red"])  # BB
        ax1.set_ylim(0, img1.shape[0])
        ax1.set_xlim(0, img1.shape[1])
        ax1.autoscale(enable=False)

    if iso_method == "Plate (Elekta)":
        ax1.imshow(img1_copy.array,
                   cmap=matplotlib.cm.prism_r,
                   interpolation="none",
                   origin="lower",
                   extent=[0, img1_copy.shape[1], 0, img1_copy.shape[0]])
        ax1.plot(center[0],
                 center[1],
                 'b+',
                 markersize=24,
                 markeredgewidth=3,
                 zorder=2)
        ax1.plot([left_end + center[0] - center_plate[0]] *
                 len(samples_horizontal),
                 samples_horizontal + center[1] - center_plate[1],
                 'wo',
                 markersize=3,
                 markeredgewidth=0,
                 zorder=2)
        ax1.plot([right_start + center[0] - center_plate[0]] *
                 len(samples_horizontal),
                 samples_horizontal + center[1] - center_plate[1],
                 'wo',
                 markersize=3,
                 markeredgewidth=0,
                 zorder=2)
        ax1.plot(samples_vertical + center[0] - center_plate[0],
                 [up_end + center[1] - center_plate[1]] *
                 len(samples_vertical),
                 'wo',
                 markersize=3,
                 markeredgewidth=0,
                 zorder=2)
        ax1.plot(samples_vertical + center[0] - center_plate[0],
                 [down_start + center[1] - center_plate[1]] *
                 len(samples_vertical),
                 'wo',
                 markersize=3,
                 markeredgewidth=0,
                 zorder=2)
        ax1.set_ylim(bb_box[0] + margin, bb_box[2] - margin)
        ax1.set_xlim(bb_box[1] + margin, bb_box[3] - margin)
        ax1.autoscale(enable=False)

    if iso_method == "Manual" or iso_method == "CAX" or iso_method == "Opposing coll angle":
        ax1.imshow(img1.array,
                   cmap=matplotlib.cm.prism_r,
                   interpolation="none",
                   origin="lower",
                   extent=[0, img1.shape[1], 0, img1.shape[0]])
        ax1.plot(center[0],
                 center[1],
                 'b+',
                 markersize=24,
                 markeredgewidth=3,
                 zorder=2)
        ax1.set_ylim(0, img1.shape[0])
        ax1.set_xlim(0, img1.shape[1])
        ax1.autoscale(enable=False)

    mpld3.plugins.connect(fig1,
                          mpld3.plugins.MousePosition(fontsize=14, fmt=".1f"))
    script1 = mpld3.fig_to_html(fig1, d3_url=D3_URL, mpld3_url=MPLD3_URL)

    # Second plot
    fig2 = Figure(figsize=(size, size), tight_layout={"w_pad": 1, "pad": 1})

    ax2 = fig2.add_subplot(1, 1, 1)
    ax2.imshow(img2.array,
               cmap=matplotlib.cm.Greys,
               interpolation="none",
               origin="lower",
               extent=[0, img2.shape[1], 0, img2.shape[0]])
    ax2.plot(center[0],
             center[1],
             marker='+',
             color="dodgerblue",
             markersize=24,
             markeredgewidth=3)
    level = np.average(np.percentile(img2.array, [5, 99.9]))
    ax2.contour(img2.array,
                levels=[level],
                colors=["magenta"],
                linewidths=1,
                alpha=0.7,
                zorder=1)
    ax2.plot(center_rad[0],
             center_rad[1],
             'm+',
             markersize=24,
             markeredgewidth=3,
             zorder=2)
    ax2.plot([None], [None],
             marker='+',
             color="dodgerblue",
             ms=15,
             mew=3,
             label="Mechanical")
    ax2.plot([None], [None], "m+", ms=15, mew=3, label="Radiation")
    ax2.plot([0, img2.shape[1]], [center[1], center[1]],
             linestyle="--",
             color="dodgerblue",
             alpha=0.5)
    ax2.plot([center[0], center[0]], [0, img2.shape[0]],
             linestyle="--",
             color="dodgerblue",
             alpha=0.5)
    ax2.legend(framealpha=0, numpoints=1, ncol=2, loc='lower left', fontsize=8)

    m1s = 5
    ms2 = 6
    if mlc_direction == "X":
        m1 = ax2.plot(penL.flatten(),
                      mlc_pixels.flatten(),
                      'ro',
                      markersize=m1s,
                      markeredgewidth=0,
                      zorder=2)
        m2 = ax2.plot(penR.flatten(),
                      mlc_pixels.flatten(),
                      'bo',
                      markersize=m1s,
                      markeredgewidth=0,
                      zorder=2)
        j1 = ax2.plot(jaw_pixels,
                      penL_jaw,
                      'go',
                      markersize=ms2,
                      markeredgewidth=0,
                      zorder=2)
        j2 = ax2.plot(jaw_pixels,
                      penR_jaw,
                      'yo',
                      markersize=ms2,
                      markeredgewidth=0,
                      zorder=2)
        #ax2.plot(np.average((penL+penR)/2), np.average((penL_jaw+penR_jaw)/2), 'y+', markersize=24, markeredgewidth=3, zorder=2)
    else:
        m1 = ax2.plot(mlc_pixels.flatten(),
                      penL.flatten(),
                      'ro',
                      markersize=m1s,
                      markeredgewidth=0,
                      zorder=2)
        m2 = ax2.plot(mlc_pixels.flatten(),
                      penR.flatten(),
                      'bo',
                      markersize=m1s,
                      markeredgewidth=0,
                      zorder=2)
        j1 = ax2.plot(penL_jaw,
                      jaw_pixels,
                      'go',
                      markersize=ms2,
                      markeredgewidth=0,
                      zorder=2)
        j2 = ax2.plot(penR_jaw,
                      jaw_pixels,
                      'yo',
                      markersize=ms2,
                      markeredgewidth=0,
                      zorder=2)
        #ax2.plot(np.average((penL_jaw+penR_jaw)/2), np.average((penL+penR)/2), 'y+', markersize=24, markeredgewidth=3, zorder=2)

    labels_m1 = [
        "Distance from center = {:04.2f} mm, width = {:04.2f} mm".format(
            penL_abs.flatten()[k] / dpmm,
            widths_mlc.flatten()[k] / dpmm)
        for k in range(0, len(penL_abs.flatten()), 1)
    ]
    labels_m2 = [
        "Distance from center = {:04.2f} mm, width = {:04.2f} mm".format(
            penR_abs.flatten()[k] / dpmm,
            widths_mlc.flatten()[k] / dpmm)
        for k in range(0, len(penR_abs.flatten()), 1)
    ]
    labels_j1 = [
        "Distance from center = {:04.2f} mm, width = {:04.2f} mm".format(
            penL_jaw_abs[k] / dpmm, widths_jaw[k] / dpmm)
        for k in range(0, len(penL_jaw_abs), 1)
    ]
    labels_j2 = [
        "Distance from center = {:04.2f} mm, width = {:04.2f} mm".format(
            penR_jaw_abs[k] / dpmm, widths_jaw[k] / dpmm)
        for k in range(0, len(penR_jaw_abs), 1)
    ]
    ttip1 = mpld3.plugins.PointLabelTooltip(m1[0],
                                            labels_m1,
                                            location='top left')
    ttip2 = mpld3.plugins.PointLabelTooltip(m2[0],
                                            labels_m2,
                                            location='top left')
    ttip3 = mpld3.plugins.PointLabelTooltip(j1[0],
                                            labels_j1,
                                            location='top left')
    ttip4 = mpld3.plugins.PointLabelTooltip(j2[0],
                                            labels_j2,
                                            location='top left')

    margin_imshow = 35
    ax2.set_ylim(bb_box2[0] - margin_imshow, bb_box2[1] + margin_imshow)
    ax2.set_xlim(bb_box2[2] - margin_imshow, bb_box2[3] + margin_imshow)
    ax2.autoscale(enable=False)

    mpld3.plugins.connect(fig2,
                          mpld3.plugins.MousePosition(fontsize=14, fmt=".1f"))

    mpld3.plugins.connect(fig2, ttip1)
    mpld3.plugins.connect(fig2, ttip2)
    mpld3.plugins.connect(fig2, ttip3)
    mpld3.plugins.connect(fig2, ttip4)

    script2 = mpld3.fig_to_html(fig2, d3_url=D3_URL, mpld3_url=MPLD3_URL)

    # Third plot
    fig3 = Figure(figsize=(10, 9), tight_layout={"w_pad": 2, "pad": 2})
    ax3 = fig3.add_subplot(3, 1, 1)
    ax4 = fig3.add_subplot(3, 1, 2)
    ax5 = fig3.add_subplot(3, 1, 3)

    ax3.plot(np.arange(0, len(leaf_numbers), 1),
             penR_abs_avg / dpmm,
             "bo-",
             linewidth=0.8)
    ax3.set_xticks(np.arange(0, len(leaf_numbers), 1))
    ax3.set_xticklabels([])
    ax3.grid(linestyle='dotted', color="gray")
    ax3.set_title("Right leaf distance from center [mm]")
    ax3.margins(0.05)

    ax4.plot(np.arange(0, len(leaf_numbers), 1),
             -penL_abs_avg / dpmm,
             "ro-",
             linewidth=0.8)
    ax4.set_xticks(np.arange(0, len(leaf_numbers), 1))
    ax4.grid(linestyle='dotted', color="gray")
    ax4.set_xticklabels([])
    ax4.set_title("Left leaf distance from center [mm]")
    ax4.margins(0.05)

    ax5.plot(np.arange(0, len(leaf_numbers), 1),
             widths_mlc_avg / dpmm,
             "ko-",
             linewidth=0.8)
    ax5.set_xticks(np.arange(0, len(leaf_numbers), 1))
    ax5.set_xticklabels(leaf_numbers)
    ax5.grid(linestyle='dotted', color="gray")
    ax5.set_xlabel("Leaf index")
    ax5.set_title("Distance between leaves [mm]")
    ax5.margins(0.05)

    script3 = mpld3.fig_to_html(fig3, d3_url=D3_URL, mpld3_url=MPLD3_URL)

    # Fourth plot
    fig4 = Figure(figsize=(10, 9), tight_layout={"w_pad": 2, "pad": 2})
    ax6 = fig4.add_subplot(3, 1, 1)
    ax7 = fig4.add_subplot(3, 1, 2)
    ax8 = fig4.add_subplot(3, 1, 3)

    ax6.plot(np.arange(0, len(penR_jaw_abs), 1),
             penR_jaw_abs / dpmm,
             "yo-",
             linewidth=0.8)
    ax6.set_xticks(np.arange(0, len(penR_jaw_abs), 1))
    ax6.set_xticklabels([])
    ax6.grid(linestyle='dotted', color="gray")
    ax6.set_title("Right jaw distance from center [mm]")
    ax6.margins(0.05)

    ax7.plot(np.arange(0, len(penL_jaw_abs), 1),
             -penL_jaw_abs / dpmm,
             "go-",
             linewidth=0.8)
    ax7.set_xticks(np.arange(0, len(penL_jaw_abs), 1))
    ax7.grid(linestyle='dotted', color="gray")
    ax7.set_xticklabels([])
    ax7.set_title("Left jaw distance from center [mm]")
    ax7.margins(0.05)

    ax8.plot(np.arange(0, len(widths_jaw), 1),
             widths_jaw / dpmm,
             "ko-",
             linewidth=0.8)
    ax8.set_xticks(np.arange(0, len(widths_jaw), 1))
    ax8.set_xticklabels(np.arange(1, len(widths_jaw) + 1, 1))
    ax8.grid(linestyle='dotted', color="gray")
    ax8.set_xlabel("Jaw sample point")
    ax8.set_title("Distance between jaws [mm]")
    ax8.margins(0.05)

    script4 = mpld3.fig_to_html(fig4, d3_url=D3_URL, mpld3_url=MPLD3_URL)

    # Caclculate stuff for the web interface
    MLC_size_full = np.average(widths_mlc_avg[1:-1] / dpmm)
    jaw_size_full = np.average(widths_jaw[1:-1] / dpmm)

    #sizes_nominal = np.array([small_nominal, medium_nominal, large_nominal])
    size_nominal_names = ["Small field", "Medium field", "Large field"]
    sizes_exp_mlc = np.array([small_exp_mlc, medium_exp_mlc, large_exp_mlc
                              ]) * 10.0
    sizes_exp_jaw = np.array([small_exp_jaw, medium_exp_jaw, large_exp_jaw
                              ]) * 10.0
    tolerances_list_mlc = np.array([
        tolerance_small_mlc, tolerance_medium_mlc, tolerance_large_mlc
    ]) * 10.0
    tolerances_list_jaw = np.array([
        tolerance_small_jaw, tolerance_medium_jaw, tolerance_large_jaw
    ]) * 10.0

    # Guess which field was chosen
    guess_ind_mlc = np.argmin(np.abs(sizes_exp_mlc - MLC_size_full))
    guess_ind_jaw = np.argmin(np.abs(sizes_exp_jaw - jaw_size_full))
    guessed_exp_mlc = sizes_exp_mlc[guess_ind_mlc]
    guessed_exp_jaw = sizes_exp_jaw[guess_ind_jaw]
    guessed_nominal_name = size_nominal_names[guess_ind_mlc]

    tolerance_final_mlc = tolerances_list_mlc[guess_ind_mlc]
    tolerance_final_jaw = tolerances_list_jaw[guess_ind_jaw]
    tolerance_iso = tolerance_iso * 10.0  # Redefine to mm

    if abs(guessed_exp_mlc - MLC_size_full) <= tolerance_final_mlc:
        passed_mlc = True
    else:
        passed_mlc = False

    if abs(guessed_exp_jaw - jaw_size_full) <= tolerance_final_jaw:
        passed_jaw = True
    else:
        passed_jaw = False

    if (center_rad[0] - center[0])**2 + (
            center_rad[1] - center[1])**2 <= dpmm * dpmm * tolerance_iso**2:
        passed_iso = True
    else:
        passed_iso = False

    variables = {
        "script1": script1,
        "script2": script2,
        "script3": script3,
        "script4": script4,
        "MLC_position_L": penL_abs_avg / dpmm,
        "MLC_position_R": penR_abs_avg / dpmm,
        "MLC_width": widths_mlc_avg / dpmm,
        "jaw_position_L": penL_jaw_abs / dpmm,
        "jaw_position_R": penR_jaw_abs / dpmm,
        "jaw_width": widths_jaw / dpmm,
        "leaf_numbers": leaf_numbers,
        "angle_mlc_L": np.arctan(m_mlcL) * 180 / PI,
        "angle_mlc_R": -np.arctan(m_mlcR) * 180 / PI,  # changed sign!
        "angle_jaw_L": -np.arctan(m_jawL) * 180 / PI,  # changed sign!
        "angle_jaw_R": np.arctan(m_jawR) * 180 / PI,
        "MLC_size_L": np.average(penL_abs_avg[1:-1] / dpmm),
        "MLC_size_R": np.average(penR_abs_avg[1:-1] / dpmm),
        "MLC_size_full": MLC_size_full,
        "jaw_size_L": np.average(penL_jaw_abs[1:-1] / dpmm),
        "jaw_size_R": np.average(penR_jaw_abs[1:-1] / dpmm),
        "jaw_size_full": jaw_size_full,
        "center_offset_x": (center_rad[0] - center[0]) / dpmm,
        "center_offset_y": (center_rad[1] - center[1]) / dpmm,
        "dpmm": 1 / dpmm,
        "center": center,
        "mlc_direction": mlc_direction,
        "center_rad": center_rad,
        "save_results": save_results,
        "passed_mlc": passed_mlc,
        "passed_jaw": passed_jaw,
        "passed_iso": passed_iso,
        "tolerance_mlc": tolerance_final_mlc,
        "tolerance_jaw": tolerance_final_jaw,
        "tolerance_iso": tolerance_iso,
        "expected_mlc": guessed_exp_mlc,
        "expected_jaw": guessed_exp_jaw,
        "guessed_fieldsize": guessed_nominal_name,
        "acquisition_datetime": acquisition_datetime,
        "iso_method": iso_method
    }
    general_functions.delete_files_in_subfolders([temp_folder1, temp_folder2
                                                  ])  # Delete image
    return template("fieldsize_results", variables)
Example #12
0
def main():
    # This function acts as a console script entry-point.

    if len(sys.argv) != 3 or not check_ip(sys.argv[1]):
        print("Invalid ip:port. Follow this example: \n"
              "pyqaserver 127.0.0.1:8080 \path_to_database")
        sys.exit()

    if not check_database(sys.argv[2]):
        print("Path to database directory does not exist.")
        sys.exit()

    # Redefine folder path in config.py
    path = sys.argv[2]
    config.WORKING_DIRECTORY = path

    # Create new general_settings database.
    config.GENERAL_DATABASE = os.path.join(path, config.GENERAL_DATABASE_NAME)
    if not os.path.exists(config.GENERAL_DATABASE):
        create_general_settings_database(config.GENERAL_DATABASE)
        print("Created {}".format(config.GENERAL_DATABASE))
    else:
        print("Using {}".format(config.GENERAL_DATABASE))

    # Create new trends database.
    config.TRENDS_DATABASE = os.path.join(path, config.TRENDS_DATABASE_NAME)
    if not os.path.exists(config.TRENDS_DATABASE):
        create_trends_database(config.TRENDS_DATABASE)
        print("Created {}".format(config.TRENDS_DATABASE))
    else:
        print("Using {}".format(config.TRENDS_DATABASE))

    # Set Orthanc ip address and credentials:
    orthanc_settings = get_orthanc_settings(config.GENERAL_DATABASE)
    config.ORTHANC_IP = orthanc_settings["IP"]
    config.ORTHANC_PORT = orthanc_settings["Port"]
    config.USERNAME_ORTHANC = orthanc_settings["User"]
    config.PASSWORD_ORTHANC = orthanc_settings["Password"]
    config.ORTHANC_URL = "http://{}:{}".format(str(config.ORTHANC_IP),
                                               str(config.ORTHANC_PORT))
    RestToolbox.SetCredentials(config.USERNAME_ORTHANC,
                               config.PASSWORD_ORTHANC)

    # Set institution name (not used a lot):
    institution_setttings = get_institution_settings(config.GENERAL_DATABASE)
    config.INSTITUTION = institution_setttings["Name"]

    ###########################################################################
    # Create new reference images folder
    config.REFERENCE_IMAGES_FOLDER = os.path.join(
        path, config.REFERENCE_IMAGES_FOLDER_NAME)
    if os.path.isdir(config.REFERENCE_IMAGES_FOLDER):
        print("Using {}".format(config.REFERENCE_IMAGES_FOLDER))
    else:
        os.makedirs(config.REFERENCE_IMAGES_FOLDER)
        print("Created {}".format(config.REFERENCE_IMAGES_FOLDER))

    ###########################################################################
    # Create dynalog folders and files
    dynpath = os.path.join(path, config.DYNALOG_FOLDER_NAME)
    config.DYNALOG_FOLDER = dynpath
    config.DYNALOG_DATABASE = os.path.join(dynpath,
                                           config.DYNALOG_DATABASE_NAME)
    config.DYNALOG_CONFIG = os.path.join(dynpath, config.DYNALOG_CONFIG_NAME)
    config.DYNALOG_ARCHIVE = os.path.join(dynpath,
                                          config.DYNALOG_ARCHIVE_FOLDER_NAME)
    config.DYNALOG_FAILED = os.path.join(dynpath,
                                         config.DYNALOG_FAILED_FOLDER_NAME)
    config.DYNALOG_SEND_POST = os.path.join(dynpath,
                                            config.DYNALOG_SEND_POST_NAME)

    if os.path.isdir(dynpath):
        print("Using {}".format(dynpath))

        # Create config.ini
        if not os.path.exists(config.DYNALOG_CONFIG):
            create_dynalog_config_ini(config.DYNALOG_CONFIG)
            print("Created {}".format(config.DYNALOG_CONFIG))
        else:
            print("Using {}".format(config.DYNALOG_CONFIG))

        # Create dynalog_send_post_request.py
        if not os.path.exists(config.DYNALOG_SEND_POST):
            create_dynalog_send_post_request_file(config.DYNALOG_SEND_POST)
            print("Created {}".format(config.DYNALOG_SEND_POST))
        else:
            print("Using {}".format(config.DYNALOG_SEND_POST))

        # Create database.db
        if not os.path.exists(config.DYNALOG_DATABASE):
            create_dynalog_database(config.DYNALOG_DATABASE)
            print("Created {}".format(config.DYNALOG_DATABASE))
        else:
            print("Using {}".format(config.DYNALOG_DATABASE))

        # Create ARCHIVE directory
        if not os.path.isdir(config.DYNALOG_ARCHIVE):
            os.makedirs(config.DYNALOG_ARCHIVE)
            print("Created {}".format(config.DYNALOG_ARCHIVE))
        else:
            print("Using {}".format(config.DYNALOG_ARCHIVE))

        # Create dynalog_failed directory
        if not os.path.isdir(config.DYNALOG_FAILED):
            os.makedirs(config.DYNALOG_FAILED)
            print("Created {}".format(config.DYNALOG_FAILED))
        else:
            print("Using {}".format(config.DYNALOG_FAILED))
    else:
        os.makedirs(dynpath)
        os.makedirs(config.DYNALOG_ARCHIVE)
        os.makedirs(config.DYNALOG_FAILED)
        create_dynalog_config_ini(config.DYNALOG_CONFIG)
        create_dynalog_send_post_request_file(config.DYNALOG_SEND_POST)
        create_dynalog_database(config.DYNALOG_DATABASE)
        print("Created {}".format(dynpath))
        print("Created {}".format(config.DYNALOG_CONFIG))
        print("Created {}".format(config.DYNALOG_SEND_POST))
        print("Created {}".format(config.DYNALOG_DATABASE))
        print("Created {}".format(config.DYNALOG_ARCHIVE))
        print("Created {}".format(config.DYNALOG_FAILED))

    ###########################################################################
    # Create temp directories
    temp_path = os.path.join(path, config.TEMP_DIRECTORY_NAME)
    config.TEMP_DIRECTORY = temp_path
    config.TEMP_DCM_FOLDER = os.path.join(temp_path,
                                          config.TEMP_DCM_FOLDER_NAME)
    config.TEMP_NONDCM_FOLDER = os.path.join(temp_path,
                                             config.TEMP_NONDCM_FOLDER_NAME)
    config.TEMP_DYNALOG_FOLDER = os.path.join(temp_path,
                                              config.TEMP_DYNALOG_FOLDER_NAME)
    config.PDF_REPORT_FOLDER = os.path.join(temp_path,
                                            config.PDF_REPORT_FOLDER_NAME)

    if os.path.isdir(temp_path):
        # Create DCM temp directory
        if not os.path.isdir(config.TEMP_DCM_FOLDER):
            os.makedirs(config.TEMP_DCM_FOLDER)
            print("Created {}".format(config.TEMP_DCM_FOLDER))
        else:
            print("Using {}".format(config.TEMP_DCM_FOLDER))

        # Create NONDCM temp directory
        if not os.path.isdir(config.TEMP_NONDCM_FOLDER):
            os.makedirs(config.TEMP_NONDCM_FOLDER)
            print("Created {}".format(config.TEMP_NONDCM_FOLDER))
        else:
            print("Using {}".format(config.TEMP_NONDCM_FOLDER))

        # Create PDF temp directory
        if not os.path.isdir(config.PDF_REPORT_FOLDER):
            os.makedirs(config.PDF_REPORT_FOLDER)
            print("Created {}".format(config.PDF_REPORT_FOLDER))
        else:
            print("Using {}".format(config.PDF_REPORT_FOLDER))

        # Create DYNALOG temp directory
        if not os.path.isdir(config.TEMP_DYNALOG_FOLDER):
            os.makedirs(config.TEMP_DYNALOG_FOLDER)
            print("Created {}".format(config.TEMP_DYNALOG_FOLDER))
        else:
            print("Using {}".format(config.TEMP_DYNALOG_FOLDER))
    else:
        os.makedirs(config.TEMP_DCM_FOLDER)
        os.makedirs(config.TEMP_NONDCM_FOLDER)
        os.makedirs(config.TEMP_DYNALOG_FOLDER)
        os.makedirs(config.PDF_REPORT_FOLDER)
        print("Created {}".format(config.TEMP_DCM_FOLDER))
        print("Created {}".format(config.TEMP_NONDCM_FOLDER))
        print("Created {}".format(config.TEMP_DYNALOG_FOLDER))
        print("Created {}".format(config.PDF_REPORT_FOLDER))

    print("\n")

    # Here starts the bottle app
    app = Bottle()

    @app.error(500)
    def custom500(error):
        return template("error_template.tpl", error_message="Cause unknown.")

    @app.route('')
    @app.route('/')
    @app.route('/qaserver')  # legacy
    @app.route('/qaserver/')  # legacy
    @app.route('/qaserver/login')  # legacy
    def redirect_to_login():
        redirect("/login")

    @app.route("/login")
    def login_form():
        images = []
        for f in os.listdir(os.path.join(CUR_DIR, "static", "images")):
            if f.endswith('.png'):
                images.append(os.path.join("images", f))

        if images:
            image = random.choice(images)
        else:
            image = "blank"

        return template("login.tpl",
                        institution=config.INSTITUTION,
                        image=image)

    @app.route('/login_check_credentials', method='POST')
    def login_check_credentials():
        username = request.forms.username
        password = request.forms.password
        user_list = get_one_user(username)
        if user_list is None:
            return "User not recognized!"
        else:
            if not check_encrypted_password(password, user_list["Password"]):
                return "Wrong password!"
            else:
                return "Success!"

    @app.route('/login', method='POST')
    def login_submit():
        username = request.forms.username
        password = request.forms.password
        user_list = get_one_user(username)
        if user_list is None:
            return template("error_template.tpl",
                            error_message="User not recognized.")
        else:
            if not check_encrypted_password(password, user_list["Password"]):
                return template("error_template.tpl",
                                error_message="Wrong password.")
            else:
                response.set_cookie("account",
                                    username,
                                    secret=config.SECRET_KEY,
                                    samesite="lax")
                return template("menu_page.tpl",
                                institution=config.INSTITUTION,
                                orthanc_url=config.ORTHANC_URL,
                                qaserver_version=config.QASERVER_VERSION,
                                displayname=user_list["DisplayName"],
                                is_admin=check_is_admin(username))

    # Merge apps
    app.merge(app_general)
    app.merge(wl_app)
    app.merge(ss_app)
    app.merge(pf_app)
    app.merge(plimg_app)
    app.merge(ctp_app)
    app.merge(dyn_app)
    app.merge(flatsym_app)
    app.merge(vmat_app)
    app.merge(fieldsize_app)
    app.merge(imgreview_app)
    app.merge(fieldrot_app)
    app.merge(admin_app)
    app.merge(trends_app)

    ip, port = sys.argv[1].split(":")

    bottle_run(app=app,
               server="waitress",
               host=ip,
               port=port,
               reloader=False,
               debug=False)
Example #13
0
def planar_imaging_helperf(args):
    # This function is used in order to prevent memory problems
    clip_box = args["clip_box"]
    phantom = args["phantom"]
    machine = args["machine"]
    beam = args["beam"]
    leedsrot1 = args["leedsrot1"]
    leedsrot2 = args["leedsrot2"]
    inv = args["inv"]
    bbox = args["bbox"]
    w1 = args["w1"]
    use_reference = args["use_reference"]
    colormap = args["colormap"]
    displayname = args["displayname"]
    acquisition_datetime = args["acquisition_datetime"]
    general_functions.set_configuration(
        args["config"])  # Transfer to this process

    # Collect data for "save results"
    tolerances = general_functions.get_tolerance_user_machine_planarimaging(
        machine, beam, phantom)  # If user_machne has specific tolerance
    if not tolerances:
        lowtresh, hightresh, generate_pdf = 0.05, 0.1, "True"
    else:
        lowtresh, hightresh, generate_pdf = tolerances

    lowtresh = float(lowtresh)
    hightresh = float(hightresh)

    # Set colormap
    cmap = matplotlib.cm.get_cmap(colormap)
    try:
        temp_folder1, file_path1 = RestToolbox.GetSingleDcm(
            config.ORTHANC_URL, w1)
    except:
        return template("error_template",
                        {"error_message": "Cannot read image."})

    ref_path1 = general_functions.get_referenceimagepath_planarimaging(
        machine, beam, phantom)

    if ref_path1 is not None:
        ref_path1 = os.path.join(config.REFERENCE_IMAGES_FOLDER, ref_path1[0])
        if os.path.exists(ref_path1):
            ref1_exists = True
        else:
            ref1_exists = False
    else:
        ref1_exists = False

    if not use_reference:
        ref1_exists = False

    # First analyze first image
    try:
        if phantom == "QC3":
            pi1 = StandardImagingQC3(file_path1)
        elif phantom == "LeedsTOR":
            pi1 = LeedsTOR(file_path1)
        elif phantom == "Las Vegas":
            pi1 = LasVegas(file_path1)
        elif phantom == "DoselabMC2MV":
            pi1 = DoselabMC2MV(file_path1)
        else:
            pi1 = DoselabMC2kV(file_path1)

        if clip_box != 0:
            try:
                #pi1.image.check_inversion_by_histogram()
                general_functions.clip_around_image(pi1.image, clip_box)
            except Exception as e:
                return template(
                    "error_template",
                    {"error_message": "Unable to apply clipbox. " + str(e)})
        pi1.analyze(low_contrast_threshold=lowtresh,
                    high_contrast_threshold=hightresh,
                    invert=inv,
                    angle_override=None if leedsrot2 == 0 else leedsrot2)
    except Exception as e:
        return template("error_template",
                        {"error_message": "Cannot analyze image 1. " + str(e)})

    # Analyze reference images if they exists

    if ref1_exists:
        try:
            if phantom == "QC3":
                ref1 = StandardImagingQC3(ref_path1)
            elif phantom == "LeedsTOR":
                ref1 = LeedsTOR(ref_path1)
            elif phantom == "Las Vegas":
                ref1 = LasVegas(ref_path1)
            elif phantom == "DoselabMC2MV":
                ref1 = DoselabMC2MV(ref_path1)
            else:
                ref1 = DoselabMC2kV(ref_path1)

            if clip_box != 0:
                try:
                    #ref1.image.check_inversion_by_histogram()
                    general_functions.clip_around_image(ref1.image, clip_box)
                except Exception as e:
                    return template("error_template", {
                        "error_message":
                        "Unable to apply clipbox. " + str(e)
                    })
            ref1.analyze(low_contrast_threshold=lowtresh,
                         high_contrast_threshold=hightresh,
                         invert=inv,
                         angle_override=None if leedsrot1 == 0 else leedsrot1)
        except:
            return template("error_template", {"error_message": "Cannot analyze reference image."\
                                               " Check that the image in the database is valid."})

    save_results = {
        "machine": machine,
        "beam": beam,
        "phantom": phantom,
        "displayname": displayname
    }

    fig = Figure(figsize=(10.5, 5), tight_layout={"w_pad": 0, "pad": 1.5})
    ax_ref = fig.add_subplot(1, 2, 1)
    ax_pi = fig.add_subplot(1, 2, 2)

    # Plot reference image and regions
    if phantom == "QC3":
        low_contrast_rois_pi1 = pi1.low_contrast_rois[
            1:]  # Exclude first point which is background
    else:
        low_contrast_rois_pi1 = pi1.low_contrast_rois

    if ref1_exists:
        if phantom == "QC3":
            low_contrast_rois_ref1 = ref1.low_contrast_rois[
                1:]  # Exclude first point which is background
        else:
            low_contrast_rois_ref1 = ref1.low_contrast_rois

    if ref1_exists:
        ax_ref.imshow(ref1.image.array,
                      cmap=cmap,
                      interpolation="none",
                      aspect="equal",
                      origin='upper')
        ax_ref.set_title(phantom + ' Reference Image')
        ax_ref.axis('off')
        # plot the background ROIS
        for roi in ref1.low_contrast_background_rois:
            roi.plot2axes(ax_ref, edgecolor='yellow')
            ax_ref.text(roi.center.x,
                        roi.center.y,
                        "B",
                        horizontalalignment='center',
                        verticalalignment='center')
        # low contrast ROIs
        for ind, roi in enumerate(low_contrast_rois_ref1):
            roi.plot2axes(ax_ref, edgecolor=roi.plot_color)
            ax_ref.text(roi.center.x,
                        roi.center.y,
                        str(ind),
                        horizontalalignment='center',
                        verticalalignment='center')
        # plot the high-contrast ROIs
        if phantom != "Las Vegas":
            mtf_temp_ref = list(ref1.mtf.norm_mtfs.values())
            for ind, roi in enumerate(ref1.high_contrast_rois):
                color = 'b' if mtf_temp_ref[
                    ind] > ref1._high_contrast_threshold else 'r'
                roi.plot2axes(ax_ref, edgecolor=color)
                ax_ref.text(roi.center.x,
                            roi.center.y,
                            str(ind),
                            horizontalalignment='center',
                            verticalalignment='center')
    else:
        ax_ref.text(0.5,
                    0.5,
                    "Reference image not available",
                    horizontalalignment='center',
                    verticalalignment='center')

    # Plot current image and regions
    ax_pi.imshow(pi1.image.array,
                 cmap=cmap,
                 interpolation="none",
                 aspect="equal",
                 origin='upper')
    ax_pi.axis('off')
    ax_pi.set_title(phantom + ' Current Image')
    # plot the background ROIS
    for roi in pi1.low_contrast_background_rois:
        roi.plot2axes(ax_pi, edgecolor='yellow')
        ax_pi.text(roi.center.x,
                   roi.center.y,
                   "B",
                   horizontalalignment='center',
                   verticalalignment='center')
    # low contrast ROIs
    for ind, roi in enumerate(low_contrast_rois_pi1):
        roi.plot2axes(ax_pi, edgecolor=roi.plot_color)
        ax_pi.text(roi.center.x,
                   roi.center.y,
                   str(ind),
                   horizontalalignment='center',
                   verticalalignment='center')
    # plot the high-contrast ROIs
    if phantom != "Las Vegas":
        mtf_temp_pi = list(pi1.mtf.norm_mtfs.values())
        for ind, roi in enumerate(pi1.high_contrast_rois):
            color = 'b' if mtf_temp_pi[
                ind] > pi1._high_contrast_threshold else 'r'
            roi.plot2axes(ax_pi, edgecolor=color)
            ax_pi.text(roi.center.x,
                       roi.center.y,
                       str(ind),
                       horizontalalignment='center',
                       verticalalignment='center')

    # Zoom on phantom if requested:
    if bbox:
        if phantom == "QC3" or phantom == "DoselabMC2MV" or phantom == "DoselabMC2kV":
            pad = 15  # Additional space between cyan bbox and plot
            if ref1_exists:
                bounding_box_ref = ref1.phantom_ski_region.bbox
                bbox_center_ref = ref1.phantom_center

                if abs(bounding_box_ref[1] -
                       bounding_box_ref[3]) >= abs(bounding_box_ref[2] -
                                                   bounding_box_ref[0]):
                    dist = abs(bounding_box_ref[1] - bounding_box_ref[3]) / 2
                    ax_ref.set_ylim(bbox_center_ref.y + dist + pad,
                                    bbox_center_ref.y - dist - pad)
                    ax_ref.set_xlim(bbox_center_ref.x - dist - pad,
                                    bbox_center_ref.x + dist + pad)
                else:
                    dist = abs(bounding_box_ref[2] - bounding_box_ref[0]) / 2
                    ax_ref.set_ylim(bbox_center_ref.y + dist + pad,
                                    bbox_center_ref.y - dist - pad)
                    ax_ref.set_xlim(bbox_center_ref.x - dist - pad,
                                    bbox_center_ref.x + dist + pad)

                ax_ref.plot([
                    bounding_box_ref[1], bounding_box_ref[1],
                    bounding_box_ref[3], bounding_box_ref[3],
                    bounding_box_ref[1]
                ], [
                    bounding_box_ref[2], bounding_box_ref[0],
                    bounding_box_ref[0], bounding_box_ref[2],
                    bounding_box_ref[2]
                ],
                            c="cyan")
                ax_ref.autoscale(False)

            bounding_box_pi = pi1.phantom_ski_region.bbox
            bbox_center_pi = pi1.phantom_center
            if abs(bounding_box_pi[1] -
                   bounding_box_pi[3]) >= abs(bounding_box_pi[2] -
                                              bounding_box_pi[0]):
                dist = abs(bounding_box_pi[1] - bounding_box_pi[3]) / 2
                ax_pi.set_ylim(bbox_center_pi.y + dist + pad,
                               bbox_center_pi.y - dist - pad)
                ax_pi.set_xlim(bbox_center_pi.x - dist - pad,
                               bbox_center_pi.x + dist + pad)
            else:
                dist = abs(bounding_box_pi[2] - bounding_box_pi[0]) / 2
                ax_pi.set_ylim(bbox_center_pi.y + dist + pad,
                               bbox_center_pi.y - dist - pad)
                ax_pi.set_xlim(bbox_center_pi.x - dist - pad,
                               bbox_center_pi.x + dist + pad)

            ax_pi.plot([
                bounding_box_pi[1], bounding_box_pi[1], bounding_box_pi[3],
                bounding_box_pi[3], bounding_box_pi[1]
            ], [
                bounding_box_pi[2], bounding_box_pi[0], bounding_box_pi[0],
                bounding_box_pi[2], bounding_box_pi[2]
            ],
                       c="cyan")
            ax_pi.autoscale(False)

        elif phantom == "Las Vegas":  # For some reason phantom_ski_regio has an underscore
            pad = 15  # Additional space between cyan bbox and plot
            if ref1_exists:
                bounding_box_ref = ref1._phantom_ski_region.bbox
                bbox_center_ref = ref1.phantom_center

                if abs(bounding_box_ref[1] -
                       bounding_box_ref[3]) >= abs(bounding_box_ref[2] -
                                                   bounding_box_ref[0]):
                    dist = abs(bounding_box_ref[1] - bounding_box_ref[3]) / 2
                    ax_ref.set_ylim(bbox_center_ref.y + dist + pad,
                                    bbox_center_ref.y - dist - pad)
                    ax_ref.set_xlim(bbox_center_ref.x - dist - pad,
                                    bbox_center_ref.x + dist + pad)
                else:
                    dist = abs(bounding_box_ref[2] - bounding_box_ref[0]) / 2
                    ax_ref.set_ylim(bbox_center_ref.y + dist + pad,
                                    bbox_center_ref.y - dist - pad)
                    ax_ref.set_xlim(bbox_center_ref.x - dist - pad,
                                    bbox_center_ref.x + dist + pad)

                ax_ref.plot([
                    bounding_box_ref[1], bounding_box_ref[1],
                    bounding_box_ref[3], bounding_box_ref[3],
                    bounding_box_ref[1]
                ], [
                    bounding_box_ref[2], bounding_box_ref[0],
                    bounding_box_ref[0], bounding_box_ref[2],
                    bounding_box_ref[2]
                ],
                            c="cyan")
                ax_ref.autoscale(False)

            bounding_box_pi = pi1._phantom_ski_region.bbox
            bbox_center_pi = pi1.phantom_center
            if abs(bounding_box_pi[1] -
                   bounding_box_pi[3]) >= abs(bounding_box_pi[2] -
                                              bounding_box_pi[0]):
                dist = abs(bounding_box_pi[1] - bounding_box_pi[3]) / 2
                ax_pi.set_ylim(bbox_center_pi.y + dist + pad,
                               bbox_center_pi.y - dist - pad)
                ax_pi.set_xlim(bbox_center_pi.x - dist - pad,
                               bbox_center_pi.x + dist + pad)
            else:
                dist = abs(bounding_box_pi[2] - bounding_box_pi[0]) / 2
                ax_pi.set_ylim(bbox_center_pi.y + dist + pad,
                               bbox_center_pi.y - dist - pad)
                ax_pi.set_xlim(bbox_center_pi.x - dist - pad,
                               bbox_center_pi.x + dist + pad)

            ax_pi.plot([
                bounding_box_pi[1], bounding_box_pi[1], bounding_box_pi[3],
                bounding_box_pi[3], bounding_box_pi[1]
            ], [
                bounding_box_pi[2], bounding_box_pi[0], bounding_box_pi[0],
                bounding_box_pi[2], bounding_box_pi[2]
            ],
                       c="cyan")
            ax_pi.autoscale(False)

        elif phantom == "LeedsTOR":
            pad = 15  # Additional space between cyan bbox and plot
            if ref1_exists:
                big_circle_idx = np.argsort([
                    ref1._regions[roi].major_axis_length for roi in ref1._blobs
                ])[-1]
                circle_roi = ref1._regions[ref1._blobs[big_circle_idx]]
                bounding_box_ref = circle_roi.bbox
                bbox_center_ref = bbox_center(circle_roi)
                max_xy = max([
                    abs(bounding_box_ref[1] - bounding_box_ref[3]) / 2,
                    abs(bounding_box_ref[0] - bounding_box_ref[2]) / 2
                ])
                ax_ref.set_ylim(bbox_center_ref.y + max_xy + pad,
                                bbox_center_ref.y - max_xy - pad)
                ax_ref.set_xlim(bbox_center_ref.x - max_xy - pad,
                                bbox_center_ref.x + max_xy + pad)
                ax_ref.plot([
                    bounding_box_ref[1], bounding_box_ref[1],
                    bounding_box_ref[3], bounding_box_ref[3],
                    bounding_box_ref[1]
                ], [
                    bounding_box_ref[2], bounding_box_ref[0],
                    bounding_box_ref[0], bounding_box_ref[2],
                    bounding_box_ref[2]
                ],
                            c="cyan")
                ax_ref.autoscale(False)

            big_circle_idx = np.argsort([
                pi1._regions[roi].major_axis_length for roi in pi1._blobs
            ])[-1]
            circle_roi = pi1._regions[pi1._blobs[big_circle_idx]]
            bounding_box_pi = circle_roi.bbox
            bbox_center_pi = bbox_center(circle_roi)

            max_xy = max([
                abs(bounding_box_pi[1] - bounding_box_pi[3]) / 2,
                abs(bounding_box_pi[0] - bounding_box_pi[2]) / 2
            ])

            ax_pi.set_ylim(bbox_center_pi.y + max_xy + pad,
                           bbox_center_pi.y - max_xy - pad)
            ax_pi.set_xlim(bbox_center_pi.x - max_xy - pad,
                           bbox_center_pi.x + max_xy + pad)
            ax_pi.plot([
                bounding_box_pi[1], bounding_box_pi[1], bounding_box_pi[3],
                bounding_box_pi[3], bounding_box_pi[1]
            ], [
                bounding_box_pi[2], bounding_box_pi[0], bounding_box_pi[0],
                bounding_box_pi[2], bounding_box_pi[2]
            ],
                       c="cyan")
            ax_pi.autoscale(False)

    # Add phantom outline:
    outline_obj_pi1, settings_pi1 = pi1._create_phantom_outline_object()
    outline_obj_pi1.plot2axes(ax_pi, edgecolor='g', **settings_pi1)
    if ref1_exists:
        outline_obj_ref1, settings_ref1 = ref1._create_phantom_outline_object()
        outline_obj_ref1.plot2axes(ax_ref, edgecolor='g', **settings_ref1)

    # Plot low frequency contrast, CNR and rMTF
    fig2 = Figure(figsize=(10.5, 10), tight_layout={"w_pad": 1})
    ax_lfc = fig2.add_subplot(2, 2, 1)
    ax_lfcnr = fig2.add_subplot(2, 2, 2)
    ax_rmtf = fig2.add_subplot(2, 2, 3)

    # lfc
    ax_lfc.plot([abs(roi.contrast) for roi in low_contrast_rois_pi1],
                marker='o',
                markersize=8,
                color='r')
    if ref1_exists:
        ax_lfc.plot([abs(roi.contrast) for roi in low_contrast_rois_ref1],
                    marker='o',
                    color='r',
                    markersize=8,
                    markerfacecolor="None",
                    linestyle="--")
    ax_lfc.plot([], [], color='r', linestyle="--", label='Reference')
    ax_lfc.plot([], [], color='r', label='Current')
    ax_lfc.plot([0, len(low_contrast_rois_pi1) - 1], [lowtresh, lowtresh],
                "-g")
    ax_lfc.grid(True)
    ax_lfc.set_title('Low-frequency Contrast')
    ax_lfc.set_xlabel('ROI #')
    ax_lfc.set_ylabel('Contrast')
    ax_lfc.set_xticks(np.arange(0, len(low_contrast_rois_pi1), 1))
    ax_lfc.legend(loc='upper right',
                  ncol=2,
                  columnspacing=0,
                  fontsize=12,
                  handletextpad=0)
    ax_lfc.margins(0.05)

    # CNR
    ax_lfcnr.plot(
        [abs(roi.contrast_to_noise) for roi in low_contrast_rois_pi1],
        marker='^',
        markersize=8,
        color='r')
    if ref1_exists:
        ax_lfcnr.plot(
            [abs(roi.contrast_to_noise) for roi in low_contrast_rois_ref1],
            marker='^',
            color='r',
            markersize=8,
            markerfacecolor="None",
            linestyle="--")
    ax_lfcnr.plot([], [], color='r', linestyle="--", label='Reference')
    ax_lfcnr.plot([], [], color='r', label='Current')
    ax_lfcnr.grid(True)
    ax_lfcnr.set_title('Contrast-Noise Ratio')
    ax_lfcnr.set_xlabel('ROI #')
    ax_lfcnr.set_ylabel('CNR')
    ax_lfcnr.set_xticks(np.arange(0, len(low_contrast_rois_pi1), 1))
    ax_lfcnr.legend(loc='upper right',
                    ncol=2,
                    columnspacing=0,
                    fontsize=12,
                    handletextpad=0)
    ax_lfcnr.margins(0.05)

    # rMTF
    if phantom != "Las Vegas":
        mtfs_pi1 = list(pi1.mtf.norm_mtfs.values())
        if ref1_exists:
            mtfs_ref1 = list(ref1.mtf.norm_mtfs.values())
        else:
            mtfs_ref1 = [np.nan] * len(mtfs_pi1)

        lppmm = pi1.mtf.spacings

        ax_rmtf.plot(lppmm, mtfs_pi1, marker='D', markersize=8, color='b')
        ax_rmtf.plot([min(lppmm), max(lppmm)], [hightresh, hightresh], "-g")

        if ref1_exists:
            ax_rmtf.plot(lppmm,
                         mtfs_ref1,
                         marker='D',
                         color='b',
                         markersize=8,
                         markerfacecolor="None",
                         linestyle="--")

        ax_rmtf.plot([], [], color='b', linestyle="--", label='Reference')
        ax_rmtf.plot([], [], color='b', label='Current')
        ax_rmtf.grid(True)
        ax_rmtf.set_title('High-frequency rMTF')
        ax_rmtf.set_xlabel('Line pairs / mm')
        ax_rmtf.set_ylabel('relative MTF')
        ax_rmtf.legend(loc='upper right',
                       ncol=2,
                       columnspacing=0,
                       fontsize=12,
                       handletextpad=0)
        ax_rmtf.margins(0.05)

        f30 = [
            ref1.mtf.relative_resolution(30) if ref1_exists else np.nan,
            pi1.mtf.relative_resolution(30)
        ]
        f40 = [
            ref1.mtf.relative_resolution(40) if ref1_exists else np.nan,
            pi1.mtf.relative_resolution(40)
        ]
        f50 = [
            ref1.mtf.relative_resolution(50) if ref1_exists else np.nan,
            pi1.mtf.relative_resolution(50)
        ]
        f80 = [
            ref1.mtf.relative_resolution(80) if ref1_exists else np.nan,
            pi1.mtf.relative_resolution(80)
        ]
    else:
        ax_rmtf.text(0.5,
                     0.5,
                     "MTF not available",
                     horizontalalignment='center',
                     verticalalignment='center')
        f30 = [np.nan, np.nan]
        f40 = [np.nan, np.nan]
        f50 = [np.nan, np.nan]
        f80 = [np.nan, np.nan]

    if ref1_exists:
        median_contrast = [
            np.median([roi.contrast for roi in low_contrast_rois_ref1]),
            np.median([roi.contrast for roi in low_contrast_rois_pi1])
        ]
        median_CNR = [
            np.median(
                [roi.contrast_to_noise for roi in low_contrast_rois_ref1]),
            np.median([roi.contrast_to_noise for roi in low_contrast_rois_pi1])
        ]
        phantom_angle = [ref1.phantom_angle, pi1.phantom_angle]
    else:
        median_contrast = [
            np.nan,
            np.median([roi.contrast for roi in low_contrast_rois_pi1])
        ]
        median_CNR = [
            np.nan,
            np.median([roi.contrast_to_noise for roi in low_contrast_rois_pi1])
        ]
        phantom_angle = [np.nan, pi1.phantom_angle]

    script = mpld3.fig_to_html(fig, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    script2 = mpld3.fig_to_html(fig2, d3_url=D3_URL, mpld3_url=MPLD3_URL)

    variables = {
        "script": script,
        "script2": script2,
        "f30": f30,
        "f40": f40,
        "f50": f50,
        "f80": f80,
        "median_contrast": median_contrast,
        "median_CNR": median_CNR,
        "pdf_report_enable": generate_pdf,
        "save_results": save_results,
        "acquisition_datetime": acquisition_datetime,
        "phantom_angle": phantom_angle
    }

    if generate_pdf == "True":
        pdf_file = tempfile.NamedTemporaryFile(delete=False,
                                               prefix="PlanarImaging_",
                                               suffix=".pdf",
                                               dir=config.PDF_REPORT_FOLDER)
        metadata = RestToolbox.GetInstances(config.ORTHANC_URL, [w1])
        try:
            patient = metadata[0]["PatientName"]
        except:
            patient = ""
        try:
            stationname = metadata[0]["StationName"]
        except:
            stationname = ""
        try:
            date_time = RestToolbox.get_datetime(metadata[0])
            date_var = datetime.datetime.strptime(
                date_time[0], "%Y%m%d").strftime("%d/%m/%Y")
        except:
            date_var = ""
        pi1.publish_pdf(pdf_file,
                        notes=[
                            "Date = " + date_var, "Patient = " + patient,
                            "Station = " + stationname
                        ])

        variables["pdf_report_filename"] = os.path.basename(pdf_file.name)

    general_functions.delete_figure([fig, fig2])
    general_functions.delete_files_in_subfolders([temp_folder1
                                                  ])  # Delete image

    #gc.collect()
    return template("planar_imaging_results", variables)
Example #14
0
def starshot_helperf(args):
    imgtype = args["imgtype"]
    w = args["w"]
    clip_box = args["clip_box"]
    radius = args["radius"]
    min_peak_height = args["min_peak_height"]
    start_x = args["start_x"]
    start_y = args["start_y"]
    dpi = args["dpi"]
    sid = args["sid"]
    fwhm = args["fwhm"]
    recursive = args["recursive"]
    invert = args["invert"]
    temp_folder = args["temp_folder"]
    file_path = args["file_path"]
    imgdescription = args["imgdescription"]
    station = args["station"]
    displayname = args["displayname"]
    acquisition_datetime = args["acquisition_datetime"]
    general_functions.set_configuration(args["config"])

    # Collect data for "save results"
    dicomenergy = general_functions.get_energy_from_imgdescription(imgdescription)
    user_machine, user_energy = general_functions.get_user_machine_and_energy(station, dicomenergy)
    machines_and_energies = general_functions.get_machines_and_energies(general_functions.get_treatmentunits_starshot())
    tolerances = general_functions.get_tolerance_user_machine_starshot(user_machine)  # If user_machne has specific tolerance
    if not tolerances:
        tolerance, pdf_report_enable = general_functions.get_settings_starshot()
    else:
        tolerance, pdf_report_enable = tolerances[0]

    tolerance = float(tolerance)  # If more than this, the test is "borderline", but not "failed"
    
    save_results = {
                    "user_machine": user_machine,
                    "user_energy": user_energy,
                    "machines_and_energies": machines_and_energies,
                    "testtype": ["Collimator", "Couch", "Gantry"],
                    "displayname": displayname
                    }

    if start_x==0 or start_y==0:
        start_point=None
    else:
        start_point=(start_x, start_y)

    if sid==0.0 and dpi==0:
        try:
            star = Starshot(file_path)
        except Exception as e:
            return template("error_template", {"error_message": "The Starshot module cannot calculate. "+str(e)})
    elif sid==0.0 and dpi!=0:
        try:
            star = Starshot(file_path, dpi=dpi)
        except Exception as e:
            return template("error_template", {"error_message": "The Starshot module cannot calculate. "+str(e)})
    elif sid!=0.0 and dpi==0:
        try:
            star = Starshot(file_path, sid=sid)
        except Exception as e:
            return template("error_template", {"error_message": "The Starshot module cannot calculate. "+str(e)})
    else:
        try:
            star = Starshot(file_path, dpi=dpi, sid=sid)
        except Exception as e:
            return template("error_template", {"error_message": "The Starshot module cannot calculate. "+str(e)})
    
    # Here we force pixels to background outside of box:
    if clip_box != 0:
        try:
            star.image.check_inversion_by_histogram(percentiles=[4, 50, 96]) # Check inversion otherwise this might not work
            general_functions.clip_around_image(star.image, clip_box)
        except Exception as e:
            return template("error_template", {"error_message": "Unable to apply clipbox. "+str(e)})
    
    # If inversion is selected:
    if invert:
        star.image.invert()

    # Now we try to analyse
    try:
        star.analyze(radius=radius, min_peak_height=min_peak_height, tolerance=tolerance,
                     start_point=start_point, fwhm=fwhm, recursive=recursive)
    except Exception as e:
        return template("error_template", {"error_message": "Module Starshot cannot calculate. "+str(e)})

    fig_ss = Figure(figsize=(10, 6), tight_layout={"w_pad":4})
    img_ax = fig_ss.add_subplot(1,2,1)
    wobble_ax = fig_ss.add_subplot(1,2,2)

    img_ax.imshow(star.image.array, cmap=matplotlib.cm.gray, interpolation="none", aspect="equal", origin='upper')

    star.lines.plot(img_ax)
    star.wobble.plot2axes(img_ax, edgecolor='green')
    star.circle_profile.plot2axes(img_ax, edgecolor='green')
    img_ax.axis('off')
    img_ax.autoscale(tight=True)
    img_ax.set_aspect(1)
    img_ax.set_xticks([])
    img_ax.set_yticks([])

    star.lines.plot(wobble_ax)
    star.wobble.plot2axes(wobble_ax, edgecolor='green')
    star.circle_profile.plot2axes(wobble_ax, edgecolor='green')
    wobble_ax.axis('off')
    xlims = [star.wobble.center.x + star.wobble.diameter, star.wobble.center.x - star.wobble.diameter]
    ylims = [star.wobble.center.y + star.wobble.diameter, star.wobble.center.y - star.wobble.diameter]
    wobble_ax.set_xlim(xlims)
    wobble_ax.set_ylim(ylims)
    wobble_ax.axis('on')
    wobble_ax.set_aspect(1)

    script = mpld3.fig_to_html(fig_ss, d3_url=D3_URL, mpld3_url=MPLD3_URL)

    variables = {
                 "script": script,
                 "passed": star.passed,
                 "radius": star.wobble.radius_mm,
                 "tolerance": star.tolerance,
                 "circle_center": star.wobble.center,
                 "pdf_report_enable": pdf_report_enable,
                 "save_results": save_results,
                 "acquisition_datetime": acquisition_datetime
                 }
 
    # Generate pylinac report:
    if pdf_report_enable == "True":
        pdf_file = tempfile.NamedTemporaryFile(delete=False, prefix="Starshot_", suffix=".pdf", dir=config.PDF_REPORT_FOLDER)
        if imgtype == "dicom":
            metadata = RestToolbox.GetInstances(config.ORTHANC_URL, [w])
            try:
                patient = metadata[0]["PatientName"]
            except:
                patient = ""
            try:
                stationname = metadata[0]["StationName"]
            except:
                stationname = ""
            try:
                date_time = RestToolbox.get_datetime(metadata[0])
                date_var = datetime.datetime.strptime(date_time[0], "%Y%m%d").strftime("%d/%m/%Y")
            except:
                date_var = ""

            star.publish_pdf(pdf_file, notes=["Date = "+date_var, "Patient = "+patient, "Station = "+stationname])
        else:
            star.publish_pdf(pdf_file)
        variables["pdf_report_filename"] = os.path.basename(pdf_file.name)

    general_functions.delete_figure([fig_ss])
    general_functions.delete_files_in_subfolders([temp_folder]) # Delete image
    return template("starshot_results", variables)
Example #15
0
def picket_fence_helperf(args):
    '''This function is used in order to prevent memory problems'''
    temp_folder = args["temp_folder"]
    file_path = args["file_path"]
    clip_box = args["clip_box"]
    py_filter = args["py_filter"]
    num_pickets = args["num_pickets"]
    sag = args["sag"]
    mlc = args["mlc"]
    invert = args["invert"]
    orientation = args["orientation"]
    w = args["w"]
    imgdescription = args["imgdescription"]
    station = args["station"]
    displayname = args["displayname"]
    acquisition_datetime = args["acquisition_datetime"]
    general_functions.set_configuration(
        args["config"])  # Transfer to this process

    # Chose module:
    if mlc in ["Varian_80", "Elekta_80", "Elekta_160"]:
        use_original_pylinac = "False"
    else:
        use_original_pylinac = "True"

    # Collect data for "save results"
    dicomenergy = general_functions.get_energy_from_imgdescription(
        imgdescription)
    user_machine, user_energy = general_functions.get_user_machine_and_energy(
        station, dicomenergy)
    machines_and_energies = general_functions.get_machines_and_energies(
        general_functions.get_treatmentunits_picketfence())
    tolerances = general_functions.get_tolerance_user_machine_picketfence(
        user_machine)  # If user_machne has specific tolerance
    if not tolerances:
        action_tolerance, tolerance, generate_pdf_report = general_functions.get_settings_picketfence(
        )
    else:
        action_tolerance, tolerance, generate_pdf_report = tolerances[0]

    tolerance = float(tolerance)
    action_tol = float(action_tolerance)

    save_results = {
        "user_machine": user_machine,
        "user_energy": user_energy,
        "machines_and_energies": machines_and_energies,
        "displayname": displayname
    }

    # Import either original pylinac module or the modified module
    if use_original_pylinac == "True":
        from pylinac import PicketFence as PicketFence  # Original pylinac analysis
    else:
        if __name__ == '__main__' or parent_module.__name__ == '__main__':
            from python_packages.pylinac.picketfence_modified import PicketFence as PicketFence
        else:
            from .python_packages.pylinac.picketfence_modified import PicketFence as PicketFence

    try:
        pf = PicketFence(file_path, filter=py_filter)
    except Exception as e:
        return template("error_template", {
            "error_message":
            "Module PicketFence cannot calculate. " + str(e)
        })

    # Here we force pixels to background outside of box:
    if clip_box != 0:
        try:
            pf.image.check_inversion_by_histogram(percentiles=[
                4, 50, 96
            ])  # Check inversion otherwise this might not work
            general_functions.clip_around_image(pf.image, clip_box)
        except Exception as e:
            return template(
                "error_template",
                {"error_message": "Unable to apply clipbox. " + str(e)})

    # Now invert if needed
    if invert:
        try:
            pf.image.invert()
        except Exception as e:
            return template(
                "error_template",
                {"error_message": "Unable to invert the image. " + str(e)})

    # Now analyze
    try:
        if use_original_pylinac == "True":
            hdmlc = True if mlc == "Varian_120HD" else False
            pf.analyze(tolerance=tolerance,
                       action_tolerance=action_tol,
                       hdmlc=hdmlc,
                       sag_adjustment=float(sag),
                       num_pickets=num_pickets,
                       orientation=orientation)
        else:
            pf.analyze(tolerance=tolerance,
                       action_tolerance=action_tol,
                       mlc_type=mlc,
                       sag_adjustment=float(sag),
                       num_pickets=num_pickets,
                       orientation=orientation)
    except Exception as e:
        return template(
            "error_template",
            {"error_message": "Picket fence module cannot analyze. " + str(e)})

    # Added an if clause to tell if num of mlc's are not the same on all pickets:

    num_mlcs = len(pf.pickets[0].mlc_meas)
    for p in pf.pickets:
        if len(p.mlc_meas) != num_mlcs:
            return template(
                "error_template", {
                    "error_message":
                    "Not all pickets have the same number of leaves. " +
                    "Probably your image si too skewed. Rotate your collimator a bit "
                    +
                    "and try again. Use the jaws perpendicular to MLCs to set the right "
                    + "collimator angle."
                })
    error_array = np.array([])
    max_error = []
    max_error_leaf = []
    passed_tol = []
    picket_offsets = []
    picket_nr = pf.num_pickets
    for k in pf.pickets.pickets:
        error_array = np.concatenate((error_array, k.error_array))
        max_error.append(k.max_error)
        max_err_leaf_ind = np.argmax(k.error_array)

        max_error_leaf.append(max_err_leaf_ind)
        passed_tol.append("Passed" if k.passed else "Failed")
        picket_offsets.append(k.dist2cax)

    # Plot images
    if pf.settings.orientation == "Left-Right":
        fig_pf = Figure(figsize=(9, 10), tight_layout={"w_pad": 0})
    else:
        fig_pf = Figure(figsize=(9.5, 7), tight_layout={"w_pad": 0})

    img_ax = fig_pf.add_subplot(1, 1, 1)
    img_ax.imshow(pf.image.array,
                  cmap=matplotlib.cm.gray,
                  interpolation="none",
                  aspect="equal",
                  origin='upper')

    # Taken from pylinac: leaf_error_subplot:
    tol_line_height = [pf.settings.tolerance, pf.settings.tolerance]
    tol_line_width = [0, max(pf.image.shape)]
    # make the new axis
    divider = make_axes_locatable(img_ax)
    if pf.settings.orientation == 'Up-Down':
        axtop = divider.append_axes('right', 1.75, pad=0.2, sharey=img_ax)
    else:
        axtop = divider.append_axes('bottom', 1.75, pad=0.5, sharex=img_ax)

    # get leaf positions, errors, standard deviation, and leaf numbers
    pos, vals, err, leaf_nums = pf.pickets.error_hist()

    # Changed leaf_nums to sequential numbers:
    leaf_nums = list(np.arange(0, len(leaf_nums), 1))

    # plot the leaf errors as a bar plot
    if pf.settings.orientation == 'Up-Down':
        axtop.barh(pos,
                   vals,
                   xerr=err,
                   height=pf.pickets[0].sample_width * 2,
                   alpha=0.4,
                   align='center')
        # plot the tolerance line(s)
        axtop.plot(tol_line_height, tol_line_width, 'r-', linewidth=3)
        if pf.settings.action_tolerance is not None:
            tol_line_height_action = [
                pf.settings.action_tolerance, pf.settings.action_tolerance
            ]
            tol_line_width_action = [0, max(pf.image.shape)]
            axtop.plot(tol_line_height_action,
                       tol_line_width_action,
                       'y-',
                       linewidth=3)

        # reset xlims to comfortably include the max error or tolerance value
        axtop.set_xlim([0, max(max(vals), pf.settings.tolerance) + 0.1])
    else:
        axtop.bar(pos,
                  vals,
                  yerr=err,
                  width=pf.pickets[0].sample_width * 2,
                  alpha=0.4,
                  align='center')
        axtop.plot(tol_line_width, tol_line_height, 'r-', linewidth=3)
        if pf.settings.action_tolerance is not None:
            tol_line_height_action = [
                pf.settings.action_tolerance, pf.settings.action_tolerance
            ]
            tol_line_width_action = [0, max(pf.image.shape)]
            axtop.plot(tol_line_width_action,
                       tol_line_height_action,
                       'y-',
                       linewidth=3)
        axtop.set_ylim([0, max(max(vals), pf.settings.tolerance) + 0.1])

    # add formatting to axis
    axtop.grid(True)
    axtop.set_title("Average Error (mm)")

    # add tooltips if interactive
    # Copied this from previous version of pylinac
    interactive = True
    if interactive:
        if pf.settings.orientation == 'Up-Down':
            labels = [[
                'Leaf pair: {0} <br> Avg Error: {1:3.3f} mm <br> Stdev: {2:3.3f} mm'
                .format(leaf_num, err, std)
            ] for leaf_num, err, std in zip(leaf_nums, vals, err)]
            voffset = 0
            hoffset = 20
        else:
            labels = [[
                'Leaf pair: {0}, Avg Error: {1:3.3f} mm, Stdev: {2:3.3f} mm'.
                format(leaf_num, err, std)
            ] for leaf_num, err, std in zip(leaf_nums, vals, err)]

        if pf.settings.orientation == 'Up-Down':
            for num, patch in enumerate(axtop.axes.patches):
                ttip = mpld3.plugins.PointHTMLTooltip(patch,
                                                      labels[num],
                                                      voffset=voffset,
                                                      hoffset=hoffset)
                mpld3.plugins.connect(fig_pf, ttip)
                mpld3.plugins.connect(fig_pf,
                                      mpld3.plugins.MousePosition(fontsize=14))
        else:
            for num, patch in enumerate(axtop.axes.patches):
                ttip = mpld3.plugins.PointLabelTooltip(patch,
                                                       labels[num],
                                                       location='top left')
                mpld3.plugins.connect(fig_pf, ttip)
                mpld3.plugins.connect(fig_pf,
                                      mpld3.plugins.MousePosition(fontsize=14))

    for p_num, picket in enumerate(pf.pickets):
        picket.add_guards_to_axes(img_ax.axes)
        for idx, mlc_meas in enumerate(picket.mlc_meas):
            mlc_meas.plot2axes(img_ax.axes, width=1.5)

    # plot CAX
    img_ax.plot(pf.settings.image_center.x,
                pf.settings.image_center.y,
                'r+',
                ms=12,
                markeredgewidth=3)

    # tighten up the plot view
    img_ax.set_xlim([0, pf.image.shape[1]])
    img_ax.set_ylim([pf.image.shape[0], 0])
    img_ax.axis('off')
    img_ax.set_xticks([])
    img_ax.set_yticks([])

    # Histogram of all errors and average profile plot
    upper_bound = pf.settings.tolerance
    upper_outliers = np.sum(error_array.flatten() >= upper_bound)
    fig_pf2 = Figure(figsize=(10, 4), tight_layout={"w_pad": 2})
    ax2 = fig_pf2.add_subplot(1, 2, 1)
    ax3 = fig_pf2.add_subplot(1, 2, 2)
    n, bins = np.histogram(error_array.flatten(),
                           density=False,
                           bins=10,
                           range=(0, upper_bound))
    ax2.bar(bins[0:-1],
            n,
            width=np.diff(bins)[0],
            facecolor='green',
            alpha=0.75)
    ax2.bar([upper_bound, upper_bound * 1.1],
            upper_outliers,
            width=0.1 * upper_bound,
            facecolor='red',
            alpha=0.75)
    ax2.plot([pf.settings.action_tolerance, pf.settings.action_tolerance],
             [0, max(n) / 2],
             color="orange")
    ax2.annotate("Action Tol.",
                 (pf.settings.action_tolerance, 1.05 * max(n) / 2),
                 color='black',
                 fontsize=6,
                 ha='center',
                 va='bottom')
    ax2.plot([pf.settings.tolerance, pf.settings.tolerance], [0, max(n) / 2],
             color="darkred")
    ax2.annotate("Tol.", (pf.settings.tolerance, 1.05 * max(n) / 2),
                 color='black',
                 fontsize=6,
                 ha='center',
                 va='bottom')

    # Plot mean inplane profile and calculate FWHM:
    mlc_mean_profile = pf.pickets.image_mlc_inplane_mean_profile
    ax3.plot(mlc_mean_profile.values, "b-")
    picket_fwhm = []
    fwhm_mean = 0
    try:
        peaks = mlc_mean_profile.find_peaks(max_number=picket_nr,
                                            min_distance=0.02,
                                            threshold=0.5)
        peaks = np.sort(peaks)
        ax3.plot(peaks, mlc_mean_profile[peaks], "ro")

        separation = int(np.mean(np.diff(peaks)) / 3)
        mmpd = 1 / pf.image.dpmm
        # Get valleys
        valleys = []
        for p in np.arange(0, len(peaks) - 1, 1):
            prof_partial = mlc_mean_profile[peaks[p]:peaks[p + 1]]
            valleys.append(peaks[p] + np.argmin(prof_partial))
        edge_points = [peaks[0] - separation
                       ] + valleys + [peaks[-1] + separation]
        ax3.plot(edge_points, mlc_mean_profile[edge_points], "yo")

        for k in np.arange(0, len(edge_points) - 1, 1):
            pr = PylinacSingleProfile(
                mlc_mean_profile[edge_points[k]:edge_points[k + 1]])
            left = pr[0]
            right = pr[-1]
            amplitude = mlc_mean_profile[peaks[k]]
            if left < right:
                x = 100 * ((amplitude - left) * 0.5 + left -
                           right) / (amplitude - right)
                a = pr._penumbra_point(x=50, side="left", interpolate=True)
                b = pr._penumbra_point(x=x, side="right", interpolate=True)
            else:
                x = 100 * ((amplitude - right) * 0.5 + right -
                           left) / (amplitude - left)
                a = pr._penumbra_point(x=x, side="left", interpolate=True)
                b = pr._penumbra_point(x=50, side="right", interpolate=True)
            left_point = edge_points[k] + a
            right_point = edge_points[k] + b
            ax3.plot([left_point, right_point], [
                np.interp(left_point,
                          np.arange(0, len(mlc_mean_profile.values), 1),
                          mlc_mean_profile.values),
                np.interp(right_point,
                          np.arange(0, len(mlc_mean_profile.values), 1),
                          mlc_mean_profile.values)
            ],
                     "-k",
                     alpha=0.5)
            picket_fwhm.append(np.abs(a - b) * mmpd)

        fwhm_mean = np.mean(picket_fwhm)
    except:
        picket_fwhm = [np.nan] * picket_nr
        fwhm_mean = np.nan
    if len(picket_fwhm) != picket_nr:
        fwhm_mean = np.mean(picket_fwhm)
        picket_fwhm = [np.nan] * picket_nr

    ax2.set_xlim([-0.025, pf.settings.tolerance * 1.15])
    ax3.set_xlim([0, pf.image.shape[1]])
    ax2.set_title("Leaf error")
    ax3.set_title("MLC mean profile")
    ax2.set_xlabel("Error [mm]")
    ax2.set_ylabel("Counts")
    ax3.set_xlabel("Pixel")
    ax3.set_ylabel("Grey value")

    passed = "Passed" if pf.passed else "Failed"

    script = mpld3.fig_to_html(fig_pf, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    script2 = mpld3.fig_to_html(fig_pf2, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    variables = {
        "script": script,
        "script2": script2,
        "passed": passed,
        "max_error": max_error,
        "max_error_leaf": max_error_leaf,
        "passed_tol": passed_tol,
        "picket_nr": picket_nr,
        "tolerance": pf.settings.tolerance,
        "perc_passing": pf.percent_passing,
        "max_error_all": pf.max_error,
        "max_error_picket_all": pf.max_error_picket,
        "max_error_leaf_all": pf.max_error_leaf,
        "median_error": pf.abs_median_error,
        "spacing": pf.pickets.mean_spacing,
        "picket_offsets": picket_offsets,
        "fwhm_mean": fwhm_mean,
        "picket_fwhm": picket_fwhm,
        "pdf_report_enable": generate_pdf_report,
        "save_results": save_results,
        "acquisition_datetime": acquisition_datetime
    }

    # Generate pylinac report:
    if generate_pdf_report == "True":
        pdf_file = tempfile.NamedTemporaryFile(delete=False,
                                               prefix="PicketFence_",
                                               suffix=".pdf",
                                               dir=config.PDF_REPORT_FOLDER)
        metadata = RestToolbox.GetInstances(config.ORTHANC_URL, [w])
        try:
            patient = metadata[0]["PatientName"]
        except:
            patient = ""
        try:
            stationname = metadata[0]["StationName"]
        except:
            stationname = ""
        try:
            date_time = RestToolbox.get_datetime(metadata[0])
            date_var = datetime.datetime.strptime(
                date_time[0], "%Y%m%d").strftime("%d/%m/%Y")
        except:
            date_var = ""
        pf.publish_pdf(pdf_file,
                       notes=[
                           "Date = " + date_var, "Patient = " + patient,
                           "Station = " + stationname
                       ])

        variables["pdf_report_filename"] = os.path.basename(pdf_file.name)
    #gc.collect()

    general_functions.delete_files_in_subfolders([temp_folder])  # Delete image
    return template("picket_fence_results", variables)
Example #16
0
def catphan_calculate_helperf(args):
    use_reference = args["use_reference"]
    phantom = args["phantom"]
    machine = args["machine"]
    beam = args["beam"]
    HU_delta = args["HU_delta"]
    colormap = args["colormap"]
    displayname = args["displayname"]
    acquisition_datetime = args["acquisition_datetime"]
    s = args["s"]
    general_functions.set_configuration(args["config"])  # Transfer to this process

    save_results = {
                    "machine": machine,
                    "beam": beam,
                    "phantom": phantom,
                    "displayname": displayname
                    }
    # Set colormap
    cmap = matplotlib.cm.get_cmap(colormap)

    # Collect data for "save results"
    tolerances = general_functions.get_tolerance_user_machine_catphan(machine, beam, phantom)  # If user_machne has specific tolerance
    if not tolerances:
        hu, lcv, scaling, thickness, lowcontrast, cnr, mtf, uniformityidx, pdf_report_enable = "100", "2", "0.5", "0.25", "1", "10", "10", "3", "False"
    else:
        hu, lcv, scaling, thickness, lowcontrast, cnr, mtf, uniformityidx, pdf_report_enable = tolerances

    hu_tolerance = float(hu)
    lcv_tolerance = float(lcv)
    scaling_tolerance = float(scaling)
    thickness_tolerance = float(thickness)
    low_contrast_tolerance = float(lowcontrast)
    cnr_threshold = float(cnr)
    mtf_tolerance = float(mtf)
    uniformityidx_tolerance = float(uniformityidx)

    ref_path = general_functions.get_referenceimagepath_catphan(machine, beam, phantom)

    if ref_path is not None:
        ref_path = os.path.join(config.REFERENCE_IMAGES_FOLDER, ref_path[0])
        if os.path.exists(ref_path):
            ref_exists = True
        else:
            ref_exists = False
    else:
        ref_exists = False
    
    folder_path = RestToolbox.GetSeries2Folder2(config.ORTHANC_URL, s)

    # Use two threads to speedup the calculation (if ref exists)
    args_current = {"hu_tolerance": hu_tolerance, "scaling_tolerance": scaling_tolerance,
                    "thickness_tolerance": thickness_tolerance, 
                    "cnr_threshold": cnr_threshold, "path": folder_path, 
                    "phantom": phantom, "low_contrast_tolerance": low_contrast_tolerance,
                    "config": general_functions.get_configuration()}
    
    args_ref = {"hu_tolerance": hu_tolerance, "scaling_tolerance": scaling_tolerance,
                "thickness_tolerance": thickness_tolerance, 
                "cnr_threshold": cnr_threshold, "path": ref_path, 
                "phantom": phantom, "low_contrast_tolerance": low_contrast_tolerance,
                "config": general_functions.get_configuration()}

    if use_reference and ref_exists:
        try:
            p = ThreadPool(2)
            [mycbct, mycbct_ref] = p.map(catphan_helperf_analyze, [args_current, args_ref])
        finally:
            p.close()
            p.join()
    else:
        mycbct = catphan_helperf_analyze(args_current)

    if use_reference and ref_exists:
        if isinstance(mycbct_ref, Exception):
            return template("error_template", {"error_message": "Unable to analyze reference image. " + str(mycbct_ref)
                                               })
    if isinstance(mycbct, Exception):
        general_functions.delete_files_in_subfolders([folder_path]) # Delete temporary images
        return template("error_template", {"error_message": "Unable to analyze image. " + str(mycbct)
                                           })
    
    try:  # add this to prevent memory problems when threads with exceptions are still alive
        
        # ######################### CTP528 - Resolution ###################################
        fig_dcm = Figure(figsize=(10.5, 5), tight_layout={"w_pad":0,  "pad": 1.5})
        ax1 = fig_dcm.add_subplot(1,2,1)
        ax2 = fig_dcm.add_subplot(1,2,2)
    
        # Reference image array
        if use_reference and ref_exists:
            ax1.imshow(mycbct_ref.ctp528.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
            ax1.autoscale(enable=False)
        else:
            ax1.text(0.5, 0.5 ,"Reference image not available", horizontalalignment='center', verticalalignment='center')
    
        # Analysed current array
        ax2.imshow(mycbct.ctp528.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
        ax2.set_title('CTP528 current image')
        ax1.set_title('CTP528 reference image')
        ax2.autoscale(enable=False)
    
        # Plot rMTF and gather some data
        fig_mtf = Figure(figsize=(5, 5), tight_layout={"w_pad":2, "pad": 1})
        ax_mtf = fig_mtf.add_subplot(1,1,1)
        msize = 8
        if use_reference and ref_exists:
            ax_mtf.plot(list(mycbct_ref.ctp528.mtf.norm_mtfs.keys()), list(mycbct_ref.ctp528.mtf.norm_mtfs.values()), marker='o', color="blue",
                        markersize=msize, markerfacecolor="None", linestyle="--")
    
        ax_mtf.plot(list(mycbct.ctp528.mtf.norm_mtfs.keys()), list(mycbct.ctp528.mtf.norm_mtfs.values()), marker='o', color="blue", markersize=msize)
        ax_mtf.margins(0.05)
        ax_mtf.grid('on')
        ax_mtf.set_xlabel('Line pairs / mm')
        ax_mtf.set_ylabel("Relative MTF")
        ax_mtf.set_title('Modulation transfer function')
        
        script_ctp528 = mpld3.fig_to_html(fig_dcm, d3_url=D3_URL, mpld3_url=MPLD3_URL)
        script_ctp528mtf = mpld3.fig_to_html(fig_mtf, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    
        # Some data:
        mtf30_ref = mycbct_ref.ctp528.mtf.relative_resolution(30) if use_reference and ref_exists else np.nan
        mtf30 = mycbct.ctp528.mtf.relative_resolution(30)
        mtf50_ref = mycbct_ref.ctp528.mtf.relative_resolution(50) if use_reference and ref_exists else np.nan
        mtf50 = mycbct.ctp528.mtf.relative_resolution(50)
        mtf80_ref = mycbct_ref.ctp528.mtf.relative_resolution(80) if use_reference and ref_exists else np.nan
        mtf80 = mycbct.ctp528.mtf.relative_resolution(80)
    
        if use_reference and ref_exists:
            mtf_passing = True if abs(100*(mtf50-mtf50_ref)/mtf50_ref)<=mtf_tolerance else False
        else:
            mtf_passing = None
    
        # ####################### CTP404 - GEOMETRY HU LINEARITY ####################
        fig_404 = Figure(figsize=(10.5, 5), tight_layout={"w_pad":0,  "pad": 1.5})
        ax404_1 = fig_404.add_subplot(1,2,1)
        ax404_2 = fig_404.add_subplot(1,2,2)
    
        def ctp404_plotROI(mycbct, fig, axis):
            # Plot lines and circles - taken from pylinac
            # plot HU linearity ROIs
            for roi in mycbct.ctp404.hu_rois.values():
                axis.add_patch(matplotlib.patches.Circle((roi.center.x, roi.center.y), edgecolor=roi.plot_color, radius=roi.radius, fill=False))
            for roi in mycbct.ctp404.bg_hu_rois.values():
                axis.add_patch(matplotlib.patches.Circle((roi.center.x, roi.center.y), edgecolor='blue', radius=roi.radius, fill=False))
            # plot thickness ROIs
            for roi in mycbct.ctp404.thickness_rois.values():
                axis.add_patch(matplotlib.patches.Rectangle((roi.bl_corner.x, roi.bl_corner.y), width=roi.width, height=roi.height,
                                                               angle=0, edgecolor="blue", alpha=1, facecolor="g", fill=False))
            # plot geometry lines
            for line in mycbct.ctp404.lines.values():
                axis.plot((line.point1.x, line.point2.x), (line.point1.y, line.point2.y), linewidth=1, color=line.pass_fail_color)
    
            # Plot tooltips for patches
            names = []
            hu_rois_centers_x = []
            hu_rois_centers_y = []
            hu_rois_radius = []
    
            for name, roi in mycbct.ctp404.hu_rois.items():
                names.append(name)
                hu_rois_centers_x.append(roi.center.x)
                hu_rois_centers_y.append(roi.center.y)
                hu_rois_radius.append((roi.radius)**2)
    
            hu_rois_ttip = axis.scatter(hu_rois_centers_x, hu_rois_centers_y, s = hu_rois_radius, alpha=0)
            labels = [names[i] for i in range(len(names))]
            tooltip = mpld3.plugins.PointLabelTooltip(hu_rois_ttip, labels=labels)
            mpld3.plugins.connect(fig, tooltip)
            # Add tooltips for 4 lines
            inc = 1
            for line in mycbct.ctp404.lines.values():
                hu_lines_ttip = axis.plot((line.point1.x, line.point2.x), (line.point1.y, line.point2.y), alpha=0, lw=7)
                tooltip2 = mpld3.plugins.LineLabelTooltip(hu_lines_ttip[0], label="Line "+str(inc))
                mpld3.plugins.connect(fig, tooltip2)
                inc += 1
    
        # Reference image
        if use_reference and ref_exists:
            ax404_1.imshow(mycbct_ref.ctp404.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
            #mycbct_ref.ctp404.plot_rois(ax404_1)
            ctp404_plotROI(mycbct_ref, fig_404, ax404_1)  # alternative
            ax404_1.autoscale(enable=False)
            ax404_1.set_xlim([0, mycbct_ref.ctp404.image.shape[1]])
            ax404_1.set_ylim([mycbct_ref.ctp404.image.shape[0], 0])
        else:
            ax404_1.text(0.5, 0.5 ,"Reference image not available", horizontalalignment='center', verticalalignment='center')
        # Current image
        ax404_2.imshow(mycbct.ctp404.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
        ctp404_plotROI(mycbct, fig_404, ax404_2)  # alternative
        #mycbct.ctp404.plot_rois(ax404_2)
        ax404_2.set_xlim([0, mycbct.ctp404.image.shape[1]])
        ax404_2.set_ylim([mycbct.ctp404.image.shape[0], 0])
        ax404_1.set_title('CTP404 reference image')
        ax404_2.set_title('CTP404 current image')
        ax404_2.autoscale(enable=False)
    
        # Draw HU linearity plot
        def plot_linearity(mycbct, fig, axis, plot_delta):
            '''Taken from pylinac'''
            nominal_x_values = [roi.nominal_val for roi in mycbct.ctp404.hu_rois.values()]
            actual_values = []
            diff_values = []
            if plot_delta:
                values = []
                names = []
                for name, roi in mycbct.ctp404.hu_rois.items():
                    names.append(name)
                    values.append(roi.value_diff)
                    actual_values.append(roi.pixel_value)
                    diff_values.append(roi.value_diff)
                nominal_measurements = [0]*len(values)
                ylabel = 'HU Delta'
            else:
                values = []
                names = []
                for name, roi in mycbct.ctp404.hu_rois.items():
                    names.append(name)
                    values.append(roi.pixel_value)
                    actual_values.append(roi.pixel_value)
                    diff_values.append(roi.value_diff)
                nominal_measurements = nominal_x_values
                ylabel = 'Measured Values'
    
            points = axis.plot(nominal_x_values, values, 'g+', markersize=15, mew=2)
            axis.plot(nominal_x_values, nominal_measurements)
            axis.plot(nominal_x_values, np.array(nominal_measurements) + mycbct.ctp404.hu_tolerance, 'r--')
            axis.plot(nominal_x_values, np.array(nominal_measurements) - mycbct.ctp404.hu_tolerance, 'r--')
            axis.margins(0.07)
            axis.grid(True, alpha=0.35)
            axis.set_xlabel("Nominal Values")
            axis.set_ylabel(ylabel)
            axis.set_title("HU linearity")
            labels = [names[i]+" -- Nom.={:.1f}, Act.={:.1f}, Diff.={:.1f}".format(nominal_x_values[i], actual_values[i], diff_values[i]) for i in range(len(names))]
            tooltip = mpld3.plugins.PointLabelTooltip(points[0], labels=labels, location="top right")
            mpld3.plugins.connect(fig, tooltip)
    
        fig_404_HU = Figure(figsize=(10.5, 5), tight_layout={"w_pad":1})
        ax_HU_ref = fig_404_HU.add_subplot(1,2,1)
        ax_HU = fig_404_HU.add_subplot(1,2,2)
        # Reference HU linearity
        if use_reference and ref_exists:
            plot_linearity(mycbct_ref, fig_404_HU, ax_HU_ref, plot_delta=HU_delta)
        else:
            ax_HU_ref.text(0.5, 0.5 ,"Reference image not available", horizontalalignment='center', verticalalignment='center')
            ax_HU_ref.set_title("HU linearity")
        # Current HU linearity
        plot_linearity(mycbct, fig_404_HU, ax_HU, plot_delta=HU_delta)
        
        # Gather data from HU holes:
        if use_reference and ref_exists:
            HU_values_ref = []
            HU_std_ref = []
            HU_diff_ref = []
            cnrs404_ref = []
            for key, value in mycbct_ref.ctp404.hu_rois.items():
                HU_values_ref.append(value.pixel_value)
                HU_std_ref.append(round(value.std, 1))
                HU_diff_ref.append(value.value_diff)
                cnrs404_ref.append(round(value.cnr, 1))

            # Background HU ROIs
            for key, value in mycbct_ref.ctp404.bg_hu_rois.items():
                HU_values_ref.append(value.pixel_value)
                HU_std_ref.append(round(value.std, 1))
                HU_diff_ref.append(np.nan)
                cnrs404_ref.append(np.nan)
            
            lcv_ref = round(mycbct_ref.ctp404.lcv, 2)
            slice_thickness_ref = round(mycbct_ref.ctp404.meas_slice_thickness, 2)
            lines_ref = []  # Line length
            for l in mycbct_ref.ctp404.lines.values():
                lines_ref.append(round(l.length_mm, 2))
            lines_avg_ref = round(mycbct_ref.ctp404.avg_line_length, 2)
            phantom_roll_ref = round(mycbct_ref.ctp404.catphan_roll, 2)
            dicom_slice_thickness_ref = round(mycbct_ref.ctp404.slice_thickness, 2)
        else:
            length = len(list(mycbct.ctp404.hu_rois.values())+list(mycbct.ctp404.bg_hu_rois.values()))
            HU_values_ref = [np.nan]*length
            HU_std_ref = [np.nan]*length
            HU_diff_ref = [np.nan]*length
            cnrs404_ref = [np.nan]*length
            lcv_ref = np.nan
            slice_thickness_ref = np.nan
            lines_ref = [np.nan]*len(mycbct.ctp404.lines.values())
            lines_avg_ref = np.nan
            phantom_roll_ref = np.nan
            dicom_slice_thickness_ref = np.nan
            
    
        HU_values = []
        HU_std = []
        HU_diff = []
        HU_nominal = []
        HU_names = []
        cnrs404 = []
        HU_CNR_values_dict = {}
        for key, value in mycbct.ctp404.hu_rois.items():
            HU_values.append(value.pixel_value)
            HU_std.append(round(value.std, 1))
            HU_diff.append(value.value_diff)
            HU_nominal.append(value.nominal_val)
            HU_names.append(key)
            cnrs404.append(round(value.cnr, 1))
            HU_CNR_values_dict[key] = [value.pixel_value, round(value.cnr, 1)] 
        
        # Background HU ROIs
        for key, value in mycbct.ctp404.bg_hu_rois.items():
            HU_values.append(value.pixel_value)
            HU_std.append(round(value.std, 1))
            HU_diff.append(np.nan)
            HU_nominal.append(0)
            HU_names.append("Background "+str(key))
            cnrs404.append(np.nan)
            HU_CNR_values_dict[key] = [value.pixel_value, "nan"] 
        
        # For easier acces of values in results
        save_results["HU_CNR_values_dict"] = HU_CNR_values_dict
        lcv = mycbct.ctp404.lcv
        slice_thickness = round(mycbct.ctp404.meas_slice_thickness, 2)
        phantom_roll = round(mycbct.ctp404.catphan_roll, 2)
        dicom_slice_thickness = round(mycbct.ctp404.slice_thickness, 2)

        # Get origin slice and phantom center and slice number of other modules
        if use_reference and ref_exists:
            mm_per_pixel_ref = round(mycbct_ref.mm_per_pixel, 2)
            origin_slice_ref = mycbct_ref.origin_slice
            ctp528_slice_ref = mycbct_ref.ctp528.slice_num
            ctp486_slice_ref = mycbct_ref.ctp486.slice_num
            ctp515_slice_ref = mycbct_ref.ctp515.slice_num if phantom != "Catphan 503" else np.nan
            phantom_center_ref = [round(mycbct_ref.ctp404.phan_center.x, 2),
                                  round(mycbct_ref.ctp404.phan_center.y, 2)]
        else:
            mm_per_pixel_ref = np.nan
            origin_slice_ref = np.nan
            ctp528_slice_ref = np.nan
            ctp486_slice_ref = np.nan
            ctp515_slice_ref = np.nan
            phantom_center_ref = [np.nan, np.nan]
        mm_per_pixel = round(mycbct.mm_per_pixel, 2)
        origin_slice = mycbct.origin_slice
        ctp528_slice = mycbct.ctp528.slice_num
        ctp486_slice = mycbct.ctp486.slice_num
        ctp515_slice = mycbct.ctp515.slice_num if phantom != "Catphan 503" else np.nan
        phantom_center = [round(mycbct.ctp404.phan_center.x, 2), 
                          round(mycbct.ctp404.phan_center.y, 2)]
        
        lines = []  # Line length
        for l in mycbct.ctp404.lines.values():
            lines.append(round(l.length_mm, 2))
        lines_avg = round(mycbct.ctp404.avg_line_length, 2)
    
        passed_HU = mycbct.ctp404.passed_hu
        passed_thickness = mycbct.ctp404.passed_thickness
        passed_geometry = mycbct.ctp404.passed_geometry
    
        passed_lcv = True if lcv >= lcv_tolerance else False
        passed_404 = passed_HU and passed_thickness and passed_geometry and passed_lcv
    
        script_404 = mpld3.fig_to_html(fig_404, d3_url=D3_URL, mpld3_url=MPLD3_URL)
        script_404_HU = mpld3.fig_to_html(fig_404_HU, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    
        # ############################## CTP486 - UNIFORMITY ####################
        fig_486 = Figure(figsize=(10.5, 5), tight_layout={"w_pad":0,  "pad": 1.5})
        ax486_1 = fig_486.add_subplot(1,2,1)
        ax486_2 = fig_486.add_subplot(1,2,2)
    
        if use_reference and ref_exists:
            ax486_1.imshow(mycbct_ref.ctp486.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
            mycbct_ref.ctp486.plot_rois(ax486_1)
            # Add text inside ROI for reference to uniformity index:
            for ind, roi in enumerate(mycbct_ref.ctp486.rois.values()):
                ax486_1.text(roi.center.x, roi.center.y, str(ind), horizontalalignment='center', verticalalignment='center')
        else:
            ax486_1.text(0.5, 0.5 ,"Reference image not available", horizontalalignment='center', verticalalignment='center')
    
        ax486_2.imshow(mycbct.ctp486.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
        mycbct.ctp486.plot_rois(ax486_2)
        # Add text inside ROI for reference to uniformity index:
        for ind, roi in enumerate(mycbct.ctp486.rois.values()):
            ax486_2.text(roi.center.x, roi.center.y, str(ind), horizontalalignment='center', verticalalignment='center')
        ax486_1.set_title('CTP486 reference image')
        ax486_1.autoscale(enable=False)
        ax486_2.set_title('CTP486 current image')
        ax486_2.autoscale(enable=False)
    
        script_486 = mpld3.fig_to_html(fig_486, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    
        # Draw orthogonal profiles:
        fig_486_profile = Figure(figsize=(10.5, 5), tight_layout={"w_pad":0,  "pad": 1.5})
        ax486_profile_ref = fig_486_profile.add_subplot(1,2,1)
        ax486_profile = fig_486_profile.add_subplot(1,2,2)
        if use_reference and ref_exists:
            mycbct_ref.ctp486.plot_profiles(ax486_profile_ref)
        else:
            ax486_profile_ref.text(0.5, 0.5 ,"Reference image not available", horizontalalignment='center', verticalalignment='center')
        mycbct.ctp486.plot_profiles(ax486_profile)
        ax486_profile_ref.set_title('Reference uniformity profiles')
        ax486_profile.set_title('Current uniformity profiles')
    
        script_486_profile = mpld3.fig_to_html(fig_486_profile, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    
        # Get mean pixel values and uniformity index:
        # take into account slope and intercept HU = slope * px + intercept
    
        if use_reference and ref_exists:
            hvalues_ref = [roi.pixel_value for roi in mycbct_ref.ctp486.rois.values()]
            uidx_ref = round(mycbct_ref.ctp486.uniformity_index, 2)
        else:
            hvalues_ref = [np.nan]*len(mycbct.ctp486.rois.values())
            uidx_ref = np.nan
        hvalues = [roi.pixel_value for roi in mycbct.ctp486.rois.values()]
        passed_uniformity = mycbct.ctp486.overall_passed
    
        uidx = round(mycbct.ctp486.uniformity_index, 2)
    
        passed_uniformity_index = True if abs(uidx)<=uniformityidx_tolerance else False
    
        # ############################## CTP515 - LOW CONTRAST ####################
        if phantom != "Catphan 503":
            show_ctp515 = True
            fig_515 = Figure(figsize=(10.5, 5), tight_layout={"w_pad":0,  "pad": 1.5})
            ax515_1 = fig_515.add_subplot(1,2,1)
            ax515_2 = fig_515.add_subplot(1,2,2)
            if use_reference and ref_exists:
                ax515_1.imshow(mycbct_ref.ctp515.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
                mycbct_ref.ctp515.plot_rois(ax515_1)
            else:
                ax515_1.text(0.5, 0.5 ,"Reference image not available", horizontalalignment='center', verticalalignment='center')
        
            ax515_2.imshow(mycbct.ctp515.image.array, cmap=cmap, interpolation="none", aspect="equal", origin='upper')
            mycbct.ctp515.plot_rois(ax515_2)
            ax515_1.set_title('CTP515 reference image')
            ax515_1.autoscale(enable=False)
            ax515_2.set_title('CTP515 current image')
            ax515_2.autoscale(enable=False)
            
            script_515 = mpld3.fig_to_html(fig_515, d3_url=D3_URL, mpld3_url=MPLD3_URL)
        
            fig_515_contrast = Figure(figsize=(10, 5), tight_layout={"w_pad":1, "pad": 1})
            ax515_contrast = fig_515_contrast.add_subplot(1,2,1)
            ax515_cnr = fig_515_contrast.add_subplot(1,2,2)
            
            cnrs_names = []
            contrasts_515 = []
            cnrs515 = []
            for key, value in mycbct.ctp515.rois.items():
                cnrs_names.append(key)
                contrasts_515.append(value.contrast_constant)
                cnrs515.append(round(value.cnr_constant, 2))
            sizes_515 = np.array(cnrs_names, dtype=int)
            
            ax515_contrast.plot(sizes_515, contrasts_515, marker='o', color="blue",
                                markersize=8, markerfacecolor="None", linestyle="-")
            ax515_cnr.plot(sizes_515, cnrs515, marker='o', color="blue",
                                markersize=8, markerfacecolor="None", linestyle="-")
            
            if use_reference and ref_exists:
                contrasts_515_ref = []
                cnrs515_ref = []
                cnrs_names_ref = []
                for key, value in mycbct_ref.ctp515.rois.items():
                    cnrs_names_ref.append(key)
                    contrasts_515_ref.append(value.contrast_constant)
                    cnrs515_ref.append(round(value.cnr_constant, 2))
                sizes_515_ref = np.array(cnrs_names_ref, dtype=int)
                ax515_contrast.plot(sizes_515_ref, contrasts_515_ref, marker='o', color="blue",
                                    markersize=8, markerfacecolor="None", linestyle="--")
                ax515_cnr.plot(sizes_515_ref, cnrs515_ref, marker='o', color="blue",
                                    markersize=8, markerfacecolor="None", linestyle="--")
                ctp515_visible_ref = mycbct_ref.ctp515.rois_visible
            else:
                ctp515_visible_ref = np.nan
                cnrs515_ref = [np.nan]*len(mycbct.ctp515.rois.values())
            ax515_contrast.margins(0.05)
            ax515_contrast.grid(True)
            ax515_contrast.set_xlabel('ROI size (mm)')
            ax515_contrast.set_ylabel("Contrast * Diameter")
            ax515_cnr.margins(0.05)
            ax515_cnr.grid(True)
            ax515_cnr.set_xlabel('ROI size (mm)')
            ax515_cnr.set_ylabel("CNR * Diameter")
    
            script_515_contrast = mpld3.fig_to_html(fig_515_contrast, d3_url=D3_URL, mpld3_url=MPLD3_URL)
    
            ctp515_passed = mycbct.ctp515.overall_passed
            #ctp515_passed = None
            ctp515_visible = mycbct.ctp515.rois_visible
        else:
            show_ctp515 = False
            script_515_contrast = None
            script_515 = None
            ctp515_visible_ref = np.nan
            ctp515_passed = None
            ctp515_visible = np.nan
            cnrs515_ref = None
            cnrs515 = None
            cnrs_names = None
    
        general_functions.delete_files_in_subfolders([folder_path]) # Delete temporary images

        variables = {
                    "script_ctp528": script_ctp528,
                    "script_ctp528mtf": script_ctp528mtf,
                    "mtf30_ref": round(mtf30_ref, 2),
                    "mtf30": round(mtf30, 2),
                    "mtf50_ref": round(mtf50_ref, 2),
                    "mtf50": round(mtf50, 2),
                    "mtf80_ref": round(mtf80_ref, 2),
                    "mtf80": round(mtf80, 2),
                    "mtf_passing": mtf_passing,
                    "script_404": script_404,
                    "script_404_HU": script_404_HU,
                    "HU_values_ref": HU_values_ref,
                    "HU_std_ref": HU_std_ref,
                    "HU_values": HU_values,
                    "HU_std": HU_std,
                    "HU_nominal": HU_nominal,
                    "HU_names": HU_names,
                    "HU_diff_ref": HU_diff_ref,
                    "HU_diff": HU_diff,
                    "passed_HU": passed_HU,
                    "passed_thickness": passed_thickness,
                    "passed_geometry": passed_geometry,
                    "passed_lcv": passed_lcv,
                    "passed_404": passed_404,
                    "lcv_ref": lcv_ref,
                    "lcv": round(lcv, 2),
                    "slice_thickness": slice_thickness,
                    "slice_thickness_ref": slice_thickness_ref,
                    "dicom_slice_thickness": dicom_slice_thickness,
                    "dicom_slice_thickness_ref": dicom_slice_thickness_ref,
                    "lines_ref": lines_ref,
                    "lines": lines,
                    "lines_avg": lines_avg,
                    "lines_avg_ref": lines_avg_ref,
                    "phantom_roll": phantom_roll,
                    "phantom_roll_ref": phantom_roll_ref,
                    "origin_slice_ref": origin_slice_ref,
                    "origin_slice": origin_slice,
                    "ctp528_slice": ctp528_slice,
                    "ctp486_slice": ctp486_slice,
                    "ctp515_slice": ctp515_slice,
                    "ctp528_slice_ref": ctp528_slice_ref,
                    "ctp486_slice_ref": ctp486_slice_ref,
                    "ctp515_slice_ref": ctp515_slice_ref,
                    "phantom_center_ref": phantom_center_ref,
                    "phantom_center": phantom_center,
                    "mm_per_pixel": mm_per_pixel,
                    "mm_per_pixel_ref": mm_per_pixel_ref,
                    "cnrs404_ref" : cnrs404_ref,
                    "cnrs404": cnrs404,
                    "script_486": script_486,
                    "script_486_profile": script_486_profile,
                    "hvalues_ref": hvalues_ref,
                    "hvalues": hvalues,
                    "passed_uniformity": passed_uniformity,
                    "passed_uniformity_index": passed_uniformity_index,
                    "uidx": uidx,
                    "uidx_ref": uidx_ref,
                    "script_515": script_515,
                    "script_515_contrast": script_515_contrast,
                    "show_ctp515": show_ctp515,
                    "ctp515_passed": ctp515_passed,
                    "ctp515_visible": ctp515_visible,
                    "ctp515_visible_ref": ctp515_visible_ref,
                    "cnrs515_ref": cnrs515_ref,
                    "cnrs515": cnrs515,
                    "cnrs_names": cnrs_names,
                    "save_results": save_results,
                    "acquisition_datetime": acquisition_datetime,
                    "pdf_report_enable": pdf_report_enable
                    }

        # Generate pylinac report:
        if pdf_report_enable == "True":
            pdf_file = tempfile.NamedTemporaryFile(delete=False, prefix="Catphan", suffix=".pdf", dir=config.PDF_REPORT_FOLDER)
            mycbct.publish_pdf(pdf_file)
            variables["pdf_report_filename"] = os.path.basename(pdf_file.name)
    except Exception as e:
        general_functions.delete_files_in_subfolders([folder_path]) # Delete temporary images
        return template("error_template", {"error_message": "Cannot analyze image. "+str(e)
                                           })
    else:
        return template("catphan_results", variables)
Example #17
0
def fieldrot_helperf(args):

    test_type = args["test_type"]
    direction = args["direction"]
    direction2 = args["direction2"]
    number_samples = args["number_samples"]
    margin = args["margin"]
    clipbox = args["clipbox"]
    invert = args["invert"]
    w1 = args["w1"]
    w2 = args["w2"]
    colormap = args["colormap"]
    med_filter = args["med_filter"]
    general_functions.set_configuration(args["config"])
    imgdescription = args["imgdescription"]
    station = args["station"]
    displayname = args["displayname"]
    acquisition_datetime = args["acquisition_datetime"]
    high_contrast = args["high_contrast"]

    # Collect data for "save results"
    dicomenergy = general_functions.get_energy_from_imgdescription(
        imgdescription)
    user_machine, user_energy = general_functions.get_user_machine_and_energy(
        station, dicomenergy)
    machines_and_energies = general_functions.get_machines_and_energies(
        general_functions.get_treatmentunits_fieldrotation())
    tolerances = general_functions.get_tolerance_user_machine_fieldrotation(
        user_machine)  # If user_machne has specific tolerance

    if not tolerances:
        tt = general_functions.get_settings_fieldrotation()
    else:
        tt = tolerances[0]

    (tolerance_collabs, tolerance_collrel, tolerance_couchrel) = tt

    tolerance_collabs = float(tolerance_collabs)
    tolerance_collrel = float(tolerance_collrel)
    tolerance_couchrel = float(tolerance_couchrel)

    save_results = {
        "user_machine": user_machine,
        "user_energy": user_energy,
        "machines_and_energies": machines_and_energies,
        "displayname": displayname,
        "nominal_angle": np.linspace(360, -360, 49).tolist()
    }

    try:
        temp_folder1, file_path1 = RestToolbox.GetSingleDcm(
            config.ORTHANC_URL, w1)
        temp_folder2, file_path2 = RestToolbox.GetSingleDcm(
            config.ORTHANC_URL, w2)
    except:
        return template("error_template",
                        {"error_message": "Cannot read images."})

    # Load first image
    try:
        img1 = pylinac_image.DicomImage(file_path1)
        # Here we force pixels to background outside of box:
        if clipbox != 0:
            try:
                img1.check_inversion_by_histogram(percentiles=[
                    4, 50, 96
                ])  # Check inversion otherwise this might not work
                general_functions.clip_around_image(img1, clipbox)
            except Exception as e:
                return template(
                    "error_template",
                    {"error_message": "Unable to apply clipbox. " + str(e)})
        else:
            img1.remove_edges(pixels=2)
        if invert:
            img1.invert()
        else:
            img1.check_inversion()
        img1.flipud()
    except:
        return template("error_template",
                        {"error_message": "Cannot read image."})
    try:
        img2 = pylinac_image.DicomImage(file_path2)
        if clipbox != 0:
            try:
                img2.check_inversion_by_histogram(percentiles=[
                    4, 50, 96
                ])  # Check inversion otherwise this might not work
                general_functions.clip_around_image(img2, clipbox)
            except Exception as e:
                return template(
                    "error_template",
                    {"error_message": "Unable to apply clipbox. " + str(e)})
        else:
            img2.remove_edges(pixels=2)
        if invert:
            img2.invert()
        else:
            img2.check_inversion()
        img2.flipud()
    except:
        return template("error_template",
                        {"error_message": "Cannot read image."})

    # Apply some filtering
    if med_filter > 0:
        img1.filter(med_filter)
        img2.filter(med_filter)

    # Get radiation field box:
    try:
        center_cax1, rad_field_bounding_box1, field_corners1 = field_rotation._find_field_centroid(
            img1)
        center_cax2, rad_field_bounding_box2, field_corners2 = field_rotation._find_field_centroid(
            img2)
    except Exception as e:
        return template("error_template", {"error_message": str(e)})

    field_corners1 = field_corners1.astype(int)
    field_corners2 = field_corners2.astype(int)

    # Set colormap
    cmap = matplotlib.cm.get_cmap(colormap)

    if test_type == "Collimator absolute":
        # Get BBs
        try:
            if high_contrast:
                bbs1 = field_rotation._find_bb2(img1, rad_field_bounding_box1)
                bbs2 = field_rotation._find_bb2(img2, rad_field_bounding_box2)
            else:
                bbs1 = field_rotation._find_bb(img1, rad_field_bounding_box1)
                bbs2 = field_rotation._find_bb(img2, rad_field_bounding_box2)
        except Exception as e:
            return template("error_template", {"error_message": str(e)})

        bb_coord1_1, bw_bb_im1_1 = bbs1[0]
        bb_coord1_2, bw_bb_im1_2 = bbs1[1]
        bb_coord2_1, bw_bb_im2_1 = bbs2[0]
        bb_coord2_2, bw_bb_im2_2 = bbs2[1]

        center1_1 = (bb_coord1_1[0], bb_coord1_1[1])
        center1_2 = (bb_coord1_2[0], bb_coord1_2[1])
        center2_1 = (bb_coord2_1[0], bb_coord2_1[1])
        center2_2 = (bb_coord2_2[0], bb_coord2_2[1])

        # Line between BBs:
        bb_angle1 = np.arctan(np.inf if bb_coord1_1[0] - bb_coord1_2[0] ==
                              0 else (bb_coord1_1[1] - bb_coord1_2[1]) /
                              (bb_coord1_1[0] - bb_coord1_2[0])) * 180 / np.pi
        bb_angle2 = np.arctan(np.inf if bb_coord2_1[0] - bb_coord2_2[0] ==
                              0 else (bb_coord2_1[1] - bb_coord2_2[1]) /
                              (bb_coord2_1[0] - bb_coord2_2[0])) * 180 / np.pi

        img1_filled = np.copy(img1.array)
        img2_filled = np.copy(img2.array)

        # Fill BBs area with neighbouring values
        field_rotation.fill_BB_hole(bb_coord1_1, bw_bb_im1_1, img1_filled)
        field_rotation.fill_BB_hole(bb_coord1_2, bw_bb_im1_2, img1_filled)
        field_rotation.fill_BB_hole(bb_coord2_1, bw_bb_im2_1, img2_filled)
        field_rotation.fill_BB_hole(bb_coord2_2, bw_bb_im2_2, img2_filled)

        # Get penumbra points
        try:
            samples_left1, samples_right1, p_left1, p_right1 = field_rotation.find_penumbra_points(
                direction, number_samples, field_corners1, margin, img1_filled)
            samples_left2, samples_right2, p_left2, p_right2 = field_rotation.find_penumbra_points(
                direction2, number_samples, field_corners2, margin,
                img2_filled)
        except Exception as e:
            return template("error_template", {"error_message": str(e)})

        # Calculate field edge slopes
        pmin = 0
        pmax = np.max(
            img1.shape)  # Maksimum point for drawing regression lines

        if direction == "X":
            left_slope1, left_P1, left_err1 = field_rotation.calculate_regression(
                p_left1, samples_left1, pmin, pmax)
            right_slope1, right_P1, right_err1 = field_rotation.calculate_regression(
                p_right1, samples_right1, pmin, pmax)
        else:
            left_slope1, left_P1, left_err1 = field_rotation.calculate_regression(
                samples_left1, p_left1, pmin, pmax)
            right_slope1, right_P1, right_err1 = field_rotation.calculate_regression(
                samples_right1, p_right1, pmin, pmax)

        if direction2 == "X":
            left_slope2, left_P2, left_err2 = field_rotation.calculate_regression(
                p_left2, samples_left2, pmin, pmax)
            right_slope2, right_P2, right_err2 = field_rotation.calculate_regression(
                p_right2, samples_right2, pmin, pmax)
        else:

            left_slope2, left_P2, left_err2 = field_rotation.calculate_regression(
                samples_left2, p_left2, pmin, pmax)
            right_slope2, right_P2, right_err2 = field_rotation.calculate_regression(
                samples_right2, p_right2, pmin, pmax)

        left_edge_angle1 = np.arctan(left_slope1) * 180 / np.pi
        left_edge_angle2 = np.arctan(left_slope2) * 180 / np.pi
        right_edge_angle1 = np.arctan(right_slope1) * 180 / np.pi
        right_edge_angle2 = np.arctan(right_slope2) * 180 / np.pi

        # First plot: field and penumbra points
        fig1 = Figure(figsize=(10.5, 5.5), tight_layout={"w_pad": 3, "pad": 3})
        ax1 = fig1.add_subplot(1, 2, 1)
        ax2 = fig1.add_subplot(1, 2, 2)
        # Plot error bars and goodness of fit
        fig2 = Figure(figsize=(10, 4), tight_layout={"w_pad": 0, "pad": 1})
        ax3 = fig2.add_subplot(1, 2, 1)
        ax4 = fig2.add_subplot(1, 2, 2)
        # Plot angled lines
        fig3 = Figure(figsize=(10, 4), tight_layout={"w_pad": 0, "pad": 1})
        ax5 = fig3.add_subplot(1, 2, 1)
        ax6 = fig3.add_subplot(1, 2, 2)

        ax1.imshow(img1.array,
                   cmap=cmap,
                   interpolation="none",
                   aspect="equal",
                   origin='lower')
        ax1.set_title('Image 1')
        ax1.axis('off')
        ax2.imshow(img2.array,
                   cmap=cmap,
                   interpolation="none",
                   aspect="equal",
                   origin='lower')
        ax2.set_title('Image 2')
        ax2.axis('off')

        #Plot field corners
        ax1.plot(field_corners1[:, 1],
                 field_corners1[:, 0],
                 "mo",
                 markersize=5,
                 markeredgewidth=0)
        ax2.plot(field_corners2[:, 1],
                 field_corners2[:, 0],
                 "mo",
                 markersize=5,
                 markeredgewidth=0)

        # Plot penumbra points
        if direction == "X":
            ax1.plot(p_left1,
                     samples_left1,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax1.plot(p_right1,
                     samples_right1,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)
        else:
            ax1.plot(samples_left1,
                     p_left1,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax1.plot(samples_right1,
                     p_right1,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)

        ax1.plot(left_P1[0], left_P1[1], "b--")
        ax1.plot(right_P1[0], right_P1[1], "y--")

        if direction2 == "X":
            ax2.plot(p_left2,
                     samples_left2,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax2.plot(p_right2,
                     samples_right2,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)
        else:
            ax2.plot(samples_left2,
                     p_left2,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax2.plot(samples_right2,
                     p_right2,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)

        ax2.plot(left_P2[0], left_P2[1], "b--")
        ax2.plot(right_P2[0], right_P2[1], "y--")

        # Plot errors:
        ax3.plot(samples_left1, left_err1, "bx", markeredgewidth=2)
        ax3.plot(samples_right1, right_err1, "yx", markeredgewidth=2)
        ax4.plot(samples_left2, left_err2, "bx", markeredgewidth=2)
        ax4.plot(samples_right2, right_err2, "yx", markeredgewidth=2)

        limits_max = np.amax([
            np.amax(left_err1),
            np.amax(right_err1),
            np.amax(left_err2),
            np.amax(right_err2)
        ]) * 1.05
        limits_min = np.amin([
            np.amin(left_err1),
            np.amin(right_err1),
            np.amin(left_err2),
            np.amin(right_err2)
        ]) * 0.95
        limits = np.amax([abs(limits_max), abs(limits_min)])
        limits = limits if limits > 1 else 1

        ax3.set_ylim([-limits, limits])
        ax4.set_ylim([-limits, limits])
        ax3.set_ylabel("Deviation from fit [px]")
        ax4.set_ylabel("Deviation from fit [px]")
        ax3.set_xlabel("Field edge [px]")
        ax4.set_xlabel("Field edge [px]")
        ax3.set_title('Image 1 - Regression error')
        ax4.set_title('Image 2 - Regression error')

        # Plot angled lines:
        # If angles are negative, convert to [pi, 2pi]
        if abs(bb_angle1) > 80 and abs(bb_angle1) <= 90:
            B1 = left_edge_angle1 if left_edge_angle1 >= 0 else 180 + left_edge_angle1
            Y1 = right_edge_angle1 if right_edge_angle1 >= 0 else 180 + right_edge_angle1
            BB1 = bb_angle1 if bb_angle1 >= 0 else 180 + bb_angle1
            ref_angle_plot = PI / 2  # Reference angle for drawing (either 0 or 90)
        else:
            B1 = left_edge_angle1
            Y1 = right_edge_angle1
            BB1 = bb_angle1
            ref_angle_plot = 0

        if abs(bb_angle2) > 80 and abs(bb_angle2) <= 90:
            B2 = left_edge_angle2 if left_edge_angle2 >= 0 else 180 + left_edge_angle2
            Y2 = right_edge_angle2 if right_edge_angle2 >= 0 else 180 + right_edge_angle2
            BB2 = bb_angle2 if bb_angle2 >= 0 else 180 + bb_angle2
        else:
            B2 = left_edge_angle2
            Y2 = right_edge_angle2
            BB2 = bb_angle2

        a = 2
        x_bb1 = [-a * np.cos(BB1 * PI / 180), a * np.cos(BB1 * PI / 180)]
        y_bb1 = [-a * np.sin(BB1 * PI / 180), a * np.sin(BB1 * PI / 180)]

        x_b1 = [-a * np.cos((B1) * PI / 180), a * np.cos((B1) * PI / 180)]
        y_b1 = [-a * np.sin((B1) * PI / 180), a * np.sin((B1) * PI / 180)]

        x_b2 = [
            -a * np.cos((BB1 - (B2 - BB2)) * PI / 180), a * np.cos(
                (BB1 - (B2 - BB2)) * PI / 180)
        ]
        y_b2 = [
            -a * np.sin((BB1 - (B2 - BB2)) * PI / 180), a * np.sin(
                (BB1 - (B2 - BB2)) * PI / 180)
        ]

        x_y1 = [-a * np.cos((Y1) * PI / 180), a * np.cos((Y1) * PI / 180)]
        y_y1 = [-a * np.sin((Y1) * PI / 180), a * np.sin((Y1) * PI / 180)]

        x_y2 = [
            -a * np.cos((BB1 - (Y2 - BB2)) * PI / 180), a * np.cos(
                (BB1 - (Y2 - BB2)) * PI / 180)
        ]
        y_y2 = [
            -a * np.sin((BB1 - (Y2 - BB2)) * PI / 180), a * np.sin(
                (BB1 - (Y2 - BB2)) * PI / 180)
        ]

        ax5.plot(x_bb1, y_bb1, "g-", label="BB")
        ax5.plot(x_b1, y_b1, "b-", label="Gantry 0")
        ax5.plot(x_b2, y_b2, "b--", label="Gantry 180")

        ax6.plot(x_bb1, y_bb1, "g-", label="BB")
        ax6.plot(x_y1, y_y1, "y-", label="Gantry 0")
        ax6.plot(x_y2, y_y2, "y--", label="Gantry 180")

        if ref_angle_plot == PI / 2:
            max_xb = np.amax([np.abs(x_b1), np.abs(x_b2)])
            max_xy = np.amax([np.abs(x_y1), np.abs(x_y2)])
            ax5.set_xlim([-2 * max_xb, 2 * max_xb])
            ax5.set_ylim([-1, 1])
            ax6.set_xlim([-2 * max_xy, 2 * max_xy])
            ax6.set_ylim([-1, 1])
        else:
            max_y = np.amax([np.abs(y_b1), np.abs(y_b2)])
            max_yy = np.amax([np.abs(y_y1), np.abs(y_y2)])
            ax5.set_ylim([-2 * max_y, 2 * max_y])
            ax5.set_xlim([-1, 1])
            ax6.set_ylim([-2 * max_yy, 2 * max_yy])
            ax6.set_xlim([-1, 1])

        ax5.legend(loc='upper right', fontsize=10, edgecolor="none")
        ax5.set_title("Blue edge")
        ax5.set_xlabel("LAT [px]")
        ax5.set_ylabel("LONG [px]")
        ax6.legend(loc='upper right', fontsize=10, edgecolor="none")
        ax6.set_title("Yellow edge")
        ax6.set_xlabel("LAT [px]")
        ax6.set_ylabel("LONG [px]")

        # Plot BB line and crosses
        ax1.plot([bb_coord1_1[0], bb_coord1_2[0]],
                 [bb_coord1_1[1], bb_coord1_2[1]], "g-")
        ax2.plot([bb_coord2_1[0], bb_coord2_2[0]],
                 [bb_coord2_1[1], bb_coord2_2[1]], "g-")
        ax1.plot(center1_1[0],
                 center1_1[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)
        ax1.plot(center1_2[0],
                 center1_2[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)
        ax2.plot(center2_1[0],
                 center2_1[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)
        ax2.plot(center2_2[0],
                 center2_2[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)

        ax1.set_xlim([0, img1.shape[1]])
        ax1.set_ylim([0, img1.shape[0]])
        ax2.set_xlim([0, img2.shape[1]])
        ax2.set_ylim([0, img2.shape[0]])

        script1 = mpld3.fig_to_html(fig1, d3_url=D3_URL, mpld3_url=MPLD3_URL)
        script2 = mpld3.fig_to_html(fig2, d3_url=D3_URL, mpld3_url=MPLD3_URL)
        script3 = mpld3.fig_to_html(fig3, d3_url=D3_URL, mpld3_url=MPLD3_URL)

        variables = {
            "script1": script1,
            "script2": script2,
            "script3": script3,
            "left_edge_angle1": left_edge_angle1,
            "left_edge_angle2": left_edge_angle2,
            "right_edge_angle1": right_edge_angle1,
            "right_edge_angle2": right_edge_angle2,
            "bb_angle1": bb_angle1,
            "bb_angle2": bb_angle2,
            "test_type": test_type,
            "tolerance": tolerance_collabs
        }

    elif test_type == "Collimator relative":

        # Get penumbra points
        try:
            samples_left1, samples_right1, p_left1, p_right1 = field_rotation.find_penumbra_points(
                direction, number_samples, field_corners1, margin, img1.array)
            samples_left2, samples_right2, p_left2, p_right2 = field_rotation.find_penumbra_points(
                direction2, number_samples, field_corners2, margin, img2.array)
        except Exception as e:
            return template("error_template", {"error_message": str(e)})

        # Calculate field edge slopes
        pmin = 0
        pmax = np.max(
            img1.shape)  # Maksimum point for drawing regression lines

        if direction == "X":
            left_slope1, left_P1, left_err1 = field_rotation.calculate_regression(
                p_left1, samples_left1, pmin, pmax)
            right_slope1, right_P1, right_err1 = field_rotation.calculate_regression(
                p_right1, samples_right1, pmin, pmax)
        else:
            left_slope1, left_P1, left_err1 = field_rotation.calculate_regression(
                samples_left1, p_left1, pmin, pmax)
            right_slope1, right_P1, right_err1 = field_rotation.calculate_regression(
                samples_right1, p_right1, pmin, pmax)

        if direction2 == "X":
            left_slope2, left_P2, left_err2 = field_rotation.calculate_regression(
                p_left2, samples_left2, pmin, pmax)
            right_slope2, right_P2, right_err2 = field_rotation.calculate_regression(
                p_right2, samples_right2, pmin, pmax)
        else:
            left_slope2, left_P2, left_err2 = field_rotation.calculate_regression(
                samples_left2, p_left2, pmin, pmax)
            right_slope2, right_P2, right_err2 = field_rotation.calculate_regression(
                samples_right2, p_right2, pmin, pmax)

        left_edge_angle1 = np.arctan(left_slope1) * 180 / np.pi
        left_edge_angle2 = np.arctan(left_slope2) * 180 / np.pi
        right_edge_angle1 = np.arctan(right_slope1) * 180 / np.pi
        right_edge_angle2 = np.arctan(right_slope2) * 180 / np.pi

        # First plot: field and penumbra points
        fig1 = Figure(figsize=(10.5, 5.5), tight_layout={"w_pad": 3, "pad": 3})
        ax1 = fig1.add_subplot(1, 2, 1)
        ax2 = fig1.add_subplot(1, 2, 2)
        # Plot error bars and goodness of fit
        fig2 = Figure(figsize=(10, 4), tight_layout={"w_pad": 0, "pad": 1})
        ax3 = fig2.add_subplot(1, 2, 1)
        ax4 = fig2.add_subplot(1, 2, 2)

        ax1.imshow(img1.array,
                   cmap=cmap,
                   interpolation="none",
                   aspect="equal",
                   origin='lower')
        ax1.set_title('Image 1')
        ax1.axis('off')
        ax2.imshow(img2.array,
                   cmap=cmap,
                   interpolation="none",
                   aspect="equal",
                   origin='lower')
        ax2.set_title('Image 2')
        ax2.axis('off')

        #Plot field corners
        ax1.plot(field_corners1[:, 1],
                 field_corners1[:, 0],
                 "mo",
                 markersize=5,
                 markeredgewidth=0)
        ax2.plot(field_corners2[:, 1],
                 field_corners2[:, 0],
                 "mo",
                 markersize=5,
                 markeredgewidth=0)

        # Plot penumbra points
        if direction == "X":
            ax1.plot(p_left1,
                     samples_left1,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax1.plot(p_right1,
                     samples_right1,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)

        else:
            ax1.plot(samples_left1,
                     p_left1,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax1.plot(samples_right1,
                     p_right1,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)

        ax1.plot(left_P1[0], left_P1[1], "b--")
        ax1.plot(right_P1[0], right_P1[1], "y--")

        if direction2 == "X":
            ax2.plot(p_left2,
                     samples_left2,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax2.plot(p_right2,
                     samples_right2,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)

        else:
            ax2.plot(samples_left2,
                     p_left2,
                     "bx",
                     markersize=5,
                     markeredgewidth=2)
            ax2.plot(samples_right2,
                     p_right2,
                     "yx",
                     markersize=5,
                     markeredgewidth=2)

        ax2.plot(left_P2[0], left_P2[1], "b--")
        ax2.plot(right_P2[0], right_P2[1], "y--")

        ax1.set_xlim([0, img1.shape[1]])
        ax1.set_ylim([0, img1.shape[0]])
        ax2.set_xlim([0, img2.shape[1]])
        ax2.set_ylim([0, img2.shape[0]])

        # Plot errors:
        ax3.plot(samples_left1, left_err1, "bx", markeredgewidth=2)
        ax3.plot(samples_right1, right_err1, "yx", markeredgewidth=2)
        ax4.plot(samples_left2, left_err2, "bx", markeredgewidth=2)
        ax4.plot(samples_right2, right_err2, "yx", markeredgewidth=2)

        limits_max = np.amax([
            np.amax(left_err1),
            np.amax(right_err1),
            np.amax(left_err2),
            np.amax(right_err2)
        ]) * 1.05
        limits_min = np.amin([
            np.amin(left_err1),
            np.amin(right_err1),
            np.amin(left_err2),
            np.amin(right_err2)
        ]) * 0.95
        limits = np.amax([abs(limits_max), abs(limits_min)])
        limits = limits if limits > 1 else 1

        ax3.set_ylim([-limits, limits])
        ax4.set_ylim([-limits, limits])
        ax3.set_ylabel("Deviation from fit [px]")
        ax4.set_ylabel("Deviation from fit [px]")
        ax3.set_xlabel("Field edge [px]")
        ax4.set_xlabel("Field edge [px]")
        ax3.set_title('Image 1 - Regression error')
        ax4.set_title('Image 2 - Regression error')

        script1 = mpld3.fig_to_html(fig1, d3_url=D3_URL, mpld3_url=MPLD3_URL)
        script2 = mpld3.fig_to_html(fig2, d3_url=D3_URL, mpld3_url=MPLD3_URL)

        variables = {
            "script1": script1,
            "script2": script2,
            "script3": "",
            "left_edge_angle1": left_edge_angle1,
            "left_edge_angle2": left_edge_angle2,
            "right_edge_angle1": right_edge_angle1,
            "right_edge_angle2": right_edge_angle2,
            "test_type": test_type,
            "tolerance": tolerance_collrel
        }

    else:  # Couch rotation
        # Get BBs
        try:
            if high_contrast:
                bbs1 = field_rotation._find_bb2(img1, rad_field_bounding_box1)
                bbs2 = field_rotation._find_bb2(img2, rad_field_bounding_box2)
            else:
                bbs1 = field_rotation._find_bb(img1, rad_field_bounding_box1)
                bbs2 = field_rotation._find_bb(img2, rad_field_bounding_box2)
        except Exception as e:
            return template("error_template", {"error_message": str(e)})

        bb_coord1_1, bw_bb_im1_1 = bbs1[0]
        bb_coord1_2, bw_bb_im1_2 = bbs1[1]
        bb_coord2_1, bw_bb_im2_1 = bbs2[0]
        bb_coord2_2, bw_bb_im2_2 = bbs2[1]

        center1_1 = [bb_coord1_1[0], bb_coord1_1[1]]
        center1_2 = [bb_coord1_2[0], bb_coord1_2[1]]
        center2_1 = [bb_coord2_1[0], bb_coord2_1[1]]
        center2_2 = [bb_coord2_2[0], bb_coord2_2[1]]

        bb_line_center1 = [
            np.average([center1_1[0], center1_2[0]]),
            np.average([center1_1[1], center1_2[1]])
        ]
        bb_line_center2 = [
            np.average([center2_1[0], center2_2[0]]),
            np.average([center2_1[1], center2_2[1]])
        ]

        # Line between BBs:
        bb_angle1 = np.arctan(np.inf if bb_coord1_1[0] - bb_coord1_2[0] ==
                              0 else (bb_coord1_1[1] - bb_coord1_2[1]) /
                              (bb_coord1_1[0] - bb_coord1_2[0])) * 180 / np.pi
        bb_angle2 = np.arctan(np.inf if bb_coord2_1[0] - bb_coord2_2[0] ==
                              0 else (bb_coord2_1[1] - bb_coord2_2[1]) /
                              (bb_coord2_1[0] - bb_coord2_2[0])) * 180 / np.pi

        fig1 = Figure(figsize=(10.5, 5.5), tight_layout={"w_pad": 3, "pad": 3})
        ax1 = fig1.add_subplot(1, 2, 1)
        ax2 = fig1.add_subplot(1, 2, 2)

        ax1.imshow(img1.array,
                   cmap=cmap,
                   interpolation="none",
                   aspect="equal",
                   origin='lower')
        ax1.set_title('Image 1')
        ax1.axis('off')
        ax2.imshow(img2.array,
                   cmap=cmap,
                   interpolation="none",
                   aspect="equal",
                   origin='lower')
        ax2.set_title('Image 2')
        ax2.axis('off')

        # Plot BB line and crosses
        ax1.plot([bb_coord1_1[0], bb_coord1_2[0]],
                 [bb_coord1_1[1], bb_coord1_2[1]], "g-")
        ax2.plot([bb_coord2_1[0], bb_coord2_2[0]],
                 [bb_coord2_1[1], bb_coord2_2[1]], "g-")
        ax1.plot(center1_1[0],
                 center1_1[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)
        ax1.plot(center1_2[0],
                 center1_2[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)
        ax2.plot(center2_1[0],
                 center2_1[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)
        ax2.plot(center2_2[0],
                 center2_2[1],
                 'r+',
                 markersize=10,
                 markeredgewidth=2)

        ax1.set_xlim([0, img1.shape[1]])
        ax1.set_ylim([0, img1.shape[0]])
        ax2.set_xlim([0, img2.shape[1]])
        ax2.set_ylim([0, img2.shape[0]])

        script1 = mpld3.fig_to_html(fig1, d3_url=D3_URL, mpld3_url=MPLD3_URL)

        variables = {
            "script1": script1,
            "script2": "",
            "script3": "",
            "bb_angle1": bb_angle1,
            "bb_angle2": bb_angle2,
            "bb_line_center1": bb_line_center1,
            "bb_line_center2": bb_line_center2,
            "test_type": test_type,
            "tolerance": tolerance_couchrel
        }
    variables["acquisition_datetime"] = acquisition_datetime
    variables["save_results"] = save_results
    general_functions.delete_files_in_subfolders([temp_folder1, temp_folder2
                                                  ])  # Delete image
    return template("fieldrot_results", variables)
Example #18
0
def image_review_helperf(args):
    converthu = args["converthu"]
    invert = args["invert"]
    calculate_histogram = args["calculate_histogram"]
    colormap = args["colormap"]
    w = args["w"]
    general_functions.set_configuration(args["config"])
    
    temp_folder, file_path = RestToolbox.GetSingleDcm(config.ORTHANC_URL, w)
    try:
        img = pylinac_image.DicomImage(file_path)
        if invert:
            img.invert()
        if converthu:  # Convert back to pixel values if needed.
            img.array = (img.array - int(img.metadata.RescaleIntercept)) / int(img.metadata.RescaleSlope)
        img_array = np.flipud(img.array)
    except:
        return template("error_template", {"error_message": "Cannot read image."})
    size_x = img_array.shape[1]
    size_y = img_array.shape[0]
    img_min = np.min(img_array)
    img_max = np.max(img_array)

    x1 = np.arange(0, size_x, 1).tolist()
    y1 = img_array[int(size_y//2), :].tolist()
    y2 = np.arange(0, size_y, 1).tolist()
    x2 = img_array[:, int(size_x//2)].tolist()
    
    source = ColumnDataSource(data=dict(x1=x1, y1=y1))  # Bottom plot
    source2 = ColumnDataSource(data=dict(x2=x2, y2=y2))  # Right plot
    sourcebp = ColumnDataSource(data=dict(xbp=[], ybp=[]))
    sourcerp = ColumnDataSource(data=dict(xrp=[], yrp=[]))

    # Add table for pixel position and pixel value
    hfmt = NumberFormatter(format="0.00")
    source6 = ColumnDataSource(dict(
                                x=[],
                                y=[],
                                value=[]
                                ))
    columns2 = [TableColumn(field="x", title="x", formatter=hfmt),
               TableColumn(field="y", title="y", formatter=hfmt),
               TableColumn(field="value", title="value", formatter=hfmt)]
    table2 = DataTable(source=source6, columns=columns2, editable=False, height=50, width = 220)


    # Plotting profiles
    callback = CustomJS(args=dict(source=source, source2=source2, sourcebp=sourcebp,
                                  sourcerp=sourcerp, source6=source6), code="""
        var geometry = cb_data['geometry'];
        var x_data = parseInt(geometry.x); // current mouse x position in plot coordinates
        var y_data = parseInt(geometry.y); // current mouse y position in plot coordinates
        var data = source.data;
        var data2 = source2.data;
        var array = """+json.dumps(img_array.tolist())+""";
        data['y1'] = array[y_data];
        var column = [];
        for(var i=0; i < array.length; i++){
          column.push(array[i][x_data]);
        }
        data2['x2'] = column;
        
        source.change.emit();
        source2.change.emit();
        
        var length_x = array[0].length;
        var length_y = array.length;

        if ((x_data<=length_x && x_data>=0)  && (y_data<=length_y && y_data>=0)){
            
            // Add points to plot:
            sourcebp.data['xbp'] = [x_data];
            sourcebp.data['ybp'] = [array[y_data][x_data]];
            sourcerp.data['xrp'] = [array[y_data][x_data]];
            sourcerp.data['yrp'] = [y_data];
            
            // Get position and pixel value for table
            source6.data["x"] = [geometry.x];
            source6.data["y"] = [geometry.y];
            source6.data["value"] = [array[y_data][x_data]];

            sourcebp.change.emit();
            sourcerp.change.emit();
            source6.change.emit();
            }
    """)

    # Add callback for calculating average value inside Rectangular ROI
    x3 = np.arange(0, size_x, 1).tolist()
    y3 = np.arange(0, size_y, 1).tolist()
    source3 = ColumnDataSource(data=dict(x3=x3, y3=y3))
    source4 = ColumnDataSource(data=dict(x4=[], y4=[], width4=[], height4=[]))
    # Add table for mean and std
    source5 = ColumnDataSource(dict(
                                mean=[],
                                median=[],
                                std=[],
                                minn=[],
                                maxx=[]
                                ))

    columns = [TableColumn(field="mean", title="mean", formatter=hfmt),
               TableColumn(field="median", title="median", formatter=hfmt),
               TableColumn(field="std", title="std", formatter=hfmt),
               TableColumn(field="minn", title="min", formatter=hfmt),
               TableColumn(field="maxx", title="max", formatter=hfmt)]
    table = DataTable(source=source5, columns=columns, editable=False, height=50, width = 480)

    # Calculate things within ROI
    callback2 = CustomJS(args=dict(source3=source3, source4=source4, source5=source5), code="""
        var geometry = cb_obj['geometry'];
        var data3 = source3.data;
        var data4 = source4.data;
        var data5 = source5.data;

        // Get data
        var x0 = parseInt(geometry['x0']);
        var x1 = parseInt(geometry['x1']);
        var y0 = parseInt(geometry['y0']);
        var y1 = parseInt(geometry['y1']);

        // calculate Rect attributes
        var width = x1 - x0;
        var height = y1 - y0;
        var x = x0 + width/2;
        var y = y0 + height/2;

        // update data source with new Rect attributes
        data4['x4'] = [x];
        data4['y4'] = [y];
        data4['width4'] = [width];
        data4['height4'] = [height];

        // Get average value inside ROI
        var array = """+json.dumps(img_array.tolist())+""";

        var length_x = array[0].length;
        var length_y = array.length;

        if ((x0<=length_x && x0>=0) && (x1<=length_x && x1>=0) && (y0<=length_y && y0>=0) && (y1<=length_y && y1>=0)){
            var avg_ROI = [];

            for (var i=y0; i< y1; i++){
                for (var j=x0; j<x1; j++){
                    avg_ROI.push(array[i][j]);
                }
            }
    
            if (avg_ROI == undefined || avg_ROI.length==0){
                data5["mean"] = [0];
                data5["median"] = [0];
                data5["std"] = [0];
                }
            else{
                data5["mean"] = [math.mean(avg_ROI)];
                data5["median"] = [math.median(avg_ROI)];
                data5["std"] = [math.std(avg_ROI, 'uncorrected')];
                data5["maxx"] = [math.max(avg_ROI)];
                data5["minn"] = [math.min(avg_ROI)];
                }
            
            source4.change.emit();
            source5.change.emit();
            }
    """)

    plot_width = 500
    plot_height = int((plot_width-20)*size_y/size_x)
    
    fig = figure(x_range=[0, size_x], y_range=[0, size_y],
                 plot_width=plot_width, plot_height=plot_height, title="",
                 toolbar_location="right",
                 tools=["crosshair, wheel_zoom, pan, reset"])
    fig_r = figure(x_range=[img_min, img_max], y_range=fig.y_range,
                 plot_width=250, plot_height=plot_height, title="",
                 toolbar_location="right",
                 tools=[])
    fig_b = figure(x_range=fig.x_range, y_range=[img_min, img_max],
                 plot_width=plot_width, plot_height=200, title="",
                 toolbar_location="right",
                 tools=[])

    # Define matplotlib palette and make it possible to be used dynamically.
    cmap = matplotlib.cm.get_cmap(colormap) #chose any matplotlib colormap here
    bokehpalette = [matplotlib.colors.rgb2hex(m) for m in cmap(np.arange(cmap.N)[::-1])]  # Reversed direction of colormap
    mapper = LinearColorMapper(palette=bokehpalette, low=np.min(img_array), high=np.max(img_array))

    callback3 = CustomJS(args=dict(mapper=mapper, x_range=fig_r.x_range, y_range=fig_b.y_range), code="""
           mapper.palette = """+json.dumps(bokehpalette)+""";
           var start = cb_obj.value[0];
           var end = cb_obj.value[1];
           mapper.low = start;
           mapper.high = end;
           x_range.setv({"start": start, "end": end});
           y_range.setv({"start": start, "end": end});
           //mapper.change.emit();
    """)

    range_slider = RangeSlider(start=np.min(img_array), end=np.max(img_array), value=(np.min(img_array), np.max(img_array)), step=10, title="Level")
    range_slider.js_on_change('value', callback3)

    fig.image([img_array], x=0, y=0, dw=size_x, dh=size_y, color_mapper=mapper)

    fig_b.line(x='x1', y='y1', source=source)
    fig_r.line(x='x2', y='y2', source=source2)
    fig_b.circle(x='xbp', y='ybp', source=sourcebp, size=7, fill_color="red", fill_alpha=0.5, line_color=None) # For point connected to crosshair
    fig_r.circle(x='xrp', y='yrp', source=sourcerp, size=7, fill_color="red", fill_alpha=0.5, line_color=None) # For point connected to crosshair
    fig.rect(x='x4', y='y4', width='width4', height='height4', source=source4, fill_alpha=0.3, fill_color='#009933')
    
    fig.add_tools(HoverTool(tooltips=None, callback=callback))
    fig.add_tools(BoxSelectTool())
    fig.js_on_event(SelectionGeometry, callback2)  # Add event_callback to Boxselecttool

    grid = gridplot([[table, range_slider],  [fig, fig_r], [fig_b, table2]])
    script, div = components(grid)
    
    # Calculate pixel value histogram
    if calculate_histogram:
        try:
            bitsstored = int(img.metadata.BitsStored)
        except:
            bitsstored = 16
        max_pixelvalue = 2**bitsstored-1
        counts, bins = np.histogram(img_array.flatten(), density=False, range=(0, max_pixelvalue), bins=max_pixelvalue+1)
    
        fig_hist = figure(x_range = [-100, max_pixelvalue*1.05], y_range=[0.5, np.max(counts)],
                         plot_width=750, plot_height=400, title="Pixel value histogram", y_axis_type="log",
                         toolbar_location="right", tools=["box_zoom, pan, reset"])
    
        fig_hist.quad(top=counts, bottom=0.5, left=bins[:-1], right=bins[1:], alpha=0.5)
        fig_hist.grid.visible = False
        fig_hist.yaxis.axis_label = "Pixel Count"
        fig_hist.xaxis.axis_label = "Pixel Value"
        script_hist, div_hist = components(fig_hist)
    else:
        script_hist, div_hist = ["", ""]
    
    variables = {"script": script,
                 "div": div,
                 "bokeh_file_css": BOKEH_FILE_CSS,
                 "bokeh_file_js": BOKEH_FILE_JS,
                 "bokeh_widgets_js": BOKEH_WIDGETS_JS,
                 "bokeh_tables_js": BOKEH_TABLES_JS,
                 "script_hist": script_hist,
                 "div_hist": div_hist
                 }
    #gc.collect()

    general_functions.delete_files_in_subfolders([temp_folder]) # Delete image
    return template("image_review_results", variables)