{"cells":[{"metadata":{},"cell_type":"markdown","source":"## About this kernel\n\n**This notebook contains Exploratory Data Analysis in the process of which a video from training images will be created grouped by diagnosis.** \n\n\n**We will do this in order, for example to be able to see how the images of the brain with one diagnosis or without any diagnosis differ from the image with five diagnosis.**\n\n\n**At the end of the notebook we get a video like [this](https://youtu.be/ZtF2Aq0d-J4).**"},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport pydicom as pdm\n\nimport cv2\nimport matplotlib.pyplot as plt\n\nimport time\nimport os\nfrom glob import glob\nprint(os.listdir(\"../input/\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# path to data \ntrain_data_path = sorted(glob(\"../input/rsna-intracranial-hemorrhage-detection/stage_1_train_images/*.dcm\"))\nlen(train_data_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# This is what the dcm file looks like\n#idx = np.random.choice(len(train_data_path))\nwtf = pdm.dcmread(train_data_path[191228])\n# SOP Instance UID : http://www.otpedia.com/entryDetails.cfm?id=199\n# Photometric Interpretation : https://www.dabsoft.ch/dicom/3/C.7.6.3.1.2/\nwtf","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_image = pdm.read_file(train_data_path[191228]).pixel_array\nrgb = np.stack((sample_image,)*3, axis=-1)\n\nfig, ax = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(15,10))\nax[0].imshow(sample_image, \"gray\");   \nax[0].set_title('Original image', fontsize=20)\nax[1].imshow(rgb, cmap='binary');\nax[1].set_title('RGB Image',fontsize=20)\nfig.subplots_adjust(hspace=0, wspace=0.1)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Preparation"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Dataset with unique UID rows(image data in csv format), and original dataset.\nall_data = pd.read_csv(\"../input/rsna-ihd/all_data_as_df.csv\")\ndf = pd.read_csv(\"../input/rsna-intracranial-hemorrhage-detection/stage_1_train.csv\")\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Code from https://www.kaggle.com/akensert/inceptionv3-prev-resnet50-keras-baseline-model/notebook\ndf[\"SOP Instance UID\"] = df[\"ID\"].str.slice(stop=12)\ndf[\"Diagnosis\"] = df[\"ID\"].str.slice(start=13)\ndf = df.loc[:, [\"Label\", \"Diagnosis\", \"SOP Instance UID\"]]\n\ndf[10:20]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Extraction all rows with unique images and without any diagnosis."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_without_d = df[(df[\"Diagnosis\"] == \"any\") & (df[\"Label\"] == 0)]\ndf_without_d = df_without_d.drop_duplicates(subset=['SOP Instance UID'])\n\n# Merge 2 df\ndf_without_d = pd.merge(df_without_d, all_data, on='SOP Instance UID')\n\n# For symmetry, we take only 1000 samples.\ndf_without_d = df_without_d.reset_index(drop=True)\nindexes = np.random.randint(0, len(df_without_d), 1000)\ndf_without_d = df_without_d.loc[indexes]\n\ndf_without_d['values'] = 'Without any diagnosis'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Extraction all rows with unique images and with diagnosis."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Label Coding\nfrom sklearn import preprocessing\nle = preprocessing.LabelEncoder()\nle.fit([\"intraparenchymal\", \"intraventricular\", \"epidural\", \"subarachnoid\", \"any\", \"subdural\"])\ndf['Coded Label'] = le.fit_transform(df['Diagnosis'])\n\ndf[500:510]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Extract all rows that do not contain “any” and which are tagged as 1\ndf = df[(df[\"Diagnosis\"] != \"any\") & (df[\"Label\"] == 1)]\ndf = df.reset_index(drop=True)\n\n# Merge 2 df\ndf_with_diagnosis = pd.merge(df, all_data, on='SOP Instance UID')\n\nprint(df_with_diagnosis.shape)\ndf_with_diagnosis[200:205]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Distribution of the number of unique samples\n\nuid = len(df_with_diagnosis['SOP Instance UID'].value_counts())\npatient_id  = len(df_with_diagnosis['Patient ID'].value_counts())\ntotal_labels = len(df_with_diagnosis)\n\ndistr = [patient_id, uid, total_labels]\n\nplt.figure(figsize=(15,7))\nplt.title('Distribution of the number of unique samples',fontsize=15)\nplt.bar(['Patient ID', 'SOP Instance UID', 'Labels'], distr,\n            color=['purple','lime',\"gold\"]);\nplt.ylabel('Number of samples',fontsize=15);\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**The hierarchy is as follows: 6K+ rows of \"Patient ID\" corresponds to 97K+ rows of UID that correspond to 133K+ labels, which means that one patient can have more than one UID that contains only 1 image, which can have more than 1 label, in other words many images contain more than one diagnosis, so we need to extract and group them into separate dataframes.**"},{"metadata":{},"cell_type":"markdown","source":"### Grouping data with any diagnosis by quantity"},{"metadata":{"trusted":true},"cell_type":"code","source":"unique_uid = df_with_diagnosis.groupby(['SOP Instance UID']).size().reset_index(name='Counts')\nunique_uid.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\ndef make_group_df(x_df, num_label):\n    main_df = df_with_diagnosis\n    df = x_df[x_df['Counts'] == num_label]\n    df = df.reset_index(drop=True)\n    df = pd.merge(df, main_df, on='SOP Instance UID')\n    return df\n\nlabel1 = make_group_df(unique_uid, 1)\nlabel2 = make_group_df(unique_uid, 2)\nlabel3 = make_group_df(unique_uid, 3)\nlabel4 = make_group_df(unique_uid, 4)\nlabel5 = make_group_df(unique_uid, 5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_df(x_df):\n    awesom_dict = {}\n    main_df = df_with_diagnosis\n    for i in range(0, len(x_df), 3) :\n        k = x_df[x_df['SOP Instance UID'] == x_df['SOP Instance UID'][i]]\n        v = k['Diagnosis'].values#to_string()\n        v = ' '.join(v)\n        k = k['SOP Instance UID'].values[0]\n        awesom_dict[k] = v\n    df = pd.DataFrame(list(awesom_dict.items()), columns=['SOP Instance UID', 'values'])\n    df = pd.merge(main_df, df, on='SOP Instance UID')  \n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nlabel1 = make_df(label1)\nlabel2 = make_df(label2)\nlabel3 = make_df(label3)\nlabel4 = make_df(label4)\nlabel5 = make_df(label5)\nlabel5.shape, label4.shape, label3.shape, label2.shape, label1.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Images with 1 labels\nprint(label1['values'].value_counts())\nlabel1_1 = label1[label1['values'] == 'subdural']\nlabel1_2 = label1[label1['values'] == 'subarachnoid']\nlabel1_3 = label1[label1['values'] == 'intraparenchymal']\nlabel1_4 = label1[label1['values'] == 'intraventricular']\nlabel1_5 = label1[label1['values'] == 'epidural']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Images with 2 labels\nvalue_counts = label2['values'].value_counts()\nprint(f'Number of unique label groups:{len(value_counts)}\\n\\n{value_counts}')\n\nlabel2_1 = label2[label2['values'] == value_counts.index[0]]\nlabel2_2 = label2[label2['values'] == value_counts.index[1]]\nlabel2_3 = label2[label2['values'] == value_counts.index[2]]\nlabel2_4 = label2[label2['values'] == value_counts.index[3]]\nlabel2_5 = label2[label2['values'] == value_counts.index[4]]\nlabel2_6 = label2[label2['values'] == value_counts.index[5]]\nlabel2_7 = label2[label2['values'] == value_counts.index[6]]\nlabel2_8 = label2[label2['values'] == value_counts.index[7]]\nlabel2_9 = label2[label2['values'] == value_counts.index[8]]\nlabel2_10 = label2[label2['values'] == value_counts.index[9]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Images with 3 labels\nvalue_counts = label3['values'].value_counts()\nprint(f'Number of unique label groups:{len(value_counts)}\\n\\n{value_counts}')\n\nlabel3_1 = label3[label3['values'] == value_counts.index[0]]\nlabel3_2 = label3[label3['values'] == value_counts.index[1]]\nlabel3_3 = label3[label3['values'] == value_counts.index[2]]\nlabel3_4 = label3[label3['values'] == value_counts.index[3]]\nlabel3_5 = label3[label3['values'] == value_counts.index[4]]\nlabel3_6 = label3[label3['values'] == value_counts.index[5]]\nlabel3_7 = label3[label3['values'] == value_counts.index[6]]\nlabel3_8 = label3[label3['values'] == value_counts.index[7]]\nlabel3_9 = label3[label3['values'] == value_counts.index[8]]\nlabel3_10 = label3[label3['values'] == value_counts.index[9]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Images with 4 labels\nvalue_counts = label4['values'].value_counts()\nprint(f'Number of unique label groups:{len(value_counts)}\\n\\n{value_counts}')\n\nlabel4_1 = label4[label4['values'] == value_counts.index[0]]\nlabel4_2 = label4[label4['values'] == value_counts.index[1]]\nlabel4_3 = label4[label4['values'] == value_counts.index[2]]\nlabel4_4 = label4[label4['values'] == value_counts.index[3]]\nlabel4_5 = label4[label4['values'] == value_counts.index[4]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Images with 5 labels\nvalue_counts = label5['values'].value_counts()\nprint(f'Number of unique label groups:{len(value_counts)}\\n\\n{value_counts}')\n\nlabel5 = label5[label5['values'] == value_counts.index[0]]\nlabel5 = label5.drop_duplicates(subset=['SOP Instance UID'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Creating Video"},{"metadata":{},"cell_type":"markdown","source":"**This function will create image-description for the corresponding data frame.**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_description(row):\n    \n   # Create description for video\n   # row: row of dataframe with label values\n\n    label = row.split(' ')\n    img = np.ones((512,512, 3), dtype=np.uint8)\n    \n    # Description parameters\n    if len(label) == 1:\n        font_size = 1.5\n        y0 = 200\n        pad = 60\n    elif len(label) > 2:\n        font_size = 1.5\n        y0 = 150\n        pad = 60\n    else:\n        font_size = 2\n        y0 = 200\n        pad = 100\n        \n    for i, line in enumerate(label):\n        y = y0 + i*pad\n        cv2.putText(img, line.capitalize(),\n                    (50, y ), cv2.FONT_ITALIC,\n                    font_size,(0,255,127),\n                    2,cv2.LINE_AA)\n    return img","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Too much data, so we will write a generator  and then we will be iterating by him when recording frames.**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def data_generator(df):\n\n    # Calculate batch size, &\n    # list of indices to remove from df\n\n    batch_size = len(df)// int(np.sqrt(len(df)))\n    del_indices=[]\n\n    while len(df) >= batch_size:\n        df = df.drop(index=del_indices) \n        df = df.reset_index(drop=True)\n\n        batch_i = []\n        del_indices = []\n  \n        if len(df) != 0:\n            for i in range(batch_size):\n                image = df['path'].sample(1).values[0]\n                # Read image as np.array (512x512)\n                image = pdm.read_file(image).pixel_array\n                \n                # Color map and normalization instance\n                cmap = plt.cm.bone\n                norm = plt.Normalize(vmin=image.min(), vmax=image.max())\n                \n                # image is now RGBA (512x512x4) \n                image = cmap(norm(image))\n\n                batch_i += [image]\n                del_indices += [i]\n\n            yield batch_i, del_indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_video(x_df, fps=5):\n    \n    # Remove duplicates\n    x_df = x_df.drop_duplicates(subset=['SOP Instance UID'])\n    x_df = x_df.reset_index(drop=True)\n    \n    # Create description\n    values = x_df['values'][0]\n    description = create_description(values)\n    description = [description] * 25\n    video_name = '-'.join(x_df['values'][0].split()) + '.avi'\n    \n    # Define the codec and create VideoWrite object\n    fourcc = cv2.VideoWriter_fourcc(*'XVID')\n    video = cv2.VideoWriter(video_name, fourcc, fps, (512, 512))\n    \n    # Write the video\n    for d in description:\n        video.write(d.astype('uint8')) \n     \n    for batch, _ in data_generator(x_df):    \n        # Take only 3 channels and normalize\n        # values to val [0.0, 1.0]\n        for img in batch:\n            img = img[:,:,:3] * 255 \n            video.write(img.astype('uint8')) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Grouped Data List\ngrouped_dfs = [label1_1, label1_2, label1_3, label1_4, label1_5,\n              label2_1, label2_2, label2_3, label2_4, label2_5,\n              label2_6, label2_7, label2_8, label2_9, label2_10,\n              label3_1, label3_2, label3_3, label3_4, label3_5,\n              label3_6, label3_7, label3_8, label3_9, label3_10,\n              label4_1, label4_2, label4_3, label4_4, label4_5,\n              label5, df_without_d]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n# Recursive function call\ndef record_all_videos(data_list):\n    a = len(data_list)\n    while a !=0:\n        a -= 1\n        time.sleep(5)\n        print(f\"{time.ctime()} creating video № {a}\")\n        make_video(data_list[a]) #df_img_video\nrecord_all_videos(grouped_dfs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"# Our outputs\n!ls","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"video_names = [f'''\nsubdural.avi\nsubarachnoid.avi\nintraparenchymal.avi\nintraventricular.avi\nepidural.avi\nintraparenchymal-intraventricular.avi\nsubarachnoid-subdural.avi\nintraparenchymal-subarachnoid.avi\nintraventricular-subarachnoid.avi\nintraparenchymal-subdural.avi\nintraventricular-subdural.avi\nepidural-subdural.avi\nepidural-intraparenchymal.avi\nepidural-subarachnoid.avi\nepidural-intraventricular.avi\nintraparenchymal-subarachnoid-subdural.avi\nintraparenchymal-intraventricular-subarachnoid.avi\nintraventricular-subarachnoid-subdural.avi\nintraparenchymal-intraventricular-subdural.avi\nepidural-subarachnoid-subdural.avi\nepidural-intraparenchymal-subdural.avi\nepidural-intraparenchymal-subarachnoid.avi\nepidural-intraparenchymal-intraventricular.avi\nepidural-intraventricular-subdural.avi\nepidural-intraventricular-subarachnoid.avi\nintraparenchymal-intraventricular-subarachnoid-subdural.avi\nepidural-intraparenchymal-subarachnoid-subdural.avi\nepidural-intraparenchymal-intraventricular-subarachnoid.avi\nepidural-intraventricular-subarachnoid-subdural.avi\nepidural-intraparenchymal-intraventricular-subdural.avi\nepidural-intraparenchymal-intraventricular-subarachnoid-subdural.avi\nWithout-any-diagnosis.avi\n''']\n\nwith open('join.txt', 'a') as f:\n    for i in video_names[0].split():\n        f.write(f\"file {i}\\n\")","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!cat join.txt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**FFmpeg installation**"},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# FFmpeg installation\n!apt install -y ffmpeg > /dev/null","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Combining all videos**"},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# Concatenate all videos\n!ffmpeg -f concat -i join.txt -c copy output.avi","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Adding ambient music to the background of the video**\n\n**get a valid link [here](https://y2mate.com/ru/youtube-to-mp3/w8TGZYc2xtc).**\n"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"! wget -O audio.mp3 http://dl17.y2mate.com/?file=M3R4SUNiN3JsOHJ6WWQ2a3NQS1Y5ZGlxVlZIOCtyaDNwOTAxMGdJd1BxUkttWWtxd3ZlZkp0RndMNm9OeEkrc0JlRlo0QzNSY3UrR01RR0N2NVlpZlhlSTRkODV2RHZ3NElFMVY4MWxSQmY3a05tdW55bEpnUmJLYVlENU1vSnZiaVZqdDBOY2dIVER4L1BFcWx2MW95N3JvVVNQYVJzYXN5OERZTENDMTRaSHdIM2ViZktoZ2JZSXZYWGFzTHdlbjZLN3JWUDd3cVk3OXQ5NlV3ODVPTU5pMFpYZ3plUHpxa0VtaEp3SnlYS2hzZDJuQzV3OEdhdUtSanBsTFNzSzF1THVVaEFYd1NFaC9HV28vNnNnNmw4YWZyVjc3SGFtK1BIaVdUS1JadjMrWHE2VGN0eWFuUHo2c09GMXZINlM5TFBNbm9wVDJBVzNHNDc4U3RnTnYxc0trZjdVdklKbmt3YjNpMTFEekxKWWxnMzViMXcwWEpKZktub0tlcDFDVUg5Tis1NjN1UFk9","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!ffmpeg -i output.avi -i audio.mp3 -c copy -map 0:v:0 -map 1:a:0 -shortest Result.avi","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Let's make a list with time codes**\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Extract duration from all videos\ndef get_duration(video_list):\n    duration_list = []\n    for video in video_list:\n        cap = cv2.VideoCapture(video)\n        fps = cap.get(cv2.CAP_PROP_FPS)\n        frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n        duration = frame_count/fps\n        duration_list.append(duration)\n        cap.release()\n    return duration_list\n\nd_list = get_duration(video_names[0].split())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Creating time codes for our grouped video**"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Recursive function call\ndef make_timecodes(video_list):\n    a = len(video_list)\n    timecodes = []\n    while a !=0:\n        a -= 1\n        duration = sum(video_list[:a])\n        minutes = int(duration/60)\n        seconds = duration%60\n        time = f\"{minutes}:{np.around(seconds,2)}\"\n        timecodes.append(time)\n    with open('timecodes.txt', 'a') as f:\n        f.write(\"Time Codes in minutes:\\n\")\n        for n, t in zip(video_names[0].split(),timecodes[::-1]):  \n            f.write(f\"{n} - {t}\\n\")\n\nmake_timecodes(d_list)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!cat timecodes.txt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Done! Video completed**"},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"! mkdir output\n! cp Result.avi timecodes.txt output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from IPython.display import FileLink, FileLinks\n# create a IPython.display.FileLink object with provided file name and path.\noutput_files = FileLinks(path='output')\n\n# print the FileLinks objects.\noutput_files\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":false},"cell_type":"code","source":"from IPython.display import YouTubeVideo\n# Getting video like this\nYouTubeVideo('ZtF2Aq0d-J4')","execution_count":null,"outputs":[]}],"metadata":{"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.6"}},"nbformat":4,"nbformat_minor":1}