//  [python]
def gen_ood_comparison(df_image, unc_key="Entropy"):
    print(f"Generating OOD plots with unc_key={unc_key}")

    df = df_image[df_image["Epsilon"]==0.0] # Remove adversarial noise experiments
    # df = df.iloc[::5]
    df_pixel = df_image_to_pixels(df, keys=["Target", "Mu", "Sigma", "OOD"])
    print ("sigma inf count :",np.sum(np.isinf(df_pixel['Sigma'])))
    inf_id = df_pixel[df_pixel.isin([np.nan, np.inf, -np.inf]).any(1)]
    print (inf_id.head())
    df_pixel["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_pixel["Sigma"]**2))
    print ("Entropy inf count :",np.sum(np.isinf(df_pixel['Entropy'])))
    df_pixel["Entropy"].mask(df_pixel["Method"]=="Laplace", np.log(2*df_pixel["Sigma"]*np.exp(1.)), inplace=True) #  entropy for laplace distirbution

    df_by_method = df_pixel.groupby(["Method","Model Path", "OOD"])
    df_by_image = df_pixel.groupby([df_pixel.index, "Method","Model Path", "OOD"])

    df_mean_unc = df_by_method[unc_key].mean().reset_index() #mean of all pixels per method
    df_mean_unc_img = df_by_image[unc_key].mean().reset_index() #mean of all pixels in every method and image

    sns.catplot(x="Method", y=unc_key, hue="OOD", data=df_mean_unc_img, kind="violin")
    plt.savefig(os.path.join(output_dir, f"ood_{unc_key}_violin.pdf"))
    plt.show()

    sns.catplot(x="Method", y=unc_key, hue="OOD", data=df_mean_unc_img, kind="box", whis=0.5, showfliers=False)
    plt.savefig(os.path.join(output_dir, f"ood_{unc_key}_box.pdf"))
    plt.show()


    ### Plot PDF for each Method on both OOD and IN
    g = sns.FacetGrid(df_mean_unc_img, col="Method", hue="OOD")
    g.map(sns.distplot, "Entropy").add_legend()
    plt.savefig(os.path.join(output_dir, f"ood_{unc_key}_pdf_per_method.pdf"))
    plt.show()


    ### Grab some sample images of most and least uncertainty
    for method in df_mean_unc_img["Method"].unique():
        imgs_max = dict()
        imgs_min = dict()
        for ood in df_mean_unc_img["OOD"].unique():
            df_subset = df_mean_unc_img[
                (df_mean_unc_img["Method"]==method) &
                (df_mean_unc_img["OOD"]==ood)]
            if len(df_subset) == 0:
                continue

            def get_imgs_from_idx(idx):
                i_img = df_subset.loc[idx]["level_0"]
                img_data = df_image.loc[i_img]
                sigma = np.array(img_data["Sigma"])
                entropy = np.log(sigma**2)

                ret = [img_data["Input"], img_data["Mu"], entropy]
                return list(map(trim, ret))

            def idxquantile(s, q=0.5, *args, **kwargs):
                qv = s.quantile(q, *args, **kwargs)
                return (s.sort_values()[::-1] <= qv).idxmax()

            imgs_max[ood] = get_imgs_from_idx(idx=idxquantile(df_subset["Entropy"], 0.95))
            imgs_min[ood] = get_imgs_from_idx(idx=idxquantile(df_subset["Entropy"], 0.05))

        all_entropy_imgs = np.array([ [d[ood][2] for ood in d.keys()] for d in (imgs_max, imgs_min)])
        entropy_bounds = (all_entropy_imgs.min(), all_entropy_imgs.max())

        Path(os.path.join(output_dir, "images")).mkdir(parents=True, exist_ok=True)
        for d in (imgs_max, imgs_min):
            for ood, (x, y, entropy) in d.items():
                id = os.path.join(output_dir, f"images/method_{method}_ood_{ood}_entropy_{entropy.mean()}")
                cv2.imwrite(f"{id}_0.png", 255*x)
                cv2.imwrite(f"{id}_1.png", apply_cmap(y, cmap=cv2.COLORMAP_JET))
                entropy = (entropy - entropy_bounds[0]) / (entropy_bounds[1]-entropy_bounds[0])
                cv2.imwrite(f"{id}_2.png", apply_cmap(entropy))



    ### Plot CDFs for every method on both OOD and IN
    df_cumdf = pd.DataFrame(columns=["Method", "Model Path", "OOD", unc_key, "CDF"])
    unc_ = np.linspace(df_mean_unc_img[unc_key].min(), df_mean_unc_img[unc_key].max(), 200)

    for method in df_mean_unc_img["Method"].unique():
        for model_path in df_mean_unc_img["Model Path"].unique():
            for ood in df_mean_unc_img["OOD"].unique():
                df = df_mean_unc_img[
                    (df_mean_unc_img["Method"]==method) &
                    (df_mean_unc_img["Model Path"]==model_path) &
                    (df_mean_unc_img["OOD"]==ood)]
                if len(df) == 0:
                    continue
                unc = np.sort(df[unc_key])
                prob = np.linspace(0,1,unc.shape[0])
                f_cdf = scipy.interpolate.interp1d(unc, prob, fill_value=(0.,1.), bounds_error=False)
                prob_ = f_cdf(unc_)

                df_single = pd.DataFrame({'Method': method, 'Model Path': model_path,
                    'OOD': ood, unc_key: unc_, 'CDF': prob_})
                df_cumdf = df_cumdf.append(df_single)

    sns.lineplot(data=df_cumdf, x=unc_key, y="CDF", hue="Method", style="OOD")
    plt.savefig(os.path.join(output_dir, f"ood_{unc_key}_cdfs.pdf"))
    plt.show()