{"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":"code","source":"import cv2\nimport numpy as np\nimport scipy as sp\nimport pandas as pd\n\nimport tensorflow as tf\nfrom keras.utils import plot_model\n\nimport seaborn as sns\nfrom tqdm import tqdm\nfrom sklearn.preprocessing import MultiLabelBinarizer\nimport matplotlib.pyplot as plt\n\ntqdm.pandas()\nimport plotly.express as px\nimport plotly.graph_objects as go\nimport plotly.figure_factory as ff\nfrom plotly.subplots import make_subplots\n\nnp.random.seed(0)\ntf.random.set_seed(0)\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS = 20\nSAMPLE_LEN = 100\nIMAGE_PATH = \"../input/plant-pathology-2021-fgvc8/train_images/\"\nTRAIN_PATH = \"../input/plant-pathology-2021-fgvc8/train.csv\"\n\ndata = pd.read_csv(TRAIN_PATH)\ntrain_data = data.copy()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_image(image_id):\n    file_path = image_id\n    image = cv2.imread(IMAGE_PATH + file_path)\n    return image\n\ntrain_images = train_data[\"image\"][:SAMPLE_LEN].progress_apply(load_image)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(20,12))\nlabels = sns.barplot(train_data.labels.value_counts().index,train_data.labels.value_counts())\nfor item in labels.get_xticklabels():\n    item.set_rotation(45)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Lượng dữ liệu chênh lệch quá nhiều, đặc biệt dữ liệu đa nhãn, có nhiều bệnh lại chiếm số lượng quá nhỏ. Vì vậy, chúng em đã nghĩ tới cách chỉ giữ lại các nhãn đơn, nhãn kép sẽ đưọc biểu diễn theo nhãn đơn dưới dạng one hot encoding.","metadata":{}},{"cell_type":"markdown","source":"# One hot encoding","metadata":{}},{"cell_type":"code","source":"train_data['labels'] = train_data['labels'].apply(lambda string: string.split(' '))\ns = list(train_data['labels'])\nmlb = MultiLabelBinarizer()\ntrainx = pd.DataFrame(mlb.fit_transform(s), columns=mlb.classes_, index=train_data.index)\ntrainx","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = pd.concat([train_data['image'], trainx], axis=1)\nlabels.head()\nfig = go.Figure([go.Pie(labels=labels.columns[1:],\n           values=labels.iloc[:, 1:].sum())])\nfig.update_layout(title_text=\"Pie chart of targets\", template=\"simple_white\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Dữ liệu sau khi được mã hoá lại đã được phân lại đều hơn, không còn mất cân bằng lớn như trưóc.","metadata":{}},{"cell_type":"markdown","source":"# Visualization","metadata":{}},{"cell_type":"code","source":"def visual(img):\n    fig, ax = plt.subplots(nrows=3, ncols=3, figsize=(30, 20))\n    ax[0][0].imshow(load_image(img[0]))\n    ax[1][0].imshow(load_image(img[1]))\n    ax[2][0].imshow(load_image(img[2]))\n    ax[0][1].imshow(load_image(img[3]))\n    ax[1][1].imshow(load_image(img[4]))\n    ax[2][1].imshow(load_image(img[5]))\n    ax[0][2].imshow(load_image(img[6]))\n    ax[1][2].imshow(load_image(img[7]))\n    ax[2][2].imshow(load_image(img[8]))\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Healthy","metadata":{}},{"cell_type":"code","source":"healthy = data[data['labels']=='healthy']['image'].values[:9]\nvisual(healthy)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Scab","metadata":{}},{"cell_type":"code","source":"scab = data[data['labels']=='scab']['image'].values[10:19]\nvisual(scab)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"scab và healthy nhìn rất giống nhau, đây có thể là 1 trở ngại rất lớn.","metadata":{}},{"cell_type":"markdown","source":"# Frog_eye_leaf_spot","metadata":{}},{"cell_type":"code","source":"frog_eye_leaf_spot = data[data['labels']=='frog_eye_leaf_spot']['image'].values[0:9]\nvisual(frog_eye_leaf_spot)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Rust","metadata":{}},{"cell_type":"code","source":"rust = data[data['labels']=='rust']['image'].values[0:9]\nvisual(rust)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Chỉ bằng mắt thuờng cũng đã thấy khó phân biệt giữa 2 bệnh rust và frog_eye_leaf_spot. Đây có thể là khó khăn trong quá trình đào tạo.","metadata":{}},{"cell_type":"markdown","source":"# Complex","metadata":{}},{"cell_type":"code","source":"complex = data[data['labels']=='complex']['image'].values[0:9]\nvisual(complex)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Powdery_mildew","metadata":{}},{"cell_type":"code","source":"powdery_mildew = data[data['labels']=='powdery_mildew']['image'].values[0:9]\nvisual(powdery_mildew)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def visualize_leaves(cond=[0, 0, 0, 0, 0, 0], cond_cols=[\"healthy\"], is_cond=True):\n    if not is_cond:\n        cols, rows = 3, min([3, len(train_images)//3])\n        fig, ax = plt.subplots(nrows=rows, ncols=cols, figsize=(30, rows*20/3))\n        for col in range(cols):\n            for row in range(rows):\n                ax[row, col].imshow(train_images.loc[train_images.index[-row*3-col-1]])\n        return None\n        \n    cond_0 = \"complex == {}\".format(cond[0])\n    cond_1 = \"frog_eye_leaf_spot == {}\".format(cond[1])\n    cond_2 = \"healthy == {}\".format(cond[2])\n    cond_3 = \"powdery_mildew == {}\".format(cond[3])\n    cond_4 = \"rust == {}\".format(cond[4])\n    cond_5 = \"scab == {}\".format(cond[5])\n    cond_list = []\n    for col in cond_cols:\n        if col == \"complex\":\n            cond_list.append(cond_0)\n        if col == \"frog_eye_leaf_spot\":\n            cond_list.append(cond_1)\n        if col == \"healthy\":\n            cond_list.append(cond_2)\n        if col == \"powdery_mildew\":\n            cond_list.append(cond_3)\n        if col == \"rust\":\n            cond_list.append(cond_4)\n        if col == \"scab\":\n            cond_list.append(cond_5)\n    \n    data = labels.loc[:100]\n    for cond in cond_list:\n        data = data.query(cond)\n        \n    images = train_images.loc[list(data.index)]\n    cols, rows = 3, min([3, len(images)//3])\n    \n    fig, ax = plt.subplots(nrows=rows, ncols=cols, figsize=(30, rows*20/3))\n    for col in range(cols):\n        for row in range(rows):\n            ax[row, col].imshow(images.loc[images.index[row*3+col]])\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = px.parallel_categories(labels[['complex', 'frog_eye_leaf_spot', 'healthy', 'powdery_mildew', 'rust','scab']], color=\"healthy\", color_continuous_scale=\"sunset\",\\\n                             title=\"Parallel categories plot of targets\")\nfig","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Đồ thị trên cho ta thấy sự liên quan giữa các nhãn với nhau. Ví dụ với các lá khỏe mạnh, đường màu xanh sẽ chỉ đi qua \"healthy\" có giá trị = 1 còn các nhãn khác đều đi qua vùng có giá trị = 0. Độ rộng của đường trên các khoảng thể hiện tần suất xuất hiện của nhãn đó khi kết hợp với các nhãn khác.","metadata":{}},{"cell_type":"markdown","source":"# Augmentation","metadata":{}},{"cell_type":"markdown","source":"# Histogram Equalization","metadata":{}},{"cell_type":"code","source":"def histogram(img):\n    R, G, B = cv2.split(img)\n\n    output1_R = cv2.equalizeHist(R)\n    output1_G = cv2.equalizeHist(G)\n    output1_B = cv2.equalizeHist(B)\n\n    equ = cv2.merge((output1_R, output1_G, output1_B))\n    fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(30, 20))\n    ax[0].imshow(img)\n    ax[0].set_title('Original Image', fontsize=24)\n    ax[1].imshow(equ)\n    ax[1].set_title('Histogram Equalization Image', fontsize=24)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"histogram(train_images[9])\nhistogram(train_images[10])\nhistogram(train_images[11])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Sau khi cân bằng histogram, ta thấy các vị trí bị bệnh trên chiếc lá sẽ nổi bật hẳn trên nền của lá. Nên ta sẽ thêm lượng dữ liệu được cân bằng histogram này nhằm giúp nhấn mạnh vùng bị bệnh.","metadata":{}},{"cell_type":"markdown","source":"# Canny Detection, Crop image (Tìm đối tượng lá trong ảnh)","metadata":{}},{"cell_type":"code","source":"def edge_and_cut(img):\n    emb_img = img.copy()\n    edges = cv2.Canny(img, 100, 200)\n    edge_coors = []\n    for i in range(edges.shape[0]):\n        for j in range(edges.shape[1]):\n            if edges[i][j] != 0:\n                edge_coors.append((i, j))\n    \n    row_min = edge_coors[np.argsort([coor[0] for coor in edge_coors])[0]][0]\n    row_max = edge_coors[np.argsort([coor[0] for coor in edge_coors])[-1]][0]\n    col_min = edge_coors[np.argsort([coor[1] for coor in edge_coors])[0]][1]\n    col_max = edge_coors[np.argsort([coor[1] for coor in edge_coors])[-1]][1]\n    new_img = img[row_min:row_max, col_min:col_max]\n    \n    emb_img[row_min-10:row_min+10, col_min:col_max] = [255, 0, 0]\n    emb_img[row_max-10:row_max+10, col_min:col_max] = [255, 0, 0]\n    emb_img[row_min:row_max, col_min-10:col_min+10] = [255, 0, 0]\n    emb_img[row_min:row_max, col_max-10:col_max+10] = [255, 0, 0]\n    \n    fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(30, 20))\n    ax[0].imshow(img, cmap='gray')\n    ax[0].set_title('Original Image', fontsize=24)\n    ax[1].imshow(edges, cmap='gray')\n    ax[1].set_title('Canny Edges', fontsize=24)\n    ax[2].imshow(emb_img, cmap='gray')\n    ax[2].set_title('Bounding Box', fontsize=24)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"edge_and_cut(train_images[3])\nedge_and_cut(train_images[4])\nedge_and_cut(train_images[5])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Áp dụng Canny Detection để tìm cạnh của lá, sau đó ở tập dữ liệu cạnh, tìm X_max, Y_max, X_min, Y_min là 4 toạ độ của vùng chữ nhật chứa chiếc lá. Sau đấy ta áp dụng để Crop ra vùng chứa chiếc lá đó để tạo thêm ảnh (có ít background) giúp gia tăng bộ dữ liệu. Ta áp dụng được cách này vì chiếc lá là đối tượng chính và rõ nét nhất.","metadata":{}},{"cell_type":"markdown","source":"# Rotate","metadata":{}},{"cell_type":"code","source":"def rotate(img):\n    fig, ax = plt.subplots(nrows=1, ncols=4, figsize=(30, 20))\n    ax[0].imshow(img)\n    ax[0].set_title('Original Image', fontsize=24)\n    ax[1].imshow(cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE))\n    ax[1].set_title('90', fontsize=24)\n    ax[2].imshow(cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE))\n    ax[2].set_title('180', fontsize=24)\n    ax[3].imshow(cv2.rotate(img, cv2.ROTATE_180))\n    ax[3].set_title('270', fontsize=24)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"rotate(train_images[9])\nrotate(train_images[10])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Vì đối tượng trong ảnh là lá, quay chiều nào cũng mang lại hình ảnh chiếc lá nên ta sẽ áp dụng cách này để tăng bộ dữ liệu.","metadata":{}},{"cell_type":"markdown","source":"# Flip","metadata":{}},{"cell_type":"code","source":"def flip(img):\n    fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(30, 20))\n    ax[0].imshow(img)\n    ax[0].set_title('Original Image', fontsize=24)\n    ax[1].imshow(cv2.flip(img, 0))\n    ax[1].set_title('Vertical Flip', fontsize=24)\n    ax[2].imshow(cv2.flip(img, 1))\n    ax[2].set_title('Horizontal Flip', fontsize=24)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"flip(train_images[9])\nflip(train_images[10])\nflip(train_images[11])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Vì đối tượng trong ảnh là lá, lật chiều nào cũng mang lại hình ảnh chiếc lá nên ta sẽ áp dụng cách này để tăng bộ dữ liệu.","metadata":{}}]}