{"cells":[{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"5ff964bd-d42d-e4ea-4265-01b1cecfe3dd"},"outputs":[],"source":"import sys\nimport os\n\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image, ImageDraw, ImageFilter\nimport glob\n\nimport cv2\nimport shapely\nimport shapely.geometry\nfrom shapely.geometry import Polygon\n\n\nSOURCEDIR = os.path.join('..', 'input')\n\n# ================ Meta ====================\n__description__ = 'Sea Lion Prognostication Engine'\n__version__ = '0.1.0'\n__license__ = 'MIT'\n__author__ = 'Gavin Crooks (@threeplusone)'\n__status__ = \"Prototype\"\n__copyright__ = \"Copyright 2017\"\n\n\nclass SeaLionData(object):\n    \n    def __init__(self, sourcedir=SOURCEDIR):\n        \n        self.class_names = (\n            'adult_males',\n            'subadult_males',\n            'adult_females',\n            'juveniles',\n            'pups')\n        \n        self.class_colors = (\n            (255,0,0),          # red\n            (250,10,250),       # magenta\n            (84,42,0),          # brown \n            (30,60,180),        # blue\n            (35,180,20),        # green\n            )\n\n        self._trainshort_nb = 11 \n        \n        self.train_nb = 947\n        \n        self.test_nb = 18636\n       \n        self.source_paths = {\n            'sample'     : os.path.join(sourcedir, 'sample_submission.csv'),\n            'counts'     : os.path.join(sourcedir, 'Train', 'train.csv'),\n            'train'      : os.path.join(sourcedir, 'Train', '{iid}.jpg'),\n            'dotted'     : os.path.join(sourcedir, 'TrainDotted', '{iid}.jpg'),\n            'test'       : os.path.join(sourcedir, 'Test', '{iid}.jpg'),   \n            }\n        \n        # From MismatchedTrainImages.txt\n        self.bad_train_ids = (\n            3, 7, 9, 21, 30, 34, 71, 81, 89, 97, 151, 184, 215, 234, 242, \n            268, 290, 311, 331, 344, 380, 384, 406, 421, 469, 475, 490, 499, \n            507, 530, 531, 605, 607, 614, 621, 638, 644, 687, 712, 721, 767, \n            779, 781, 794, 800, 811, 839, 840, 869, 882, 901, 903, 905, 909, \n            913, 927, 946)\n\n    def class_from_color(self, rgb) :\n        MAX_DIFF = 80\n        colors = np.array(self.class_colors) \n        diff = (abs(colors - rgb)).sum(axis=-1)\n        cls = np.argmin(diff)\n        \n        if diff[cls]>MAX_DIFF: \n            #print(cls, rgb, self.class_colors[cls], diff[cls])\n            return None\n        \n        return cls\n        \n    @property\n    def trainshort_ids(self):\n        return (0,1,2,4,5,6,8,10) #range(41,51) \n        \n    @property \n    def train_ids(self):\n        tids = range(0, self.train_nb)\n        tids = list(set( tids) - set(self.bad_train_ids) )  # Remove bad ids\n        tids.sort()\n        return tids\n                    \n    @property \n    def test_ids(self):\n        return range(0, self.test_nb)\n    \n    def path(self, name, **kwargs):\n        \"\"\"Return path to various source files\"\"\"\n        path = self.source_paths[name].format(**kwargs)\n        return path        \n\n    def counts(self) :\n        counts = {}\n        fn = self.path('counts')\n        with open(fn) as f:\n            f.readline()\n            for line in f:\n                tid_counts = list(map(int, line.split(',')))\n                counts[tid_counts[0]] = tid_counts[1:]\n        return counts\n\n    def load_train_image(self, train_id): \n        fn = self.path('train', iid=train_id)\n        img = Image.open(fn)\n        return img\n   \n    def load_dotted_image(self, train_id):\n        fn = self.path('dotted', iid=train_id)\n        img = Image.open(fn)\n        return img\n            \n\n    def coords(self, train_id):\n        # Empirical constants\n        MIN_DIFFERENCE = 30\n        MIN_AREA = 15\n        MAX_AREA = 100\n        MAX_AVG_DIFF = 50\n       \n        src_fn = self.path('train', iid=train_id)\n        src_img = np.asarray(Image.open(src_fn), dtype = np.float)\n    \n        dot_fn = self.path('dotted', iid=train_id)\n        dot_img = np.asarray(Image.open(dot_fn), dtype = np.float)\n\n        img_diff = np.abs(src_img-dot_img)\n        \n        # Detect bad data. If train and dotted images very different somethings wrong.\n        img_diff[dot_img==0] = 0              # Mask out black masks in dotted images\n        avg_diff = img_diff.sum() / (img_diff.shape[0] * img_diff.shape[1])\n        if avg_diff > MAX_AVG_DIFF: return None\n        \n        img_diff[dot_img==0] = 255              # Mask out black masks in dotted images\n        img_diff = np.max(img_diff, axis=-1)\n           \n        img_diff[img_diff<MIN_DIFFERENCE] = 0\n        img_diff[img_diff>=MIN_DIFFERENCE] = 255\n\n        # debug\n        #img = Image.fromarray(img_diff.astype(np.uint8) )\n        #img.save('img_diff.png')\n\n        sealions = []\n\n        contours = cv2.findContours(img_diff.astype(np.uint8), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]\n        \n        for cnt in contours :\n            area = cv2.contourArea(cnt) \n            if(area>MIN_AREA and area<MAX_AREA) :\n                p = Polygon(shell=cnt[:, 0, :])\n                x,y = p.centroid.coords[0]\n                x = int(x)\n                y = int(y)\n                cls = self.class_from_color(dot_img[y,x])\n                if cls is None: continue\n                #print(cls, x,y)\n                sealions.append ( (cls, x, y) )\n\n        return sealions\n\n\n# Count sea lion dots and compare to truth from train.csv\nsld = SeaLionData()\ntrue_counts = sld.counts()\n\nprint('train_id','true_counts','counted_dots', 'difference', sep='\\t')\nfor train_id in sld.trainshort_ids:\n#for train_id in (6,):   \n    coords = sld.coords(train_id)\n    print (coords)\n    if coords is None: \n        print(train_id, '\\tBAD DATA ') \n        continue\n    counts = [0,0,0,0,0]\n    for c in coords :\n        counts[c[0]] +=1\n    print(train_id, true_counts[train_id], counts, np.array(true_counts[train_id]) - np.array(counts) , sep='\\t' )"}],"metadata":{"_change_revision":0,"_is_fork":false,"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.0"}},"nbformat":4,"nbformat_minor":0}