{"metadata": {"language_info": {"mimetype": "text/x-python", "pygments_lexer": "ipython3", "codemirror_mode": {"name": "ipython", "version": 3}, "name": "python", "nbconvert_exporter": "python", "file_extension": ".py", "version": "3.6.1"}, "kernelspec": {"language": "python", "name": "python3", "display_name": "Python 3"}}, "nbformat": 4, "nbformat_minor": 2, "cells": [{"metadata": {"_cell_guid": "966bee1e-60b6-4a49-bf85-9c0492c54cab", "_uuid": "2bea4bb07bc2719c40121c4618ba3f029e7c64e5"}, "execution_count": null, "outputs": [], "source": "<H1>Overview</H1>\n\nThe High Definition-Advanced Imaging Technology (HD-AIT) system files supplied in this contest range in size from 10MB to over 2GB per subject.  With just under 1200 examples, almost any approach will have to substantially reduce the size of the data to be fed into a machine learning model.  In the instructions, the organizers suggest that one may even be able to win the contest with one of the smaller image suites. So in this notebook, I take a whack at preprocessing the lowest res images we have and providing some basic building blocks for the preprocessing pipeline.\n\nA quick disclaimer, I'm not an expert on these systems or the related scans.  If you see something I've misunderstood or you think I've made an error, let me know and I'll correct it.  This contest is aimed at a critical problem.  I'm in this to help us get better at threat detection.  The community can definitely improve the predictive veracity of these scans.  Anyway, I'm excited to get going so let's jump in.  \n\nTo begin I collect all of the imports used in the notebook at the top.  It makes it easier when you're converting to a preprocessing script.", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "318c4bad-bca2-47f0-a446-6718c91a1779", "_uuid": "8d75dd1df97c2d3fbbee49df7dc97922d36a4bf7", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "# imports\nfrom __future__ import print_function\nfrom __future__ import division\nimport numpy as np\nimport os\nfrom matplotlib import pyplot as plt\nimport cv2\nimport pandas as pd\nimport seaborn as sns\nimport scipy.stats as stats\n", "cell_type": "code"}, {"metadata": {"_cell_guid": "b9b815e6-6a8f-40e6-850c-6a5c3ad8fea4", "_uuid": "9154ff9c82d74d25af8ded0c454fd600d638efd7"}, "execution_count": null, "outputs": [], "source": "Next I collect the constants.  You'll need to replace the various file name reference constants with a path to your corresponding folder structure.", "cell_type": "markdown"}, {"metadata": {"collapsed": true, "_cell_guid": "5b6f5d41-3f51-47d1-b6fa-83b6153c8955", "_uuid": "7ac7047806bf64a4b615ca56266aa7bb0c044f3f", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "# constants\nCOLORMAP = 'pink'\nAPS_FILE_NAME = 'tsa_datasets/stage1/aps/00360f79fd6e02781457eda48f85da90.aps'\nBODY_ZONES = 'tsa_datasets/stage1/body_zones.png'\nTHREAT_LABELS = 'tsa_datasets/stage1/stage1_labels.csv'", "cell_type": "code"}, {"metadata": {"_cell_guid": "d1ed2007-fd47-43b4-940c-dfe593710dd1", "_uuid": "d2da34cd2bc539e766d4a4a4cc275f0c03daed2f"}, "execution_count": null, "outputs": [], "source": "<H3>Analyzing Threat Zones</H3>\n\nThe scans to be analyzed in this contest all segment the body into numbered \"threat zones\".  Your modeling and results will have to give probability of contraband within a given threat zone.  The contest sponsors included a  visualization of the threat zones and the corresponding numbering scheme.  (Note that you will need to uncomment the code in the next block for it to run in your own environment)", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "1f49fbc2-e8d4-4385-90f4-f78525bd4189", "_uuid": "955af49973c5a794328ddbab08f297ca25f9c1ba", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "# show the threat zones\n#body_zones_img = plt.imread(BODY_ZONES)\n#fig, ax = plt.subplots(figsize=(15,15))\n#ax.imshow(body_zones_img)\n", "cell_type": "code"}, {"metadata": {"collapsed": false, "_cell_guid": "52fc3a66-1bb1-43f3-b9fd-c309f1d72a5c", "_uuid": "6d6d8638a9618306f0bb3095e174924ffcc94d33", "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "![Body Zones][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/body_zones.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "e33a7d68-e0d5-4902-a46a-46369e698242", "_uuid": "f43dee9a95547ad4c53c01828c3302e488bfd44e"}, "execution_count": null, "outputs": [], "source": "<H3>Segmenting and Cropping the Scans</H3>\n\nThe .aps version of the scans are images (sometimes refered to herein also as slices) with views from shots taken at a regular interval of 22.5-degree rotation, 360 degrees around the subject scanned.  So for each subject, we'll get 16 scans. Each scan will contain a view from a given angle of any visible threat zones.  \n\nPart of my approach in this notebook is to isolate each individual threat zone from every visible angle.  Later, I want to be able to make features out of each individual threat zone from each angle that a given threat zone is visible. That will allow me to later train on each threat zone individually from every view in a 2D format.\n\nEach image (or slice) is 512 x 660 pixels. Since I will want to be able to isolate each threat zone, I divide the full image into \"sectors\".  \n\nIn the [0] image (the one that is face forward, the leftmost figure in the visualization above) the sector numbers used below correspond to the threat zone number.  But as the images in the sequence rotate around the subject, threat zones may be visible from another sector.  Below, we'll use these vertices to capture isolated views of a given threat zone.\n\nOnce the scans have been masked, then they can be cropped to reduce the size of the image.  The crop dimensions make 250x250 scans and are included in sector_crop_list.", "cell_type": "markdown"}, {"metadata": {"collapsed": true, "_cell_guid": "3dd62f34-2ae2-43ff-9f6f-2a060a76138f", "_uuid": "993f6c9cd71afb72e22163bce65087e60fb21e89", "trusted": false}, "execution_count": null, "outputs": [], "source": "# Divide the available space on an image into 16 sectors. In the [0] image these\n# zones correspond to the TSA threat zones.  But on rotated images, the slice\n# list uses the sector that best shows the threat zone\nsector01_pts = np.array([[0,160],[200,160],[200,230],[0,230]], np.int32)\nsector02_pts = np.array([[0,0],[200,0],[200,160],[0,160]], np.int32)\nsector03_pts = np.array([[330,160],[512,160],[512,240],[330,240]], np.int32)\nsector04_pts = np.array([[350,0],[512,0],[512,160],[350,160]], np.int32)\n\n# sector 5 is used for both threat zone 5 and 17\nsector05_pts = np.array([[0,220],[512,220],[512,300],[0,300]], np.int32) \n\nsector06_pts = np.array([[0,300],[256,300],[256,360],[0,360]], np.int32)\nsector07_pts = np.array([[256,300],[512,300],[512,360],[256,360]], np.int32)\nsector08_pts = np.array([[0,370],[225,370],[225,450],[0,450]], np.int32)\nsector09_pts = np.array([[225,370],[275,370],[275,450],[225,450]], np.int32)\nsector10_pts = np.array([[275,370],[512,370],[512,450],[275,450]], np.int32)\nsector11_pts = np.array([[0,450],[256,450],[256,525],[0,525]], np.int32)\nsector12_pts = np.array([[256,450],[512,450],[512,525],[256,525]], np.int32)\nsector13_pts = np.array([[0,525],[256,525],[256,600],[0,600]], np.int32)\nsector14_pts = np.array([[256,525],[512,525],[512,600],[256,600]], np.int32)\nsector15_pts = np.array([[0,600],[256,600],[256,660],[0,660]], np.int32)\nsector16_pts = np.array([[256,600],[512,600],[512,660],[256,660]], np.int32)\n\n# crop dimensions, upper left x, y, width, height\nsector_crop_list = [[ 50,  50, 250, 250], # sector 1\n                    [  0,   0, 250, 250], # sector 2\n                    [ 50, 250, 250, 250], # sector 3\n                    [250,   0, 250, 250], # sector 4\n                    [150, 150, 250, 250], # sector 5/17\n                    [200, 100, 250, 250], # sector 6\n                    [200, 150, 250, 250], # sector 7\n                    [250,  50, 250, 250], # sector 8\n                    [250, 150, 250, 250], # sector 9\n                    [300, 200, 250, 250], # sector 10\n                    [400, 100, 250, 250], # sector 11\n                    [350, 200, 250, 250], # sector 12\n                    [410,   0, 250, 250], # sector 13\n                    [410, 200, 250, 250], # sector 14\n                    [410,   0, 250, 250], # sector 15\n                    [410, 200, 250, 250], # sector 16\n                   ]\n", "cell_type": "code"}, {"metadata": {"_cell_guid": "d0b8cfbc-5a60-4719-a3c3-be361a25be76", "_uuid": "01ab10b88f75c6c8798e45ac6914ce80652bb021"}, "execution_count": null, "outputs": [], "source": "<H3>Threat Zone Slice Lists and Crop Lists</H3>\n\nFor each zone, we pick the sector with the best view of that threat zone.  Since there are 16 images per subject, each zone's slice list contains 16 entries each naming the sector where the given threat zone is best visible.  At some angles, a given threat zone may not be visible.  We just set those values to None so that its easy to iterate the list and ignore the scans where a given zone is not visible.  \n\nIn this notebook, I call this a threat zone slice list.  To create a feature for a given threat zone, I'll iterate this list and use the vertices to establish a region of interest and mask off the threat zone.  Hopefully that's clear.  If not it will be more clear below.\n\nI use the same technique to then crop the segmented images to reduce the size.\n\nAlso its worth noting, that I haven't yet extensively tested to make sure that there is no clipping.  If someone finds a more optimal set of vertices or notes meaningful clipping, please post it in the discussion.", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "28db955f-7f09-4785-a248-f01afecbda11", "_uuid": "c4177e7f059cacc8b64aa8bc8e5a1f4427bfc3e3", "trusted": false}, "execution_count": null, "outputs": [], "source": "# Each element in the zone_slice_list contains the sector to use in the call to roi()\nzone_slice_list = [ [ # threat zone 1\n                      sector01_pts, sector01_pts, sector01_pts, None, \n                      None, None, sector03_pts, sector03_pts, \n                      sector03_pts, sector03_pts, sector03_pts, \n                      None, None, sector01_pts, sector01_pts, sector01_pts ], \n    \n                    [ # threat zone 2\n                      sector02_pts, sector02_pts, sector02_pts, None, \n                      None, None, sector04_pts, sector04_pts, \n                      sector04_pts, sector04_pts, sector04_pts, None, \n                      None, sector02_pts, sector02_pts, sector02_pts ],\n    \n                    [ # threat zone 3\n                      sector03_pts, sector03_pts, sector03_pts, sector03_pts, \n                      None, None, sector01_pts, sector01_pts,\n                      sector01_pts, sector01_pts, sector01_pts, sector01_pts, \n                      None, None, sector03_pts, sector03_pts ],\n    \n                    [ # threat zone 4\n                      sector04_pts, sector04_pts, sector04_pts, sector04_pts, \n                      None, None, sector02_pts, sector02_pts, \n                      sector02_pts, sector02_pts, sector02_pts, sector02_pts, \n                      None, None, sector04_pts, sector04_pts ],\n    \n                    [ # threat zone 5\n                      sector05_pts, sector05_pts, sector05_pts, sector05_pts, \n                      sector05_pts, sector05_pts, sector05_pts, sector05_pts,\n                      None, None, None, None, \n                      None, None, None, None ],\n    \n                    [ # threat zone 6\n                      sector06_pts, None, None, None, \n                      None, None, None, None, \n                      sector07_pts, sector07_pts, sector06_pts, sector06_pts, \n                      sector06_pts, sector06_pts, sector06_pts, sector06_pts ],\n    \n                    [ # threat zone 7\n                      sector07_pts, sector07_pts, sector07_pts, sector07_pts, \n                      sector07_pts, sector07_pts, sector07_pts, sector07_pts, \n                      None, None, None, None, \n                      None, None, None, None ],\n    \n                    [ # threat zone 8\n                      sector08_pts, sector08_pts, None, None, \n                      None, None, None, sector10_pts, \n                      sector10_pts, sector10_pts, sector10_pts, sector10_pts, \n                      sector08_pts, sector08_pts, sector08_pts, sector08_pts ],\n    \n                    [ # threat zone 9\n                      sector09_pts, sector09_pts, sector08_pts, sector08_pts, \n                      sector08_pts, None, None, None,\n                      sector09_pts, sector09_pts, None, None, \n                      None, None, sector10_pts, sector09_pts ],\n    \n                    [ # threat zone 10\n                      sector10_pts, sector10_pts, sector10_pts, sector10_pts, \n                      sector10_pts, sector08_pts, sector10_pts, None, \n                      None, None, None, None, \n                      None, None, None, sector10_pts ],\n    \n                    [ # threat zone 11\n                      sector11_pts, sector11_pts, sector11_pts, sector11_pts, \n                      None, None, sector12_pts, sector12_pts,\n                      sector12_pts, sector12_pts, sector12_pts, None, \n                      sector11_pts, sector11_pts, sector11_pts, sector11_pts ],\n    \n                    [ # threat zone 12\n                      sector12_pts, sector12_pts, sector12_pts, sector12_pts, \n                      sector12_pts, sector11_pts, sector11_pts, sector11_pts, \n                      sector11_pts, sector11_pts, sector11_pts, None, \n                      None, sector12_pts, sector12_pts, sector12_pts ],\n    \n                    [ # threat zone 13\n                      sector13_pts, sector13_pts, sector13_pts, sector13_pts, \n                      None, None, sector14_pts, sector14_pts,\n                      sector14_pts, sector14_pts, sector14_pts, None, \n                      sector13_pts, sector13_pts, sector13_pts, sector13_pts ],\n    \n                    [ # sector 14\n                      sector14_pts, sector14_pts, sector14_pts, sector14_pts, \n                      sector14_pts, None, sector13_pts, sector13_pts, \n                      sector13_pts, sector13_pts, sector13_pts, None, \n                      None, None, None, None ],\n    \n                    [ # threat zone 15\n                      sector15_pts, sector15_pts, sector15_pts, sector15_pts, \n                      None, None, sector16_pts, sector16_pts,\n                      sector16_pts, sector16_pts, None, sector15_pts, \n                      sector15_pts, None, sector15_pts, sector15_pts ],\n    \n                    [ # threat zone 16\n                      sector16_pts, sector16_pts, sector16_pts, sector16_pts, \n                      sector16_pts, sector16_pts, sector15_pts, sector15_pts, \n                      sector15_pts, sector15_pts, sector15_pts, None, \n                      None, None, sector16_pts, sector16_pts ],\n    \n                    [ # threat zone 17\n                      None, None, None, None, \n                      None, None, None, None,\n                      sector05_pts, sector05_pts, sector05_pts, sector05_pts, \n                      sector05_pts, sector05_pts, sector05_pts, sector05_pts ] ]\n\n# Each element in the zone_slice_list contains the sector to use in the call to roi()\nzone_crop_list =  [ [ # threat zone 1\n                      sector_crop_list[0], sector_crop_list[0], sector_crop_list[0], None, \n                      None, None, sector_crop_list[2], sector_crop_list[2], \n                      sector_crop_list[2], sector_crop_list[2], sector_crop_list[2], None, \n                      None, sector_crop_list[0], sector_crop_list[0], \n                      sector_crop_list[0] ],\n    \n                    [ # threat zone 2\n                      sector_crop_list[1], sector_crop_list[1], sector_crop_list[1], None, \n                      None, None, sector_crop_list[3], sector_crop_list[3], \n                      sector_crop_list[3], sector_crop_list[3], sector_crop_list[3], \n                      None, None, sector_crop_list[1], sector_crop_list[1], \n                      sector_crop_list[1] ],\n    \n                    [ # threat zone 3\n                      sector_crop_list[2], sector_crop_list[2], sector_crop_list[2], \n                      sector_crop_list[2], None, None, sector_crop_list[0], \n                      sector_crop_list[0], sector_crop_list[0], sector_crop_list[0], \n                      sector_crop_list[0], sector_crop_list[0], None, None, \n                      sector_crop_list[2], sector_crop_list[2] ],\n               \n                    [ # threat zone 4\n                      sector_crop_list[3], sector_crop_list[3], sector_crop_list[3], \n                      sector_crop_list[3], None, None, sector_crop_list[1], \n                      sector_crop_list[1], sector_crop_list[1], sector_crop_list[1], \n                      sector_crop_list[1], sector_crop_list[1], None, None, \n                      sector_crop_list[3], sector_crop_list[3] ],\n                    \n                    [ # threat zone 5\n                      sector_crop_list[4], sector_crop_list[4], sector_crop_list[4], \n                      sector_crop_list[4], sector_crop_list[4], sector_crop_list[4], \n                      sector_crop_list[4], sector_crop_list[4],\n                      None, None, None, None, None, None, None, None ],\n                    \n                    [ # threat zone 6\n                      sector_crop_list[5], None, None, None, None, None, None, None, \n                      sector_crop_list[6], sector_crop_list[6], sector_crop_list[5], \n                      sector_crop_list[5], sector_crop_list[5], sector_crop_list[5], \n                      sector_crop_list[5], sector_crop_list[5] ],\n    \n                    [ # threat zone 7\n                      sector_crop_list[6], sector_crop_list[6], sector_crop_list[6], \n                      sector_crop_list[6], sector_crop_list[6], sector_crop_list[6], \n                      sector_crop_list[6], sector_crop_list[6], \n                      None, None, None, None, None, None, None, None ],\n    \n                    [ # threat zone 8\n                      sector_crop_list[7], sector_crop_list[7], None, None, None, \n                      None, None, sector_crop_list[9], sector_crop_list[9], \n                      sector_crop_list[9], sector_crop_list[9], sector_crop_list[9], \n                      sector_crop_list[7], sector_crop_list[7], sector_crop_list[7], \n                      sector_crop_list[7] ],\n    \n                    [ # threat zone 9\n                      sector_crop_list[8], sector_crop_list[8], sector_crop_list[7], \n                      sector_crop_list[7], sector_crop_list[7], None, None, None,\n                      sector_crop_list[8], sector_crop_list[8], None, None, None, \n                      None, sector_crop_list[9], sector_crop_list[8] ],\n    \n                    [ # threat zone 10\n                      sector_crop_list[9], sector_crop_list[9], sector_crop_list[9], \n                      sector_crop_list[9], sector_crop_list[9], sector_crop_list[7], \n                      sector_crop_list[9], None, None, None, None, None, None, None, \n                      None, sector_crop_list[9] ],\n    \n                    [ # threat zone 11\n                      sector_crop_list[10], sector_crop_list[10], sector_crop_list[10], \n                      sector_crop_list[10], None, None, sector_crop_list[11], \n                      sector_crop_list[11], sector_crop_list[11], sector_crop_list[11], \n                      sector_crop_list[11], None, sector_crop_list[10], \n                      sector_crop_list[10], sector_crop_list[10], sector_crop_list[10] ],\n    \n                    [ # threat zone 12\n                      sector_crop_list[11], sector_crop_list[11], sector_crop_list[11], \n                      sector_crop_list[11], sector_crop_list[11], sector_crop_list[11], \n                      sector_crop_list[11], sector_crop_list[11], sector_crop_list[11], \n                      sector_crop_list[11], sector_crop_list[11], None, None, \n                      sector_crop_list[11], sector_crop_list[11], sector_crop_list[11] ],\n    \n                    [ # threat zone 13\n                      sector_crop_list[12], sector_crop_list[12], sector_crop_list[12], \n                      sector_crop_list[12], None, None, sector_crop_list[13], \n                      sector_crop_list[13], sector_crop_list[13], sector_crop_list[13], \n                      sector_crop_list[13], None, sector_crop_list[12], \n                      sector_crop_list[12], sector_crop_list[12], sector_crop_list[12] ],\n    \n                    [ # sector 14\n                      sector_crop_list[13], sector_crop_list[13], sector_crop_list[13], \n                      sector_crop_list[13], sector_crop_list[13], None, \n                      sector_crop_list[13], sector_crop_list[13], sector_crop_list[12], \n                      sector_crop_list[12], sector_crop_list[12], None, None, None, \n                      None, None ],\n    \n                    [ # threat zone 15\n                      sector_crop_list[14], sector_crop_list[14], sector_crop_list[14], \n                      sector_crop_list[14], None, None, sector_crop_list[15], \n                      sector_crop_list[15], sector_crop_list[15], sector_crop_list[15], \n                      None, sector_crop_list[14], sector_crop_list[14], None, \n                      sector_crop_list[14], sector_crop_list[14] ],\n    \n                    [ # threat zone 16\n                      sector_crop_list[15], sector_crop_list[15], sector_crop_list[15], \n                      sector_crop_list[15], sector_crop_list[15], sector_crop_list[15], \n                      sector_crop_list[14], sector_crop_list[14], sector_crop_list[14], \n                      sector_crop_list[14], sector_crop_list[14], None, None, None, \n                      sector_crop_list[15], sector_crop_list[15] ],\n    \n                    [ # threat zone 17\n                      None, None, None, None, None, None, None, None,\n                      sector_crop_list[4], sector_crop_list[4], sector_crop_list[4], \n                      sector_crop_list[4], sector_crop_list[4], sector_crop_list[4], \n                      sector_crop_list[4], sector_crop_list[4] ] ]\n", "cell_type": "code"}, {"metadata": {"_cell_guid": "9d0ab299-01e6-45cf-a90a-37e0193b34e5", "_uuid": "4990bba665060bf322b96e9b7401cd7c2472f93d"}, "execution_count": null, "outputs": [], "source": "<H3>For What its Worth</H3>\n\nI recognize that this is a pretty inexact and brute force-ish way to segment the data, but I tried a variety of other techniques (canny, different combos of erosion/dilation, and cv2.findContours) to recognize the shape and size of the threat zones in the data, and found these unreliable.  I am new to much of this, so if someone has a more sophisticated approach to segmentation, I hope you'll post in the discussion and we can incorporate it.  Its also worth noting that I did not exhaust the parameter tuning or combinations of these techniques, so if someone has knowledge/expertise with those transformations, its is an area for expansion.  Other ideas welcome!", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "fc0d4a8d-3169-4abe-979c-0c4fec979253", "_uuid": "3edefe3f0c0dd4281db3e040f9eff77ea012d3d0"}, "execution_count": null, "outputs": [], "source": "<H3>Incorporating Cukierski</H3>\n\nIn any event, next I incorporate William Cukierski's read_header(...) and read_images(...) from the <A HREF=\"http://www.kaggle.com/tsa/notebook.html\">Reading Images notebook</A>.  Special thanks to William for hammering through that long list of parameters in the header and for the whizbang image reader!  \n\nIf anyone knows where there is documentation for all of those header features, post it in the discussion.  I assume its all basically unhelpful, but I am new to this data so I don't really know.\n\nAlso extra thanks to William for showing me np.flipud().  That's a new one for me!  Definitely will be using that one in the future.\n", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "235dacfa-ae0f-49fa-ae66-c088a44cc275", "_uuid": "f9fd0ec883ec6cb11f56958d6f7e947033d3309b", "trusted": false}, "execution_count": null, "outputs": [], "source": "#----------------------------------------------------------------------------------\n# read_header(infile):  takes an aps file and creates a dict of the data\n#\n# infile:               an aps file\n#\n# returns:              all of the fields in the header\n#----------------------------------------------------------------------------------\n\ndef read_header(infile):\n    # declare dictionary\n    h = dict()\n    \n    with open(infile, 'r+b') as fid:\n\n        h['filename'] = b''.join(np.fromfile(fid, dtype = 'S1', count = 20))\n        h['parent_filename'] = b''.join(np.fromfile(fid, dtype = 'S1', count = 20))\n        h['comments1'] = b''.join(np.fromfile(fid, dtype = 'S1', count = 80))\n        h['comments2'] = b''.join(np.fromfile(fid, dtype = 'S1', count = 80))\n        h['energy_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['config_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['file_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['trans_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['scan_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['data_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['date_modified'] = b''.join(np.fromfile(fid, dtype = 'S1', count = 16))\n        h['frequency'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['mat_velocity'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['num_pts'] = np.fromfile(fid, dtype = np.int32, count = 1)\n        h['num_polarization_channels'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['spare00'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['adc_min_voltage'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['adc_max_voltage'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['band_width'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['spare01'] = np.fromfile(fid, dtype = np.int16, count = 5)\n        h['polarization_type'] = np.fromfile(fid, dtype = np.int16, count = 4)\n        h['record_header_size'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['word_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['word_precision'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['min_data_value'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['max_data_value'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['avg_data_value'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['data_scale_factor'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['data_units'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['surf_removal'] = np.fromfile(fid, dtype = np.uint16, count = 1)\n        h['edge_weighting'] = np.fromfile(fid, dtype = np.uint16, count = 1)\n        h['x_units'] = np.fromfile(fid, dtype = np.uint16, count = 1)\n        h['y_units'] = np.fromfile(fid, dtype = np.uint16, count = 1)\n        h['z_units'] = np.fromfile(fid, dtype = np.uint16, count = 1)\n        h['t_units'] = np.fromfile(fid, dtype = np.uint16, count = 1)\n        h['spare02'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['x_return_speed'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_return_speed'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_return_speed'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['scan_orientation'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['scan_direction'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['data_storage_order'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['scanner_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['x_inc'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_inc'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_inc'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['t_inc'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['num_x_pts'] = np.fromfile(fid, dtype = np.int32, count = 1)\n        h['num_y_pts'] = np.fromfile(fid, dtype = np.int32, count = 1)\n        h['num_z_pts'] = np.fromfile(fid, dtype = np.int32, count = 1)\n        h['num_t_pts'] = np.fromfile(fid, dtype = np.int32, count = 1)\n        h['x_speed'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_speed'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_speed'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['x_acc'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_acc'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_acc'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['x_motor_res'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_motor_res'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_motor_res'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['x_encoder_res'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_encoder_res'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_encoder_res'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['date_processed'] = b''.join(np.fromfile(fid, dtype = 'S1', count = 8))\n        h['time_processed'] = b''.join(np.fromfile(fid, dtype = 'S1', count = 8))\n        h['depth_recon'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['x_max_travel'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_max_travel'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['elevation_offset_angle'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['roll_offset_angle'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_max_travel'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['azimuth_offset_angle'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['adc_type'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['spare06'] = np.fromfile(fid, dtype = np.int16, count = 1)\n        h['scanner_radius'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['x_offset'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['y_offset'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['z_offset'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['t_delay'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['range_gate_start'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['range_gate_end'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['ahis_software_version'] = np.fromfile(fid, dtype = np.float32, count = 1)\n        h['spare_end'] = np.fromfile(fid, dtype = np.float32, count = 10)\n   \n    return h\n  \n#unit test ----------------------------------\n#header = read_header(APS_FILE_NAME)\n\n#for data_item in sorted(header):\n#    print ('{} -> {}'.format(data_item, header[data_item]))", "cell_type": "code"}, {"metadata": {"_cell_guid": "ca21bdc1-5bec-4c3f-a60a-8ad820cee502", "_uuid": "e8ba0ce91224850565c4219af8596d7c7955bb10"}, "execution_count": null, "outputs": [], "source": "<H3>Style/Use Note</H3>\n\nFrom a style perspective, I like to stick a unit test at the bottom of a function or code block.  I find it especially helpful when debugging.  Since there is no contest scan data on Kaggle, I've commented the unit tests out.  If you want to run it in your own environment, you'll want to uncomment these.", "cell_type": "markdown"}, {"metadata": {"collapsed": true, "_cell_guid": "f7b156bb-ea89-44c7-a27c-cd6bafee1120", "_uuid": "7653c72028db948d85166bf76ba92c04beaea0ce", "trusted": false}, "execution_count": null, "outputs": [], "source": "#----------------------------------------------------------------------------------\n# read_data(infile):  reads and rescales any of the four image types\n#\n# infile:             an .aps, .aps3d, .a3d, or ahi file\n#\n# returns:            the stack of images\n#\n# note:               word_type == 7 is an np.float32, word_type == 4 is np.uint16      \n#----------------------------------------------------------------------------------\n\ndef read_data(infile):\n    \n    # read in header and get dimensions\n    h = read_header(infile)\n    nx = int(h['num_x_pts'])\n    ny = int(h['num_y_pts'])\n    nt = int(h['num_t_pts'])\n    \n    extension = os.path.splitext(infile)[1]\n    \n    with open(infile, 'rb') as fid:\n      \n        # skip the header\n        fid.seek(512) \n\n        # handle .aps and .a3aps files\n        if extension == '.aps' or extension == '.a3daps':\n        \n            if(h['word_type']==7):\n                data = np.fromfile(fid, dtype = np.float32, count = nx * ny * nt)\n\n            elif(h['word_type']==4): \n                data = np.fromfile(fid, dtype = np.uint16, count = nx * ny * nt)\n\n            # scale and reshape the data\n            data = data * h['data_scale_factor'] \n            data = data.reshape(nx, ny, nt, order='F').copy()\n\n        # handle .a3d files\n        elif extension == '.a3d':\n          \n            if(h['word_type']==7):\n                data = np.fromfile(fid, dtype = np.float32, count = nx * ny * nt)\n                \n            elif(h['word_type']==4):\n                data = np.fromfile(fid, dtype = np.uint16, count = nx * ny * nt)\n\n            # scale and reshape the data\n            data = data * h['data_scale_factor']\n            data = data.reshape(nx, nt, ny, order='F').copy() \n            \n        # handle .ahi files\n        elif extension == '.ahi':\n            data = np.fromfile(fid, dtype = np.float32, count = 2* nx * ny * nt)\n            data = data.reshape(2, ny, nx, nt, order='F').copy()\n            real = data[0,:,:,:].copy()\n            imag = data[1,:,:,:].copy()\n\n        if extension != '.ahi':\n            return data\n        else:\n            return real, imag\n\n#unit test ----------------------------------\n#d = read_data(APS_FILE_NAME)", "cell_type": "code"}, {"metadata": {"_cell_guid": "1affc159-6b5a-4e67-a663-924ec510b25b", "_uuid": "cfb0a365123289b7836f8fde872fd03a4ef463b1"}, "execution_count": null, "outputs": [], "source": "<H2>Basic Descriptive Probabilities for Each Threat Zone </H2>\n\nThe labels provided in the contest treat the subject number and threat zone as a combined label.  But to calculate the descriptive stats I want to separate them so that we can get total counts for each threat zone.  Also, in this preprocessing approach, we will make individual examples out of each threat zone treating each threat zone as a separate model.  The next three functions work with this data.", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "2e4d9bfa-f595-42db-8351-7a032ca58294", "_uuid": "761c6b3705e6a3b2fd191478437b8afddbec10fe", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "#----------------------------------------------------------------------------------------\n# get_hit_rate_stats(infile):  gets the threat probabilities in a useful form\n#\n# infile:                      labels csv file\n#\n# returns:                     a dataframe of the summary hit probabilities\n#\n#----------------------------------------------------------------------------------------\n\ndef get_hit_rate_stats(infile):\n    # pull the labels for a given patient\n    df = pd.read_csv(infile)\n\n    # Separate the zone and patient id into a df\n    df['Subject'], df['Zone'] = df['Id'].str.split('_',1).str\n    df = df[['Subject', 'Zone', 'Probability']]\n\n    # make a df of the sums and counts by zone and calculate hit rate per zone, then sort high to low\n    df_summary = df.groupby('Zone')['Probability'].agg(['sum','count'])\n    df_summary['Zone'] = df_summary.index\n    df_summary['pct'] = df_summary['sum'] / df_summary['count']\n    df_summary.sort_values('pct', axis=0, ascending= False, inplace=True)\n    \n    return df_summary\n\n# unit test -----------------------\n#df = get_hit_rate_stats(THREAT_LABELS)\n#df.head()", "cell_type": "code"}, {"metadata": {"collapsed": false, "_cell_guid": "76f49181-bc7d-4e1c-919f-845cbbf06a81", "_uuid": "51daace8081ea950b6adfa7389ca6e303ab42d62", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "#------------------------------------------------------------------------------------------\n# chart_hit_rate_stats(df_summary): charts threat probabilities in desc order by zone\n#\n# df_summary:                 a dataframe like that returned from get_hit_rate_stats(...)\n#\n#-------------------------------------------------------------------------------------------\n\ndef chart_hit_rate_stats(df_summary):\n    fig, ax = plt.subplots(figsize=(15,5))\n    sns.barplot(ax=ax, x=df_summary['Zone'], y=df_summary['pct']*100)\n\n# unit test ------------------\n#chart_hit_rate_stats(df)", "cell_type": "code"}, {"metadata": {"collapsed": false, "_cell_guid": "ad51e645-86bf-4118-8c96-8810e8113593", "_uuid": "73b246e395a3a0659e0bf71799bede072ab672bb", "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "![Hit Rate Bar Chart][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/barchart.png", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "e54f5a32-7e56-42b6-9c6b-171e0b9fb5f1", "_uuid": "7d73cd1bcf50a98fc2340038aaa5ec16c756ed52", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "#------------------------------------------------------------------------------------------\n# print_hit_rate_stats(df_summary): lists threat probabilities by zone\n#\n# df_summary:               a dataframe like that returned from get_hit_rate_stats(...)\n#\n#------------------------------------------------------------------------------------------\n\ndef print_hit_rate_stats(df_summary):\n    # print the table of values readbly\n    print ('{:6s}   {:>4s}   {:6s}'.format('Zone', 'Hits', 'Pct %'))\n    print ('------   ----- ----------')\n    for zone in df_summary.iterrows():\n        print ('{:6s}   {:>4d}   {:>6.3f}%'.format(zone[0], np.int16(zone[1]['sum']), zone[1]['pct']*100))\n    print ('------   ----- ----------')\n    print ('{:6s}   {:>4d}   {:6.3f}%'.format('Total ', np.int16(df_summary['sum'].sum(axis=0)), \n                                             ( df_summary['sum'].sum(axis=0) / df_summary['count'].sum(axis=0))*100))\n\n# unit test -----------------------\n#print_hit_rate_stats(df)", "cell_type": "code"}, {"metadata": {"collapsed": false, "_cell_guid": "e5c6c425-f7e2-45ea-946e-d89210446e45", "_uuid": "ee0158cb36406a4db994127bb46a1c710ce315b3", "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "![Hit Rate Stats List][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/hitratestats.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "6f18cf5b-11aa-43aa-8e40-ac7b80a9269c", "_uuid": "56b8b904ed1503553084757481ce379f72c50ce4"}, "execution_count": null, "outputs": [], "source": "<H3>Zone 1?  Really?</H3>\n\nLooking at this distribution I'm left to wonder whether the stage 2 data might be substantially different, and certainly one might think real world data would be different.  Zone 1, the right arm, seems unlikely to be the population's most likely zone to hide contraband.  To begin with, you would have to put your  contraband in place with your left hand.  So unless most airline passengers that carry contraband are left handed, I am guessing that the real world (and perhaps stage 2) will look different.  Its just a guess of course...  Anyway, I am convinced it will be important to figure out how to get as many examples by threat zone as possible.\n\nIn any event, it will also be handy to easily get a list of zones and probabilities from the labels file, so I added this in here.  Note that the subject has contraband in zone 14 (left leg).  We'll keep an eye out for that!", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "d72b0d93-eb7e-469f-8e31-f7f072d55a4c", "_uuid": "1bd1b49900db4e13b4e3f6650d8bd20dd750577b", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "#----------------------------------------------------------------------------------------\n# def get_subject_labels(infile, subject_id): lists threat probabilities by zone\n#\n# infile:                          labels csv file\n#\n# subject_id:                      the individual you want the threat zone labels for\n#\n# returns:                         a df with the list of zones and contraband (0 or 1)\n#\n#---------------------------------------------------------------------------------------\n\ndef get_subject_labels(infile, subject_id):\n\n    # read labels into a dataframe\n    df = pd.read_csv(infile)\n\n    # Separate the zone and subject id into a df\n    df['Subject'], df['Zone'] = df['Id'].str.split('_',1).str\n    df = df[['Subject', 'Zone', 'Probability']]\n    threat_list = df.loc[df['Subject'] == subject_id]\n    \n    return threat_list\n    \n\n    \n# unit test ----------------------------------------------------------------------\n#print(get_subject_labels(THREAT_LABELS, '00360f79fd6e02781457eda48f85da90'))", "cell_type": "code"}, {"metadata": {"collapsed": false, "_cell_guid": "c4f7d61e-38e2-4458-8f74-b88480ced784", "_uuid": "fc00aca89f2bda71fa6a9a7cd435b6e116968cd8", "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "![Subject Threat List][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/subject_threat_list.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "d5386963-74ee-4668-bb73-9649418cf38e", "_uuid": "63ca3cd3b36aee34537864b3e57910b32e6812e4"}, "execution_count": null, "outputs": [], "source": "<H3>Viewing and Selecting Images</H3>\n\nI always like to actually see the data before I start messing with it.  So this next function prints a nice, but small 4x4 matrix of the 16 images.  Since I did this originally in Google Datalab, there is an output size constraint that this function hits.  So I used a cv2.resize to get it under the wire.  Since there is no data on Kaggle it doesn't matter for this notebook.  But watch out for this if you run it yourself.  If your environment does not have the same size constraint, show the images full size.  Its good grounding.  After you've seen them once, you can comment out the unit test.  Note also that this only applies the unit test and visualization.\n", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "518dea80-81bc-4256-afeb-a0b24dea8daa", "_uuid": "f485eabdb3c27182633ddecafeb1123d33b74ee7", "trusted": false}, "execution_count": null, "outputs": [], "source": "#----------------------------------------------------------------------------------\n# plot_image_set(infile):  takes an aps file and shows all 16 90 degree shots\n#\n# infile:                  an aps file\n#----------------------------------------------------------------------------------\ndef plot_image_set(infile):\n\n    # read in the aps file, it comes in as shape(512, 620, 16)\n    img = read_data(infile)\n    \n    # transpose so that the slice is the first dimension shape(16, 620, 512)\n    img = img.transpose()\n        \n    # show the graphs\n    fig, axarr = plt.subplots(nrows=4, ncols=4, figsize=(10,10))\n    \n    i = 0\n    for row in range(4):\n        for col in range(4):\n            resized_img = cv2.resize(img[i], (0,0), fx=0.1, fy=0.1)\n            axarr[row, col].imshow(np.flipud(resized_img), cmap=COLORMAP)\n            i += 1\n    \n    print('Done!')\n\n# unit test ----------------------------------\n#plot_image_set(APS_FILE_NAME)", "cell_type": "code"}, {"metadata": {"_cell_guid": "54ebde83-c215-45e1-b6cf-a3caaf287f3a", "_uuid": "d202cdaa46039a1997fea54f1bf97b3f86adda7d"}, "execution_count": null, "outputs": [], "source": "Your output should look something like this (without the yellow dots):\n![4x4 Scan View][1]\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/4x4_scan_viewx.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "111382b8-20c4-49da-a4f2-51465672dc85", "_uuid": "df136da2d6243540a23678f35b81d869e8ed49da"}, "execution_count": null, "outputs": [], "source": "<H2>Digging into the Images</H2>\n\nWhen we get to running a pipeline, we will want to pull the scans out one at a time so that they can be processed.  So here's a function to return the nth image.  In the unit test, I added a histogram of the values in the image.", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "a32b691b-cb3f-4662-aed2-ca49a592bc72", "_uuid": "9b9a55eb6cac2a7f39ffb44147e86ee9465802b3", "trusted": false}, "execution_count": null, "outputs": [], "source": "#----------------------------------------------------------------------------------\n# get_single_image(infile, nth_image):  returns the nth image from the image stack\n#\n# infile:                              an aps file\n#\n# returns:                             an image\n#----------------------------------------------------------------------------------\n\ndef get_single_image(infile, nth_image):\n\n    # read in the aps file, it comes in as shape(512, 620, 16)\n    img = read_data(infile)\n    \n    # transpose so that the slice is the first dimension shape(16, 620, 512)\n    img = img.transpose()\n    \n    return np.flipud(img[nth_image])\n\n  \n  \n  \n# unit test ---------------------------------------------------------------\n#an_img = get_single_image(APS_FILE_NAME, 0)\n\n#fig, axarr = plt.subplots(nrows=1, ncols=2, figsize=(20, 5))\n\n#axarr[0].imshow(an_img, cmap=COLORMAP)\n#plt.subplot(122)\n#plt.hist(an_img.flatten(), bins=256, color='c')\n#plt.xlabel(\"Raw Scan Pixel Value\")\n#plt.ylabel(\"Frequency\")\n#plt.show()\n", "cell_type": "code"}, {"metadata": {"_cell_guid": "2466640b-ec0a-4e3c-9774-47dba69f3b5d", "_uuid": "b010973cb5b8cac4f12e7514db39a56f5f88cd52"}, "execution_count": null, "outputs": [], "source": "Your output should look something like this (sans the meany face and the privacy barrel!):\n![Raw Image and Histogram][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/raw_img_scan_hist.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "0ec6406a-6f0a-46bd-8460-1446e36cfcee", "_uuid": "efbd4d49b4f5be5993f4a6b09ea6614c96f173a0"}, "execution_count": null, "outputs": [], "source": "<H3>Rescaling the Image</H3>\n\nMost image preprocessing functions want the image as grayscale.  So here's a function that rescales to the normal grayscale range.", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "26d6c4fd-4f9f-4b1b-bdc9-e86c7abc7884", "_uuid": "c33d2eaa57b35bf67228d31c3b516c74c4b8a8a0", "trusted": false}, "execution_count": null, "outputs": [], "source": "#----------------------------------------------------------------------------------\n# convert_to_grayscale(img):           converts a ATI scan to grayscale\n#\n# infile:                              an aps file\n#\n# returns:                             an image\n#----------------------------------------------------------------------------------\ndef convert_to_grayscale(img):\n    # scale pixel values to grayscale\n    base_range = np.amax(img) - np.amin(img)\n    rescaled_range = 255 - 0\n    img_rescaled = (((img - np.amin(img)) * rescaled_range) / base_range)\n\n    return np.uint8(img_rescaled)\n\n# unit test ------------------------------------------\n#img_rescaled = convert_to_grayscale(an_img)\n\n#fig, axarr = plt.subplots(nrows=1, ncols=2, figsize=(20, 5))\n\n#axarr[0].imshow(img_rescaled, cmap=COLORMAP)\n#plt.subplot(122)\n#plt.hist(img_rescaled.flatten(), bins=256, color='c')\n#plt.xlabel(\"Grayscale Pixel Value\")\n#plt.ylabel(\"Frequency\")\n#plt.show()\n", "cell_type": "code"}, {"metadata": {"_cell_guid": "b2ea9c57-159c-4e8c-ae08-9b73f690c0bc", "_uuid": "81eca38f966cb8141903fd708cda2943fa02a368"}, "execution_count": null, "outputs": [], "source": "Your output should look something like this:\n![Grayscale Scan and Histogram][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/grayscale_img_scan_hist.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "a58c1083-0dae-40bf-8bbd-c7b992fba657", "_uuid": "9149db7c8989734ddb08834dd16361a117eecb39"}, "execution_count": null, "outputs": [], "source": "<H3>Spreading the Spectrum</H3>\n\nFrom the histogram, you can see that most pixels are found between a value of 0 and and about 25.  The entire range of grayscale values in the scan is less than ~125.  You can also see a fair amount of ghosting or noise around the core image.  Maybe the millimeter wave technology scatters some noise?  Not sure... Anyway, if someone knows what this is caused by, drop a note in the coments.  That said, let's see what we can do to clean the image up.\n\nIn the following function, I first threshold the background.  I've played quite a bit with the threshmin setting (12 has worked best so far), but this is obviously a parameter to play with.\n\nNext we equalize the distribution of the grayscale spectrum in this image.  See <A HREF=\"http://docs.opencv.org/3.1.0/d5/daf/tutorial_py_histogram_equalization.html\">this tutorial</A> if you want to learn more about this technique.  But in the main, it redistributes pixel values to a the full grayscale spectrum in order to increase contrast.\n\n", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "e622b304-baff-4af8-8219-03822b4d89b8", "_uuid": "71dbadd16c92ec24e21db4b5fbd11794b91f96f6", "trusted": false}, "execution_count": null, "outputs": [], "source": "#-------------------------------------------------------------------------------\n# spread_spectrum(img):        applies a histogram equalization transformation\n#\n# img:                         a single scan\n#\n# returns:                     a transformed scan\n#-------------------------------------------------------------------------------\n\ndef spread_spectrum(img):\n    img = stats.threshold(img, threshmin=12, newval=0)\n    \n    # see http://docs.opencv.org/3.1.0/d5/daf/tutorial_py_histogram_equalization.html\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))\n    img= clahe.apply(img)\n    \n    return img\n  \n# unit test ------------------------------------------\n#img_high_contrast = spread_spectrum(img_rescaled)\n\n#fig, axarr = plt.subplots(nrows=1, ncols=2, figsize=(20, 5))\n\n#axarr[0].imshow(img_high_contrast, cmap=COLORMAP)\n#plt.subplot(122)\n#plt.hist(img_high_contrast.flatten(), bins=256, color='c')\n#plt.xlabel(\"Grayscale Pixel Value\")\n#plt.ylabel(\"Frequency\")\n#plt.show()\n", "cell_type": "code"}, {"metadata": {"_cell_guid": "9cb50c9e-0111-470e-8dc6-35708e510129", "_uuid": "9fb39c60b5ca2870da5c0ebbec66b731e5474faa"}, "execution_count": null, "outputs": [], "source": "![High Contrast Scan and Histogram][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/spreadspect_img_scan_hist.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "0f25f3da-c4f1-431d-a77f-2c7a53f336d2", "_uuid": "7bafefad3a12e8f6e099c74cb1258b7f043dfb61"}, "execution_count": null, "outputs": [], "source": "In the histogram, you can see that the distribution of pixel values has been spread and all of the noise below 12 has been reduced to 0. The image is cleaner and ready for masking and cropping.  Recall that this subject has contraband in zone 14, and at least to my eye it looks misshapen. ", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "904cf39d-5c8a-44c6-8302-fe46728fca7a", "_uuid": "f1c6b4cb1db84e3387e3f3c969a49c17d2e1d561"}, "execution_count": null, "outputs": [], "source": "<H3>Masking the Region of Interest</H3>\n\nUsing the slice lists from above, getting a set of masked images for a given threat zone is straight forward.  The same note applies here as in the 4x4 visualization above, I used a cv2.resize to get around a size constraint (see above), therefore the images are quite blurry at this resolution.  Note that the blurriness only applies the unit test and visualization.  The data returned by this function is at full resolution.\n", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "5b508405-5261-4b17-97fb-2531c4335dc7", "_uuid": "f730a9cd75a734bf649e35558858464090a9b331", "trusted": false}, "execution_count": null, "outputs": [], "source": "#-----------------------------------------------------------------------------------------\n# roi(img, vertices):              uses vertices to mask the image\n#\n# img:                             the image to be masked\n#\n# vertices:                        a set of vertices that define the region of interest\n#\n# returns:                         a masked image\n#-----------------------------------------------------------------------------------------\ndef roi(img, vertices):\n  \n    # blank mask\n    mask = np.zeros_like(img)\n\n    # fill the mask\n    cv2.fillPoly(mask, [vertices], 255)\n\n    # now only show the area that is the mask\n    masked = cv2.bitwise_and(img, mask)\n    \n\n    return masked\n  \n# unit test -----------------------------------------------------------------\n#fig, axarr = plt.subplots(nrows=4, ncols=4, figsize=(10,10))\n#    \n#i = 0\n#for row in range(4):\n#    for col in range(4):\n#        an_img = get_single_image(APS_FILE_NAME, i)\n#        img_rescaled = convert_to_grayscale(an_img)\n#        img_high_contrast = spread_spectrum(img_rescaled)\n#        if zone_slice_list[0][i] is not None:\n#            masked_img = roi(img_high_contrast, zone_slice_list[0][i])\n#            resized_img = cv2.resize(masked_img, (0,0), fx=0.1, fy=0.1)\n#            axarr[row, col].imshow(resized_img, cmap=COLORMAP)\n#        i += 1\n", "cell_type": "code"}, {"metadata": {"_cell_guid": "429e014f-7b5f-4668-bc45-ef4a213c4037", "_uuid": "b159c05be36944a11867c92f22ee4595a7cf7496"}, "execution_count": null, "outputs": [], "source": "![4x4 Feature Matrix][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/4x4_feature_matrix.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "a8a8c9be-5974-4623-a988-e6c46f0c13f1", "_uuid": "107bdb0c0ee72ab37959aaeed4631777c33deb93"}, "execution_count": null, "outputs": [], "source": "<H3>Cropping the Images</H3>\n\nUsing the crop lists from above, getting a set of cropped images for a given threat zone is also straight forward.  The same note applies here as in the 4x4 visualization above, I used a cv2.resize to get around a size constraint (see above), therefore the images are quite blurry at this resolution.  If you do not face this size constraint, drop the resize.  Note that the blurriness only applies the unit test and visualization.  The data returned by this function is at full resolution.", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "ecf8baf5-7370-4861-9ea6-65e78e2ae2f3", "_uuid": "cfa6b898e800aaf6b3e341b3901d9c9db3b2eb25", "trusted": false}, "execution_count": null, "outputs": [], "source": "#-----------------------------------------------------------------------------------------\n# crop(img, crop_list):                uses vertices to mask the image\n#\n# img:                                 the image to be cropped\n#\n# crop_list:                           a crop_list entry with [x , y, width, height]\n#\n# returns:                             a cropped image\n#-----------------------------------------------------------------------------------------\ndef crop(img, crop_list):\n\n    x_coord = crop_list[0]\n    y_coord = crop_list[1]\n    width = crop_list[2]\n    height = crop_list[3]\n    cropped_img = img[x_coord:x_coord+width, y_coord:y_coord+height]\n    \n    return cropped_img\n  \n# unit test -----------------------------------------------------------------\n\n#fig, axarr = plt.subplots(nrows=4, ncols=4, figsize=(10,10))\n#    \n#i = 0\n#for row in range(4):\n#    for col in range(4):\n#        an_img = get_single_image(APS_FILE_NAME, i)\n#        img_rescaled = convert_to_grayscale(an_img)\n#        img_high_contrast = spread_spectrum(img_rescaled)\n#        if zone_slice_list[0][i] is not None:\n#            masked_img = roi(img_high_contrast, zone_slice_list[0][i])\n#            cropped_img = crop(masked_img, zone_crop_list[0][i])\n#            resized_img = cv2.resize(cropped_img, (0,0), fx=0.1, fy=0.1)\n#            axarr[row, col].imshow(resized_img, cmap=COLORMAP)\n#        i += 1", "cell_type": "code"}, {"metadata": {"_cell_guid": "184b680b-d274-4cc1-bf5a-5fd1cb5a0720", "_uuid": "9de977a21027d112d7bc431b6916f6c1cbbe51bd"}, "execution_count": null, "outputs": [], "source": "![4x4 Crop Matrix][1]\n\n\n  [1]: https://storage.googleapis.com/kaggle-datasets-jbf/tsa_datasets/4x4_crop_matrix.png", "cell_type": "markdown"}, {"metadata": {"_cell_guid": "dd01af6a-640c-40c2-8ea1-84f1f7000271", "_uuid": "e1b719b9ab731539f2f51e9bb0c704f129693f29"}, "execution_count": null, "outputs": [], "source": "<H3>Normalize and Zero Center</H3>\n\nWith the data cropped, we can normalize and zero center.  Note that more work needs to be done to confirm a reasonable pixel mean for the zero center.", "cell_type": "markdown"}, {"metadata": {"collapsed": false, "_cell_guid": "78b05f17-0289-4eb6-9301-ff3e9561fb99", "_uuid": "84afe2c54e5c009d54d0d06eb07cc93602341cd1", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "#------------------------------------------------------------------------------------------\n# normalize(image): Take segmented tsa image and normalize pixel values to be \n#                   between 0 and 1\n#\n# parameters:      image - a tsa scan\n#\n# returns:         a normalized image\n#\n#------------------------------------------------------------------------------------------\n\ndef normalize(image):\n    MIN_BOUND = 0.0\n    MAX_BOUND = 255.0\n    \n    image = (image - MIN_BOUND) / (MAX_BOUND - MIN_BOUND)\n    image[image>1] = 1.\n    image[image<0] = 0.\n    return image\n\n#unit test ---------------------\n#an_img = get_single_image(APS_FILE_NAME, 0)\n#img_rescaled = convert_to_grayscale(an_img)\n#img_high_contrast = spread_spectrum(img_rescaled)\n#masked_img = roi(img_high_contrast, zone_slice_list[0][0])\n#cropped_img = crop(masked_img, zone_crop_list[0][0])\n#normalized_img = normalize(cropped_img)\n#print ('Normalized: length:width -> {:d}:{:d}|mean={:f}'.format(len(normalized_img), len(normalized_img[0]), normalized_img.mean()))\n#print (' -> type ', type(normalized_img))\n#print (' -> shape', normalized_img.shape)\n", "cell_type": "code"}, {"metadata": {"collapsed": false, "_cell_guid": "c0da7785-f0a6-4cec-aa0e-5738fbfbca99", "_uuid": "be3073c03109ace10f7917fa85c7c2127cdbc878", "trusted": false, "_execution_state": "idle"}, "execution_count": null, "outputs": [], "source": "#-------------------------------------------------------------------------------------\n# zero_center(image): Shift normalized image data and move the range so it is 0 c\n#                     entered at the PIXEL_MEAN\n#\n# parameters:         image\n#\n# returns:            a zero centered image\n#\n#-----------------------------------------------------------------------------------------------------------\ndef zero_center(image):\n  \n    PIXEL_MEAN = 0.014327\n    \n    image = image - PIXEL_MEAN\n    return image\n\n#unit test ---------------------\n#an_img = get_single_image(APS_FILE_NAME, 0)\n#img_rescaled = convert_to_grayscale(an_img)\n#img_high_contrast = spread_spectrum(img_rescaled)\n#masked_img = roi(img_high_contrast, zone_slice_list[0][0])\n#cropped_img = crop(masked_img, zone_crop_list[0][0])\n#normalized_img = normalize(cropped_img)\n#zero_centered_img = zero_center(normalized_img)\n#print ('Zero Centered: length:width -> {:d}:{:d}|mean={:f}'.format(len(zero_centered_img), len(zero_centered_img[0]), zero_centered_img.mean()))\n#print ('Conformed: Type ->', type(zero_centered_img), 'Shape ->', zero_centered_img.shape)", "cell_type": "code"}, {"metadata": {"_cell_guid": "aa98784b-606c-42be-a925-46cd474ffada", "_uuid": "402c552061821d844938f659aae390acbd6628d1"}, "execution_count": null, "outputs": [], "source": "<H3>Wrap Up and Extensions</H3>\n\nWith a full set of isolated scans by threat zone that are normalized and zero centered, we can make examples in several ways.  \n\n<ul>\n  <li>Each individual view of the threat zone could be treated as an example, thus creating a larger pool to train from.  But of course, the contraband may not be visible in every view of the threat zone</li>\n  <li>We could also make threat zone plates that assemble the individual views of a threat zone into a plate.</li>\n  <li>To generate additional examples, we could also flip each cropped scan in all 4 directions</li>\n  <li>Add noise to the data</li>\n</ul>  \n \nPS - If you found this notebook helpful, I hope you'll give me an up vote!  \n\nGood luck!", "cell_type": "markdown"}]}