{"cells":[{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"fbacaea4-e039-c417-e6bf-e498135fdb43"},"outputs":[],"source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport cv2\nimport pandas as pd\nfrom shapely.wkt import loads as wkt_loads\nimport tifffile as tiff\nimport os\nimport random\nfrom keras.models import Model\nfrom keras.layers import Input, merge, Convolution2D, MaxPooling2D, UpSampling2D, Reshape, core, Dropout\nfrom keras.optimizers import Adam\nfrom keras.callbacks import ModelCheckpoint, LearningRateScheduler\nfrom keras import backend as K\nfrom sklearn.metrics import jaccard_similarity_score\nfrom shapely.geometry import MultiPolygon, Polygon\nimport shapely.wkt\nimport shapely.affinity\nfrom collections import defaultdict\n\nN_Cls = 10\ninDir = '/home/n01z3/dataset/dstl'\nDF = pd.read_csv(inDir + '/train_wkt_v4.csv')\nGS = pd.read_csv(inDir + '/grid_sizes.csv', names=['ImageId', 'Xmax', 'Ymin'], skiprows=1)\nSB = pd.read_csv(os.path.join(inDir, 'sample_submission.csv'))\nISZ = 160\nsmooth = 1e-12\n\n\ndef _convert_coordinates_to_raster(coords, img_size, xymax):\n    # __author__ = visoft\n    # https://www.kaggle.com/visoft/dstl-satellite-imagery-feature-detection/export-pixel-wise-mask\n    Xmax, Ymax = xymax\n    H, W = img_size\n    W1 = 1.0 * W * W / (W + 1)\n    H1 = 1.0 * H * H / (H + 1)\n    xf = W1 / Xmax\n    yf = H1 / Ymax\n    coords[:, 1] *= yf\n    coords[:, 0] *= xf\n    coords_int = np.round(coords).astype(np.int32)\n    return coords_int\n\n\ndef _get_xmax_ymin(grid_sizes_panda, imageId):\n    # __author__ = visoft\n    # https://www.kaggle.com/visoft/dstl-satellite-imagery-feature-detection/export-pixel-wise-mask\n    xmax, ymin = grid_sizes_panda[grid_sizes_panda.ImageId == imageId].iloc[0, 1:].astype(float)\n    return (xmax, ymin)\n\n\ndef _get_polygon_list(wkt_list_pandas, imageId, cType):\n    # __author__ = visoft\n    # https://www.kaggle.com/visoft/dstl-satellite-imagery-feature-detection/export-pixel-wise-mask\n    df_image = wkt_list_pandas[wkt_list_pandas.ImageId == imageId]\n    multipoly_def = df_image[df_image.ClassType == cType].MultipolygonWKT\n    polygonList = None\n    if len(multipoly_def) > 0:\n        assert len(multipoly_def) == 1\n        polygonList = wkt_loads(multipoly_def.values[0])\n    return polygonList\n\n\ndef _get_and_convert_contours(polygonList, raster_img_size, xymax):\n    # __author__ = visoft\n    # https://www.kaggle.com/visoft/dstl-satellite-imagery-feature-detection/export-pixel-wise-mask\n    perim_list = []\n    interior_list = []\n    if polygonList is None:\n        return None\n    for k in range(len(polygonList)):\n        poly = polygonList[k]\n        perim = np.array(list(poly.exterior.coords))\n        perim_c = _convert_coordinates_to_raster(perim, raster_img_size, xymax)\n        perim_list.append(perim_c)\n        for pi in poly.interiors:\n            interior = np.array(list(pi.coords))\n            interior_c = _convert_coordinates_to_raster(interior, raster_img_size, xymax)\n            interior_list.append(interior_c)\n    return perim_list, interior_list\n\n\ndef _plot_mask_from_contours(raster_img_size, contours, class_value=1):\n    # __author__ = visoft\n    # https://www.kaggle.com/visoft/dstl-satellite-imagery-feature-detection/export-pixel-wise-mask\n    img_mask = np.zeros(raster_img_size, np.uint8)\n    if contours is None:\n        return img_mask\n    perim_list, interior_list = contours\n    cv2.fillPoly(img_mask, perim_list, class_value)\n    cv2.fillPoly(img_mask, interior_list, 0)\n    return img_mask\n\n\ndef generate_mask_for_image_and_class(raster_size, imageId, class_type, grid_sizes_panda=GS, wkt_list_pandas=DF):\n    # __author__ = visoft\n    # https://www.kaggle.com/visoft/dstl-satellite-imagery-feature-detection/export-pixel-wise-mask\n    xymax = _get_xmax_ymin(grid_sizes_panda, imageId)\n    polygon_list = _get_polygon_list(wkt_list_pandas, imageId, class_type)\n    contours = _get_and_convert_contours(polygon_list, raster_size, xymax)\n    mask = _plot_mask_from_contours(raster_size, contours, 1)\n    return mask\n\n\ndef M(image_id):\n    # __author__ = amaia\n    # https://www.kaggle.com/aamaia/dstl-satellite-imagery-feature-detection/rgb-using-m-bands-example\n    filename = os.path.join(inDir, 'sixteen_band', '{}_M.tif'.format(image_id))\n    img = tiff.imread(filename)\n    img = np.rollaxis(img, 0, 3)\n    return img\n\n\ndef stretch_n(bands, lower_percent=5, higher_percent=95):\n    out = np.zeros_like(bands)\n    n = bands.shape[2]\n    for i in range(n):\n        a = 0  # np.min(band)\n        b = 1  # np.max(band)\n        c = np.percentile(bands[:, :, i], lower_percent)\n        d = np.percentile(bands[:, :, i], higher_percent)\n        t = a + (bands[:, :, i] - c) * (b - a) / (d - c)\n        t[t < a] = a\n        t[t > b] = b\n        out[:, :, i] = t\n\n    return out.astype(np.float32)\n\n\ndef jaccard_coef(y_true, y_pred):\n    # __author__ = Vladimir Iglovikov\n    intersection = K.sum(y_true * y_pred, axis=[0, -1, -2])\n    sum_ = K.sum(y_true + y_pred, axis=[0, -1, -2])\n\n    jac = (intersection + smooth) / (sum_ - intersection + smooth)\n\n    return K.mean(jac)\n\n\ndef jaccard_coef_int(y_true, y_pred):\n    # __author__ = Vladimir Iglovikov\n    y_pred_pos = K.round(K.clip(y_pred, 0, 1))\n\n    intersection = K.sum(y_true * y_pred_pos, axis=[0, -1, -2])\n    sum_ = K.sum(y_true + y_pred, axis=[0, -1, -2])\n    jac = (intersection + smooth) / (sum_ - intersection + smooth)\n    return K.mean(jac)\n\n\ndef stick_all_train():\n    print \"let's stick all imgs together\"\n    s = 835\n\n    x = np.zeros((5 * s, 5 * s, 8))\n    y = np.zeros((5 * s, 5 * s, N_Cls))\n\n    ids = sorted(DF.ImageId.unique())\n    print len(ids)\n    for i in range(5):\n        for j in range(5):\n            id = ids[5 * i + j]\n\n            img = M(id)\n            img = stretch_n(img)\n            print img.shape, id, np.amax(img), np.amin(img)\n            x[s * i:s * i + s, s * j:s * j + s, :] = img[:s, :s, :]\n            for z in range(N_Cls):\n                y[s * i:s * i + s, s * j:s * j + s, z] = generate_mask_for_image_and_class(\n                    (img.shape[0], img.shape[1]), id, z + 1)[:s, :s]\n\n    print np.amax(y), np.amin(y)\n\n    np.save('data/x_trn_%d' % N_Cls, x)\n    np.save('data/y_trn_%d' % N_Cls, y)\n\n\ndef get_patches(img, msk, amt=10000, aug=True):\n    is2 = int(1.0 * ISZ)\n    xm, ym = img.shape[0] - is2, img.shape[1] - is2\n\n    x, y = [], []\n\n    tr = [0.4, 0.1, 0.1, 0.15, 0.3, 0.95, 0.1, 0.05, 0.001, 0.005]\n    for i in range(amt):\n        xc = random.randint(0, xm)\n        yc = random.randint(0, ym)\n\n        im = img[xc:xc + is2, yc:yc + is2]\n        ms = msk[xc:xc + is2, yc:yc + is2]\n\n        for j in range(N_Cls):\n            sm = np.sum(ms[:, :, j])\n            if 1.0 * sm / is2 ** 2 > tr[j]:\n                if aug:\n                    if random.uniform(0, 1) > 0.5:\n                        im = im[::-1]\n                        ms = ms[::-1]\n                    if random.uniform(0, 1) > 0.5:\n                        im = im[:, ::-1]\n                        ms = ms[:, ::-1]\n\n                x.append(im)\n                y.append(ms)\n\n    x, y = 2 * np.transpose(x, (0, 3, 1, 2)) - 1, np.transpose(y, (0, 3, 1, 2))\n    print x.shape, y.shape, np.amax(x), np.amin(x), np.amax(y), np.amin(y)\n    return x, y\n\n\ndef make_val():\n    print \"let's pick some samples for validation\"\n    img = np.load('data/x_trn_%d.npy' % N_Cls)\n    msk = np.load('data/y_trn_%d.npy' % N_Cls)\n    x, y = get_patches(img, msk, amt=3000)\n\n    np.save('data/x_tmp_%d' % N_Cls, x)\n    np.save('data/y_tmp_%d' % N_Cls, y)\n\n\ndef get_unet():\n    inputs = Input((8, ISZ, ISZ))\n    conv1 = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(inputs)\n    conv1 = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(conv1)\n    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)\n\n    conv2 = Convolution2D(64, 3, 3, activation='relu', border_mode='same')(pool1)\n    conv2 = Convolution2D(64, 3, 3, activation='relu', border_mode='same')(conv2)\n    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)\n\n    conv3 = Convolution2D(128, 3, 3, activation='relu', border_mode='same')(pool2)\n    conv3 = Convolution2D(128, 3, 3, activation='relu', border_mode='same')(conv3)\n    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)\n\n    conv4 = Convolution2D(256, 3, 3, activation='relu', border_mode='same')(pool3)\n    conv4 = Convolution2D(256, 3, 3, activation='relu', border_mode='same')(conv4)\n    pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)\n\n    conv5 = Convolution2D(512, 3, 3, activation='relu', border_mode='same')(pool4)\n    conv5 = Convolution2D(512, 3, 3, activation='relu', border_mode='same')(conv5)\n\n    up6 = merge([UpSampling2D(size=(2, 2))(conv5), conv4], mode='concat', concat_axis=1)\n    conv6 = Convolution2D(256, 3, 3, activation='relu', border_mode='same')(up6)\n    conv6 = Convolution2D(256, 3, 3, activation='relu', border_mode='same')(conv6)\n\n    up7 = merge([UpSampling2D(size=(2, 2))(conv6), conv3], mode='concat', concat_axis=1)\n    conv7 = Convolution2D(128, 3, 3, activation='relu', border_mode='same')(up7)\n    conv7 = Convolution2D(128, 3, 3, activation='relu', border_mode='same')(conv7)\n\n    up8 = merge([UpSampling2D(size=(2, 2))(conv7), conv2], mode='concat', concat_axis=1)\n    conv8 = Convolution2D(64, 3, 3, activation='relu', border_mode='same')(up8)\n    conv8 = Convolution2D(64, 3, 3, activation='relu', border_mode='same')(conv8)\n\n    up9 = merge([UpSampling2D(size=(2, 2))(conv8), conv1], mode='concat', concat_axis=1)\n    conv9 = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(up9)\n    conv9 = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(conv9)\n\n    conv10 = Convolution2D(N_Cls, 1, 1, activation='sigmoid')(conv9)\n\n    model = Model(input=inputs, output=conv10)\n    model.compile(optimizer=Adam(), loss='binary_crossentropy', metrics=[jaccard_coef, jaccard_coef_int, 'accuracy'])\n    return model\n\n\ndef calc_jacc(model):\n    img = np.load('data/x_tmp_%d.npy' % N_Cls)\n    msk = np.load('data/y_tmp_%d.npy' % N_Cls)\n\n    prd = model.predict(img, batch_size=4)\n    print prd.shape, msk.shape\n    avg, trs = [], []\n\n    for i in range(N_Cls):\n        t_msk = msk[:, i, :, :]\n        t_prd = prd[:, i, :, :]\n        t_msk = t_msk.reshape(msk.shape[0] * msk.shape[2], msk.shape[3])\n        t_prd = t_prd.reshape(msk.shape[0] * msk.shape[2], msk.shape[3])\n\n        m, b_tr = 0, 0\n        for j in range(10):\n            tr = j / 10.0\n            pred_binary_mask = t_prd > tr\n\n            jk = jaccard_similarity_score(t_msk, pred_binary_mask)\n            if jk > m:\n                m = jk\n                b_tr = tr\n        print i, m, b_tr\n        avg.append(m)\n        trs.append(b_tr)\n\n    score = sum(avg) / 10.0\n    return score, trs\n\n\ndef mask_for_polygons(polygons, im_size):\n    # __author__ = Konstantin Lopuhin\n    # https://www.kaggle.com/lopuhin/dstl-satellite-imagery-feature-detection/full-pipeline-demo-poly-pixels-ml-poly\n    img_mask = np.zeros(im_size, np.uint8)\n    if not polygons:\n        return img_mask\n    int_coords = lambda x: np.array(x).round().astype(np.int32)\n    exteriors = [int_coords(poly.exterior.coords) for poly in polygons]\n    interiors = [int_coords(pi.coords) for poly in polygons\n                 for pi in poly.interiors]\n    cv2.fillPoly(img_mask, exteriors, 1)\n    cv2.fillPoly(img_mask, interiors, 0)\n    return img_mask\n\n\ndef mask_to_polygons(mask, epsilon=5, min_area=1.):\n    # __author__ = Konstantin Lopuhin\n    # https://www.kaggle.com/lopuhin/dstl-satellite-imagery-feature-detection/full-pipeline-demo-poly-pixels-ml-poly\n\n    # first, find contours with cv2: it's much faster than shapely\n    image, contours, hierarchy = cv2.findContours(\n        ((mask == 1) * 255).astype(np.uint8),\n        cv2.RETR_CCOMP, cv2.CHAIN_APPROX_TC89_KCOS)\n    # create approximate contours to have reasonable submission size\n    approx_contours = [cv2.approxPolyDP(cnt, epsilon, True)\n                       for cnt in contours]\n    if not contours:\n        return MultiPolygon()\n    # now messy stuff to associate parent and child contours\n    cnt_children = defaultdict(list)\n    child_contours = set()\n    assert hierarchy.shape[0] == 1\n    # http://docs.opencv.org/3.1.0/d9/d8b/tutorial_py_contours_hierarchy.html\n    for idx, (_, _, _, parent_idx) in enumerate(hierarchy[0]):\n        if parent_idx != -1:\n            child_contours.add(idx)\n            cnt_children[parent_idx].append(approx_contours[idx])\n    # create actual polygons filtering by area (removes artifacts)\n    all_polygons = []\n    for idx, cnt in enumerate(approx_contours):\n        if idx not in child_contours and cv2.contourArea(cnt) >= min_area:\n            assert cnt.shape[1] == 1\n            poly = Polygon(\n                shell=cnt[:, 0, :],\n                holes=[c[:, 0, :] for c in cnt_children.get(idx, [])\n                       if cv2.contourArea(c) >= min_area])\n            all_polygons.append(poly)\n    # approximating polygons might have created invalid ones, fix them\n    all_polygons = MultiPolygon(all_polygons)\n    if not all_polygons.is_valid:\n        all_polygons = all_polygons.buffer(0)\n        # Sometimes buffer() converts a simple Multipolygon to just a Polygon,\n        # need to keep it a Multi throughout\n        if all_polygons.type == 'Polygon':\n            all_polygons = MultiPolygon([all_polygons])\n    return all_polygons\n\n\ndef get_scalers(im_size, x_max, y_min):\n    # __author__ = Konstantin Lopuhin\n    # https://www.kaggle.com/lopuhin/dstl-satellite-imagery-feature-detection/full-pipeline-demo-poly-pixels-ml-poly\n    h, w = im_size  # they are flipped so that mask_for_polygons works correctly\n    h, w = float(h), float(w)\n    w_ = 1.0 * w * (w / (w + 1))\n    h_ = 1.0 * h * (h / (h + 1))\n    return w_ / x_max, h_ / y_min\n\n\ndef train_net():\n    print \"start train net\"\n    x_val, y_val = np.load('data/x_tmp_%d.npy' % N_Cls), np.load('data/y_tmp_%d.npy' % N_Cls)\n    img = np.load('data/x_trn_%d.npy' % N_Cls)\n    msk = np.load('data/y_trn_%d.npy' % N_Cls)\n\n    x_trn, y_trn = get_patches(img, msk)\n\n    model = get_unet()\n    model.load_weights('weights/unet_10_jk0.7878')\n    model_checkpoint = ModelCheckpoint('weights/unet_tmp.hdf5', monitor='loss', save_best_only=True)\n    for i in range(1):\n        model.fit(x_trn, y_trn, batch_size=64, nb_epoch=1, verbose=1, shuffle=True,\n                  callbacks=[model_checkpoint], validation_data=(x_val, y_val))\n        del x_trn\n        del y_trn\n        x_trn, y_trn = get_patches(img, msk)\n        score, trs = calc_jacc(model)\n        print 'val jk', score\n        model.save_weights('weights/unet_10_jk%.4f' % score)\n\n    return model\n\n\ndef predict_id(id, model, trs):\n    img = M(id)\n    x = stretch_n(img)\n\n    cnv = np.zeros((960, 960, 8)).astype(np.float32)\n    prd = np.zeros((N_Cls, 960, 960)).astype(np.float32)\n    cnv[:img.shape[0], :img.shape[1], :] = x\n\n    for i in range(0, 6):\n        line = []\n        for j in range(0, 6):\n            line.append(cnv[i * ISZ:(i + 1) * ISZ, j * ISZ:(j + 1) * ISZ])\n\n        x = 2 * np.transpose(line, (0, 3, 1, 2)) - 1\n        tmp = model.predict(x, batch_size=4)\n        for j in range(tmp.shape[0]):\n            prd[:, i * ISZ:(i + 1) * ISZ, j * ISZ:(j + 1) * ISZ] = tmp[j]\n\n    # trs = [0.4, 0.1, 0.4, 0.3, 0.3, 0.5, 0.3, 0.6, 0.1, 0.1]\n    for i in range(N_Cls):\n        prd[i] = prd[i] > trs[i]\n\n    return prd[:, :img.shape[0], :img.shape[1]]\n\n\ndef predict_test(model, trs):\n    print( \"predict test\")\n    for i, id in enumerate(sorted(set(SB['ImageId'].tolist()))):\n        msk = predict_id(id, model, trs)\n        np.save('msk/10_%s' % id, msk)\n        if i % 100 == 0: print i, id\n\n\ndef make_submit():\n    print (\"make submission file\")\n    df = pd.read_csv(os.path.join(inDir, 'sample_submission.csv'))\n    print df.head()\n    for idx, row in df.iterrows():\n        id = row[0]\n        kls = row[1] - 1\n\n        msk = np.load('msk/10_%s.npy' % id)[kls]\n        pred_polygons = mask_to_polygons(msk)\n        x_max = GS.loc[GS['ImageId'] == id, 'Xmax'].as_matrix()[0]\n        y_min = GS.loc[GS['ImageId'] == id, 'Ymin'].as_matrix()[0]\n\n        x_scaler, y_scaler = get_scalers(msk.shape, x_max, y_min)\n\n        scaled_pred_polygons = shapely.affinity.scale(pred_polygons, xfact=1.0 / x_scaler, yfact=1.0 / y_scaler,\n                                                      origin=(0, 0, 0))\n\n        df.iloc[idx, 2] = shapely.wkt.dumps(scaled_pred_polygons)\n        if idx % 100 == 0: print idx\n    print df.head()\n    df.to_csv('subm/1.csv', index=False)\n\n\ndef check_predict(id='6120_2_3'):\n    model = get_unet()\n    model.load_weights('weights/unet_10_jk0.7878')\n\n    msk = predict_id(id, model, [0.4, 0.1, 0.4, 0.3, 0.3, 0.5, 0.3, 0.6, 0.1, 0.1])\n    img = M(id)\n\n    plt.figure()\n    ax1 = plt.subplot(131)\n    ax1.set_title('image ID:6120_2_3')\n    ax1.imshow(img[:, :, 5], cmap=plt.get_cmap('gist_ncar'))\n    ax2 = plt.subplot(132)\n    ax2.set_title('predict bldg pixels')\n    ax2.imshow(msk[0], cmap=plt.get_cmap('gray'))\n    ax3 = plt.subplot(133)\n    ax3.set_title('predict bldg polygones')\n    ax3.imshow(mask_for_polygons(mask_to_polygons(msk[0], epsilon=1), img.shape[:2]), cmap=plt.get_cmap('gray'))\n\n    plt.show()\n\n\nif __name__ == '__main__':\n    stick_all_train()\n    make_val()\n    model = train_net()\n    score, trs = calc_jacc(model)\n    predict_test(model, trs)\n    make_submit()\n\n    # bonus\n    check_predict()"}],"metadata":{"_change_revision":0,"_is_fork":false,"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.0"}},"nbformat":4,"nbformat_minor":0}