{"cells":[{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","collapsed":true},"cell_type":"markdown","source":"A simple approach for nuclei segmentation using R; keras, tidyverse and EBImage. \n\nSome settings are just for illustration i.e. height, width, number of epochs or the number levels in the U-Net, etc. \n\nLoading required libraries:"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","scrolled":true,"trusted":false},"cell_type":"code","source":"library(keras)\nlibrary(tidyverse)\nlibrary(EBImage)\noptions(EBImage.display = \"raster\") ","execution_count":1,"outputs":[]},{"metadata":{"_cell_guid":"d21df1ab-3820-4775-80bb-dde59af77837","_uuid":"8a768395a615863917c712a8601d5db3add708cc"},"cell_type":"markdown","source":"Defining some parameters:"},{"metadata":{"_cell_guid":"d106de4f-bb9a-4a38-9d01-091352cc1892","_uuid":"6b6e4a1acca55d5d753b2ea1a9cee507fcff1698","trusted":false},"cell_type":"code","source":"TRAIN_PATH = '../input/stage1_train/'\nTEST_PATH = '../input/stage1_test/'\nHEIGHT = 256\nWIDTH = 256\nCHANNELS = 1\nSHAPE = c(WIDTH, HEIGHT, CHANNELS)\nBATCH_SIZE = 16\nEPOCHS = 25","execution_count":7,"outputs":[]},{"metadata":{"_cell_guid":"ed5406a7-e42d-4a43-930c-fd270cf2ef0f","_uuid":"de406590e2278fd8337de0e46f9edb3e4d0e3df0"},"cell_type":"markdown","source":"## Prepare training images\n   \t\nPrepare for reading training data:"},{"metadata":{"_cell_guid":"d6be6cb2-9cc6-4c51-baaf-f2122f269a98","_uuid":"c6ea94a8994993ed0e6a2e93667214821144492b","scrolled":true,"trusted":false},"cell_type":"code","source":"train_data <- read_csv(\"../input/stage1_train_labels.csv\") %>%\n    group_by(ImageId) %>%\n    summarize(EncodedPixels = list(EncodedPixels)) %>%\n    mutate(ImageFile = file.path(TRAIN_PATH, ImageId, \"images\", paste0(ImageId, \".png\")),\n           MaskPath = file.path(TRAIN_PATH, ImageId, \"masks\"),\n           MaskFiles = map(MaskPath, list.files, pattern=\"*.png\", full.names = TRUE),\n           ImageShape =  map(ImageFile, .f = function(file) dim(readImage(file))[1:2]))\ntrain_data %>%\n    glimpse()","execution_count":6,"outputs":[]},{"metadata":{"_cell_guid":"35803fd8-e516-4fb1-81e7-649d043d3b19","_uuid":"3282b99a701585588977f9ae7409ea32401c54bf"},"cell_type":"markdown","source":"I use the encoded the masks in stead of reading them. Note that  for\ndecoding the original image dimensions are required.\n\nHere is a function to decode the run length encoded masks into images, combining all masks in one image (Optionally labelled image is returned):"},{"metadata":{"_cell_guid":"ed14759b-40ca-4a6f-8a99-4699c6d643d1","_uuid":"edcf4178eeb4a5d339d853276af3ebf2cfc79557","trusted":false},"cell_type":"code","source":"rle2masks <- function(encodings, shape, labels = FALSE) {\n\n    ## Convert rle encoded mask to image\n    rle2mask <- function(encoding, shape){\n\n        splitted <- as.integer(str_split(encoding, pattern = \"\\\\s+\", simplify=TRUE))\n        positions <- splitted[seq(1, length(splitted), 2)]\n        lengths <- splitted[seq(2, length(splitted), 2)] - 1\n\n        ## decode\n        mask_indices <- unlist(map2(positions, lengths, function(pos, len) seq.int(pos, pos+len)))\n\n        ## shape as 2D image\n        mask <- numeric(prod(shape))\n        mask[mask_indices] <- 1\n        mask <- matrix(mask, nrow=shape[1], ncol=shape[2], byrow=TRUE)\n        mask\n    }\n\n    if(!labels) {     ##reduce to one image\n        masks <- matrix(0, nrow=shape[1], ncol=shape[2])\n        for(i in 1:length(encodings))\n            masks <- masks + rle2mask(encodings[i], shape)\n    }\n    else {           ##each mask in channel\n        masks <- array(0, dim = c(shape[1], shape[2], length(encodings)))\n        for(i in 1:length(encodings))\n            masks[,,i] <- i*rle2mask(encodings[i], shape)\n    }\n\n    masks\n}","execution_count":8,"outputs":[]},{"metadata":{"_cell_guid":"65f28bc8-e5c8-4191-a4e4-9ab1b24663a3","_uuid":"353fb5677d144ceebdc21d571954b6d3ac0d4a63"},"cell_type":"markdown","source":"A little preprocessing is performed on the original images,\ni.e. resizing, inversion of brightfield images and normalization:\n"},{"metadata":{"_cell_guid":"2b280aee-8f8d-4b70-8cb0-4cc9cec5f87f","_uuid":"fe6e3c8aaef2c7f29b25fc7a6269e1131abcdc39","trusted":false},"cell_type":"code","source":"## Invert Brightfield images: background black/foreground white\ninvert <- function(x) {\n    if(mean(x) > .5)\n        x <- 1 - x\n    x\n}\nto_gray_scale <- function(x) {\n    y <- rgbImage(red = getFrame(x, 1),\n                  green = getFrame(x, 2),\n                  blue = getFrame(x, 3))\n    y <- channel(y, mode=\"luminance\")\n    dim(y) <- c(dim(y), 1)\n    y\n}\n\n## Preprocess original images\npreprocess_image <- function(file, shape){\n    image <- readImage(file, type=\"png\")\n    image <- to_gray_scale(image)                       ## convert to gray scale  \n    image <- resize(image, w = shape[1], h = shape[2])  ## make all images of dimensions\n    image <- clahe(image)                               ## local adaptive contrast enhancement\n    image <- normalize(image)                           ## standardize between [0, 1]\n    image <- invert(image)                              ## invert brightfield\n    imageData(image)                                    ## return as array\n}\n\n## Preprocess masks\npreprocess_masks <- function(encoding, old_shape, new_shape, labels = FALSE){\n    require(EBImage)\n\n    masks <- rle2masks(encoding, old_shape, labels = labels)\n\n    if(any(old_shape[1:2] != new_shape[1:2])) {\n        masks <- resize(masks, w = new_shape[1], h = new_shape[2])\n        if(labels) {\n            for(i in 1:dim(masks)[3]) ##recover labeling after reshaping\n                masks[,,i] <- i*(masks[,,i] > 0)\n        }\n    }\n\n    masks <- Reduce(\"+\", getFrames(masks))\n\n    dim(masks) <- c(dim(masks), 1) ##masks have no color channels\n    masks\n}","execution_count":9,"outputs":[]},{"metadata":{"_cell_guid":"cb33015c-71ef-4d1a-849e-3499f54a33af","_uuid":"3ebc480c6308022922a71a83566409afa75e70ac"},"cell_type":"markdown","source":"The last two preprocessing functions combine all preprocessing steps\n(additional steps are easily added; i.e, global contrast\nnormalization).\n\nExtract a random train image and display:"},{"metadata":{"_cell_guid":"49b4cb11-6cdb-4284-a19b-b26a93e208ba","_uuid":"055619d0501c8b73661f9968a03a591bbf014540","trusted":false},"cell_type":"code","source":"rand_images <- sample_n(train_data, 1) %>%\n    mutate(Y = map2(EncodedPixels, ImageShape, preprocess_masks, new_shape = SHAPE, labels = TRUE),\n           X = map(ImageFile, preprocess_image, shape = SHAPE)) %>%\n    select(X,Y)\nstr(rand_images)","execution_count":10,"outputs":[]},{"metadata":{"_cell_guid":"f03af684-77f8-41db-ac77-6fb9f6402876","_uuid":"bdd54ae298de6dd42c19a7504c2bad1084834afd","trusted":false},"cell_type":"code","source":"display(combine(rand_images$Y[[1]], rand_images$X[[1]]), all = TRUE)\ndisplay(colorLabels(rand_images$Y[[1]]))","execution_count":11,"outputs":[]},{"metadata":{"_cell_guid":"c55bb496-6048-487f-bc6e-0997ee0a9e15","_uuid":"10ae2d863972609118eda781f270cf628741644c"},"cell_type":"markdown","source":"## Define U-Net\n\t\nThe following two functions are used to construct the U-net; a function two construct a general U-Net layer and a function to construct the U-Net itself:\n"},{"metadata":{"_cell_guid":"11ab1d79-593f-4a27-9d47-5d4010ac2276","_uuid":"26b48682de8ecddbb8932d985b7a01cc7e11f8df","trusted":false},"cell_type":"code","source":"## unet 2x2 2DConv layer\nunet_layer <- function(object, filters, kernel_size = c(3, 3),\n                       padding = \"same\", kernel_initializer = \"he_normal\",\n                       dropout = 0.1, activation=\"relu\"){\n\n    object %>%\n        layer_conv_2d(filters = filters, kernel_size = kernel_size, padding = padding) %>%\n        ##layer_batch_normalization() %>%\n        layer_activation(activation) %>%\n        layer_spatial_dropout_2d(rate = dropout) %>%\n        layer_conv_2d(filters = filters, kernel_size = kernel_size, padding = padding) %>%\n        ##layer_batch_normalization() %>%\n        layer_activation(activation)\n}\nunet <- function(shape, nlevels = 4, nfilters = 16, dropouts = c(0.1, 0.1, 0.2, 0.2, 0.3)){\n\n    message(\"Constructing U-Net with \", nlevels, \" levels initial number of filters is: \", nfilters)\n    \n    filter_sizes <- nfilters*2^seq.int(0, nlevels)\n\n    ## Loop over contracting layers\n    clayers <- clayers_pooled <- list()\n\n    ## inputs\n    clayers_pooled[[1]] <- layer_input(shape = shape)\n\n    for(i in 2:(nlevels+1)) {\n        clayers[[i]] <- unet_layer(clayers_pooled[[i - 1]],\n                                   filters = filter_sizes[i - 1],\n                                   dropout = dropouts[i-1])\n\n        clayers_pooled[[i]] <- layer_max_pooling_2d(clayers[[i]],\n                                                    pool_size = c(2, 2),\n                                                    strides = c(2, 2))\n    }\n\n    ## Loop over expanding layers\n    elayers <- list()\n\n    ## center\n    elayers[[nlevels + 1]] <- unet_layer(clayers_pooled[[nlevels + 1]],\n                                         filters = filter_sizes[nlevels + 1],\n                                         dropout = dropouts[nlevels + 1])\n\n    for(i in nlevels:1) {\n        elayers[[i]] <- layer_conv_2d_transpose(elayers[[i+1]],\n                                                filters = filter_sizes[i],\n                                                kernel_size = c(2, 2),\n                                                strides = c(2, 2),\n                                                padding = \"same\")\n\n        elayers[[i]] <- layer_concatenate(list(elayers[[i]], clayers[[i + 1]]), axis = 3)\n        elayers[[i]] <- unet_layer(elayers[[i]], filters = filter_sizes[i], dropout = dropouts[i])\n\n    }\n\n    ## Output layer\n    outputs <- layer_conv_2d(elayers[[1]], filters = 1, kernel_size = c(1, 1), activation = \"sigmoid\")\n\n    return(keras_model(inputs = clayers_pooled[[1]], outputs = outputs))\n}\n","execution_count":12,"outputs":[]},{"metadata":{"_cell_guid":"f1f11652-6d53-41d6-a220-ea116ef2769b","_uuid":"a72944877fa76d9214b1f4b9618d4db55d99cf5c"},"cell_type":"markdown","source":"Define a U-Net model with 3 contracting- and expanding-levels and 16\nfilters in the first convolution layer.\n"},{"metadata":{"_cell_guid":"e01f1c93-f04c-400c-8742-14f883a3cacb","_uuid":"9275c9a1591b41eee456b209c9242756402f016c","scrolled":false,"trusted":false},"cell_type":"code","source":"model <- unet(shape = SHAPE, nlevels = 3, nfilters = 16, dropouts = c(0.1, 0.1, 0.2, 0.3))\nsummary(model)","execution_count":13,"outputs":[]},{"metadata":{"_cell_guid":"ed49d0bb-7f72-48a3-a37c-97e50d658f01","_uuid":"93869779e9a15eecbe7938982a4e828b16887ea4"},"cell_type":"markdown","source":"Defining dice coefficient and its negative as loss function using\nkeras-backend functions (`k_*`).\n"},{"metadata":{"_cell_guid":"495f0263-8216-4cff-858f-0001754976ec","_uuid":"68f8966f72b0afb9ae7a5f8e7febb49fa2e66ecc","trusted":false},"cell_type":"code","source":"dice_coef <- function(y_true, y_pred, smooth = 1.0) {\n    y_true_f <- k_flatten(y_true)\n    y_pred_f <- k_flatten(y_pred)\n    intersection <- k_sum(y_true_f * y_pred_f)\n    (2 * intersection + smooth) / (k_sum(y_true_f) + k_sum(y_pred_f) + smooth)\n}\nattr(dice_coef, \"py_function_name\") <- \"dice_coef\"\n\ndice_coef_loss <- function(y_true, y_pred) -dice_coef(y_true, y_pred)\nattr(dice_coef_loss, \"py_function_name\") <- \"dice_coef_loss\"","execution_count":14,"outputs":[]},{"metadata":{"_cell_guid":"39192098-7fe4-4499-b2c2-fffe60bd16f5","_uuid":"474ab9415f4c1c7af5ba55d9c5a8db6d0c05ae45"},"cell_type":"markdown","source":"The `attr` specification is needed if you want to store the fitted\nmodel for later use!\n\nAdd the metric and loss to the model:\n"},{"metadata":{"_cell_guid":"8d7e43c5-f67d-4757-8c4c-caebdf9ee75e","_uuid":"c2324acf3012530b28435c2cc2aa0d8e72d9abcb","trusted":false},"cell_type":"code","source":"model <- model %>%\n    compile(\n        optimizer = 'adam',\n        loss = dice_coef_loss,\n        metrics = c(dice_coef)\n    )","execution_count":15,"outputs":[]},{"metadata":{"_cell_guid":"3813a9c0-a224-4f6c-aa7e-ea489df22899","_uuid":"3978ab2ca9908edae5af67ad598a038ca1d44c74"},"cell_type":"markdown","source":"## Training the U-Net\n   \t\nExtract all data, shuffled, and convert to tensors:\n"},{"metadata":{"_cell_guid":"77fa2ab5-169e-446d-88cf-d7116a6de0b2","_uuid":"1a24e2d0eaaaac1f512df2f2c3e4ee206b5b1bda","trusted":false},"cell_type":"code","source":"input <- sample_n(train_data, nrow(train_data)) %>%\n    mutate(Y = map2(EncodedPixels, ImageShape, preprocess_masks, new_shape = SHAPE, labels= TRUE),\n           X = map(ImageFile, preprocess_image, shape = SHAPE)) %>%\n    select(X,Y)\n\ninput %>%\n    glimpse()","execution_count":16,"outputs":[]},{"metadata":{"_cell_guid":"4447a6cc-9d95-49ae-932c-cdb9080f9177","_uuid":"6a2fa14d5a6554b59bd44224b5060ddce171c273","trusted":false},"cell_type":"code","source":"list2tensor <- function(xList) {\n    xTensor <- simplify2array(xList)\n    aperm(xTensor, c(4, 1, 2, 3))    \n}\n\nW <- map(input$Y, function(x) {bw <- x > 0; 1*bw}) ##binarize\n         \nX <- list2tensor(input$X)\nY <- list2tensor(input$Y)\nYbw <- list2tensor(W)         \ndim(Ybw)\ndim(Y)\ndim(X)","execution_count":17,"outputs":[]},{"metadata":{"_cell_guid":"136a224d-80fb-4de1-a8ce-07cc8be52165","_uuid":"2c47d55cf962ed51880c02798a16ff11a9e25eec"},"cell_type":"markdown","source":"`input` is a `data.frame`, actually a `tibble`, with so-called\nlist-columns `X` and `Y` each element containing an image. For use\nwith keras/tensorflow these need to be transformed to tensors (samples\nx width x height x channels).\n\nFit the model:"},{"metadata":{"_cell_guid":"54af4710-e92a-4c51-8853-9d4bfbc7a8f8","_kg_hide-input":false,"_uuid":"c96d5841c635f7a43e3d2f79299143d6dc48e01e","trusted":false},"cell_type":"code","source":"early_stopping <- callback_early_stopping(patience = 5)\n\nhistory <- model %>%\n    fit(X, Ybw,\n        batch_size = BATCH_SIZE,\n        epochs = EPOCHS,\n        validation_split = 0.2,\n        verbose = 1,\n        callbacks = list(early_stopping))","execution_count":18,"outputs":[]},{"metadata":{"_cell_guid":"d4c38bce-a08a-41ed-abe1-508af0ada592","_uuid":"facb3b258fa1e88d7309b1a39d4444f25d313e20","scrolled":true,"trusted":false},"cell_type":"code","source":"plot(history)","execution_count":19,"outputs":[]},{"metadata":{"_cell_guid":"6c6cd2ef-357a-404f-a1ac-15247fac2e04","_uuid":"88652a1e6b6acbbd07a6aff3a78261b2b2816d65"},"cell_type":"markdown","source":"Optionally save the model:"},{"metadata":{"_cell_guid":"990c9168-9314-4b65-b848-b372c74d4747","_kg_hide-input":false,"_kg_hide-output":false,"_uuid":"c6983c57c00992a8f1b7b483945f23348bfc2f8e","trusted":false},"cell_type":"code","source":"## save_model_hdf5(model, filepath=\"unet_model.hdf5\")","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"327c5ef8-378a-479e-bb44-3f9a835b66c1","_uuid":"dbd2bdd65cfed5c795e398815ca1334eceda6c69"},"cell_type":"markdown","source":"## Evaluate the fitted model\n\t\nPredict and evaluate on training images: \n"},{"metadata":{"_cell_guid":"4483ec6b-8081-4a7e-95f8-cf4c23912053","_uuid":"9378e6be4c71ddd01556d406559da4286ab39276","trusted":false},"cell_type":"code","source":"Y_hat <- predict(model, x = X)\nY_hat_lab <- map(array_branch(Y_hat, 1), .f = function(z) bwlabel(z > .5)) ##binarize and label\nY_hat_lab <- list2tensor(Y_hat_lab) ","execution_count":33,"outputs":[]},{"metadata":{"_cell_guid":"ca6c1908-3c3f-4e9c-9a77-07cbc88e5b42","_uuid":"0f37bfe7068a4197fd9e4d4874e963baa3b41bd0"},"cell_type":"markdown","source":"Display an original image with given masks:"},{"metadata":{"_cell_guid":"122f1324-e61a-449e-af42-720000875b4b","_uuid":"7dac1b31ec5b8fea6120acfbd4d54260ae7464db","trusted":false},"cell_type":"code","source":"display(combine(Ybw[1,,,], X[1,,,]), all = TRUE)","execution_count":34,"outputs":[]},{"metadata":{"_cell_guid":"98a1e970-d483-48f5-8345-1bd5eb208666","_uuid":"d386a45ac580e66bd32b1641b27e638557af1901"},"cell_type":"markdown","source":"Display original with predicted masks:"},{"metadata":{"_cell_guid":"64c04600-dec4-4e2d-925d-5cbdf4cd8d53","_uuid":"f716e16e4ee401591dff1b243046a7729c963eed","trusted":false},"cell_type":"code","source":"display(combine(Ybw[1,,,], Y_hat[1,,,]), all = TRUE)","execution_count":22,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"35f2b61ffd5ab145b7d62f61586e043868dacf07"},"cell_type":"code","source":"  \ndisplay(colorLabels(combine(Y[1,,,], Y_hat_lab[1,,,])), all = TRUE)","execution_count":37,"outputs":[]},{"metadata":{"_cell_guid":"04560214-6a89-4e83-b356-85a393bfc872","_uuid":"14829e27035d144f8c8fe0325f938525afeba6de"},"cell_type":"markdown","source":"## Intersection over Union and Mean Precision\n\t\nThese functions require labeled images as input!"},{"metadata":{"_cell_guid":"95fc615a-98c0-445a-b8eb-28f62a18d956","_uuid":"196cc24ea583579f466be54ef6bb050eb70dcdee","trusted":false},"cell_type":"code","source":"iou <- function(y_true, y_pred, smooth = 1){\n    y_true <- as.vector(y_true)\n    y_pred <- as.vector(y_pred)\n    nbins <- max(y_true, y_pred) + 1\n    yt <- to_categorical(y_true, num_classes = nbins)[,-1] ##drop background\n    yp <- to_categorical(y_pred, num_classes = nbins)[,-1]\n    intersections <- diag(crossprod(yt, yp))\n    true_sizes <- colSums(yt^2)\n    pred_sizes <- colSums(yp^2)\n    unions <- true_sizes + pred_sizes - intersections\n    (intersections + smooth)/(unions + smooth) ##smoothed\n}\n\nmean_precision <- function(y_true, y_pred, thresholds = seq(0.5, 0.95, 0.05), smooth = 1) {\n    ious <- iou(y_true, y_pred, smooth = smooth)\n    ground_truth <- logical(max(y_true, y_pred))\n    ground_truth[1:max(y_true)] <- TRUE\n    precision <- 0\n    for(t in thresholds) {\n        tp <- sum(ious[ground_truth] > t)\n        fp <- sum(ious[!ground_truth] > t)\n        fn <- sum(ious[ground_truth] <= t)\n        precision <- precision + tp/(tp+fp+fn)\n    }\n    precision/length(thresholds)\n}\n","execution_count":66,"outputs":[]},{"metadata":{"_uuid":"182971183cd79ac00d2243123a5a4a40e2751b5e"},"cell_type":"markdown","source":"Estimate mean precision on the training data:"},{"metadata":{"_cell_guid":"737a00f6-ce3d-4d53-802a-16c5961eab52","_uuid":"29be59207949b0e4a08f93d2a8ca21c605a5fe30","trusted":false},"cell_type":"code","source":"indices <- sample(1:670, 100)\nyl <- array_branch(Y[indices,,,], 1)\nyhl <- array_branch(Y_hat_lab[indices,,,], 1)","execution_count":64,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"1cd677341cab69e178d0a1722173c065a4f8e189"},"cell_type":"code","source":"mp <- map2_dbl(yl, yhl, mean_precision)\nround(mean(mp), 2)\nboxplot(mp)","execution_count":67,"outputs":[]},{"metadata":{"_cell_guid":"a35c2ba2-921e-4a7b-bac2-78ea3edf0385","_uuid":"b33fc7988e5ae1da39a63d905720d80fcc2ab31d"},"cell_type":"markdown","source":"## Perform predictions on the test images\n   \t\nPrepare for reading stage I test data:\n"},{"metadata":{"_cell_guid":"19694b9c-2a4f-45b0-8719-45bbcc3ceb16","_uuid":"1857994aa9bedf8e05169318bf85a0cc3cfa2e3e","trusted":false},"cell_type":"code","source":"test_data <- tibble(ImageId = dir(TEST_PATH)) %>%\n    mutate(ImageFile = file.path(TEST_PATH, ImageId, \"images\", paste0(ImageId, \".png\")),\n           ImageShape =  map(ImageFile, .f = function(file) dim(readImage(file))[1:2]),\n           X = map(ImageFile, preprocess_image, shape = SHAPE))\ntest_data %>%\n    glimpse()","execution_count":68,"outputs":[]},{"metadata":{"_cell_guid":"e1d9b235-a623-48a6-aaa4-dc27b4afe9d1","_uuid":"5fd6d1eca89dc5920cd4108cd80f70066ebf93be"},"cell_type":"markdown","source":"Extract the test images and predict:\t"},{"metadata":{"_cell_guid":"b2217a5b-e254-4e50-9e5f-13ebc43f7d46","_uuid":"2c87b648c563bad4943ad34e6c5f6f9c33679f40","trusted":false},"cell_type":"code","source":"X <- list2tensor(test_data$X)\nY_hat <- predict(model, x = X)\ndisplay(combine(X[1,,,], Y_hat[1,,,]), all = TRUE)","execution_count":69,"outputs":[]},{"metadata":{"_cell_guid":"34e45843-8e94-4537-b790-755790cbb30c","_uuid":"3a5b4e51929c1c38e43dfc26c50a742a4ad5326d"},"cell_type":"markdown","source":"Perform encoding:"},{"metadata":{"_cell_guid":"3afbc781-2e92-4eea-a197-4199226e20da","_uuid":"d45a3965b8549a9c47b9bd96e6e248f9330c30d9","trusted":false},"cell_type":"code","source":"image2rle <- function(image){\n\n    labels <- 1:max(image) ## assuming background  == 0\n\n    x <- as.vector(t(image))\n\n    encoding <- rle(x)\n\n    ## Adding start positions\n    encoding$positions <- 1 + c(0, cumsum(encoding$lengths[-length(encoding$lengths)]))\n    \n    mask2rle <- function(label, enc) {\n        indices <- enc$values == label\n        list(position = enc$positions[indices][1],\n             encoding = paste(enc$positions[indices], enc$lengths[indices], collapse=\" \"))\n    }\n\n    ##return encodings with increasing positions\n    map_df(labels, mask2rle, encoding) %>%\n        arrange(position) %>%\n        pull(encoding)\n}\n\npostprocess_image <- function(image, shape){\n    image <- resize(image[,,1], w = shape[1], h = shape[2])\n    image <- bwlabel(image > .5) ##binarize and label\n    image2rle(image)\n}\n","execution_count":70,"outputs":[]},{"metadata":{"_cell_guid":"031cd416-0229-4146-9d0a-ec330b1ca658","_uuid":"c4d3dbcab60260e0fe7bad1cd108557e4eb4f42e"},"cell_type":"markdown","source":"The post processing steps include; resizing to original image\ndimension, label segmented image and perform run length encoding. \n\nCheck the encoding-decoding by display the images.\n"},{"metadata":{"_cell_guid":"3b486d04-4003-46ac-a24e-0413856e4591","_uuid":"f9253c0311ae68307c6b19fe011a8eee9a33d49e","scrolled":false,"trusted":false},"cell_type":"code","source":"submission <- test_data %>%\n    add_column(Masks = array_branch(Y_hat, 1)) %>%\n    mutate(EncodedPixels = map2(Masks, ImageShape, postprocess_image))\n\nrsamples <- sample_n(submission, 1) %>%\n    mutate(Y = map2(EncodedPixels, ImageShape, preprocess_masks, new_shape = SHAPE))\n\nZ <- list2tensor(rsamples$Masks)\nX <- list2tensor(rsamples$X)\nY <- list2tensor(rsamples$Y)\n\ndisplay(combine(Z[1,,,], Y[1,,,], X[1,,,]), all = TRUE, nx = 3)\n\nsubmission <- submission %>%\n    unnest(EncodedPixels) %>%\n    mutate(EncodedPixels = as.character(EncodedPixels)) %>%\n    select(ImageId, EncodedPixels)                           ","execution_count":72,"outputs":[]},{"metadata":{"_cell_guid":"2eebf052-6bc5-4682-8ea9-e9eeed1d9f3c","_uuid":"9b8e29868f5b3a51c2b163d464a5741eba81530e"},"cell_type":"markdown","source":"Submit:"},{"metadata":{"_cell_guid":"b937fd06-a7de-4f36-bc1b-8ef4c4b07114","_uuid":"620c7a3be03adc3a2eba06ddc6c0e3f180a210be","trusted":false},"cell_type":"code","source":"submission %>%\n    glimpse()\nwrite_csv(submission, \"submission.csv\")\n","execution_count":73,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"R","language":"R","name":"ir"},"language_info":{"codemirror_mode":"r","file_extension":".r","mimetype":"text/x-r-source","name":"R","pygments_lexer":"r","version":"3.4.2"}},"nbformat":4,"nbformat_minor":1}