{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":61446,"databundleVersionId":6962461,"sourceType":"competition"},{"sourceId":197062,"sourceType":"modelInstanceVersion","modelInstanceId":168053,"modelId":190392},{"sourceId":199730,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":168053,"modelId":190392}],"dockerImageVersionId":30804,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!rm -rf /kaggle/working/*\n\nimport cv2\nimport gc\nimport os\nimport matplotlib.pyplot as plot\nimport numpy as np\nimport pandas as pd\nimport pickle\nimport tensorflow as tf\n\nfrom collections import deque\nfrom glob import glob\nfrom itertools import starmap\n\n\nCLIP_MIN_RATIO = 0  # 0.001\nCLIP_MAX_RATIO = 0  # 0.001\nCANDIDATE_RATIO = 0.002\nBLOCK_THRESHOLD = 100\n\nPATCH_WIDTH = 64\nPATCH_HEIGHT = 64\nPATCH_DEPTH = 16\ninput_shape = (16, 64, 64, 1)\n\nMODEL_PATH = '/kaggle/input/blood-vessel-segmentation-baseline/tensorflow2/default/3/best_model.keras'\n\ndef conv_block(x, filters):\n    \"\"\"Convolutional block with batch normalization\"\"\"\n    x = tf.keras.layers.Conv3D(filters, 3, padding='same')(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.ReLU()(x)\n\n    x = tf.keras.layers.Conv3D(filters, 3, padding='same')(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.ReLU()(x)\n\n    return x\n\ndef create_memory_efficient_unet3d(input_shape):\n    inputs = tf.keras.Input(shape=input_shape, dtype=tf.float32)\n\n    # Encoder\n    conv1 = conv_block(inputs, 32)\n    pool1 = tf.keras.layers.MaxPooling3D(pool_size=(2, 2, 2))(conv1)\n\n    conv2 = conv_block(pool1, 64)\n    pool2 = tf.keras.layers.MaxPooling3D(pool_size=(2, 2, 2))(conv2)\n\n    conv3 = conv_block(pool2, 128)\n\n    # Decoder\n    up2 = tf.keras.layers.Conv3DTranspose(64, 2, strides=(2, 2, 2), padding='same')(conv3)\n    up2 = tf.keras.layers.concatenate([conv2, up2])\n    conv4 = conv_block(up2, 64)\n\n    up1 = tf.keras.layers.Conv3DTranspose(32, 2, strides=(2, 2, 2), padding='same')(conv4)\n    up1 = tf.keras.layers.concatenate([conv1, up1])\n    conv5 = conv_block(up1, 32)\n\n    outputs = tf.keras.layers.Conv3D(1, 1, activation='sigmoid')(conv5)\n\n    return tf.keras.Model(inputs=[inputs], outputs=[outputs])\n\nmodel = create_memory_efficient_unet3d(input_shape)\nmodel.load_weights(MODEL_PATH)\n\ndef rle_encode(img):\n    pixels = img.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\n\ndef submit(data_folder_path, submission_file):\n    data_paths = tuple(sorted(glob(f\"{data_folder_path}/images/*.tif\")))\n\n    depth = len(data_paths)\n    height, width = np.shape(cv2.imread(data_paths[0], cv2.IMREAD_GRAYSCALE))\n\n    def get_min_max():\n        values = np.zeros((depth, height, width), dtype=np.uint8)\n        for i in range(depth):\n            values[i] = cv2.imread(data_paths[i], cv2.IMREAD_GRAYSCALE)\n\n        values = np.ravel(values)\n        index_min = int((len(values) - 1) * CLIP_MIN_RATIO)\n        index_max = int((len(values) - 1) * (1 - CLIP_MAX_RATIO))\n\n        return np.partition(values, index_min)[index_min] / 255, np.partition(values, index_max)[index_max] / 255\n\n    min, max = get_min_max()\n    print(f\"depth = {depth}\\theight = {height}\\twidth = {width}\\tmin = {min}\\tmax = {max}\")\n\n    def get_predict():\n        result = np.zeros(tuple(starmap(lambda patch_size, size: int(patch_size * np.ceil(size / patch_size)),\n                                        ((PATCH_DEPTH, depth), (PATCH_HEIGHT, height), (PATCH_WIDTH, width)))),\n                          dtype=np.uint16)\n\n        for i in range(0, depth, PATCH_DEPTH):\n            print(f\"z = {i}\")\n            volumetric_image = np.stack(tuple(map(lambda path: (cv2.imread(path, cv2.IMREAD_GRAYSCALE) / 256).astype(np.float32) if path is not None else np.zeros((height, width), dtype=np.float32),\n                                                  map(lambda index: data_paths[index] if index < depth else None,\n                                                      range(i, i + PATCH_DEPTH)))))\n            volumetric_image = np.pad(volumetric_image, ((0, PATCH_DEPTH - np.shape(volumetric_image)[0]), (0, np.shape(result)[1] - np.shape(volumetric_image)[1]), (0, np.shape(result)[2] - np.shape(volumetric_image)[2])))\n\n            volumetric_image[volumetric_image > max] = max\n            volumetric_image[volumetric_image < min] = min\n\n            volumetric_image = (volumetric_image - min) / (max - min)\n\n            for j in range(0, height, PATCH_HEIGHT):\n                for k in range(0, width, PATCH_WIDTH):\n                    x = np.reshape(volumetric_image[:, j: j + PATCH_HEIGHT, k: k + PATCH_WIDTH], (1, PATCH_DEPTH, PATCH_HEIGHT, PATCH_WIDTH, 1))\n                    y = model.predict_on_batch(x)\n\n                    result[i: i + PATCH_DEPTH, j: j + PATCH_HEIGHT, k: k + PATCH_WIDTH] = np.reshape((y * 65535).astype(np.uint16), (PATCH_DEPTH, PATCH_HEIGHT, PATCH_WIDTH))\n\n                    del y, x\n                    gc.collect()\n\n            del volumetric_image\n            gc.collect()\n\n        return result[0: depth, 0: height, 0: width]\n\n    def get_candidate(predict):\n        values = np.ravel(predict)\n        index = int((len(values) - 1) * CANDIDATE_RATIO)\n        threshold = np.partition(values, -index)[-index]\n        \n        result = predict >= threshold\n\n        print(f\"threshold = {threshold / 65535}\\tratio = {np.count_nonzero(result) / (depth * height * width)}\")\n\n        return result\n    \n    def get_blood_vessel(candidate):\n        checked = np.zeros((depth, height, width), dtype=np.uint8)\n        deltas = tuple(filter(lambda x: x != (0, 0, 0),\n                              map(lambda x: (x[0] - 1, x[1] - 1, x[2] - 1),\n                                  zip(*np.where(np.ones((3, 3, 3)))))))\n\n        result = np.zeros((depth, height, width), dtype=np.uint8)\n\n        for start_z, start_y, start_x in zip(*np.where(candidate)):\n            if checked[start_z, start_y, start_x]:\n                continue\n\n            block = np.zeros((depth, height, width), dtype=np.uint8)\n\n            stack = deque()\n            stack.append((start_z, start_y, start_x))\n\n            block[start_z, start_y, start_x] = checked[start_z, start_y, start_x] = True\n\n            n = 1\n            while stack:\n                z, y, x = stack.pop()\n\n                for delta_x, delta_y, delta_z in deltas:\n                    next_z = z + delta_z\n                    next_y = y + delta_y\n                    next_x = x + delta_x\n\n                    if not (0 <= next_z < depth and 0 <= next_y < height and 0 <= next_x < width):\n                        continue\n\n                    if not candidate[next_z, next_y, next_x]:\n                        continue\n\n                    if block[next_z, next_y, next_x]:\n                        continue\n\n                    block[next_z, next_y, next_x] = checked[next_z, next_y, next_x] = True\n                    stack.append((next_z, next_y, next_x))\n\n                    n += 1\n\n            if n >= BLOCK_THRESHOLD:\n                result |= block\n\n                print(f\"n = {n}\\tratio = {np.count_nonzero(result) / (depth * height * width)}\")\n\n        return result\n\n    predict = get_predict()\n\n    # with open(f\"{data_folder_path.split('/')[-1]}_predict.pickle\", mode='wb') as f:\n    #     pickle.dump(predict, f)\n\n    candidate = get_candidate(predict)\n\n    del predict\n    gc.collect()\n\n    # with open(f\"{data_folder_path.split('/')[-1]}_candidate.pickle\", mode='wb') as f:\n    #     pickle.dump(candidate, f)\n\n    blood_vessel = get_blood_vessel(candidate)\n\n    del candidate\n    gc.collect()\n\n    # with open(f\"{data_folder_path.split('/')[-1]}.pickle\", mode='wb') as f:\n    #     pickle.dump(blood_vessel, f)\n\n    for i in range(depth):\n        image = blood_vessel[i]\n\n        print(f\"{data_folder_path.split('/')[-1]}_{data_paths[i].split('/')[-1].split('.')[0]},{rle_encode(image) if np.count_nonzero(image) else '1 0'}\", file=submission_file)\n\n    del blood_vessel\n    gc.collect()\n\n\nwith open('submission.csv', mode='w') as f:\n    print('id,rle', file=f)\n\n    for data_folder_path in sorted(glob('/kaggle/input/blood-vessel-segmentation/test/*')):\n        submit(data_folder_path, f)\n\n    # submit('../input/blood-vessel-segmentation/train/kidney_1_dense', f)\n    # submit('../input/blood-vessel-segmentation/train/kidney_3_sparse', f)\n    # submit('../input/blood-vessel-segmentation/train/kidney_2', f)\n\n\n# submission_data_frame = pd.read_csv('/kaggle/working/submission.csv')\n# submission_data_frame.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-13T12:49:08.879355Z","iopub.execute_input":"2024-12-13T12:49:08.879715Z","iopub.status.idle":"2024-12-13T12:53:44.443074Z","shell.execute_reply.started":"2024-12-13T12:49:08.879650Z","shell.execute_reply":"2024-12-13T12:53:44.442341Z"}},"outputs":[],"execution_count":null}]}