{"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":"none","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"},{"sourceId":9220769,"sourceType":"datasetVersion","datasetId":5566426},{"sourceId":9257708,"sourceType":"datasetVersion","datasetId":5597030}],"dockerImageVersionId":30746,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# https://www.kaggle.com/code/hengck23/ver-1-demo-workflow-2-stage-approach\n# Adaptation to my pipeline","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:07.592037Z","iopub.execute_input":"2024-08-27T12:56:07.593783Z","iopub.status.idle":"2024-08-27T12:56:07.600528Z","shell.execute_reply.started":"2024-08-27T12:56:07.593696Z","shell.execute_reply":"2024-08-27T12:56:07.599135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%script false --no-raise-error\n\n'''\nThis is a demo workflow. It has following the limitations:\n\n1. We use only sagittal_t2 and axial_t2. \n   Future version will use sagittal_t1.\n\n2. We assume each view (sagittal_t2, axial_t2) has only one series_id.\n   Future version will allow multiple series_id.\n \n3. the level assignent to axial slices is still under experiment. \nWhen there are more than one possible assigments (when the slices overlap), it is not known\nif multiple or single assignment work better,\n'''","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:07.604066Z","iopub.execute_input":"2024-08-27T12:56:07.604678Z","iopub.status.idle":"2024-08-27T12:56:07.634272Z","shell.execute_reply.started":"2024-08-27T12:56:07.604632Z","shell.execute_reply":"2024-08-27T12:56:07.632575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"try:\n    import natsort\nexcept:\n    !pip install '/kaggle/input/hengck23-ver-1-demo-workflow-2-stage-approach/natsort-8.4.0-py3-none-any.whl'\n","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:07.636382Z","iopub.execute_input":"2024-08-27T12:56:07.636909Z","iopub.status.idle":"2024-08-27T12:56:07.686980Z","shell.execute_reply.started":"2024-08-27T12:56:07.636875Z","shell.execute_reply":"2024-08-27T12:56:07.685224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys, os\nsys.path.append('/kaggle/input/hengck23-ver-1-demo-workflow-2-stage-approach')\n\nfrom _dir_setting_ import *\nprint('NOT_KAGGLE:', NOT_KAGGLE)\nprint('DATA_KAGGLE_DIR:', DATA_KAGGLE_DIR)\n\n\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nfrom helper import *\nfrom data import *\n\nimport torch.nn as nn\nimport torch\n\nprint('import ok!')","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:07.689308Z","iopub.execute_input":"2024-08-27T12:56:07.689855Z","iopub.status.idle":"2024-08-27T12:56:10.763045Z","shell.execute_reply.started":"2024-08-27T12:56:07.689809Z","shell.execute_reply":"2024-08-27T12:56:10.761825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install segmentation_models_pytorch\nimport segmentation_models_pytorch as smp","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:10.766432Z","iopub.execute_input":"2024-08-27T12:56:10.766954Z","iopub.status.idle":"2024-08-27T12:56:30.668167Z","shell.execute_reply.started":"2024-08-27T12:56:10.766920Z","shell.execute_reply":"2024-08-27T12:56:30.667162Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"PATCH_SIZE = 512","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:30.669541Z","iopub.execute_input":"2024-08-27T12:56:30.669861Z","iopub.status.idle":"2024-08-27T12:56:30.675205Z","shell.execute_reply.started":"2024-08-27T12:56:30.669834Z","shell.execute_reply":"2024-08-27T12:56:30.673939Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# STEP0 : SETUP DATA ==========================\n\ncfg=dotdict(\n    point_net=dotdict(\n        checkpoint='/kaggle/input/rsna2024-demo-workflow/00002484.pth',\n        image_size=PATCH_SIZE,\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}/train_series_descriptions.csv')\n#valid_id = [ 113758629, 13317052, 60612428, 74294498, 142991438, 168833126, 189360935, 58813022, ] #these are not used in training\nvalid_id = id_df.study_id.unique()\n","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:30.676671Z","iopub.execute_input":"2024-08-27T12:56:30.676999Z","iopub.status.idle":"2024-08-27T12:56:30.705989Z","shell.execute_reply.started":"2024-08-27T12:56:30.676972Z","shell.execute_reply":"2024-08-27T12:56:30.704804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_coor = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_label_coordinates.csv')\ndf_coor.tail()","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:30.707531Z","iopub.execute_input":"2024-08-27T12:56:30.707877Z","iopub.status.idle":"2024-08-27T12:56:30.824566Z","shell.execute_reply.started":"2024-08-27T12:56:30.707848Z","shell.execute_reply":"2024-08-27T12:56:30.823396Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S = df_coor[\n    df_coor['condition'] == 'Spinal Canal Stenosis'\n].sort_values([\n    'study_id',\n    'series_id',\n    'level'\n]).reset_index(drop=True)\nS.tail()","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:30.826203Z","iopub.execute_input":"2024-08-27T12:56:30.826636Z","iopub.status.idle":"2024-08-27T12:56:30.860369Z","shell.execute_reply.started":"2024-08-27T12:56:30.826598Z","shell.execute_reply":"2024-08-27T12:56:30.859163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S['x_mean_fraction'] = S['x']/S.groupby(['study_id','series_id'])['x'].mean().loc[[(study_id,series_id) for study_id,series_id in S[['study_id','series_id']].values]].values\nS.tail()","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:30.861823Z","iopub.execute_input":"2024-08-27T12:56:30.862206Z","iopub.status.idle":"2024-08-27T12:56:30.983734Z","shell.execute_reply.started":"2024-08-27T12:56:30.862176Z","shell.execute_reply":"2024-08-27T12:56:30.982495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.boxplot(S['x_mean_fraction'])","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:30.985380Z","iopub.execute_input":"2024-08-27T12:56:30.985742Z","iopub.status.idle":"2024-08-27T12:56:31.246007Z","shell.execute_reply.started":"2024-08-27T12:56:30.985713Z","shell.execute_reply":"2024-08-27T12:56:31.244896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S[S['x_mean_fraction'] < .8]","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:31.247738Z","iopub.execute_input":"2024-08-27T12:56:31.248770Z","iopub.status.idle":"2024-08-27T12:56:31.268877Z","shell.execute_reply.started":"2024-08-27T12:56:31.248725Z","shell.execute_reply":"2024-08-27T12:56:31.267639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S.loc[S['x_mean_fraction'] < .8,['x','y']] = torch.nan","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:31.270323Z","iopub.execute_input":"2024-08-27T12:56:31.270720Z","iopub.status.idle":"2024-08-27T12:56:31.282660Z","shell.execute_reply.started":"2024-08-27T12:56:31.270689Z","shell.execute_reply":"2024-08-27T12:56:31.281537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S = S[[\n    'study_id',\n    'series_id',\n    'level',\n    'x',\n    'y'\n]].groupby(['study_id','series_id','level']).mean().reset_index()\nS.tail()","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:31.289268Z","iopub.execute_input":"2024-08-27T12:56:31.289990Z","iopub.status.idle":"2024-08-27T12:56:31.314117Z","shell.execute_reply.started":"2024-08-27T12:56:31.289925Z","shell.execute_reply":"2024-08-27T12:56:31.312961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"centers = {}\nfor i in range(len(S)):\n    row = S.iloc[i]\n    centers[row['study_id']]={}\nfor i in range(len(S)):\n    row = S.iloc[i]\n    centers[row['study_id']][row['series_id']]={'L1/L2':[], 'L2/L3':[],'L3/L4':[],'L4/L5':[],'L5/S1':[]}\nfor i in range(len(S)):\n    row = S.iloc[i]\n    centers[row['study_id']][row['series_id']][row['level']].append([row['x'],row['y']])","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:31.315750Z","iopub.execute_input":"2024-08-27T12:56:31.316757Z","iopub.status.idle":"2024-08-27T12:56:33.685368Z","shell.execute_reply.started":"2024-08-27T12:56:31.316714Z","shell.execute_reply":"2024-08-27T12:56:33.684429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coordinates = np.zeros((len(S),10))\ncoordinates[:] = np.nan\nfor i in range(len(S)):\n    row = S.iloc[i]\n    for level in centers[row['study_id']][row['series_id']]:\n            if len(centers[row['study_id']][row['series_id']][level]) > 0:\n                center = np.array(centers[row['study_id']][row['series_id']][level]).mean(0)\n                coordinates[\n                    i,\n                    {'L1/L2':0, 'L2/L3':2,'L3/L4':4,'L4/L5':6,'L5/S1':8}[level]:{'L1/L2':0, 'L2/L3':2,'L3/L4':4,'L4/L5':6,'L5/S1':8}[level]+2\n                ] = center","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:33.686613Z","iopub.execute_input":"2024-08-27T12:56:33.686962Z","iopub.status.idle":"2024-08-27T12:56:37.045788Z","shell.execute_reply.started":"2024-08-27T12:56:33.686935Z","shell.execute_reply":"2024-08-27T12:56:37.044722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S = S[[\n    'study_id',\n    'series_id'   \n]]\nS[[\n    'x_L1L2',\n    'y_L1L2',\n    'x_L2L3',\n    'y_L2L3',\n    'x_L3L4',\n    'y_L3L4',\n    'x_L4L5',\n    'y_L4L5',\n    'x_L5S1',\n    'y_L5S1'\n]] = coordinates\nS = S.drop_duplicates().reset_index(drop=True)\nS.tail()","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.047715Z","iopub.execute_input":"2024-08-27T12:56:37.048125Z","iopub.status.idle":"2024-08-27T12:56:37.083603Z","shell.execute_reply.started":"2024-08-27T12:56:37.048078Z","shell.execute_reply":"2024-08-27T12:56:37.082429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import math","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.084810Z","iopub.execute_input":"2024-08-27T12:56:37.085159Z","iopub.status.idle":"2024-08-27T12:56:37.090445Z","shell.execute_reply.started":"2024-08-27T12:56:37.085123Z","shell.execute_reply":"2024-08-27T12:56:37.089245Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in []:#range(len(S)):\n    row = S.iloc[i]\n    print(row.values.astype(int))\n    sagittal_t2_id = row['series_id']\n    centers = torch.as_tensor([x for x in row[[\n        'x_L1L2',\n        'y_L1L2',\n        'x_L2L3',\n        'y_L2L3',\n        'x_L3L4',\n        'y_L3L4',\n        'x_L4L5',\n        'y_L4L5',\n        'x_L5S1',\n        'y_L5S1'\n    ]]]).view(5,2).float()\n        \n    sample = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/'\n    sample = sample+str(int(row['study_id']))+'/'+str(int(row['series_id']))\n    \n    images = [x for x in glob.glob(sample+'/*.dcm')]\n    images.sort(key = lambda x:int(x.split('/')[-1].replace('.dcm','')))\n#   Trust central slices only, cut 2/5 to head and tail\n    head = tail = (2*len(images))//5\n    images = images[head:-tail]\n        \n    image = np.stack([pydicom.dcmread(x).pixel_array for x in images])\n    D,H,W = image.shape\n    centers[:,0] = centers[:,0]*512/W\n    centers[:,1] = centers[:,1]*512/H\n    image = resize_volume(image,cfg.point_net.image_size)\n    x_map = torch.stack([torch.arange(512)]*512).float()\n    y_map = torch.stack([torch.arange(512)]*512).T.float()\n    idx_map = torch.stack([x_map,y_map]).view(1,2,512,512)\n    s2 = torch.as_tensor([512/8]*5)\n#   Then the corresponding alphas and normalization constants would be\n    A = -1/(2*s2)\n    K = 1/torch.sqrt(2*math.pi*s2)\n#   Ideal heatmaps\n    mask = idx_map - centers.view(5,2,1,1)\n    mask = torch.exp((A.view(5,1,1,1)*mask*mask).sum(1))*100\n    probability = np.zeros((6,512,512))\n    probability[1:] = mask\n    img = np.float32(image[len(image)//2])\n    p = probability_to_rgb(probability)\n    m = cv2.cvtColor(\n                    img,\n                    cv2.COLOR_GRAY2BGR\n    )\n    m = 255 - (255 - m * 0.8) * (1 - p / 255)\n\n    plt.imshow(m / 255)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.091837Z","iopub.execute_input":"2024-08-27T12:56:37.092218Z","iopub.status.idle":"2024-08-27T12:56:37.110106Z","shell.execute_reply.started":"2024-08-27T12:56:37.092175Z","shell.execute_reply":"2024-08-27T12:56:37.108901Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Basic flow:\n- We use affine transformation for projection of dicom array's 2d points into 3d world corrdinates. See our previous notebook: https://www.kaggle.com/code/hengck23/2d-to-3d-projection-for-dicom\n\n- Given sagittal volume, choose central slice z.\n- Use a 2d unet to predict the (x,y) coordinates of the 5 level key points (spinal_canal_stenosis label points)\n- Project x,y,z inpto word coordinates xx,yy,zz.\n- Now given the axial volume, compute the level (l1_l2, ..., l5_s1) for each slice, using their distances from xx,yy,zz points.\n\n","metadata":{}},{"cell_type":"code","source":"class myUNet(nn.Module):\n    def __init__(self):\n        super(myUNet, self).__init__()\n\n        self.UNet = smp.Unet(\n            encoder_name=\"resnet18\",\n            classes=5,\n            in_channels=1\n        ).to(device)\n\n    def forward(self,X):\n        x = self.UNet(X)\n#       MinMaxScaling along the class plane to generate a heatmap\n        min_values = x.view(-1,5,PATCH_SIZE*PATCH_SIZE).min(-1)[0].view(-1,5,1,1)\n        max_values = x.view(-1,5,PATCH_SIZE*PATCH_SIZE).max(-1)[0].view(-1,5,1,1)\n        x = (x - min_values)/(max_values - min_values)\n        \n        return x","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.111773Z","iopub.execute_input":"2024-08-27T12:56:37.112639Z","iopub.status.idle":"2024-08-27T12:56:37.127269Z","shell.execute_reply.started":"2024-08-27T12:56:37.112598Z","shell.execute_reply":"2024-08-27T12:56:37.126098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = 'cuda' if torch.cuda.is_available() else 'cpu'","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.128651Z","iopub.execute_input":"2024-08-27T12:56:37.129063Z","iopub.status.idle":"2024-08-27T12:56:37.142990Z","shell.execute_reply.started":"2024-08-27T12:56:37.129033Z","shell.execute_reply":"2024-08-27T12:56:37.141845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = torch.load('/kaggle/input/sagittal-t2-workflow/Sagittal_T2_segmentation_1',map_location=torch.device(device))","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.144391Z","iopub.execute_input":"2024-08-27T12:56:37.144704Z","iopub.status.idle":"2024-08-27T12:56:37.290079Z","shell.execute_reply.started":"2024-08-27T12:56:37.144678Z","shell.execute_reply":"2024-08-27T12:56:37.288885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S = S[S.isna().sum(1)==0].reset_index(drop=True)\nS.tail()","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.291656Z","iopub.execute_input":"2024-08-27T12:56:37.292497Z","iopub.status.idle":"2024-08-27T12:56:37.314114Z","shell.execute_reply.started":"2024-08-27T12:56:37.292462Z","shell.execute_reply":"2024-08-27T12:56:37.312889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tqdm","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:56:37.315610Z","iopub.execute_input":"2024-08-27T12:56:37.316007Z","iopub.status.idle":"2024-08-27T12:56:37.324675Z","shell.execute_reply.started":"2024-08-27T12:56:37.315976Z","shell.execute_reply":"2024-08-27T12:56:37.323616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"assignments = {}\nfor i in tqdm.tqdm(range(len(S))):\n    row = S.iloc[i]\n    study_id = int(row['study_id'])\n    axial_t2_ids    = id_df[(id_df.study_id == int(row['study_id'])) & (id_df.series_description=='Axial T2')].series_id\n    sagittal_t2_id = int(row['series_id'])\n    for axial_t2_id in axial_t2_ids:\n        try:\n                sagittal_t2_point = torch.as_tensor([x for x in row[[\n                    'x_L1L2',\n                    'y_L1L2',\n                    'x_L2L3',\n                    'y_L2L3',\n                    'x_L3L4',\n                    'y_L3L4',\n                    'x_L4L5',\n                    'y_L4L5',\n                    'x_L5S1',\n                    'y_L5S1'\n                ]]]).view(5,2).float()\n\n\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\n                D,H,W = sagittal_t2.shape\n                \n                sagittal_t2_point[:,0] = sagittal_t2_point[:,0]*512/W\n                sagittal_t2_point[:,1] = sagittal_t2_point[:,1]*512/H\n                \n                sagittal_t2_z = D//2            \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 = view_to_world(sagittal_t2_point, sagittal_t2_z, sagittal_t2_df, cfg.point_net.image_size)\n                assigned_level, closest_z, dis  = axial_t2_level = point_to_level(world_point, axial_t2_df)\n                \n                key = str(study_id)+'_'+str(sagittal_t2_id)+'_'+str(axial_t2_id)\n                assignments[key] = {1:{},2:{},3:{},4:{},5:{}}\n                for k in range(5):\n                    assignments[key][k + 1]['dis'] = dis[k][assigned_level == k + 1]\n                    assignments[key][k + 1]['instance_numbers'] = axial_t2_df.instance_number[assigned_level == k + 1].values\n\n                if 0:\n                    print('assigned_level:', assigned_level)\n                    ###################################################################\n                    #visualisation\n                    # https://matplotlib.org/stable/gallery/mplot3d/mixed_subplots.html\n                    fig = plt.figure(figsize=(23, 6))\n                    ax2 = fig.add_subplot(1, 2, 2, projection='3d')\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            print(study_id,sagittal_t2_id,axial_t2_id)","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:59:05.879233Z","iopub.execute_input":"2024-08-27T12:59:05.879747Z","iopub.status.idle":"2024-08-27T12:59:07.276325Z","shell.execute_reply.started":"2024-08-27T12:59:05.879710Z","shell.execute_reply":"2024-08-27T12:59:07.274891Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pickle","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:57:33.190872Z","iopub.execute_input":"2024-08-27T12:57:33.191972Z","iopub.status.idle":"2024-08-27T12:57:33.197149Z","shell.execute_reply.started":"2024-08-27T12:57:33.191932Z","shell.execute_reply":"2024-08-27T12:57:33.195921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open('level_assignments.pkl', 'wb') as f:\n    pickle.dump(assignments, f)","metadata":{"execution":{"iopub.status.busy":"2024-08-27T12:57:36.611049Z","iopub.execute_input":"2024-08-27T12:57:36.611994Z","iopub.status.idle":"2024-08-27T12:57:36.623997Z","shell.execute_reply.started":"2024-08-27T12:57:36.611954Z","shell.execute_reply":"2024-08-27T12:57:36.622952Z"},"trusted":true},"execution_count":null,"outputs":[]}]}