{"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":"# --- CSS STYLE ---\nfrom IPython.core.display import HTML\ndef css_styling():\n    styles = open(\"../input/2020-cost-of-living/alerts.css\", \"r\").read()\n    return HTML(\"<style>\"+styles+\"</style>\")\ncss_styling()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-12-19T02:52:29.201443Z","iopub.execute_input":"2021-12-19T02:52:29.202018Z","iopub.status.idle":"2021-12-19T02:52:29.214511Z","shell.execute_reply.started":"2021-12-19T02:52:29.201969Z","shell.execute_reply":"2021-12-19T02:52:29.213695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" We did that above to **embed HTML into ipyhton output** ","metadata":{}},{"cell_type":"markdown","source":"<img src=\"https://i.imgur.com/YcUJEhW.png\">\n\n<center><h1>SIIM-FISABIO-RSNA COVID-19 Detection</h1></center>\n\nThis is **an object detection and classification problem**, meaning that for each instance we'll have to *predict* a **bounding box** and a **class**.\n\n<div class=\"alert success-alert\">\n📌 <b>Project Goal</b>: Categorize chest radiographs as negative for pneumonia, typical, indeterminate, or atypical for COVID-19. If some abnormalities are found, provide the bounding boxes.\n</div>\n\nOK! Covid-19 sympthoms look very similar to other viral or bacterial pneumonias/ chest radiographs, hence it's much harder to correctly (and I might add quickly) diagnose.\n\n### ⬇️ Libraries\n* Link to my W&B Dashboard here: https://wandb.ai/mehreet/siim-covid19?workspace=user-mehreet\n* How to use W&B: [Experiment Tracking with Weights and Biases](https://www.kaggle.com/ayuraj/experiment-tracking-with-weights-and-biases)","metadata":{}},{"cell_type":"markdown","source":"# LIBRARIES :\nNow we will be importing the libraries which will be needed in our notebook \n\n\n- **OS** : To use OS Functions in python interface \n- **re (regular expression)** :  specifies a set of strings that matches it; the functions in this module let you check if a particular string matches a given regular expression (or if a given regular expression matches a particular string, which comes down to the same thing).\n- **wandb (Weights & Biases) **: a python package that allows us to monitor our training in real-time \n    \n    [Experiment Tracking]\n    [Dataset Versioning ]\n    [Modelset management ]\n    [Prediction Visualization]\n    Keep all your results at one place \n    Visualize things \n    Share results \n    Log metrics directly to w&b --> then visualize and describetheir results \n    shows the metrics and performances in a dashboard format \n     \n- **tqdm**  : a python library used for creating Progress Meters or Progress Bars.\n- **warnings** : warn the developer of situations that aren’t necessarily exceptions.\n- **glob** : search for a specific file pattern, or perhaps more usefully, search for files where the filename matches a certain pattern by using wildcard characters.\n- **ast **: used for typecasting if data type is unknown\n- **cv2** : is a library of Python bindings designed to solve computer vision problems.                         cv2.imread() method loads an image from the specified file. If the image cannot be read (because of missing file, improper permissions, unsupported or invalid format) then this method returns an empty matrix.\n- **math **: To use mathematical functions in python\n- **pandas** : data wrangling and analysis , cleaning, transforming, manipulating and analyzing data\n- **numpy** :  Python library used for working with arrays\n- **IPython.display import display_html** : embed rendered HTML output into IPython output\n- **seaborn** :for visualization\n- **matplotlib** : visualization\n- **import matplotlib.patches as patches** :Patches are arbitrary two dimensional regions. There are a lot of fancy wrappers and helpers, like Rectangles, Circles, Boxes, and Ellipses\n- **import matplotlib.pyplot as plt** : collection of command style functions that make matplotlib work like MATLAB. Each pyplot function makes some change to a figure: e.g., creates a figure, creates a plotting area in a figure, plots some lines in a plotting area, decorates the plot with labels, etc. In matplotlib.\n- **from scipy.stats import pearsonr** :The Pearson correlation coefficient measures the linear relationship between two datasets\n Like other correlation coefficients, this one varies between -1 and +1 with 0 implying no correlation. Correlations of -1 or +1 imply an exact linear relationship. Positive correlations imply that as x increases, so does y. Negative correlations imply that as x increases, y decreases.\n- **from matplotlib.offsetbox import AnnotationBbox, OffsetImage **: The OffsetBox is a simple container artist.\nThe child artists are meant to be drawn at a relative position to its parent.Being an artist itself, all parameters are passed on to Artist.\nAnnotationBbox creates an annotation using an OffsetBox, and provides more fine-grained control than Axes.annotate\n\n- **pydicom** : pydicom is a pure python package for working with DICOM files such as medical images, reports, and radiotherapy objects. ... It is designed to let you manipulate data elements in DICOM files with python code.\nDICOM files are images that come digitally from medical scans, such as MRIs and ultrasounds. You can view these files with a free online viewer called Jack Image viewer on any computer.\n- **from pydicom.pixel_data_handlers.util import apply_voi_lut**: \nLUT means -  Look Up table \n4 type of LUT found within DICOM : \n1. Modality LUT : purpose of the \"Modality LUT\" step in the theoretical grayscale pipeline is to map stored pixel values to some kind of meaning physical unit.\nThis may be a linear mapping as Dee describes or using a lookup table.\nNot all image objects support this feature. It depends on the modality since some modalities like CT have a meaningful physical unit space.\n2. Identity (no LUT)\n3. VOI LUT\n4. Presentation LUT\nVOI LUT Sequence : Value of interest look Up table defines a Sequence .One or more Items shall be included in this Sequence.\nRequired if Window Center (0028,1050) is not present. May be present otherwise.\nThe Value Of Interest(VOI) LUT transformation transforms the modality pixel values into pixel values which are meaningful for the user or the application. The VOI LUT is described by the VOI LUT Sequence\nWindowing\nThis can be linear or sigmoid. Both types are defined by the Window Centre (0028, 1050) and Window Width (0028, 1051) values, but the resulting shapes derived from those figures differ at the extremes of the range.\n- **from sklearn.cluster import KMeans** : using K means clustering algorithm \nhttps://www.medicalconnections.co.uk/kb/lookup-tables/\n- **from skimage import morphology, measure** :skimage.morphology. binary_opening(image, footprint=None, out=None)[source] Return fast binary morphological opening of an image. This function returns the same result as grayscale opening but performs faster for binary images","metadata":{}},{"cell_type":"code","source":"# Libraries\nimport os\nimport re\nimport wandb\nimport tqdm\nimport warnings\nimport glob\nimport ast\nimport cv2\nimport math\nimport pandas as pd\nimport numpy as np\nfrom IPython.display import display_html\nimport seaborn as sns\nimport matplotlib as mpl\nimport matplotlib.patches as patches\nimport matplotlib.pyplot as plt\nfrom scipy.stats import pearsonr\nfrom matplotlib.offsetbox import AnnotationBbox, OffsetImage\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom sklearn.cluster import KMeans\nfrom skimage import morphology, measure","metadata":{"execution":{"iopub.status.busy":"2021-12-19T02:53:16.587217Z","iopub.execute_input":"2021-12-19T02:53:16.587827Z","iopub.status.idle":"2021-12-19T02:53:16.594042Z","shell.execute_reply.started":"2021-12-19T02:53:16.587790Z","shell.execute_reply":"2021-12-19T02:53:16.593136Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Environment check\nwarnings.filterwarnings(\"ignore\")\nos.environ[\"WANDB_SILENT\"] = \"true\"\nCONFIG = {'competition': 'siim-fisabio-rsna', '_wandb_kernel': 'aot'}\n","metadata":{"execution":{"iopub.status.busy":"2021-12-19T02:53:20.088883Z","iopub.execute_input":"2021-12-19T02:53:20.089502Z","iopub.status.idle":"2021-12-19T02:53:20.094271Z","shell.execute_reply.started":"2021-12-19T02:53:20.089451Z","shell.execute_reply":"2021-12-19T02:53:20.093243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- **warnings.filterwarnings(\"ignore\")**- ignoringthe warnings in python \n- **os.environ[\"WANDB_SILENT\"]** = \"true\" - Set this to true to silence wandb log statements. If this is set all logs will be written to WANDB_DIR/debug.log\n- **CONFIG** is a dictionary which will be later used in a function save_dataset_artifact , here we are just setting the key value pairs \n\n\n","metadata":{}},{"cell_type":"code","source":"# Secrets 🤫\nfrom kaggle_secrets import UserSecretsClient\nuser_secrets = UserSecretsClient()\nsecret_value_0 = user_secrets.get_secret(\"mehreet_secret_wand\")\n","metadata":{"execution":{"iopub.status.busy":"2021-12-19T02:54:44.634381Z","iopub.execute_input":"2021-12-19T02:54:44.634924Z","iopub.status.idle":"2021-12-19T02:54:44.899948Z","shell.execute_reply.started":"2021-12-19T02:54:44.634889Z","shell.execute_reply":"2021-12-19T02:54:44.898843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**secret section** - a feature of kaggle like API Keys, access tokens, etc) more securely in Kernels! \n \nSetting the secret key for calling the wandb API as secret_value_0","metadata":{}},{"cell_type":"markdown","source":"**Selecting** some of our **favourite color** selections for seaborn plots ","metadata":{}},{"cell_type":"code","source":"\n# Custom colors\nmy_colors = [\"#E97777\", \"#E9B077\", \"#E9E977\", \n             \"#77E977\", \"#7777E9\", \"#6F6BAC\", \"#B677E9\"]\nsns.palplot(sns.color_palette(my_colors))\n\n# Set Style\nsns.set_style(\"white\")\nmpl.rcParams['xtick.labelsize'] = 18\nmpl.rcParams['ytick.labelsize'] = 18\nmpl.rcParams['axes.spines.left'] = False\nmpl.rcParams['axes.spines.right'] = False\nmpl.rcParams['axes.spines.top'] = False\nplt.rcParams.update({'font.size': 22})\n\nclass color:\n    BOLD = '\\033[1m' + '\\033[93m'\n    END = '\\033[0m'","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-12-19T02:55:16.489087Z","iopub.execute_input":"2021-12-19T02:55:16.489655Z","iopub.status.idle":"2021-12-19T02:55:16.614663Z","shell.execute_reply.started":"2021-12-19T02:55:16.489615Z","shell.execute_reply":"2021-12-19T02:55:16.613461Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"sns is seaborn and setting the value of colors \n**sns.set_style** = Set the parameters that control the general style of the plots.\n\nCustomizing Matplotlib with style sheets and rcParams\n\ndynamically change the default rc (runtime configuration) settings in a python script or interactively from the python shell.\n\n All rc settings are stored in a dictionary-like variable called matplotlib.rcParams\n \n ","metadata":{}},{"cell_type":"markdown","source":"> 📌 **Note**: If this line throws an error, try using `wandb.login()` instead. It will ask for the API key to login, which you can get from your W&B profile (click on Profile -> Settings -> scroll to API keys).","metadata":{}},{"cell_type":"code","source":"! wandb login $secret_value_0","metadata":{"_kg_hide-input":false,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-12-19T02:55:47.390347Z","iopub.execute_input":"2021-12-19T02:55:47.390810Z","iopub.status.idle":"2021-12-19T02:55:50.128169Z","shell.execute_reply.started":"2021-12-19T02:55:47.390771Z","shell.execute_reply":"2021-12-19T02:55:50.126790Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### ⬇️ Handy Functions","metadata":{}},{"cell_type":"code","source":"def show_values_on_bars(axs, h_v=\"v\", space=0.4):\n    '''Plots the value at the end of the a seaborn barplot.\n    axs: the ax of the plot\n    h_v: weather or not the barplot is vertical/ horizontal'''\n    \n    def _show_on_single_plot(ax):\n        if h_v == \"v\":\n            for p in ax.patches:\n                _x = p.get_x() + p.get_width() / 2\n                _y = p.get_y() + p.get_height()\n                value = int(p.get_height())\n                ax.text(round(_x, 5), round(_y, 5), format(round(value, 5), ','), ha=\"center\") \n        elif h_v == \"h\":\n            for p in ax.patches:\n                _x = p.get_x() + p.get_width() + float(space)\n                _y = p.get_y() + p.get_height()\n                value = int(p.get_width())\n                ax.text(_x, _y, format(value, ','), ha=\"left\")\n\n    if isinstance(axs, np.ndarray):\n        for idx, ax in np.ndenumerate(axs):\n            _show_on_single_plot(ax)\n    else:\n        _show_on_single_plot(axs)\n        \n        \ndef offset_png(x, y, path, ax, zoom, offset, border=2):\n    '''For adding other .png images to the graph.\n    source: https://stackoverflow.com/questions/61971090/how-can-i-add-images-to-bars-in-axes-matplotlib'''\n    \n    img = plt.imread(path)\n    im = OffsetImage(img, zoom=zoom)\n    im.image.axes = ax\n    x_offset = offset\n    ab = AnnotationBbox(im, (x, y), xybox=(x_offset, 0), frameon=False,\n                        xycoords='data', boxcoords=\"offset points\", pad=0)\n    ax.add_artist(ab)\n    \n    \ndef get_image_metadata(study_id, df):\n    '''Returns the label and bounding boxes (if any)\n    for a speciffic study id.'''\n    \n    data = df[df[\"study_id\"] == study_id]\n    \n    if data[\"Negative for Pneumonia\"].values == 1:\n        label = \"negative_for_pneumonia\"\n    elif data[\"Typical Appearance\"].values == 1:\n        label = \"typical\"\n    elif data[\"Indeterminate Appearance\"].values == 1:\n        label = \"indeterminate\"\n    else:\n        label = \"atypical\"\n        \n    bbox = list(data[\"boxes\"].values)\n    \n    return label, bbox\n\n\ndef save_dataset_artifact(run_name, artifact_name, path):\n    '''Saves dataset to W&B Artifactory.\n    run_name: name of the experiment\n    artifact_name: under what name should the dataset be stored\n    path: path to the dataset'''\n    \n    run = wandb.init(project='siim-covid19', \n                     name=run_name, \n                     config=CONFIG, anonymous=\"allow\")\n    artifact = wandb.Artifact(name=artifact_name, \n                              type='dataset')\n    artifact.add_file(path)\n\n    wandb.log_artifact(artifact)\n    wandb.finish()\n    print(\"Artifact has been saved successfully.\")\n    \n    \ndef create_wandb_plot(x_data=None, y_data=None, x_name=None, y_name=None, title=None, log=None, plot=\"line\"):\n    '''Create and save lineplot/barplot in W&B Environment.\n    x_data & y_data: Pandas Series containing x & y data\n    x_name & y_name: strings containing axis names\n    title: title of the graph\n    log: string containing name of log'''\n    \n    data = [[label, val] for (label, val) in zip(x_data, y_data)]\n    table = wandb.Table(data=data, columns = [x_name, y_name])\n    \n    if plot == \"line\":\n        wandb.log({log : wandb.plot.line(table, x_name, y_name, title=title)})\n    elif plot == \"bar\":\n        wandb.log({log : wandb.plot.bar(table, x_name, y_name, title=title)})\n    elif plot == \"scatter\":\n        wandb.log({log : wandb.plot.scatter(table, x_name, y_name, title=title)})\n        \n        \ndef create_wandb_hist(x_data=None, x_name=None, title=None, log=None):\n    '''Create and save histogram in W&B Environment.\n    x_data: Pandas Series containing x values\n    x_name: strings containing axis name\n    title: title of the graph\n    log: string containing name of log'''\n    \n    data = [[x] for x in x_data]\n    table = wandb.Table(data=data, columns=[x_name])\n    wandb.log({log : wandb.plot.histogram(table, x_name, title=title)})\n    \n    \ndef return_coords(box):\n    '''Returns coordinates from a bbox'''\n    # Get the list of dictionaries\n    box = ast.literal_eval(box)[0]\n    # Get the exact x and y coordinates\n    x1, y1, x2, y2 = box[\"x\"], box[\"y\"], box[\"x\"] + box[\"width\"], box[\"y\"] + box[\"height\"]\n    # Save coordinates\n    return (int(x1), int(y1), int(x2), int(y2))\n\n\ndef fix_inverted_radiograms(data, img):\n    '''Fixes inverted radiograms - with PhotometricInterpretation == \"MONOCHROME1\"\n    data: the .dcm dataset\n    img: the .dcm pixel_array'''\n    \n    if data.PhotometricInterpretation == \"MONOCHROME1\":\n        img = np.amax(img) - img\n    \n    img = img - np.min(img)\n    img = img / np.max(img)\n    img = (img * 255).astype(np.uint8)\n    \n    return img","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-12-19T03:00:41.772949Z","iopub.execute_input":"2021-12-19T03:00:41.773468Z","iopub.status.idle":"2021-12-19T03:00:41.800780Z","shell.execute_reply.started":"2021-12-19T03:00:41.773435Z","shell.execute_reply":"2021-12-19T03:00:41.799702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Function Dictionary : **\n1. show_values_on_bars(axs, h_v=\"v\", space=0.4):\n    '''Plots the value at the end of the a seaborn barplot.\n    axs: the ax of the plot\n    h_v: weather or not the barplot is vertical/ horizontal'''\n2. _show_on_single_plot(ax):\n    if the h_v = vertical ","metadata":{}},{"cell_type":"markdown","source":"# 1. 🗃 Metadata\n\nOur data consists of images + `.csv` files, containing custom information for each radiography.\n\n**Metadata structure**:\n1. `train_study_level.csv` - contains one row for each study, including correct labels.\n2. `train_image_level.csv` - containing one row for each image, including both correct labels and any bounding boxes in a dictionary format\n\n<center><img src=\"https://i.imgur.com/WFXxolI.png\" width=650></center>\n\n> 📌 **Important**: An image can have *multiple bounding boxes*.","metadata":{}},{"cell_type":"code","source":"# Save data to W&B Dashboard\nsave_dataset_artifact(run_name='save-train1',\n                      artifact_name='train_study_level', \n                      path=\"../input/siim-covid19-detection/train_study_level.csv\")\n\nsave_dataset_artifact(run_name='save-train2',\n                      artifact_name='train_image_level', \n                      path=\"../input/siim-covid19-detection/train_image_level.csv\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:00:46.166956Z","iopub.execute_input":"2021-12-19T03:00:46.167346Z","iopub.status.idle":"2021-12-19T03:01:04.781008Z","shell.execute_reply.started":"2021-12-19T03:00:46.167306Z","shell.execute_reply":"2021-12-19T03:01:04.779475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- The function **save_dataset_artifact**: Saves dataset to W&B Artifactory.\n    run_name: name of the experiment\n    \n    - **artifact_name**: under what name should the dataset be stored\n    path: path to the dataset'''","metadata":{}},{"cell_type":"code","source":"# Read in metadata\ntrain_study = pd.read_csv(\"../input/siim-covid19-detection/train_study_level.csv\")\ntrain_image = pd.read_csv(\"../input/siim-covid19-detection/train_image_level.csv\")\n\nprint(color.BOLD + \"Train Study Shape:\" + color.END, train_study.shape, \"\\n\" +\n      color.BOLD + \"Train Image Shape:\" + color.END, train_image.shape, \"\\n\" +\n      \"\\n\" +\n      \"Note: There are {} missing values in train_image.\".\\\n                              format(train_image[\"boxes\"].isna().sum()), \"\\n\" +\n      \"This happens for labels = 'none' - no checkboxes.\", 3*\"\\n\")\n\n\n# Head of our 2 training metadata\ndf1_styler = train_study.head(3).style.set_table_attributes(\"style='display:inline'\").\\\n                                set_caption('TRAIN STUDY')\ndf2_styler = train_image.head(3).style.set_table_attributes(\"style='display:inline'\").\\\n                                set_caption('TRAIN IMAGE')","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:02:01.821886Z","iopub.execute_input":"2021-12-19T03:02:01.822293Z","iopub.status.idle":"2021-12-19T03:02:01.924402Z","shell.execute_reply.started":"2021-12-19T03:02:01.822256Z","shell.execute_reply":"2021-12-19T03:02:01.923411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Train study , Train image df - **getting rows and columns**\nC**SV's color.BOLD**- Uses a color and bold till the particular text until we mention **color.END**\nAlso note that we are removing the missing values in train_image format() - allows to do data \n\nformatting in train_image[\"boxes\"] section removing null values **is.na().sum()** - tells us how many null values are there {} adds the information\nSo here we get to know that \n**Train Study has 6054 rows and 5 columns and Train Image has 6334 Rows and 4 Columns**","metadata":{}},{"cell_type":"code","source":"# Head of our 2 training metadata\ndf1_styler = train_study.head(3).style.set_table_attributes(\"style='display:inline'\").\\\n                                set_caption('TRAIN STUDY')\ndf2_styler = train_image.head(3).style.set_table_attributes(\"style='display:inline'\").\\\n                                set_caption('TRAIN IMAGE')\n\n","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:03:46.301958Z","iopub.execute_input":"2021-12-19T03:03:46.302374Z","iopub.status.idle":"2021-12-19T03:03:46.308087Z","shell.execute_reply.started":"2021-12-19T03:03:46.302329Z","shell.execute_reply":"2021-12-19T03:03:46.306961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We are finding first few rows (to be exact 3) of the data \nstyle.set_table_attributes --> styles the attributes of table in particular manner \nset_caption ---> sets the caption of the tables \ndisplay_html --> When you want to display HTML in the output in Jupyter notebook","metadata":{}},{"cell_type":"code","source":"print(df1_styler)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:06:15.414012Z","iopub.execute_input":"2021-12-19T03:06:15.414714Z","iopub.status.idle":"2021-12-19T03:06:15.424072Z","shell.execute_reply.started":"2021-12-19T03:06:15.414654Z","shell.execute_reply":"2021-12-19T03:06:15.423171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now if we directly priint the dataframe objects it shows the location (in this case hexadecimal bec it has abc in it ) where the df object is stored ","metadata":{}},{"cell_type":"code","source":"#df1_styler + df2_styler","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:05:04.547829Z","iopub.execute_input":"2021-12-19T03:05:04.548203Z","iopub.status.idle":"2021-12-19T03:05:04.578873Z","shell.execute_reply.started":"2021-12-19T03:05:04.548173Z","shell.execute_reply":"2021-12-19T03:05:04.577788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This will give error since it is not possible to print two dataframe objects directly together , Thats why we use display_html method ","metadata":{}},{"cell_type":"code","source":"display_html(df1_styler._repr_html_() + df2_styler._repr_html_(), raw=True)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:06:54.955285Z","iopub.execute_input":"2021-12-19T03:06:54.955682Z","iopub.status.idle":"2021-12-19T03:06:54.965234Z","shell.execute_reply.started":"2021-12-19T03:06:54.955647Z","shell.execute_reply":"2021-12-19T03:06:54.964246Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we see that using display_html method we are able to print two dataframe objects at once ","metadata":{}},{"cell_type":"markdown","source":"## 1.1 train_study analysis\n\n🔎 **Findings**:\n1. `id`: there are 6,054 unique ids - there are **no duplicates**\n2. `target`: one of our targets is to predict is the radiography is `negative_for_pneumonia`, has `typical` appearance, has `indeterminate` appearance or it's just `atypical`.\n3. an image can have **positive value for only 1 label**. For example, there aren't any images which are both `negative_for_pneumonia` and `indeterminate appearance` in the same time. It's only one or the other.\n4. **class imbalance is present** - especially for `Indeterminate Appearance` and `Atypical Appearance`\n\n### Target labels distribution\n> Now let's see how the **labels we'll have to predict** are layed out.","metadata":{}},{"cell_type":"code","source":"run = wandb.init(project='siim-covid19', name='metadata_eda', config=CONFIG, anonymous=\"allow\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:11:01.827227Z","iopub.execute_input":"2021-12-19T03:11:01.827678Z","iopub.status.idle":"2021-12-19T03:11:05.736964Z","shell.execute_reply.started":"2021-12-19T03:11:01.827643Z","shell.execute_reply":"2021-12-19T03:11:05.735688Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**wandb.init()** spawns a new background process to log data to a run, and it also syncs data to wandb.ai by default so you can see live visualizations.\nCall **wandb.init()** to start a run before logging data with wandb.log():\n\n**project**\n(str, optional) The name of the project where you're sending the new run. If the project is not specified, the run is put in an \"Uncategorized\" project.\n\n**name**\n(str, optional) A short display name for this run, which is how you'll identify this run in the UI. By default we generate a random two-word name that lets you easily cross-reference runs from the table to charts. Keeping these run names short makes the chart legends and tables easier to read. If you're looking for a place to save your hyperparameters, we recommend saving those in config.\n\n**config**\n(dict, argparse, absl.flags, str, optional) This sets wandb.config, a dictionary-like object for saving inputs to your job, like hyperparameters for a model or settings for a data preprocessing job. The config will show up in a table in the UI that you can use to group, filter, and sort runs. Keys should not contain . in their names, and values should be under 10 MB. If dict, argparse or absl.flags: will load the key value pairs into the wandb.config object. If str: will look for a yaml file by that name, and load config from that file into the wandb.config object.\n\n**anonymous**\n(str, optional) Controls anonymous data logging. Options: - \"never\" (default): requires you to link your W&B account before tracking the run so you don't accidentally create an anonymous run. - \"allow\": lets a logged-in user track runs with their account, but lets someone who is running the script without a W&B account see the charts in the UI. - \"must\": sends the run to an anonymous account instead of to a signed-up user account.","metadata":{}},{"cell_type":"code","source":"print(train_study[\"id\"].head(5))","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:11:39.330837Z","iopub.execute_input":"2021-12-19T03:11:39.331260Z","iopub.status.idle":"2021-12-19T03:11:39.345781Z","shell.execute_reply.started":"2021-12-19T03:11:39.331223Z","shell.execute_reply":"2021-12-19T03:11:39.344966Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Process id\ntrain_study[\"study_id\"] = train_study[\"id\"].apply(lambda x: x.split(\"_\")[0])\nprint(train_study[\"study_id\"].head(5))","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:13:04.577484Z","iopub.execute_input":"2021-12-19T03:13:04.577916Z","iopub.status.idle":"2021-12-19T03:13:04.598908Z","shell.execute_reply.started":"2021-12-19T03:13:04.577878Z","shell.execute_reply":"2021-12-19T03:13:04.597777Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we are **trying to change the name** of the id column in train_study as '**study_id**'\n**.apply** --> if we want to apply some function in the column \n\nA **lambda function** is a small function containing a single expression. Lambda functions can also act as anonymous functions where they don’t require any name. These are very helpful when we have to perform small tasks with less code.\nuse lambda functions when we have to pass a small function to another function\nHere lambda x : is being passed the function x.split()\nhere we are splitting 00086460a852_study into two parts 00086460a852 as [0] index and study as [1] index and we just want the id numbernot study attached to it \nSo we do x.split(\"_\")[0]\n\n","metadata":{}},{"cell_type":"code","source":"# Data for plots\npneumonia = train_study[\"Negative for Pneumonia\"]\ntypical = train_study[\"Typical Appearance\"]\nindeterminate = train_study[\"Indeterminate Appearance\"]\natypical = train_study[\"Atypical Appearance\"]","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:13:22.515270Z","iopub.execute_input":"2021-12-19T03:13:22.516019Z","iopub.status.idle":"2021-12-19T03:13:22.524748Z","shell.execute_reply.started":"2021-12-19T03:13:22.515967Z","shell.execute_reply":"2021-12-19T03:13:22.523440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Storing values of different columns in different variables for easy implementation ","metadata":{}},{"cell_type":"code","source":"# Plotting\nfig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(21,20))\naxs = [ax1, ax2, ax3, ax4]\ndfs = [pneumonia, typical, indeterminate, atypical]\ntitles = [\"Pneumonia\", \"Typical\", \"Indeterminate\", \"Atypical\"]\n\nfor ax, df, title in zip(axs, dfs, titles):\n    sns.countplot(y=df, ax=ax, palette=my_colors[1:])\n    ax.set_title(title, fontsize=25, weight='bold')\n    show_values_on_bars(ax, h_v=\"h\", space=0.4)\n    ax.set_xticklabels([])\n    ax.set_ylabel('')\n    ax.set_xlabel('')\n    \n# Virus png\npath='../input/siimfisabiorsna-covid-2021/PinClipart.com_virus-clip-art_742280.png'\noffset_png(x=4378, y=1, path=path, ax=ax1, zoom=0.05, offset=-360, border=1)\noffset_png(x=2855, y=1, path=path, ax=ax2, zoom=0.05, offset=-50, border=1)\noffset_png(x=1049, y=1, path=path, ax=ax3, zoom=0.05, offset=-43, border=1)\noffset_png(x=474, y=1, path=path, ax=ax4, zoom=0.05, offset=40, border=1)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:13:43.847771Z","iopub.execute_input":"2021-12-19T03:13:43.848353Z","iopub.status.idle":"2021-12-19T03:13:45.130544Z","shell.execute_reply.started":"2021-12-19T03:13:43.848318Z","shell.execute_reply":"2021-12-19T03:13:45.129483Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**plt.subplots** - to create figure and multiple axes (most useful)\npyplot.subplots(nrows=1, ncols=1, , sharex=False, sharey=False, squeeze=True, subplot_kw=None, gridspec_kw=None, *fig_kw)\nnrows, ncolsint, default: 1 here we have mentioned 2,2 so 2 rows and 2 col Number of rows/columns of the subplot grid.\nReturns fig : Figure\naxs = [ax1, ax2, ax3, ax4] - Making a list of axs dfs = [pneumonia, typical, indeterminate, atypical] - making a list of dfs for diffrent catagories titles = [\"Pneumonia\", \"Typical\", \"Indeterminate\", \"Atypical\"] - title\n\n\nNow using for l**oop zip(*iterables)** . The function takes in iterables as arguments and returns an iterator. This iterator generates a series of tuples containing elements from each iterable. zip() can accept any type of iterable, such as files, lists, tuples, dictionaries, sets, and so on.\n**sns.countplot** - Seaborn library has a function countpot() for creating couplot\nseaborn.countplot(, x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=0.75, dodge=True, ax=None, kwargs)*\nx, y, hue : names of variables in data or vector data, optional This is the input provided for building the plot. data : DataFrame, array, or list of arrays, optional Here we pass the data for the purpose of plotting the graph. order, hue_order : lists of strings, optional This is the order used for plotting categorical levels. orient : “v” | “h”, optional Through this parameter, we can set the orientation of plot as horizontal or vertical. color : matplotlib color, optional In this parameter, we are setting the color of the plot. palette : palette name, list, or dict The palette will be deciding the colors for the graph. ax : matplotlib Axes, optional These are the axes over which the plot is built.\n\n**ax.set_title** - sets the title\nshow_values_on_bars(ax, h_v=\"h\", space=0.4 - shows values in graph on axis with horizontal pattern and 0.4 space","metadata":{}},{"cell_type":"code","source":"# Save plots into W&B Dashboard\nfor title, df in zip(titles, dfs):\n    create_wandb_plot(x_data=[0, 1], \n                      y_data=df.value_counts().values, \n                      x_name=\"Flag\", y_name=\"Freq\", title=title, \n                      log=title, plot=\"bar\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:15:51.186518Z","iopub.execute_input":"2021-12-19T03:15:51.187083Z","iopub.status.idle":"2021-12-19T03:15:59.465001Z","shell.execute_reply.started":"2021-12-19T03:15:51.187035Z","shell.execute_reply":"2021-12-19T03:15:59.463859Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"creating plot in wandb","metadata":{}},{"cell_type":"markdown","source":"### How many instances per label?\n> 📌 **Note**: 50% of our images have typical appearance. The rest 50% is split in order into *negative for pneumonia* (28%), *indeterminate* (17%) and the rest *atypical*.","metadata":{}},{"cell_type":"code","source":"# Get data and transform frequencies to percentages\ndf = train_study.groupby(['Negative for Pneumonia', 'Typical Appearance',\n       'Indeterminate Appearance', 'Atypical Appearance']).count().reset_index()\n\ndf[\"label\"] = ['Atypical Appearance', 'Indeterminate Appearance',\n               'Typical Appearance', 'Negative for Pneumonia']\ndf[\"perc\"] = df[\"id\"]/df[\"id\"].sum()*100\n\n# Plot\nbar,ax = plt.subplots(figsize=(21,10))\nax = sns.barplot(x=df[\"label\"], y=df[\"perc\"], \n                 ci=None, palette=my_colors, orient='v')\nax.set_title(\"Label % in Total Observations\", fontsize=25,\n             weight = \"bold\")\nax.set_xlabel(\" \")\nax.set_ylabel(\"Percentage\")\nfor rect in ax.patches:\n    ax.text (rect.get_x() + rect.get_width() / 2,rect.get_height(),\n             \"%.1f%%\"% rect.get_height(), weight='bold')","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:17:08.679801Z","iopub.execute_input":"2021-12-19T03:17:08.680341Z","iopub.status.idle":"2021-12-19T03:17:08.934753Z","shell.execute_reply.started":"2021-12-19T03:17:08.680279Z","shell.execute_reply":"2021-12-19T03:17:08.933840Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**groupby** - allows you to split your data into separate groups to perform computations for better analysis.\nHere we are grouping by splitting by Negative for Pneumonia', 'Typical Appearance',\n       'Indeterminate Appearance', 'Atypical Appearance\nWe are plotting labels vs the percentage of apperances in chest MRI's","metadata":{}},{"cell_type":"code","source":"create_wandb_plot(x_data=df[\"label\"].values,\n                  y_data=df[\"perc\"].values,\n                  x_name=\"Label\", y_name=\"Percentage\", \n                  title=\"Label % in Total Observations\", \n                  log=\"perc\", plot=\"bar\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:18:33.201312Z","iopub.execute_input":"2021-12-19T03:18:33.201752Z","iopub.status.idle":"2021-12-19T03:18:35.167193Z","shell.execute_reply.started":"2021-12-19T03:18:33.201718Z","shell.execute_reply":"2021-12-19T03:18:35.166254Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wandb.finish()","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:21:17.553483Z","iopub.execute_input":"2021-12-19T03:21:17.553921Z","iopub.status.idle":"2021-12-19T03:21:20.974714Z","shell.execute_reply.started":"2021-12-19T03:21:17.553887Z","shell.execute_reply":"2021-12-19T03:21:20.973310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1.2 train_image analysis\n\n🔎 **Findings**:\n1. `StudyInstanceUID` corresponds 1:1 to `new_id` created for `train_study`.\n2. `image_id` is unique in the `image_train` data.\n3. There are images with multiple bounding boxes!\n4. There can be multiple images per study!\n\n### How many images have some sort of abnormality?\n\nTo have a bounding box we need to have something weird detected in the scan. If there is nothing weird ... then we don't need a bounding box!","metadata":{}},{"cell_type":"code","source":"run = wandb.init(project='siim-covid19', name='train_imgs_eda', config=CONFIG, anonymous=\"allow\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:22:31.020482Z","iopub.execute_input":"2021-12-19T03:22:31.020905Z","iopub.status.idle":"2021-12-19T03:22:34.831942Z","shell.execute_reply.started":"2021-12-19T03:22:31.020872Z","shell.execute_reply":"2021-12-19T03:22:34.830916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Initalising wandb environment for our image analysis ","metadata":{}},{"cell_type":"code","source":"train_image.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:23:20.810463Z","iopub.execute_input":"2021-12-19T03:23:20.810846Z","iopub.status.idle":"2021-12-19T03:23:20.827757Z","shell.execute_reply.started":"2021-12-19T03:23:20.810814Z","shell.execute_reply":"2021-12-19T03:23:20.826952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we can see in **id col _image is unnecesary** so we will remove that using code below  ","metadata":{}},{"cell_type":"code","source":"# Process id\ntrain_image[\"image_id\"] = train_image[\"id\"].apply(lambda x: x.split(\"_\")[0])\n\n# Data for plotting\ndf = train_image[\"label\"].apply(lambda x: x.split(\" \")[0]).\\\n                                    value_counts().reset_index()\n\n# Plot\nplt.figure(figsize=(21, 15))\nax = sns.barplot(data=df, y=\"index\", x=\"label\", palette=my_colors[3:])\nshow_values_on_bars(ax, h_v=\"h\", space=0.4)\nplt.title(\"How many images have a bounding box present?\", \n          fontsize=30, weight='bold')\nplt.xticks([])\nplt.ylabel('')\nplt.xlabel('');\n\n# Virus png\npath='../input/siimfisabiorsna-covid-2021/PinClipart.com_virus-clip-art_742280.png'\noffset_png(x=4294, y=0, path=path, ax=ax, zoom=0.1, offset=-110, border=1)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:25:06.090882Z","iopub.execute_input":"2021-12-19T03:25:06.091261Z","iopub.status.idle":"2021-12-19T03:25:06.455776Z","shell.execute_reply.started":"2021-12-19T03:25:06.091226Z","shell.execute_reply":"2021-12-19T03:25:06.454465Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we will try to plot index VS labels To se how many images have bounding box present\n- **plt.figure()**- creates a figure kind of object. when we want to tweak the size of the figure and when we want to add multiple Axes objects in a single figure.\n\n- **sns.barplot** - making a barplot\n\nHere we can observe that 4,294 cases have bounding boxees present and 2,040 have no bounding boxes","metadata":{}},{"cell_type":"code","source":"create_wandb_plot(x_data=df[\"index\"], \n                  y_data=df[\"label\"].values, \n                  x_name=\"BBox\", y_name=\"Freq\", \n                  title=\"Images with bbox\",\n                  log=\"bbox\", plot=\"bar\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:25:42.816081Z","iopub.execute_input":"2021-12-19T03:25:42.816517Z","iopub.status.idle":"2021-12-19T03:25:44.759622Z","shell.execute_reply.started":"2021-12-19T03:25:42.816481Z","shell.execute_reply":"2021-12-19T03:25:44.758496Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### How many images per each study?\n\n> Majority of studies have only 1 images. That being said, we have ~230 studies that have multiple images available (up to 9).","metadata":{}},{"cell_type":"code","source":"train_image.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:27:09.286074Z","iopub.execute_input":"2021-12-19T03:27:09.286482Z","iopub.status.idle":"2021-12-19T03:27:09.303088Z","shell.execute_reply.started":"2021-12-19T03:27:09.286450Z","shell.execute_reply":"2021-12-19T03:27:09.301497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Crate df\ndf = train_image[\"StudyInstanceUID\"].value_counts().reset_index().\\\n                        sort_values(\"StudyInstanceUID\", ascending=False)\nprint(color.BOLD + \"Max number of images available per study:\" + color.END, \n      df[\"StudyInstanceUID\"].max(), \"\\n\" +\n      color.BOLD + \"Min number of images available per study:\" + color.END, \n      df[\"StudyInstanceUID\"].min(), 2*\"\\n\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:27:18.349855Z","iopub.execute_input":"2021-12-19T03:27:18.350256Z","iopub.status.idle":"2021-12-19T03:27:18.636176Z","shell.execute_reply.started":"2021-12-19T03:27:18.350214Z","shell.execute_reply":"2021-12-19T03:27:18.635115Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- **.value_counts()** :returns object containing counts of unique values\n- **.reset_index()** : method to reset index of a Data Frame\n\nHere we are checking the min and max images available per study ","metadata":{}},{"cell_type":"code","source":"\n# Plot\nplt.figure(figsize=(21, 14))\nsns.distplot(a=df[\"StudyInstanceUID\"], color=my_colors[6], \n             hist=False, kde_kws=dict(lw=7, ls=\"-\"))\nplt.title(\"How many images per study?\", \n          fontsize=30, weight='bold')\nplt.xticks([])\nplt.ylabel('Study Frequency')\nplt.xlabel('Image Ids');","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:27:37.163284Z","iopub.execute_input":"2021-12-19T03:27:37.163700Z","iopub.status.idle":"2021-12-19T03:27:37.405767Z","shell.execute_reply.started":"2021-12-19T03:27:37.163667Z","shell.execute_reply":"2021-12-19T03:27:37.404765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wandb.log({\"max_images_on_study\" : df[\"StudyInstanceUID\"].max()})\ncreate_wandb_hist(x_data=df[\"StudyInstanceUID\"], \n                  x_name=\"Image Freq\", title=\"No. images per study\", \n                  log=\"hist\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:27:49.541719Z","iopub.execute_input":"2021-12-19T03:27:49.544777Z","iopub.status.idle":"2021-12-19T03:27:51.740950Z","shell.execute_reply.started":"2021-12-19T03:27:49.544706Z","shell.execute_reply":"2021-12-19T03:27:51.739675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wandb.finish()","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:28:25.255393Z","iopub.execute_input":"2021-12-19T03:28:25.255785Z","iopub.status.idle":"2021-12-19T03:28:27.970042Z","shell.execute_reply.started":"2021-12-19T03:28:25.255753Z","shell.execute_reply":"2021-12-19T03:28:27.969158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> After EDA, my [W&B Dashboard](https://wandb.ai/andrada/siim-covid19?workspace=user-andrada) looks like this:\n\n<center><img src=\"https://i.imgur.com/HQBdtSd.gif\" width=600></center>\n\n### Create the full train dataset\n\nThis is also how the `final_label` will need to look before submission.\n\n<center><img src=\"https://i.imgur.com/8Ckg1L0.png\" width=600></center>","metadata":{}},{"cell_type":"code","source":"# Merge all info together\ntrain = pd.merge(train_image, train_study, \n                 left_on=\"StudyInstanceUID\", right_on=\"study_id\")\n\ntrain.drop([\"id_x\", \"StudyInstanceUID\", \"id_y\"], axis=1, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:30:20.781281Z","iopub.execute_input":"2021-12-19T03:30:20.781734Z","iopub.status.idle":"2021-12-19T03:30:20.799338Z","shell.execute_reply.started":"2021-12-19T03:30:20.781694Z","shell.execute_reply":"2021-12-19T03:30:20.798206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Using **.merge** function we are combinig two different Dataframes and dropping useless columns","metadata":{}},{"cell_type":"code","source":"train.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:30:25.247606Z","iopub.execute_input":"2021-12-19T03:30:25.248177Z","iopub.status.idle":"2021-12-19T03:30:25.263187Z","shell.execute_reply.started":"2021-12-19T03:30:25.248125Z","shell.execute_reply":"2021-12-19T03:30:25.262085Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> Let's also look at the study id `0fd2db233deb`, which has most of the images available. 8 images have nothing unusual in them and 1 is labeled as `Indeterminate Appearance`.","metadata":{}},{"cell_type":"code","source":"train[train[\"study_id\"] == \"0fd2db233deb\"]","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:31:28.150482Z","iopub.execute_input":"2021-12-19T03:31:28.150939Z","iopub.status.idle":"2021-12-19T03:31:28.174592Z","shell.execute_reply.started":"2021-12-19T03:31:28.150902Z","shell.execute_reply":"2021-12-19T03:31:28.173328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. 📷 Images\n\nGood, now that we've explored the metadata and the `target` labels, we can start focusing on the good stuff - meaning the **CT scans**.\n\n<center><img src=\"https://i.imgur.com/DFDDdvD.png\" width=550></center>\n\n### WHAT ARE CT SCANS?\n\n\"A **Computerized Tomography** scan (CT or CAT scan) uses computers and rotating X-ray machines to **create cross-sectional images** of the body. These images provide **more detailed** information than normal X-ray images. They can show the **soft tissues**, **blood vessels**, and **bones** in various parts of the body.\"\n\n<center><img src=\"https://i.imgur.com/5WUxAHZ.png\" width=550></center>","metadata":{}},{"cell_type":"code","source":"run = wandb.init(project='siim-covid19', name='image_explore', config=CONFIG, anonymous=\"allow\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:32:50.704679Z","iopub.execute_input":"2021-12-19T03:32:50.705242Z","iopub.status.idle":"2021-12-19T03:32:54.548518Z","shell.execute_reply.started":"2021-12-19T03:32:50.705206Z","shell.execute_reply":"2021-12-19T03:32:54.547616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Lets create a function **show_dcm_info()** which converts an image file saved in the Digital Imaging and Communications in Medicine (**DICOM**) image format into more visual and scan form .\n\nwe will feed this in wand_logs \nour fig will have 2 rows and 3 col and fig size is 21,10\n\nglob is used to take out files with similar nature \nour train dataset is stored in path : \nf\"../input/siim-covid19-detection/train\"\n\nwe are storing the paths to dcm files here with different study_id \nthe study_idf which we are considering here are shown above \n\nHow to read DICOM files ? \n- using pydicom\nimport pydicom\ndataset = pydicom.dcmread('path/to/file')\nstoring images as arrays in VOI look up tables","metadata":{}},{"cell_type":"code","source":"def show_dcm_info(study_ids, df):\n    '''Show .dcm images along with description.'''\n    wandb_logs = []\n    \n    fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(21,10))\n\n    # Get .dcm paths\n    dcm_paths = [glob.glob(f\"../input/siim-covid19-detection/train/{study_id}/*/*\")[0]\n                 for study_id in study_ids]\n    datasets = [pydicom.dcmread(path) for path in dcm_paths]\n    images = [apply_voi_lut(dataset.pixel_array, dataset) for dataset in datasets]\n\n    # Loop through the information\n    for study_id, data, img, i in zip(study_ids, datasets, images, range(2*3)):\n        # Fix inverted images\n        img = fix_inverted_radiograms(data, img)\n\n        # Below function available in functions section ;)\n        label, bbox = get_image_metadata(study_id, df)\n        \n        # Check for bounding box and add if it's the case\n        try: \n            # For no bbox, the list is [nan]\n            no_box = math.isnan(bbox[0])\n            pass\n        except TypeError:\n            # Retrieve the bounding box\n            all_coords = []\n            for box in bbox:\n                all_coords.append(return_coords(box))\n\n            for (x1, y1, x2, y2) in all_coords:\n                cv2.rectangle(img, (x1, y1), (x2, y2), (0, 80, 255), 15)\n                cv2.putText(img, label, (x1, y1-14), \n                            cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 0, 0), 4)\n                \n        # Plot the image\n        x = i // 3\n        y = i % 3\n        \n        axes[x, y].imshow(img, cmap=\"rainbow\")\n        axes[x, y].set_title(f\"Label: {label} \\n Sex: {data.PatientSex} | Body Part: {data.BodyPartExamined}\", \n                  fontsize=14, weight='bold')\n        axes[x, y].axis('off');\n        \n        # Save to W&B\n        wandb_logs.append(wandb.Image(img, \n                                      caption=f\"Label: {label} \\n Sex: {data.PatientSex} | Body Part: {data.BodyPartExamined}\"))\n          \n    wandb.log({f\"{label}\": wandb_logs})","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:58:44.940826Z","iopub.execute_input":"2021-12-19T03:58:44.941265Z","iopub.status.idle":"2021-12-19T03:58:44.958407Z","shell.execute_reply.started":"2021-12-19T03:58:44.941219Z","shell.execute_reply":"2021-12-19T03:58:44.957280Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Basically we are trying to find the dcm files , reading them and converting them in a readable form , finding the bounding boxes, returning error if no bounding box found and fetching the images from the one we found . ,\nThen we appending it to our wandb_Log in wandbimages","metadata":{}},{"cell_type":"markdown","source":"### Typical Appearance","metadata":{}},{"cell_type":"code","source":"show_dcm_info(study_ids=[\"72044bb44d41\", \"5b65a69885b6\", \"6aa32e76f998\",\n                         \"c9ffe6312921\", \"082cafb03942\", \"d3e83031ebea\"], \n              df=train)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T03:59:19.260561Z","iopub.execute_input":"2021-12-19T03:59:19.260939Z","iopub.status.idle":"2021-12-19T03:59:48.644935Z","shell.execute_reply.started":"2021-12-19T03:59:19.260908Z","shell.execute_reply":"2021-12-19T03:59:48.644006Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we can see that how the dcm information is presented in beautiful cheat radiographs showing bounding boxes around infections showing typical appearance ","metadata":{}},{"cell_type":"markdown","source":"### Atypical Appearance","metadata":{}},{"cell_type":"code","source":"show_dcm_info(study_ids=[\"f807cd855d31\", \"8087e3bc0efe\", \"7249de10ed69\",\n                         \"e300a4e86207\", \"4bac6c7da8b8\", \"f2d30ac37f7b\"], \n              df=train)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:01:33.887290Z","iopub.execute_input":"2021-12-19T04:01:33.887756Z","iopub.status.idle":"2021-12-19T04:02:00.816021Z","shell.execute_reply.started":"2021-12-19T04:01:33.887715Z","shell.execute_reply":"2021-12-19T04:02:00.814875Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we can see that how the dcm information is presented in beautiful cheat radiographs showing bounding boxes around infections showing Atypical appearance ","metadata":{}},{"cell_type":"markdown","source":"### Indeterminate Appearance","metadata":{}},{"cell_type":"code","source":"show_dcm_info(study_ids=[\"b949689a9ef1\", \"fe7e6015560d\", \"feffa20fac13\",\n                         \"747483509d0e\", \"c70369caef91\", \"1e1b4b1b53cb\"], \n              df=train)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:03:01.976943Z","iopub.execute_input":"2021-12-19T04:03:01.977414Z","iopub.status.idle":"2021-12-19T04:03:30.391338Z","shell.execute_reply.started":"2021-12-19T04:03:01.977344Z","shell.execute_reply":"2021-12-19T04:03:30.390515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Negative for Pneumonia","metadata":{}},{"cell_type":"code","source":"show_dcm_info(study_ids=[\"612ea5194007\", \"db14e640e037\", \"d4ab797396b4\",\n                         \"6ae8a88c4b0c\", \"b3cf474bee3b\", \"0ba55e5422ab\"], \n              df=train)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:03:42.401167Z","iopub.execute_input":"2021-12-19T04:03:42.401918Z","iopub.status.idle":"2021-12-19T04:03:58.256132Z","shell.execute_reply.started":"2021-12-19T04:03:42.401877Z","shell.execute_reply":"2021-12-19T04:03:58.255343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.2 Magic - save images & bounding boxes to W&B\n\n> Here we are trying to create a function which basically ingests images,boundingbox (bboxes),truee_label, class_id_to_label\nBasically we are trying to save images and bounding boxes in W&B","metadata":{}},{"cell_type":"code","source":"def wandb_bbox(image, bboxes, true_label, class_id_to_label):\n    all_boxes = []\n    for bbox in bboxes:\n        box_data = {\"position\": {\n                        \"minX\": bbox[0],\n                        \"minY\": bbox[1],\n                        \"maxX\": bbox[2],\n                        \"maxY\": bbox[3]\n                    },\n                     \"class_id\" : int(true_label),\n                     \"box_caption\": class_id_to_label[true_label],\n                     \"domain\" : \"pixel\"}\n        all_boxes.append(box_data)\n    \n\n    return wandb.Image(image, boxes={\n        \"ground_truth\": {\n            \"box_data\": all_boxes,\n          \"class_labels\": class_id_to_label\n        }\n    })\n\n\ndef resize_img_and_coord(img, coord, resize):\n    '''Resizes the image and its coordinates.\n    img: the pixel.array image\n    coord: the speciffic coordinates from return_coordinates() function\n    resize: and integer specifying the desired size of new image'''\n    \n    # Resize the image\n    w_old, h_old = img.shape\n\n    # Resize the coordinates\n    img = cv2.resize(img, (resize,resize))\n\n    new_x1 = int(coord[0][0] / (w_old/resize))\n    new_y1 = int(coord[0][1] / (h_old/resize))\n    new_x2 = int(coord[0][2] / (w_old/resize))\n    new_y2 = int(coord[0][3] / (h_old/resize))\n    new_coord = [(new_x1, new_y1, new_x2, new_y2)]\n    \n    return img, new_coord","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-12-19T04:04:53.350947Z","iopub.execute_input":"2021-12-19T04:04:53.351371Z","iopub.status.idle":"2021-12-19T04:04:53.364069Z","shell.execute_reply.started":"2021-12-19T04:04:53.351324Z","shell.execute_reply":"2021-12-19T04:04:53.362951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's look at an **example of 10 images** (below you can see how all the code outputs in the [W&B Dashboard](https://wandb.ai/mehreet/siim-covid19?workspace=user-mehreet)!)\n<center><video src=\"https://i.imgur.com/q0jCbza.mp4\" width=650 controls></center>","metadata":{}},{"cell_type":"code","source":"# Get a few example ids (image_id)\nexample_ids = [\"000a312787f2\", \"0012ff7358bc\", \"001398f4ff4f\",\n               \"001bd15d1891\", \"002e9b2128d0\", \"ffbeafe30b77\",\n               \"0022227f5adf\", \"00a129830f4e\", \"01376c1ba556\", \"008ca392cff3\"]\n\n# Read in datas\nstudy_ids = train[train[\"image_id\"].isin(example_ids)][\"study_id\"].values\npaths = [glob.glob(f\"../input/siim-covid19-detection/train/{i}/*/*\")[0]\n         for i in study_ids]\n\n# Retrieve resized information\nimages, coords, labels = [], [], []\nfor path, study_id in zip(paths, study_ids):\n    try:\n        # Read data file\n        data = pydicom.dcmread(path)\n        # Get image data\n        img = apply_voi_lut(data.pixel_array, data)\n        # Get image coordinates\n        label, bbox = get_image_metadata(study_id=study_id, df=train)\n        coord = [return_coords(box) for box in bbox]\n\n        # Fix inverted radiograms + resize\n        img = fix_inverted_radiograms(data, img)\n        resized_img, resized_coord = resize_img_and_coord(img, coord, resize=200)\n\n        images.append(resized_img)\n        coords.append(resized_coord)\n        labels.append(label)\n    except RuntimeError:\n        pass","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:07:50.750820Z","iopub.execute_input":"2021-12-19T04:07:50.751388Z","iopub.status.idle":"2021-12-19T04:07:56.220199Z","shell.execute_reply.started":"2021-12-19T04:07:50.751318Z","shell.execute_reply":"2021-12-19T04:07:56.219102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Map each label to a number\nclass_label_to_id = {'atypical': 0, 'indeterminate': 1, 'typical': 2}\n# And each number to a label\nclass_id_to_label = {val: key for key, val in class_label_to_id.items()}\n\n# Log each image\nwandb_bbox_list = []\n\nfor image, coord, label in zip(images, coords, labels):\n    wandb_bbox_list.append(wandb_bbox(image=image,\n                                      bboxes=coord, \n                                      true_label=class_label_to_id[label],\n                                      class_id_to_label=class_id_to_label))\n\n# Save images to W&B Dashboard\nwandb.log({\"radiograph\": wandb_bbox_list})\n\nprint(color.BOLD + \"Finished! Your Images were uploaded in your W&B Dashboard!\" + color.END)","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:10:18.561748Z","iopub.execute_input":"2021-12-19T04:10:18.562167Z","iopub.status.idle":"2021-12-19T04:10:18.657606Z","shell.execute_reply.started":"2021-12-19T04:10:18.562134Z","shell.execute_reply":"2021-12-19T04:10:18.655809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wandb.finish()","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:10:36.217467Z","iopub.execute_input":"2021-12-19T04:10:36.217957Z","iopub.status.idle":"2021-12-19T04:10:38.866785Z","shell.execute_reply.started":"2021-12-19T04:10:36.217912Z","shell.execute_reply":"2021-12-19T04:10:38.865636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Extract Metadata from .dcm\n\n> 📌 **Important**: We can create *more* **metadata** (more information on the images) from the information stored in the `.dcm` files. Below I am extracting all features stored in each `.dcm` file and storing them into a sepparate dataframe.\n\n## 3.1 Store and Save Metadata\n\n<center><img src=\"https://i.imgur.com/8Fhupoe.png\" width=650></center>","metadata":{}},{"cell_type":"code","source":"def get_observation_data(path):\n    \"\"\"Get information from the .dcm files.\n    path: complete path to the .dcm file\"\"\"\n\n    image_data = pydicom.read_file(path)\n    \n    # Dictionary to store the information from the image\n    observation_data = {\n        \"FileNumber\" : path.split(\"/\")[5],\n        \"Rows\" : image_data.get(\"Rows\"),\n        \"Columns\" : image_data.get(\"Columns\"),\n        \"PatientID\" : image_data.get(\"PatientID\"),\n        \"PatientName\" : image_data.get(\"PatientName\"),\n        \"PhotometricInterpretation\" : image_data.get(\"PhotometricInterpretation\"),\n        \"StudyInstanceUID\" : image_data.get(\"StudyInstanceUID\"),\n        \"SamplesPerPixel\" : image_data.get(\"SamplesPerPixel\"),\n        \"BitsAllocated\" : image_data.get(\"BitsAllocated\"),\n        \"BitsStored\" : image_data.get(\"BitsStored\"),\n        \"HighBit\" : image_data.get(\"HighBit\"),\n        \"PixelRepresentation\" : image_data.get(\"PixelRepresentation\"),\n    }\n\n    # String columns\n    str_columns = [\"ImageType\", \"Modality\", \"PatientSex\", \"BodyPartExamined\"]\n    for k in str_columns:\n        observation_data[k] = str(image_data.get(k)) if k in image_data else None\n\n    \n    return observation_data","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:11:23.691950Z","iopub.execute_input":"2021-12-19T04:11:23.692481Z","iopub.status.idle":"2021-12-19T04:11:23.700140Z","shell.execute_reply.started":"2021-12-19T04:11:23.692446Z","shell.execute_reply":"2021-12-19T04:11:23.699199Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # An example\n# p = \"../input/siim-covid19-detection/train/00792b5c8852/1f52bcb3143e/3fadf4b48db3.dcm\"\n# example = get_observation_data(p)\n# print(example)\n\n# # === GET ALL METADATA ===\n# # Get all paths to .dcm files\n# all_paths = glob.glob(\"../input/siim-covid19-detection/train/*/*/*\")\n\n# # === Get metadata ===\n# exceptions = 0\n# dicts = []\n\n# for path in tqdm.tqdm(all_paths):\n#     # Get .dcm metadata\n#     ### TODO: add .dcm id\n#     try:\n#         d = get_observation_data(path)\n#         dicts.append(d)\n#     except Exception as e:\n#         exceptions += 1\n#         continue\n        \n# # === SAVE METADATA ===\n# # Convert into df\n# meta_train_data = pd.DataFrame(data=dicts, columns=example.keys())\n# meta_train_data[\"\"]\n# # Export information\n# meta_train_data.to_csv(\"meta_train.csv\", index=False)\n\n# print(\"Metadata processed & saved successfuly :)\")","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-12-18T17:37:31.635743Z","iopub.execute_input":"2021-12-18T17:37:31.636591Z","iopub.status.idle":"2021-12-18T17:37:31.657023Z","shell.execute_reply.started":"2021-12-18T17:37:31.636537Z","shell.execute_reply":"2021-12-18T17:37:31.655274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.2 Let's Analyse the new information","metadata":{}},{"cell_type":"code","source":"# Save data to W&B Dashboard\nsave_dataset_artifact(run_name='dave-dcm-meta',\n                   artifact_name='dcm_metadata', \n                      path=\"../input/siimfisabiorsna-covid-2021/meta_train.csv\")","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:11:53.506866Z","iopub.execute_input":"2021-12-19T04:11:53.507459Z","iopub.status.idle":"2021-12-19T04:12:00.231924Z","shell.execute_reply.started":"2021-12-19T04:11:53.507423Z","shell.execute_reply":"2021-12-19T04:12:00.230492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Import\ndcm_meta = pd.read_csv(\"../input/siimfisabiorsna-covid-2021/meta_train.csv\")\ndcm_meta = pd.concat([dcm_meta, train], axis=1)\n\ndcm_meta.head(2)\n#dcm_meta.info()","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:15:43.540579Z","iopub.execute_input":"2021-12-19T04:15:43.540950Z","iopub.status.idle":"2021-12-19T04:15:43.579898Z","shell.execute_reply.started":"2021-12-19T04:15:43.540915Z","shell.execute_reply":"2021-12-19T04:15:43.579164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Show data from particular column\ndcm_meta[dcm_meta[\"Negative for Pneumonia\"].isin([0, 1])]","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:17:38.722589Z","iopub.execute_input":"2021-12-19T04:17:38.723177Z","iopub.status.idle":"2021-12-19T04:17:38.773103Z","shell.execute_reply.started":"2021-12-19T04:17:38.723135Z","shell.execute_reply":"2021-12-19T04:17:38.772307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Patient's Gender","metadata":{}},{"cell_type":"code","source":"# Get the Data\nlabels = ['Negative for Pneumonia', 'Typical Appearance', \n          'Indeterminate Appearance', 'Atypical Appearance']\ndt = dcm_meta.groupby(\"PatientSex\")[labels].sum().reset_index()\n\n# Plotting\nfig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(21,20))\naxs = [ax1, ax2, ax3, ax4]\n\nfor ax, title in zip(axs, labels):\n    sns.barplot(data=dt, x=title, y=\"PatientSex\", \n                ax=ax, palette=my_colors[0:])\n    ax.set_title(title, fontsize=25, weight='bold')\n    show_values_on_bars(ax, h_v=\"h\", space=0.4)\n    ax.set_xticklabels([])\n    ax.set_ylabel('')\n    ax.set_xlabel('')","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:19:31.752913Z","iopub.execute_input":"2021-12-19T04:19:31.753383Z","iopub.status.idle":"2021-12-19T04:19:32.223298Z","shell.execute_reply.started":"2021-12-19T04:19:31.753329Z","shell.execute_reply":"2021-12-19T04:19:32.222150Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Location of X-rays","metadata":{}},{"cell_type":"code","source":"# Get the Data\nlabels = ['Negative for Pneumonia', 'Typical Appearance', \n          'Indeterminate Appearance', 'Atypical Appearance']\ndt = dcm_meta.groupby(\"BodyPartExamined\")[labels].sum().reset_index()\ndt = dt.sort_values(\"Negative for Pneumonia\", ascending=False)\n\n# Plotting\nfig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(21,20))\naxs = [ax1, ax2, ax3, ax4]\n\nfor ax, title in zip(axs, labels):\n    sns.barplot(data=dt, x=title, y=\"BodyPartExamined\", \n                ax=ax, palette=my_colors)\n    ax.set_title(title, fontsize=25, weight='bold')\n    show_values_on_bars(ax, h_v=\"h\", space=0.4)\n    ax.set_xticklabels([])\n    ax.set_ylabel('')\n    ax.set_xlabel('')\n    fig.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:20:08.088394Z","iopub.execute_input":"2021-12-19T04:20:08.088922Z","iopub.status.idle":"2021-12-19T04:20:09.466691Z","shell.execute_reply.started":"2021-12-19T04:20:08.088868Z","shell.execute_reply":"2021-12-19T04:20:09.465641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### MONOCHROME1 or MONOCHROME2? That's the question ...","metadata":{}},{"cell_type":"code","source":"# Get the Data\nlabels = ['Negative for Pneumonia', 'Typical Appearance', \n          'Indeterminate Appearance', 'Atypical Appearance']\ndt = dcm_meta.groupby(\"PhotometricInterpretation\")[labels].sum().reset_index()\n\n# Plotting\nfig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(21,20))\naxs = [ax1, ax2, ax3, ax4]\n\nfor ax, title in zip(axs, labels):\n    sns.barplot(data=dt, x=title, y=\"PhotometricInterpretation\", \n                ax=ax, palette=my_colors[5:])\n    ax.set_title(title, fontsize=25, weight='bold')\n    show_values_on_bars(ax, h_v=\"h\", space=0.4)\n    ax.set_xticklabels([])\n    ax.set_ylabel('')\n    ax.set_xlabel('')\n    fig.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2021-12-19T04:22:40.360977Z","iopub.execute_input":"2021-12-19T04:22:40.361433Z","iopub.status.idle":"2021-12-19T04:22:41.190032Z","shell.execute_reply.started":"2021-12-19T04:22:40.361393Z","shell.execute_reply":"2021-12-19T04:22:41.188706Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"MONOCHROME1\nPixel data represent a single monochrome image plane. The minimum sample value is intended to be displayed as white after any VOI gray scale transformations have been performed. See PS3.4. This value may be used only when Samples per Pixel (0028,0002) has a value of 1. May be used for pixel data in a Native (uncompressed) or Encapsulated (compressed) format; see Section 8.2 in PS3.5 .\n\nMONOCHROME2\nPixel data represent a single monochrome image plane. The minimum sample value is intended to be displayed as black after any VOI gray scale transformations have been performed. See PS3.4. This value may be used only when Samples per Pixel (0028,0002) has a value of 1. May be used for pixel data in a Native (uncompressed) or Encapsulated (compressed) format; see Section 8.2 in PS3.5 .\n\nPALETTE COLOR\nPixel data describe a color image with a single sample per pixel (single image plane). The pixel value is used as an index into each of the Red, Blue, and Green Palette Color Lookup Tables (0028,1101-1103&1201-1203). This value may be used only when Samples per Pixel (0028,0002) has a value of 1. May be used for pixel data in a Native (uncompressed) or Encapsulated (compressed) format; see Section 8.2 in PS3.5 . When the Photometric Interpretation is Palette Color; Red, Blue, and Green Palette Color Lookup Tables shall be present.\n\nRGB\nPixel data represent a color image described by red, green, and blue image planes. The minimum sample value for each color plane represents minimum intensity of the color. This value may be used only when Samples per Pixel (0028,0002) has a value of 3. Planar Configuration (0028,0006) may be 0 or 1. May be used for pixel data in a Native (uncompressed) or Encapsulated (compressed) format; see Section 8.2 in PS3.5 .\n\n","metadata":{}},{"cell_type":"markdown","source":"> The Exploratory Dashboard in W&B!\n<center><video src=\"https://i.imgur.com/P8tdXpU.mp4\" width=650 controls></center>\n\n","metadata":{}}]}