{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"},{"sourceId":1421668,"sourceType":"datasetVersion","datasetId":832340},{"sourceId":4721425,"sourceType":"datasetVersion","datasetId":2726166},{"sourceId":5470061,"sourceType":"datasetVersion","datasetId":3159441},{"sourceId":8941571,"sourceType":"datasetVersion","datasetId":2292106},{"sourceId":9126231,"sourceType":"datasetVersion","datasetId":5480747},{"sourceId":9212737,"sourceType":"datasetVersion","datasetId":3118989,"isSourceIdPinned":true},{"sourceId":9212962,"sourceType":"datasetVersion","datasetId":5309490,"isSourceIdPinned":true},{"sourceId":9562515,"sourceType":"datasetVersion","datasetId":5141507},{"sourceId":9562564,"sourceType":"datasetVersion","datasetId":5716874},{"sourceId":9575405,"sourceType":"datasetVersion","datasetId":5836645},{"sourceId":9586974,"sourceType":"datasetVersion","datasetId":5310342},{"sourceId":126632540,"sourceType":"kernelVersion"},{"sourceId":181280574,"sourceType":"kernelVersion"},{"sourceId":181722791,"sourceType":"kernelVersion"},{"sourceId":192931945,"sourceType":"kernelVersion"},{"sourceId":193148815,"sourceType":"kernelVersion"},{"sourceId":196461391,"sourceType":"kernelVersion"},{"sourceId":198924449,"sourceType":"kernelVersion"},{"sourceId":199903764,"sourceType":"kernelVersion"},{"sourceId":199954278,"sourceType":"kernelVersion"}],"dockerImageVersionId":30733,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"* T4 -> P100\n* Replace missing rows insertions at the end\n* Free GPU memory and RAM ","metadata":{}},{"cell_type":"markdown","source":"# Yuji part","metadata":{}},{"cell_type":"code","source":"%%capture\n!pip install '/kaggle/input/rsna2024-demo-workflow/natsort-8.4.0-py3-none-any.whl'\n!pip install /kaggle/input/notebook0248a0e3f9/pycocotools-2.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl /kaggle/input/notebook0248a0e3f9/dicomsdl-0.109.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl /kaggle/input/notebook0248a0e3f9/loguru-0.7.2-py3-none-any.whl /kaggle/input/notebook0248a0e3f9/pydicom-2.4.4-py3-none-any.whl /kaggle/input/notebook0248a0e3f9/python_gdcm-3.0.24.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl  /kaggle/input/notebook97381653b4/ensemble_boxes-1.0.9-py3-none-any.whl\n\n!pip install -qq /kaggle/working/python-packages/timm-0.9.10-py3-none-any.whl","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-10-17T01:32:08.194234Z","iopub.execute_input":"2024-10-17T01:32:08.194588Z","iopub.status.idle":"2024-10-17T01:33:37.105920Z","shell.execute_reply.started":"2024-10-17T01:32:08.194558Z","shell.execute_reply":"2024-10-17T01:33:37.104854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1, axial level estimation by hengck23's code\nref: https://www.kaggle.com/code/hengck23/ver-1-demo-workflow-2-stage-approach?scriptVersionId=191553260","metadata":{}},{"cell_type":"code","source":"from glob import glob\ndebug = False\ndebug_fold = 2\nplot = False\ntrain_test = 'test'\n\nimport sys, os\nsys.path.append('/kaggle/input/rsna2024-demo-workflow')\n\nfrom _dir_setting_ import *\n\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nfrom helper import *\nfrom data import *\nfrom model import *\n\n# STEP0 : SETUP DATA ==========================\n\ncfg=dotdict(\n    point_net=dotdict(\n        checkpoint='/kaggle/input/rsna2024-demo-workflow/00002484.pth',\n        image_size=160,\n    ),\n)\n\nlevel_to_label={\n    'l1_l2':1,\n    'l2_l3':2,\n    'l3_l4':3,\n    'l4_l5':4,\n    'l5_s1':5,\n}\n\n#################################################\n\n# study id used for demo\nid_df = pd.read_csv(f'{DATA_KAGGLE_DIR}/test_series_descriptions.csv')    \n\npoint_net = Net(pretrained=False)\nf = torch.load(cfg.point_net.checkpoint, map_location=lambda storage, loc: storage)\nstate_dict = f['state_dict']\npoint_net.load_state_dict(state_dict, strict=False)\npoint_net.cuda()\npoint_net.eval()\npoint_net.output_type = ['infer']\n\n\nfrom _dir_setting_ import *\nfrom natsort import natsorted\nimport pandas as pd\npd.set_option('mode.chained_assignment', None) # disable SettingWithCopyWarning\n\nimport numpy as np\nimport pydicom\nimport glob\nimport timm\nimport cv2\n\nfrom matplotlib.patches import FancyArrowPatch\nfrom mpl_toolkits.mplot3d import proj3d\nfrom pdb import set_trace as st\n\nclass dotdict(dict):\n    __setattr__ = dict.__setitem__\n    __delattr__ = dict.__delitem__\n\n    def __getattr__(self, name):\n        try:\n            return self[name]\n        except KeyError:\n            raise AttributeError(name)\n\n################################################################################3\n\n\n# read into volume (np_array) + dicom_header(df)\n'''\nimport notes:\n- instance_number may not be sequentially (can have missing num)\n- instance_number is 1-indexed\n\n'''\n\n## 3d/2d processing #########################################################\ndef np_dot(a,b):\n    return np.sum(a * b, 1)\n\ndef project_to_3d(x,y,z, df):\n    d = df.iloc[z]\n    H, W = d.H, d.W\n    sx, sy, sz = [float(v) for v in d.ImagePositionPatient]\n    o0, o1, o2, o3, o4, o5, = [float(v) for v in d.ImageOrientationPatient]\n    delx, dely = d.PixelSpacing\n\n    xx = o0 * delx * x + o3 * dely * y + sx\n    yy = o1 * delx * x + o4 * dely * y + sy\n    zz = o2 * delx * x + o5 * dely * y + sz\n    return xx,yy,zz\n\n## read data #########################################################\ndef resize_volume(volume, image_size):\n    image = volume.copy()\n    image = np.ascontiguousarray(image.transpose((1, 2, 0)))\n    image = cv2.resize(image, (image_size, image_size), interpolation=cv2.INTER_LINEAR)\n    image = np.ascontiguousarray(image.transpose((2, 0, 1)))  # cv2.INTER_LINEAR=1\n    return image\n\ndef normalise_to_8bit(x, lower=0.1, upper=99.9): # 1, 99 #0.05, 99.5 #0, 100\n    lower, upper = np.percentile(x, (lower, upper))\n    x = np.clip(x, lower, upper)\n    x = x - np.min(x)\n    x = x / np.max(x)\n    return (x * 255).astype(np.uint8)\ndef read_series(study_id,series_id,series_description):\n    data_kaggle_dir = DATA_KAGGLE_DIR\n    dicom_dir = f'{data_kaggle_dir}/{train_test}_images/{study_id}/{series_id}'\n\n    # read dicom file\n    dicom_file = natsorted(glob.glob(f'{dicom_dir}/*.dcm'))\n    instance_number = [int(f.split('/')[-1].split('.')[0]) for f in dicom_file]\n    dicom = [pydicom.dcmread(f) for f in dicom_file]\n\n    # make dicom header df\n    H, W = dicom[0].pixel_array.shape\n    dicom_df = []\n    for i, d in zip(instance_number, dicom):  # d__.dict__\n        dicom_df.append(\n            dotdict(\n                study_id=study_id,\n                series_id=series_id,\n                series_description=series_description,\n                instance_number=i,\n                # InstanceNumber = d.InstanceNumber,\n                ImagePositionPatient=[float(v) for v in d.ImagePositionPatient],\n                ImageOrientationPatient=[float(v) for v in d.ImageOrientationPatient],\n                PixelSpacing=[float(v) for v in d.PixelSpacing],\n                SpacingBetweenSlices=float(d.SpacingBetweenSlices),\n                SliceThickness=float(d.SliceThickness),\n                grouping=str([round(float(v), 3) for v in d.ImageOrientationPatient]),\n                H=H,\n                W=W,\n            )\n        )\n    dicom_df = pd.DataFrame(dicom_df)\n\n    # sort slices\n    dicom_df = [d for _, d in dicom_df.groupby('grouping')]\n\n    data = []\n    sort_data_by_group = []\n    for df in dicom_df:\n        position = np.array(df['ImagePositionPatient'].values.tolist())\n        orientation = np.array(df['ImageOrientationPatient'].values.tolist())\n        normal = np.cross(orientation[:, :3], orientation[:, 3:])\n        projection = np_dot(normal, position)\n        df.loc[:, 'projection'] = projection\n        df = df.sort_values('projection')\n\n\n        # todo: assert all slices are continous ??\n        # use  (position[-1]-position[0])/N = SpacingBetweenSlices ??\n        assert len(df.SliceThickness.unique()) == 1\n        assert len(df.SpacingBetweenSlices.unique()) == 1\n\n\n        volume = [\n            dicom[instance_number.index(i)].pixel_array for i in df.instance_number\n        ]\n        volume = np.stack(volume)\n        volume = normalise_to_8bit(volume)\n        data.append(dotdict(\n            df=df,\n            volume=volume,\n        ))\n\n        if 'sagittal' in series_description.lower():\n            sort_data_by_group.append(position[0, 0])  # x\n        if 'axial' in series_description.lower():\n            sort_data_by_group.append(position[0, 2])  # z\n\n    data = [r for _, r in sorted(zip(sort_data_by_group, data))]\n    for i, r in enumerate(data):\n        r.df.loc[:, 'group'] = i\n\n    df = pd.concat([r.df for r in data])\n    df.loc[:, 'z'] = np.arange(len(df))\n    try:    \n        volume = np.concatenate([r.volume for r in data])\n    except:\n        volume = []\n        for r in data:\n            # st()\n            volume.append([cv2.resize(im, (608, 608)) for im in r.volume])\n        volume = np.concatenate(volume)\n    data = dotdict(\n        series_id=series_id,\n        df=df,\n        volume=volume,\n    )\n    return data\ndef read_axial_df(study_id,series_id,series_description):\n    data_kaggle_dir = DATA_KAGGLE_DIR\n    dicom_dir = f'{data_kaggle_dir}/{train_test}_images/{study_id}/{series_id}'\n\n    # read dicom file\n    dicom_file = natsorted(glob.glob(f'{dicom_dir}/*.dcm'))\n    instance_number = [int(f.split('/')[-1].split('.')[0]) for f in dicom_file]\n    dicom = [pydicom.dcmread(f) for f in dicom_file]\n\n    # make dicom header df\n    H, W = dicom[0].pixel_array.shape\n    dicom_df = []\n    for i, d in zip(instance_number, dicom):  # d__.dict__\n        dicom_df.append(\n            dotdict(\n                study_id=study_id,\n                series_id=series_id,\n                series_description=series_description,\n                instance_number=i,\n                # InstanceNumber = d.InstanceNumber,\n                ImagePositionPatient=[float(v) for v in d.ImagePositionPatient],\n                ImageOrientationPatient=[float(v) for v in d.ImageOrientationPatient],\n                PixelSpacing=[float(v) for v in d.PixelSpacing],\n                SpacingBetweenSlices=float(d.SpacingBetweenSlices),\n                SliceThickness=float(d.SliceThickness),\n                grouping=str([round(float(v), 3) for v in d.ImageOrientationPatient]),\n                H=H,\n                W=W,\n            )\n        )\n    dicom_df = pd.DataFrame(dicom_df)\n\n    # sort slices\n    dicom_df = [d for _, d in dicom_df.groupby('grouping')]\n\n    data = []\n    sort_data_by_group = []\n    for df in dicom_df:\n        position = np.array(df['ImagePositionPatient'].values.tolist())\n        orientation = np.array(df['ImageOrientationPatient'].values.tolist())\n        normal = np.cross(orientation[:, :3], orientation[:, 3:])\n        projection = np_dot(normal, position)\n        df.loc[:, 'projection'] = projection\n        df = df.sort_values('projection')\n\n\n        # todo: assert all slices are continous ??\n        # use  (position[-1]-position[0])/N = SpacingBetweenSlices ??\n        assert len(df.SliceThickness.unique()) == 1\n        if len(df.SpacingBetweenSlices.unique()) != 1:\n            import pdb;pdb.set_trace()\n        assert len(df.SpacingBetweenSlices.unique()) == 1\n\n\n        volume = [\n            dicom[instance_number.index(i)].pixel_array for i in df.instance_number\n        ]\n        volume = np.stack(volume)\n        volume = normalise_to_8bit(volume)\n        data.append(dotdict(\n            df=df,\n            volume=volume,\n        ))\n\n        if 'sagittal' in series_description.lower():\n            sort_data_by_group.append(position[0, 0])  # x\n        if 'axial' in series_description.lower():\n            sort_data_by_group.append(position[0, 2])  # z\n\n    data = [r for _, r in sorted(zip(sort_data_by_group, data))]\n    for i, r in enumerate(data):\n        r.df.loc[:, 'group'] = i\n\n    df = pd.concat([r.df for r in data])\n    df.loc[:, 'z'] = np.arange(len(df))\n    return df\n\ndef read_study(study_id, sagittal_t2_id, axial_t2_id):\n    return dotdict(\n        study_id = study_id,\n        sagittal_t2 =read_series(study_id, sagittal_t2_id, 'sagittal_t2'),\n        axial_t2 =read_series(study_id, axial_t2_id, 'axial_t2'),\n    )\ndef get_true_sagittal_t2_point(study_id, sagittal_t2_df):\n    series_id = sagittal_t2_df.iloc[0].series_id\n    label_coord_df = pd.read_csv(f'{DATA_KAGGLE_DIR}/{train_test}_label_coordinates.csv')\n    label_df = label_coord_df[\n         (label_coord_df.study_id == study_id)\n       & (label_coord_df.series_id == series_id)\n    ]\n    label_df = label_df.sort_values('level')\n    point=label_df[['x','y']].values\n    instance_number = label_df.instance_number.values\n\n    #mapping from instance num to z (array index)\n    map_instance_number, map_z = sagittal_t2_df[['instance_number','z',]].values.T\n    map = {n:z for n,z in zip(map_instance_number,map_z)}\n    z = [map[n] for n in instance_number]\n    return point,z\n\n############################################################3\n#post processing (sagittal_t2 point net)\ndef probability_to_point(probability, threshold=0.5):\n    #todo: handle mssing point\n    point=[]\n    for l in range(1, 6):\n        y, x = np.where(probability[l] > threshold)\n        y = round(y.mean())\n        x = round(x.mean())\n        point.append((x, y))\n    return point\n\n\ndef view_to_world(sagittal_t2_point, z, sagittal_t2_df, image_size):\n\n    H = sagittal_t2_df.iloc[0].H\n    W = sagittal_t2_df.iloc[0].W\n    scale_x = W / image_size\n    scale_y = H / image_size\n\n    xxyyzz = []\n    for l in range(1, 6):\n        x,y = sagittal_t2_point[l-1]\n        xx,yy,zz = project_to_3d(x*scale_x, y*scale_y, z, sagittal_t2_df)\n        xxyyzz.append((xx, yy, zz))\n\n    xxyyzz = np.array(xxyyzz)\n    return xxyyzz\n\ndef point_to_level(world_point, axial_t2_df):\n\n    # we get closest axial slices (z) to the CSC world points\n\n    xxyyzz = world_point\n    orientation = np.array(axial_t2_df.ImageOrientationPatient.values.tolist())\n    position = np.array(axial_t2_df.ImagePositionPatient.values.tolist())\n    ox = orientation[:, :3]\n    oy = orientation[:, 3:]\n    oz = np.cross(ox, oy)\n    t = xxyyzz.reshape(-1, 1, 3) - position.reshape(1, -1, 3)\n    dis = (oz.reshape(1, -1, 3) * t).sum(-1)  # np.dot(point-s,oz)\n    fdis = np.fabs(dis)\n    closest_z = fdis.argmin(-1)\n    closest_fdis = fdis.min(-1)\n    closest_df = axial_t2_df.iloc[closest_z]\n\n    if 1:\n        #<todo> hard/soft assigment, multi/single assigment\n        # no assignment based on distance\n\n        # allow point found in multi group\n        num_group   = len(axial_t2_df['group'].unique())\n        point_group = axial_t2_df.group.values[fdis.argsort(-1)[:, :3]].tolist()\n        point_group = [list(set(g)) for g in point_group]\n        group_point = [[] for g in range(num_group)]\n        for l in range(5):\n            for k in point_group[l]:\n                group_point[k].append(l)\n                # print(k)\n                # print(group_point[k])\n                # print(group_point)\n        group_point = [sorted(list(set(g))) for g in group_point]\n\n    D = len(axial_t2_df)\n    assigned_level=np.full(D,fill_value=0, dtype=int)\n    for group in range(num_group):\n        point_in_this_group = np.array(group_point[group])  # np.where(closest_df['group'] == group)[0]\n        slice_in_this_group = np.where(axial_t2_df['group'] == group)[0]\n        if len(point_in_this_group) == 0:\n            continue # unassigned, level=0\n\n        level = point_in_this_group[fdis[point_in_this_group][:, slice_in_this_group].argmin(0)] + 1\n        assigned_level[slice_in_this_group] = level\n\n    # sor =  (fdis.argmin(0)+1)[closest_z]\n    # closest_z= [ fdis.argmin(0)+1]\n    return assigned_level, closest_z, closest_fdis #dis is soft assignment\n\n\n#########################################################################3\n#visualisation\nlevel_color = [\n    [0, 0, 0],\n    [255, 0, 0],\n    [0, 255, 0],\n    [0, 0, 255],\n    [255, 255, 0],\n    [0, 255, 255],\n]\n\ndef probability_to_rgb(probability):\n    _6_,H,W = probability.shape\n    rgb = np.zeros((H, W, 3))\n    for i in range(1, 6):\n        rgb += probability[i].reshape(H, W, 1) * [[level_color[i]]]\n    rgb = rgb.astype(np.uint8)\n    return rgb\n\n\nclass Arrow3D(FancyArrowPatch):\n    def __init__(self, xs, ys, zs, *args, **kwargs):\n        super().__init__((0,0), (0,0), *args, **kwargs)\n        self._verts3d = xs, ys, zs\n\n    def do_3d_projection(self, renderer=None):\n        xs3d, ys3d, zs3d = self._verts3d\n        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)\n        self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))\n        return np.min(zs)\n\ndef draw_slice(\n    ax, df,\n    is_slice =True, scolor=[[1,0,0]], salpha=[0.1],\n    is_border=True, bcolor=[[1,0,0]], balpha=[0.1],\n    is_origin=True, ocolor=[[1,0,0]], oalpha=[0.1],\n    is_arrow=True,\n):\n    df = df.copy()\n    df = df.reset_index(drop=True)\n\n    D = len(df)\n    if len(scolor)==1: scolor = scolor*D\n    if len(salpha)==1: salpha = salpha*D\n    if len(bcolor)==1: bcolor = bcolor*D\n    if len(balpha)==1: balpha = balpha*D\n    if len(ocolor)==1: ocolor = bcolor*D\n    if len(oalpha)==1: oalpha = balpha*D\n\n\n    #for i,d in df.iterrows():\n    for i in range(D):\n        d = df.iloc[i]\n        W, H = d.W, d.H\n        o0, o1, o2, o3, o4, o5 = d.ImageOrientationPatient\n        ox = np.array([o0, o1, o2])\n        oy = np.array([o3, o4, o5])\n        sx, sy, sz = d.ImagePositionPatient\n        s = np.array([sx, sy, sz])\n        delx, dely = d.PixelSpacing\n\n        p0 = s\n        p1 = s + W * delx * ox\n        p2 = s + H * dely * oy\n        p3 = s + H * dely * oy + W * delx * ox\n\n        grid = np.stack([p0, p1, p2, p3]).reshape(2, 2, 3)\n        gx = grid[:, :, 0]\n        gy = grid[:, :, 1]\n        gz = grid[:, :, 2]\n\n        #outline\n        if is_slice:\n            ax.plot_surface(gx, gy, gz, color=scolor[i], alpha=salpha[i])\n\n        if is_border:\n            line = np.stack([p0, p1, p3, p2] )\n            ax.plot(line[:,0], line[:,1], zs=line[:,2], color=ocolor[i], alpha=oalpha[i])\n\n        if is_origin:\n            ax.scatter([sx], [sy], [sz],   color=ocolor[i], alpha=oalpha[i])\n\n    #check ordering of slice\n    if is_arrow :\n        sx0, sy0, sz0 = df.iloc[0].ImagePositionPatient\n        sx1, sy1, sz1 = df.iloc[-1].ImagePositionPatient\n        arrow_prop_dict = dict(mutation_scale=20, arrowstyle='-|>', color='k', shrinkA=0, shrinkB=0)\n        a = Arrow3D([sx0, sx1], [sy0, sy1], [sz0, sz1], **arrow_prop_dict)\n        ax.add_artist(a)\n\n\nplot = False        \n\n\nfrom tqdm import tqdm\ndfs = []\nerror_ids = []\nall_dfs = []\nfor study_id_n, study_id in enumerate(tqdm(id_df.study_id.unique())):\n    for axial_t2_id in id_df[(id_df.study_id == study_id) & (id_df.series_description=='Axial T2')].series_id.values:\n        try:\n\n            sagittal_t2_id = id_df[(id_df.study_id == study_id) & (id_df.series_description=='Sagittal T2/STIR')].iloc[0].series_id\n            data = read_study(study_id, axial_t2_id=axial_t2_id, sagittal_t2_id=sagittal_t2_id)\n\n\n            #--- step.1 : detect 2d point in sagittal_t2\n            sagittal_t2 = data.sagittal_t2.volume\n            sagittal_t2_df = data.sagittal_t2.df\n            axial_t2_df = data.axial_t2.df\n            all_dfs.append(axial_t2_df)\n\n            D,H,W = sagittal_t2.shape\n            image = resize_volume(sagittal_t2,cfg.point_net.image_size)\n\n            sagittal_t2_z = D//2\n            image = image[sagittal_t2_z] #we use only center image #todo: better selection\n\n            batch = dotdict(\n                sagittal=torch.from_numpy(image).unsqueeze(0).unsqueeze(0).byte()\n            )\n            with torch.cuda.amp.autocast(enabled=True):\n                with torch.no_grad():\n                    output = point_net(batch)\n\n            probability = output['probability'][0].float().data.cpu().numpy()\n            sagittal_t2_point = probability_to_point(probability) #5 level 2d points, todo: check invalid output point\n\n#             #for debug and development\n#             point_hat, z_hat = sagittal_t2_point_hat = get_true_sagittal_t2_point(study_id, sagittal_t2_df)\n#             point_hat = point_hat*[[cfg.point_net.image_size/W, cfg.point_net.image_size/H]]\n\n\n            #--- step.2 : perdict slice level of axial_t2\n            world_point = world_point = view_to_world(sagittal_t2_point, sagittal_t2_z, sagittal_t2_df, cfg.point_net.image_size)\n            assigned_level, closest_z, closest_fdis = axial_t2_level = point_to_level(world_point, axial_t2_df)\n            \n            axial_t2_df['level'] = assigned_level\n#             break\n            exist_closest_fdis = closest_fdis[np.unique([c for c in assigned_level if c != 0])-1]\n            exist_closest_z = closest_z[np.unique([c for c in assigned_level if c != 0])-1]\n            ns = axial_t2_df.iloc[exist_closest_z].instance_number    \n            axial_t2_df.loc[~axial_t2_df.instance_number.isin(ns), 'closest'] = 0\n            axial_t2_df.loc[axial_t2_df.instance_number.isin(ns), 'closest'] = 1\n            axial_t2_df['closest'] = axial_t2_df['closest'].astype(int)\n            for level in range(1, 6):\n                axial_t2_df.loc[axial_t2_df.level == level, 'dis'] = closest_fdis[level-1]            \n                \n            for n, dis in zip(ns, exist_closest_fdis):\n                axial_t2_df.loc[axial_t2_df.instance_number == n, 'dis'] = dis\n                \n            assert len(assigned_level)==len(axial_t2_df)\n            dfs.append(axial_t2_df)        \n\n\n\n            if plot:\n#             if exist_closest_fdis.max() > 5:                \n                print(axial_t2_id, exist_closest_fdis.max(), assigned_level)\n                ###################################################################\n                #visualisation\n\n                # https://matplotlib.org/stable/gallery/mplot3d/mixed_subplots.html\n                fig = plt.figure(figsize=(23, 6))\n                ax1 = fig.add_subplot(1, 1, 1)\n                ax2 = fig.add_subplot(1, 2, 2, projection='3d')\n\n                # detection result\n                p = probability_to_rgb(probability)\n                m = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)\n                m = 255 - (255 - m * 0.8) * (1 - p / 255)\n\n                ax1.imshow(m / 255)\n                ax1.set_title(f'sagittal keypoint detection (unet)\\n series_id: {axial_t2_id}')\n\n\n                # draw  assigned_level\n                level_ncolor = np.array(level_color) / 255\n                coloring = level_ncolor[assigned_level].tolist()\n                draw_slice(\n                    ax2, axial_t2_df,\n                    is_slice=True,   scolor=coloring, salpha=[0.1],\n                    is_border=True,  bcolor=coloring, balpha=[0.2],\n                    is_origin=False, ocolor=[[0, 0, 0]], oalpha=[0.0],\n                    is_arrow=True\n                )\n\n            #     draw world_point\n                ax2.scatter(world_point[:, 0], world_point[:, 1], world_point[:, 2], alpha=1, color='black')\n\n\n                ### draw closest slice\n                coloring = level_ncolor[1:].tolist()\n                draw_slice(\n                    ax2, axial_t2_df.iloc[closest_z],\n                    is_slice=True, scolor=coloring, salpha=[0.1],\n                    is_border=True, bcolor=coloring, balpha=[1],\n                    is_origin=False, ocolor=[[1, 0, 0]], oalpha=[0],\n                    is_arrow=False\n                )\n\n                ax2.set_aspect('equal')\n                ax2.set_title(f'axial slice assignment\\n series_id:{sagittal_t2_id}')\n                ax2.set_xlabel('x')\n                ax2.set_ylabel('y')\n                ax2.set_zlabel('z')\n                ax2.view_init(elev=0, azim=-10, roll=0)\n                plt.tight_layout(pad=2)\n                plt.show()\n    \n        except:\n            error_ids.append(axial_t2_id)\naxial_closest_df = pd.concat(dfs)\naxial_direction = pd.concat(all_dfs)\naxial_direction.to_csv('axial_direction.csv', index=False)\naxial_closest_df.to_csv('axial_closest_df.csv', index=False)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:37.108654Z","iopub.execute_input":"2024-10-17T01:33:37.109282Z","iopub.status.idle":"2024-10-17T01:33:49.306953Z","shell.execute_reply.started":"2024-10-17T01:33:37.109244Z","shell.execute_reply":"2024-10-17T01:33:49.306000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2, setup","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nfrom glob import glob\nimport albumentations as A\nimport torch\nfrom torch.utils.data import Dataset\nfrom torch.utils.data import DataLoader\nimport copy\nfrom scipy.special import softmax\ndef sigmoid(x):\n    return 1/(1 + np.exp(-x))\nfrom sklearn.metrics import roc_auc_score, confusion_matrix, mean_squared_error, average_precision_score, recall_score\nimport warnings\nwarnings.simplefilter('ignore')\n\nimport os\nimport cv2\nimport gdcm\nimport pydicom\nimport zipfile\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport dicomsdl\nfrom pdb import set_trace as st\nfrom tqdm import tqdm\nfrom joblib import Parallel, delayed\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport torch","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:49.308311Z","iopub.execute_input":"2024-10-17T01:33:49.308673Z","iopub.status.idle":"2024-10-17T01:33:50.353287Z","shell.execute_reply.started":"2024-10-17T01:33:49.308641Z","shell.execute_reply":"2024-10-17T01:33:50.352283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def standardize_pixel_array(dcm: pydicom.dataset.FileDataset) -> np.ndarray:\n    \"\"\"\n    Source : https://www.kaggle.com/competitions/rsna-2023-abdominal-trauma-detection/discussion/427217\n    \"\"\"\n    # Correct DICOM pixel_array if PixelRepresentation == 1.\n    pixel_array = dcm.pixel_array\n    if dcm.PixelRepresentation == 1:\n        bit_shift = dcm.BitsAllocated - dcm.BitsStored\n        dtype = pixel_array.dtype \n        new_array = (pixel_array << bit_shift).astype(dtype) >>  bit_shift\n        pixel_array = pydicom.pixel_data_handlers.util.apply_modality_lut(new_array, dcm)\n    return pixel_array\n\ndef get_center_x_path(study_id, series_id):\n    path_x_map = {}\n    for dcm_path in sorted(glob(f\"{dicom_dir}/{study_id}/{series_id}/*.dcm\")):\n        filename = dcm_path.split('/')[-1].replace('.dcm', '')\n    #         if int(instance_number) % 2 == 1:\n    #             continue\n        dicom = dicomsdl.open(dcm_path)\n        pos_x = dicom['ImagePositionPatient'][0]\n        path_x_map[pos_x] = dcm_path\n        xs = []\n        for i, k in enumerate(sorted(path_x_map.keys())):\n            xs.append(k)\n    return path_x_map[xs[len(xs)//2-1]], path_x_map[xs[len(xs)//2]], path_x_map[xs[len(xs)//2+1]]\n\ndef read_sagittal_x_center_dicom(args, verbose=False):\n    study_id, series_id = args\n    ch_imgs = []\n    dcm_path_3 = get_center_x_path(study_id, series_id)\n    for dcm_path in dcm_path_3:\n        dicom = dicomsdl.open(dcm_path)\n        img = dicom.pixelData(storedvalue = True)\n\n        if dicom['PixelRepresentation'] == 1:\n            bit_shift = dicom['BitsAllocated'] - dicom['BitsStored']\n            dtype = img.dtype\n            img = (img << bit_shift).astype(dtype) >>  bit_shift\n        img = img.astype(np.float32)\n\n        intercept = dicom['RescaleIntercept']\n        slope = dicom['RescaleSlope']\n        if (slope is not None) & (intercept is not None):\n            img = img * slope + intercept\n\n        img = (img - img.min()) / (img.max() - img.min() + 1e-6)\n\n        if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n            img = 1 - img\n        img = (img*255.0).astype('uint8')\n\n        ch_imgs.append(img)\n    if verbose:\n        plt.imshow(ch_imgs[1], 'gray')\n        plt.show()\n    img = np.array(ch_imgs).transpose((1,2,0))\n    cv2.imwrite(f'/kaggle/temp/sagittal_all_images/{study_id}___{series_id}.png', img)\n    \n    \n    \ndef select_path_list(lst, N, offset=0, skip=1):\n    if not lst:\n        return [''] * (2 * N + 1)\n    \n    center = (len(lst) // 2) + offset\n    result = [''] * (2 * N + 1)\n    \n    if 0 <= center < len(lst):\n        result[N] = lst[center]\n    \n    for i in range(1, N + 1):\n        left_index = center - i*skip\n        right_index = center + i*skip\n        \n        if 0 <= left_index < len(lst):\n            result[N - i] = lst[left_index]\n        else:\n            result[N - i] = lst[0]\n        if 0 <= right_index < len(lst):\n            result[N + i] = lst[right_index]\n        else:\n            result[N + i] = lst[len(lst)-1]\n    \n    return result\n\n    \ndef read_sagittal_dicom(args, verbose=False):\n    study_id, series_id = args\n    imgs = {}\n    origin_paths = []\n    zs = []\n    xyzs = []\n    paths = []\n    for dcm_path in glob(f\"{dicom_dir}/{study_id}/{series_id}/*.dcm\"):\n        filename = dcm_path.split('/')[-1].replace('.dcm', '')\n        dicom = dicomsdl.open(dcm_path)\n        img = dicom.pixelData(storedvalue = True)\n\n        if dicom['PixelRepresentation'] == 1:\n            bit_shift = dicom['BitsAllocated'] - dicom['BitsStored']\n            dtype = img.dtype\n            img = (img << bit_shift).astype(dtype) >>  bit_shift\n        img = img.astype(np.float32)\n\n        intercept = dicom['RescaleIntercept']\n        slope = dicom['RescaleSlope']\n        if (slope is not None) & (intercept is not None):\n            img = img * slope + intercept\n\n        img = (img - img.min()) / (img.max() - img.min() + 1e-6)\n\n        if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n            img = 1 - img\n        img = (img*255.0).astype('uint8')\n        save_path = f'/kaggle/temp/sagittal_all_images/{study_id}___{series_id}___{filename}.png'\n        paths.append(save_path)\n\n        imgs[save_path] = img\n        origin_paths.append(dcm_path)\n        xyzs.append(dicom['ImagePositionPatient'])\n\n    df = pd.DataFrame({\n        'path': paths,\n        'origin_path': origin_paths,\n    })\n    df[['x_pos', 'y_pos', 'z_pos']] = np.array(xyzs)\n    df['study_id'] = study_id\n    df['series_id'] = series_id\n    df['instance_number'] = df['path'].apply(lambda x: int(x.split('___')[-1].replace('.png', '')))\n    df = df.sort_values(['x_pos', 'instance_number'])\n    df = df.drop_duplicates('x_pos')        \n    path_list = df.path.values\n    for path_n, path in enumerate(df.path):\n        if path_n == 0:\n            prev_path = df.path.values[0]\n        else:\n            prev_path = df.path.values[path_n-1]\n        prev_im = imgs[prev_path]            \n        \n        im = imgs[path]\n        if path_n == len(df)-1:\n            next_path = df.path.values[-1]\n        else:\n            next_path = df.path.values[path_n+1]\n        next_im = imgs[next_path]\n  \n        if not (prev_im.shape == im.shape == next_im.shape):\n            s = prev_im.shape\n            im = cv2.resize(im, s)\n            next_im = cv2.resize(next_im, s)\n\n        image = np.array([prev_im, im, next_im]).transpose((1,2,0))\n        cv2.imwrite(path, image)\n    \n    if verbose:\n        rows = 7\n        for n, k in enumerate(sorted(imgs.keys())):\n            if n % rows == 0:\n                fig = plt.figure(figsize=(20, 20))\n            fig.add_subplot(1, rows, n%rows+1)\n\n            im = imgs[k]\n            plt.imshow(im, 'gray')\n\n            if n % rows == rows-1:\n                plt.show()        \n        \n    return df\n\ndef read_axial_dicom(args, verbose=False):\n    study_id, series_id = args\n    imgs = {}\n    origin_paths = []\n    zs = []\n    xyzs = []\n    paths = []\n    series_axial_direction = axial_direction[axial_direction.series_id==series_id]\n    for dcm_path in glob(f\"{dicom_dir}/{study_id}/{series_id}/*.dcm\"):\n        filename = dcm_path.split('/')[-1].replace('.dcm', '')\n#         if int(instance_number) % 2 == 1:\n#             continue\n        dicom = dicomsdl.open(dcm_path)\n        img = dicom.pixelData(storedvalue = True)\n\n        if dicom['PixelRepresentation'] == 1:\n            bit_shift = dicom['BitsAllocated'] - dicom['BitsStored']\n            dtype = img.dtype\n            img = (img << bit_shift).astype(dtype) >>  bit_shift\n        img = img.astype(np.float32)\n\n        intercept = dicom['RescaleIntercept']\n        slope = dicom['RescaleSlope']\n        if (slope is not None) & (intercept is not None):\n            img = img * slope + intercept\n\n        pos_z = dicom['ImagePositionPatient'][-1]\n        \n        img = (img - img.min()) / (img.max() - img.min() + 1e-6)\n\n        if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n            img = 1 - img\n        img = (img*255.0).astype('uint8')\n        save_path = f'/kaggle/temp/axial_all_images/{study_id}___{series_id}___{filename}.png'\n        paths.append(save_path)\n\n        imgs[save_path] = img\n        origin_paths.append(dcm_path)\n        xyzs.append(dicom['ImagePositionPatient'])\n\n    df = pd.DataFrame({\n        'path': paths,\n        'origin_path': origin_paths,\n    })\n    df[['x_pos', 'y_pos', 'z_pos']] = np.array(xyzs)\n    df['study_id'] = study_id\n    df['series_id'] = series_id\n    df['instance_number'] = df['path'].apply(lambda x: int(x.split('___')[-1].replace('.png', '')))\n\n    l = len(df)\n    if len(df.merge(series_axial_direction[['instance_number', 'z']], on='instance_number')) == l:\n        df = df.merge(series_axial_direction[['instance_number', 'z']], on='instance_number')\n        df = df.sort_values(['z', 'instance_number'])\n    else:\n        df = df.sort_values(['z_pos', 'instance_number'])\n    path_list = df.path.values\n\n    for path_n, path in enumerate(df.path):\n        im = imgs[path]\n        if path_n == 0:\n            prev_path = df.path.values[0]\n        else:\n            prev_path = df.path.values[path_n-1]\n        prev_im = imgs[prev_path]\n        \n        if path_n == len(df)-1:\n            next_path = df.path.values[-1]\n        else:\n            next_path = df.path.values[path_n+1]\n        next_im = imgs[next_path]\n  \n        if not (prev_im.shape == im.shape == next_im.shape):\n\n            s = prev_im.shape\n            im = cv2.resize(im, s)\n            next_im = cv2.resize(next_im, s)\n\n        image = np.array([prev_im, im, next_im]).transpose((1,2,0))\n        cv2.imwrite(path, image)\n\n    if series_id not in axial_closest_df.series_id.unique():\n        for path_n, path in enumerate(df.path):\n            im = imgs[path]\n            if path_n <= 1:\n                prev_path = df.path.values[0]\n            else:\n                prev_path = df.path.values[path_n-2]\n            prev_im = imgs[prev_path]\n            \n            if path_n >= len(df)-2:\n                next_path = df.path.values[-1]\n            else:\n                next_path = df.path.values[path_n+2]\n            next_im = imgs[next_path]\n      \n            if not (prev_im.shape == im.shape == next_im.shape):\n\n                s = prev_im.shape\n                im = cv2.resize(im, s)\n                next_im = cv2.resize(next_im, s)\n\n            image = np.array([prev_im, im, next_im]).transpose((1,2,0))\n            cv2.imwrite(path.replace('.png', '_stride2.png'), image)\n\n        for path_n, path in enumerate(df.path):\n            im = imgs[path]\n            if path_n <= 2:\n                prev_path = df.path.values[0]\n            else:\n                prev_path = df.path.values[path_n-3]\n            prev_im = imgs[prev_path]\n            \n            if path_n >= len(df)-3:\n                next_path = df.path.values[-1]\n            else:\n                next_path = df.path.values[path_n+3]\n            next_im = imgs[next_path]\n      \n            if not (prev_im.shape == im.shape == next_im.shape):\n\n                s = prev_im.shape\n                im = cv2.resize(im, s)\n                next_im = cv2.resize(next_im, s)\n\n            image = np.array([prev_im, im, next_im]).transpose((1,2,0))\n            cv2.imwrite(path.replace('.png', '_stride3.png'), image)\n        \n    if verbose:\n        rows = 7\n        for n, k in enumerate(sorted(imgs.keys())):\n            if n % rows == 0:\n                fig = plt.figure(figsize=(20, 20))\n            fig.add_subplot(1, rows, n%rows+1)\n\n            im = imgs[k]\n            plt.imshow(im, 'gray')\n\n            if n % rows == rows-1:\n                plt.show()        \n        \n    return df\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:50.355597Z","iopub.execute_input":"2024-10-17T01:33:50.356033Z","iopub.status.idle":"2024-10-17T01:33:50.412497Z","shell.execute_reply.started":"2024-10-17T01:33:50.356008Z","shell.execute_reply":"2024-10-17T01:33:50.411454Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DEVICE = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n\nuse_local_box_when_debug = False\n\nif debug:\n    df = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_series_descriptions.csv')\n    fold_df = pd.read_csv('/kaggle/input/rsna2024-csvs/train_v3.csv')\n    df = df[df.study_id.isin(fold_df[fold_df.fold==debug_fold].study_id)]\n    if short_debug:\n        df = df[df.study_id.isin(use_ids)]\n#         df = df[df.series_id==3636216534]\n\nelse:\n    df = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_series_descriptions.csv')\ndicom_dir = f'/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/{train_test}_images'\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:50.413686Z","iopub.execute_input":"2024-10-17T01:33:50.414000Z","iopub.status.idle":"2024-10-17T01:33:50.435728Z","shell.execute_reply.started":"2024-10-17T01:33:50.413972Z","shell.execute_reply":"2024-10-17T01:33:50.434694Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# dicom to png","metadata":{}},{"cell_type":"code","source":"\nimage_save_dir = '/kaggle/temp/axial_all_images'\nos.makedirs(image_save_dir, exist_ok=True)\n\naxial_df = df[df.series_description == 'Axial T2']\nargs = axial_df.drop_duplicates(['study_id', 'series_id'])[['study_id', 'series_id']].values\n\n\nfrom multiprocessing import Pool\n\np = Pool(processes=4)\nresults = []\nwith tqdm(total=len(args)) as pbar:\n    for res in p.imap(read_axial_dicom, args):\n        results.append(res)\n        pbar.update(1)\np.close()\n\naxial_df = pd.concat(results)\naxial_df.to_csv('axial_df.csv', index=False)\n\nimage_save_dir = '/kaggle/temp/sagittal_all_images'\nos.makedirs(image_save_dir, exist_ok=True)\n\nsagittal_df = df[df.series_description != 'Axial T2']\nargs = sagittal_df.drop_duplicates(['study_id', 'series_id'])[['study_id', 'series_id']].values\n\nfrom multiprocessing import Pool\n\np = Pool(processes=4)\nresults = []\nwith tqdm(total=len(args)) as pbar:\n    for res in p.imap(read_sagittal_dicom, args):\n        results.append(res)\n        pbar.update(1)\np.close()\n\nsagittal_df = pd.concat(results)\nsagittal_df.to_csv('sagittal_df.csv', index=False)\n\nsagittal_df = pd.read_csv('sagittal_df.csv')\nseries_description_df = pd.read_csv(f'/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/{train_test}_series_descriptions.csv')\nsagittal_df = sagittal_df.merge(series_description_df, on=['study_id', 'series_id'])\n\nfor study_id_n, (study_id, idf) in enumerate(tqdm(sagittal_df.groupby('study_id'))):\n    t1 = idf[idf.series_description=='Sagittal T1'].sort_values('instance_number')\n    if len(t1)!=0:\n        t1 = t1[t1.series_id==t1.series_id.values[0]]\n    t2 = idf[idf.series_description=='Sagittal T2/STIR'].sort_values('instance_number')\n    if len(t2)!=0:\n        t2 = t2[t2.series_id==t2.series_id.values[0]]\n    if len(t1)==0:\n        m = t2.instance_number.max()\n        mi = t2.instance_number.min()\n    elif (len(t2)==0):\n        m = t1.instance_number.max()\n        mi = t1.instance_number.min()\n    else:\n        m = max([t1.instance_number.max(), t2.instance_number.max()])\n        mi = min([t1.instance_number.min(), t2.instance_number.min()])\n    for n in range(mi, m+1):\n        n1 = t1[t1.instance_number == n]\n        n2 = t2[t2.instance_number == n]\n        if len(n1) != 0:\n            # print(n1.path.values[0])\n            im1 = cv2.imread(n1.path.values[0])[:,:,1]\n        else:\n            im1 = None\n        if len(n2) != 0:\n            # print(n2.path.values[0])\n            im2 = cv2.imread(n2.path.values[0])[:,:,1]\n        else:\n            im2 = None\n        if im1 is None:\n            # raise\n            im1 = np.zeros(im2.shape)\n        if im2 is None:\n            # raise\n            im2 = np.zeros(im1.shape)\n        if im1.shape!=im2.shape:\n            im1 = cv2.resize(im1, (im2.shape[1], im2.shape[0]))\n        im = np.array([im1, im2, im1]).transpose((1,2,0))\n\n        cv2.imwrite(f'/kaggle/temp/sagittal_all_images/{study_id}___{n}.png', im)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:50.437507Z","iopub.execute_input":"2024-10-17T01:33:50.437844Z","iopub.status.idle":"2024-10-17T01:33:53.785782Z","shell.execute_reply.started":"2024-10-17T01:33:50.437814Z","shell.execute_reply":"2024-10-17T01:33:53.784670Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"axial_closest_df = axial_closest_df[axial_closest_df.closest==1]\naxial_closest_df['pred_level'] = axial_closest_df.level.values\naxial_closest_df['path'] = '/kaggle/temp/axial_all_images/' + axial_closest_df.study_id.astype(str) + '___' + axial_closest_df.series_id.astype(str) + '___' + axial_closest_df.instance_number.astype(str) + '.png'\naxial_closest_df['level_pred'] = 1\naxial_closest_df[['study_id', 'series_id', 'path', 'level_pred', 'pred_level', 'instance_number', 'z']]\naxial_all_level_df = axial_closest_df[['study_id', 'series_id', 'path', 'level_pred', 'pred_level', 'instance_number', 'z', 'dis']]\naxial_all_level_df.to_csv('axial_all_level_df.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:53.787571Z","iopub.execute_input":"2024-10-17T01:33:53.788459Z","iopub.status.idle":"2024-10-17T01:33:53.801175Z","shell.execute_reply.started":"2024-10-17T01:33:53.788397Z","shell.execute_reply":"2024-10-17T01:33:53.800247Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4, spinal slice / nfn slice classification for sagittal","metadata":{}},{"cell_type":"code","source":"import albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport timm\n\nclass RSNA2024DatasetV1(Dataset):\n    def __init__(self, cfg):\n        self.transforms = cfg.transforms\n        self.cfg = cfg\n        self.paths = cfg.df.path.values\n        if 'x_min' in list(cfg.df):\n            self.boxes = cfg.df[['x_min', 'y_min', 'x_max', 'y_max']].values\n    def __len__(self):\n        return len(self.paths)\n\n    def __getitem__(self, idx):\n        path = self.paths[idx]\n        image = cv2.imread(path)[:,:,::-1]\n\n        if hasattr(self.cfg, 'box_crop') and self.cfg.box_crop:\n            box = self.boxes[idx]\n            x_pad = (box[2] - box[0])//2 * self.cfg.box_crop_x_ratio\n            y_pad = (box[3] - box[1])//2 * self.cfg.box_crop_y_ratio\n            x_min = np.max([box[0]-x_pad, 0])\n            y_min = np.max([box[1]-y_pad, 0])\n            if hasattr(self.cfg, 'box_crop_y_upper_ratio'):\n                y_upper_pad = (box[3] - box[1])//2 * self.cfg.box_crop_y_upper_ratio\n                y_min = np.max([box[1]-y_upper_pad, 0])\n            x_max = np.min([box[2]+x_pad, image.shape[1]])\n            y_max = np.min([box[3]+y_pad, image.shape[0]])\n            s = image.shape\n            image = image[int(y_min):int(y_max), int(x_min):int(x_max), :]\n            # print(s, image.shape)\n\n        image = self.transforms(image=image)['image']\n\n        return image\n\nclass base():\n    def __init__(self):\n        self.model_name = 'convnext_small.in12k_ft_in1k_384'\n        self.image_size = 384\n        self.batch_size = 16\n        self.tta = False\n        self.box_crop = None\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        \nclass rsna_sagittal_cl(base):\n    def __init__(self):\n        super().__init__()\n        self.df = pd.read_csv('sagittal_df.csv')\n        self.label_features = ['l1_spinal', 'l2_spinal', 'l3_spinal', 'l4_spinal', 'l5_spinal',\n                               'l1_right_neural', 'l2_right_neural', 'l3_right_neural', 'l4_right_neural', 'l5_right_neural',\n                               'l1_left_neural', 'l2_left_neural', 'l3_left_neural', 'l4_left_neural', 'l5_left_neural']\n        self.image_size = 256\n        \n        d = '/kaggle/input/rsna-2024-sagittal-models-v1/rsna-sagittal-level-cl-spinal-nfn-v2'\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features))\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.transforms = A.Compose([\n            A.Compose([\n                A.Resize(self.image_size, self.image_size),\n                A.Normalize(),\n                ToTensorV2(),\n            ])\n        ])              \n\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:53.802510Z","iopub.execute_input":"2024-10-17T01:33:53.802798Z","iopub.status.idle":"2024-10-17T01:33:53.824226Z","shell.execute_reply.started":"2024-10-17T01:33:53.802776Z","shell.execute_reply":"2024-10-17T01:33:53.823462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_preds = []\nfor cfg in [\n    rsna_sagittal_cl(),\n]:\n    models = []\n    for model_path in cfg.model_paths:\n        # print(model_path)\n        state_dict = torch.load(model_path, map_location=torch.device('cpu'))\n        model = copy.deepcopy(cfg.model)\n        model.load_state_dict(state_dict)\n        model.to(DEVICE)\n        model.eval()\n        models.append(model)\n\n    ds = RSNA2024DatasetV1(cfg)\n    loader = DataLoader(ds, batch_size=cfg.batch_size, shuffle=False, drop_last=False, num_workers=4)\n    preds = []\n    for images in tqdm(loader, smoothing=0):\n        images = images.to(DEVICE)\n        batch_preds = []\n        for model in models:\n            batch_preds.append(model(images).detach().cpu().numpy())\n\n        preds += np.mean(batch_preds, axis=0).tolist()\n    all_preds.append(preds)\nfs = [f'pred_{col}' for col in cfg.label_features]\ncfg.df[fs] = sigmoid(np.mean(all_preds, 0))\ncfg.df.to_csv(f'sagittal_with_position_preds.csv', index=False)\n\n\nsagittal = cfg.df.copy()\nsagittal['pred_spinal'] = sagittal[['pred_l1_spinal', 'pred_l2_spinal', 'pred_l3_spinal', 'pred_l4_spinal', 'pred_l5_spinal']].mean(1)\nsagittal['pred_right_neural'] = sagittal[['pred_l1_right_neural', 'pred_l2_right_neural', 'pred_l3_right_neural', 'pred_l4_right_neural', 'pred_l5_right_neural']].mean(1)\nsagittal['pred_left_neural'] = sagittal[['pred_l1_left_neural', 'pred_l2_left_neural', 'pred_l3_left_neural', 'pred_l4_left_neural', 'pred_l5_left_neural']].mean(1)\n\n\nseries_description_df = pd.read_csv(f'/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/{train_test}_series_descriptions.csv')\nt2_ids = series_description_df[series_description_df.series_description=='Sagittal T2/STIR'].series_id\n\ndfs = []\nfor id, idf in sagittal[sagittal.series_id.isin(t2_ids)].groupby('series_id'):\n    idf = idf.sort_values(['x_pos', 'instance_number'])\n    idf = idf.drop_duplicates('x_pos')\n    ldf = idf[idf['pred_spinal']==idf['pred_spinal'].max()].iloc[:1]\n    dfs.append(ldf)\n\nspinal_sagittal = pd.concat(dfs)\nsagittal.to_csv('sagittal_df.csv', index=False)\nspinal_sagittal.to_csv('spinal_sagittal_df.csv', index=False)\nspinal_sagittal.study_id.nunique(), spinal_sagittal.series_id.nunique()\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:33:53.825633Z","iopub.execute_input":"2024-10-17T01:33:53.825939Z","iopub.status.idle":"2024-10-17T01:34:10.164942Z","shell.execute_reply.started":"2024-10-17T01:33:53.825915Z","shell.execute_reply":"2024-10-17T01:34:10.163723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# yolox","metadata":{}},{"cell_type":"code","source":"box_cols = ['x_min', 'y_min', 'x_max','y_max']\nyolo_for_config_dir = '/kaggle/input/rsna-2024-axial-models-v1/rsna-axial-all-images-left-yolox-x'\n\nyolo_for_config = 'rsna_axial_all_images_left_yolox_x'\n\nimport sys\nsys.path.append('/kaggle/input/yolox20230421/YOLOX')\n\nfrom yolox.utils import postprocess\nfrom yolox.data.data_augment import ValTransform\n\n\nclass MyDataset(Dataset):\n    def __init__(self, df):\n        self.paths = df.path.values\n        self.preproc = ValTransform(legacy = False)\n        self.size = 512\n\n    def __len__(self):\n        return len(self.paths)\n\n    def __getitem__(self, idx):\n        path = self.paths[idx]\n        img = cv2.imread(path)\n        ratio = min(self.size / img.shape[0], self.size / img.shape[1])\n\n        img, _ = self.preproc(img, None, (self.size, self.size))\n        img = torch.from_numpy(img).float()\n        img = img.float()\n\n        return img, path, ratio\n\nfrom yolox.exp import Exp as MyExp\n\nclass ExpX(MyExp):\n    def __init__(self):\n        super(ExpX, self).__init__()\n        self.depth = 1.33\n        self.width = 1.25\n        self.exp_name = ''\n        self.data_dir = \"\"\n\n        ### need change ###\n        self.max_epoch = 10\n        self.output_dir = \".\"\n        self.input_size = (512, 512)\n        self.test_size = (512, 512)\n        self.no_aug_epochs = 10 # 15\n        self.warmup_epochs = 5 # 5\n        self.num_classes = 1\n        ### need change ###\n\n        ### fyi ###\n        self.data_num_workers = 16\n        self.eval_interval = 1\n        self.seed = 42\n        self.print_interval = 100000\n        self.eval_interval = 1\n        self.save_history_ckpt = False\n        self.mosaic_prob = 1.0\n        self.mixup_prob = 1.0\n        self.hsv_prob = 1.0\n        self.flip_prob = 0.5\n        self.degrees = 10.0\n        self.translate = 0.1\n        self.mosaic_scale = (0.1, 2)\n        self.enable_mixup = True\n        self.mixup_scale = (0.5, 1.5)\n        self.shear = 2.0\n        self.min_lr_ratio = 0.05\n        self.basic_lr_per_img = 0.00015625\n        self.scheduler = 'yoloxwarmcos'\n        self.ema = True\n        self.weight_decay = 0.0005\n        self.momentum = 0.9\n        self.test_conf = 0.01\n        self.nmsthre = 0.65\n        self.class_id_name_map = {0: 'left'}\n\nclass ExpL(MyExp):\n    def __init__(self):\n        super(ExpL, self).__init__()\n        self.depth = 1.0\n        self.width = 1.0\n        self.exp_name = ''\n        self.data_dir = \"\"\n\n        ### need change ###\n        self.max_epoch = 10\n        self.output_dir = \".\"\n        self.input_size = (512, 512)\n        self.test_size = (512, 512)\n        self.no_aug_epochs = 10 # 15\n        self.warmup_epochs = 5 # 5\n        self.num_classes = 1\n        ### need change ###\n\n        ### fyi ###\n        self.data_num_workers = 16\n        self.eval_interval = 1\n        self.seed = 42\n        self.print_interval = 100000\n        self.eval_interval = 1\n        self.save_history_ckpt = False\n        self.mosaic_prob = 1.0\n        self.mixup_prob = 1.0\n        self.hsv_prob = 1.0\n        self.flip_prob = 0.5\n        self.degrees = 10.0\n        self.translate = 0.1\n        self.mosaic_scale = (0.1, 2)\n        self.enable_mixup = True\n        self.mixup_scale = (0.5, 1.5)\n        self.shear = 2.0\n        self.min_lr_ratio = 0.05\n        self.basic_lr_per_img = 0.00015625\n        self.scheduler = 'yoloxwarmcos'\n        self.ema = True\n        self.weight_decay = 0.0005\n        self.momentum = 0.9\n        self.test_conf = 0.01\n        self.nmsthre = 0.65\n        self.class_id_name_map = {0: 'left'}\n\nexp_x = ExpX()\n\n# set inference parameters\nconfthre = 0.03\nnmsthre = 0.45\n\n# get YOLOX model\nmodels = []\nfor fold in range(1):\n    model = exp_x.get_model()\n    model.to(DEVICE)\n    model.eval()\n    model.head.training=False\n    model.training=False\n    ckpt_file = \"/kaggle/input/notebookfa5fb155d5/rsna-axial-all-images-left-yolox-x/0_best_ckpt.ckpt\"\n    # print(ckpt_file)\n    ckpt = torch.load(ckpt_file, map_location=\"cpu\")\n    model.load_state_dict(ckpt[\"model\"])\n    models.append(model)\n\nds = MyDataset(axial_all_level_df)\nloader = DataLoader(ds, batch_size=16, shuffle=False, drop_last=False, num_workers=4)\n\nall_preds_list = [[]]\nall_ratios_list = [[]]\nall_paths_list = [[]]\n\n# print('inf start...')\nwith torch.no_grad():\n    for loader_n, input in tqdm(enumerate(loader)):\n        # if loader_n % 100 == 0:\n        #     print(loader_n)\n        images, paths, ratios = input\n        images = images.to(DEVICE)\n        for model_n, model in enumerate(models):\n            outputs = model(images)\n#             outputs2 = postprocess(\n#                         outputs, exp_x.num_classes, confthre,\n#                         nmsthre, class_agnostic=True\n#                     )\n            \n#             st()\n            \n            outputs = postprocess(\n                        outputs, exp_x.num_classes, confthre,\n                        nmsthre, class_agnostic=True\n                    )\n            all_preds_list[model_n] += outputs\n            all_ratios_list[model_n] += list(ratios)\n            all_paths_list[model_n] += list(paths)\n\ndf_5models = []\nfor model_n, (all_preds, all_paths, all_ratios) in enumerate(zip(all_preds_list, all_paths_list, all_ratios_list)):\n    dfs = []\n    all_boxes = []\n    all_class_ids = []\n    all_scores = []\n    for n, (predictions, path, ratio)  in enumerate(zip(all_preds, all_paths, all_ratios)):\n        if predictions is None:\n            continue\n        predictions = predictions.cpu().numpy()\n\n        bboxes = predictions[:, 0:4]\n\n        bboxes /= ratio\n        bboxes = bboxes.tolist()\n        bbclasses = predictions[:, 6]\n        scores = predictions[:, 4] * predictions[:, 5]\n        path_df = axial_all_level_df[axial_all_level_df.path==path].iloc[:1]\n        for box, score, class_id in zip(bboxes, scores, bbclasses):\n            all_boxes.append(box)\n            all_scores.append(score)\n            all_class_ids.append(class_id)\n            dfs.append(path_df)\n    tmp = pd.concat(dfs)\n    tmp['class_id'] = all_class_ids\n    tmp['class_id'] = tmp['class_id'].astype(int)\n    tmp['class_name'] = tmp['class_id'].map(exp_x.class_id_name_map)\n    tmp['conf'] = all_scores\n    tmp[box_cols] = all_boxes\n    tmp[box_cols] = np.round(tmp[box_cols]).astype(int)\n    tmp['model_n'] = model_n\n    df_5models.append(tmp)\n\nfrom ensemble_boxes import *\nfrom multiprocessing import cpu_count\nimport copy\n\nfrom multiprocessing import Pool\n\ndef exec(args):\n    path, path_df = args\n    boxes_list = []\n    confs_list = []\n    labels_list = []\n    for _, model_df in path_df.groupby('model_n'):\n        boxes_list.append(model_df[box_cols].values/max_value)\n        confs_list.append(model_df['conf'].values.tolist())\n        labels_list.append(model_df['class_id'].values.tolist())\n    boxes, confs, labels = weighted_boxes_fusion(boxes_list, confs_list, labels_list, weights=[1]*len(boxes_list), iou_thr=iou_thr, skip_box_thr=skip_box_thr)\n    boxes *= max_value\n    results = []\n    for idx, box in enumerate(boxes):\n        results.append({\n            \"path\": path,\n            \"class_id\": int(labels[idx]),\n            'conf':confs[idx],\n            \"x_min\": box[0],\n            \"y_min\": box[1],\n            \"x_max\": box[2],\n            \"y_max\": box[3],\n        })\n    return results\n\nresult_oof = copy.deepcopy(df_5models[0])\nyolo_pred_df = pd.concat(df_5models)\nweights = [1]* len(df_5models)\niou_thr = 0.4\nmin_conf = yolo_pred_df.conf.min()\nskip_box_thr = 0.0001\nresults = []\nmax_value = 12800\n\nclass_id_name_map = {}\ntmp = result_oof.drop_duplicates('class_name')\nfor class_id, class_name in zip(tmp.class_id.values, tmp.class_name.values):\n    class_id_name_map[class_id] = class_name\n\nwbf_result_maps_list = []\ndf_list = list(yolo_pred_df.groupby('path'))\np = Pool(processes=cpu_count())\nwith tqdm(total=len(df_list)) as pbar:\n    for wbf_result_maps in p.imap(exec, df_list):\n        wbf_result_maps_list += wbf_result_maps\n        pbar.update(1)\np.close()\n\nresults = pd.DataFrame(wbf_result_maps_list)\nfor col in ['class_name', 'class_id', 'conf', 'x_min', 'y_min', 'x_max', 'y_max']:\n    if col in list(result_oof):\n        del result_oof[col]\nresults = results.merge(result_oof.drop_duplicates('path'), on='path')\nresults['class_name'] = results['class_id'].map(class_id_name_map)\ndel results['model_n']\n\ndfs = []\nfor i, idf in results[['path', 'conf','class_id' ,'x_min','y_min','x_max','y_max']].groupby('path'):\n    dfs.append(idf[idf.conf==idf.conf.max()].iloc[:1])\nresults = pd.concat(dfs)\n\nresults.to_csv(f'yolo_results_axial_left.csv', index=False)\nresults.head()\n\nif plot:\n    for path, pdf in results[results.path.isin(results.path.unique()[:5])].groupby('path'):\n        pdf = pdf[pdf.conf==pdf.conf.max()].iloc[:1]\n        im = cv2.imread(path)\n        im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)\n        for class_id, box in zip(pdf.class_id.values, pdf[box_cols].values):\n            cv2.rectangle(\n                im,\n                pt1=(int(box[0]), int(box[1])),\n                pt2=(int(box[2]), int(box[3])),\n                color=(255,255,255),\n                thickness=3\n            )\n        plt.imshow(im[:,:,0], 'gray')\n        plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:34:10.169440Z","iopub.execute_input":"2024-10-17T01:34:10.169798Z","iopub.status.idle":"2024-10-17T01:34:22.423889Z","shell.execute_reply.started":"2024-10-17T01:34:10.169765Z","shell.execute_reply":"2024-10-17T01:34:22.422232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"box_cols = ['x_min', 'y_min', 'x_max','y_max']\nyolo_for_config_dir = '/kaggle/input/rsna-2024-axial-models-v1/rsna-axial-all-images-right-yolox-x'\n\nyolo_for_config = 'rsna_axial_all_images_right_yolox_x'\n\nimport sys\nsys.path.append('/kaggle/input/yolox20230421/YOLOX')\n\nfrom yolox.utils import postprocess\nfrom yolox.data.data_augment import ValTransform\n\n\nclass MyDataset(Dataset):\n    def __init__(self, df):\n        self.paths = df.path.values\n        self.preproc = ValTransform(legacy = False)\n        self.size = 512\n\n    def __len__(self):\n        return len(self.paths)\n\n    def __getitem__(self, idx):\n        path = self.paths[idx]\n        img = cv2.imread(path)\n        ratio = min(self.size / img.shape[0], self.size / img.shape[1])\n\n        img, _ = self.preproc(img, None, (self.size, self.size))\n        img = torch.from_numpy(img).float()\n        img = img.float()\n\n        return img, path, ratio\n\nfrom yolox.exp import Exp as MyExp\n\nclass ExpX(MyExp):\n    def __init__(self):\n        super(ExpX, self).__init__()\n        self.depth = 1.33\n        self.width = 1.25\n        self.exp_name = ''\n        self.data_dir = \"\"\n\n        ### need change ###\n        self.max_epoch = 10\n        self.output_dir = \".\"\n        self.input_size = (512, 512)\n        self.test_size = (512, 512)\n        self.no_aug_epochs = 10 # 15\n        self.warmup_epochs = 5 # 5\n        self.num_classes = 1\n        ### need change ###\n\n        ### fyi ###\n        self.data_num_workers = 16\n        self.eval_interval = 1\n        self.seed = 42\n        self.print_interval = 100000\n        self.eval_interval = 1\n        self.save_history_ckpt = False\n        self.mosaic_prob = 1.0\n        self.mixup_prob = 1.0\n        self.hsv_prob = 1.0\n        self.flip_prob = 0.5\n        self.degrees = 10.0\n        self.translate = 0.1\n        self.mosaic_scale = (0.1, 2)\n        self.enable_mixup = True\n        self.mixup_scale = (0.5, 1.5)\n        self.shear = 2.0\n        self.min_lr_ratio = 0.05\n        self.basic_lr_per_img = 0.00015625\n        self.scheduler = 'yoloxwarmcos'\n        self.ema = True\n        self.weight_decay = 0.0005\n        self.momentum = 0.9\n        self.test_conf = 0.01\n        self.nmsthre = 0.65\n        self.class_id_name_map = {0: 'right'}\n\nclass ExpL(MyExp):\n    def __init__(self):\n        super(ExpL, self).__init__()\n        self.depth = 1.0\n        self.width = 1.0\n        self.exp_name = ''\n        self.data_dir = \"\"\n\n        ### need change ###\n        self.max_epoch = 10\n        self.output_dir = \".\"\n        self.input_size = (512, 512)\n        self.test_size = (512, 512)\n        self.no_aug_epochs = 10 # 15\n        self.warmup_epochs = 5 # 5\n        self.num_classes = 1\n        ### need change ###\n\n        ### fyi ###\n        self.data_num_workers = 16\n        self.eval_interval = 1\n        self.seed = 42\n        self.print_interval = 100000\n        self.eval_interval = 1\n        self.save_history_ckpt = False\n        self.mosaic_prob = 1.0\n        self.mixup_prob = 1.0\n        self.hsv_prob = 1.0\n        self.flip_prob = 0.5\n        self.degrees = 10.0\n        self.translate = 0.1\n        self.mosaic_scale = (0.1, 2)\n        self.enable_mixup = True\n        self.mixup_scale = (0.5, 1.5)\n        self.shear = 2.0\n        self.min_lr_ratio = 0.05\n        self.basic_lr_per_img = 0.00015625\n        self.scheduler = 'yoloxwarmcos'\n        self.ema = True\n        self.weight_decay = 0.0005\n        self.momentum = 0.9\n        self.test_conf = 0.01\n        self.nmsthre = 0.65\n        self.class_id_name_map = {0: 'right'}\n\nexp_x = ExpX()\n\n# set inference parameters\nconfthre = 0.03\nnmsthre = 0.45\n\n# get YOLOX model\nmodels = []\nfor fold in range(1):\n    model = exp_x.get_model()\n    model.to(DEVICE)\n    model.eval()\n    model.head.training=False\n    model.training=False\n    ckpt_file = \"/kaggle/input/notebookfa5fb155d5/rsna-axial-all-images-right-yolox-x/0_best_ckpt.ckpt\"\n    # print(ckpt_file)\n    ckpt = torch.load(ckpt_file, map_location=\"cpu\")\n    model.load_state_dict(ckpt[\"model\"])\n    models.append(model)\n\nds = MyDataset(axial_all_level_df)\nloader = DataLoader(ds, batch_size=16, shuffle=False, drop_last=False, num_workers=4)\n\nall_preds_list = [[]]\nall_ratios_list = [[]]\nall_paths_list = [[]]\n\n# print('inf start...')\nwith torch.no_grad():\n    for loader_n, input in tqdm(enumerate(loader)):\n        # if loader_n % 100 == 0:\n        #     print(loader_n)\n        images, paths, ratios = input\n        images = images.to(DEVICE)\n        for model_n, model in enumerate(models):\n            outputs = model(images)\n            outputs = postprocess(\n                        outputs, exp_x.num_classes, confthre,\n                        nmsthre, class_agnostic=True\n                    )\n            all_preds_list[model_n] += outputs\n            all_ratios_list[model_n] += list(ratios)\n            all_paths_list[model_n] += list(paths)\n\ndf_5models = []\nfor model_n, (all_preds, all_paths, all_ratios) in enumerate(zip(all_preds_list, all_paths_list, all_ratios_list)):\n    dfs = []\n    all_boxes = []\n    all_class_ids = []\n    all_scores = []\n    for n, (predictions, path, ratio)  in enumerate(zip(all_preds, all_paths, all_ratios)):\n        if predictions is None:\n            continue\n        predictions = predictions.cpu().numpy()\n\n        bboxes = predictions[:, 0:4]\n\n        bboxes /= ratio\n        bboxes = bboxes.tolist()\n        bbclasses = predictions[:, 6]\n        scores = predictions[:, 4] * predictions[:, 5]\n        path_df = axial_all_level_df[axial_all_level_df.path==path].iloc[:1]\n        for box, score, class_id in zip(bboxes, scores, bbclasses):\n            all_boxes.append(box)\n            all_scores.append(score)\n            all_class_ids.append(class_id)\n            dfs.append(path_df)\n    tmp = pd.concat(dfs)\n    tmp['class_id'] = all_class_ids\n    tmp['class_id'] = tmp['class_id'].astype(int)\n    tmp['class_name'] = tmp['class_id'].map(exp_x.class_id_name_map)\n    tmp['conf'] = all_scores\n    tmp[box_cols] = all_boxes\n    tmp[box_cols] = np.round(tmp[box_cols]).astype(int)\n    tmp['model_n'] = model_n\n    df_5models.append(tmp)\n\n\n\n\nfrom ensemble_boxes import *\nfrom multiprocessing import cpu_count\nimport copy\n\nfrom multiprocessing import Pool\n\ndef exec(args):\n    path, path_df = args\n    boxes_list = []\n    confs_list = []\n    labels_list = []\n    for _, model_df in path_df.groupby('model_n'):\n        boxes_list.append(model_df[box_cols].values/max_value)\n        confs_list.append(model_df['conf'].values.tolist())\n        labels_list.append(model_df['class_id'].values.tolist())\n    boxes, confs, labels = weighted_boxes_fusion(boxes_list, confs_list, labels_list, weights=[1]*len(boxes_list), iou_thr=iou_thr, skip_box_thr=skip_box_thr)\n    boxes *= max_value\n    results = []\n    for idx, box in enumerate(boxes):\n        results.append({\n            \"path\": path,\n            \"class_id\": int(labels[idx]),\n            'conf':confs[idx],\n            \"x_min\": box[0],\n            \"y_min\": box[1],\n            \"x_max\": box[2],\n            \"y_max\": box[3],\n        })\n    return results\n\nresult_oof = copy.deepcopy(df_5models[0])\nyolo_pred_df = pd.concat(df_5models)\nweights = [1]* len(df_5models)\niou_thr = 0.4\nmin_conf = yolo_pred_df.conf.min()\nskip_box_thr = 0.0001\nresults = []\nmax_value = 12800\n\nclass_id_name_map = {}\ntmp = result_oof.drop_duplicates('class_name')\nfor class_id, class_name in zip(tmp.class_id.values, tmp.class_name.values):\n    class_id_name_map[class_id] = class_name\n\nwbf_result_maps_list = []\ndf_list = list(yolo_pred_df.groupby('path'))\np = Pool(processes=cpu_count())\nwith tqdm(total=len(df_list)) as pbar:\n    for wbf_result_maps in p.imap(exec, df_list):\n        wbf_result_maps_list += wbf_result_maps\n        pbar.update(1)\np.close()\n\nresults = pd.DataFrame(wbf_result_maps_list)\nfor col in ['class_name', 'class_id', 'conf', 'x_min', 'y_min', 'x_max', 'y_max']:\n    if col in list(result_oof):\n        del result_oof[col]\nresults = results.merge(result_oof.drop_duplicates('path'), on='path')\nresults['class_name'] = results['class_id'].map(class_id_name_map)\ndel results['model_n']\n\ndfs = []\nfor i, idf in results[['path', 'conf','class_id' ,'x_min','y_min','x_max','y_max']].groupby('path'):\n    dfs.append(idf[idf.conf==idf.conf.max()].iloc[:1])\nresults = pd.concat(dfs)    \n\nresults.to_csv(f'yolo_results_axial_right.csv', index=False)\nresults.head()\nif plot:\n\n    for path, pdf in results[results.path.isin(results.path.unique()[:5])].groupby('path'):\n        pdf = pdf[pdf.conf==pdf.conf.max()].iloc[:1]\n        im = cv2.imread(path)\n        im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)\n        for class_id, box in zip(pdf.class_id.values, pdf[box_cols].values):\n            cv2.rectangle(\n                im,\n                pt1=(int(box[0]), int(box[1])),\n                pt2=(int(box[2]), int(box[3])),\n                color=(255,255,255),\n                thickness=3\n            )\n        plt.imshow(im[:,:,0], 'gray')\n        plt.show()\n\n\nleft = pd.read_csv('yolo_results_axial_left.csv')\nright = pd.read_csv('yolo_results_axial_right.csv')\nfor c in ['x_min', 'y_min', 'x_max', 'y_max']:\n    right = right.rename(columns={c: 'right_'+c})\n    left = left.rename(columns={c: 'left_'+c})\naxial_box_df = right.merge(left[['path']+['left_x_min', 'left_y_min', 'left_x_max', 'left_y_max']], on='path')\naxial_box_df['x_min'] = axial_box_df[['right_x_min', 'left_x_min']].min(1)\naxial_box_df['y_min'] = axial_box_df[['right_y_min', 'left_y_min']].min(1)\naxial_box_df['x_max'] = axial_box_df[['right_x_max', 'left_x_max']].max(1)\naxial_box_df['y_max'] = axial_box_df[['right_y_max', 'left_y_max']].max(1)\naxial_box_df.path.nunique(), len(axial_all_level_df)\nif 'x_min' not in list(axial_all_level_df):\n    axial_all_level_df = axial_all_level_df.merge(axial_box_df[['x_min', 'y_min', 'x_max', 'y_max', 'path']], on='path')\n\n\nfrom multiprocessing import Pool\n\ndef exec(p):\n    im=cv2.imread(p)\n    return im.shape[:2]\n\np = Pool(processes=4)\nargs = axial_all_level_df.path.values\nresults = p.map(exec, args)    \nimage_size_df = pd.DataFrame(results)\nimage_size_df.columns = ['image_height', 'image_width']\nimage_size_df['path'] = args\nif 'image_height' not in list(axial_all_level_df):\n    axial_all_level_df = axial_all_level_df.merge(image_size_df, on='path')\naxial_all_level_df = axial_all_level_df.sort_values(['series_id', 'pred_level'])    \naxial_all_level_df.to_csv('axial_with_box_df.csv', index=False)    \naxial_all_level_df.head(6)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:34:22.429036Z","iopub.execute_input":"2024-10-17T01:34:22.429506Z","iopub.status.idle":"2024-10-17T01:34:33.279020Z","shell.execute_reply.started":"2024-10-17T01:34:22.429460Z","shell.execute_reply":"2024-10-17T01:34:33.277611Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"box_cols = ['x_min', 'y_min', 'x_max','y_max']\nyolo_for_config_dir = '/kaggle/input/rsna-10classes-yolox-x'\nyolo_for_config = 'rsna_10classes_yolox_x'\n\nimport sys\nsys.path.append('/kaggle/input/yolox20230421/YOLOX')\n\nfrom yolox.utils import postprocess\nfrom yolox.data.data_augment import ValTransform\n\nclass MyDataset(Dataset):\n    def __init__(self, df):\n        self.paths = df.path.values\n        self.preproc = ValTransform(legacy = False)\n        self.size = 512\n\n    def __len__(self):\n        return len(self.paths)\n\n    def __getitem__(self, idx):\n        path = self.paths[idx]\n        img = cv2.imread(path)\n        ratio = min(self.size / img.shape[0], self.size / img.shape[1])\n\n        img, _ = self.preproc(img, None, (self.size, self.size))\n        img = torch.from_numpy(img).float()\n        img = img.float()\n\n        return img, path, ratio\n\nfrom yolox.exp import Exp as MyExp\n\nclass Exp(MyExp):\n    def __init__(self):\n        super(Exp, self).__init__()\n        self.depth = 1.33\n        self.width = 1.25\n        self.exp_name = ''\n        self.data_dir = \"\"\n\n        ### need change ###\n        self.max_epoch = 10\n        self.output_dir = \".\"\n        self.input_size = (512, 512)\n        self.test_size = (512, 512)\n        self.no_aug_epochs = 10 # 15\n        self.warmup_epochs = 5 # 5\n        self.num_classes = 10\n        ### need change ###\n\n        ### fyi ###\n        self.data_num_workers = 16\n        self.eval_interval = 1\n        self.seed = 42\n        self.print_interval = 100\n        self.eval_interval = 1\n        self.save_history_ckpt = False\n        self.mosaic_prob = 1.0\n        self.mixup_prob = 1.0\n        self.hsv_prob = 1.0\n        self.flip_prob = 0.5\n        self.degrees = 10.0\n        self.translate = 0.1\n        self.mosaic_scale = (0.1, 2)\n        self.enable_mixup = True\n        self.mixup_scale = (0.5, 1.5)\n        self.shear = 2.0\n        self.min_lr_ratio = 0.05\n        self.basic_lr_per_img = 0.00015625\n        self.scheduler = 'yoloxwarmcos'\n        self.ema = True\n        self.weight_decay = 0.0005\n        self.momentum = 0.9\n        self.test_conf = 0.01\n        self.nmsthre = 0.65\n        self.class_id_name_map = {\n             0: 'L1/L2_L',\n             1: 'L1/L2_R',\n             2: 'L2/L3_L',\n             3: 'L2/L3_R',\n             4: 'L3/L4_L',\n             5: 'L3/L4_R',\n             6: 'L4/L5_L',\n             7: 'L4/L5_R',\n             8: 'L5/S1_L',\n             9: 'L5/S1_R'\n        }\n\nexp = Exp()\n\n# set inference parameters\nconfthre = 0.03\nnmsthre = 0.45\n\n# get YOLOX model\nmodels = []\nfor fold in range(5):\n    model = exp.get_model()\n    model.cuda()\n    model.eval()\n    model.head.training=False\n    model.training=False\n    if debug:\n        ckpt_file = f\"/kaggle/input/notebookfa5fb155d5/rsna-10classes-yolox-x/{debug_fold}_best_ckpt.ckpt\"\n    else:\n        ckpt_file = f\"/kaggle/input/notebookfa5fb155d5/rsna-10classes-yolox-x/{fold}_best_ckpt.ckpt\"\n    # print(ckpt_file)\n    ckpt = torch.load(ckpt_file, map_location=\"cpu\")\n    model.load_state_dict(ckpt[\"model\"])\n    models.append(model)\nds = MyDataset(spinal_sagittal)\nloader = DataLoader(ds, batch_size=16, shuffle=False, drop_last=False, num_workers=4)\n\nall_preds_list = [[], [], [], [], []]\nall_ratios_list = [[], [], [], [], []]\nall_paths_list = [[], [], [], [], []]\n\n# print('inf start...')\nwith torch.no_grad():\n    for loader_n, input in tqdm(enumerate(loader)):\n        # if loader_n % 100 == 0:\n        #     print(loader_n)\n        images, paths, ratios = input\n        images = images.cuda()\n        for model_n, model in enumerate(models):\n            outputs = model(images)\n            outputs = postprocess(\n                        outputs, exp.num_classes, confthre,\n                        nmsthre, class_agnostic=True\n                    )\n            all_preds_list[model_n] += outputs\n            all_ratios_list[model_n] += list(ratios)\n            all_paths_list[model_n] += list(paths)\n\ndf_5models = []\nfor model_n, (all_preds, all_paths, all_ratios) in enumerate(zip(all_preds_list, all_paths_list, all_ratios_list)):\n    dfs = []\n    all_boxes = []\n    all_class_ids = []\n    all_scores = []\n    for n, (predictions, path, ratio)  in enumerate(zip(all_preds, all_paths, all_ratios)):\n        if predictions is None:\n            continue\n        predictions = predictions.cpu().numpy()\n\n        bboxes = predictions[:, 0:4]\n\n        bboxes /= ratio\n        bboxes = bboxes.tolist()\n        bbclasses = predictions[:, 6]\n        scores = predictions[:, 4] * predictions[:, 5]\n        path_df = sagittal_df[sagittal_df.path==path]\n        for box, score, class_id in zip(bboxes, scores, bbclasses):\n            all_boxes.append(box)\n            all_scores.append(score)\n            all_class_ids.append(class_id)\n            dfs.append(path_df)\n    tmp = pd.concat(dfs)\n    tmp['class_id'] = all_class_ids\n    tmp['class_id'] = tmp['class_id'].astype(int)\n    tmp['class_name'] = tmp['class_id'].map(exp.class_id_name_map)\n    tmp['conf'] = all_scores\n    tmp[box_cols] = all_boxes\n    tmp[box_cols] = np.round(tmp[box_cols]).astype(int)\n    tmp['model_n'] = model_n\n    df_5models.append(tmp)\n\nfrom ensemble_boxes import *\nfrom multiprocessing import cpu_count\nimport copy\n\nfrom multiprocessing import Pool\n\ndef exec(args):\n    path, path_df = args\n    boxes_list = []\n    confs_list = []\n    labels_list = []\n    for _, model_df in path_df.groupby('model_n'):\n        boxes_list.append(model_df[box_cols].values/max_value)\n        confs_list.append(model_df['conf'].values.tolist())\n        labels_list.append(model_df['class_id'].values.tolist())\n    boxes, confs, labels = weighted_boxes_fusion(boxes_list, confs_list, labels_list, weights=[1]*len(boxes_list), iou_thr=iou_thr, skip_box_thr=skip_box_thr)\n    boxes *= max_value\n    results = []\n    for idx, box in enumerate(boxes):\n        results.append({\n            \"path\": path,\n            \"class_id\": int(labels[idx]),\n            'conf':confs[idx],\n            \"x_min\": box[0],\n            \"y_min\": box[1],\n            \"x_max\": box[2],\n            \"y_max\": box[3],\n        })\n    return results\n\nresult_oof = copy.deepcopy(df_5models[0])\nyolo_pred_df = pd.concat(df_5models)\nweights = [1]* len(df_5models)\niou_thr = 0.4\nmin_conf = yolo_pred_df.conf.min()\nskip_box_thr = 0.0001\nresults = []\nmax_value = 12800\n\nclass_id_name_map = {}\ntmp = result_oof.drop_duplicates('class_name')\nfor class_id, class_name in zip(tmp.class_id.values, tmp.class_name.values):\n    class_id_name_map[class_id] = class_name\n\nwbf_result_maps_list = []\ndf_list = list(yolo_pred_df.groupby('path'))\np = Pool(processes=cpu_count())\nwith tqdm(total=len(df_list)) as pbar:\n    for wbf_result_maps in p.imap(exec, df_list):\n        wbf_result_maps_list += wbf_result_maps\n        pbar.update(1)\np.close()\n\nresults = pd.DataFrame(wbf_result_maps_list)\nfor col in ['class_name', 'class_id', 'conf', 'x_min', 'y_min', 'x_max', 'y_max']:\n    if col in list(result_oof):\n        del result_oof[col]\nresults = results.merge(result_oof.drop_duplicates('path'), on='path')\nresults['class_name'] = results['class_id'].map(class_id_name_map)\ndel results['model_n']\n\ndfs = []\nfor i, idf in results.groupby(['path', 'class_id']):\n    dfs.append(idf[idf.conf==idf.conf.max()].iloc[:1])\nresults = pd.concat(dfs)    \n\n\nresults.to_csv(f'yolo_results_sagittal.csv', index=False)\nresults.head()\nif plot:\n\n    for path, pdf in results[results.path.isin(results.path.unique()[:5])].groupby('path'):\n        im = cv2.imread(path)\n        im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)\n        for class_id, box in zip(pdf.class_id.values, pdf[box_cols].values):\n            cv2.rectangle(\n                im,\n                pt1=(int(box[0]), int(box[1])),\n                pt2=(int(box[2]), int(box[3])),\n                color=(255,255,255),\n                thickness=3\n            )\n        plt.imshow(im[:,:,0], 'gray')\n        plt.show()\n\ndel models\ntorch.cuda.empty_cache()\n\n\nspinal_sagittal = pd.read_csv('spinal_sagittal_df.csv')\nl = len(spinal_sagittal)\nyolo_results_sagittal = pd.read_csv('yolo_results_sagittal.csv')[['path', 'class_id', 'class_name', 'conf','x_min','y_min','x_max','y_max']]\nyolo_results_sagittal['study_id'] = yolo_results_sagittal.path.apply(lambda x: int(x.split('/')[-1].split('___')[0]))\ndel yolo_results_sagittal['path']\n\nsagittal_box_df = spinal_sagittal.merge(yolo_results_sagittal, on='study_id')\nsagittal_box_df['level'] = sagittal_box_df.class_name.apply(lambda x: x.split('_')[0])\nsagittal_box_df['lr'] = sagittal_box_df.class_name.apply(lambda x: x.split('_')[1])\nsagittal_box_df = sagittal_box_df[['study_id', 'path', 'level', 'lr']+box_cols]\nsagittal_box_df.to_csv('sagittal_box_df.csv', index=False)\n\nsagittal_df = pd.read_csv('sagittal_df.csv')                                                        \n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:34:33.282108Z","iopub.execute_input":"2024-10-17T01:34:33.282547Z","iopub.status.idle":"2024-10-17T01:35:20.971739Z","shell.execute_reply.started":"2024-10-17T01:34:33.282504Z","shell.execute_reply":"2024-10-17T01:35:20.970671Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# classification for axial","metadata":{}},{"cell_type":"code","source":"class axial_base(base):\n    def __init__(self):\n        super().__init__()\n        self.df = pd.read_csv('axial_with_box_df.csv')\n        cols = []\n        label_features = [\n            'spinal_canal_stenosis',\n            # 'left_neural_foraminal_narrowing',\n            # 'right_neural_foraminal_narrowing',\n            # 'left_subarticular_stenosis',\n            # 'right_subarticular_stenosis',\n        ]\n        for col in label_features:\n            cols.append(f'{col}_normal')\n            cols.append(f'{col}_moderate')\n            cols.append(f'{col}_severe')\n        self.label_features = cols\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features))\n        d = '/kaggle/input/rsna-2024-axial-models-v1/rsna-axial-spinal-dis3-crop-x05-y6'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        self.batch_size = 16\n\nclass rsna_axial_spinal_ss_dis3(axial_base):\n    def __init__(self):\n        super().__init__()\n        cols = []\n        label_features = [\n            'spinal_canal_stenosis',\n            # 'left_neural_foraminal_narrowing',\n            # 'right_neural_foraminal_narrowing',\n            'left_subarticular_stenosis',\n            'right_subarticular_stenosis',\n        ]\n        for col in label_features:\n            cols.append(f'{col}_normal')\n            cols.append(f'{col}_moderate')\n            cols.append(f'{col}_severe')\n        self.label_features = cols\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features))\n        d = '/kaggle/input/rsna-2024-reduce-noise-models/axial-spinal-ss-reduce-noise-08'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n            \nclass rsna_axial_spinal_dis3_crop_x05_y6(axial_base):\n    def __init__(self):\n        super().__init__()\n        cols = []\n        label_features = [\n            'spinal_canal_stenosis',\n            # 'left_neural_foraminal_narrowing',\n            # 'right_neural_foraminal_narrowing',\n#             'left_subarticular_stenosis',\n#             'right_subarticular_stenosis',\n        ]\n        for col in label_features:\n            cols.append(f'{col}_normal')\n            cols.append(f'{col}_moderate')\n            cols.append(f'{col}_severe')\n        self.label_features = cols\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features))\n        d = '/kaggle/input/rsna-2024-reduce-noise-models/axial-spinal-dis3-crop-x05-y6-reduce-noise-08'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.box_crop = True\n        self.box_crop_x_ratio = 0.5\n        self.box_crop_y_ratio = 6\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n            \nclass rsna_axial_spinal_dis3_crop_x1_y2(axial_base):\n    def __init__(self):\n        super().__init__()\n        cols = []\n        label_features = [\n            'spinal_canal_stenosis',\n            # 'left_neural_foraminal_narrowing',\n            # 'right_neural_foraminal_narrowing',\n#             'left_subarticular_stenosis',\n#             'right_subarticular_stenosis',\n        ]\n        for col in label_features:\n            cols.append(f'{col}_normal')\n            cols.append(f'{col}_moderate')\n            cols.append(f'{col}_severe')\n        self.label_features = cols\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features))\n        d = '/kaggle/input/rsna-2024-reduce-noise-models/axial-spinal-dis3-crop-x1-y2-reduce-noise-08'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.box_crop = True\n        self.box_crop_x_ratio = 1\n        self.box_crop_y_ratio = 2\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\n\nclass rsna_axial_ss_nfn_x2_y2_center_pad0(axial_base):\n    def __init__(self):\n        super().__init__()\n        self.box_crop = True\n        self.box_crop_x_ratio = 0\n        image_width_ratio = 2\n        self.box_crop_y_ratio = 2\n        center_pad_ratio = 0\n\n        df_path = 'axial_with_box_df.csv'\n\n        df_left = pd.read_csv(df_path)\n        df_left['x_min'] = (df_left.x_max + df_left.x_min)/2\n        del df_left['x_max']\n        df_left['left_right'] = 'left'\n        if center_pad_ratio != 0:\n            df_left['x_min'] = df_left['x_min'] - df_left['image_width']/center_pad_ratio\n        df_left['x_max'] = df_left['x_min'] + df_left['image_width']/image_width_ratio\n\n        df_right = pd.read_csv(df_path)\n        df_right['x_max'] = (df_right.x_max + df_right.x_min)/2\n        del df_right['x_min']\n        df_right['left_right'] = 'right'\n        df_right['x_min'] = df_right['x_max'] - df_right['image_width']/image_width_ratio\n        if center_pad_ratio != 0:\n            df_right['x_max'] = df_right['x_max'] + df_right['image_width']/center_pad_ratio\n\n        self.df = pd.concat([df_left, df_right])\n\n        # self.df = self.df[(self.df.dis < 3) | (self.df.dis.isnull())]\n\n        cols = []\n        label_features = [\n            'neural_foraminal_narrowing',\n            'subarticular_stenosis',\n        ]\n        for col in label_features:\n            cols.append(f'{col}_normal')\n            cols.append(f'{col}_moderate')\n            cols.append(f'{col}_severe')\n\n        self.label_features = cols\n        self.image_size = 384\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features),\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        n = 'axial_ss_nfn_x2_y2_pad0_reduce_noise'\n        d = f'/kaggle/input/rsna-2024-reduce-noise-models-v2/{n.replace(\"_\", \"-\")}'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\nclass rsna_axial_ss_nfn_x2_y6_center_pad0(axial_base):\n    def __init__(self):\n        super().__init__()\n        self.box_crop = True\n        self.box_crop_x_ratio = 0\n        image_width_ratio = 2\n        self.box_crop_y_ratio = 6\n        center_pad_ratio = 0\n\n        df_path = 'axial_with_box_df.csv'\n\n        df_left = pd.read_csv(df_path)\n        df_left['x_min'] = (df_left.x_max + df_left.x_min)/2\n        del df_left['x_max']\n        df_left['left_right'] = 'left'\n        if center_pad_ratio != 0:\n            df_left['x_min'] = df_left['x_min'] - df_left['image_width']/center_pad_ratio\n        df_left['x_max'] = df_left['x_min'] + df_left['image_width']/image_width_ratio\n\n        df_right = pd.read_csv(df_path)\n        df_right['x_max'] = (df_right.x_max + df_right.x_min)/2\n        del df_right['x_min']\n        df_right['left_right'] = 'right'\n        df_right['x_min'] = df_right['x_max'] - df_right['image_width']/image_width_ratio\n        if center_pad_ratio != 0:\n            df_right['x_max'] = df_right['x_max'] + df_right['image_width']/center_pad_ratio\n\n        self.df = pd.concat([df_left, df_right])\n\n        # self.df = self.df[(self.df.dis < 3) | (self.df.dis.isnull())]\n\n        cols = []\n        label_features = [\n            'neural_foraminal_narrowing',\n            'subarticular_stenosis',\n        ]\n        for col in label_features:\n            cols.append(f'{col}_normal')\n            cols.append(f'{col}_moderate')\n            cols.append(f'{col}_severe')\n\n        self.label_features = cols\n        self.image_size = 384\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features),\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        n = 'axial_ss_nfn_x2_y6_pad0_reduce_noise'\n        d = f'/kaggle/input/rsna-2024-reduce-noise-models-v2/{n.replace(\"_\", \"-\")}'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\nclass rsna_axial_ss_nfn_x2_y8_center_pad10(axial_base):\n    def __init__(self):\n        super().__init__()\n        self.box_crop = True\n        self.box_crop_x_ratio = 0\n        image_width_ratio = 2\n        self.box_crop_y_ratio = 8\n        center_pad_ratio = 10\n\n        df_path = 'axial_with_box_df.csv'\n\n        df_left = pd.read_csv(df_path)\n        df_left['x_min'] = (df_left.x_max + df_left.x_min)/2\n        del df_left['x_max']\n        df_left['left_right'] = 'left'\n        if center_pad_ratio != 0:\n            df_left['x_min'] = df_left['x_min'] - df_left['image_width']/center_pad_ratio\n        df_left['x_max'] = df_left['x_min'] + df_left['image_width']/image_width_ratio\n\n        df_right = pd.read_csv(df_path)\n        df_right['x_max'] = (df_right.x_max + df_right.x_min)/2\n        del df_right['x_min']\n        df_right['left_right'] = 'right'\n        df_right['x_min'] = df_right['x_max'] - df_right['image_width']/image_width_ratio\n        if center_pad_ratio != 0:\n            df_right['x_max'] = df_right['x_max'] + df_right['image_width']/center_pad_ratio\n\n        self.df = pd.concat([df_left, df_right])\n\n        # self.df = self.df[(self.df.dis < 3) | (self.df.dis.isnull())]\n\n        cols = []\n        label_features = [\n            'neural_foraminal_narrowing',\n            'subarticular_stenosis',\n        ]\n        for col in label_features:\n            cols.append(f'{col}_normal')\n            cols.append(f'{col}_moderate')\n            cols.append(f'{col}_severe')\n\n        self.label_features = cols\n        self.image_size = 384\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0\n        self.model = timm.create_model(self.model_name, pretrained=False, num_classes=len(self.label_features),\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        n = 'axial_ss_nfn_x2_y8_pad10_reduce_noise'\n        d = f'/kaggle/input/rsna-2024-reduce-noise-models-v2/{n.replace(\"_\", \"-\")}'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:35:20.973389Z","iopub.execute_input":"2024-10-17T01:35:20.973742Z","iopub.status.idle":"2024-10-17T01:35:21.020079Z","shell.execute_reply.started":"2024-10-17T01:35:20.973713Z","shell.execute_reply":"2024-10-17T01:35:21.019270Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import copy\nconfig_names = [\n    'rsna_axial_ss_nfn_x2_y2_center_pad0',\n    'rsna_axial_ss_nfn_x2_y6_center_pad0',\n    'rsna_axial_ss_nfn_x2_y8_center_pad10',\n\n    'rsna_axial_spinal_dis3_crop_x05_y6',\n    'rsna_axial_spinal_dis3_crop_x1_y2',\n]\nfor config_name in config_names:\n    cfg = eval(config_name)()\n    models = []\n    for model_path in cfg.model_paths:\n        state_dict = torch.load(model_path, map_location=torch.device('cpu'))\n        model = copy.deepcopy(cfg.model)\n        model.load_state_dict(state_dict)\n        model.to(DEVICE)\n        model.eval()\n        models.append(model)\n\n\n    ds = RSNA2024DatasetV1(cfg)\n    loader = DataLoader(ds, batch_size=cfg.batch_size, shuffle=False, drop_last=False, num_workers=4)\n    preds = []\n    for images in tqdm(loader, smoothing=0):\n        images = images.to(DEVICE)\n        batch_preds = []\n        for model in models:\n            batch_preds.append(model(images).detach().cpu().numpy())\n\n        if cfg.tta:\n            imsize = images.size()\n            images = torch.flip(images, (3,))\n            assert imsize == images.size()\n            for model in models:\n                batch_preds.append(model(images).detach().cpu().numpy())\n\n        preds += np.mean(batch_preds, axis=0).tolist()\n    pred_cols = [f'pred_{c}' for c in cfg.label_features]\n    cfg.df[pred_cols] = np.array(preds)\n    cfg.df.to_csv(f'{config_name}.csv', index=False)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:35:21.021196Z","iopub.execute_input":"2024-10-17T01:35:21.021463Z","iopub.status.idle":"2024-10-17T01:36:42.580789Z","shell.execute_reply.started":"2024-10-17T01:35:21.021440Z","shell.execute_reply":"2024-10-17T01:36:42.579768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# sagittal classification","metadata":{}},{"cell_type":"code","source":"# preprocess for classification\n\nrolling = 5\nrange_n  = 2\n\nsaigittal_box_df = pd.read_csv('sagittal_box_df.csv')\nsagittal_df = pd.read_csv('sagittal_df.csv')\nsagittal_df.path = '/kaggle/temp/sagittal_all_images/' + sagittal_df.study_id.astype(str) + '___' + sagittal_df.instance_number.astype(str) + '.png'\n\ndfs = []\nfor id, idf in sagittal_df.groupby('series_id'):\n    idf = idf.sort_values(['x_pos', 'instance_number'])\n    idf = idf.drop_duplicates('x_pos')\n    idf[f'pred_spinal_rolling'] = idf[f'pred_spinal'].rolling(rolling, center=True).mean()    \n\n    paths = []\n    for col in ['pred_spinal_rolling']:\n        n = idf[idf[col]==idf[col].max()].instance_number.values[0]\n    \n        ldf = idf[(idf.instance_number >= n-range_n) & (idf.instance_number <= n+range_n)]\n        l_paths = ['nan'] * (1+range_n*2)\n        for path_n, path in enumerate(ldf.path):\n            l_paths[path_n] = path\n        paths += l_paths\n    ldf = ldf.iloc[:1]\n    ldf['paths'] = ','.join(paths)\n    dfs.append(ldf)\nspinal_df = pd.concat(dfs)\nspinal_df = spinal_df.drop_duplicates('study_id')\ndel spinal_df['path']\nspinal_df = spinal_df.merge(saigittal_box_df, on=['study_id'])\ndfs = []\nfor i, idf in spinal_df.groupby(['study_id', 'level']):\n    l = idf[idf.lr == 'L']\n    r = idf[idf.lr == 'R']\n    if (len(l) == 0) | (len(r) == 0):\n        continue\n    idf['l_x'] = (l.x_max.values[0] + l.x_min.values[0])/2\n    idf['l_y'] = (l.y_max.values[0] + l.y_min.values[0])/2\n    idf['r_x'] = (r.x_max.values[0] + r.x_min.values[0])/2\n    idf['r_y'] = (r.y_max.values[0] + r.y_min.values[0])/2\n    idf = idf.iloc[:1]\n    dfs.append(idf)\nspinal_df = pd.concat(dfs)\n\np = f'sagittal_spinal_range{range_n}_rolling{rolling}_v2.csv'\nspinal_df.to_csv(p, index=False)\n# print(p)\n\nrolling = 5\nrange_n  = 2\n\nsaigittal_box_df = pd.read_csv('sagittal_box_df.csv')\nsagittal_df = pd.read_csv('sagittal_df.csv')\nsagittal_df = sagittal_df[sagittal_df.series_id.isin(t2_ids)]\ndfs = []\nfor id, idf in sagittal_df.groupby('series_id'):\n    idf = idf.sort_values(['x_pos', 'instance_number'])\n    idf = idf.drop_duplicates('x_pos')\n    idf[f'pred_spinal_rolling'] = idf[f'pred_spinal'].rolling(rolling, center=True).mean()    \n\n    paths = []\n    for col in ['pred_spinal_rolling']:\n        n = idf[idf[col]==idf[col].max()].instance_number.values[0]\n    \n        ldf = idf[(idf.instance_number >= n-range_n) & (idf.instance_number <= n+range_n)]\n        l_paths = ['nan'] * (1+range_n*2)\n        for path_n, path in enumerate(ldf.path):\n            l_paths[path_n] = path\n        paths += l_paths\n    ldf = ldf.iloc[:1]\n    ldf['paths'] = ','.join(paths)\n    dfs.append(ldf)\nspinal_df = pd.concat(dfs)\nspinal_df = spinal_df.drop_duplicates('study_id')\ndel spinal_df['path']\nspinal_df = spinal_df.merge(saigittal_box_df, on=['study_id'])\ndfs = []\nfor i, idf in spinal_df.groupby(['study_id', 'level']):\n    l = idf[idf.lr == 'L']\n    r = idf[idf.lr == 'R']\n    if (len(l) == 0) | (len(r) == 0):\n        continue\n    idf['l_x'] = (l.x_max.values[0] + l.x_min.values[0])/2\n    idf['l_y'] = (l.y_max.values[0] + l.y_min.values[0])/2\n    idf['r_x'] = (r.x_max.values[0] + r.x_min.values[0])/2\n    idf['r_y'] = (r.y_max.values[0] + r.y_min.values[0])/2\n    idf = idf.iloc[:1]\n    dfs.append(idf)\nspinal_df = pd.concat(dfs)\n\np = f'sagittal_spinal_range{range_n}_rolling{rolling}_t2.csv'\nspinal_df.to_csv(p, index=False)\n\nrolling = 5\nrange_n  = 2\n\nfor left_right in ['left', 'right']:\n    col = f'pred_{left_right}_neural_rolling'\n    saigittal_box_df = pd.read_csv('sagittal_box_df.csv')\n    sagittal_df = pd.read_csv('sagittal_df.csv')\n    sagittal_df.path = '/kaggle/temp/sagittal_all_images/' + sagittal_df.study_id.astype(str) + '___' + sagittal_df.instance_number.astype(str) + '.png'\n\n    dfs = []\n    for id, idf in sagittal_df.groupby('series_id'):\n        idf = idf.sort_values(['x_pos', 'instance_number'])\n        idf = idf.drop_duplicates('x_pos')\n        idf[col] = idf[f'pred_{left_right}_neural'].rolling(rolling, center=True).mean()\n\n        n = idf[idf[col]==idf[col].max()].instance_number.values[0]\n    \n        ldf = idf[(idf.instance_number >= n-range_n) & (idf.instance_number <= n+range_n)]\n        l_paths = ['nan'] * (1+range_n*2)\n        for path_n, path in enumerate(ldf.path):\n            l_paths[path_n] = path\n        ldf = ldf.iloc[:1]\n        ldf['paths'] = ','.join(l_paths)\n        dfs.append(ldf)\n    nfn_df = pd.concat(dfs)\n    nfn_df = nfn_df.drop_duplicates('study_id')\n    del nfn_df['path']\n    nfn_df = nfn_df.merge(saigittal_box_df, on=['study_id'])\n    dfs = []\n    for i, idf in nfn_df.groupby(['study_id', 'level']):\n        l = idf[idf.lr == 'L']\n        r = idf[idf.lr == 'R']\n        if (len(l) == 0) | (len(r) == 0):\n            continue\n        idf['l_x'] = (l.x_max.values[0] + l.x_min.values[0])/2\n        idf['l_y'] = (l.y_max.values[0] + l.y_min.values[0])/2\n        idf['r_x'] = (r.x_max.values[0] + r.x_min.values[0])/2\n        idf['r_y'] = (r.y_max.values[0] + r.y_min.values[0])/2\n        idf = idf.iloc[:1]\n        dfs.append(idf)\n    nfn_df = pd.concat(dfs)\n\n    p = f'sagittal_{left_right}_nfn_range{range_n}_rolling{rolling}.csv'\n    nfn_df.to_csv(p, index=False)\n\nrolling = 5\nrange_n  = 2\n\nfor left_right in ['left', 'right']:\n    col = f'pred_{left_right}_neural_rolling'\n    saigittal_box_df = pd.read_csv('sagittal_box_df.csv')\n    sagittal_df = pd.read_csv('sagittal_df.csv')\n    sagittal_df.path = '/kaggle/temp/sagittal_all_images/' + sagittal_df.study_id.astype(str) + '___' + sagittal_df.instance_number.astype(str) + '.png'\n\n    dfs = []\n    for id, idf in sagittal_df.groupby('series_id'):\n        idf = idf.sort_values(['x_pos', 'instance_number'])\n        idf = idf.drop_duplicates('x_pos')\n        idf['pred_spinal_rolling'] = idf[f'pred_spinal'].rolling(rolling, center=True).mean()        \n        idf[col] = idf[f'pred_{left_right}_neural'].rolling(rolling, center=True).mean()\n\n        spinal_n = idf[idf['pred_spinal_rolling']==idf['pred_spinal_rolling'].max()].instance_number.values[0]\n        n = idf[idf[col]==idf[col].max()].instance_number.values[0]\n        n = (spinal_n + n)//2\n    \n        ldf = idf[(idf.instance_number >= n-range_n) & (idf.instance_number <= n+range_n)]\n        l_paths = ['nan'] * (1+range_n*2)\n        for path_n, path in enumerate(ldf.path):\n            l_paths[path_n] = path\n        ldf = ldf.iloc[:1]\n        ldf['paths'] = ','.join(l_paths)\n        dfs.append(ldf)\n    nfn_df = pd.concat(dfs)\n    nfn_df = nfn_df.drop_duplicates('study_id')\n    del nfn_df['path']\n    nfn_df = nfn_df.merge(saigittal_box_df, on=['study_id'])\n    dfs = []\n    for i, idf in nfn_df.groupby(['study_id', 'level']):\n        l = idf[idf.lr == 'L']\n        r = idf[idf.lr == 'R']\n        if (len(l) == 0) | (len(r) == 0):\n            continue\n        idf['l_x'] = (l.x_max.values[0] + l.x_min.values[0])/2\n        idf['l_y'] = (l.y_max.values[0] + l.y_min.values[0])/2\n        idf['r_x'] = (r.x_max.values[0] + r.x_min.values[0])/2\n        idf['r_y'] = (r.y_max.values[0] + r.y_min.values[0])/2\n        idf = idf.iloc[:1]\n        dfs.append(idf)\n    nfn_df = pd.concat(dfs)\n\n    p = f'sagittal_{left_right}_ss_range{range_n}_rolling{rolling}.csv'\n    nfn_df.to_csv(p, index=False)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:36:42.582860Z","iopub.execute_input":"2024-10-17T01:36:42.583182Z","iopub.status.idle":"2024-10-17T01:36:42.813690Z","shell.execute_reply.started":"2024-10-17T01:36:42.583154Z","shell.execute_reply":"2024-10-17T01:36:42.812629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import math\ndef crop_between_keypoints(img, keypoint1, keypoint2, ratio=0.1):\n    h, w = img.shape[:2]\n    x1, y1 = int(keypoint1[0]), int(keypoint1[1])\n    x2, y2 = int(keypoint2[0]), int(keypoint2[1])\n    \n    # Calculate bounding box around the keypoints\n    left = int(min(x1, x2))\n    right = int(max(x1, x2))\n    top = int(min(y1, y2) - (h * 0.1))\n    bottom = int(max(y1, y2) + (h * 0.1))\n            \n    # Crop the image\n    return img[top:bottom, left:right, :]\n\ndef angle_of_line(x1, y1, x2, y2):\n    return math.degrees(math.atan2(-(y2-y1), x2-x1))\n\nclass RSNA2024DatasetMIL(Dataset):\n    def __init__(self, cfg):\n        self.transforms = cfg.transforms\n        self.paths = cfg.df.path.values\n        self.paths_list = cfg.df.paths.values\n        self.cfg = cfg\n        self.l_points = cfg.df[['l_x', 'l_y']].values\n        self.r_points = cfg.df[['r_x', 'r_y']].values\n\n    def __len__(self):\n        return len(self.paths)\n\n    def load_image(self, path, a, b, origin_size):\n        if path == 'nan':\n            image = np.zeros((origin_size[0], origin_size[1], 3)).astype(np.uint8)\n        else:\n            image = cv2.imread(path)\n            image = cv2.resize(image, (origin_size[1], origin_size[0]))\n\n        rotate_angle = angle_of_line(a[0], a[1], b[0], b[1])\n        transform = A.Compose([\n            A.Rotate(limit=(-rotate_angle, -rotate_angle), p=1.0),\n        ], keypoint_params= A.KeypointParams(format='xy', remove_invisible=False),\n        )\n\n        t = transform(image=image, keypoints=[a, b])\n        image = t[\"image\"]\n        a, b = t[\"keypoints\"]\n        if a[0]<0:\n            a = (0, a[1])\n        if b[0]<0:\n            b = (0, b[1])\n        if a[1]<0:\n            a = (a[0], 0)\n        if b[1]<0:\n            b = (b[0], 0) \n        if self.cfg.box_crop:\n            if ((hasattr(self.cfg, 'xy_center_point')) and (self.cfg.xy_center_point)):\n                x = int((a[0]+b[0])/2)\n                y = int((a[1]+b[1])/2)\n            else:\n                x = int(b[0])\n                y = int(b[1])\n            \n            w = abs(b[0]-a[0])\n            h = image.shape[0]*0.2\n\n            crop_x = int(w * self.cfg.box_crop_x_ratio)\n            crop_y = int(h * self.cfg.box_crop_y_ratio)\n            x_min = max(x-crop_x, 0)\n            y_min = max(y-crop_y, 0)\n\n            image = image[y_min:y+crop_y, x_min:x+crop_x]\n        else:\n            image = crop_between_keypoints(image, a, b)\n        return image\n\n    def __getitem__(self, idx):\n        path = self.paths[idx]\n        paths = self.paths_list[idx]\n\n        origin_size = cv2.imread(path).shape[:2]\n        a = self.l_points[idx]\n        b = self.r_points[idx]\n        images = []\n        for path in paths.split(','):\n            image = self.load_image(path, a, b, origin_size)\n            images.append(self.transforms(image=image.astype(np.uint8))['image'])\n\n        images = np.stack(images, 0)\n        images = torch.tensor(images).float()\n\n        return images\n\nclass Flatten(nn.Module):\n    def __init__(self, dim=1):\n        super().__init__()\n        self.dim = dim\n\n    def forward(self, x):\n        input_shape = x.shape\n        output_shape = [input_shape[i] for i in range(self.dim)] + [-1]\n        return x.view(*output_shape)\n\nclass AdaptiveConcatPool2d(nn.Module):\n    def __init__(self, sz=None, flatten=False):\n        super().__init__()\n        sz = sz or (1, 1)\n        self.ap = nn.AdaptiveAvgPool2d(sz)\n        self.mp = nn.AdaptiveMaxPool2d(sz)\n        self.flatten_layer = Flatten()\n        self.flatten = flatten\n\n    def forward(self, x):\n        x = torch.cat([self.mp(x), self.ap(x)], 1)\n        if self.flatten:\n            x = self.flatten_layer(x)\n        return x\n\n\ndef drop_fc(model):\n    nc = model.head.fc.in_features\n    model.head.global_pool = nn.Identity()\n    model.head.norm = nn.Identity()\n    model.head.flatten = nn.Identity()\n    model.head.drop = nn.Identity()\n    model.head.fc = nn.Identity()\n    new_model = model\n\n    return new_model, nc\n\nclass RSNA2ndModel(nn.Module):\n    def __init__(\n        self,\n        num_classes=1,\n        base_model=None,\n    ):\n        super().__init__()\n        self.encoder, nc = drop_fc(base_model)\n        self.head = nn.Sequential(\n            AdaptiveConcatPool2d(),\n            Flatten(),\n            nn.Linear(2 * nc, num_classes),\n        )\n    def forward(self, x):\n        bs, n, ch, w, h = x.shape\n        x = x.view(bs * n, ch, w, h)\n        x = self.encoder(x)\n        bs2, ch2, w2, h2 = x.shape\n\n        x = (\n            x.view(-1, n, ch2, w2, h2)\n            .permute(0, 2, 1, 3, 4)\n            .contiguous()\n            .view(bs, ch2, n * w2, h2)\n        )\n\n        x = self.head(x)\n        return x\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:36:42.815123Z","iopub.execute_input":"2024-10-17T01:36:42.815458Z","iopub.status.idle":"2024-10-17T01:36:42.848336Z","shell.execute_reply.started":"2024-10-17T01:36:42.815431Z","shell.execute_reply":"2024-10-17T01:36:42.847460Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class rsna_saggital_spinal_v2(base):\n    def __init__(self):\n        super().__init__()\n        self.df = pd.read_csv('sagittal_spinal_range2_rolling5_v2.csv')\n        self.label_features = [\n            'spinal_canal_stenosis_normal',\n            'spinal_canal_stenosis_moderate',\n            'spinal_canal_stenosis_severe',\n        ]\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0        \n        base_model = timm.create_model(self.model_name, pretrained=False, num_classes=1,\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        self.model = RSNA2ndModel(\n            base_model=base_model,\n            num_classes=len(self.label_features)\n        )\n        \n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/rsna-saggital-spinal-v2'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\n        self.image_size = 128\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        self.box_crop = True\n        self.box_crop_x_ratio = 0.5\n        self.box_crop_y_ratio = 0.5\n        self.xy_center_point = True\n        \nclass rsna_saggital_mil_spinal_crop_x03_y05(rsna_saggital_spinal_v2):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 0.5\n        self.xy_center_point = False\n        d = '/kaggle/input/rsna-2024-reduce-noise-models/saggital-spinal-x03-y05-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        \nclass rsna_saggital_mil_spinal_crop_x03_y07(rsna_saggital_spinal_v2):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 0.7\n        self.xy_center_point = False\n        d = '/kaggle/input/rsna-2024-reduce-noise-models/saggital-spinal-x03-y07-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        \nclass rsna_saggital_mil_spinal_crop_x05_y15(rsna_saggital_spinal_v2):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop_x_ratio = 0.5\n        self.box_crop_y_ratio = 1.5\n        self.xy_center_point = False\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/rsna-saggital-mil-spinal-crop-x05-y15'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\nclass rsna_saggital_mil_spinal_crop_x03_y07_t2_v4(rsna_saggital_spinal_v2):\n    def __init__(self):\n        super().__init__()\n        self.df = pd.read_csv('sagittal_spinal_range2_rolling5_t2.csv')\n        self.label_features = [\n            'spinal_canal_stenosis_normal',\n            'spinal_canal_stenosis_moderate',\n            'spinal_canal_stenosis_severe',\n        ]\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0        \n        base_model = timm.create_model(self.model_name, pretrained=False, num_classes=1,\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        self.model = RSNA2ndModel(\n            base_model=base_model,\n            num_classes=len(self.label_features)\n        )\n\n        \n        d = '/kaggle/input/rsna-2024-reduce-noise-models/saggital-spinal-x03-y07-t2-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\n        self.box_crop = True\n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 0.7\n        self.xy_center_point = False\n        self.image_size = 128\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        \nclass rsna_saggital_mil_spinal_crop_x03_y05_t2_v4_auxloss(rsna_saggital_spinal_v2):\n    def __init__(self):\n        super().__init__()\n        self.df = pd.read_csv('sagittal_spinal_range2_rolling5_t2.csv')\n        self.label_features = [\n            'spinal_canal_stenosis_normal',\n            'spinal_canal_stenosis_moderate',\n            'spinal_canal_stenosis_severe',\n            'spinal_canal_stenosis',\n        ]\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0        \n        base_model = timm.create_model(self.model_name, pretrained=False, num_classes=1,\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        self.model = RSNA2ndModel(\n            base_model=base_model,\n            num_classes=len(self.label_features)\n        )\n        \n        d = '/kaggle/input/rsna-2024-reduce-noise-models/saggital-spinal-x03-y05-t2-reduce-noise-auxloss'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\n        self.box_crop = True\n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 0.5\n        self.xy_center_point = False\n        self.image_size = 384\n        self.batch_size = 1\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        \nclass rsna_saggital_mil_spinal_crop_x03_y07_t2_v4_auxloss(rsna_saggital_spinal_v2):\n    def __init__(self):\n        super().__init__()\n        self.df = pd.read_csv('sagittal_spinal_range2_rolling5_t2.csv')\n        self.label_features = [\n            'spinal_canal_stenosis_normal',\n            'spinal_canal_stenosis_moderate',\n            'spinal_canal_stenosis_severe',\n            'spinal_canal_stenosis',\n        ]\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0        \n        base_model = timm.create_model(self.model_name, pretrained=False, num_classes=1,\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        self.model = RSNA2ndModel(\n            base_model=base_model,\n            num_classes=len(self.label_features)\n        )\n        \n        self.box_crop = True\n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 0.7\n        self.xy_center_point = False\n                                \n        d = '/kaggle/input/rsna-2024-reduce-noise-models/saggital-spinal-x03-y07-t2-reduce-noise-auxloss'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 384\n        self.batch_size = 1\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        \n\nclass rsna_saggital_mil_crop_lr_nfn_model1_datav2(base):\n    def __init__(self):\n        super().__init__()\n        left = pd.read_csv('sagittal_left_nfn_range2_rolling5.csv')\n        left['left_right'] = 'left'\n        right = pd.read_csv('sagittal_right_nfn_range2_rolling5.csv')\n        right['left_right'] = 'right'\n        self.df = pd.concat([left, right])\n\n        self.label_features = [\n            'neural_foraminal_narrowing_normal',\n            'neural_foraminal_narrowing_moderate',\n            'neural_foraminal_narrowing_severe',\n        ]\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0        \n        base_model = timm.create_model(self.model_name, pretrained=False, num_classes=1,\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        self.model = RSNA2ndModel(\n            base_model=base_model,\n            num_classes=len(self.label_features)\n        )\n        \n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-nfn-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\n        self.image_size = 128\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        self.box_crop = None\n        \nclass rsna_saggital_mil_nfn_crop_x07_y1_v2(rsna_saggital_mil_crop_lr_nfn_model1_datav2):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop = True\n        self.box_crop_x_ratio = 0.7\n        self.box_crop_y_ratio = 1.0\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-nfn-x07-y1-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 160\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\nclass rsna_saggital_mil_nfn_crop_x15_y1_v2(rsna_saggital_mil_crop_lr_nfn_model1_datav2):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop = True\n        self.box_crop_x_ratio = 1.5\n        self.box_crop_y_ratio = 1.0\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-nfn-x15-y1-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 160\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\nclass rsna_saggital_mil_nfn_crop_x03_y1_v2(rsna_saggital_mil_crop_lr_nfn_model1_datav2):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop = True\n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 1.0\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-nfn-x03-y1-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 160\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\nclass rsna_saggital_mil_nfn_crop_x05_y05_v2(rsna_saggital_mil_crop_lr_nfn_model1_datav2):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop = True\n        self.box_crop_x_ratio = 0.5\n        self.box_crop_y_ratio = 0.5\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-nfn-x05-y05-reduce-noise'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 160\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n\n\n\nclass rsna_saggital_mil_ss_crop_x03_y05_96(base):\n    def __init__(self):\n        super().__init__()\n        self.box_crop = True\n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 0.5\n\n        left = pd.read_csv('sagittal_left_ss_range2_rolling5.csv')\n        left['left_right'] = 'left'\n        right = pd.read_csv('sagittal_right_ss_range2_rolling5.csv')\n        right['left_right'] = 'right'\n        self.df = pd.concat([left, right])\n\n        self.label_features = [\n            'subarticular_stenosis_normal',\n            'subarticular_stenosis_moderate',\n            'subarticular_stenosis_severe',\n        ]\n        self.drop_rate = 0.0\n        self.drop_path_rate = 0.0        \n        base_model = timm.create_model(self.model_name, pretrained=False, num_classes=1,\n            drop_rate=self.drop_rate, drop_path_rate=self.drop_path_rate)\n        self.model = RSNA2ndModel(\n            base_model=base_model,\n            num_classes=len(self.label_features)\n        )\n        \n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-ss-x03-y05-reduce-noise-96'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n\n        self.image_size = 96\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        \nclass rsna_saggital_mil_ss_crop_x03_y07_96(rsna_saggital_mil_ss_crop_x03_y05_96):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop = True\n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 0.7\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-ss-x03-y07-reduce-noise-96'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 96\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        \nclass rsna_saggital_mil_ss_crop_x03_y2_96(rsna_saggital_mil_ss_crop_x03_y05_96):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop = True\n        self.box_crop_x_ratio = 0.3\n        self.box_crop_y_ratio = 2.0\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-ss-x03-y2-reduce-noise-96'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 96\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n        \nclass rsna_saggital_mil_ss_crop_x1_y07_96(rsna_saggital_mil_ss_crop_x03_y05_96):\n    def __init__(self):\n        super().__init__()        \n        self.box_crop = True\n        self.box_crop_x_ratio = 1.0\n        self.box_crop_y_ratio = 0.7\n        d = '/kaggle/input/rsna-2024-reduce-noise-models-v2/saggital-ss-x1-y07-reduce-noise-96'\n        if debug:\n            self.model_paths = [f'{d}/last_fold{debug_fold}.ckpt' for fold in range(5)]\n        else:\n            self.model_paths = [f'{d}/last_fold{fold}.ckpt' for fold in range(5)]\n        self.oof_path = f'{d}/oof_fold{debug_fold}.csv'\n        self.image_size = 96\n        self.transforms = A.Compose([\n            A.Resize(self.image_size, self.image_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:36:42.849631Z","iopub.execute_input":"2024-10-17T01:36:42.849891Z","iopub.status.idle":"2024-10-17T01:36:42.912364Z","shell.execute_reply.started":"2024-10-17T01:36:42.849868Z","shell.execute_reply":"2024-10-17T01:36:42.911463Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import copy\nconfig_names = [\n'rsna_saggital_mil_spinal_crop_x03_y05',\n'rsna_saggital_mil_spinal_crop_x03_y07',\n\n'rsna_saggital_mil_ss_crop_x03_y05_96',\n'rsna_saggital_mil_ss_crop_x03_y07_96',\n'rsna_saggital_mil_ss_crop_x03_y2_96',\n'rsna_saggital_mil_ss_crop_x1_y07_96',    \n    \n'rsna_saggital_mil_nfn_crop_x07_y1_v2',\n'rsna_saggital_mil_nfn_crop_x15_y1_v2',\n'rsna_saggital_mil_nfn_crop_x03_y1_v2',\n'rsna_saggital_mil_nfn_crop_x05_y05_v2',      \n]\nfor config_name in config_names:\n    cfg = eval(config_name)()\n    models = []\n    for model_path in cfg.model_paths:\n        state_dict = torch.load(model_path, map_location=torch.device('cpu'))\n        model = copy.deepcopy(cfg.model)\n        model.load_state_dict(state_dict)\n        model.to(DEVICE)\n        model.eval()\n        models.append(model)\n\n\n    ds = RSNA2024DatasetMIL(cfg)\n    loader = DataLoader(ds, batch_size=cfg.batch_size, shuffle=False, drop_last=False, num_workers=4)\n    preds = []\n    for images in tqdm(loader, smoothing=0):\n        images = images.to(DEVICE)\n        batch_preds = []\n        for model in models:\n            batch_preds.append(model(images).detach().cpu().numpy())\n\n        preds += np.mean(batch_preds, axis=0).tolist()\n    pred_cols = [f'pred_{c}' for c in cfg.label_features]\n    cfg.df[pred_cols] = np.array(preds)\n    cfg.df.to_csv(f'{config_name}.csv', index=False)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:36:42.913506Z","iopub.execute_input":"2024-10-17T01:36:42.913789Z","iopub.status.idle":"2024-10-17T01:39:12.831250Z","shell.execute_reply.started":"2024-10-17T01:36:42.913765Z","shell.execute_reply":"2024-10-17T01:39:12.830115Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# aggregate predictions","metadata":{}},{"cell_type":"code","source":"# axial\ndef aggregate_axial_preds(configs, targets):\n    cols = []\n    pred_cols = []\n    for target in targets:\n        for c in ['normal', 'moderate', 'severe']:\n            cols.append(f'pred_{target}_{c}')\n            pred_cols.append(f'pred_{c}')\n                \n    preds = []\n    for c in configs:\n        df = pd.read_csv(f'{c}.csv')\n        preds.append(df[cols].values)\n        \n    df[cols] = np.mean(preds, 0)\n\n    if 'left_right' not in list(df):\n        df['left_right'] = 'center'\n    df = df[df.dis < 5]\n    df = df.groupby(['study_id', 'pred_level', 'left_right'])[cols].mean().reset_index().sort_values(['study_id', 'pred_level', 'left_right'])\n    df.columns = ['study_id', 'pred_level', 'left_right']+pred_cols\n    \n    return df\n\nspinal_configs = [\n    'rsna_axial_spinal_dis3_crop_x05_y6',\n    'rsna_axial_spinal_dis3_crop_x1_y2',\n]\naxial_spinal = aggregate_axial_preds(spinal_configs, ['spinal_canal_stenosis'])\n\nnfn_configs = [\n    'rsna_axial_ss_nfn_x2_y2_center_pad0',\n    'rsna_axial_ss_nfn_x2_y6_center_pad0',\n    'rsna_axial_ss_nfn_x2_y8_center_pad10',\n]\naxial_nfn = aggregate_axial_preds(nfn_configs, ['neural_foraminal_narrowing'])\n\nss_configs = [\n    'rsna_axial_ss_nfn_x2_y2_center_pad0',\n    'rsna_axial_ss_nfn_x2_y6_center_pad0',\n    'rsna_axial_ss_nfn_x2_y8_center_pad10',\n]\naxial_ss = aggregate_axial_preds(ss_configs, ['subarticular_stenosis'])\naxial_spinal\n\n# sagittal\ndef aggregate_sagittal_preds(configs, targets):\n    cols = []\n    pred_cols = []\n    for target in targets:\n        for c in ['normal', 'moderate', 'severe']:\n            cols.append(f'pred_{target}_{c}')\n            pred_cols.append(f'pred_{c}')\n                \n    preds = []\n    for c in configs:\n        df = pd.read_csv(f'{c}.csv')\n        preds.append(df[cols].values)\n        \n    df[cols] = np.mean(preds, 0)\n    if 'left_right' not in list(df):\n        df['left_right'] = 'center'\n    df = df.groupby(['study_id', 'level', 'left_right'])[cols].mean().reset_index().sort_values(['study_id', 'level', 'left_right'])\n    df.columns = ['study_id', 'level', 'left_right']+pred_cols\n    df['pred_level'] = df.level.map({\n        'L1/L2': 1,\n        'L2/L3': 2,\n        'L3/L4': 3,\n        'L4/L5': 4,\n        'L5/S1': 5,\n    })\n    del df['level']\n    return df\n\nspinal_configs = [\n    'rsna_saggital_mil_spinal_crop_x03_y05',\n    'rsna_saggital_mil_spinal_crop_x03_y07',\n]\nsagittal_spinal = aggregate_sagittal_preds(spinal_configs, ['spinal_canal_stenosis'])\n\nnfn_configs = [\n    'rsna_saggital_mil_nfn_crop_x07_y1_v2',\n    'rsna_saggital_mil_nfn_crop_x15_y1_v2',\n    'rsna_saggital_mil_nfn_crop_x03_y1_v2',\n    'rsna_saggital_mil_nfn_crop_x05_y05_v2',  \n]\nsagittal_nfn = aggregate_sagittal_preds(nfn_configs, ['neural_foraminal_narrowing'])\n\nss_configs = [\n    'rsna_saggital_mil_ss_crop_x03_y05_96',\n    'rsna_saggital_mil_ss_crop_x03_y07_96',\n    'rsna_saggital_mil_ss_crop_x03_y2_96',\n    'rsna_saggital_mil_ss_crop_x1_y07_96',\n]\nsagittal_ss = aggregate_sagittal_preds(ss_configs, ['subarticular_stenosis'])\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:12.832981Z","iopub.execute_input":"2024-10-17T01:39:12.833293Z","iopub.status.idle":"2024-10-17T01:39:12.941217Z","shell.execute_reply.started":"2024-10-17T01:39:12.833264Z","shell.execute_reply":"2024-10-17T01:39:12.940280Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"study_ids = []\ntargets = []\nlevels = []\nis_axials = []\nlevel_preds = []\ndistances = []\nlrs = []\ntrues = []\npreds = []\n\nfor (study_id, level), idf in axial_spinal.groupby(['study_id', 'pred_level']):\n    preds.append(idf[['pred_normal', 'pred_moderate', 'pred_severe']].mean(0).values)\n    study_ids.append(study_id)\n    targets.append('spinal')\n    levels.append(level)\n    is_axials.append(1)\n    lrs.append('center')\n\nfor (study_id, level, lr), idf in axial_nfn.groupby(['study_id', 'pred_level', 'left_right']):\n    preds.append(idf[['pred_normal', 'pred_moderate', 'pred_severe']].mean(0).values)\n    study_ids.append(study_id)\n    targets.append('nfn')\n    levels.append(level)\n    is_axials.append(1)\n    lrs.append(lr)\nfor (study_id, level, lr), idf in axial_ss.groupby(['study_id', 'pred_level', 'left_right']):\n    preds.append(idf[['pred_normal', 'pred_moderate', 'pred_severe']].mean(0).values)\n    study_ids.append(study_id)\n    targets.append('ss')\n    levels.append(level)\n    is_axials.append(1)\n    lrs.append(lr)\nfor (study_id, level), idf in sagittal_spinal.groupby(['study_id', 'pred_level']):\n    preds.append(idf[['pred_normal', 'pred_moderate', 'pred_severe']].mean(0).values)\n    study_ids.append(study_id)\n    targets.append('spinal')\n    levels.append(level)\n    is_axials.append(0)\n    lrs.append('center')\nfor (study_id, level, lr), idf in sagittal_nfn.groupby(['study_id', 'pred_level', 'left_right']):\n    preds.append(idf[['pred_normal', 'pred_moderate', 'pred_severe']].mean(0).values)\n    study_ids.append(study_id)\n    targets.append('nfn')\n    levels.append(level)\n    is_axials.append(0)\n    lrs.append(lr)\nfor (study_id, level, lr), idf in sagittal_ss.groupby(['study_id', 'pred_level', 'left_right']):\n    preds.append(idf[['pred_normal', 'pred_moderate', 'pred_severe']].mean(0).values)\n    study_ids.append(study_id)\n    targets.append('ss')\n    levels.append(level)\n    is_axials.append(0)\n    lrs.append(lr)    \noof = pd.DataFrame({\n    'study_id': study_ids,\n    'target': targets,\n    'level': levels,\n    'is_axial': is_axials,\n    'lr': lrs,\n})\noof[['pred_normal', 'pred_moderate', 'pred_severe']] = np.array(preds)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:12.942404Z","iopub.execute_input":"2024-10-17T01:39:12.942744Z","iopub.status.idle":"2024-10-17T01:39:13.009078Z","shell.execute_reply.started":"2024-10-17T01:39:12.942717Z","shell.execute_reply":"2024-10-17T01:39:13.008310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"axial = oof[oof.is_axial==1]\naxial.head(2)\naxial.columns = [\n'study_id',\n'target',\n'level',\n'is_axial',\n'lr',\n'axial_pred_normal',\n'axial_pred_moderate',\n'axial_pred_severe'\n]\ndel axial['is_axial']\n\nsagittal = oof[oof.is_axial==0]\nsagittal.columns = [\n'study_id',\n'target',\n'level',\n'is_axial',\n'lr',\n'sagittal_pred_normal',\n'sagittal_pred_moderate',\n'sagittal_pred_severe']\n\ndel sagittal['is_axial']\n\ndf = axial.merge(sagittal, on=['study_id', 'target', 'level', 'lr'], how='outer')\n\ndf.loc[df.sagittal_pred_normal.isnull(), 'sagittal_pred_normal'] = df.loc[df.sagittal_pred_normal.isnull(), 'axial_pred_normal']\ndf.loc[df.sagittal_pred_moderate.isnull(), 'sagittal_pred_moderate'] = df.loc[df.sagittal_pred_moderate.isnull(), 'axial_pred_moderate']\ndf.loc[df.sagittal_pred_severe.isnull(), 'sagittal_pred_severe'] = df.loc[df.sagittal_pred_severe.isnull(), 'axial_pred_severe']\n\ndf.loc[df.axial_pred_normal.isnull(), 'axial_pred_normal'] = df.loc[df.axial_pred_normal.isnull(), 'sagittal_pred_normal']\ndf.loc[df.axial_pred_moderate.isnull(), 'axial_pred_moderate'] = df.loc[df.axial_pred_moderate.isnull(), 'sagittal_pred_moderate']\ndf.loc[df.axial_pred_severe.isnull(), 'axial_pred_severe'] = df.loc[df.axial_pred_severe.isnull(), 'sagittal_pred_severe']\n\ndf.loc[((df.target=='nfn') & (df.lr == 'left')), 'target'] = 'left_neural_foraminal_narrowing'\ndf.loc[((df.target=='nfn') & (df.lr == 'right')), 'target'] = 'right_neural_foraminal_narrowing'\ndf.loc[((df.target=='ss') & (df.lr == 'left')), 'target'] = 'left_subarticular_stenosis'\ndf.loc[((df.target=='ss') & (df.lr == 'right')), 'target'] = 'right_subarticular_stenosis'\ndf.loc[(df.target=='spinal'), 'target'] = 'spinal_canal_stenosis'\ndf.level = df.level.map({\n    1: 'l1_l2',\n    2: 'l2_l3',\n    3: 'l3_l4',\n    4: 'l4_l5',\n    5: 'l5_s1',\n})\ndf=df.sort_values(['study_id', 'target'])\n","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:13.010215Z","iopub.execute_input":"2024-10-17T01:39:13.010547Z","iopub.status.idle":"2024-10-17T01:39:13.044681Z","shell.execute_reply.started":"2024-10-17T01:39:13.010521Z","shell.execute_reply":"2024-10-17T01:39:13.043759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"m = {\n    'study_id': []\n}\nmeta_cols = []\nfor axial_sagittal in ['sagittal', 'axial']:\n    for target in ['spinal_canal_stenosis', 'left_neural_foraminal_narrowing', 'right_neural_foraminal_narrowing', 'left_subarticular_stenosis', 'right_subarticular_stenosis']:\n        for level in ['l1_l2', 'l2_l3', 'l3_l4', 'l4_l5', 'l5_s1']:\n            for condition in ['normal', 'moderate', 'severe']:\n                m[f'{axial_sagittal}_pred_{target}_{level}_{condition}'] = []\n                meta_cols.append(f'{axial_sagittal}_pred_{target}_{level}_{condition}')\n    \nts  = []\nfor i, idf in df.groupby('study_id'):\n    m['study_id'].append(i)\n\n    for level in ['l1_l2', 'l2_l3', 'l3_l4', 'l4_l5', 'l5_s1']:\n        ldf = idf[idf.level == level]\n        for target in ['spinal_canal_stenosis', 'left_neural_foraminal_narrowing', 'right_neural_foraminal_narrowing', 'left_subarticular_stenosis', 'right_subarticular_stenosis']:\n            tdf = ldf[ldf.target == target]\n            for condition in ['normal', 'moderate', 'severe']:\n                \n                for axial_sagittal in ['sagittal', 'axial']:\n                    if len(tdf) == 0:\n                        m[f'{axial_sagittal}_pred_{target}_{level}_{condition}'].append(np.nan)\n                    else:\n                        assert len(tdf) == 1\n                        m[f'{axial_sagittal}_pred_{target}_{level}_{condition}'].append(tdf[f'{axial_sagittal}_pred_{condition}'].values[0])\ndf = pd.DataFrame(m)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:13.047022Z","iopub.execute_input":"2024-10-17T01:39:13.047320Z","iopub.status.idle":"2024-10-17T01:39:13.084814Z","shell.execute_reply.started":"2024-10-17T01:39:13.047297Z","shell.execute_reply":"2024-10-17T01:39:13.083967Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def normalize_probabilities_to_one_torch(tensor: torch.Tensor) -> torch.Tensor:\n    # Normalize the sum of each row's probabilities to 100%.\n    # 0.75, 0.75 => 0.5, 0.5\n    # 0.1, 0.1 => 0.5, 0.5\n    row_totals = tensor.sum(dim=1, keepdim=True)\n    if (row_totals == 0).any():\n        return tensor\n        raise ValueError('All rows must contain at least one non-zero prediction')\n    normalized_tensor = tensor / row_totals\n    return normalized_tensor\ncols = ['spinal_canal_stenosis_l1_l2_normal', 'spinal_canal_stenosis_l1_l2_moderate', 'spinal_canal_stenosis_l1_l2_severe', 'left_neural_foraminal_narrowing_l1_l2_normal', 'left_neural_foraminal_narrowing_l1_l2_moderate', 'left_neural_foraminal_narrowing_l1_l2_severe', 'right_neural_foraminal_narrowing_l1_l2_normal', 'right_neural_foraminal_narrowing_l1_l2_moderate', 'right_neural_foraminal_narrowing_l1_l2_severe', 'left_subarticular_stenosis_l1_l2_normal', 'left_subarticular_stenosis_l1_l2_moderate', 'left_subarticular_stenosis_l1_l2_severe', 'right_subarticular_stenosis_l1_l2_normal', 'right_subarticular_stenosis_l1_l2_moderate', 'right_subarticular_stenosis_l1_l2_severe', 'spinal_canal_stenosis_l2_l3_normal', 'spinal_canal_stenosis_l2_l3_moderate', 'spinal_canal_stenosis_l2_l3_severe', 'left_neural_foraminal_narrowing_l2_l3_normal', 'left_neural_foraminal_narrowing_l2_l3_moderate', 'left_neural_foraminal_narrowing_l2_l3_severe', 'right_neural_foraminal_narrowing_l2_l3_normal', 'right_neural_foraminal_narrowing_l2_l3_moderate', 'right_neural_foraminal_narrowing_l2_l3_severe', 'left_subarticular_stenosis_l2_l3_normal', 'left_subarticular_stenosis_l2_l3_moderate', 'left_subarticular_stenosis_l2_l3_severe', 'right_subarticular_stenosis_l2_l3_normal', 'right_subarticular_stenosis_l2_l3_moderate', 'right_subarticular_stenosis_l2_l3_severe', 'spinal_canal_stenosis_l3_l4_normal', 'spinal_canal_stenosis_l3_l4_moderate', 'spinal_canal_stenosis_l3_l4_severe', 'left_neural_foraminal_narrowing_l3_l4_normal', 'left_neural_foraminal_narrowing_l3_l4_moderate', 'left_neural_foraminal_narrowing_l3_l4_severe', 'right_neural_foraminal_narrowing_l3_l4_normal', 'right_neural_foraminal_narrowing_l3_l4_moderate', 'right_neural_foraminal_narrowing_l3_l4_severe', 'left_subarticular_stenosis_l3_l4_normal', 'left_subarticular_stenosis_l3_l4_moderate', 'left_subarticular_stenosis_l3_l4_severe', 'right_subarticular_stenosis_l3_l4_normal', 'right_subarticular_stenosis_l3_l4_moderate', 'right_subarticular_stenosis_l3_l4_severe', 'spinal_canal_stenosis_l4_l5_normal', 'spinal_canal_stenosis_l4_l5_moderate', 'spinal_canal_stenosis_l4_l5_severe', 'left_neural_foraminal_narrowing_l4_l5_normal', 'left_neural_foraminal_narrowing_l4_l5_moderate', 'left_neural_foraminal_narrowing_l4_l5_severe', 'right_neural_foraminal_narrowing_l4_l5_normal', 'right_neural_foraminal_narrowing_l4_l5_moderate', 'right_neural_foraminal_narrowing_l4_l5_severe', 'left_subarticular_stenosis_l4_l5_normal', 'left_subarticular_stenosis_l4_l5_moderate', 'left_subarticular_stenosis_l4_l5_severe', 'right_subarticular_stenosis_l4_l5_normal', 'right_subarticular_stenosis_l4_l5_moderate', 'right_subarticular_stenosis_l4_l5_severe', 'spinal_canal_stenosis_l5_s1_normal', 'spinal_canal_stenosis_l5_s1_moderate', 'spinal_canal_stenosis_l5_s1_severe', 'left_neural_foraminal_narrowing_l5_s1_normal', 'left_neural_foraminal_narrowing_l5_s1_moderate', 'left_neural_foraminal_narrowing_l5_s1_severe', 'right_neural_foraminal_narrowing_l5_s1_normal', 'right_neural_foraminal_narrowing_l5_s1_moderate', 'right_neural_foraminal_narrowing_l5_s1_severe', 'left_subarticular_stenosis_l5_s1_normal', 'left_subarticular_stenosis_l5_s1_moderate', 'left_subarticular_stenosis_l5_s1_severe', 'right_subarticular_stenosis_l5_s1_normal', 'right_subarticular_stenosis_l5_s1_moderate', 'right_subarticular_stenosis_l5_s1_severe']\nlabel_features = ['pred_'+c for c in cols]\n\nws = [0.7, 0.4, 0.8]\nfor condition, w in zip(['spinal', 'neural_foraminal_narrowing', 'subarticular_stenosis'], ws):    \n    c_cols = [c for c in label_features if condition in c]\n    for c in c_cols:\n        df[c] = df['axial_'+c]*w + df['sagittal_'+c]*(1-w)\n\nfor c in label_features:\n    df[c] = df[c].fillna(df[c].mean())\n\npreds = sigmoid(df[label_features].values)\npreds = torch.FloatTensor(preds)\nfor i in range(5):\n    preds[:, i*3+1] *= 1.8\n    preds[:, i*3+2] *= 5\n    preds[:, i*3:(i+1)*3] = normalize_probabilities_to_one_torch(preds[:, i*3:(i+1)*3])\nfor i in range(5, 15):\n    preds[:, i*3+1] *= 2.2\n    preds[:, i*3+2] *= 5\n    preds[:, i*3:(i+1)*3] = normalize_probabilities_to_one_torch(preds[:, i*3:(i+1)*3])\nfor i in range(15, 25):\n    preds[:, i*3+1] *= 2.2\n    preds[:, i*3+2] *= 5.5\n    preds[:, i*3:(i+1)*3] = normalize_probabilities_to_one_torch(preds[:, i*3:(i+1)*3])\ndf[label_features] = preds.numpy()\ndf = df.sort_values('study_id')\ndf.head()\n\ndf[['study_id']+label_features]\nmy_sub = df.copy()\n\nrow_ids = []\npreds = []\nfor i, row in my_sub.iterrows():\n    for c in [c for c in cols if 'normal' in c]:\n        c = c.replace('_normal', '')\n        row_ids.append(f'{int(row.study_id)}_{c}')\n        preds.append([\n            row[f'pred_{c}_normal'],\n            row[f'pred_{c}_moderate'],\n            row[f'pred_{c}_severe'],\n        ])\npred_df = pd.DataFrame({'row_id': row_ids})\npred_df[['normal_mild','moderate','severe']] = np.array(preds)\nmy_sub = pred_df.sort_values('row_id')\nmy_sub.to_csv('my_sub.csv', index=False)\nsub = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/sample_submission.csv').sort_values('row_id')\nsub = sub[~sub.row_id.isin(my_sub.row_id)]\nsub['normal_mild'] = my_sub['normal_mild'].mean()\nsub['moderate'] = my_sub['moderate'].mean()\nsub['severe'] = my_sub['severe'].mean()\nsub = pd.concat([sub, my_sub])\n\nif not debug:\n    os.system('rm -rf ./*')\n    os.system('rm -rf /kaggle/temp')\n\nsub.to_csv('yuji_submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:13.085985Z","iopub.execute_input":"2024-10-17T01:39:13.086334Z","iopub.status.idle":"2024-10-17T01:39:13.235642Z","shell.execute_reply.started":"2024-10-17T01:39:13.086302Z","shell.execute_reply":"2024-10-17T01:39:13.234757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !unlink /kaggle/working/timm","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:13.236946Z","iopub.execute_input":"2024-10-17T01:39:13.237562Z","iopub.status.idle":"2024-10-17T01:39:13.242166Z","shell.execute_reply.started":"2024-10-17T01:39:13.237528Z","shell.execute_reply":"2024-10-17T01:39:13.240658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport gc\ndef free_gpu_and_ram():\n    gc.collect()\n    torch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:13.243487Z","iopub.execute_input":"2024-10-17T01:39:13.243813Z","iopub.status.idle":"2024-10-17T01:39:13.254077Z","shell.execute_reply.started":"2024-10-17T01:39:13.243784Z","shell.execute_reply":"2024-10-17T01:39:13.253122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"free_gpu_and_ram()","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:13.255263Z","iopub.execute_input":"2024-10-17T01:39:13.255967Z","iopub.status.idle":"2024-10-17T01:39:13.699027Z","shell.execute_reply.started":"2024-10-17T01:39:13.255941Z","shell.execute_reply":"2024-10-17T01:39:13.698213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!nvidia-smi","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:13.700551Z","iopub.execute_input":"2024-10-17T01:39:13.700832Z","iopub.status.idle":"2024-10-17T01:39:14.826582Z","shell.execute_reply.started":"2024-10-17T01:39:13.700801Z","shell.execute_reply":"2024-10-17T01:39:14.825396Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nimport os\nsys.path.append('/kaggle/input/random-data/')\n\nos.environ['CUDA_VISIBLE_DEVICES'] = '0'\nos.environ['KAGGLE_IS_COMPETITION_RERUN'] = 'true'\n\n# Dir to store imgs\nfor path in [\"/tmp/images\"]:\n    if not os.path.exists(path):\n        os.mkdir(path)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:14.835872Z","iopub.execute_input":"2024-10-17T01:39:14.836190Z","iopub.status.idle":"2024-10-17T01:39:14.842758Z","shell.execute_reply.started":"2024-10-17T01:39:14.836163Z","shell.execute_reply":"2024-10-17T01:39:14.841808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport shutil\n\ndef clean_working(directory_path: str = \"/kaggle/working/\"):\n    \"\"\"\n    Clean kaggle output directory.\n    \"\"\"\n    if os.path.exists(directory_path):\n        for item in os.listdir(directory_path):\n            if \"submission\" in item:\n                continue\n            item_path = os.path.join(directory_path, item)\n            try:\n                if os.path.islink(item_path):\n                    os.unlink(item_path)\n                elif os.path.isfile(item_path):\n                    os.remove(item_path)\n                elif os.path.isdir(item_path):\n                    shutil.rmtree(item_path)\n            except Exception as e:\n                pass\n        print(f\"All items in '{directory_path}' have been removed.\")\n    else:\n        print(f\"'{directory_path}' does not exist.\")\n                \n        \nclean_working()","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:14.843986Z","iopub.execute_input":"2024-10-17T01:39:14.844254Z","iopub.status.idle":"2024-10-17T01:39:14.857674Z","shell.execute_reply.started":"2024-10-17T01:39:14.844222Z","shell.execute_reply":"2024-10-17T01:39:14.856809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Notes\n\n### TODO\n\n- Make sure study_id's with no SagT2 are still predicted..","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport os\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    FOLD= 0\n    df= pd.read_csv(\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_series_descriptions.csv\")\n    df_all= df.copy()\nelse:\n    FOLD= 0\n    N_SAMPLES= 100\n    cols= ['study_id', 'series_id', 'series_description'] \n    df= pd.read_csv(\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_series_descriptions.csv\")\n    md= pd.read_csv(\"/kaggle/input/rsna-data/metadata.csv\")\n    if FOLD != -1:\n        md= md.loc[md.fold == FOLD, :].head(N_SAMPLES)\n    df= pd.merge(df[cols], md[cols], how=\"inner\", on=cols).reset_index(drop=True)\n    df_all= df.copy()\n\ndf= df[df.series_description != \"Axial T2\"]\ndisplay(df)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:14.858729Z","iopub.execute_input":"2024-10-17T01:39:14.859007Z","iopub.status.idle":"2024-10-17T01:39:14.878918Z","shell.execute_reply.started":"2024-10-17T01:39:14.858987Z","shell.execute_reply":"2024-10-17T01:39:14.878110Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Log Models","metadata":{}},{"cell_type":"code","source":"import glob\nimport json\n\nMODEL_DIR= \"/kaggle/input/rsna-models\"\nMODELS= {\n    \"S2\": {\n#         \"stage1\": glob.glob(\"{}/cfg_stage1_h*_{}_*\".format(MODEL_DIR, FOLD)),\n#         \"stage2\": glob.glob(\"{}/cfg_stage2_h*_{}_*\".format(MODEL_DIR, FOLD)),\n        \"stage1\": glob.glob(\"{}/cfg_stage1_h*\".format(MODEL_DIR)),\n        \"stage2\": glob.glob(\"{}/cfg_stage2_h*_-100_*\".format(MODEL_DIR)),\n    },\n    \"S1\": {\n#         \"stage1\": glob.glob(\"{}/cfg_stage1_s1_*_{}_*\".format(MODEL_DIR, FOLD)),\n#         \"stage2\": glob.glob(\"{}/cfg_stage2_s1_*_{}_*\".format(MODEL_DIR, FOLD)),\n        \"stage1\": glob.glob(\"{}/cfg_stage1_s1_*\".format(MODEL_DIR)),\n        \"stage2\": glob.glob(\"{}/cfg_stage2_s1_*_-100_*\".format(MODEL_DIR)),\n    },\n}\nprint(json.dumps(MODELS, indent=4))","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:14.879968Z","iopub.execute_input":"2024-10-17T01:39:14.880235Z","iopub.status.idle":"2024-10-17T01:39:14.903037Z","shell.execute_reply.started":"2024-10-17T01:39:14.880213Z","shell.execute_reply":"2024-10-17T01:39:14.902221Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Imports","metadata":{}},{"cell_type":"code","source":"from src.preprocess.rsna import RsnaProcessor\n\nfrom src.configs.cfg_stage1_s1 import cfg as cfg_stage1_s1\nfrom src.configs.cfg_stage2_s1 import cfg as cfg_stage2_s1\nfrom src.configs.cfg_stage1 import cfg as cfg_stage1_s2\nfrom src.configs.cfg_stage2 import cfg as cfg_stage2_s2\n\nfrom src.data.dataset_stage1 import Stage1Dataset\nfrom src.data.dataset_stage2 import Stage2Dataset\n\nfrom src.data.utils import get_val_dataloader\nfrom src.models.mil import (\n    MILModel, MILOneHead, MILThreeHeads,\n)\n\nfrom src.modules.train_stage1 import run_eval as run_eval_stage1\nfrom src.modules.train_stage2 import run_eval as run_eval_stage2\n\n\nfrom src.modules.infer import (\n    stage1_s1_preds,\n    stage1_s2_preds,\n    stage2_s1_preds,\n    stage2_s2_preds,\n)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:14.904032Z","iopub.execute_input":"2024-10-17T01:39:14.904297Z","iopub.status.idle":"2024-10-17T01:39:15.150083Z","shell.execute_reply.started":"2024-10-17T01:39:14.904274Z","shell.execute_reply":"2024-10-17T01:39:15.149308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Stage 0","metadata":{}},{"cell_type":"code","source":"# Process slides\nz= df.groupby('study_id')[['series_id', 'series_description']].apply(lambda x: x.to_dict('records')).reset_index().copy()\np= RsnaProcessor(\n    df= z,\n    coords= pd.DataFrame([]),\n    stage = 1,\n    img_size_stage1= 256,\n    img_size_stage2= 128,\n    percentiles= (1,99),\n    in_dir=\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification\", \n    out_dir=\"/tmp/images/stage1_128/\",\n    mode=\"test\" if os.getenv('KAGGLE_IS_COMPETITION_RERUN') else \"train\",\n    )\np.run()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-10-17T01:39:15.151307Z","iopub.execute_input":"2024-10-17T01:39:15.151922Z","iopub.status.idle":"2024-10-17T01:39:18.168960Z","shell.execute_reply.started":"2024-10-17T01:39:15.151887Z","shell.execute_reply":"2024-10-17T01:39:18.167852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Stage 1","metadata":{}},{"cell_type":"code","source":"# S1\nstage1_s1_preds(\n    df= df,\n    cfg= cfg_stage1_s1,\n    wpaths= MODELS[\"S1\"][\"stage1\"],\n    img_dir= \"/tmp/images/stage1_128/\",\n)\n\n# S2\nstage1_s2_preds(\n    df= df,\n    cfg= cfg_stage1_s2,\n    wpaths= MODELS[\"S2\"][\"stage1\"],\n    img_dir= \"/tmp/images/stage1_128/\",\n)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:18.170655Z","iopub.execute_input":"2024-10-17T01:39:18.170975Z","iopub.status.idle":"2024-10-17T01:39:44.857387Z","shell.execute_reply.started":"2024-10-17T01:39:18.170947Z","shell.execute_reply":"2024-10-17T01:39:44.856298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Stage 2","metadata":{}},{"cell_type":"code","source":"fpaths= glob.glob(\"stage1*csv\")\ncoords= pd.concat([pd.read_csv(_) for _ in fpaths], axis=0)\ncoords= coords.groupby(['series_id', 'level', 'side'])[['relative_x', 'relative_y']].mean().reset_index()\ncoords= coords.sort_values([\"series_id\", \"level\", \"side\"]).reset_index(drop=True)\ncoords= coords[[\"series_id\", \"level\", \"side\", \"relative_x\", \"relative_y\"]]\nclean_working()\nprint(coords.shape)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:44.859316Z","iopub.execute_input":"2024-10-17T01:39:44.859609Z","iopub.status.idle":"2024-10-17T01:39:44.885286Z","shell.execute_reply.started":"2024-10-17T01:39:44.859581Z","shell.execute_reply":"2024-10-17T01:39:44.884447Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"p= RsnaProcessor(\n    df= z,\n    coords= coords,\n    stage = 2,\n    img_size_stage1= 256,\n    img_size_stage2= 128,\n    percentiles= (1,99),\n    in_dir=\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification\",  \n    out_dir= \"/tmp/images/stage2_128/\",\n    mode= \"test\" if os.getenv('KAGGLE_IS_COMPETITION_RERUN') else \"train\",\n    )\np.run()","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:44.886376Z","iopub.execute_input":"2024-10-17T01:39:44.886690Z","iopub.status.idle":"2024-10-17T01:39:49.285449Z","shell.execute_reply.started":"2024-10-17T01:39:44.886667Z","shell.execute_reply":"2024-10-17T01:39:49.284281Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# S1\nstage2_s1_preds(\n    df= df,\n    cfg= cfg_stage2_s1,\n    wpaths= MODELS[\"S1\"][\"stage2\"],\n    img_dir= \"/tmp/images/stage2_128/\",\n    n_tta= 9,\n)\n\n# S2\nstage2_s2_preds(\n    df= df,\n    cfg= cfg_stage2_s2,\n    wpaths= MODELS[\"S2\"][\"stage2\"],\n    img_dir= \"/tmp/images/stage2_128/\",\n    n_tta= 9,\n)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:39:49.287372Z","iopub.execute_input":"2024-10-17T01:39:49.288255Z","iopub.status.idle":"2024-10-17T01:41:20.399449Z","shell.execute_reply.started":"2024-10-17T01:39:49.288215Z","shell.execute_reply":"2024-10-17T01:41:20.398333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Combine","metadata":{}},{"cell_type":"code","source":"# Merge DFs\nfpaths= glob.glob(\"/kaggle/working/stage2*.csv\")\nsub= pd.concat([pd.read_csv(os.path.join(\"/kaggle/working/\", x)) for x in fpaths], axis=0)\nsub= sub.groupby(\"row_id\").mean().reset_index()\nsub","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:41:20.401771Z","iopub.execute_input":"2024-10-17T01:41:20.402626Z","iopub.status.idle":"2024-10-17T01:41:20.444862Z","shell.execute_reply.started":"2024-10-17T01:41:20.402584Z","shell.execute_reply":"2024-10-17T01:41:20.443991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfrom copy import deepcopy\n\ndef insert_missing_rows(sub: pd.DataFrame):\n    if os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n        df_all= pd.read_csv(\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_series_descriptions.csv\")\n    else:\n        return sub\n\n    label_cols= [\n                'spinal_canal_stenosis_l1_l2', 'spinal_canal_stenosis_l2_l3', 'spinal_canal_stenosis_l3_l4', \n                'spinal_canal_stenosis_l4_l5', 'spinal_canal_stenosis_l5_s1', 'left_neural_foraminal_narrowing_l1_l2', \n                'left_neural_foraminal_narrowing_l2_l3', 'left_neural_foraminal_narrowing_l3_l4', 'left_neural_foraminal_narrowing_l4_l5', \n                'left_neural_foraminal_narrowing_l5_s1', 'right_neural_foraminal_narrowing_l1_l2', 'right_neural_foraminal_narrowing_l2_l3', \n                'right_neural_foraminal_narrowing_l3_l4', 'right_neural_foraminal_narrowing_l4_l5', 'right_neural_foraminal_narrowing_l5_s1', \n                'left_subarticular_stenosis_l1_l2', 'left_subarticular_stenosis_l2_l3', 'left_subarticular_stenosis_l3_l4', \n                'left_subarticular_stenosis_l4_l5', 'left_subarticular_stenosis_l5_s1', 'right_subarticular_stenosis_l1_l2', \n                'right_subarticular_stenosis_l2_l3', 'right_subarticular_stenosis_l3_l4', 'right_subarticular_stenosis_l4_l5', \n                'right_subarticular_stenosis_l5_s1',\n            ]\n    \n    # Create dummy row\n    row= deepcopy(sub.iloc[0])\n    row.normal_mild= 1/3\n    row.moderate= 1/3\n    row.severe= 1/3\n    \n    # Check every row exists\n    z= sub[\"row_id\"].values\n    arr= []\n    for val in df_all[\"study_id\"].unique():\n        for col in label_cols:\n            value= \"{}_{}\".format(val, col)\n            if value not in z:\n                row= deepcopy(row)\n                row.row_id= value\n                arr.append(row)\n    \n    # Add new rows\n    sub= pd.concat([sub, pd.DataFrame(arr)], axis=0, ignore_index=True)\n    sub= sub.sort_values(\"row_id\").reset_index(drop=True)\n    return sub\n            \n# sub= insert_missing_rows(sub)\nsub.to_csv('submission1.csv', index=False)\ndisplay(sub)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:41:20.446255Z","iopub.execute_input":"2024-10-17T01:41:20.446531Z","iopub.status.idle":"2024-10-17T01:41:20.466856Z","shell.execute_reply.started":"2024-10-17T01:41:20.446508Z","shell.execute_reply":"2024-10-17T01:41:20.465850Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"free_gpu_and_ram()","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:41:20.467933Z","iopub.execute_input":"2024-10-17T01:41:20.468252Z","iopub.status.idle":"2024-10-17T01:41:20.725694Z","shell.execute_reply.started":"2024-10-17T01:41:20.468223Z","shell.execute_reply":"2024-10-17T01:41:20.724782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    clean_working(\"/tmp/images/\")\n    clean_working(\"/kaggle/working/\")\n    pass\nelse:\n    \n    # Remove rows that have no labels\n    sol= pd.read_csv(\"/kaggle/input/rsna-data/study_id_labels.csv\")\n    sol= pd.merge(sol, sub[\"row_id\"], how=\"inner\", on=\"row_id\")\n    sub= pd.merge(sub, sol[\"row_id\"], how=\"inner\", on=\"row_id\")\n\n    sol= sol.sort_values(\"row_id\").reset_index(drop=True)\n    sub= sub.sort_values(\"row_id\").reset_index(drop=True)\n\n    weights= np.array([1.0, 2.0, 4.0])\n    sol[\"sample_weight\"]= weights[np.argmax(sol[['normal_mild', 'moderate', 'severe']], axis=1)]\n    \n    from src.metrics.metric import score\n    import json\n    d= score(solution= sol.copy(), submission= sub.copy(), row_id_column_name=\"row_id\", any_severe_scalar=1.0)\n    print(json.dumps(d, indent=4))","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:41:20.726920Z","iopub.execute_input":"2024-10-17T01:41:20.727276Z","iopub.status.idle":"2024-10-17T01:41:20.741107Z","shell.execute_reply.started":"2024-10-17T01:41:20.727246Z","shell.execute_reply":"2024-10-17T01:41:20.740179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Ian's Part","metadata":{}},{"cell_type":"markdown","source":"## Install Packages","metadata":{}},{"cell_type":"code","source":"!pip install -qq /kaggle/working/python-packages/timm-0.9.10-py3-none-any.whl\nimport timm\ntimm.__version__","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:41:20.742119Z","iopub.execute_input":"2024-10-17T01:41:20.742384Z","iopub.status.idle":"2024-10-17T01:41:42.792749Z","shell.execute_reply.started":"2024-10-17T01:41:20.742362Z","shell.execute_reply":"2024-10-17T01:41:42.791652Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cp -r /kaggle/input/python-packages /kaggle/working\n!pip install -qq /kaggle/working/python-packages/pylibjpeg_libjpeg-1.3.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install -qq /kaggle/working/python-packages/pylibjpeg_openjpeg-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install -qq /kaggle/working/python-packages/pylibjpeg_rle-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install -qq /kaggle/working/python-packages/portalocker-2.8.2-py3-none-any.whl\n!pip install -qq /kaggle/working/python-packages/iopath-0.1.9-py3-none-any.whl\n!pip install -qq /kaggle/working/python-packages/av-10.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install -qq /kaggle/working/python-packages/yacs-0.1.8-py3-none-any.whl\n!pip install -qq /kaggle/working/python-packages/fvcore-0.1.5.post20220512/\n!pip install -qq /kaggle/working/python-packages/parameterized-0.8.1-py2.py3-none-any.whl\n!pip install -qq /kaggle/working/python-packages/pytorchvideo-0.1.5/\n# !pip install -qq /kaggle/working/python-packages/timm-0.9.10-py3-none-any.whl\n!pip install -qq /kaggle/working/python-packages/antlr4-python3-runtime-4.9.3/\n!pip install -qq /kaggle/working/python-packages/monai-1.3.0-202310121228-py3-none-any.whl\n\n!cp /kaggle/input/gdcm-conda-install/gdcm.tar /kaggle/working/\n!tar -xzvf gdcm.tar\n!conda install --offline /kaggle/working/gdcm/gdcm-2.8.9-py37h71b2a6d_0.tar.bz2","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:41:42.794508Z","iopub.execute_input":"2024-10-17T01:41:42.794841Z","iopub.status.idle":"2024-10-17T01:48:46.365704Z","shell.execute_reply.started":"2024-10-17T01:41:42.794812Z","shell.execute_reply":"2024-10-17T01:48:46.364530Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Imports","metadata":{}},{"cell_type":"code","source":"import albumentations as A\nimport glob\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport pandas as pd\nimport pydicom\nimport sys\nimport torch\n\nfrom collections import defaultdict\nfrom importlib import import_module\nfrom sklearn.metrics import log_loss\nfrom tqdm import tqdm\n\nsys.path.insert(0, \"/kaggle/input/rsna-lspine-src/skp\")","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:48:46.367629Z","iopub.execute_input":"2024-10-17T01:48:46.368392Z","iopub.status.idle":"2024-10-17T01:48:46.374606Z","shell.execute_reply.started":"2024-10-17T01:48:46.368352Z","shell.execute_reply":"2024-10-17T01:48:46.373698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Functions","metadata":{}},{"cell_type":"code","source":"def load_model(checkpoint_path, cfg):\n    cfg.pretrained = False\n    print(f\"Loading weights from {checkpoint_path} ...\")\n    wts = torch.load(checkpoint_path)[\"state_dict\"]\n    wts = {k.replace(\"model.\", \"\"): v for k, v in wts.items()}\n    model = import_module(f\"models.{cfg.model}\").Net(cfg)\n    model.load_state_dict(wts)\n    model = model.eval().cuda()\n    return model\n\n\ndef convert_to_8bit(x):\n    lower, upper = np.percentile(x, (1, 99))\n    x = np.clip(x, lower, upper)\n    x = x - np.min(x)\n    x = x / np.max(x) \n    return (x * 255).astype(\"uint8\")\n\n\ndef convert_world_space_to_pixel_space(dicom, x, y, z):\n    sx, sy, sz = [float(_) for _ in dicom.ImagePositionPatient]\n    o0, o1, o2, o3, o4, o5, = [float(_) for _ in dicom.ImageOrientationPatient]\n    dx, dy = dicom.PixelSpacing\n    dz = dicom.SpacingBetweenSlices\n    #\n    ax = np.array([o0, o1, o2])\n    ay = np.array([o3, o4, o5])\n    az = np.cross(ax, ay)\n    #\n    p = np.array([x-sx, y-sy, z-sz])\n    px = np.dot(ax, p) / dx\n    py = np.dot(ay, p) / dy\n    pz = np.dot(az, p) / dz\n    #\n    return px, py, pz\n\n\ndef convert_pixel_space_to_world_space(dicom, x, y):\n    sx, sy, sz = [float(_) for _ in dicom.ImagePositionPatient]\n    o0, o1, o2, o3, o4, o5 = [float(_) for _ in dicom.ImageOrientationPatient]\n    dx, dy = dicom.PixelSpacing\n    wx = o0*dx*x + o3*dy*y + sx\n    wy = o1*dx*x + o4*dy*y + sy\n    wz = o2*dx*x + o5*dy*y + sz\n    return wx, wy, wz\n\n\ndef get_axial_pz(sagittal_t2_coords, axial_t2_dicoms):\n    sag_world_coords = {}\n    for lvl in [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]:\n        sag_world_coords[lvl] = convert_pixel_space_to_world_space(sagittal_t2_coords[f\"{lvl}_dicom\"], \n                                                                   sagittal_t2_coords[f\"{lvl}_x\"],\n                                                                   sagittal_t2_coords[f\"{lvl}_y\"])\n    axial_pz_list = []\n    for each_dicom in axial_t2_dicoms:\n        pz_list = []\n        for lvl, (wx, wy, wz) in sag_world_coords.items():\n            px, py, pz = convert_world_space_to_pixel_space(each_dicom, wx, wy, wz)\n            pz_list.append(pz)\n        axial_pz_list.append(np.asarray(pz_list))\n    return np.stack(axial_pz_list)\n        \n            \ndef load_dicom_stack(dicom_folder, plane, reverse_sort=False, sort_by_instance=False, sagittal_t2_coords=None):\n    dicom_files = glob.glob(os.path.join(dicom_folder, \"*.dcm\"))\n    dicoms = [pydicom.dcmread(f) for f in dicom_files]\n    # There was one axial T2 study where orientation was coronal but when I checked the images they were axial\n    # So we should probably just trust the series description rather than determine orientation ourselves\n    plane = {\"sagittal\": 0, \"coronal\": 1, \"axial\": 2}[plane.lower()]\n    instances = np.asarray([int(d.InstanceNumber) for d in dicoms])\n    positions = np.asarray([float(d.ImagePositionPatient[plane]) for d in dicoms])\n    idx = np.argsort(positions)\n    if sort_by_instance:\n        # Sometimes for axial images sorting by instance is better than sorting by ImagePositionPatient2\n        # Due to oblique slice acquisitions\n        # Determine if instances are in reverse order from positions\n        # Because we will still be sorting from CRANIAL->CAUDAL\n        ascending = instances[idx[-1]] > instances[idx[0]]\n        idx = np.argsort(instances)\n        if not ascending:\n            idx = idx[::-1]\n    # If reverse_sort=True, then increasing array index will be from LEFT->RIGHT and CRANIAL->CAUDAL\n    if reverse_sort:\n        idx = idx[::-1]\n    array_shapes = np.vstack([d.pixel_array.shape for d in dicoms])\n    h, w = array_shapes[:, 0].max(), array_shapes[:, 1].max()\n    # For some axial T2 series, the arrays are not all the same shape- resize if necessary\n    resizer = A.Resize(height=h, width=w, p=1)\n    array = [resizer(image=d.pixel_array.astype(\"float32\"))[\"image\"] for d in dicoms]\n    array = np.stack(array)\n    array = array[idx]\n    sorted_dicom_files = [dicom_files[ii] for ii in idx]\n    if isinstance(sagittal_t2_coords, dict):\n        try:\n            pz = get_axial_pz(sagittal_t2_coords, [dicoms[ii] for ii in idx])\n        except Exception as e:\n            print(e)\n            pz = None\n        return convert_to_8bit(array), sorted_dicom_files, pz\n    return convert_to_8bit(array), sorted_dicom_files, None\n\n\ndef convert_probas_to_foramina_slice_indices(logits, arr_shape):\n    # targets of interest are 0-9 for probas, 10-19 for dist\n    assert logits.shape[1] == 20\n    indices = np.argmax(logits[:, :10], 0)\n    levels = [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]\n    levels = [f\"rt_{lvl}\" for lvl in levels] + [f\"lt_{lvl}\" for lvl in levels]\n    d = {}\n    for idx, lvl in enumerate(levels):\n        d[f\"{lvl}_slice_index\"] = indices[idx]\n    return d\n\n\ndef convert_dists_to_foramina_slice_indices(logits, arr_shape):\n    # targets of interest are 0-9 for probas, 10-19 for dist\n    assert logits.shape[1] == 20\n    indices = np.argmin(logits[:, 10:], 0)\n    levels = [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]\n    levels = [f\"rt_{lvl}\" for lvl in levels] + [f\"lt_{lvl}\" for lvl in levels]\n    d = {}\n    for idx, lvl in enumerate(levels):\n        d[f\"{lvl}_slice_index\"] = indices[idx]\n    return d\n\n\ndef convert_dists_to_spinal_slice_indices(logits, arr_shape):\n    # targets of interest are 0-4 for probas, 5-9 for dist\n    assert logits.shape[1] == 10\n    indices = np.argmin(logits[:, 5:], 0)\n    levels = [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]\n    d = {}\n    for idx, lvl in enumerate(levels):\n        d[f\"{lvl}_slice_index\"] = indices[idx]\n    return d\n    \n\ndef convert_probas_to_subarticular_slice_indices(logits, arr_shape, thresh=0.2):\n    # NOTE: SIGMOID ALREADY APPLIED\n    # logits.shape = (num_images, 26)\n    # targets of interest are 6-10, 11-15 for rt and lt subarticular for each level\n    assert logits.shape[1] == 26\n    rt_prob = logits[:, 6:11]\n    lt_prob = logits[:, 11:16]\n    rt_indices, rt_max_prob = np.argmax(rt_prob, 0), rt_prob.max(0)\n    lt_indices, lt_max_prob = np.argmax(lt_prob, 0), lt_prob.max(0)\n    levels = [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]\n    d = {}\n    for idx, lvl in enumerate(levels):\n        if rt_max_prob[idx] >= thresh:\n            d[f\"rt_{lvl}_slice_index\"] = rt_indices[idx]\n        if lt_max_prob[idx] >= thresh:\n            d[f\"lt_{lvl}_slice_index\"] = lt_indices[idx]\n    return d\n\n\ndef crop_square_around_center(img, xc, yc, size_factor=0.15, crop_3d=False):\n    img_shape = (img.shape[1], img.shape[2]) if crop_3d else (img.shape[0], img.shape[1])\n    h, w = size_factor * img_shape[0], size_factor * img_shape[1]\n    x1, y1 = xc - w / 2, yc - h / 2\n    x2, y2 = x1 + w, y1 + h\n    x1, y1, x2, y2 = [int(_) for _ in [x1, y1, x2, y2]]\n    x1, y1 = max(0, x1), max(0, y1)\n    x2, y2 = min(img_shape[1], x2), min(img_shape[0], y2)\n    return img[:, y1:y2, x1:x2] if crop_3d else img[y1:y2, x1:x2]\n\n\ndef generate_multiaug_crops(arr, slice_index, x, y, size_factor, side, lvl, offset=0.0175, slice_stagger=1):\n    if slice_stagger == 1:\n        # Use 1 slice on either side\n        slice_indices = [slice_index, \n                         max(0, slice_index - 1), \n                         min(len(arr) - 1, slice_index + 1)]\n    elif slice_stagger == 2:\n        # Use 2 slices on either side\n        slice_indices = [slice_index, \n                         max(0, slice_index - 1), \n                         min(len(arr) - 1, slice_index + 1), \n                         max(0, slice_index - 2), \n                         min(len(arr) - 1, slice_index + 2)]\n    else:\n        raise Exception(\"`slice_stagger` must be 1 or 2\")\n    offset_x, offset_y = int(offset * arr.shape[2]), int(offset * arr.shape[1])\n    xs = [x, x - offset_x, x + offset_x]\n    ys = [y, y - offset_y, y + offset_y]\n    crop_dict = defaultdict(list)\n    for each_slice_index in slice_indices:\n        for xi in xs:\n            for yi in ys:\n                crop_dict[f\"{side}{lvl}\"].append(crop_square_around_center(arr[each_slice_index], xi, yi, size_factor=size_factor))\n    return crop_dict\n\n\ndef crop_using_coords_dict(arr, coords_dict, bilat=True, size_factor=0.15, offset=0.0175, slice_stagger=1):\n    sides = [\"rt_\", \"lt_\"] if bilat else [\"\"]\n    crop_dict = {}\n    for lvl in [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]:\n        for side in sides:\n            try:\n                slice_index = coords_dict[f\"{side}{lvl}_slice_index\"]\n                x = coords_dict[f\"{side}{lvl}_x\"]\n                y = coords_dict[f\"{side}{lvl}_y\"]\n                crop_dict.update(generate_multiaug_crops(arr, slice_index, x, y, size_factor=size_factor, side=side, lvl=lvl, offset=offset, slice_stagger=slice_stagger))\n            except KeyError:\n                # If a level does not exist (mainly for axial series), just skip\n                continue\n    return crop_dict\n\n\ndef generate_multiaug_crops_all_slices(arr, x, y, size_factor, side, lvl, offset=0.0175):\n    offset_x, offset_y = int(offset * arr.shape[2]), int(offset * arr.shape[1])\n    coords = [\n              (x, y), \n              (x - offset_x, y - offset_y), \n              (x + offset_x, y + offset_y), \n              (x - offset_x, y + offset_y), \n              (x + offset_x, y - offset_y)\n             ]\n    crop_dict = defaultdict(list)\n    for xi, yi in coords:\n        tmp_crops = crop_square_around_center(arr, xi, yi, size_factor=size_factor, crop_3d=True)\n        for tc in tmp_crops:\n            crop_dict[f\"{side}{lvl}\"].append(tc)\n    return crop_dict\n\n\ndef crop_using_coords_dict_all_slices(arr, coords_dict, bilat=True, size_factor=0.15, offset=0.0175):\n    sides = [\"rt_\", \"lt_\"] if bilat else [\"\"]\n    crop_dict = {}\n    for lvl in [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]:\n        for side in sides:\n            try:\n                x = coords_dict[f\"{side}{lvl}_x\"]\n                y = coords_dict[f\"{side}{lvl}_y\"]\n                crop_dict.update(generate_multiaug_crops_all_slices(arr, x, y, size_factor=size_factor, side=side, lvl=lvl, offset=offset))\n            except KeyError:\n                continue\n    return crop_dict\n\n\ndef normalize(x):\n    return x / np.expand_dims(x.sum(1), axis=-1)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:48:46.376180Z","iopub.execute_input":"2024-10-17T01:48:46.376510Z","iopub.status.idle":"2024-10-17T01:48:46.429925Z","shell.execute_reply.started":"2024-10-17T01:48:46.376486Z","shell.execute_reply":"2024-10-17T01:48:46.429029Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Specify What To Run","metadata":{}},{"cell_type":"code","source":"SAG_T1_FORAMINAL = True\nSAG_T2_SPINAL = True\nAX_T2_SUBARTICULAR = True\nAX_T2_SPINAL = True","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:48:46.431017Z","iopub.execute_input":"2024-10-17T01:48:46.431292Z","iopub.status.idle":"2024-10-17T01:48:46.443211Z","shell.execute_reply.started":"2024-10-17T01:48:46.431270Z","shell.execute_reply":"2024-10-17T01:48:46.442454Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load Models","metadata":{}},{"cell_type":"code","source":"MODELS_DIR = \"/kaggle/input/rsna-lspine-models-v5-sub/\"","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:48:46.444233Z","iopub.execute_input":"2024-10-17T01:48:46.444556Z","iopub.status.idle":"2024-10-17T01:48:46.456537Z","shell.execute_reply.started":"2024-10-17T01:48:46.444527Z","shell.execute_reply":"2024-10-17T01:48:46.455709Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Foraminal (Sagittal T1)","metadata":{}},{"cell_type":"code","source":"if SAG_T1_FORAMINAL:\n    cfg_file = \"cfg000_sagittal_t1_foramina_slice_dist_level_seq_224\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_localizer_cls_fold0.pt\")\n\n    foramina_localizer_cls0 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    cfg_file = \"cfg000_sagittal_t1_foramina_slice_dist_level_seq_224\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_localizer_cls_fold1.pt\")\n\n    foramina_localizer_cls1 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    cfg_file = \"cfg000_sagittal_t1_regression\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_localizer_reg_fold0.pt\")\n\n    foramina_localizer_reg0 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    cfg_file = \"cfg000_sagittal_t1_regression\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_localizer_reg_fold1.pt\")\n\n    foramina_localizer_reg1 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    foramina_stenosis_models = []\n    \n    cfg_file = \"cfg000_genv4_foramen_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"maxvit_tiny_tf_512\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_stenosis_maxvit_fold0.pt\")\n\n    foramina_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg), \n        \"name\": \"maxvit\", \"act_fn\": \"sigmoid\", \"aggregate\": \"median\"\n    })\n    \n    cfg_file = \"cfg000_genv4_foramen_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.model = \"net_csn_r101\"\n    cfg.num_input_channels = 1\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_stenosis_csn_r101_fold1.pt\")\n\n    foramina_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"csn_r101\", \"act_fn\": \"sigmoid\", \"aggregate\": \"median\"\n    })\n    \n    cfg_file = \"cfg000_genv4_foramen_crops_bb_bce_gt_attn_pool\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"coatnet_1_rw_224\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_stenosis_coatnet_pool_fold2.pt\")\n\n    foramina_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"coatnet_pool\", \"act_fn\": \"sigmoid\", \"aggregate\": \"median\"\n    })\n    \n    cfg_file = \"cfg000_genv4_foramen_crops_bb_bce_gt_attn_pool\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"maxvit_tiny_tf_512\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"foramen_stenosis_maxvit_pool_fold3.pt\")\n\n    foramina_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"maxvit_pool\", \"act_fn\": \"sigmoid\", \"aggregate\": \"median\"\n    })","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:48:46.457571Z","iopub.execute_input":"2024-10-17T01:48:46.457882Z","iopub.status.idle":"2024-10-17T01:49:34.727806Z","shell.execute_reply.started":"2024-10-17T01:48:46.457859Z","shell.execute_reply":"2024-10-17T01:49:34.726802Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Spinal (Sagittal T2)","metadata":{}},{"cell_type":"code","source":"if SAG_T2_SPINAL:\n    cfg_file = \"cfg000_sagittal_t2_spinal_slice_dist_level_seq\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_localizer_cls_fold0.pt\")\n\n    spinal_sag_localizer_cls0 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    cfg_file = \"cfg000_sagittal_t2_spinal_slice_dist_level_seq\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_localizer_cls_fold1.pt\")\n\n    spinal_sag_localizer_cls1 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    cfg_file = \"cfg000_sagittal_t2_regression\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_localizer_reg_fold0.pt\")\n\n    spinal_sag_localizer_reg0 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    cfg_file = \"cfg000_sagittal_t2_regression\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_localizer_reg_fold1.pt\")\n\n    spinal_sag_localizer_reg1 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    spinal_sag_stenosis_models = []\n    \n    cfg_file = \"cfg000_genv5_spinal_crops_gt_half\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"maxvit_tiny_tf_512\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_stenosis_maxvit_all_fold0.pt\")\n\n    spinal_sag_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg), \n        \"name\": \"maxvit_all\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n    \n    cfg_file = \"cfg000_genv5_spinal_crops_gt_half\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"tiny_vit_21m_512.dist_in22k_ft_in1k\"\n    cfg.backbone_img_size = False\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_stenosis_tinyvit_all_fold1.pt\")\n\n    spinal_sag_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"tinyvit_all\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n    \n    cfg_file = \"cfg000_genv5_spinal_crops_gt_half\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.model = \"net_csn_r101\"\n    cfg.num_input_channels = 1\n    cfg.backbone_img_size = False\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_stenosis_csn_r101_all_fold2.pt\")\n\n    spinal_sag_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"csn_r101_all\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n    \n    cfg_file = \"cfg000_genv5_spinal_crops_gt_half_attn_pool\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"maxvit_tiny_tf_512\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_stenosis_maxvit_pool_all_fold3.pt\")\n\n    spinal_sag_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"maxvit_pool_all\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n    \n    cfg_file = \"cfg000_genv5_spinal_crops_gt_half_attn_pool\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"tf_efficientnetv2_m\"\n    cfg.backbone_img_size = False\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_sag_stenosis_effnet_pool_all_fold4.pt\")\n\n    spinal_sag_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"effnet_pool_all\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:49:34.729746Z","iopub.execute_input":"2024-10-17T01:49:34.730022Z","iopub.status.idle":"2024-10-17T01:50:22.407151Z","shell.execute_reply.started":"2024-10-17T01:49:34.729999Z","shell.execute_reply":"2024-10-17T01:50:22.406352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Axial T2 Localizers","metadata":{}},{"cell_type":"code","source":"if AX_T2_SUBARTICULAR or AX_T2_SPINAL:\n\n    cfg_file = \"cfg000_axial_subarticular_slice_dist_level_seq\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"resnet18d\"\n    checkpoint_path = os.path.join(MODELS_DIR, \"ax_localizer_cls_old_resnet_fold0.pt\")\n    \n    ax_localizer_cls_old = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n\n\n    cfg_file = \"cfg000_axial_subarticular_slice_dist_level_seq_v2\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"ax_localizer_cls_resnet_fold0.pt\")\n    \n    ax_localizer_cls1 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n    \n    cfg_file = \"cfg000_axial_subarticular_slice_dist_level_seq_v2\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"tinynet_c\"\n    checkpoint_path = os.path.join(MODELS_DIR, \"ax_localizer_cls_tinynet_fold1.pt\")\n    \n    ax_localizer_cls2 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n\n    cfg_file = \"cfg000_axial_subarticular_slice_dist_level_seq_v2\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"mobilenetv3_large_100\"\n    checkpoint_path = os.path.join(MODELS_DIR, \"ax_localizer_cls_mobilenet_fold2.pt\")\n    \n    ax_localizer_cls3 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n\n    cfg_file = \"cfg000_axial_t2_regression\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n#     checkpoint_path = \"/kaggle/input/rsna-lspine-models-v4-sub/ax_localizer_reg_fold0.pt\")\n    checkpoint_path = os.path.join(MODELS_DIR, \"ax_localizer_reg_fold0.pt\")\n    \n    ax_localizer_reg1 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }\n\n    cfg_file = \"cfg000_axial_t2_regression\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    checkpoint_path = os.path.join(MODELS_DIR, \"ax_localizer_reg_fold1.pt\")\n    \n    ax_localizer_reg2 = {\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg)\n    }","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:50:22.408784Z","iopub.execute_input":"2024-10-17T01:50:22.409183Z","iopub.status.idle":"2024-10-17T01:50:31.912408Z","shell.execute_reply.started":"2024-10-17T01:50:22.409151Z","shell.execute_reply":"2024-10-17T01:50:31.911556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Subarticular (Axial T2)","metadata":{}},{"cell_type":"code","source":"if AX_T2_SUBARTICULAR:\n    subarticular_stenosis_models = []\n    \n    cfg_file = \"cfg000_genv4_subarticular_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.model = \"net_2d\"\n    cfg.num_input_channels = 3\n    cfg.backbone = \"dm_nfnet_f0.dm_in1k\"\n    cfg.backbone_img_size = False\n    checkpoint_path = os.path.join(MODELS_DIR, \"subarticular_stenosis_nfnet_fold0.pt\")\n\n    subarticular_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"nfnet\", \"act_fn\": \"sigmoid\", \"aggregate\": \"median\"\n    })\n    \n    cfg_file = \"cfg000_genv4_subarticular_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.model = \"net_csn_r101\"\n    cfg.num_input_channels = 1\n    checkpoint_path = os.path.join(MODELS_DIR, \"subarticular_stenosis_csn_r101_fold1.pt\")\n\n    subarticular_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"csn_r101\", \"act_fn\": \"sigmoid\", \"aggregate\": \"median\"\n    })\n    \n    cfg_file = \"cfg000_gen5_subarticular_crops_5ch_clam\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"maxvit_tiny_tf_512\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"subarticular_stenosis_maxvit_clam_fold2.pt\")\n\n    subarticular_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg), \n        \"name\": \"maxvit_clam\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n\n    cfg_file = \"cfg000_gen5_subarticular_crops_5ch_clam_xcit\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"xcit_tiny_12_p8_384.fb_dist_in1k\"\n    checkpoint_path = os.path.join(MODELS_DIR, \"subarticular_stenosis_xcit_clam_fold3.pt\")\n\n    subarticular_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"xcit_clam\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:50:31.914450Z","iopub.execute_input":"2024-10-17T01:50:31.914759Z","iopub.status.idle":"2024-10-17T01:51:02.634752Z","shell.execute_reply.started":"2024-10-17T01:50:31.914733Z","shell.execute_reply":"2024-10-17T01:51:02.633903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Spinal (Axial T2)","metadata":{}},{"cell_type":"code","source":"if AX_T2_SPINAL:\n    spinal_ax_stenosis_models = []\n    \n    cfg_file = \"cfg000_genv5_axial_spinal_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.model = \"net_2d\"\n    cfg.num_input_channels = 3\n    cfg.backbone = \"maxvit_tiny_tf_512\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_ax_stenosis_maxvit_fold0.pt\")\n\n    spinal_ax_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"maxvit\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n    \n    cfg_file = \"cfg000_genv5_axial_spinal_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"coatnet_1_rw_224\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_ax_stenosis_coatnet_fold1.pt\")\n\n    spinal_ax_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"coatnet\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n    \n    cfg_file = \"cfg000_genv5_axial_spinal_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"tiny_vit_21m_512.dist_in22k_ft_in1k\"\n    cfg.backbone_img_size = False\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_ax_stenosis_tinyvit_fold2.pt\")\n\n    spinal_ax_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"tinyvit\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n    \n    cfg_file = \"cfg000_genv5_axial_spinal_crops_bb_bce_gt\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.model = \"net_csn_r101\"\n    cfg.num_input_channels = 1\n    cfg.backbone_img_size = False\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_ax_stenosis_csn_r101_fold3.pt\")\n\n    spinal_ax_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"csn_r101\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })\n \n    cfg_file = \"cfg000_genv5_axial_spinal_crops_bb_bce_gt_attn_pool\"\n    cfg = import_module(f\"configs.{cfg_file}\").cfg\n    cfg.backbone = \"coatnet_1_rw_224\"\n    cfg.backbone_img_size = True\n    checkpoint_path = os.path.join(MODELS_DIR, \"spinal_ax_stenosis_coatnet_pool_fold4.pt\")\n\n    spinal_ax_stenosis_models.append({\n        \"cfg\": cfg, \"model\": load_model(checkpoint_path, cfg),\n        \"name\": \"coatnet_pool\", \"act_fn\": \"sigmoid\", \"aggregate\": \"mean\"\n    })","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:02.636350Z","iopub.execute_input":"2024-10-17T01:51:02.636661Z","iopub.status.idle":"2024-10-17T01:51:40.390159Z","shell.execute_reply.started":"2024-10-17T01:51:02.636636Z","shell.execute_reply":"2024-10-17T01:51:40.389310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load Data Info","metadata":{}},{"cell_type":"code","source":"description_df = pd.read_csv(\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_series_descriptions.csv\")\nstudy_series_id_description_dict = {f\"{row.study_id}-{row.series_id}\": row.series_description for row in description_df.itertuples()}\ndicom_dir = \"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images\"\nall_study_ids = os.listdir(dicom_dir)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:40.392125Z","iopub.execute_input":"2024-10-17T01:51:40.392532Z","iopub.status.idle":"2024-10-17T01:51:40.403871Z","shell.execute_reply.started":"2024-10-17T01:51:40.392497Z","shell.execute_reply":"2024-10-17T01:51:40.402821Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Run Inference","metadata":{}},{"cell_type":"code","source":"levels = [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]\nlevels_dict = {lvl: ii for ii, lvl in enumerate(levels)}\nlevels_lat = [f\"rt_{lvl}\" for lvl in levels] + [f\"lt_{lvl}\" for lvl in levels]\n\nforamina_stenosis_preds_by_study = {}\nspinal_sag_stenosis_preds_by_study = {}\nsubarticular_stenosis_preds_by_study = {}\nspinal_ax_stenosis_preds_by_study = {}\n\nfor study_id in tqdm(all_study_ids, total=len(all_study_ids)):\n\n    # --- FIND AVAILABLE SERIES --- #\n    series = glob.glob(os.path.join(dicom_dir, str(study_id), \"*\"))\n    series_path_dict = defaultdict(list)\n    for each_series in series:\n        series_path_dict[study_series_id_description_dict[f\"{study_id}-{os.path.basename(each_series)}\"]].append(each_series)\n\n    # --- BEGIN FORAMINAL --- #\n    SAG_T1_AVAILABLE = len(series_path_dict[\"Sagittal T1\"]) > 0\n    # A few studies had multiple sagittal T1 series\n    # Upon manual review, it seems that they were all duplicates of each other\n    # So we should be fine just taking any of them\n    if SAG_T1_AVAILABLE and SAG_T1_FORAMINAL:\n        sag_t1 = series_path_dict[\"Sagittal T1\"][0]\n        sag_t1, sag_t1_dicom_files, _ = load_dicom_stack(sag_t1, plane=\"sagittal\")\n\n    foramina_stenosis_preds = defaultdict(dict)\n    \n    if SAG_T1_AVAILABLE and SAG_T1_FORAMINAL:\n        # Create 3-channel stacked volume # (N, H, W) -> (N, H, W, 3)\n        sag_t1 = np.stack([\n            np.concatenate([np.expand_dims(sag_t1[0], axis=0), sag_t1[:-1]]),\n            sag_t1,\n            np.concatenate([sag_t1[1:], np.expand_dims(sag_t1[-1], axis=0)])\n        ], axis=-1)\n\n        # Get slice indices and coordinates for each side/level \n        sag_t1_torch = np.stack([foramina_localizer_cls1[\"cfg\"].val_transforms(image=_)[\"image\"] for _ in sag_t1])\n        sag_t1_torch = sag_t1_torch.transpose(0, 3, 1, 2)\n        sag_t1_torch = torch.from_numpy(sag_t1_torch).float().to(\"cuda:0\").unsqueeze(0)\n        with torch.inference_mode():\n            out1 = foramina_localizer_cls0[\"model\"]({\"x\": sag_t1_torch})[\"logits\"][0].cpu().abs().numpy()\n            out2 = foramina_localizer_cls1[\"model\"]({\"x\": sag_t1_torch})[\"logits\"][0].cpu().abs().numpy()\n        \n        out = (out1 + out2) / 2.0\n        \n        coords_dict = convert_dists_to_foramina_slice_indices(out, arr_shape=sag_t1.shape[1:])\n        store_coords_preds = {}\n        for k, v in coords_dict.copy().items():\n            tmp_sag_t1_torch = foramina_localizer_reg1[\"cfg\"].val_transforms(image=sag_t1[v], keypoints=[])[\"image\"]\n            tmp_sag_t1_torch = torch.from_numpy(tmp_sag_t1_torch).permute(2, 0, 1).unsqueeze(0).float().to(\"cuda:0\")\n            if v in store_coords_preds:\n                # If coordinates were already predicted for that slice\n                # No need to predict again\n                coords = store_coords_preds[v]\n            else:\n                with torch.inference_mode():\n                    reg_out1 = foramina_localizer_reg0[\"model\"]({\"x\": tmp_sag_t1_torch})[\"logits\"].sigmoid().cpu().numpy()[0]\n                    reg_out2 = foramina_localizer_reg1[\"model\"]({\"x\": tmp_sag_t1_torch})[\"logits\"].sigmoid().cpu().numpy()[0]\n                \n                reg_out = (reg_out1 + reg_out2) / 2.0\n                coords = np.stack([reg_out[:5], reg_out[5:]], axis=1)\n                store_coords_preds[v] = coords\n            x, y = coords[levels_dict[k.replace(\"_slice_index\", \"\")[3:]]]\n            coords_dict[k.replace(\"slice_index\", \"x\")] = x * sag_t1.shape[2]\n            coords_dict[k.replace(\"slice_index\", \"y\")] = y * sag_t1.shape[1]\n\n        # Generate multiple crops\n        foramina_crops = crop_using_coords_dict(sag_t1, coords_dict, bilat=True, size_factor=0.15)\n\n        # Run 2D crop-aggregate models\n        for k, v in foramina_crops.items():\n            # All backbones use same val_transforms (resize to 64 x 64)\n            v = np.stack([foramina_stenosis_models[0][\"cfg\"].val_transforms(image=_crop)[\"image\"] for _crop in v])\n            v = torch.from_numpy(v.transpose(0, 3, 1, 2)).float().to(\"cuda:0\")\n            out_list = []\n            for model_idx, each_model in enumerate(foramina_stenosis_models):\n                \n                with torch.inference_mode():\n                    if each_model[\"name\"].startswith(\"csn_r101\"):\n                        # For 3D CSN need to unsqueeze 3rd spatial dimension\n                        tmp_out = each_model[\"model\"]({\"x\": v.unsqueeze(1)})\n                    else:\n                        tmp_out = each_model[\"model\"]({\"x\": v})\n                    \n                if each_model[\"act_fn\"] == \"sigmoid\":\n                    tmp_out = tmp_out[\"logits\"].sigmoid().cpu().numpy()\n                elif each_model[\"act_fn\"] == \"softmax\": \n                    tmp_out = torch.softmax(tmp_out[\"logits\"], dim=1).cpu().numpy()\n\n                if each_model[\"aggregate\"] == \"median\":\n                    tmp_out = np.median(tmp_out, axis=0)\n                elif each_model[\"aggregate\"] == \"mean\":\n                    tmp_out = np.mean(tmp_out, axis=0)\n\n                foramina_stenosis_preds[k][each_model[\"name\"]] = tmp_out\n            \n    if SAG_T1_FORAMINAL:\n        # Fill in missing levels\n        for each_lvl in levels_lat:\n            if each_lvl not in [*foramina_stenosis_preds]:\n                for each_model in foramina_stenosis_models:\n                    foramina_stenosis_preds[each_lvl][each_model[\"name\"]] = np.asarray([1/3., 1/3., 1/3.])\n                    \n        foramina_stenosis_preds_by_study[study_id] = foramina_stenosis_preds\n\n    # --- END FORAMINAL --- #\n\n    # --- BEGIN SPINAL (SAG T2) --- #\n    SAG_T2_AVAILABLE = len(series_path_dict[\"Sagittal T2/STIR\"]) > 0\n    # If more than 1 sagittal T2 series, just take the first one\n    if SAG_T2_AVAILABLE and SAG_T2_SPINAL:\n        sag_t2 = series_path_dict[\"Sagittal T2/STIR\"][0]\n        sag_t2_series = os.path.basename(sag_t2)\n        sag_t2, sag_t2_dicom_files, _ = load_dicom_stack(sag_t2, plane=\"sagittal\")\n    \n    sagittal_t2_coords_for_axial = None\n    \n    spinal_sag_stenosis_preds = defaultdict(dict)\n    \n    if SAG_T2_AVAILABLE and SAG_T2_SPINAL:\n        # Create 3-channel stacked volume # (N, H, W) -> (N, H, W, 3)\n        sag_t2 = np.stack([\n            np.concatenate([np.expand_dims(sag_t2[0], axis=0), sag_t2[:-1]]),\n            sag_t2,\n            np.concatenate([sag_t2[1:], np.expand_dims(sag_t2[-1], axis=0)])\n        ], axis=-1)\n\n        # Get slice indices and coordinates for each side/level \n        sag_t2_torch = np.stack([spinal_sag_localizer_cls1[\"cfg\"].val_transforms(image=_)[\"image\"] for _ in sag_t2])\n        sag_t2_torch = sag_t2_torch.transpose(0, 3, 1, 2)\n        sag_t2_torch = torch.from_numpy(sag_t2_torch).float().to(\"cuda:0\").unsqueeze(0)\n        with torch.inference_mode():\n            out1 = spinal_sag_localizer_cls0[\"model\"]({\"x\": sag_t2_torch})[\"logits\"][0].cpu().abs().numpy()\n            out2 = spinal_sag_localizer_cls1[\"model\"]({\"x\": sag_t2_torch})[\"logits\"][0].cpu().abs().numpy()\n        \n        out = (out1 + out2) / 2.0\n        coords_dict = convert_dists_to_spinal_slice_indices(out, arr_shape=sag_t2.shape[1:])\n        store_coords_preds = {}\n        for k, v in coords_dict.copy().items():\n            tmp_sag_t2_torch = spinal_sag_localizer_reg1[\"cfg\"].val_transforms(image=sag_t2[v], keypoints=[])[\"image\"]\n            tmp_sag_t2_torch = torch.from_numpy(tmp_sag_t2_torch).permute(2, 0, 1).unsqueeze(0).float().to(\"cuda:0\")\n            if v in store_coords_preds:\n                # If coordinates were already predicted for that slice\n                # No need to predict again\n                coords = store_coords_preds[v]\n            else:\n                with torch.inference_mode():\n                    reg_out1 = spinal_sag_localizer_reg0[\"model\"]({\"x\": tmp_sag_t2_torch})[\"logits\"].sigmoid().cpu().numpy()[0]\n                    reg_out2 = spinal_sag_localizer_reg1[\"model\"]({\"x\": tmp_sag_t2_torch})[\"logits\"].sigmoid().cpu().numpy()[0]\n                \n                reg_out = (reg_out1 + reg_out2) / 2.0\n                coords = np.stack([reg_out[:5], reg_out[5:]], axis=1)\n                store_coords_preds[v] = coords\n            x, y = coords[levels_dict[k.replace(\"_slice_index\", \"\")]]\n            coords_dict[k.replace(\"slice_index\", \"x\")] = x * sag_t2.shape[2]\n            coords_dict[k.replace(\"slice_index\", \"y\")] = y * sag_t2.shape[1]\n\n        # Generate multiple crops\n        spinal_sag_crops = crop_using_coords_dict(sag_t2, coords_dict, bilat=False, size_factor=0.15)\n        spinal_sag_crops_all_slices = crop_using_coords_dict_all_slices(sag_t2, coords_dict, bilat=False, size_factor=0.15)\n        \n        # Assemble sagittal T2 coords for axial model\n        sagittal_t2_coords_for_axial = coords_dict.copy()\n        for k, v in sagittal_t2_coords_for_axial.copy().items():\n            if k.endswith(\"slice_index\"):\n                sagittal_t2_coords_for_axial[k.replace(\"slice_index\", \"dicom\")] = pydicom.dcmread(sag_t2_dicom_files[v], stop_before_pixels=True)\n\n        # Run 2D crop-aggregate models\n        for k, v in spinal_sag_crops.items():\n            # All backbones use same val_transforms (resize to 64 x 64)\n            v = np.stack([spinal_sag_stenosis_models[0][\"cfg\"].val_transforms(image=_crop)[\"image\"] for _crop in v])\n            v = torch.from_numpy(v.transpose(0, 3, 1, 2)).float().to(\"cuda:0\")\n            out_list = []\n            for model_idx, each_model in enumerate(spinal_sag_stenosis_models):\n                \n                with torch.inference_mode():\n                    if each_model[\"name\"].startswith(\"csn_r101\"):\n                            # For 3D CSN need to unsqueeze 3rd spatial dimension\n                        tmp_out = each_model[\"model\"]({\"x\": v.unsqueeze(1)})[\"logits\"]\n                    else:\n                        tmp_out = each_model[\"model\"]({\"x\": v})[\"logits\"]\n                        \n                if each_model[\"name\"].endswith(\"_all\"):\n                    valid_slice_pred = tmp_out[:, 3].sigmoid().cpu().numpy()\n                    valid_slices = np.where(valid_slice_pred >= 0.5)[0]\n                    if len(valid_slices) == 0:\n                        valid_slices = np.ascontiguousarray(np.argsort(valid_slice_pred)[::-1][:5])\n                    tmp_out = tmp_out[valid_slices, :3]\n                        \n                if each_model[\"act_fn\"] == \"sigmoid\":\n                    tmp_out = tmp_out.sigmoid().cpu().numpy()\n                elif each_model[\"act_fn\"] == \"softmax\": \n                    tmp_out = torch.softmax(tmp_out, dim=1).cpu().numpy()\n\n                if each_model[\"aggregate\"] == \"median\":\n                    tmp_out = np.median(tmp_out, axis=0)\n                elif each_model[\"aggregate\"] == \"mean\":\n                    tmp_out = np.mean(tmp_out, axis=0)\n\n                spinal_sag_stenosis_preds[k][each_model[\"name\"]] = tmp_out\n\n\n    if SAG_T2_SPINAL:\n        # Fill in missing levels\n        for each_lvl in levels:\n            if each_lvl not in [*spinal_sag_stenosis_preds]:\n                for each_model in spinal_sag_stenosis_models:\n                    spinal_sag_stenosis_preds[each_lvl][each_model[\"name\"]] = np.asarray([1/3., 1/3., 1/3.])\n                    \n        spinal_sag_stenosis_preds_by_study[study_id] = spinal_sag_stenosis_preds\n\n    # --- END SPINAL (SAG T2) --- #\n\n    # --- BEGIN SUBARTICULAR (+/- SPINAL AX T2) --- #\n    AX_T2_AVAILABLE = len(series_path_dict[\"Axial T2\"]) > 0\n    # Some studies split axial T2s into segments \n    # So we  need to load all the available axial series\n    if AX_T2_AVAILABLE and (AX_T2_SUBARTICULAR or AX_T2_SPINAL):\n        ax_t2 = series_path_dict[\"Axial T2\"]\n        ax_t2 = {\n            each_ax_t2: load_dicom_stack(each_ax_t2, \n                                         plane=\"axial\", \n                                         sort_by_instance=True, \n                                         sagittal_t2_coords=sagittal_t2_coords_for_axial) \n            for each_ax_t2 in ax_t2\n        }\n        \n    subarticular_stenosis_preds = defaultdict(dict)\n    spinal_ax_stenosis_preds = defaultdict(dict)\n    \n    if AX_T2_AVAILABLE and (AX_T2_SUBARTICULAR or AX_T2_SPINAL):\n        # Loop over series\n        subarticular_crops_over_series = {}\n        subarticular_crops_5ch_over_series = {}\n        spinal_ax_crops_over_series = {}\n        \n        for each_ax_t2_series, (each_ax_t2, each_ax_t2_dicom_files, pz) in ax_t2.items():\n            \n            # Create 5-channel stacked volume # (N, H, W) -> (N, H, W, 5)\n            each_ax_t2_5ch = np.stack([\n                np.concatenate([np.expand_dims(each_ax_t2[0], axis=0), np.expand_dims(each_ax_t2[0], axis=0), each_ax_t2[:-2]]),\n                np.concatenate([np.expand_dims(each_ax_t2[0], axis=0), each_ax_t2[:-1]]),\n                each_ax_t2,\n                np.concatenate([each_ax_t2[1:], np.expand_dims(each_ax_t2[-1], axis=0)]),\n                np.concatenate([each_ax_t2[2:], np.expand_dims(each_ax_t2[-1], axis=0), np.expand_dims(each_ax_t2[-1], axis=0)])\n            ], axis=-1)\n            \n            # Create 3-channel stacked volume # (N, H, W) -> (N, H, W, 3)\n            each_ax_t2 = np.stack([\n                np.concatenate([np.expand_dims(each_ax_t2[0], axis=0), each_ax_t2[:-1]]),\n                each_ax_t2,\n                np.concatenate([each_ax_t2[1:], np.expand_dims(each_ax_t2[-1], axis=0)])\n            ], axis=-1)\n\n            # Get slice indices and coordinates for each side/level \n            if isinstance(pz, np.ndarray):\n                each_ax_t2_torch = np.stack([ax_localizer_cls1[\"cfg\"].val_transforms(image=_)[\"image\"] for _ in each_ax_t2])\n                each_ax_t2_torch = each_ax_t2_torch.transpose(0, 3, 1, 2)\n                each_ax_t2_torch = torch.from_numpy(each_ax_t2_torch).float().to(\"cuda:0\").unsqueeze(0)\n                pz = torch.from_numpy(pz).float().to(\"cuda:0\").unsqueeze(0)\n                with torch.inference_mode():\n                    out1 = ax_localizer_cls1[\"model\"]({\"x\": each_ax_t2_torch, \"pz\": pz})[\"logits\"][0].cpu().sigmoid().numpy()\n                    out2 = ax_localizer_cls2[\"model\"]({\"x\": each_ax_t2_torch, \"pz\": pz})[\"logits\"][0].cpu().sigmoid().numpy()\n                    out3 = ax_localizer_cls3[\"model\"]({\"x\": each_ax_t2_torch, \"pz\": pz})[\"logits\"][0].cpu().sigmoid().numpy()\n                    out = (out1 + out2 + out3) / 3.0\n            else:\n                each_ax_t2_torch = np.stack([ax_localizer_cls_old[\"cfg\"].val_transforms(image=_)[\"image\"] for _ in each_ax_t2])\n                each_ax_t2_torch = each_ax_t2_torch.transpose(0, 3, 1, 2)\n                each_ax_t2_torch = torch.from_numpy(each_ax_t2_torch).float().to(\"cuda:0\").unsqueeze(0)\n                with torch.inference_mode():\n                    out = ax_localizer_cls_old[\"model\"]({\"x\": each_ax_t2_torch})[\"logits\"][0].cpu().sigmoid().numpy()\n            \n            coords_dict = convert_probas_to_subarticular_slice_indices(out, arr_shape=each_ax_t2.shape[1:], thresh=0.1)\n            store_coords_preds = {}\n            for k, v in coords_dict.copy().items():\n                tmp_ax_t2_torch = ax_localizer_reg1[\"cfg\"].val_transforms(image=each_ax_t2[v], keypoints=[])[\"image\"]\n                tmp_ax_t2_torch = torch.from_numpy(tmp_ax_t2_torch).permute(2, 0, 1).unsqueeze(0).float().to(\"cuda:0\")\n                with torch.inference_mode():\n                    reg_out1 = ax_localizer_reg1[\"model\"]({\"x\": tmp_ax_t2_torch})[\"logits\"].sigmoid().cpu().numpy()[0]\n                    reg_out2 = ax_localizer_reg2[\"model\"]({\"x\": tmp_ax_t2_torch})[\"logits\"].sigmoid().cpu().numpy()[0]\n                \n                reg_out = (reg_out1 + reg_out2) / 2.0\n                if k.startswith(\"rt_\"):\n                    x, y = reg_out[:2]\n                elif k.startswith(\"lt_\"):\n                    x, y = reg_out[2:]\n                coords_dict[k.replace(\"slice_index\", \"x\")] = x * each_ax_t2.shape[2]\n                coords_dict[k.replace(\"slice_index\", \"y\")] = y * each_ax_t2.shape[1]\n\n            if AX_T2_SUBARTICULAR:\n                subarticular_crops = crop_using_coords_dict(each_ax_t2, coords_dict, bilat=True, size_factor=0.125, slice_stagger=1)\n                for k, v in subarticular_crops.items():\n                    if k in subarticular_crops_over_series:\n                        subarticular_crops_over_series[k].extend(v)\n                    else:\n                        subarticular_crops_over_series[k] = v\n                        \n                subarticular_crops_5ch = crop_using_coords_dict(each_ax_t2_5ch, coords_dict, bilat=True, size_factor=0.125, slice_stagger=1)\n                for k, v in subarticular_crops_5ch.items():\n                    if k in subarticular_crops_5ch_over_series:\n                        subarticular_crops_5ch_over_series[k].extend(v)\n                    else:\n                        subarticular_crops_5ch_over_series[k] = v\n                        \n            if AX_T2_SPINAL:\n                spinal_ax_coords_dict = {}\n                # Edit coordinates for spinal\n                for lvl in levels:\n                    try:\n                        spinal_ax_coords_dict[f\"{lvl}_slice_index\"] = (coords_dict[f\"rt_{lvl}_slice_index\"] + coords_dict[f\"lt_{lvl}_slice_index\"]) // 2\n                        spinal_ax_coords_dict[f\"{lvl}_x\"] = (coords_dict[f\"rt_{lvl}_x\"] + coords_dict[f\"lt_{lvl}_x\"]) / 2\n                        spinal_ax_coords_dict[f\"{lvl}_y\"] = 1.05 * ((coords_dict[f\"rt_{lvl}_y\"] + coords_dict[f\"lt_{lvl}_y\"]) / 2)\n                    except KeyError:\n                        # If level doesn't exist, just skip\n                        continue\n                spinal_ax_crops = crop_using_coords_dict(each_ax_t2, spinal_ax_coords_dict, bilat=False, size_factor=0.15, slice_stagger=1)\n                for k, v in spinal_ax_crops.items():\n                    if k in spinal_ax_crops_over_series:\n                        spinal_ax_crops_over_series[k].extend(v)\n                    else:\n                        spinal_ax_crops_over_series[k] = v\n                    \n        # Run 2D crop-aggregate models\n        if AX_T2_SUBARTICULAR:\n            for k, v in subarticular_crops_over_series.items():\n                # All backbones use same val_transforms (resize to 64 x 64)\n                v = np.stack([subarticular_stenosis_models[0][\"cfg\"].val_transforms(image=_crop)[\"image\"] for _crop in v])\n                v = torch.from_numpy(v.transpose(0, 3, 1, 2)).float().to(\"cuda:0\")\n                out_list = []\n                for model_idx, each_model in enumerate(subarticular_stenosis_models):\n                    \n                    if each_model[\"name\"].endswith(\"_clam\"):\n                        continue\n\n                    with torch.inference_mode():\n                        if each_model[\"name\"].startswith(\"csn_r101\"):\n                            # For 3D CSN need to unsqueeze 3rd spatial dimension\n                            tmp_out = each_model[\"model\"]({\"x\": v.unsqueeze(1)})\n                        else:\n                            tmp_out = each_model[\"model\"]({\"x\": v})\n                        \n                    if each_model[\"act_fn\"] == \"sigmoid\":\n                        tmp_out = tmp_out[\"logits\"].sigmoid().cpu().numpy()\n                    elif each_model[\"act_fn\"] == \"softmax\": \n                        tmp_out = torch.softmax(tmp_out[\"logits\"], dim=1).cpu().numpy()\n    \n                    if each_model[\"aggregate\"] == \"median\":\n                        tmp_out = np.median(tmp_out, axis=0)\n                    elif each_model[\"aggregate\"] == \"mean\":\n                        tmp_out = np.mean(tmp_out, axis=0)\n    \n                    subarticular_stenosis_preds[k][each_model[\"name\"]] = tmp_out\n        \n            for k, v in subarticular_crops_5ch_over_series.items():\n                # All backbones use same val_transforms (resize to 64 x 64)\n                v = np.stack([subarticular_stenosis_models[-1][\"cfg\"].val_transforms(image=_crop)[\"image\"] for _crop in v])\n                v = torch.from_numpy(v.transpose(0, 3, 1, 2)).float().to(\"cuda:0\")\n                out_list = []\n                for model_idx, each_model in enumerate(subarticular_stenosis_models):\n\n                    if not each_model[\"name\"].endswith(\"_clam\"):\n                        continue\n                    \n                    with torch.inference_mode():\n                        if each_model[\"name\"].startswith(\"csn_r101\"):\n                            # For 3D CSN need to unsqueeze 3rd spatial dimension\n                            tmp_out = each_model[\"model\"]({\"x\": v.unsqueeze(1)})\n                        else:\n                            tmp_out = each_model[\"model\"]({\"x\": v})\n                        \n                    if each_model[\"act_fn\"] == \"sigmoid\":\n                        tmp_out = tmp_out[\"logits\"].sigmoid().cpu().numpy()\n                    elif each_model[\"act_fn\"] == \"softmax\": \n                        tmp_out = torch.softmax(tmp_out[\"logits\"], dim=1).cpu().numpy()\n    \n                    if each_model[\"aggregate\"] == \"median\":\n                        tmp_out = np.median(tmp_out, axis=0)\n                    elif each_model[\"aggregate\"] == \"mean\":\n                        tmp_out = np.mean(tmp_out, axis=0)\n    \n                    subarticular_stenosis_preds[k][each_model[\"name\"]] = tmp_out\n        \n        if AX_T2_SPINAL:\n            for k, v in spinal_ax_crops_over_series.items():\n                v = np.stack([spinal_ax_stenosis_models[0][\"cfg\"].val_transforms(image=_crop)[\"image\"] for _crop in v])\n                v = torch.from_numpy(v.transpose(0, 3, 1, 2)).float().to(\"cuda:0\")\n                out_list = []\n                for model_idx, each_model in enumerate(spinal_ax_stenosis_models):\n                    \n                    with torch.inference_mode():\n                        if each_model[\"name\"].startswith(\"csn_r101\"):\n                            # For 3D CSN need to unsqueeze 3rd spatial dimension\n                            tmp_out = each_model[\"model\"]({\"x\": v.unsqueeze(1)})\n                        else:\n                            tmp_out = each_model[\"model\"]({\"x\": v})\n                        \n                    if each_model[\"act_fn\"] == \"sigmoid\":\n                        tmp_out = tmp_out[\"logits\"].sigmoid().cpu().numpy()\n                    elif each_model[\"act_fn\"] == \"softmax\": \n                        tmp_out = torch.softmax(tmp_out[\"logits\"], dim=1).cpu().numpy()\n    \n                    if each_model[\"aggregate\"] == \"median\":\n                        tmp_out = np.median(tmp_out, axis=0)\n                    elif each_model[\"aggregate\"] == \"mean\":\n                        tmp_out = np.mean(tmp_out, axis=0)\n    \n                    spinal_ax_stenosis_preds[k][each_model[\"name\"]] = tmp_out\n            \n    if AX_T2_SUBARTICULAR:\n        # Fill in missing levels\n        # Note: Do NOT fill in missing levels for axial T2 spinal, since we will just use sagittal predictions\n        #       instead of averaging\n        for each_lvl in levels_lat:\n            if each_lvl not in [*subarticular_stenosis_preds]:\n                for each_model in subarticular_stenosis_models:\n                    subarticular_stenosis_preds[each_lvl][each_model[\"name\"]] = np.asarray([1/3., 1/3., 1/3.])\n                    \n        subarticular_stenosis_preds_by_study[study_id] = subarticular_stenosis_preds\n\n    if AX_T2_SPINAL:\n        spinal_ax_stenosis_preds_by_study[study_id] = spinal_ax_stenosis_preds","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:40.405774Z","iopub.execute_input":"2024-10-17T01:51:40.406248Z","iopub.status.idle":"2024-10-17T01:51:52.127635Z","shell.execute_reply.started":"2024-10-17T01:51:40.406212Z","shell.execute_reply":"2024-10-17T01:51:52.126718Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Assemble Predictions","metadata":{}},{"cell_type":"markdown","source":"### Foraminal","metadata":{}},{"cell_type":"code","source":"if SAG_T1_FORAMINAL:\n    df_dict = {}\n    for model_idx, each_model in enumerate(foramina_stenosis_models):\n        study_id_list, level_list, proba_list = [], [], []\n        for study_id, stenosis_preds in foramina_stenosis_preds_by_study.items():\n            for level, probas in stenosis_preds.items():\n                study_id_list.append(study_id)\n                level_list.append(level)\n                proba_list.append(probas[each_model[\"name\"]])\n        tmp_df = pd.DataFrame(np.stack(proba_list))\n        tmp_df.columns = [\"normal_mild\", \"moderate\", \"severe\"]\n        tmp_df[\"study_id\"] = study_id_list\n        tmp_df[\"side_level\"] = level_list\n        tmp_df[\"backbone\"] = each_model[\"name\"]\n        tmp_df[\"level\"] = tmp_df.side_level.apply(lambda x: x[3:].upper())\n        tmp_df[\"laterality\"] = tmp_df.side_level.apply(lambda x: x[0].upper())\n        df_dict[model_idx] = tmp_df\n\n    pred_df = pd.concat([v for v in df_dict.values()])\n    grades = [\"normal_mild\", \"moderate\", \"severe\"]\n    pred_df[grades] = normalize(pred_df[grades].values)\n\n    foramina_pred_df = pred_df.loc[pred_df.backbone == pred_df.backbone.unique()[0]].copy()\n    for bb in pred_df.backbone.unique()[1:]:\n        foramina_pred_df[grades] += pred_df.loc[pred_df.backbone == bb][grades].values\n\n    foramina_pred_df[grades] /= len(pred_df.backbone.unique())\n    print(foramina_pred_df.head())","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.128806Z","iopub.execute_input":"2024-10-17T01:51:52.129086Z","iopub.status.idle":"2024-10-17T01:51:52.163510Z","shell.execute_reply.started":"2024-10-17T01:51:52.129062Z","shell.execute_reply":"2024-10-17T01:51:52.162594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Spinal (Sagittal)","metadata":{}},{"cell_type":"code","source":"if SAG_T2_SPINAL:\n    df_dict = {}\n    for model_idx, each_model in enumerate(spinal_sag_stenosis_models):\n        study_id_list, level_list, proba_list = [], [], []\n        for study_id, stenosis_preds in spinal_sag_stenosis_preds_by_study.items():\n            for level, probas in stenosis_preds.items():\n                study_id_list.append(study_id)\n                level_list.append(level)\n                proba_list.append(probas[each_model[\"name\"]])\n        tmp_df = pd.DataFrame(np.stack(proba_list))\n        tmp_df.columns = [\"normal_mild\", \"moderate\", \"severe\"]\n        tmp_df[\"study_id\"] = study_id_list\n        tmp_df[\"backbone\"] = each_model[\"name\"]\n        tmp_df[\"level\"] = [_.upper() for _ in level_list]\n        df_dict[model_idx] = tmp_df\n\n    pred_df = pd.concat([v for v in df_dict.values()])\n    grades = [\"normal_mild\", \"moderate\", \"severe\"]\n    pred_df[grades] = normalize(pred_df[grades].values)\n\n    spinal_sag_pred_df = pred_df.loc[pred_df.backbone == pred_df.backbone.unique()[0]].copy()\n    for bb in pred_df.backbone.unique()[1:]:\n        spinal_sag_pred_df[grades] += pred_df.loc[pred_df.backbone == bb][grades].values\n\n    spinal_sag_pred_df[grades] /= len(pred_df.backbone.unique())\n\n    print(spinal_sag_pred_df.head())","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.164566Z","iopub.execute_input":"2024-10-17T01:51:52.164836Z","iopub.status.idle":"2024-10-17T01:51:52.196795Z","shell.execute_reply.started":"2024-10-17T01:51:52.164812Z","shell.execute_reply":"2024-10-17T01:51:52.195971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Subarticular","metadata":{}},{"cell_type":"code","source":"if AX_T2_SUBARTICULAR:\n    df_dict = {}\n    for model_idx, each_model in enumerate(subarticular_stenosis_models):\n        study_id_list, level_list, proba_list = [], [], []\n        for study_id, stenosis_preds in subarticular_stenosis_preds_by_study.items():\n            for level, probas in stenosis_preds.items():\n                study_id_list.append(study_id)\n                level_list.append(level)\n                proba_list.append(probas[each_model[\"name\"]])\n        tmp_df = pd.DataFrame(np.stack(proba_list))\n        tmp_df.columns = [\"normal_mild\", \"moderate\", \"severe\"]\n        tmp_df[\"study_id\"] = study_id_list\n        tmp_df[\"side_level\"] = level_list\n        tmp_df[\"backbone\"] = each_model[\"name\"]\n        tmp_df[\"level\"] = tmp_df.side_level.apply(lambda x: x[3:].upper())\n        tmp_df[\"laterality\"] = tmp_df.side_level.apply(lambda x: x[0].upper())\n        df_dict[model_idx] = tmp_df\n\n    pred_df = pd.concat([v for v in df_dict.values()])\n    grades = [\"normal_mild\", \"moderate\", \"severe\"]\n    pred_df[grades] = normalize(pred_df[grades].values)\n\n    subarticular_pred_df = pred_df.loc[pred_df.backbone == pred_df.backbone.unique()[0]].copy()\n    for bb in pred_df.backbone.unique()[1:]:\n        subarticular_pred_df[grades] += pred_df.loc[pred_df.backbone == bb][grades].values\n\n    subarticular_pred_df[grades] /= len(pred_df.backbone.unique())\n    print(subarticular_pred_df.head())","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.197791Z","iopub.execute_input":"2024-10-17T01:51:52.198049Z","iopub.status.idle":"2024-10-17T01:51:52.231366Z","shell.execute_reply.started":"2024-10-17T01:51:52.198027Z","shell.execute_reply":"2024-10-17T01:51:52.230467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Spinal (Axial)","metadata":{}},{"cell_type":"code","source":"if AX_T2_SPINAL:\n    df_dict = {}\n    for model_idx, each_model in enumerate(spinal_ax_stenosis_models):\n        study_id_list, level_list, proba_list = [], [], []\n        for study_id, stenosis_preds in spinal_ax_stenosis_preds_by_study.items():\n            for level, probas in stenosis_preds.items():\n                study_id_list.append(study_id)\n                level_list.append(level)\n                proba_list.append(probas[each_model[\"name\"]])\n        tmp_df = pd.DataFrame(np.stack(proba_list))\n        tmp_df.columns = [\"normal_mild\", \"moderate\", \"severe\"]\n        tmp_df[\"study_id\"] = study_id_list\n        tmp_df[\"backbone\"] = each_model[\"name\"]\n        tmp_df[\"level\"] = [_.upper() for _ in level_list]\n        df_dict[model_idx] = tmp_df\n\n    pred_df = pd.concat([v for v in df_dict.values()])\n    grades = [\"normal_mild\", \"moderate\", \"severe\"]\n    pred_df[grades] = normalize(pred_df[grades].values)\n\n    spinal_ax_pred_df = pred_df.loc[pred_df.backbone == pred_df.backbone.unique()[0]].copy()\n    for bb in pred_df.backbone.unique()[1:]:\n        spinal_ax_pred_df[grades] += pred_df.loc[pred_df.backbone == bb][grades].values\n\n    spinal_ax_pred_df[grades] /= len(pred_df.backbone.unique())\n\n    print(spinal_ax_pred_df.head())","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.232812Z","iopub.execute_input":"2024-10-17T01:51:52.233279Z","iopub.status.idle":"2024-10-17T01:51:52.264759Z","shell.execute_reply.started":"2024-10-17T01:51:52.233247Z","shell.execute_reply":"2024-10-17T01:51:52.263900Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Combine Spinal Sagittal and Axial","metadata":{}},{"cell_type":"code","source":"spinal_sag_pred_df[\"study_level\"] = spinal_sag_pred_df.study_id.astype(\"str\") + \"_\" + spinal_sag_pred_df.level\nspinal_ax_pred_df[\"study_level\"] = spinal_ax_pred_df.study_id.astype(\"str\") + \"_\" + spinal_ax_pred_df.level\n\nspinal_sag_missing_ax = spinal_sag_pred_df.loc[~spinal_sag_pred_df.study_level.isin(spinal_ax_pred_df.study_level.tolist())]\nspinal_sag_present_ax = spinal_sag_pred_df.loc[spinal_sag_pred_df.study_level.isin(spinal_ax_pred_df.study_level.tolist())]\n\nspinal_sag_present_ax = spinal_sag_present_ax.sort_values(\"study_level\").reset_index(drop=True)\nspinal_ax_pred_df = spinal_ax_pred_df.sort_values(\"study_level\").reset_index(drop=True)\n\nassert len(spinal_sag_present_ax) == len(spinal_ax_pred_df)\nassert np.sum(spinal_sag_present_ax.study_level == spinal_ax_pred_df.study_level) == len(spinal_sag_present_ax)\n\nweights = [1, 0.5]\nspinal_sag_ax_ensemble = spinal_sag_present_ax.copy()\nspinal_sag_ax_ensemble[grades] = (weights[0] * spinal_sag_present_ax[grades].values + weights[1] * spinal_ax_pred_df[grades].values) / np.sum(weights)\nspinal_sag_ax_ensemble = pd.concat([spinal_sag_ax_ensemble, spinal_sag_missing_ax])\n\nprint(spinal_sag_ax_ensemble.head())","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.265811Z","iopub.execute_input":"2024-10-17T01:51:52.266073Z","iopub.status.idle":"2024-10-17T01:51:52.290909Z","shell.execute_reply.started":"2024-10-17T01:51:52.266051Z","shell.execute_reply":"2024-10-17T01:51:52.290064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"side_dict = {\"L\": \"left\", \"R\": \"right\"}\nforamina_pred_df[\"row_id\"] = foramina_pred_df.study_id.astype(\"str\") + \"_\" + foramina_pred_df.laterality.map(side_dict) + \\\n                            \"_neural_foraminal_narrowing_\" + foramina_pred_df.level.apply(lambda x: x.lower())\nforamina_pred_df = foramina_pred_df[[\"row_id\", \"normal_mild\", \"moderate\", \"severe\"]]\n\nspinal_sag_ax_ensemble[\"row_id\"] = spinal_sag_ax_ensemble.study_id.astype(\"str\") + \"_spinal_canal_stenosis_\" + \\\n                           spinal_sag_ax_ensemble.level.apply(lambda x: x.lower())\nspinal_sag_ax_ensemble = spinal_sag_ax_ensemble[[\"row_id\", \"normal_mild\", \"moderate\", \"severe\"]]\n\nsubarticular_pred_df[\"row_id\"] = subarticular_pred_df.study_id.astype(\"str\") + \"_\" + subarticular_pred_df.laterality.map(side_dict) + \\\n                                 \"_subarticular_stenosis_\" + subarticular_pred_df.level.apply(lambda x: x.lower())\nsubarticular_pred_df = subarticular_pred_df[[\"row_id\", \"normal_mild\", \"moderate\", \"severe\"]]\n\nsubmission = pd.concat([foramina_pred_df, spinal_sag_ax_ensemble, subarticular_pred_df]).sort_values(\"row_id\")","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.292038Z","iopub.execute_input":"2024-10-17T01:51:52.292380Z","iopub.status.idle":"2024-10-17T01:51:52.312207Z","shell.execute_reply.started":"2024-10-17T01:51:52.292346Z","shell.execute_reply":"2024-10-17T01:51:52.311386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv(\"submission2.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.313694Z","iopub.execute_input":"2024-10-17T01:51:52.314016Z","iopub.status.idle":"2024-10-17T01:51:52.327797Z","shell.execute_reply.started":"2024-10-17T01:51:52.313991Z","shell.execute_reply":"2024-10-17T01:51:52.326988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!rm -rf python-packages\n!rm -rf gdcm*","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:52.328866Z","iopub.execute_input":"2024-10-17T01:51:52.329146Z","iopub.status.idle":"2024-10-17T01:51:54.546556Z","shell.execute_reply.started":"2024-10-17T01:51:52.329123Z","shell.execute_reply":"2024-10-17T01:51:54.545170Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Combine Submissions","metadata":{}},{"cell_type":"code","source":"sub1 = pd.read_csv(\"submission1.csv\")\nsub2 = pd.read_csv(\"submission2.csv\")\n\nsub1 = sub1.sort_values(\"row_id\")\nsub2 = sub2.sort_values(\"row_id\")","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:54.548263Z","iopub.execute_input":"2024-10-17T01:51:54.548613Z","iopub.status.idle":"2024-10-17T01:51:54.559997Z","shell.execute_reply.started":"2024-10-17T01:51:54.548584Z","shell.execute_reply":"2024-10-17T01:51:54.559132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub1 = pd.read_csv(\"submission1.csv\")\nsub2 = pd.read_csv(\"submission2.csv\")\nyuji_sub = pd.read_csv('yuji_submission.csv')\n\nsub = pd.concat([sub1, sub2, sub2, yuji_sub, yuji_sub]) # 1:2:2\n# sub = pd.concat([sub1, sub1, sub1, sub2, sub2, sub2, sub2, sub2, yuji_sub, yuji_sub, yuji_sub, yuji_sub, yuji_sub]) # 3:5:5\n# sub = pd.concat([sub1, sub1, sub2, sub2, sub2, sub2, sub2, yuji_sub, yuji_sub, yuji_sub, yuji_sub, yuji_sub]) # 2:5:5\n\nsub = sub.groupby(\"row_id\").mean().reset_index()\n\nsub = insert_missing_rows(sub)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:54.561115Z","iopub.execute_input":"2024-10-17T01:51:54.561489Z","iopub.status.idle":"2024-10-17T01:51:54.580898Z","shell.execute_reply.started":"2024-10-17T01:51:54.561458Z","shell.execute_reply":"2024-10-17T01:51:54.580031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.to_csv(\"submission_before_pp.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:54.582183Z","iopub.execute_input":"2024-10-17T01:51:54.582679Z","iopub.status.idle":"2024-10-17T01:51:54.588715Z","shell.execute_reply.started":"2024-10-17T01:51:54.582642Z","shell.execute_reply":"2024-10-17T01:51:54.587792Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:54.589747Z","iopub.execute_input":"2024-10-17T01:51:54.590027Z","iopub.status.idle":"2024-10-17T01:51:54.609917Z","shell.execute_reply.started":"2024-10-17T01:51:54.590004Z","shell.execute_reply":"2024-10-17T01:51:54.608933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def normalize_probabilities_to_one_numpy(array: np.ndarray) -> np.ndarray:\n    total = np.sum(array)\n    normalized_array = array / total\n    return normalized_array\n\nth = 0\n\nsub['study_id'] = sub.row_id.apply(lambda x: int(x.split('_')[0]))\ndfs = []\nfor id, idf in sub.groupby('study_id'):\n    spinal = idf[idf.row_id.str.contains('spinal')]\n    spinal_severe_max_val = spinal.severe.max()\n    if spinal_severe_max_val > th:\n        max_severe_row_id = spinal[spinal.severe == spinal_severe_max_val].row_id.values[0]\n        idf.loc[idf.row_id == max_severe_row_id, 'severe'] *= 1.25\n        vals = idf.loc[idf.row_id == max_severe_row_id, ['normal_mild', 'moderate', 'severe']].values[0]\n        idf.loc[idf.row_id == max_severe_row_id, ['normal_mild', 'moderate', 'severe']] = normalize_probabilities_to_one_numpy(vals)\n    dfs.append(idf)\n\nsub_pp = pd.concat(dfs)\ndel sub_pp['study_id']","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:54.611119Z","iopub.execute_input":"2024-10-17T01:51:54.611466Z","iopub.status.idle":"2024-10-17T01:51:54.631564Z","shell.execute_reply.started":"2024-10-17T01:51:54.611436Z","shell.execute_reply":"2024-10-17T01:51:54.630672Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_pp.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:54.632763Z","iopub.execute_input":"2024-10-17T01:51:54.633350Z","iopub.status.idle":"2024-10-17T01:51:54.644138Z","shell.execute_reply.started":"2024-10-17T01:51:54.633316Z","shell.execute_reply":"2024-10-17T01:51:54.643294Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_pp","metadata":{"execution":{"iopub.status.busy":"2024-10-17T01:51:54.645264Z","iopub.execute_input":"2024-10-17T01:51:54.645562Z","iopub.status.idle":"2024-10-17T01:51:54.664766Z","shell.execute_reply.started":"2024-10-17T01:51:54.645531Z","shell.execute_reply":"2024-10-17T01:51:54.663915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}