{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Exploratory Data Analysis (EDA)   \nIn statistics, exploratory data analysis is an approach of analyzing data sets to summarize their main characteristics, often using statistical graphics and other data visualization methods. \n\n참고할 링크:    \n* https://www.kaggle.com/isaienkov/pulmonary-embolism-detection-eda\n* https://www.kaggle.com/nitindatta/pulmonary-embolism-dicom-preprocessing-eda\n* https://www.kaggle.com/atharvagundawar/pe-detection-with-keras-model-creation\n* https://www.kaggle.com/omidchehrehgosha/pe-detection-keras\n* https://www.kaggle.com/redwankarimsony/cnn-gru-baseline-stage2-train-inference\n* https://ballentain.tistory.com/53","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"# What is the competition?     \n**Competition** [Link](https://www.kaggle.com/c/rsna-str-pulmonary-embolism-detection/overview/description)      \n> This competition is about identifying and localizing COVID-19 abnormalities on chest radiographs.   \n> In particular, you'll categorize the radiographs as negative for pneumonia or typical, indeterminate, or atypical for COVID-19.   \n> We need to make predictions at both a study (multi-image) and image level.    \n\n**Data fields**\n>- StudyInstanceUID - unique ID for each study (exam) in the data.\n>- SeriesInstanceUID - unique ID for each series within the study.\n>- SOPInstanceUID - unique ID for each image within the study (and data).\n>- pe_present_on_image - image-level, notes whether any form of PE is present on the image.\n>- negative_exam_for_pe - exam-level, whether there are any images in the study that have PE present.\n>- qa_motion - informational, indicates whether radiologists noted an issue with motion in the study.\n>- qa_contrast - informational, indicates whether radiologists noted an issue with contrast in the study.\n>- flow_artifact - informational\n>- rv_lv_ratio_gte_1 - exam-level, indicates whether the RV/LV ratio present in the study is >= 1\n>- rv_lv_ratio_lt_1 - exam-level, indicates whether the RV/LV ratio present in the study is < 1\n>- leftsided_pe - exam-level, indicates that there is PE present on the left side of the images in the study\n>- chronic_pe - exam-level, indicates that the PE in the study is chronic\n>- true_filling_defect_not_pe - informational, indicates a defect that is NOT PE\n>- rightsided_pe - exam-level, indicates that there is PE present on the right side of the images in the study\n>- acute_and_chronic_pe - exam-level, indicates that the PE present in the study is both acute AND chronic\n>- central_pe - exam-level, indicates that there is PE present in the center of the images in the study\n>- indeterminate -exam-level, indicates that while the study is not negative for PE, an ultimate set of exam-level labels could not be created, due to QA issues\n\n**Evaluation Metrics** [Link](https://www.kaggle.com/c/rsna-str-pulmonary-embolism-detection/overview/evaluation) \n> The metric used in this competition is weighted log loss. It is weighted to account for the relative importance of some labels.    \n> There are 9 study-level labels and one image-level label, detailed further on the Data page. \n> Here is no concept of `difficult` classes in VOC 2010(Perhaps VOC 2012 will have that concept.).     \n  You can see more details in [mAP understanding with code and its tips](https://www.kaggle.com/its7171/map-understanding-with-code-and-its-tips) for understanding it.","metadata":{}},{"cell_type":"markdown","source":"# 1. Importing the libraries","metadata":{}},{"cell_type":"code","source":"import os\nfrom os import listdir, mkdir\nimport numpy as np \nimport pandas as pd \nimport glob\nfrom tqdm import tqdm\n\nfrom colorama import Fore, Back, Style\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n#=== pydicom \nimport pydicom\nimport ast\nimport cv2\nfrom pydicom.encaps import defragment_data, encapsulate\n\n#=== tensorflow\nimport tensorflow as tf\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras import losses, optimizers\nfrom tensorflow.keras.utils import Sequence\n\n#=== Suppress warnings \nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2022-01-05T09:12:48.172011Z","iopub.execute_input":"2022-01-05T09:12:48.172264Z","iopub.status.idle":"2022-01-05T09:12:48.179085Z","shell.execute_reply.started":"2022-01-05T09:12:48.172235Z","shell.execute_reply":"2022-01-05T09:12:48.178243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('CV version: ', cv2.__version__)\nprint('Tensorflow version: ', tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2022-01-05T08:59:49.21723Z","iopub.execute_input":"2022-01-05T08:59:49.218661Z","iopub.status.idle":"2022-01-05T08:59:49.223837Z","shell.execute_reply.started":"2022-01-05T08:59:49.218622Z","shell.execute_reply":"2022-01-05T08:59:49.223165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Reading the CSV\nCovid_Train.csv & Covid_Test.csv => train_study_level & train_image_level","metadata":{}},{"cell_type":"code","source":"#=== set path\npath_data = '../input/rsna-str-pulmonary-embolism-detection/'\npath_img_train = path_data + 'train/'\npath_img_test = path_data + 'test/'","metadata":{"execution":{"iopub.status.busy":"2022-01-05T08:59:49.224828Z","iopub.execute_input":"2022-01-05T08:59:49.225205Z","iopub.status.idle":"2022-01-05T08:59:49.237861Z","shell.execute_reply.started":"2022-01-05T08:59:49.225169Z","shell.execute_reply":"2022-01-05T08:59:49.237153Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def find_match_dataset(df_Info, id_data):\n#     fidx = [df_Info.index[df_Info['StudyInstanceUID'] == id_data.iloc[idx]['StudyInstanceUID']].tolist() for idx in range(0, len(id_data))]\n#     print('fidx: ', len(fidx), fidx)\n\n    fidx = df_Info.index[df_Info['StudyInstanceUID'] == id_data.iloc[len(id_data)-1]['StudyInstanceUID']].tolist() \n    return fidx[-1]    \n\ndef split_train_minitest(df_InfoTrain):\n    _, idx_unq = np.unique(df_InfoTrain.StudyInstanceUID, return_index=True)\n    df_Unique = df_InfoTrain.loc[np.sort(idx_unq), ['StudyInstanceUID', 'SeriesInstanceUID']]\n    \n    size_all = len(df_Unique)\n    size_train = np.round(size_all * 0.8).astype(np.int)\n    \n    id_Train = df_Unique.iloc[0:size_train]\n    id_MiniTest = df_Unique.iloc[size_train:size_all]  \n    \n    display(id_Train.head(2))\n    fidx = find_match_dataset(df_InfoTrain, id_Train)\n    \n    df_Train = df_InfoTrain.iloc[0:fidx]\n    df_MiniTest = df_InfoTrain.iloc[fidx:len(df_InfoTrain)]\n    return df_Train, df_MiniTest","metadata":{"execution":{"iopub.status.busy":"2022-01-05T08:59:49.240715Z","iopub.execute_input":"2022-01-05T08:59:49.240897Z","iopub.status.idle":"2022-01-05T08:59:49.249834Z","shell.execute_reply.started":"2022-01-05T08:59:49.240874Z","shell.execute_reply":"2022-01-05T08:59:49.249067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#=== load csv dataset \ndf_InfoTrain = pd.read_csv(path_data + 'train.csv', index_col=None)\ndf_InfoTest = pd.read_csv(path_data + 'test.csv', index_col=None)\ndf_Submission = pd.read_csv(path_data + 'sample_submission.csv', index_col=None)\n\n#=== split train/mini-test\ndf_Train, df_MiniTest = split_train_minitest(df_InfoTrain)\n\nprint(Fore.BLUE, 'All Train shape: ', Style.RESET_ALL, df_InfoTrain.shape, Fore.YELLOW)\nprint(Fore.BLUE, 'All Test shape: ', Style.RESET_ALL, df_InfoTest.shape, Fore.YELLOW)\nprint(Fore.BLUE, 'Train shape: ', Style.RESET_ALL, df_Train.shape, Fore.YELLOW)\nprint(Fore.BLUE, 'Mini Test shape: ', Style.RESET_ALL, df_MiniTest.shape, Fore.YELLOW)\n\n# display(df_InfoTrain.head(5))\ndisplay(df_Train.head(3))\ndisplay(df_MiniTest.head(3))","metadata":{"execution":{"iopub.status.busy":"2022-01-05T08:59:49.25141Z","iopub.execute_input":"2022-01-05T08:59:49.251683Z","iopub.status.idle":"2022-01-05T08:59:54.899127Z","shell.execute_reply.started":"2022-01-05T08:59:49.25165Z","shell.execute_reply":"2022-01-05T08:59:54.898378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Load DICOM image","metadata":{}},{"cell_type":"code","source":"def load_dicom_info(fpath):\n    dcm_file = pydicom.dcmread(fpath)  \n    return dcm_file\n\ndef transform_to_hu(dcm_file):\n    dcm_image = dcm_file.pixel_array   \n    edit_image = dcm_image\n    \n    #=== convert ouside pixel-values to air:\n    #=== I'm using <= -1000 to be sure that other defaults are captured as well\n    edit_image[edit_image <= -1000] = 0\n    \n    #=== convert to HU\n    param_intercept = dcm_file.RescaleIntercept\n    param_slope = dcm_file.RescaleSlope\n        \n    if param_slope != 1:\n        edit_image = edit_image.astype(np.float64) * param_slope + np.int16(param_intercept)\n    return np.asarray(edit_image, dtype=np.float64)  \n\ndef normalize_convert_rgb_image(image):\n    #=== normalization\n    img_ths = np.array(image.copy(), dtype=np.float64)\n    img_ths /= np.max(img_ths)    # (512, 512)\n    \n    #=== convert to rgb\n    img_rgb = np.expand_dims(img_ths, axis=2)   # (512, 512, 1)\n    img_rgb = np.concatenate([img_rgb, img_rgb, img_rgb], axis=2)   # (512, 512, 3)\n    return img_rgb\n\ndef get_dicom(fpath):\n    print('Path: ', fpath)\n    dcm_file = load_dicom_info(fpath)\n    img_dicom = transform_to_hu(dcm_file)\n    img_norm = normalize_convert_rgb_image(img_dicom)\n    return img_norm","metadata":{"execution":{"iopub.status.busy":"2022-01-05T09:19:05.35642Z","iopub.execute_input":"2022-01-05T09:19:05.357113Z","iopub.status.idle":"2022-01-05T09:19:05.366874Z","shell.execute_reply.started":"2022-01-05T09:19:05.357077Z","shell.execute_reply":"2022-01-05T09:19:05.366189Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> # dcm read에서 convert image (img.pixel_array)가 error 생기는 이미지가 있음.\n> # **체크해보기**","metadata":{}},{"cell_type":"code","source":"#=======\nimport SimpleITK as sitk\nzz = sitk.ReadImage ('../input/rsna-str-pulmonary-embolism-detection/train/2bd689f31b73/d20a597a9e8c/d0ab8eb8f0b9.dcm') \ndcm_image = sitk.GetArrayFromImage (zz).squeeze()\nprint('dcm image min: ', np.min(dcm_image), ', max: ', np.max(dcm_image), ', Type: ', dcm_image.dtype, dcm_image.shape)\n\nplt.imshow(dcm_image, cmap=\"gray\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-05T09:20:27.395723Z","iopub.execute_input":"2022-01-05T09:20:27.396013Z","iopub.status.idle":"2022-01-05T09:20:27.607717Z","shell.execute_reply.started":"2022-01-05T09:20:27.395981Z","shell.execute_reply":"2022-01-05T09:20:27.607028Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Show sample dicom image","metadata":{}},{"cell_type":"code","source":"# fpath = path_img_train + df_Train.StudyInstanceUID[0] + '/' + df_Train.SeriesInstanceUID[0] + '/' + 'a098c6594df8.dcm'\nfpath = '../input/rsna-str-pulmonary-embolism-detection/train/2bd689f31b73/d20a597a9e8c/d0ab8eb8f0b9.dcm'\nprint('File Name: ', fpath )\n\ndcm_file = load_dicom_info(fpath)\nprint('Dicom info: ', dcm_file)\n\nimg_dicom = transform_to_hu(dcm_file)\nimg_norm = normalize_convert_rgb_image(img_dicom)\nprint('dcm image min: ', np.min(img_dicom), ', max: ', np.max(img_dicom), ', Type: ', img_dicom.dtype, img_dicom.shape)\nprint('norm image min: ', np.min(img_norm), ', max: ', np.max(img_norm), ', Type: ', img_norm.dtype, img_norm.shape)\n\nfig, ax = plt.subplots(1,3,figsize=(20, 4))\nax[0].set_title(\"CT-scan in HU\")\nax[0].imshow(img_dicom, cmap=\"gray\")\nax[1].set_title(\"CT-scan in Normalization [0-1]\")\nax[1].imshow(img_norm, cmap=\"gray\")\nax[2].set_title(\"HU values distribution\");\nsns.distplot(img_dicom.flatten(), ax=ax[2], color='red', kde_kws=dict(lw=2, ls=\"--\",color='blue'));\nax[2].grid(False)","metadata":{"execution":{"iopub.status.busy":"2022-01-05T09:17:46.048014Z","iopub.execute_input":"2022-01-05T09:17:46.04864Z","iopub.status.idle":"2022-01-05T09:17:46.460422Z","shell.execute_reply.started":"2022-01-05T09:17:46.048599Z","shell.execute_reply":"2022-01-05T09:17:46.458632Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Data Generator","metadata":{}},{"cell_type":"code","source":"#=== Data Generator (Train)\nclass TrainDataGenerator(Sequence):\n    def __init__(self, list_dataset, img_dir, batch_size=32, img_size=(512, 512), img_ch=3, isShuffle=False):\n        self.list_dataset = list_dataset\n        self.img_dir = img_dir\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.img_ch = img_ch\n        self.isShuffle = isShuffle        \n        self.on_epoch_end()\n\n    def __len__(self):\n        self.len_batch = int(np.ceil(len(self.list_dataset) / float(self.batch_size)))\n        return self.len_batch\n\n    def on_epoch_end(self):\n        self.list_dataset = self.list_dataset.reset_index()\n        self.list_label = self.list_dataset[['pe_present_on_image', 'rv_lv_ratio_gte_1', 'rv_lv_ratio_lt_1', 'leftsided_pe', \n                                                 'chronic_pe', 'rightsided_pe', 'acute_and_chronic_pe', 'central_pe', 'indeterminate']].values\n        self.indexes = np.arange(len(self.list_dataset))\n        \n        if self.isShuffle:            \n            np.random.shuffle(self.indexes)\n\n    def __getitem__(self, idx):        \n        # Generate indexes of the batch\n        indexes = self.indexes[idx * self.batch_size:(idx + 1) * self.batch_size] \n        list_cut_dicom = self.list_dataset.loc[indexes, ['StudyInstanceUID', 'SeriesInstanceUID', 'SOPInstanceUID']]\n        list_cut_label = self.list_label[indexes]\n        size_idx = len(indexes)\n        \n        data_img, data_label = self.__data_generation(size_idx, list_cut_dicom.values.tolist(), list_cut_label)\n        return data_img, data_label\n    \n    def __data_generation(self, size_idx, list_img, list_lb):\n        data_img = np.empty((size_idx, *self.img_size, self.img_ch), dtype=np.float64)\n        data_label = np.empty((size_idx, 9), dtype=np.int)\n        \n        for idx, (img, label) in enumerate(zip(list_img, list_lb)):\n            fpath = path_img_train + img[0] + '/' + img[1] + '/' + img[2] + '.dcm'\n            data_img[idx] = get_dicom(fpath)\n            data_label[idx] = label\n        return data_img, data_label\n\n#=== Data Generator (Test)\nclass TestDataGenerator(Sequence):\n    def __init__(self, list_dataset, img_dir, batch_size=32, img_size=(512, 512), img_ch=3):\n        self.list_dataset = list_dataset\n        self.img_dir = img_dir\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.img_ch = img_ch    \n        self.on_epoch_end()\n\n    def __len__(self):\n        self.len_batch = int(np.ceil(len(self.list_dataset) / float(self.batch_size)))\n        return self.len_batch\n\n    def on_epoch_end(self):        \n        self.indexes = np.arange(len(self.list_dataset))\n\n    def __getitem__(self, idx):\n        # Generate indexes of the batch\n        indexes = self.indexes[idx * self.batch_size:(idx + 1) * self.batch_size]              \n        list_cut_dicom = self.list_dataset.loc[indexes, ['StudyInstanceUID', 'SeriesInstanceUID', 'SOPInstanceUID']]\n        size_idx = len(indexes)\n        \n        data_img = self.__data_generation(size_idx, list_cut_dicom.values.tolist())\n        return data_img            \n    \n    def __data_generation(self, size_idx, list_img, list_lb):\n        data_img = np.empty((size_idx, *self.img_size, self.img_ch), dtype=np.float64)\n        \n        for idx, (img) in enumerate(zip(list_img)):\n            fpath = path_img_train + img[0] + '/' + img[1] + '/' + img[2] + '.dcm'\n            data_img[idx] = get_dicom(fpath)\n        return data_img\n    \n# train_generator = TrainDataGenerator(df_Train.copy(), path_img_train, batch_size=2, img_size=(512, 512), img_ch=3, isShuffle=True)\n# for idx, (x, y) in enumerate(train_generator):\n#     print('z1: ', idx, x.shape, y.shape, y)\n#     if idx >= 2:\n#         break;\n\n# mini_test_generator = TrainDataGenerator(df_MiniTest.copy(), path_img_train, batch_size=10, img_size=(512, 512), img_ch=3, isShuffle=False)\n# for idx, (x, y) in enumerate(mini_test_generator):\n#     print('z2: ', idx, x.shape, y.shape, y)\n#     if idx >= 2:\n#         break;\n        \n# test_generator = TestDataGenerator(df_InfoTest.copy(), path_img_test, batch_size=10, img_size=(512, 512), img_ch=3)\n# for idx, (x) in enumerate(test_generator):\n#     print('z3: ', idx, x.shape)\n#     if idx >= 2:\n#         break;","metadata":{"execution":{"iopub.status.busy":"2022-01-05T08:59:56.545774Z","iopub.execute_input":"2022-01-05T08:59:56.546009Z","iopub.status.idle":"2022-01-05T08:59:56.566703Z","shell.execute_reply.started":"2022-01-05T08:59:56.545976Z","shell.execute_reply":"2022-01-05T08:59:56.565772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Defining Model Architecture","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.layers import Input, concatenate, Conv2D, MaxPooling2D, Dropout, Flatten, Dense\n\ndef define_model(img_size):\n    inputs = Input(img_size)\n    out = Conv2D(32, (3, 3), padding='same')(inputs)\n    out = MaxPooling2D((2, 2), strides=(2, 2))(out)\n    out = Flatten()(out)    \n    out = Dropout(0.25)(out)\n    outputs = Dense(64, activation='relu')(out)\n    \n    ppoi = Dense(1, activation='sigmoid', name='pe_present_on_image')(outputs)\n    rlrg1 = Dense(1, activation='sigmoid', name='rv_lv_ratio_gte_1')(outputs)\n    rlrl1 = Dense(1, activation='sigmoid', name='rv_lv_ratio_lt_1')(outputs) \n    lspe = Dense(1, activation='sigmoid', name='leftsided_pe')(outputs)\n    cpe = Dense(1, activation='sigmoid', name='chronic_pe')(outputs)\n    rspe = Dense(1, activation='sigmoid', name='rightsided_pe')(outputs)\n    aacpe = Dense(1, activation='sigmoid', name='acute_and_chronic_pe')(outputs)\n    cnpe = Dense(1, activation='sigmoid', name='central_pe')(outputs)\n    indt = Dense(1, activation='sigmoid', name='indeterminate')(outputs)\n    \n    model = Model(inputs=inputs, outputs={'pe_present_on_image':ppoi,\n                                      'rv_lv_ratio_gte_1':rlrg1,\n                                      'rv_lv_ratio_lt_1':rlrl1,\n                                      'leftsided_pe':lspe,\n                                      'chronic_pe':cpe,\n                                      'rightsided_pe':rspe,\n                                      'acute_and_chronic_pe':aacpe,\n                                      'central_pe':cnpe,\n                                      'indeterminate':indt})    \n    model.compile(optimizer=optimizers.Adam(lr=0.001), loss=losses.binary_crossentropy)    \n    return model","metadata":{"execution":{"iopub.status.busy":"2022-01-05T08:59:56.568337Z","iopub.execute_input":"2022-01-05T08:59:56.56887Z","iopub.status.idle":"2022-01-05T08:59:56.582477Z","shell.execute_reply.started":"2022-01-05T08:59:56.568786Z","shell.execute_reply":"2022-01-05T08:59:56.58179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 6. Training Process","metadata":{}},{"cell_type":"code","source":"#=== setting parameter\nimg_size = (512, 512)\nimg_ch = 3\nbatch_size = 4\n\n#=== data generator\ntrain_generator = TrainDataGenerator(df_Train.copy(), path_img_train, batch_size=batch_size, img_size=img_size, img_ch=img_ch, isShuffle=True)\n\n#=== load & train model\nmodel = define_model((*img_size, img_ch))\n# model.summary()\n\nmodel.fit_generator(\n        generator=train_generator,\n        epochs=3,\n        verbose=2,  # verbose => 0: slient, 1: progress bar, 2: one line per epochs\n        )\n\n# for idx, (x, y) in enumerate(train_generator):\n#     print('idx: ', idx, 'X: ', x.shape, 'y: ', y.shape, y)\n#     hist = model.fit(\n#         x, #Y values are in a dict as there's more than one target for training output\n#         {'pe_present_on_image':y[0],\n#          'rv_lv_ratio_gte_1':y[1],\n#          'rv_lv_ratio_lt_1':y[2],\n#          'leftsided_pe':y[3],\n#          'chronic_pe':y[4],\n#          'rightsided_pe':y[5],\n#          'acute_and_chronic_pe':y[6],\n#          'central_pe':y[7],\n#          'indeterminate':y[8]},\n#         validation_split=0.2,\n#         epochs=3,\n#         batch_size=batch_size,\n#         verbose=2\n#     )","metadata":{"execution":{"iopub.status.busy":"2022-01-05T09:04:26.118353Z","iopub.execute_input":"2022-01-05T09:04:26.118842Z","iopub.status.idle":"2022-01-05T09:04:47.684394Z","shell.execute_reply.started":"2022-01-05T09:04:26.118769Z","shell.execute_reply":"2022-01-05T09:04:47.68303Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 7. Testing Process","metadata":{}},{"cell_type":"code","source":"#=== data generator\nmini_test_generator = TrainDataGenerator(df_MiniTest.copy(), path_img_train, batch_size=batch_size, img_size=img_size, img_ch=img_ch, isShuffle=False)\ntest_generator = TestDataGenerator(df_InfoTest.copy(), path_img_test, batch_size=batch_size, img_size=img_size, img_ch=img_ch)","metadata":{"execution":{"iopub.status.busy":"2022-01-05T09:00:09.075556Z","iopub.status.idle":"2022-01-05T09:00:09.075849Z","shell.execute_reply.started":"2022-01-05T09:00:09.075691Z","shell.execute_reply":"2022-01-05T09:00:09.075714Z"},"trusted":true},"execution_count":null,"outputs":[]}]}