Exemple #1
0
def histogram_cnn(args, doses_dtype=np.uint8):
    
    print(f"Starting calculation of histograms for results from CNN")
    if (args.rois_file is None):
        raise Exception("--rois_file option is required for histogram_cnn command.")
    if (args.doses_file is None):
        raise Exception("--doses_file option is required for histogram_cnn command.")

    print(f"Input file with roi markers: {args.rois_file}, (assuming np.unit32)")
    print(f"Input file with doses: {args.doses_file} (assuming {doses_dtype})")

    from bdfileutils import read_ndarray

    rois = read_ndarray(args.rois_file, dtype=np.uint32)
    doses = read_ndarray(args.doses_file, dtype=doses_dtype)

    roi_mapping_name_to_cnn_index = {}
    roi_mapping_cnn_index_to_name = {}
    roi_names_in_cnn = []
    if hasattr(args, "roi_mapping"):
        with open(args.roi_mapping) as fin:
            roi_mapping_name_to_cnn_index.update(json.load(fin))
        for (rn, rv) in roi_mapping_name_to_cnn_index.items():
            roi_mapping_cnn_index_to_name[int(rv)] = rn
            roi_names_in_cnn.append(rn)

    roi_sids_to_names = {}
    if hasattr(args, "roi_sids"):
        with open(args.roi_sids) as f:
            for line in f:
                n,sid = line.split(":")
                roi_sids_to_names[int(sid)] = n

    print(roi_mapping_cnn_index_to_name)
    print(roi_names_in_cnn)
    print(roi_sids_to_names)

    print(f"Orginal rois shape: {rois.shape}")
    print(f"Predicted doses by CNN: {doses.shape}")

    rois_f = rois.flatten()
    doses_f = doses.flatten()

    hist = []
    for sid in roi_sids_to_names.keys():
        name = roi_sids_to_names[sid] 
        if name in roi_names_in_cnn:
            print(f"Analyzing sid: {sid} with name: {name}")
            mind, avgd, maxd, h = histogram(doses_f, rois_f,sid, fname=None)
            hist.append({"name": name, "sid": sid, 
                        "roi_index_in_cnn": roi_mapping_name_to_cnn_index[name],   
                        "mind": mind, "avgd": avgd, "maxd": maxd, "data": h} )
        else:
            print(f"Skipping sid: {sid} with name: {name}")

    return (rois, doses, hist)
Exemple #2
0
def do_run(args):
    root_folder = args.root_folder
    if hasattr(args, "single") and args.single:
        subs = [root_folder]
    else:
        subs = next(os.walk(root_folder))[1]

    rows = []
    for sub in subs:
        row = []
        folder = os.path.join(root_folder, sub)
        rd = RASSData(root_folder=folder)

        with open(rd.root("meta.json")) as f:
            meta = json.load(f)

        if hasattr(args, "folder") and args.folder:
            row.append(sub)

        if hasattr(args, "patient_id") and args.patient_id:
            row.append(meta["patient_id"])

        if hasattr(args, "absolute_folder") and args.absolute_folder:
            row.append(folder)

        if hasattr(args, "plan_label") and args.plan_label:
            row.append(meta["plan_label"])

        if hasattr(args, "piers") and args.piers:
            row.append(str(meta["piers"]))

        if hasattr(args, "blizna") and args.blizna:
            row.append(str(meta["blizna"]))

        if hasattr(args, "max_dose") and args.max_dose:
            doses = read_ndarray(rd.output("total_doses.nparray"))
            row.append(np.max(doses))

        rows.append(row)

    res = None
    for r in rows:
        rjoined = ";".join(map(str, r))
        res = "\n".join([res, rjoined]) if res is not None else rjoined

    return res
Exemple #3
0
def do_run(args):
    root_folder = args.root_folder

    if hasattr(args,'single') and args.single:
        print(r"Single folder: {args.root_folder}")
        subs = ["."]
    else:
        subs = next(os.walk(root_folder))[1]

    for sub in subs:
        folder = os.path.join(root_folder, sub)
        rd = RASSData(root_folder=folder)

        files_exist = True
        for rf in required_files:
            if not rd.output_exists(rf):
                log.info(f"Missing file {rd.output(rf)}")
                files_exist = False
        
        if not files_exist:
            log.info(f"Brakuje wymaganych plików w folderze: {folder}. Uruchamiam obliczenia.")
            
            ########################################################
            # Uruchamiam obliczenia w folderze folder (obiekt rd)
            ########################################################
            args4bd4cnn = lambda: None
            args4bd4cnn.rass_data = rd
            bd4cnn.do_run(args4bd4cnn)

            log.info(f"Zakończyłem obliczenia w folderze: {folder}")

    ####################################################################################
    # Analizuję wymiary w wszystkich folderach (dimensions) - w tym celu wczytuję dawki
    # Dodatkowo ciągam maksymalną dawk globalnie!
    ####################################################################################
    log.info("Finding maximum dimensions and dose value.")
    adims = []
    max_dose_global = 0
    for sub in subs:
        rd = RASSData(root_folder=os.path.join(root_folder, sub))
        doses = read_ndarray(rd.output("total_doses.nparray"))
        m = np.max(doses)
        if max_dose_global < m:
            max_dose_global = m
            log.info(f"Updating max dose: {m}")
        adims.append(doses.shape)
    log.info(f"Final, total max dose: {max_dose_global}")

    nadims = np.array(adims)
    
    final_shape_min=np.min(nadims, axis=0)
    log.info(f"W przypadku dopasowania do najmniejszego rozmiaru: final shape min: {final_shape_min}")

    final_shape_max=np.max(nadims, axis=0) # [z,y,x]
    log.info(f"W przypadku dopasowania do największego rozmiaru: final shape max: {final_shape_max}")

    ################################################################
    # Generuje dane z przycinamiem lub rozszerzaniem obszarów ROIów
    ################################################################
    for sub in subs:
        folder = os.path.join(root_folder, sub)
        rd = RASSData(root_folder=folder)

        meta_data = {}
        if os.path.isfile(rd.input("meta_data.json")):
            with open(rd.input("meta_data.json")) as fin:
                meta_data.update(json.load(fin))
        if os.path.isfile(rd.root("meta.json")):
            with open(rd.root("meta.json")) as fin:
                meta_data.update(json.load(fin))

        meta_processing = {}
        meta_processing['cmd'] = " ".join(sys.argv)

        patient_id = meta_data["patient_id"] if "patient_id" in meta_data else sub
        meta_processing['patient_id'] = patient_id

        log.info(f"Analizuję pacjenta: {patient_id}")

        log.info("Wczytuję dawki")
        meta_processing['doses_source'] =  rd.output("total_doses.nparray")
        doses = read_ndarray(meta_processing['doses_source'])

        ########################################################################
        # Wykonuję progowanie dawek (czyli wartości przewidywanych)
        # Normalizacja dawki do wartości maksymalnej dawki dla danego pacjenta
        ########################################################################
        log.info(f"Proguję dawki do {args.dose_levels} poziomów względem wartości maksymalnej: {max_dose_global}")
        meta_processing['dose_levels'] = int(args.dose_levels)
        meta_processing['max_dose_global'] = float(max_dose_global)
        meta_processing['max_dose'] = float(np.max(doses))
        meta_processing['dose_levels_scale'] =  float(args.dose_levels / max_dose_global)
        meta_processing['dose_levels_upscale'] =  float(max_dose_global / args.dose_levels)

        doses = np.round(doses/max_dose_global * args.dose_levels)

        log.info("Wczytuję informację o znacznikach ROI.")
        meta_processing['rois_source'] =  rd.output("roi_marks.nparray")
        roi_marks = read_ndarray(meta_processing['rois_source'], dtype=np.int64)


        log.info("Wczytuję informację o danych CT.")
        meta_processing['ct_source'] =  rd.output("approximated_ct.nparray")
        ct = read_ndarray(rd.output("approximated_ct.nparray"))
        
        # wczytuję outline aby zbudować boundingbox, którego ostatecznie nie uzywam, ale ładnie wygląda
        log.info("Wczytuję Patient Outline do ładnych wizualizacji.")
        marks_patient_outline = read_ndarray(rd.output(f"roi_marks_Patient Outline.nparray"), dtype=np.int32)

        # środkowy slice
        middle_z_idx = marks_patient_outline.shape[0] // 2
        ref_slice = marks_patient_outline[middle_z_idx, :,:]

        # zbiór współrzędnych pikseli, która maja wartość równą 1
        idx = np.where( ref_slice == 1 )
        bbox = ( (min(idx[0]), min(idx[1])) , (max(idx[0]), max(idx[1])) ) 
        m =  ( (bbox[0][0] + bbox[1][0]) // 2,
               (bbox[0][1] + bbox[1][1]) // 2 )
        log.info(f"bbox of Patient Outline middle z slice ={bbox}")
        
        # zaznaczam na referencyjnym przekroju krzyżyk środka bounding boxa
        ref_slice[m[0],:] = 2
        ref_slice[:,m[1]] = 2

        # zaznaczam na referencyjnym przekroju bounding boxa
        ref_slice[bbox[0][0],:] = 3
        ref_slice[bbox[1][0],:] = 3
        ref_slice[:,bbox[0][1]] = 4
        ref_slice[:, bbox[1][1]] = 4

        # zapisuję referencyjny obrazek do głównego folderu symulacji
        plt.imsave((f"{root_folder}/ref_slice_{patient_id}.png"), ref_slice)

        ################################################################################################
        # Określam rozmiary dla każdego obrazka jak obcinać zgodnie z algorytmem
        # dopasowania do najmniejszego obrazka - robimy obcięcie od góry po y i symetrycznie po x-ach
        ################################################################################################
        ref_x_m = ref_slice.shape[1]//2
        final_x = final_shape_min[2]
        xfrom = ref_x_m - final_x//2
        xto = ref_x_m + final_x//2

        # tutaj obcinam początkowe yki, gdy ref_slice jest za duży
        yfrom = ref_slice.shape[0] - final_shape_min[1] 
        # tutaj jadę do końca do dołu obrazka
        yto = ref_slice.shape[0] 

        log.debug(f"[{patient_id}] min_xfrom: {xfrom}")
        log.debug(f"[{patient_id}] min_xto: {xto}")
        log.debug(f"[{patient_id}] min_yfrom: {yfrom}")
        log.debug(f"[{patient_id}] min_yto: {yto}")

        ref_slice_cropped_to_min = ref_slice[ yfrom:yto, xfrom:xto]
        plt.imsave((f"{root_folder}/ref_slice_{patient_id}_cropped_to_min.png"), ref_slice_cropped_to_min)


        ################################################################################################
        # Określam rozmiary dla każdego obrazka jak obcinać zgodnie z algorytmem
        # dopasowania do NAJWIĘKSZEGO obrazka - robimy doklejenie pustych pikseli od góry po y 
        # i symetrycznie po x-ach
        ################################################################################################
        final_x = final_shape_max[2]
        ref_x_m = ref_slice.shape[1]//2
        xfrom = final_x//2 - ref_x_m
        yfrom = final_shape_max[1] - ref_slice.shape[0]
        print(xfrom)
        print(yfrom)
        ref_slice_cropped_to_max = np.zeros( (final_shape_max[1], final_shape_max[2]))

        ref_slice_cropped_to_max[ yfrom:yfrom+ref_slice.shape[0],xfrom:xfrom+ref_slice.shape[1] ] = ref_slice
        plt.imsave((f"{root_folder}/ref_slice_{patient_id}_cropped_to_max.png"), ref_slice_cropped_to_max)

        meta_processing['shape_original'] = ", ".join(map(str,roi_marks.shape))

        mapping_file = rd.input("roi_mapping.json")
        if not os.path.exists(mapping_file):
            mapping_file = rd.root("roi_mapping.json")

        if (os.path.exists(mapping_file)):
            # w obecnej wersji uzywam max
            m = {}
            with open(mapping_file) as fin:
                m.update(json.load(fin))

            lst = [(rvalue, rname) for rname, rvalue in m.items()]
            lst = sorted(lst, key=lambda i: i[0])

            # zetów jest tyle co w roi_marks.shape[0], natomiast rozmiary x i y są z final_shape_max
            roi_marks_mapped_full = np.zeros( (roi_marks.shape[0], final_shape_max[1], final_shape_max[2]) )
            roi_marks_original_full = np.zeros( (roi_marks.shape[0], final_shape_max[1], final_shape_max[2]) )

            meta_processing['shape_final'] = ", ".join(map(str,roi_marks_mapped_full.shape))                
            meta_processing['shape_final_yfrom'] = int(yfrom)
            meta_processing['shape_final_xfrom'] = int(xfrom)
            meta_processing['shape_final_yto'] = int(yfrom+ref_slice.shape[0])
            meta_processing['shape_final_xto'] = int(xfrom+ref_slice.shape[1])

            for (rvalue, rname) in lst:
                marks = read_ndarray(rd.output(f"roi_marks_{rname}.nparray"), dtype=np.int32) # mniejszy
                b = (marks == 1) # gdzie wstawic przyporzadkowana w mapowaniu wartosc rvalue?

                # rozmiar marks jest taki sam jak tmp
                tmp = roi_marks_mapped_full[:, yfrom:yfrom+ref_slice.shape[0], xfrom:xfrom+ref_slice.shape[1]]
                tmp[b] = rvalue
                roi_marks_mapped_full[:, yfrom:yfrom+ref_slice.shape[0],xfrom:xfrom+ref_slice.shape[1]] = tmp

            roi_marks_original_full[:, yfrom:yfrom+ref_slice.shape[0],xfrom:xfrom+ref_slice.shape[1]] = roi_marks

            log.info(f"Rozpoczynam zapisywanie {roi_marks_mapped_full.shape[0]} plików z obrazami ROI (zmapowane) w formacie uint8")
            for i in range(roi_marks_mapped_full.shape[0]):
                if hasattr(args, "savepng") and args.savepng:
                    plt.imsave(rd.root_path(args.cnn_output, "roi_mapped_to_max_png", fname=f"roi_marks_mapped_{patient_id}_{i}.png"), roi_marks_mapped_full[i,:,:])
                pil_im = Image.fromarray(roi_marks_mapped_full[i,:,:].astype(np.uint8))
                pil_im.save(rd.root_path(args.cnn_output,"roi_mapped_to_max_pil", fname=f"pil_im_{patient_id}_{i}.png"))
            
            save_ndarray(rd.root_path(args.cnn_output, fname="rois_marks_original.nparray"), roi_marks_original_full.astype(np.int64))
            save_ndarray(rd.root_path(args.cnn_output, fname="rois_marks_mapped_to_max.nparray"), roi_marks_mapped_full.astype(np.int32))

            meta_processing['rois_marks_original'] =  "rois_marks_original.nparray"
            meta_processing['rois_marks_mapped'] =  "rois_marks_mapped_to_max.nparray"

            ## DOSES
            doses_full = np.zeros( (doses.shape[0], final_shape_max[1], final_shape_max[2]) )
            doses_full[:, yfrom:yfrom+ref_slice.shape[0],xfrom:xfrom+ref_slice.shape[1]] = doses
            log.info(f"Rozpoczynam zapisywanie {doses_full.shape[0]} plików z dawkami (progowane do {args.dose_levels} poziomów) w formacie uint8 będą pliki pil")
            for i in range(doses_full.shape[0]):
                if hasattr(args, "savepng") and args.savepng:
                    plt.imsave(rd.root_path(args.cnn_output, "doses_to_max_png", fname=f"doses_{patient_id}_{i}.png"), doses_full[i,:,:])
                pil_im = Image.fromarray(doses_full[i,:,:].astype(np.uint8))
                pil_im.save(rd.root_path(args.cnn_output, "doses_to_max_pil", fname=f"pil_im_{patient_id}_{i}.png"))

            save_ndarray(rd.root_path(args.cnn_output, fname=f"doses_to_max.nparray"), doses_full.astype(np.float32))
            meta_processing['doses_mapped'] =  "doses_to_max.nparray"

            # CT
            ct_full = np.zeros( (doses.shape[0], final_shape_max[1], final_shape_max[2]) )
            ct_full[:, yfrom:yfrom+ref_slice.shape[0],xfrom:xfrom+ref_slice.shape[1]] = ct
            log.info(f"Rozpoczynam zapisywanie {ct_full.shape[0]} plików z danymi CT w formacie uint8 będą pliki pil")
            for i in range(ct_full.shape[0]):
                if hasattr(args, "savepng") and args.savepng:
                    plt.imsave(rd.root_path(args.cnn_output, "ct_to_max_png", fname=f"ct_{patient_id}_{i}.png"), ct_full[i,:,:], cmap=cm.gray)
                pil_im = Image.fromarray(ct_full[i,:,:].astype(np.uint8))
                pil_im.save(rd.root_path(args.cnn_output, "ct_to_max_pil", fname=f"pil_im_{patient_id}_{i}.png"))


            if args.mask_rcnn_output != "no_ouput_maskrcnn":
                log.info(f"Rozpoczynam zapisywanie {doses_full.shape[0]} plików z danymi o obrazach ROI i dawek w formacie uint8 będą pliki pil dla sieci Mask-RCNN")
                output = rd.root_path(args.mask_rcnn_output)
                log.info(f"Folder for mark-rcnn: {output}")
                pathlib.Path(output).mkdir(exist_ok=True)
                for i in range(doses_full.shape[0]):
                    image_id = f"{patient_id}_{i}"
                    output_image_id_path = os.path.join(output, image_id)
                    pathlib.Path(output_image_id_path).mkdir(exist_ok=True)

                    output_pngs = os.path.join(output_image_id_path, "pngs")                
                    pathlib.Path(output_pngs).mkdir(exist_ok=True)
                    plt.imsave(os.path.join(output_pngs, f"{image_id}_rois.png"), roi_marks_mapped_full[i,:,:]) # obrazek z roiami

                    output_images = os.path.join(output_image_id_path, "images")
                    pathlib.Path(output_images).mkdir(exist_ok=True)
                    pil_im = Image.fromarray(roi_marks_mapped_full[i,:,:].astype(np.uint8))
                    pil_im.save(os.path.join(output_images, f"pil_{image_id}.png"))

                    output_masks = os.path.join(output_image_id_path, "masks")
                    pathlib.Path(output_masks).mkdir(exist_ok=True)
                    for level in range(int(args.dose_levels)):
                        level_mask = doses_full[i,:,:].astype(np.int32)
                        level_mask[ level_mask != level ] = 0
                        level_mask[ level_mask == level ] = 1
                        plt.imsave(os.path.join(output_pngs, f"{image_id}_{level}.png"), level_mask) # obrazek z roiami

                        pil_im = Image.fromarray(level_mask.astype(np.uint8))
                        pil_im.save(os.path.join(output_masks, f"pil_{image_id}_{level}.png"))
        else:
            log.warn(f"Pomijam katalog {sub}, ponieważ w katalogu input brakuje pliku `roi_mapping.json`")

        with open(rd.root_path(args.cnn_output, fname=f"meta_processing.json"), "w") as f:
            json.dump(meta_processing, f, indent=4,sort_keys=True)

    return
Exemple #4
0
def calc_statistics(meta, meta_processing, roi_file, doses_file, dtype):
    metrics = {}

    # reading data
    rois = read_ndarray(roi_file, dtype=np.int64)
    doses = read_ndarray(doses_file, dtype=dtype)

    # getting meta data
    target_dose = meta['TargetPrescriptionDose']
    gridDoseScaling = meta['DoseGridScaling']
    total_max = float(meta_processing["max_dose_global"])
    levels = int(meta_processing["dose_levels"])

    # To jest porypane, bo musimy przeskalować wartości np. 50 progów na odpowiednią dawkę,
    # a w dodatku, to skalowanie mogło być robione z porypaną skalą- nie wiadomo jakim total maxem
    # kurde < warto byłoby to do meta danych zapisywać.
    #
    # the doses are scaled to arg.level number of integer classes
    # we need to scale the classes (eg. 50) to original doses
    # we need to use doseGrdiScaling value to get absolute value in grays
    upscale = gridDoseScaling * float(total_max) / float(levels)

    print("-" * 120)
    print(f"   Prescribed dose: {target_dose}")
    print("-" * 120)
    print(
        f"{'ROI Name':^30} [{'Bit':^10}]{'min':^6} | {'max':^6} | {'avg':^6} | {'d99%':^6} | {'d98%':^6} | {'d95%':^6} | {'V95%':^6} | {'V98%':^6} | {'V105%':^6}"
    )
    print("-" * 120)
    for k, r in meta['roi_bits'].items():
        b = 2**(r - 1)
        mask = np.bitwise_and(rois, b) == b
        min = None
        max = None
        avg = None
        d99 = 0
        d98 = 0
        d95 = 0
        v95 = 0
        v98 = 0
        v105 = 0
        if np.max(mask) > 0:
            the = doses[mask] * upscale
            min = np.min(the)
            max = np.max(the)
            avg = np.mean(the)
            if "ptv" in k.lower():
                roi_vol = np.sum(mask)

                yk = target_dose * np.array([
                    0.4, 0.5, 0.60, 0.70, 0.80, 0.85, 0.88, 0.92, 0.94, 0.99,
                    1, 1.01, 1.05, 1.1
                ])
                xk = [np.sum(the > y) / roi_vol * 100 for y in yk]
                #print(xk)
                #print(yk)
                cs = interp1d(xk, yk)
                if np.max(xk) > 99:
                    d99 = cs(99)
                    d98 = cs(98)
                    d95 = cs(95)

                v90 = np.sum(the > target_dose * 0.90) / roi_vol * 100
                v95 = np.sum(the > target_dose * 0.95) / roi_vol * 100
                v98 = np.sum(the > target_dose * 0.98) / roi_vol * 100
                v99 = np.sum(the > target_dose * 0.99) / roi_vol * 100
                v105 = np.sum(the > target_dose * 1.05) / roi_vol * 100
                v107 = np.sum(the > target_dose * 1.07) / roi_vol * 100

                #fig, ax = plt.subplots(figsize=(6.5, 4))
                #ax.plot(xk, yk, 'o', label='data')
                #print(np.arange(p80,p100-0.01,0.01))
                #ax.plot(np.arange(p80,p100-0.01,0.01), cs(np.arange(p80,p100-0.01,0.01)), label='spline')
                #plt.show()

        if not "zz" in k and np.max(mask) > 0:
            print(
                f"{k:>30} [{b:>10}]{min:>6.2f} | {max:>6.2f} | {avg:>6.2f} | {d99:>6.2f} | {d98:>6.2f} | {d95:>6.2f} | {v95:>6.2f} | {v98:>6.2f} | {v105:>6.2f}"
            )

            metrics[k] = {
                'bit': int(b),
                'min': float(min),
                'max': float(max),
                'avg': float(avg),
                'd99': float(d99),
                'd98': float(d98),
                'd95': float(d95),
                'v95': float(v95),
                'v98': float(v98),
                'v105': float(v105)
            }

    return metrics
Exemple #5
0
if not os.path.isdir(roi_path):
    os.makedirs(roi_path)

#utworz katalog do zapisu wyjscia sieci
dose_path = rass_data.output() + "dose"
if not os.path.isdir(dose_path):
    os.makedirs(dose_path)

#liczba klas do ktorych ma byc klasyfikowana dawka
if len(sys.argv) > 2:
    levels = int(sys.argv[2])
else:
    levels = 255

# ct[z,y,x]
ct = read_ndarray(rass_data.output("approximated_ct.nparray"))
for i in range(ct.shape[0]):
    plt.imsave(rass_data.output(f"ct_{i}.png"), ct[i,:,:], cmap = cm.gray)

# doses[z,y,x]
doses = read_ndarray(rass_data.output("total_doses.nparray"))
doses = np.round(doses/np.max(doses) * levels)
for i in range(doses.shape[0]):
    plt.imsave(rass_data.output(f"doses_{i}.png"), doses[i,:,:])
    pil_im = Image.fromarray(doses[i,:,:].astype(np.uint32))
    pil_im.save(rass_data.output(os.path.join("dose", f"{i}.png")))

# roi_marks[z,y,x]
roi_marks = read_ndarray(rass_data.output("roi_marks.nparray"), dtype=np.int64)
for i in range(roi_marks.shape[0]):
    plt.imsave(rass_data.output(f"roi_marks_{i}.png"), roi_marks[i,:,:])