[
  {
    "path": ".gitignore",
    "content": "__pycache__*/\n.idea/\noutput/*\n._*\n*/._*\n.pyc\n.DS_Store\n*.swp\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2019-present NAVER Corp.\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "NOTICE",
    "content": "TedEval\nCopyright (c) 2019-present NAVER Corp.\n\nThis project contains subcomponents with separate copyright notices and license terms. \nYour use of the source code for these subcomponents is subject to the terms and conditions of the following licenses.\n\n=====================\n\nTedEval solves the drawbacks of previous metrics such as IoU and DetEval. \nThis code is based on ICDAR15 official evaluation code from https://rrc.cvc.uab.es/.\n\n=====================\n\njquery/jquery from http://jquery.com/\n\n=====================\n\nCopyright jQuery Foundation and other contributors, https://jquery.org/\n\nThis software consists of voluntary contributions made by many\nindividuals. For exact contribution history, see the revision history\navailable at https://github.com/jquery/jquery\n\nThe following license applies to all parts of this software except as\ndocumented below:\n\n====\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n====\n\nAll files located in the node_modules and external directories are\nexternally maintained libraries used by this software which have their\nown licenses; we recommend you read them, as their terms may differ from\nthe terms above.\n\n=====================\n\njquery/jquery-ui from https://github.com/jquery/jquery-ui\n\n=====================\n\nCopyright jQuery Foundation and other contributors, https://jquery.org/\n\nThis software consists of voluntary contributions made by many\nindividuals. For exact contribution history, see the revision history\navailable at https://github.com/jquery/jquery-ui\n\nThe following license applies to all parts of this software except as\ndocumented below:\n\n====\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n====\n\nCopyright and related rights for sample code are waived via CC0. Sample\ncode is defined as all source code contained within the demos directory.\n\nCC0: http://creativecommons.org/publicdomain/zero/1.0/\n\n====\n\nAll files located in the node_modules and external directories are\nexternally maintained libraries used by this software which have their\nown licenses; we recommend you read them, as their terms may differ from\nthe terms above.\n\n=====================\n\nmalsup/form from https://github.com/malsup/form\n\n=====================\n\nCopyright 2006-2013 (c) M. Alsup\n\nAll versions, present and past, of the jQuery Form plugin are dual licensed under the MIT and GPL licenses:\n\nMIT\nGPL\nYou may use either license. The MIT License is recommended for most projects because it is simple and easy to understand and it places almost no restrictions on what you can do with the plugin.\n\nIf the GPL suits your project better you are also free to use the plugin under that license.\n\nYou don't have to do anything special to choose one license or the other and you don't have to notify anyone which license you are using. You are free to use the jQuery Form Plugin in commercial projects as long as the copyright header is left intact.\n\n-----\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n=====\n"
  },
  {
    "path": "README.MD",
    "content": "# TedEval: A Fair Evaluation Metric for Scene Text Detectors\n\nOfficial Python 3 implementation of TedEval | [paper](https://arxiv.org/abs/1907.01227) | [slides](https://docs.google.com/presentation/d/1EFK_WjpdLExZVDPt4C7yCcxjpXNvIyAOL9zUnKx1VoY/edit?usp=sharing)\n\n**[Chae Young Lee](mailto:cylee7133@gmail.com), Youngmin Baek, and Hwalsuk Lee.**\n\nClova AI Research, NAVER Corp.\n\n### Overview\n\nWe propose a new evaluation metric for scene text detectors called TedEval. Through separate instance-level matching policy and character-level scoring policy, TedEval solves the drawbacks of previous metrics such as IoU and DetEval. This code is based on [ICDAR15 official evaluation code](http://rrc.cvc.uab.es/).\n\n## Methodology\n\n### 1. Mathcing Policy\n\n- Non-exclusively gathers all possible matches of not only one-to-one but also one-to-many and many-to-one.\n- The threshold of both area recall and area precision are set to 0.4.\n- Multiline is identified and rejected when _|min(theta, 180 - theta)| > 45_ from Fig. 2.\n\n<p align=\"center\"><img height=200 src=images/multiline.png>\n\n### 2. Scoring Policy\n\nWe compute Pseudo Character Center (PCC) from word-level bounding boxes and penalize matches when PCCs are missing or overlapping.\n\n<p align=\"center\"><img height=170 src=images/pcc.png>\n\n### Sample Evaluation\n\n<img src='images/sample.png'>\n\n## Experiments\n\nWe evaluated state-of-the-art scene text detectors with TedEval on two benchmark datasets: ICDAR 2013 Focused Scene Text (IC13) and ICDAR 2015 Incidental Scene Text (IC15). Detectors are listed in the order of published dates.\n\n### ICDAR 2013\n\n|                        Detector                         | Date (YY/MM/DD) | Recall (%) | Precision (%) | H-mean (%) |\n| :-----------------------------------------------------: | :-------------: | :--------: | :-----------: | :--------: |\n|      [CTPN](https://arxiv.org/pdf/1609.03605.pdf)       |    16/09/12     |    82.1    |     92.7      |    87.6    |\n|      [RRPN](https://arxiv.org/pdf/1703.01086.pdf)       |    17/03/03     |    89.0    |     94.2      |    91.6    |\n|     [SegLink](https://arxiv.org/pdf/1703.06520.pdf)     |    17/03/19     |    65.6    |     74.9      |    70.0    |\n|      [EAST](https://arxiv.org/pdf/1704.03155.pdf)       |    17/04/11     |    77.7    |     87.1      |    82.5    |\n|       [WordSup](https://arxiv.org/pdf/1708.06720)       |    17/08/22     |    87.5    |     92.2      |    90.2    |\n|    [PixelLink](https://arxiv.org/pdf/1801.01315.pdf)    |    18/01/04     |    84.0    |     87.2      |    86.1    |\n|      [FOTS](https://arxiv.org/pdf/1801.01671.pdf)       |    18/01/05     |    91.5    |     93.0      |    92.6    |\n|   [TextBoxes++](https://arxiv.org/pdf/1801.02765.pdf)   |    18/01/09     |    87.4    |     92.3      |    90.0    |\n| [MaskTextSpotter](https://arxiv.org/pdf/1807.02242.pdf) |    18/07/06     |    90.2    |     95.4      |    92.9    |\n|      [PMTD](https://arxiv.org/pdf/1903.11800.pdf)       |    19/03/28     |    94.0    |     95.2      |    94.7    |\n|      [CRAFT](https://arxiv.org/pdf/1904.01941.pdf)      |    19/04/03     |    93.6    |     96.5      |    95.1    |\n\n### ICDAR 2015\n\n|                        Detector                         | Date (YY/MM/DD) | Recall (%) | Precision (%) | H-mean (%) |\n| :-----------------------------------------------------: | :-------------: | :--------: | :-----------: | :--------: |\n|      [CTPN](https://arxiv.org/pdf/1609.03605.pdf)       |    16/09/12     |    85.0    |     81.1      |    67.8    |\n|      [RRPN](https://arxiv.org/pdf/1703.01086.pdf)       |    17/03/03     |    79.5    |     85.9      |    82.6    |\n|     [SegLink](https://arxiv.org/pdf/1703.06520.pdf)     |    17/03/19     |    77.1    |     83.9      |    80.6    |\n|      [EAST](https://arxiv.org/pdf/1704.03155.pdf)       |    17/04/11     |    82.5    |     90.0      |    86.3    |\n|       [WordSup](https://arxiv.org/pdf/1708.06720)       |    17/08/22     |    83.2    |     87.1      |    85.2    |\n|    [PixelLink](https://arxiv.org/pdf/1801.01315.pdf)    |    18/01/04     |    85.7    |     86.1      |    86.0    |\n|      [FOTS](https://arxiv.org/pdf/1801.01671.pdf)       |    18/01/05     |    89.0    |     93.4      |    91.2    |\n|   [TextBoxes++](https://arxiv.org/pdf/1801.02765.pdf)   |    18/01/09     |    82.4    |     90.8      |    86.5    |\n| [MaskTextSpotter](https://arxiv.org/pdf/1807.02242.pdf) |    18/07/06     |    82.5    |     91.8      |    86.9    |\n|      [PMTD](https://arxiv.org/pdf/1903.11800.pdf)       |    19/03/28     |    89.2    |     92.8      |    91.0    |\n|      [CRAFT](https://arxiv.org/pdf/1904.01941.pdf)      |    19/04/03     |    88.5    |     93.1      |    90.9    |\n\n### Frequency\n\n<img src='images/counts.png'>\n\n## Getting Started\n\n### Clone repository\n\n`git clone https://github.com/clovaai/TedEval.git`\n\n### Requirements\n\n- python 3\n- python 3.x Polygon, Bottle, Pillow\n\n```python3\n# install\npip3 install Polygon3 bottle Pillow\n```\n\n### Supported Annotation Type\n\n- LTRB (xmin, ymin, xmax, ymax)\n- QUAD (x1, y1, x2, y2, x3, y3, x4, y4)\n\n## Evaluation\n\n### Prepare data\n\nThe ground truth and the result data should be text files, one for each sample. Note that the default naming rule of each text file is that there must be `img_{number}` in the filename and that the number indicate the image sample (this can be changed in `default_evaluation_params()` in `script.py`).\n\n```\n# gt/gt_img_38.txt\n644,101,932,113,932,168,643,156,concierge@L3\n477,138,487,139,488,149,477,148,###\n344,131,398,130,398,149,344,149,###\n1195,148,1277,138,1277,177,1194,187,###\n23,270,128,267,128,282,23,284,###\n\n# result/res_img_38.txt\n644,101,932,113,932,168,643,156,{Transcription},{Confidence}\n477,138,487,139,488,149,477,148\n344,131,398,130,398,149,344,149\n1195,148,1277,138,1277,177,1194,187\n23,270,128,267,128,282,23,284\n```\n\nCompress these text files without the parent directory.\n\n```python3\nzip gt.zip gt/*\nzip result.zip result/*\n```\n\nRefer to `gt/result.zip` and `gt/gt_*.zip` for examples.\n\n### Run stand-alone evaluation\n\n```python3\npython script.py –g=gt/gt.zip –s=result/result.zip\n```\n\nFor evaluation setup, please refer to the following parameter list to edit `default_evaluation_params()` in `script.py`.\n\n### Important Parameters\n\n<!--\n### Paramters for evaluation script\n| name | type | default | description |\n| ---- | ---- | ------- | ---- |\n| -g | ```string``` | | path to ground truth zip file |\n| -s | ```string``` | | path to result zip file |\n| -o | ```string``` | | path to save per-sample result file 'results.zip' | -->\n\n| name                      | type      | default | description                                                   |\n| ------------------------- | --------- | ------- | ------------------------------------------------------------- |\n| AREA_RECALL_CONSTRAINT    | `float`   | `0.4`   | area recall constraint (0 <= R <= 1)                          |\n| AREA_PRECISION_CONSTRAINT | `float`   | `0.4`   | area precision constraint (0 <= P <= 1)                       |\n| GT_LTRB                   | `boolean` | `False` | GT file annotation type (True if LTRB, False if QUAD)         |\n| DET_LTRB                  | `boolean` | `False` | prediction file annotation type (True if LTRB, False if QUAD) |\n| TRANSCRIPTION             | `boolean` | `False` | set True if result file has transcription                     |\n| CONFIDENCES               | `boolean` | `False` | set True if result file has confidence                        |\n\n\n### Run Visualizer\n\n```python3\npython web.py\n```\n\n- Place the zip file of images and GTs of the dataset named `images.zip` and `gt.zip`, respectively, in the `gt` directory.\n- Create an empty directory name `output`. This is where the DB, submission files, and result files will be created.\n- You can change the host and port number in the final line of `web.py`.\n\nThe file structure should then be:\n\n```\n.\n├── gt\n│   ├── gt.zip\n│   └── images.zip\n├── output   # empty dir\n├── script.py\n├── web.py\n├── README.md\n└── ...\n```\n\n<img src='images/visualizer.png'>\n\n## Citation\n\n```\n@article{lee2019tedeval,\n  title={TedEval: A Fair Evaluation Metric for Scene Text Detectors},\n  author={Lee, Chae Young and Baek, Youngmin and Lee, Hwalsuk},\n  journal={arXiv preprint arXiv:1907.01227},\n  year={2019}\n}\n```\n\n## Contact us\n\nWe welcome any feedbacks to our metric. Please contact the authors via `{cylee7133, youngmin.baek, hwalsuk.lee}@gmail.com`. In case of code errors, open an issue and we will get to you.\n\n## License\n\n```\nCopyright (c) 2019-present NAVER Corp.\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n"
  },
  {
    "path": "config/config.py",
    "content": "#!/usr/bin/env python3\r\n#encoding: UTF-8\r\nimport json\r\n#Name of the script used for the evalution\r\nevaluation_script = 'script'\r\n\r\n#Upload instructions\r\ninstructions = \"\"\"<ul>\r\n\t<li>A single zip file is expected, containing a set of text files.</li>\r\n\t<li>No directory structure within the zip file is permitted, just the set of text files.</li>\r\n\t<li>The containing text files should be named as&nbsp;<strong><em>res_img_#.txt</em></strong>, where&nbsp;<em><strong>#</strong></em>&nbsp;is the number of the corresponding test-set image.</li>\r\n\t<li>Each text file should contain as many lines as text bounding boxes found. Each line should contain eight comma separated values only. The values should correspond to the coordinates of the four corners of the bounding quadrilateral of the word.</li>\r\n\t<li>New lines in the text files should be indicated with the windows CR/LF termination.</li>\r\n</ul>\r\n\r\n<p>The submitted zip file is automatically checked at the time of submission, and a submission log is presented to the user along with a confirmation of the submission. The checks performed are the following:</p>\r\n\r\n<ul>\r\n\t<li>That the file submitted is a valid zip file, it can be opened and the contents can be extracted.</li>\r\n\t<li>That the names of the text files contained are correct and the image numbers are within the bounds of the test set.</li>\r\n\t<li>That each text file contains eight comma separated values per line.</li>\r\n\t<li>That the coordinates passed are within the bounds of the image and that the coordinates are in clocwise order</li>\r\n</ul>\r\n\r\n<p>See here an example of the&nbsp;<a href=\"http://rrc.cvc.uab.es/files/task1_ch4_sample.zip\">expected submission file</a></p>\r\n\"\"\"\r\n#Extension of the GT file. gt.[extension]\r\ngt_ext = \"zip\"\r\n#Acronym for the task. It's used to cache the Images\r\nacronym = \"IST-T1\"\r\n#Title of the Task\r\ntitle = \"Incidental Scene Text - Task 1 Text Localization TEST DATASET (evaluation:IoU)\"\r\n#Custom JavaScript for the visualiztion.\r\ncustomJS = 'visualization_TL_iou.js'\r\n#Custom CSS for the visualiztion.\r\ncustomCSS = 'visualization_TL_iou.css'\r\n#Parameters used to show the results of a method and the method's ranking\r\nmethod_params = json.loads(\"\"\"{\"recall\":{\"long_name\":\"Recall\",\"type\":\"double\",\"order\":\"\",\"grafic\":\"1\",\"format\":\"perc\"},\"precision\":{\"long_name\":\"Precision\",\"type\":\"double\",\"order\":\"\",\"grafic\":\"1\",\"format\":\"perc\"},\"hmean\":{\"long_name\":\"Hmean\",\"type\":\"double\",\"order\":\"desc\",\"grafic\":\"1\",\"format\":\"perc\"}}\"\"\")\r\n#Parameters to show for each sample\r\nsample_params = json.loads(\"\"\"{\"recall\":{\"long_name\":\"Recall\",\"type\":\"double\",\"order\":\"\",\"grafic\":\"\",\"format\":\"perc\"},\"precision\":{\"long_name\":\"Precision\",\"type\":\"double\",\"order\":\"\",\"grafic\":\"\",\"format\":\"perc\"},\"hmean\":{\"long_name\":\"Hmean\",\"type\":\"double\",\"order\":\"desc\",\"grafic\":\"\",\"format\":\"perc\"}}\"\"\")\r\n#Parameters to ask for for each submition\r\nsubmit_params = json.loads(\"\"\"{}\"\"\")\r\n#Regular expression to get the Sample ID from the image name. ID must be the first capturing group.\r\nimage_name_to_id_str = '*([0-9]+)*.(jpg|gif|png)'\r\n"
  },
  {
    "path": "rrc_evaluation_funcs.py",
    "content": "#!/usr/bin/env python3\n#encoding: UTF-8\nimport json\nimport sys;sys.path.append('./')\nimport zipfile\nimport re\nimport sys\nimport os\nimport codecs\nimport importlib\nfrom io import StringIO\n\ndef print_help():\n    sys.stdout.write('Usage: python %s.py -g=<gtFile> -s=<submFile> [-o=<outputFolder> -p=<jsonParams>]' %sys.argv[0])\n    sys.exit(2)\n    \n\ndef load_zip_file_keys(file,fileNameRegExp=''):\n    \"\"\"\n    Returns an array with the entries of the ZIP file that match with the regular expression.\n    The key's are the names or the file or the capturing group definied in the fileNameRegExp\n    \"\"\"\n    try:\n        archive=zipfile.ZipFile(file, mode='r', allowZip64=True)\n    except :\n        raise Exception('Error loading the ZIP archive.')\n\n    pairs = []\n    \n    for name in archive.namelist():\n        addFile = True\n        keyName = name\n        if fileNameRegExp!=\"\":\n            m = re.match(fileNameRegExp,name)\n            if m == None:\n                addFile = False\n            else:\n                if len(m.groups())>0:\n                    keyName = m.group(1)\n                    \n        if addFile:\n            pairs.append( keyName )\n                \n    return pairs\n    \n\ndef load_zip_file(file,fileNameRegExp='',allEntries=False):\n    \"\"\"\n    Returns an array with the contents (filtered by fileNameRegExp) of a ZIP file.\n    The key's are the names or the file or the capturing group definied in the fileNameRegExp\n    allEntries validates that all entries in the ZIP file pass the fileNameRegExp\n    \"\"\"\n    try:\n        archive=zipfile.ZipFile(file, mode='r', allowZip64=True)\n    except :\n        raise Exception('Error loading the ZIP archive')    \n\n    pairs = []\n    for name in archive.namelist():\n        addFile = True\n        keyName = name\n        # if fileNameRegExp!=\"\":\n            # m = re.match(fileNameRegExp,name)\n            # if m == None:\n            #     addFile = False\n            # else:\n            #     if len(m.groups())>0:\n            #         keyName = m.group(1)\n        keyName = name.replace('gt_', '').replace('res_', '').replace('.txt', '')\n        \n        if addFile:\n            pairs.append( [ keyName , archive.read(name)] )\n        else:\n            if allEntries:\n                raise Exception('ZIP entry not valid: %s' %name)             \n\n    return dict(pairs)\n\ndef decode_utf8(raw):\n    \"\"\"\n    Returns a Unicode object on success, or None on failure\n    \"\"\"\n    try:\n        raw = codecs.decode(raw,'utf-8', 'replace')\n        #extracts BOM if exists\n        raw = raw.encode('utf8')\n        if raw.startswith(codecs.BOM_UTF8):\n            raw = raw.replace(codecs.BOM_UTF8, '', 1)\n        return raw.decode('utf-8')\n    except:\n       return None\n   \ndef validate_lines_in_file(fileName,file_contents,CRLF=True,LTRB=True,withTranscription=False,withConfidence=False,imWidth=0,imHeight=0):\n    \"\"\"\n    This function validates that all lines of the file calling the Line validation function for each line\n    \"\"\"\n    utf8File = decode_utf8(file_contents)\n    if (utf8File is None) :\n        raise Exception(\"The file %s is not UTF-8\" %fileName)\n\n    lines = utf8File.split( \"\\r\\n\" if CRLF else \"\\n\" )\n    for line in lines:\n        line = line.replace(\"\\r\",\"\").replace(\"\\n\",\"\")\n        # if(line != \"\"):\n        #     try:\n        #         validate_tl_line(line,LTRB,withTranscription,withConfidence,imWidth,imHeight)\n        #     except Exception as e:\n        #         raise Exception((\"Line in sample not valid. Sample: %s Line: %s Error: %s\" %(fileName,line,str(e))).encode('utf-8', 'replace'))\n    \n   \n   \ndef validate_tl_line(line,LTRB=True,withTranscription=True,withConfidence=True,imWidth=0,imHeight=0):\n    \"\"\"\n    Validate the format of the line. If the line is not valid an exception will be raised.\n    If maxWidth and maxHeight are specified, all points must be inside the imgage bounds.\n    Posible values are:\n    LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] \n    LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] \n    \"\"\"\n    get_tl_line_values(line,LTRB,withTranscription,withConfidence,imWidth,imHeight)\n    \n   \ndef get_tl_line_values(line,LTRB=True,withTranscription=False,withConfidence=False,imWidth=0,imHeight=0):\n    \"\"\"\n    Validate the format of the line. If the line is not valid an exception will be raised.\n    If maxWidth and maxHeight are specified, all points must be inside the imgage bounds.\n    Posible values are:\n    LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] \n    LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] \n    Returns values from a textline. Points , [Confidences], [Transcriptions]\n    \"\"\"\n    confidence = 0.0\n    transcription = \"\";\n    points = []\n    \n    numPoints = 4;\n    \n    if LTRB:\n    \n        numPoints = 4;\n        \n        if withTranscription and withConfidence:\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-1].?[0-9]*)\\s*,(.*)$',line)\n            if m == None :\n                m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-1].?[0-9]*)\\s*,(.*)$',line)\n                raise Exception(\"Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence,transcription\")\n        elif withConfidence:\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-1].?[0-9]*)\\s*$',line)\n            if m == None :\n                raise Exception(\"Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence\")\n        elif withTranscription:\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,(.*)$',line)\n            if m == None :\n                raise Exception(\"Format incorrect. Should be: xmin,ymin,xmax,ymax,transcription\")\n        else:\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,?\\s*$',line)\n            if m == None :\n                raise Exception(\"Format incorrect. Should be: xmin,ymin,xmax,ymax\")\n            \n        xmin = int(m.group(1))\n        ymin = int(m.group(2))\n        xmax = int(m.group(3))\n        ymax = int(m.group(4))\n        if(xmax<xmin):\n                raise Exception(\"Xmax value (%s) not valid (Xmax < Xmin).\" %(xmax))\n        if(ymax<ymin):\n                raise Exception(\"Ymax value (%s)  not valid (Ymax < Ymin).\" %(ymax))  \n\n        points = [ float(m.group(i)) for i in range(1, (numPoints+1) ) ]\n        \n        if (imWidth>0 and imHeight>0):\n            validate_point_inside_bounds(xmin,ymin,imWidth,imHeight);\n            validate_point_inside_bounds(xmax,ymax,imWidth,imHeight);\n\n    else:\n        \n        numPoints = 8;\n\n        if withTranscription and withConfidence:\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*([0-1].?[0-9]*)\\s*,(.*)$',line)\n            if m == None :\n                raise Exception(\"Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence,transcription\")\n        elif withConfidence:\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*([0-1].?[0-9]*)\\s*$',line)\n            if m == None :\n                raise Exception(\"Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence\")\n        elif withTranscription:\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,(.*)$',line)\n            if m == None :\n                raise Exception(\"Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,transcription\")\n        else:\n            if line[-1] == ',' : line = line[:-1]\n            m = re.match(r'^\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*,\\s*(-?[0-9]+)\\s*$',line)\n            if m == None :\n                raise Exception(\"Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4\")\n            \n        points = [ float(m.group(i)) for i in range(1, (numPoints+1) ) ]\n        \n        validate_clockwise_points(points)\n        \n        if (imWidth>0 and imHeight>0):\n            validate_point_inside_bounds(points[0],points[1],imWidth,imHeight);\n            validate_point_inside_bounds(points[2],points[3],imWidth,imHeight);\n            validate_point_inside_bounds(points[4],points[5],imWidth,imHeight);\n            validate_point_inside_bounds(points[6],points[7],imWidth,imHeight);\n            \n    \n    if withConfidence:\n        try:\n            confidence = float(m.group(numPoints+1))\n        except ValueError:\n            raise Exception(\"Confidence value must be a float\")       \n            \n    if withTranscription:\n        posTranscription = numPoints + (2 if withConfidence else 1)\n        transcription = m.group(posTranscription)\n        m2 = re.match(r'^\\s*\\\"(.*)\\\"\\s*$',transcription)\n        if m2 != None : #Transcription with double quotes, we extract the value and replace escaped characters\n            transcription = m2.group(1).replace(\"\\\\\\\\\", \"\\\\\").replace(\"\\\\\\\"\", \"\\\"\")\n    \n    return points,confidence,transcription\n    \n            \ndef validate_point_inside_bounds(x,y,imWidth,imHeight):\n    if(x<0 or x>imWidth):\n            raise Exception(\"X value (%s) not valid. Image dimensions: (%s,%s)\" %(xmin,imWidth,imHeight))\n    if(y<0 or y>imHeight):\n            raise Exception(\"Y value (%s)  not valid. Image dimensions: (%s,%s) Sample: %s Line:%s\" %(ymin,imWidth,imHeight))\n\ndef validate_clockwise_points(points):\n    \"\"\"\n    Validates that the points that the 4 points that dlimite a polygon are in clockwise order.\n    \"\"\"\n    \n    if len(points) != 8:\n        raise Exception(\"Points list not valid.\" + str(len(points)))\n    \n    point = [\n                [int(points[0]) , int(points[1])],\n                [int(points[2]) , int(points[3])],\n                [int(points[4]) , int(points[5])],\n                [int(points[6]) , int(points[7])]\n            ]\n    edge = [\n                ( point[1][0] - point[0][0])*( point[1][1] + point[0][1]),\n                ( point[2][0] - point[1][0])*( point[2][1] + point[1][1]),\n                ( point[3][0] - point[2][0])*( point[3][1] + point[2][1]),\n                ( point[0][0] - point[3][0])*( point[0][1] + point[3][1])\n    ]\n    \n    summatory = edge[0] + edge[1] + edge[2] + edge[3];\n    # if summatory>0:\n    #     raise Exception(\"Points are not clockwise. The coordinates of bounding quadrilaterals have to be given in clockwise order. Regarding the correct interpretation of 'clockwise' remember that the image coordinate system used is the standard one, with the image origin at the upper left, the X axis extending to the right and Y axis extending downwards.\")\n\ndef get_tl_line_values_from_file_contents(content,CRLF=True,LTRB=True,withTranscription=False,withConfidence=False,imWidth=0,imHeight=0,sort_by_confidences=True):\n    \"\"\"\n    Returns all points, confindences and transcriptions of a file in lists. Valid line formats:\n    xmin,ymin,xmax,ymax,[confidence],[transcription]\n    x1,y1,x2,y2,x3,y3,x4,y4,[confidence],[transcription]\n    \"\"\"\n    pointsList = []\n    transcriptionsList = []\n    confidencesList = []\n    \n    lines = content.split( \"\\r\\n\" if CRLF else \"\\n\" )\n    for line in lines:\n        line = line.replace(\"\\r\",\"\").replace(\"\\n\",\"\")\n        if(line != \"\") :\n            points, confidence, transcription = get_tl_line_values(line,LTRB,withTranscription,withConfidence,imWidth,imHeight);\n            pointsList.append(points)\n            transcriptionsList.append(transcription)\n            confidencesList.append(confidence)\n\n    if withConfidence and len(confidencesList)>0 and sort_by_confidences:\n        import numpy as np\n        sorted_ind = np.argsort(-np.array(confidencesList))\n        confidencesList = [confidencesList[i] for i in sorted_ind]\n        pointsList = [pointsList[i] for i in sorted_ind]\n        transcriptionsList = [transcriptionsList[i] for i in sorted_ind]        \n        \n    return pointsList,confidencesList,transcriptionsList\n\ndef main_evaluation(p,default_evaluation_params_fn,validate_data_fn,evaluate_method_fn,show_result=True,per_sample=True):\n    \"\"\"\n    This process validates a method, evaluates it and if it succed generates a ZIP file with a JSON entry for each sample.\n    Params:\n    p: Dictionary of parmeters with the GT/submission locations. If None is passed, the parameters send by the system are used.\n    default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation\n    validate_data_fn: points to a method that validates the corrct format of the submission\n    evaluate_method_fn: points to a function that evaluated the submission and return a Dictionary with the results\n    \"\"\"\n    \n    if (p == None):\n        p = dict([s[1:].split('=') for s in sys.argv[1:]])\n        if(len(sys.argv)<3):\n            print_help()\n\n    evalParams = default_evaluation_params_fn()\n    if 'p' in p.keys():\n        evalParams.update( p['p'] if isinstance(p['p'], dict) else json.loads(p['p'][1:-1]) )\n\n    resDict={'calculated':True,'Message':'','method':'{}','per_sample':'{}'}    \n    validate_data_fn(p['g'], p['s'], evalParams)  \n    evalData = evaluate_method_fn(p['g'], p['s'], evalParams)\n    resDict.update(evalData)\n\n    if 'o' in p:\n        if not os.path.exists(p['o']):\n            os.makedirs(p['o'])\n\n        resultsOutputname = p['o'] + '/results.zip'\n        outZip = zipfile.ZipFile(resultsOutputname, mode='w', allowZip64=True)\n\n        del resDict['per_sample']\n        if 'output_items' in resDict.keys():\n            del resDict['output_items']\n\n        outZip.writestr('method.json',json.dumps(resDict))\n        \n    if not resDict['calculated']:\n        if show_result:\n            sys.stderr.write('Error!\\n'+ resDict['Message'])\n        if 'o' in p:\n            outZip.close()\n        return resDict\n    \n    if 'o' in p:\n        if per_sample == True:\n            for k,v in evalData['per_sample'].items():\n                outZip.writestr( k + '.json',json.dumps(v)) \n\n            if 'output_items' in evalData.keys():\n                for k, v in evalData['output_items'].items():\n                    outZip.writestr( k,v) \n\n        outZip.close()\n\n    if show_result:\n        sys.stdout.write(\"Calculated!\")\n        sys.stdout.write(json.dumps(resDict['method']))\n    \n    return resDict\n\n\ndef main_validation(default_evaluation_params_fn,validate_data_fn):\n    \"\"\"\n    This process validates a method\n    Params:\n    default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation\n    validate_data_fn: points to a method that validates the corrct format of the submission\n    \"\"\"    \n    p = dict([s[1:].split('=') for s in sys.argv[1:]])\n    evalParams = default_evaluation_params_fn()\n    if 'p' in p.keys():\n        evalParams.update( p['p'] if isinstance(p['p'], dict) else json.loads(p['p'][1:-1]) )\n    validate_data_fn(p['g'], p['s'], evalParams)              \n    print ('SUCCESS')\n    sys.exit(0)\n"
  },
  {
    "path": "script.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom collections import namedtuple\nimport rrc_evaluation_funcs\nimport importlib\nimport math\n\ndef evaluation_imports():\n    \"\"\"\n    evaluation_imports: Dictionary ( key = module name , value = alias  )  with python modules used in the evaluation. \n    \"\"\"    \n    return {\n            'Polygon':'plg',\n            'numpy':'np'\n            }\n\ndef default_evaluation_params():\n    \"\"\"\n    default_evaluation_params: Default parameters to use for the validation and evaluation.\n    \"\"\"\n    return {\n            'AREA_RECALL_CONSTRAINT' : 0.4,\n            'AREA_PRECISION_CONSTRAINT' :0.4,\n            'EV_PARAM_IND_CENTER_DIFF_THR': 1,\n            'GT_SAMPLE_NAME_2_ID':'.*([0-9]+).*',\n            'DET_SAMPLE_NAME_2_ID':'.*([0-9]+).*',\n            'GT_LTRB': False, # LTRB: 2points(left,top,right,bottom) or 4 points(x1,y1,x2,y2,x3,y3,x4,y4)\n            'GT_CRLF': False, # Lines are delimited by Windows CRLF format\n            'DET_LTRB': False, # LTRB: 2points(left,top,right,bottom) or 4 points(x1,y1,x2,y2,x3,y3,x4,y4)\n            'DET_CRLF': False, # Lines are delimited by Windows CRLF format\n            'CONFIDENCES': False, # Detections must include confidence value. AP will be calculated\n            'TRANSCRIPTION': False, # Does prediction has transcription or not\n            'PER_SAMPLE_RESULTS': True, # Generate per sample results and produce data for visualization\n            }\n\ndef validate_data(gtFilePath, submFilePath,evaluationParams):\n    \"\"\"\n    Method validate_data: validates that all files in the results folder are correct (have the correct name contents).\n                            Validates also that there are no missing files in the folder.\n                            If some error detected, the method raises the error\n    \"\"\"\n    gt = rrc_evaluation_funcs.load_zip_file(gtFilePath,evaluationParams['GT_SAMPLE_NAME_2_ID'])\n\n    subm = rrc_evaluation_funcs.load_zip_file(submFilePath,evaluationParams['DET_SAMPLE_NAME_2_ID'],True)\n    \n    #Validate format of GroundTruth\n    for k in gt:\n        rrc_evaluation_funcs.validate_lines_in_file(k,gt[k],evaluationParams['GT_CRLF'],evaluationParams['GT_LTRB'],True)\n\n    #Validate format of results\n    for k in subm:\n        if (k in gt) == False :\n            raise Exception(\"The sample %s not present in GT\" %k)\n        \n        rrc_evaluation_funcs.validate_lines_in_file(k,subm[k],evaluationParams['DET_CRLF'],evaluationParams['DET_LTRB'],evaluationParams['TRANSCRIPTION'],evaluationParams['CONFIDENCES'])\n\n    \ndef evaluate_method(gtFilePath, submFilePath, evaluationParams):\n    \"\"\"\n    Method evaluate_method: evaluate method and returns the results\n        Results. Dictionary with the following values:\n        - method (required)  Global method metrics. Ex: { 'Precision':0.8,'Recall':0.9 }\n        - samples (optional) Per sample metrics. Ex: {'sample1' : { 'Precision':0.8,'Recall':0.9 } , 'sample2' : { 'Precision':0.8,'Recall':0.9 }\n    \"\"\"    \n    \n    for module,alias in evaluation_imports().items():\n        globals()[alias] = importlib.import_module(module)    \n    \n    def polygon_from_points(points):\n        \"\"\"\n        Returns a Polygon object to use with the Polygon2 class from a list of 8 points: x1,y1,x2,y2,x3,y3,x4,y4\n        \"\"\"        \n        resBoxes=np.empty([1,8],dtype='int32')\n        resBoxes[0,0]=int(points[0])\n        resBoxes[0,4]=int(points[1])\n        resBoxes[0,1]=int(points[2])\n        resBoxes[0,5]=int(points[3])\n        resBoxes[0,2]=int(points[4])\n        resBoxes[0,6]=int(points[5])\n        resBoxes[0,3]=int(points[6])\n        resBoxes[0,7]=int(points[7])\n        pointMat = resBoxes[0].reshape([2,4]).T\n        return plg.Polygon( pointMat)    \n    \n    def rectangle_to_polygon(rect):\n        resBoxes=np.empty([1,8],dtype='int32')\n        resBoxes[0,0]=int(rect.xmin)\n        resBoxes[0,4]=int(rect.ymin)\n        resBoxes[0,1]=int(rect.xmax)\n        resBoxes[0,5]=int(rect.ymin)\n        resBoxes[0,2]=int(rect.xmax)\n        resBoxes[0,6]=int(rect.ymax)\n        resBoxes[0,3]=int(rect.xmin)\n        resBoxes[0,7]=int(rect.ymax)\n        pointMat = resBoxes[0].reshape([2,4]).T     \n        return plg.Polygon( pointMat)\n    \n    def rectangle_to_points(rect):\n        points = [int(rect.xmin), int(rect.ymax), int(rect.xmax), int(rect.ymax), int(rect.xmax), int(rect.ymin), int(rect.xmin), int(rect.ymin)]\n        return points\n\n    def polygon_to_points(pol):\n        pointMat = []\n        for p in pol:\n            for i in range(len(p)):\n                pointMat.extend(p[i])\n        return pointMat\n        \n    def get_intersection(pD,pG):\n        pInt = pD & pG\n        if len(pInt) == 0:\n            return 0\n        return pInt.area()\n    \n    def compute_ap(confList, matchList,numGtCare):\n        correct = 0\n        AP = 0\n        if len(confList)>0:\n            confList = np.array(confList)\n            matchList = np.array(matchList)\n            sorted_ind = np.argsort(-confList)\n            confList = confList[sorted_ind]\n            matchList = matchList[sorted_ind]\n            for n in range(len(confList)):\n                match = matchList[n]\n                if match:\n                    correct += 1\n                    AP += float(correct)/(n + 1)\n            if numGtCare>0:\n                AP /= numGtCare     \n        return AP\n\n    def point_distance(a, b):\n        distx = math.fabs(a[0] - b[0])\n        disty = math.fabs(a[1] - b[1])\n        return math.sqrt(distx * distx + disty * disty)\n\n    def diag(points):\n        diag1 = point_distance((points[0], points[1]), (points[4], points[5]))\n        diag2 = point_distance((points[2], points[3]), (points[6], points[7]))\n        return (diag1 + diag2) / 2\n\n    def center_distance(p1, p2):\n        return point_distance(p1.center(), p2.center())\n    \n    def get_midpoints(p1,p2):\n        return ((p1[0]+p2[0])/2, (p1[1]+p2[1])/2)\n    \n    def get_angle_3pt(a, b, c):\n        \"\"\"Counterclockwise angle in degrees by turning from a to c around b\n            Returns a float between 0.0 and 360.0\"\"\"\n        ang = math.degrees(\n            math.atan2(c[1]-b[1], c[0]-b[0]) - math.atan2(a[1]-b[1], a[0]-b[0]))\n        return ang + 360 if ang < 0 else ang\n    \n    def gtBoxtoChars(num, points):\n        chars = []     \n        assert len(points) == 8\n        p1 = get_midpoints([points[0],points[1]], [points[6],points[7]])\n        p2 = get_midpoints([points[2],points[3]], [points[4],points[5]])\n        unitx = (p2[0] - p1[0]) / num\n        unity = (p2[1] - p1[1]) / num\n        for i in range(num):\n            x = p1[0] + unitx/2 + unitx * i\n            y = p1[1] + unity/2 + unity * i\n            chars.append((x,y))\n        return chars\n\n    def char_fill(detNums, matchMat):\n        for detNum in detNums:\n            detPol = detPols[detNum]\n            for gtNum, gtChars in enumerate(gtCharPoints):\n                if matchMat[gtNum, detNum] == 1:\n                    for gtCharNum, gtChar in enumerate(gtChars):\n                        if detPol.isInside(gtChar[0], gtChar[1]):\n                            gtCharCounts[gtNum][detNum][gtCharNum] = 1\n\n    def one_to_one_match(row, col):\n        cont = 0\n        for j in range(len(recallMat[0])):    \n            if recallMat[row,j] >= evaluationParams['AREA_RECALL_CONSTRAINT'] and precisionMat[row,j] >= evaluationParams['AREA_PRECISION_CONSTRAINT'] :\n                cont = cont +1\n        if (cont != 1):\n            return False\n        cont = 0\n        for i in range(len(recallMat)):    \n            if recallMat[i,col] >= evaluationParams['AREA_RECALL_CONSTRAINT'] and precisionMat[i,col] >= evaluationParams['AREA_PRECISION_CONSTRAINT'] :\n                cont = cont +1\n        if (cont != 1):\n            return False\n        \n        if recallMat[row,col] >= evaluationParams['AREA_RECALL_CONSTRAINT'] and precisionMat[row,col] >= evaluationParams['AREA_PRECISION_CONSTRAINT'] :\n            return True\n        return False\n\n    def one_to_many_match(gtNum):\n        many_sum = 0\n        detRects = []\n        for detNum in range(len(recallMat[0])):\n            if detNum not in detDontCarePolsNum and gtExcludeMat[gtNum] == 0 and detExcludeMat[detNum] == 0:\n                if precisionMat[gtNum,detNum] >= evaluationParams['AREA_PRECISION_CONSTRAINT']:\n                    many_sum += recallMat[gtNum,detNum]\n                    detRects.append(detNum)\n        if many_sum >= evaluationParams['AREA_RECALL_CONSTRAINT'] and len(detRects) >= 2:\n            pivots = []\n            for matchDet in detRects:\n                pD = polygon_from_points(detPolPoints[matchDet])\n                pivots.append([get_midpoints(pD[0][0], pD[0][3]), pD.center()])\n            for i in range(len(pivots)):\n                for k in range(len(pivots)):\n                    if k == i:\n                        continue\n                    angle = get_angle_3pt(pivots[i][0], pivots[k][1], pivots[i][1])\n                    if angle > 180:\n                        angle = 360 - angle\n                    if min(angle, 180 - angle) >= 45:\n                        return False, []\n            return True, detRects\n        else:\n            return False, []\n\n    def many_to_one_match(detNum):\n        many_sum = 0\n        gtRects = []\n        for gtNum in range(len(recallMat)):\n            if gtNum not in gtDontCarePolsNum and gtExcludeMat[gtNum] == 0 and detExcludeMat[detNum] == 0:\n                if recallMat[gtNum,detNum] >= evaluationParams['AREA_RECALL_CONSTRAINT']:\n                    many_sum += precisionMat[gtNum,detNum]\n                    gtRects.append(gtNum)\n        if many_sum >= evaluationParams['AREA_PRECISION_CONSTRAINT'] and len(gtRects) >= 2:\n            pivots = []\n            for matchGt in gtRects:\n                pG = gtPols[matchGt]\n                pivots.append([get_midpoints(pG[0][0], pG[0][3]), pG.center()])\n            for i in range(len(pivots)):\n                for k in range(len(pivots)):\n                    if k == i:\n                        continue\n                    angle = get_angle_3pt(pivots[i][0], pivots[k][1], pivots[i][1])\n                    if angle > 180:\n                        angle = 360 - angle\n                    if min(angle, 180 - angle) >= 45:\n                        return False, []\n            return True, gtRects\n        else:\n            return False, []\n\n    perSampleMetrics = {}\n    \n    methodRecallSum = 0\n    methodPrecisionSum = 0\n    \n    Rectangle = namedtuple('Rectangle', 'xmin ymin xmax ymax')\n    \n    gt = rrc_evaluation_funcs.load_zip_file(gtFilePath,evaluationParams['GT_SAMPLE_NAME_2_ID'])\n    subm = rrc_evaluation_funcs.load_zip_file(submFilePath,evaluationParams['DET_SAMPLE_NAME_2_ID'],True)\n\n    numGlobalCareGt = 0;\n    numGlobalCareDet = 0;\n    \n    arrGlobalConfidences = [];\n    arrGlobalMatches = [];\n\n    for resFile in gt:\n\n        gtFile = rrc_evaluation_funcs.decode_utf8(gt[resFile])\n        recall = 0\n        precision = 0\n        hmean = 0    \n        recallAccum = 0.\n        precisionAccum = 0.\n\n        detMatched = 0\n        numGtCare = 0\n        numDetCare = 0\n        \n        recallMat = np.empty([1,1])\n        precisionMat = np.empty([1,1])\n        matchMat = np.zeros([1,1])\n        \n        gtPols = []\n        detPols = []\n        \n        gtPolPoints = []\n        detPolPoints = []  \n        \n        # pseudo character centers\n        gtCharPoints = []\n        gtCharCounts = []\n        \n        # visualization\n        charCounts = np.zeros([1,1])\n        recallScore = list()\n        precisionScore = list()\n\n        #Array of Ground Truth Polygons' keys marked as don't Care\n        gtDontCarePolsNum = []\n        #Array of Detected Polygons' matched with a don't Care GT\n        detDontCarePolsNum = []\n        \n        pairs = [] \n        detMatchedNums = []\n        gtExcludeNums = []\n        \n        arrSampleConfidences = [];\n        arrSampleMatch = [];\n        sampleAP = 0;\n\n        evaluationLog = \"\"\n        \n        pointsList,_,transcriptionsList = rrc_evaluation_funcs.get_tl_line_values_from_file_contents(gtFile, evaluationParams['GT_CRLF'], evaluationParams['GT_LTRB'], True, False)\n        for n in range(len(pointsList)):\n            points = pointsList[n]\n            transcription = transcriptionsList[n]\n            dontCare = transcription == \"###\"\n            if evaluationParams['GT_LTRB']:\n                gtRect = Rectangle(*points)\n                gtPol = rectangle_to_polygon(gtRect)\n                points = polygon_to_points(gtPol)\n            else:\n                gtPol = polygon_from_points(points)\n            gtPols.append(gtPol)\n            if dontCare:\n                gtDontCarePolsNum.append( len(gtPols)-1 )\n                gtPolPoints.append(points)\n                gtCharPoints.append([])\n            else:\n                gtCharSize = len(transcription)\n                aspect_ratio = gtPol.aspectRatio()\n                if aspect_ratio > 1.5:\n                    points_ver =  [points[6], points[7], points[0], points[1], points[2], points[3], points[4], points[5]]\n                    gtPolPoints.append(points_ver)\n                    gtCharPoints.append(gtBoxtoChars(gtCharSize, points_ver))\n                else:\n                    gtCharPoints.append(gtBoxtoChars(gtCharSize, points))\n                    gtPolPoints.append(points)\n        evaluationLog += \"GT polygons: \" + str(len(gtPols)) + (\" (\" + str(len(gtDontCarePolsNum)) + \" don't care)\\n\" if len(gtDontCarePolsNum)>0 else \"\\n\")\n\n        # GT Don't Care overlap\n        for DontCare in gtDontCarePolsNum:\n            for gtNum in list(set(range(len(gtPols))) - set(gtDontCarePolsNum)):\n                if get_intersection(gtPols[gtNum], gtPols[DontCare]) > 0:\n                    gtPols[DontCare] -= gtPols[gtNum]\n\n        if resFile in subm:\n            \n            detFile = rrc_evaluation_funcs.decode_utf8(subm[resFile]) \n\n            pointsList,confidencesList,_ = rrc_evaluation_funcs.get_tl_line_values_from_file_contents(detFile,evaluationParams['DET_CRLF'],evaluationParams['DET_LTRB'],evaluationParams['TRANSCRIPTION'],evaluationParams['CONFIDENCES'])\n            for n in range(len(pointsList)):\n                points = pointsList[n]\n                \n                if evaluationParams['DET_LTRB']:\n                    detRect = Rectangle(*points)\n                    detPol = rectangle_to_polygon(detRect)\n                    points = polygon_to_points(detPol)\n                else:\n                    detPol = polygon_from_points(points)                    \n                detPols.append(detPol)\n                detPolPoints.append(points)\n                \n            evaluationLog += \"DET polygons: \" + str(len(detPols))\n            \n            if len(gtPols)>0 and len(detPols)>0:\n                #Calculate IoU and precision matrixs\n                outputShape=[len(gtPols),len(detPols)]\n                recallMat = np.empty(outputShape)\n                precisionMat = np.empty(outputShape)\n                matchMat = np.zeros(outputShape)\n                gtRectMat = np.zeros(len(gtPols),np.int8)\n                detRectMat = np.zeros(len(detPols),np.int8)\n                gtExcludeMat = np.zeros(len(gtPols),np.int8)\n                detExcludeMat = np.zeros(len(detPols),np.int8)\n                for gtNum in range(len(gtPols)):\n                    detCharCounts = []\n                    for detNum in range(len(detPols)):\n                        pG = gtPols[gtNum]\n                        pD = detPols[detNum]\n                        intersected_area = get_intersection(pD,pG)\n                        recallMat[gtNum,detNum] = 0 if pG.area()==0 else intersected_area / pG.area()\n                        precisionMat[gtNum,detNum] = 0 if pD.area()==0 else intersected_area / pD.area()\n                        detCharCounts.append(np.zeros(len(gtCharPoints[gtNum])))\n                    gtCharCounts.append(detCharCounts)\n                    \n                # Find detection Don't Care\n                if len(gtDontCarePolsNum)>0 :\n                    for detNum in range(len(detPols)):\n                        # many-to-one\n                        many_sum = 0\n                        for gtNum in gtDontCarePolsNum:\n                            if recallMat[gtNum, detNum] > evaluationParams['AREA_RECALL_CONSTRAINT']:\n                                many_sum += precisionMat[gtNum, detNum]\n                        if many_sum >= evaluationParams['AREA_PRECISION_CONSTRAINT']:\n                            detDontCarePolsNum.append(detNum)\n                        else:\n                            for gtNum in gtDontCarePolsNum:\n                                if precisionMat[gtNum, detNum] > evaluationParams['AREA_PRECISION_CONSTRAINT']:\n                                    detDontCarePolsNum.append(detNum)\n                                    break\n                        # many-to-one for mixed DC and non-DC\n                        for gtNum in gtDontCarePolsNum:\n                            if recallMat[gtNum, detNum] > 0:\n                                detPols[detNum] -= gtPols[gtNum]\n                                    \n                    evaluationLog += \" (\" + str(len(detDontCarePolsNum)) + \" don't care)\\n\" if len(detDontCarePolsNum)>0 else \"\\n\"\n                \n                # Recalculate matrices\n                for gtNum in range(len(gtPols)):\n                    for detNum in range(len(detPols)):\n                        pG = gtPols[gtNum]\n                        pD = detPols[detNum]\n                        intersected_area = get_intersection(pD,pG)\n                        recallMat[gtNum,detNum] = 0 if pG.area()==0 else intersected_area / pG.area()\n                        precisionMat[gtNum,detNum] = 0 if pD.area()==0 else intersected_area / pD.area()\n                        \n                # Find many-to-one matches\n                evaluationLog += \"Find many-to-one matches\\n\"\n                for detNum in range(len(detPols)):\n                    if detNum not in detDontCarePolsNum:\n                        match, matchesGt = many_to_one_match(detNum)\n                        if match:\n                            pairs.append({'gt':matchesGt, 'det':[detNum], 'type':'MO'})\n                            evaluationLog += \"Match GT #\" + str(matchesGt) + \" with Det #\" + str(detNum) + \"\\n\"\n                            \n                # Find one-to-one matches\n                evaluationLog += \"Find one-to-one matches\\n\"\n                for gtNum in range(len(gtPols)):\n                    for detNum in range(len(detPols)):\n                        if gtNum not in gtDontCarePolsNum and detNum not in detDontCarePolsNum :\n                            match = one_to_one_match(gtNum, detNum)\n                            if match:\n                                normDist = center_distance(gtPols[gtNum], detPols[detNum]);\n                                normDist /= diag(gtPolPoints[gtNum]) + diag(detPolPoints[detNum]);\n                                normDist *= 2.0;\n                                if normDist < evaluationParams['EV_PARAM_IND_CENTER_DIFF_THR'] :\n                                    pairs.append({'gt':[gtNum],'det':[detNum],'type':'OO'})\n                                    evaluationLog += \"Match GT #\" + str(gtNum) + \" with Det #\" + str(detNum) + \"\\n\"\n                                    \n                # Find one-to-many matches\n                evaluationLog += \"Find one-to-many matches\\n\"\n                for gtNum in range(len(gtPols)):\n                    if gtNum not in gtDontCarePolsNum:\n                        match, matchesDet = one_to_many_match(gtNum)\n                        if match:\n                            pairs.append({'gt':[gtNum], 'det':matchesDet, 'type':'OM'})\n                            evaluationLog += \"Match Gt #\" + str(gtNum) + \" with Det #\" + str(matchesDet) + \"\\n\"\n\n                # Fill match matrix\n                for pair in pairs:\n                    matchMat[pair['gt'],pair['det']] = 1\n                \n                # Fill character matrix\n                char_fill(np.where(matchMat.sum(axis=0) > 0)[0], matchMat)\n\n                # Recall score\n                for gtNum in range(len(gtRectMat)):\n                    if matchMat.sum(axis=1)[gtNum] > 0:\n                        recallAccum += len(np.where(sum(gtCharCounts[gtNum]) == 1)[0]) / len(gtCharPoints[gtNum])\n                        if len(np.where(sum(gtCharCounts[gtNum]) == 1)[0]) / len(gtCharPoints[gtNum]) < 1:\n                            recallScore.append(\"<font color=red>\" + str(len(np.where(sum(gtCharCounts[gtNum]) == 1)[0])) + \"/\" + str(len(gtCharPoints[gtNum])) + \"</font>\")\n                        else: recallScore.append(str(len(np.where(sum(gtCharCounts[gtNum]) == 1)[0])) + \"/\" + str(len(gtCharPoints[gtNum])))\n                    else: recallScore.append(\"\")\n\n                # Precision score\n                for detNum in range(len(detRectMat)):\n                    if matchMat.sum(axis=0)[detNum] > 0:\n                        detTotal = 0; detContain = 0\n                        for gtNum in range(len(gtRectMat)):\n                            if matchMat[gtNum, detNum] > 0:\n                                detTotal += len(gtCharCounts[gtNum][detNum])\n                                detContain += len(np.where(gtCharCounts[gtNum][detNum] == 1)[0])\n                        precisionAccum += detContain / detTotal\n                        if detContain / detTotal < 1:\n                            precisionScore.append(\"<font color=red>\" + str(detContain) + \"/\" + str(detTotal) + \"</font>\")\n                        else: precisionScore.append(str(detContain) + \"/\" + str(detTotal))\n                    else:\n                        precisionScore.append(\"\")\n\n                # Visualization\n                charCounts = np.zeros((len(gtRectMat), len(detRectMat)))\n                for gtNum in range(len(gtRectMat)):\n                    for detNum in range(len(detRectMat)):\n                        charCounts[gtNum][detNum] = sum(gtCharCounts[gtNum][detNum])\n\n\n            if evaluationParams['CONFIDENCES']:\n                for detNum in range(len(detPols)):\n                    if detNum not in detDontCarePolsNum :\n                        match = detNum in detMatchedNums\n                        arrSampleConfidences.append(confidencesList[detNum])\n                        arrSampleMatch.append(match)\n                        arrGlobalConfidences.append(confidencesList[detNum]);\n                        arrGlobalMatches.append(match);\n\n        numGtCare = (len(gtPols) - len(gtDontCarePolsNum))\n        numDetCare = (len(detPols) - len(detDontCarePolsNum))\n        if numGtCare == 0:\n            recall = float(1)\n            precision = float(0) if numDetCare >0 else float(1)\n            sampleAP = precision\n        else:\n            recall = float(recallAccum) / numGtCare\n            precision = float(0) if numDetCare==0 else float(precisionAccum) / numDetCare\n            if evaluationParams['CONFIDENCES'] and evaluationParams['PER_SAMPLE_RESULTS']:\n                sampleAP = compute_ap(arrSampleConfidences, arrSampleMatch, numGtCare )                    \n\n        hmean = 0 if (precision + recall)==0 else 2.0 * precision * recall / (precision + recall)                \n\n        evaluationLog += \"<b>Recall = \" + str(round(recallAccum,2)) + \" / \" + str(numGtCare) + \" = \" + str(round(recall,2)) + \"\\n</b>\"\n        evaluationLog += \"<b>Precision = \" + str(round(precisionAccum,2)) + \" / \" + str(numDetCare) + \" = \"+ str(round(precision,2)) + \"\\n</b>\"\n        \n        methodRecallSum += recallAccum\n        methodPrecisionSum += precisionAccum\n        numGlobalCareGt += numGtCare\n        numGlobalCareDet += numDetCare\n\n        if evaluationParams['PER_SAMPLE_RESULTS']:\n            perSampleMetrics[resFile] = {\n                                            'precision':precision,\n                                            'recall':recall,\n                                            'hmean':hmean,\n                                            'pairs':pairs,\n                                            'AP':sampleAP,\n                                            'recallMat':[] if len(detPols)>100 else recallMat.tolist(),\n                                            'precisionMat':[] if len(detPols)>100 else precisionMat.tolist(),\n                                            'gtPolPoints':gtPolPoints,\n                                            'detPolPoints':detPolPoints,\n                                            'gtCharPoints':gtCharPoints,\n                                            'gtCharCounts':[sum(k).tolist() for k in gtCharCounts],\n                                            'charCounts': charCounts.tolist(),\n                                            'recallScore': recallScore,\n                                            'precisionScore': precisionScore,\n                                            'gtDontCare':gtDontCarePolsNum,\n                                            'detDontCare':detDontCarePolsNum,\n                                            'evaluationParams': evaluationParams,\n                                            'evaluationLog': evaluationLog\n                                        }\n                                    \n    # Compute MAP and MAR\n    AP = 0\n    if evaluationParams['CONFIDENCES']:\n        AP = compute_ap(arrGlobalConfidences, arrGlobalMatches, numGlobalCareGt)\n\n    methodRecall = 0 if numGlobalCareGt == 0 else methodRecallSum/numGlobalCareGt\n    methodPrecision = 0 if numGlobalCareDet == 0 else methodPrecisionSum/numGlobalCareDet\n    methodHmean = 0 if methodRecall + methodPrecision==0 else 2* methodRecall * methodPrecision / (methodRecall + methodPrecision)\n    \n    methodMetrics = {'recall':methodRecall, 'precision':methodPrecision, 'hmean':methodHmean, 'AP':AP  }\n    \n    resDict = {'calculated':True,'Message':'','method': methodMetrics,'per_sample': perSampleMetrics}\n    \n    return resDict;\n\n\nif __name__=='__main__':\n        \n    rrc_evaluation_funcs.main_evaluation(None, default_evaluation_params, validate_data, evaluate_method)\n"
  },
  {
    "path": "static/funcs.js",
    "content": "\nvar getUrlParameter = function getUrlParameter(sParam) {\n    var sPageURL = decodeURIComponent(window.location.search.substring(1)),\n        sURLVariables = sPageURL.split('&'),\n        sParameterName,\n        i;\n\n    for (i = 0; i < sURLVariables.length; i++) {\n        sParameterName = sURLVariables[i].split('=');\n\n        if (sParameterName[0] === sParam) {\n            return sParameterName[1] === undefined ? true : sParameterName[1];\n        }\n    }\n};"
  },
  {
    "path": "static/jquery-mousewheel.js",
    "content": "/**\n *\n * credits for this plugin go to brandonaaron.net\n *\n * unfortunately his site is down\n *\n * @param {Object} up\n * @param {Object} down\n * @param {Object} preventDefault\n */\njQuery.fn.extend({\n\tmousewheel: function(up, down, preventDefault) {\n\t\treturn this.hover(\n\t\t\tfunction() {\n\t\t\t\tjQuery.event.mousewheel.giveFocus(this, up, down, preventDefault);\n\t\t\t},\n\t\t\tfunction() {\n\t\t\t\tjQuery.event.mousewheel.removeFocus(this);\n\t\t\t}\n\t\t);\n\t},\n\tmousewheeldown: function(fn, preventDefault) {\n\t\treturn this.mousewheel(function(){}, fn, preventDefault);\n\t},\n\tmousewheelup: function(fn, preventDefault) {\n\t\treturn this.mousewheel(fn, function(){}, preventDefault);\n\t},\n\tunmousewheel: function() {\n\t\treturn this.each(function() {\n\t\t\tjQuery(this).unmouseover().unmouseout();\n\t\t\tjQuery.event.mousewheel.removeFocus(this);\n\t\t});\n\t},\n\tunmousewheeldown: jQuery.fn.unmousewheel,\n\tunmousewheelup: jQuery.fn.unmousewheel\n});\n\n\njQuery.event.mousewheel = {\n\tgiveFocus: function(el, up, down, preventDefault) {\n\t\tif (el._handleMousewheel) jQuery(el).unmousewheel();\n\n\t\tif (preventDefault == window.undefined && down && down.constructor != Function) {\n\t\t\tpreventDefault = down;\n\t\t\tdown = null;\n\t\t}\n\n\t\tel._handleMousewheel = function(event) {\n\t\t\tif (!event) event = window.event;\n\t\t\tif (preventDefault)\n\t\t\t\tif (event.preventDefault) event.preventDefault();\n\t\t\t\telse event.returnValue = false;\n\t\t\tvar delta = 0;\n\t\t\tif (event.wheelDelta) {\n\t\t\t\tdelta = event.wheelDelta/120;\n\t\t\t\tif (window.opera) delta = -delta;\n\t\t\t} else if (event.detail) {\n\t\t\t\tdelta = -event.detail/3;\n\t\t\t}\n\t\t\tif (up && (delta > 0 || !down))\n\t\t\t\tup.apply(el, [event, delta]);\n\t\t\telse if (down && delta < 0)\n\t\t\t\tdown.apply(el, [event, delta]);\n\t\t};\n\n\t\tif (window.addEventListener)\n\t\t\twindow.addEventListener('DOMMouseScroll', el._handleMousewheel, false);\n\t\twindow.onmousewheel = document.onmousewheel = el._handleMousewheel;\n\t},\n\n\tremoveFocus: function(el) {\n\t\tif (!el._handleMousewheel) return;\n\n\t\tif (window.removeEventListener)\n\t\t\twindow.removeEventListener('DOMMouseScroll', el._handleMousewheel, false);\n\t\twindow.onmousewheel = document.onmousewheel = null;\n\t\tel._handleMousewheel = null;\n\t}\n};"
  },
  {
    "path": "static/jquery.form-3.51.js",
    "content": "/*!\n * jQuery Form Plugin\n * version: 3.51.0-2014.06.20\n * Requires jQuery v1.5 or later\n * Copyright (c) 2014 M. Alsup\n * Examples and documentation at: http://malsup.com/jquery/form/\n * Project repository: https://github.com/malsup/form\n * Dual licensed under the MIT and GPL licenses.\n * https://github.com/malsup/form#copyright-and-license\n */\n!function(e){\"use strict\";\"function\"==typeof define&&define.amd?define([\"jquery\"],e):e(\"undefined\"!=typeof jQuery?jQuery:window.Zepto)}(function(e){\"use strict\";function t(t){var r=t.data;t.isDefaultPrevented()||(t.preventDefault(),e(t.target).ajaxSubmit(r))}function r(t){var r=t.target,a=e(r);if(!a.is(\"[type=submit],[type=image]\")){var n=a.closest(\"[type=submit]\");if(0===n.length)return;r=n[0]}var i=this;if(i.clk=r,\"image\"==r.type)if(void 0!==t.offsetX)i.clk_x=t.offsetX,i.clk_y=t.offsetY;else if(\"function\"==typeof e.fn.offset){var o=a.offset();i.clk_x=t.pageX-o.left,i.clk_y=t.pageY-o.top}else i.clk_x=t.pageX-r.offsetLeft,i.clk_y=t.pageY-r.offsetTop;setTimeout(function(){i.clk=i.clk_x=i.clk_y=null},100)}function a(){if(e.fn.ajaxSubmit.debug){var t=\"[jquery.form] \"+Array.prototype.join.call(arguments,\"\");window.console&&window.console.log?window.console.log(t):window.opera&&window.opera.postError&&window.opera.postError(t)}}var n={};n.fileapi=void 0!==e(\"<input type='file'/>\").get(0).files,n.formdata=void 0!==window.FormData;var i=!!e.fn.prop;e.fn.attr2=function(){if(!i)return this.attr.apply(this,arguments);var e=this.prop.apply(this,arguments);return e&&e.jquery||\"string\"==typeof e?e:this.attr.apply(this,arguments)},e.fn.ajaxSubmit=function(t){function r(r){var a,n,i=e.param(r,t.traditional).split(\"&\"),o=i.length,s=[];for(a=0;o>a;a++)i[a]=i[a].replace(/\\+/g,\" \"),n=i[a].split(\"=\"),s.push([decodeURIComponent(n[0]),decodeURIComponent(n[1])]);return s}function o(a){for(var n=new FormData,i=0;i<a.length;i++)n.append(a[i].name,a[i].value);if(t.extraData){var o=r(t.extraData);for(i=0;i<o.length;i++)o[i]&&n.append(o[i][0],o[i][1])}t.data=null;var s=e.extend(!0,{},e.ajaxSettings,t,{contentType:!1,processData:!1,cache:!1,type:u||\"POST\"});t.uploadProgress&&(s.xhr=function(){var r=e.ajaxSettings.xhr();return r.upload&&r.upload.addEventListener(\"progress\",function(e){var r=0,a=e.loaded||e.position,n=e.total;e.lengthComputable&&(r=Math.ceil(a/n*100)),t.uploadProgress(e,a,n,r)},!1),r}),s.data=null;var c=s.beforeSend;return s.beforeSend=function(e,r){r.data=t.formData?t.formData:n,c&&c.call(this,e,r)},e.ajax(s)}function s(r){function n(e){var t=null;try{e.contentWindow&&(t=e.contentWindow.document)}catch(r){a(\"cannot get iframe.contentWindow document: \"+r)}if(t)return t;try{t=e.contentDocument?e.contentDocument:e.document}catch(r){a(\"cannot get iframe.contentDocument: \"+r),t=e.document}return t}function o(){function t(){try{var e=n(g).readyState;a(\"state = \"+e),e&&\"uninitialized\"==e.toLowerCase()&&setTimeout(t,50)}catch(r){a(\"Server abort: \",r,\" (\",r.name,\")\"),s(k),j&&clearTimeout(j),j=void 0}}var r=f.attr2(\"target\"),i=f.attr2(\"action\"),o=\"multipart/form-data\",c=f.attr(\"enctype\")||f.attr(\"encoding\")||o;w.setAttribute(\"target\",p),(!u||/post/i.test(u))&&w.setAttribute(\"method\",\"POST\"),i!=m.url&&w.setAttribute(\"action\",m.url),m.skipEncodingOverride||u&&!/post/i.test(u)||f.attr({encoding:\"multipart/form-data\",enctype:\"multipart/form-data\"}),m.timeout&&(j=setTimeout(function(){T=!0,s(D)},m.timeout));var l=[];try{if(m.extraData)for(var d in m.extraData)m.extraData.hasOwnProperty(d)&&l.push(e.isPlainObject(m.extraData[d])&&m.extraData[d].hasOwnProperty(\"name\")&&m.extraData[d].hasOwnProperty(\"value\")?e('<input type=\"hidden\" name=\"'+m.extraData[d].name+'\">').val(m.extraData[d].value).appendTo(w)[0]:e('<input type=\"hidden\" name=\"'+d+'\">').val(m.extraData[d]).appendTo(w)[0]);m.iframeTarget||v.appendTo(\"body\"),g.attachEvent?g.attachEvent(\"onload\",s):g.addEventListener(\"load\",s,!1),setTimeout(t,15);try{w.submit()}catch(h){var x=document.createElement(\"form\").submit;x.apply(w)}}finally{w.setAttribute(\"action\",i),w.setAttribute(\"enctype\",c),r?w.setAttribute(\"target\",r):f.removeAttr(\"target\"),e(l).remove()}}function s(t){if(!x.aborted&&!F){if(M=n(g),M||(a(\"cannot access response document\"),t=k),t===D&&x)return x.abort(\"timeout\"),void S.reject(x,\"timeout\");if(t==k&&x)return x.abort(\"server abort\"),void S.reject(x,\"error\",\"server abort\");if(M&&M.location.href!=m.iframeSrc||T){g.detachEvent?g.detachEvent(\"onload\",s):g.removeEventListener(\"load\",s,!1);var r,i=\"success\";try{if(T)throw\"timeout\";var o=\"xml\"==m.dataType||M.XMLDocument||e.isXMLDoc(M);if(a(\"isXml=\"+o),!o&&window.opera&&(null===M.body||!M.body.innerHTML)&&--O)return a(\"requeing onLoad callback, DOM not available\"),void setTimeout(s,250);var u=M.body?M.body:M.documentElement;x.responseText=u?u.innerHTML:null,x.responseXML=M.XMLDocument?M.XMLDocument:M,o&&(m.dataType=\"xml\"),x.getResponseHeader=function(e){var t={\"content-type\":m.dataType};return t[e.toLowerCase()]},u&&(x.status=Number(u.getAttribute(\"status\"))||x.status,x.statusText=u.getAttribute(\"statusText\")||x.statusText);var c=(m.dataType||\"\").toLowerCase(),l=/(json|script|text)/.test(c);if(l||m.textarea){var f=M.getElementsByTagName(\"textarea\")[0];if(f)x.responseText=f.value,x.status=Number(f.getAttribute(\"status\"))||x.status,x.statusText=f.getAttribute(\"statusText\")||x.statusText;else if(l){var p=M.getElementsByTagName(\"pre\")[0],h=M.getElementsByTagName(\"body\")[0];p?x.responseText=p.textContent?p.textContent:p.innerText:h&&(x.responseText=h.textContent?h.textContent:h.innerText)}}else\"xml\"==c&&!x.responseXML&&x.responseText&&(x.responseXML=X(x.responseText));try{E=_(x,c,m)}catch(y){i=\"parsererror\",x.error=r=y||i}}catch(y){a(\"error caught: \",y),i=\"error\",x.error=r=y||i}x.aborted&&(a(\"upload aborted\"),i=null),x.status&&(i=x.status>=200&&x.status<300||304===x.status?\"success\":\"error\"),\"success\"===i?(m.success&&m.success.call(m.context,E,\"success\",x),S.resolve(x.responseText,\"success\",x),d&&e.event.trigger(\"ajaxSuccess\",[x,m])):i&&(void 0===r&&(r=x.statusText),m.error&&m.error.call(m.context,x,i,r),S.reject(x,\"error\",r),d&&e.event.trigger(\"ajaxError\",[x,m,r])),d&&e.event.trigger(\"ajaxComplete\",[x,m]),d&&!--e.active&&e.event.trigger(\"ajaxStop\"),m.complete&&m.complete.call(m.context,x,i),F=!0,m.timeout&&clearTimeout(j),setTimeout(function(){m.iframeTarget?v.attr(\"src\",m.iframeSrc):v.remove(),x.responseXML=null},100)}}}var c,l,m,d,p,v,g,x,y,b,T,j,w=f[0],S=e.Deferred();if(S.abort=function(e){x.abort(e)},r)for(l=0;l<h.length;l++)c=e(h[l]),i?c.prop(\"disabled\",!1):c.removeAttr(\"disabled\");if(m=e.extend(!0,{},e.ajaxSettings,t),m.context=m.context||m,p=\"jqFormIO\"+(new Date).getTime(),m.iframeTarget?(v=e(m.iframeTarget),b=v.attr2(\"name\"),b?p=b:v.attr2(\"name\",p)):(v=e('<iframe name=\"'+p+'\" src=\"'+m.iframeSrc+'\" />'),v.css({position:\"absolute\",top:\"-1000px\",left:\"-1000px\"})),g=v[0],x={aborted:0,responseText:null,responseXML:null,status:0,statusText:\"n/a\",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(t){var r=\"timeout\"===t?\"timeout\":\"aborted\";a(\"aborting upload... \"+r),this.aborted=1;try{g.contentWindow.document.execCommand&&g.contentWindow.document.execCommand(\"Stop\")}catch(n){}v.attr(\"src\",m.iframeSrc),x.error=r,m.error&&m.error.call(m.context,x,r,t),d&&e.event.trigger(\"ajaxError\",[x,m,r]),m.complete&&m.complete.call(m.context,x,r)}},d=m.global,d&&0===e.active++&&e.event.trigger(\"ajaxStart\"),d&&e.event.trigger(\"ajaxSend\",[x,m]),m.beforeSend&&m.beforeSend.call(m.context,x,m)===!1)return m.global&&e.active--,S.reject(),S;if(x.aborted)return S.reject(),S;y=w.clk,y&&(b=y.name,b&&!y.disabled&&(m.extraData=m.extraData||{},m.extraData[b]=y.value,\"image\"==y.type&&(m.extraData[b+\".x\"]=w.clk_x,m.extraData[b+\".y\"]=w.clk_y)));var D=1,k=2,A=e(\"meta[name=csrf-token]\").attr(\"content\"),L=e(\"meta[name=csrf-param]\").attr(\"content\");L&&A&&(m.extraData=m.extraData||{},m.extraData[L]=A),m.forceSync?o():setTimeout(o,10);var E,M,F,O=50,X=e.parseXML||function(e,t){return window.ActiveXObject?(t=new ActiveXObject(\"Microsoft.XMLDOM\"),t.async=\"false\",t.loadXML(e)):t=(new DOMParser).parseFromString(e,\"text/xml\"),t&&t.documentElement&&\"parsererror\"!=t.documentElement.nodeName?t:null},C=e.parseJSON||function(e){return window.eval(\"(\"+e+\")\")},_=function(t,r,a){var n=t.getResponseHeader(\"content-type\")||\"\",i=\"xml\"===r||!r&&n.indexOf(\"xml\")>=0,o=i?t.responseXML:t.responseText;return i&&\"parsererror\"===o.documentElement.nodeName&&e.error&&e.error(\"parsererror\"),a&&a.dataFilter&&(o=a.dataFilter(o,r)),\"string\"==typeof o&&(\"json\"===r||!r&&n.indexOf(\"json\")>=0?o=C(o):(\"script\"===r||!r&&n.indexOf(\"javascript\")>=0)&&e.globalEval(o)),o};return S}if(!this.length)return a(\"ajaxSubmit: skipping submit process - no element selected\"),this;var u,c,l,f=this;\"function\"==typeof t?t={success:t}:void 0===t&&(t={}),u=t.type||this.attr2(\"method\"),c=t.url||this.attr2(\"action\"),l=\"string\"==typeof c?e.trim(c):\"\",l=l||window.location.href||\"\",l&&(l=(l.match(/^([^#]+)/)||[])[1]),t=e.extend(!0,{url:l,success:e.ajaxSettings.success,type:u||e.ajaxSettings.type,iframeSrc:/^https/i.test(window.location.href||\"\")?\"javascript:false\":\"about:blank\"},t);var m={};if(this.trigger(\"form-pre-serialize\",[this,t,m]),m.veto)return a(\"ajaxSubmit: submit vetoed via form-pre-serialize trigger\"),this;if(t.beforeSerialize&&t.beforeSerialize(this,t)===!1)return a(\"ajaxSubmit: submit aborted via beforeSerialize callback\"),this;var d=t.traditional;void 0===d&&(d=e.ajaxSettings.traditional);var p,h=[],v=this.formToArray(t.semantic,h);if(t.data&&(t.extraData=t.data,p=e.param(t.data,d)),t.beforeSubmit&&t.beforeSubmit(v,this,t)===!1)return a(\"ajaxSubmit: submit aborted via beforeSubmit callback\"),this;if(this.trigger(\"form-submit-validate\",[v,this,t,m]),m.veto)return a(\"ajaxSubmit: submit vetoed via form-submit-validate trigger\"),this;var g=e.param(v,d);p&&(g=g?g+\"&\"+p:p),\"GET\"==t.type.toUpperCase()?(t.url+=(t.url.indexOf(\"?\")>=0?\"&\":\"?\")+g,t.data=null):t.data=g;var x=[];if(t.resetForm&&x.push(function(){f.resetForm()}),t.clearForm&&x.push(function(){f.clearForm(t.includeHidden)}),!t.dataType&&t.target){var y=t.success||function(){};x.push(function(r){var a=t.replaceTarget?\"replaceWith\":\"html\";e(t.target)[a](r).each(y,arguments)})}else t.success&&x.push(t.success);if(t.success=function(e,r,a){for(var n=t.context||this,i=0,o=x.length;o>i;i++)x[i].apply(n,[e,r,a||f,f])},t.error){var b=t.error;t.error=function(e,r,a){var n=t.context||this;b.apply(n,[e,r,a,f])}}if(t.complete){var T=t.complete;t.complete=function(e,r){var a=t.context||this;T.apply(a,[e,r,f])}}var j=e(\"input[type=file]:enabled\",this).filter(function(){return\"\"!==e(this).val()}),w=j.length>0,S=\"multipart/form-data\",D=f.attr(\"enctype\")==S||f.attr(\"encoding\")==S,k=n.fileapi&&n.formdata;a(\"fileAPI :\"+k);var A,L=(w||D)&&!k;t.iframe!==!1&&(t.iframe||L)?t.closeKeepAlive?e.get(t.closeKeepAlive,function(){A=s(v)}):A=s(v):A=(w||D)&&k?o(v):e.ajax(t),f.removeData(\"jqxhr\").data(\"jqxhr\",A);for(var E=0;E<h.length;E++)h[E]=null;return this.trigger(\"form-submit-notify\",[this,t]),this},e.fn.ajaxForm=function(n){if(n=n||{},n.delegation=n.delegation&&e.isFunction(e.fn.on),!n.delegation&&0===this.length){var i={s:this.selector,c:this.context};return!e.isReady&&i.s?(a(\"DOM not ready, queuing ajaxForm\"),e(function(){e(i.s,i.c).ajaxForm(n)}),this):(a(\"terminating; zero elements found by selector\"+(e.isReady?\"\":\" (DOM not ready)\")),this)}return n.delegation?(e(document).off(\"submit.form-plugin\",this.selector,t).off(\"click.form-plugin\",this.selector,r).on(\"submit.form-plugin\",this.selector,n,t).on(\"click.form-plugin\",this.selector,n,r),this):this.ajaxFormUnbind().bind(\"submit.form-plugin\",n,t).bind(\"click.form-plugin\",n,r)},e.fn.ajaxFormUnbind=function(){return this.unbind(\"submit.form-plugin click.form-plugin\")},e.fn.formToArray=function(t,r){var a=[];if(0===this.length)return a;var i,o=this[0],s=this.attr(\"id\"),u=t?o.getElementsByTagName(\"*\"):o.elements;if(u&&!/MSIE [678]/.test(navigator.userAgent)&&(u=e(u).get()),s&&(i=e(':input[form=\"'+s+'\"]').get(),i.length&&(u=(u||[]).concat(i))),!u||!u.length)return a;var c,l,f,m,d,p,h;for(c=0,p=u.length;p>c;c++)if(d=u[c],f=d.name,f&&!d.disabled)if(t&&o.clk&&\"image\"==d.type)o.clk==d&&(a.push({name:f,value:e(d).val(),type:d.type}),a.push({name:f+\".x\",value:o.clk_x},{name:f+\".y\",value:o.clk_y}));else if(m=e.fieldValue(d,!0),m&&m.constructor==Array)for(r&&r.push(d),l=0,h=m.length;h>l;l++)a.push({name:f,value:m[l]});else if(n.fileapi&&\"file\"==d.type){r&&r.push(d);var v=d.files;if(v.length)for(l=0;l<v.length;l++)a.push({name:f,value:v[l],type:d.type});else a.push({name:f,value:\"\",type:d.type})}else null!==m&&\"undefined\"!=typeof m&&(r&&r.push(d),a.push({name:f,value:m,type:d.type,required:d.required}));if(!t&&o.clk){var g=e(o.clk),x=g[0];f=x.name,f&&!x.disabled&&\"image\"==x.type&&(a.push({name:f,value:g.val()}),a.push({name:f+\".x\",value:o.clk_x},{name:f+\".y\",value:o.clk_y}))}return a},e.fn.formSerialize=function(t){return e.param(this.formToArray(t))},e.fn.fieldSerialize=function(t){var r=[];return this.each(function(){var a=this.name;if(a){var n=e.fieldValue(this,t);if(n&&n.constructor==Array)for(var i=0,o=n.length;o>i;i++)r.push({name:a,value:n[i]});else null!==n&&\"undefined\"!=typeof n&&r.push({name:this.name,value:n})}}),e.param(r)},e.fn.fieldValue=function(t){for(var r=[],a=0,n=this.length;n>a;a++){var i=this[a],o=e.fieldValue(i,t);null===o||\"undefined\"==typeof o||o.constructor==Array&&!o.length||(o.constructor==Array?e.merge(r,o):r.push(o))}return r},e.fieldValue=function(t,r){var a=t.name,n=t.type,i=t.tagName.toLowerCase();if(void 0===r&&(r=!0),r&&(!a||t.disabled||\"reset\"==n||\"button\"==n||(\"checkbox\"==n||\"radio\"==n)&&!t.checked||(\"submit\"==n||\"image\"==n)&&t.form&&t.form.clk!=t||\"select\"==i&&-1==t.selectedIndex))return null;if(\"select\"==i){var o=t.selectedIndex;if(0>o)return null;for(var s=[],u=t.options,c=\"select-one\"==n,l=c?o+1:u.length,f=c?o:0;l>f;f++){var m=u[f];if(m.selected){var d=m.value;if(d||(d=m.attributes&&m.attributes.value&&!m.attributes.value.specified?m.text:m.value),c)return d;s.push(d)}}return s}return e(t).val()},e.fn.clearForm=function(t){return this.each(function(){e(\"input,select,textarea\",this).clearFields(t)})},e.fn.clearFields=e.fn.clearInputs=function(t){var r=/^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i;return this.each(function(){var a=this.type,n=this.tagName.toLowerCase();r.test(a)||\"textarea\"==n?this.value=\"\":\"checkbox\"==a||\"radio\"==a?this.checked=!1:\"select\"==n?this.selectedIndex=-1:\"file\"==a?/MSIE/.test(navigator.userAgent)?e(this).replaceWith(e(this).clone(!0)):e(this).val(\"\"):t&&(t===!0&&/hidden/.test(a)||\"string\"==typeof t&&e(this).is(t))&&(this.value=\"\")})},e.fn.resetForm=function(){return this.each(function(){(\"function\"==typeof this.reset||\"object\"==typeof this.reset&&!this.reset.nodeType)&&this.reset()})},e.fn.enable=function(e){return void 0===e&&(e=!0),this.each(function(){this.disabled=!e})},e.fn.selected=function(t){return void 0===t&&(t=!0),this.each(function(){var r=this.type;if(\"checkbox\"==r||\"radio\"==r)this.checked=t;else if(\"option\"==this.tagName.toLowerCase()){var a=e(this).parent(\"select\");t&&a[0]&&\"select-one\"==a[0].type&&a.find(\"option\").selected(!1),this.selected=t}})},e.fn.ajaxSubmit.debug=!1});"
  },
  {
    "path": "static/ranking.js",
    "content": "/* global web, google */\nvar ranking_task_graphic_options_default = {\n    height: 240,\n    fontSize:12,\n    animation:{'duration':0},\n    title:'',\n    backgroundColor:'transparent',\n    chartArea:{left:50,top:20,width:350,height:180},\n    width: 400,\n    focusTarget:'category',\n    legend: {position:'bottom'},\n    vAxis:{\n\n        format:'none',\n        textStyle:{fontSize: 8},\n        //title:'%',\n        titleTextStyle:{fontSize: 12,fontStyle:'bold'},\n        textPosition:'out'\n    },\n    hAxis:{\n        textStyle:{fontSize: 8}\n    }\n};\n\nfunction delete_methods(){\n    \n    if(!confirm(\"Are you sure to delete all methods?\")){\n        return;\n    }\n    var url = \"/delete_all\";\n    $.post(url, function (data) {\n        document.location.reload();\n    });\n}\n\nfunction delete_method(id){\n    if(!confirm(\"Are you sure to delete the method?\")){\n        return;\n    }\n    var url = \"/delete_method\";\n    $.post(url,{\"id\":id} , function (data) {\n        document.location.reload();\n    });\n}\n\nfunction edit_method(id,el){\n    \n    var current_name = $(el).closest(\"tr\").find(\"span.title\").text();\n    var name = prompt(\"Enter the method's name\", current_name);\n    if (name != null) {\n        var url = \"/edit_method\";\n        $.post(url,{\"id\":id,\"name\":name} , function (data) {\n            document.location.reload();\n        });\n    }\n}\n\nfunction upload_subm(){\n    $(\"form\").submit();\n}\n\nfunction wait_screen(msg){\n    $(\"body\").append(\"<div class='overlay'>\" + (msg!=undefined? \"<div class='info'><span class='msg'>\" + msg + \"</span><img class='wait' src='/static/wait.gif'></div>\" : \"<img src='/static/wait.gif'>\") + \"</div>\");\n}\n\nfunction close_overlay(){\n    $(\"div.overlay\").remove();\n}\n\nfunction show_error(msg){\n    if(!$(\"div.overlay\").length){\n        $(\"body\").append(\"<div class='overlay'></div>\");\n    }\n    $(\"div.overlay\").html(\"<div class='error'><span class='msg'>\" + msg + \"</span><button onclick='close_overlay()'>OK</button></div>\");\n}\nfunction show_info(msg){\n    if(!$(\"div.overlay\").length){\n        $(\"body\").append(\"<div class='overlay'></div>\");\n    }\n    $(\"div.overlay\").html(\"<div class='info'><span class='msg'>\" + msg + \"</span><button onclick='close_overlay()'>OK</button></div>\");\n}\n\n$(document).ready( function(){\n\n    $(\"form\").submit(function() {\n       wait_screen(\"Please wait, your results are being uploaded,validated and evaluated..\");\n       var options = {\n       url : '/evaluate?json=1' ,\n       dataType: 'json',\n       success : function(result){\n           close_overlay();\n           if (result.calculated){\n               document.location.href = \"/method/?m=\" + result.id;\n           }else{\n               show_error(result.Message);\n           }\n       },\n       error : function(){\n           close_overlay();\n           show_error(\"Error on server\");\n       }\n       };\n       var $form = $(this);\n       setTimeout(function(){\n           $form.ajaxSubmit(options);\n       },10);\n       return false;\n    });\n\n    function carregar_api_grafics(callback){\n          google.load('visualization', '1',\n          {packages:['corechart'],callback:callback});\n    };\n\n    var grafic_1 = null;\n    var grafic_2 = null;\n    var loaded=false;\n\n    function init_graphic(){\n        carregar_api_grafics(function(){\n            grafic_1 = new google.visualization.ColumnChart(document.getElementById('div_ranking_1'));\n            if($(\"#div_ranking_2\").length){\n                grafic_2 = new google.visualization.ColumnChart(document.getElementById('div_ranking_2'));\n            }\n            loaded=true;\n            show_current_graphic();\n        });\n    }\n    \n    if ($(\"#div_ranking_1\").length){\n        show_current_graphic();\n    }\n    \n    function show_current_graphic(){\n        if(!loaded){\n            init_graphic();\n            return;\n        }\n        var Base64={_keyStr:\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\",encode:function(e){var t=\"\";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t=\"\";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,\"\");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/rn/g,\"n\");var t=\"\";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t=\"\";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3;}}return t}};\n        var id_data = \"graphic\";\n        var dadesArray = eval(Base64.decode($(\"#\" + id_data + \"\").val()));\n        var ordenacio = $(\"#\" + id_data + \"-sort\").val();\n        var format = $(\"#\" + id_data + \"-format\").val();\n        var type = $(\"#\" + id_data + \"-type\").val();\n        \n        var dades = google.visualization.arrayToDataTable(dadesArray);\n        var width = $(window).width() - $(\"table.results\").width()-40;\n        \n        if (width<$(window).width()/2){\n            width = $(window).width() - 20;\n            $(\"#div_rankings\").removeClass(\"ib\");\n        }else{\n            $(\"#div_rankings\").addClass(\"ib\");\n        }\n        if(grafic_2!=null){\n            width = width/2 -10;\n        }\n        var height = $(window).height() - 320;\n        \n        var options = jQuery.extend(true, {}, ranking_task_graphic_options_default);\n        options.animation.duration= 300;\n        options.width= width;\n        options.height= height;\n        options.chartArea.width =  width-40;\n        options.chartArea.height =  height-100;\n        options.vAxis.title =  ordenacio;\n        options.vAxis.format =  (format==\"perc\"? 'percent' : ( type!=\"string\"? 'decimal' : 'none' ) );\n        if(format==\"perc\"){\n            options.vAxis.minValue = 0;\n            options.vAxis.maxValue = 1;\n        }\n\n        grafic_1.draw(dades,options);\n        \n        if(grafic_2!=null){\n            var ordenacio = $(\"#\" + id_data + \"-gr2-sort\").val();\n            var format = $(\"#\" + id_data + \"-gr2-format\").val();\n            var type = $(\"#\" + id_data + \"-gr2-type\").val();\n            var options = jQuery.extend(true, {}, ranking_task_graphic_options_default);\n            options.animation.duration= 300;\n            options.width= width;\n            options.height= height;\n            options.chartArea.width =  width-40;\n            options.chartArea.height =  height-100;\n            options.vAxis.title =  ordenacio;\n            options.vAxis.format =  (format==\"perc\"? 'percent' : ( type!=\"string\"? 'decimal' : 'none' ) );\n            if(format==\"perc\"){\n                options.vAxis.minValue = 0;\n                options.vAxis.maxValue = 1;\n            }\n            var dadesArray = eval(Base64.decode($(\"#\" + id_data + \"-gr2\").val()));\n            var dades = google.visualization.arrayToDataTable(dadesArray);\n            options.vAxis.title = ordenacio;\n            grafic_2.draw(dades,options);\n        }        \n\n    }\n\n});\n\n\nfunction instructions(){\n    $(\"#div_instructions\").removeClass(\"hidden\");\n}\n\n$(document).ready(function(){\n    $(\"#div_instructions button.close\").click(function(){\n        $(\"#div_instructions\").addClass(\"hidden\");\n    });\n});"
  },
  {
    "path": "static/style.css",
    "content": "body{\n    font-family: 'Open Sans',helvetica,arial,sans-serif;\n    font-size: 1.0em;\n    margin: 10px;\n}\n\na{\n    color:#2694E8;\n    text-decoration: none;\n}\n\n.right{\n    float: right;\n}\na:hover{\n    color:#0580de;\n}\n\n#logo{\n    height: 30px;\n    margin-right: 20px;\n    margin-top: -8px;\n    margin-bottom: -8px;\n}\n\n.ib{\n    display: inline-block;\n    vertical-align: top;\n}\n.ml20{\n    margin-left: 20px;\n}\n.mr5{\n    margin-right: 5px;\n}\n\n.small{\n    font-size: 0.8em;\n}\n\nimg.wait{\n    display: block;\n    margin: 10px auto;\n}\n\ndiv.overlay{\n    position: fixed;\n    top: 0px;\n    left: 0px;\n    right: 0px;\n    bottom: 0px;\n    background-color: rgba(0,0,0,0.4);\n    display: flex;\n    justify-content:center;\n    align-items:center;\n}\ndiv.overlay div.info{\n    background-color: #ececec;\n    border: 2px solid #4c4c4c;\n    padding: 10px;\n    color: #2b2b2b;\n}\ndiv.overlay div.error{\n    background-color: #F44336;\n    border: 2px solid #C62828;\n    padding: 10px;\n    color: #fff;\n}\n\ndiv.overlay button{\n    display: block;\n    margin: 0px auto;\n    margin-top: 20px;\n}\n\n\nh1{\n    background-color: #4CAF50;\n    color:#fff;\n    margin: -10px;\n    padding: 8px;\n    font-size: 1.0em;\n    border-bottom: 1px solid #999;\n    margin-bottom: 0px;\n}\n    \ndiv.breadcrumbs {\n    display: block;\n    vertical-align: middle;\n    position: relative;\n    background-color: #eee;\n    border: 1px solid #999;\n    border-top: none;\n    padding: 8px;\n    margin: 0px -10px;\n    margin-bottom: 6px;\n    font-size: 0.9em;\n}\n\ntable.samples{\n    border-collapse: collapse;\n    \n}\n\ntable.samples th{\n    border: 1px solid #999;\n    background-color: #B3E5FC;\n}\n\ntable.samples td{\n    border: 1px solid #aaa;\n    text-align: right;\n    padding: 2px;\n}\n\nform{\n    padding: 10px;\n    margin: -6px -10px;\n    margin-bottom: 10px;\n    background-color: #616161;\n    color: #fff;\n}\n\n\n\ndiv.samples_list{\n    display: flex;\n    flex-wrap: wrap;\n    \n    background-color: #eee;\n    border: 1px solid #999;\n    padding: 10px;\n}\n\ndiv.samples_list div.sample{\n    flex-basis: 205px;\n    font-size: 0.8em;\n    text-align: center;\n    background-color: #fff;\n    border: 1px solid #dadada;\n    margin: 5px;\n}\n\ndiv.samples_list div.sample img{\n    display: block;\n    margin: 0px auto;\n}\ndiv.samples_list div.sample p{\n    margin: 0px;\n}\n\n\ntable.results{ font-size: 8pt; border-collapse: collapse; border: 1px solid #aaa;}\ntable.results thead th{ background-color:#ccc; line-height: 18px; text-align: left; padding: 3px 2px; }\ntable.results tbody td{ border-bottom: 1px solid #aaa; padding: 3px 6px;}\n\ntable.results a.methodname{\n    text-overflow: ellipsis;\n    overflow: hidden;\n    white-space: nowrap;\n    width:100px;\n    display: block;\n}\n\ndiv.summary{\n    background-color: #c7c7c7;\n    border: 1px solid #999;\n    padding: 8px;\n}\ndiv.summary h2{\n    margin: 0px;\n    display: inline-block;\n    margin-right: 10px;\n    font-size: 1.1em;\n}\n\ndiv.summary p{\n    margin: 0px 5px;\n    display: inline-block;\n}\n\ndiv.navigation {\n    display: block;\n    vertical-align: middle;\n    position: relative;\n    background-color: #d8d7d7;\n    border: 1px solid #999;\n    border-top:none;\n    border-bottom:none;\n    padding: 2px 20px;\n    margin: 0px;\n}\n\n\n\n.pure-button {\n    /* Structure */\n    display: inline-block;\n    zoom: 1;\n    line-height: normal;\n    white-space: nowrap;\n    vertical-align: middle;\n    text-align: center;\n    cursor: pointer;\n    -webkit-user-drag: none;\n    user-select: none;\n    box-sizing: border-box;\n}\n\n/* Firefox: Get rid of the inner focus border */\n.pure-button::-moz-focus-inner {\n    padding: 0;\n    border: 0;\n}\n\n.pure-button {\n    font-family: inherit;\n    font-size: 0.8rem;\n    padding: 0.5em 1em;\n    color: #444; /* rgba not supported (IE 8) */\n    color: rgba(0, 0, 0, 0.80); /* rgba supported */\n    border: 1px solid #999;  /*IE 6/7/8*/\n    border: none rgba(0, 0, 0, 0);  /*IE9 + everything else*/\n    background-color: #E6E6E6;\n    text-decoration: none;\n    border-radius: 2px;\n}\n\n.pure-button-hover,\n.pure-button:hover,\n.pure-button:focus {\n    /* csslint ignore:start */\n    filter: alpha(opacity=90);\n    /* csslint ignore:end */\n    background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));\n    text-decoration: none;\n    \n}\n.pure-button:focus {\n    outline: 0;\n}\n.pure-button-active,\n.pure-button:active {\n    box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset;\n    border-color: #000\\9;\n}\n\n.pure-button[disabled],\n.pure-button-disabled,\n.pure-button-disabled:hover,\n.pure-button-disabled:focus,\n.pure-button-disabled:active {\n    border: none;\n    background-image: none;\n    /* csslint ignore:start */\n    filter: alpha(opacity=40);\n    /* csslint ignore:end */\n    opacity: 0.40;\n    cursor: not-allowed;\n    box-shadow: none;\n    pointer-events: none;\n}\n\n.pure-button-hidden {\n    display: none;\n}\n\n.pure-button-primary,\n.pure-button-selected,\na.pure-button-primary,\na.pure-button-selected {\n    background-color: rgb(0, 120, 231);\n    color: #fff;\n}\n\n/* Button Groups */\n.pure-button-group .pure-button {\n    margin: 0;\n    border-radius: 0;\n    border-right: 1px solid #111;  /* fallback color for rgba() for IE7/8 */\n    border-right: 1px solid rgba(0, 0, 0, 0.2);\n\n}\n\n.pure-button-group .pure-button:first-child {\n    border-top-left-radius: 2px;\n    border-bottom-left-radius: 2px;\n}\n.pure-button-group .pure-button:last-child {\n    border-top-right-radius: 2px;\n    border-bottom-right-radius: 2px;\n    border-right: none;\n}\n\n.button-success,\n.button-error,\n.button-warning,\n.button-secondary {\n    color: white !important;\n    border-radius: 4px;\n    text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);\n}\n.button-success {\n    background: rgb(28, 184, 65); /* this is a green */\n}\n\n.button-error {\n    background: rgb(202, 60, 60); /* this is a maroon */\n}\n\n.button-warning {\n    background: rgb(223, 117, 20); /* this is an orange */\n}\n\n.button-secondary {\n    background: rgb(66, 184, 221); /* this is a light blue */\n}\n\ntable.results .pure-button{\n    font-size: 8pt;\n}\n\n\n\np.info{\n    border: 1px solid #dad55e;\n    padding: 0.8em;\n    background: #fffa90;\n    color: #777620;\n    border-bottom-right-radius: 3px;\n    border-bottom-left-radius: 3px;\n    border-top-right-radius: 3px;\n    border-top-left-radius: 3px;\n}\n\nfieldset{\n    border: 1px solid #ddd;\n    display: inline-block;\n}\nfieldset legend{\n    border: 1px solid #ccc;\n    color:#000;\n    padding:4px 8px;\n    background-color: #ddd;\n\n}\n\n#div_instructions{\n    position: fixed;\n    top: 0px;\n    left: 0px;\n    right: 0px;\n    bottom: 0px;\n    background-color: rgba(0,0,0,0.4);\n    display: flex;\n    justify-content:center;\n    align-items:center;\n}\n\n#div_instructions div.wrap{\n    background-color: #ececec;\n    border: 2px solid #4c4c4c;\n    padding: 10px;\n    color: #2b2b2b;\n    max-width: 600px;\n}\n\n#div_instructions h1{\n    background-color:#9E9E9E;\n}\n#div_instructions button.close{\n    float: right;\n    margin-top: -6px;\n}\n\n.hidden{\n    display: none !important;\n}"
  },
  {
    "path": "static/visualization.css",
    "content": "#div_sample{\n    position: relative;\n}\ndiv.filter {\n    display: block;\n    vertical-align: middle;\n    position: relative;\n    background-color: #eee;\n    border: 1px solid #999;\n    padding: 2px 20px;\n    margin-bottom: 6px;\n}\ndiv.im_filters{\n    position: absolute;\n    left:160px;\n    top:5px;\n    z-index: 9;\n}\ndiv.im_filters.det{\n    left:50%;\n    margin-left:160px;\n}\n\n\ndiv.filter #span_evaluation_nav{\n    margin-left: 20px;\n}\ndiv.filter #span_evaluation_nav a{\n    margin: 0px 4px;\n}\n\ndiv.method_info{\n    background-color: #f2f2f2;\n    margin: 20px 0px;\n    padding: 0px;\n    box-shadow: 2px 2px 4px rgba(0,0,0,0.4);\n    border: 1px solid #999;\n}\ndiv.method_info.small{\n    box-shadow: none;\n}\n\n\nh2.method{\n    background-color: #d8d8d8;\n    \n    padding-top: 8px;\n    padding-bottom: 8px;\n    border-bottom: 1px solid #999;\n    padding-left: 10px;\n    \n    margin: 0px;\n}\ndiv.method_info span.method{\n    color:#666;font-size:12pt;\n}\n\nh2.method.competition{\n    background-color: #e1f5fe;\n}\n\ndiv.method_info.small h2.method.competition img{\n    width: 16px; height: 16px;\n}\n\n#div_summary{\n    padding: 10px;\n}\n\ndiv.top{\n    display: flex;\n    justify-content:space-between;\n}\n\ndiv.top.methods_list{\n    flex-wrap: wrap;\n}\ndiv.top.methods_list div.method_info{\n    flex-basis: 32%;\n    margin: 0.5%;\n}\n\ndiv.method_info.small{\n    flex-basis: 33%;\n    width:33%;\n    box-sizing: border-box;\n    display: inline-block;\n    vertical-align: top;\n}\n\ndiv.method_info.small #div_summary{\n    box-shadow: none;\n    font-size: 8pt;\n}\ndiv.method_info.small h2.method{\n    box-shadow:none;\n    font-size: 10pt;\n    \n    \n}\ndiv.method_info.small h2.method a{\n    color: #2694E8;\n    text-decoration: none;\n}\ndiv.method_info.small h2.method a:hover{ color:#F6A828}\n\ndiv.method_info.small span.method{\n    color:#666;font-size:8pt;\n}\n\n\n#div_comparation{\n    /*padding: 10px;\n    margin: 9px;\n    border: 1px solid #999;*/\n    max-height: 200px;\n    overflow: auto;\n    margin-bottom: 6px;\n}\n#div_comparation h3{\n    background-color: #eee;\n    margin: -10px;\n    padding: 6px 10px;\n    margin-bottom: 20px;\n}\n\ntable.sample_methods{\n    border-collapse: collapse;\n}\ntable.sample_methods td,table.sample_methods th{\n    border: 1px solid #aaa;\n    padding: 2px;\n    text-align: right;\n}\ntable.sample_methods td.method,table.sample_methods th.method{\n    text-align: left;\n}\ntable.sample_methods th{\n    background-color: #c7c7c7;\n}\ntable.sample_methods tr.current td{\n    background-color: #FFC107;\n    font-weight: bold;\n}\ntable.sample_methods td a{\n    text-decoration: none;\n    color: #2694E8;\n}\n\n.div_log{\n    font-size: 8pt;\n}\n\ndiv.group{\n    border:1px solid #c5c5c5;\n    padding: 10px;\n    background-color: #fff;\n    border-radius: 5px;\n}\ndiv.group div.header{\n    background-color: #dedede;\n    padding: 10px;\n    margin: -7px;\n    margin-bottom: 10px;\n    border:1px solid #c5c5c5;\n    border-radius: 5px;\n    border-bottom-left-radius: 0px;\n    border-bottom-right-radius: 0px;\n     line-height: 20px;\n}"
  },
  {
    "path": "static/visualization_default.js",
    "content": "/* global web */\nvar ClassVisualization = function(){\n    this.sampleData = null;\n    \n    //Parameters for GT/Det Canvas visualization\n    this.canvas_gt = null;\n    this.canvas_det = null;\n    this.ctx_gt = null;\n    this.ctx_det = null;\n    this.scale=2;\n    this.offset_x=0;\n    this.offset_y=0;\n    this.im_w=0;\n    this.im_h=0;\n    this.curr_im_w=0;\n    this.curr_im_h=0;\n    this.det_rect=-1;\n    this.gt_rect=-1;\n    this.draws=0;\n    \n    //Mouse events on canvas\n    this.mm_point=null;\n    this.initial_point=null;\n    this.last_point=null;\n    this.mouse_clicked=false;\n    this.initial_offset=null;\n    \n    this.image_loaded = false;\n    this.image_details_loaded=false;\n    this.sampleData = null;    \n    \n    var self = this;\n    \n    this.load_sample_info = function(){\n        //web.pantalla_espera();\n        \n        var urlInfo = \"/sampleInfo/?m=\" + getUrlParameter(\"m\");\n        var extraParmasNames = [\"file\",\"eval\",\"sample\",\"gtv\"];\n        for(var i=0;i<extraParmasNames.length;i++){\n            var parameterValue = getUrlParameter( extraParmasNames[i] );\n            if (parameterValue!= undefined){\n                urlInfo += \"&\" + extraParmasNames[i] + \"=\" + parameterValue;\n            }\n        }\n        $.get(urlInfo, function (data) {\n            visualization.sampleData = data;\n            visualization.load_visualization();\n        }, \"json\");\n    };\n    \n    this.init_image_details = function(){\n        this.canvas_gt = document.createElement(\"canvas\");\n        var dest = document.getElementById(\"div_canvas_gt\");\n        dest.appendChild(this.canvas_gt);\n\n        this.canvas_gt.setAttribute(\"id\",\"canvas_gt\");\n        this.canvas_det = document.createElement(\"canvas\");\n        var dest2 = document.getElementById(\"div_canvas_det\");\n        dest2.appendChild(this.canvas_det);\n\n        this.canvas_det.setAttribute(\"id\",\"canvas_det\");\n\n        this.canvas_gt.width=$(\"#div_canvas_gt\").width();\n        this.canvas_gt.height=$(\"#div_canvas_gt\").height();\n        this.canvas_det.width=$(\"#div_canvas_gt\").width();\n        this.canvas_det.height=$(\"#div_canvas_gt\").height();\n\n        var self = this;\n        \n        $(\"#canvas_gt\").mousedown(function(e) {self.mousedown(e);});\n        $(\"#canvas_gt\").mousemove(function(e) {self.mousemove(e);});\n        $(\"#canvas_gt\").mouseup(function(e) {self.mouseup(e);});\n        $(\"#canvas_gt\").mouseleave(function(e) {self.mouseleave(e);});\n        $(\"#canvas_gt\").mousewheel(function(e, d) {self.mousewheel(e,d);});\n        $(\"#canvas_det\").mousedown(function(e) {self.mousedown(e);});\n        $(\"#canvas_det\").mousemove(function(e) {self.mousemove(e);});\n        $(\"#canvas_det\").mouseup(function(e) {self.mouseup(e);});\n        $(\"#canvas_det\").mouseleave(function(e) {self.mouseleave(e);});\n        $(\"#canvas_det\").mousewheel(function(e, d) {self.mousewheel(e,d);});\n\n        this.ctx_gt = canvas_gt.getContext(\"2d\");\n        this.ctx_det = canvas_det.getContext(\"2d\");\n        this.ctx_gt.mozImageSmoothingEnabled = false;\n        this.ctx_gt.webkitImageSmoothingEnabled = false;\n        this.ctx_det.mozImageSmoothingEnabled = false;\n        this.ctx_det.webkitImageSmoothingEnabled = false;\n        this.scale = Math.min($(\"#canvas_gt\").width()/this.im_w,$(\"#canvas_gt\").height()/this.im_h );\n        setTimeout(function(){self.adapt_controls();},500);\n\n    };\n    this.adapt_controls = function(){\n        if(!this.image_details_loaded){\n            return;\n        }\n        var height = Math.max(220,$(window).height()-536);\n        $(\"#div_container_gt\").css(\"height\",height + \"px\");\n        $(\"#div_container_det\").css(\"height\",height + \"px\");\n\n        this.canvas_gt.width=$(\"#div_canvas_gt\").width();\n        this.canvas_gt.height=$(\"#div_canvas_gt\").height();\n        this.canvas_det.width=$(\"#div_canvas_gt\").width();\n        this.canvas_det.height=$(\"#div_canvas_gt\").height();\n        this.ctx_gt.mozImageSmoothingEnabled = false;\n        this.ctx_gt.webkitImageSmoothingEnabled = false;\n        this.ctx_det.mozImageSmoothingEnabled = false;\n        this.ctx_det.webkitImageSmoothingEnabled = false;\n        $(\"#div_container_method\").css({\"height\": ($(window).height()-80)+ \"px\"});\n\n        this.table_sizes();      \n        this.zoom_changed();\n        this.correct_image_offset();\n        this.draw();\n    };\n    \n    this.mousemove = function(e){\n        var layer = this.getOffset(e);\n        this.mm_point = Array(layer.x,layer.y);\n    };\n\n    this.mousedown = function(e){\n        var layer = this.getOffset(e);\n        var mouseX = layer.x;\n        var mouseY = layer.y;\n        this.initial_point = Array(mouseX,mouseY);\n        this.initial_offset = Array(this.offset_x,this.offset_y);\n        this.mm_point = Array(mouseX,mouseY);\n        this.last_point = Array(mouseX,mouseY);\n        this.mouse_clicked = true;\n        this.refresh_canvas_position_on_mousemove();\n    };\n    this.mouseup = function(e){    \n        this.mouse_clicked = false;\n    };\n    this.mouseleave = function(e){\n        this.mouse_clicked = false;\n    };\n    this.mousewheel = function(e,d){        \n         var new_scale = this.scale + ((d>0)? this.scale*0.1 : -this.scale*0.1);\n\n        var point = this.mm_point;\n        var real_point = this.zoom_to_original(point);\n\n        var dx = point[0] - this.original_to_zoom_val(real_point[0]);\n        var dy = point[1] - this.original_to_zoom_val_y(real_point[1]);\n        this.offset_x= point[0] - real_point[0]*new_scale - dx;// - this.scale;\n        this.offset_y= point[1] - real_point[1]*new_scale - dy;// - this.scale;\n\n        this.scale = new_scale;\n\n        this.zoom_changed();\n        this.correct_image_offset();\n        this.draw();\n        e.preventDefault();\n        return false;\n    };\n\n    this.original_to_zoom = function(punt){\n        return Array(this.original_to_zoom_val(punt[0]),this.original_to_zoom_val_y(punt[1]));\n    };\n    this.original_to_zoom_val = function(x){\n        return Math.floor(x*this.scale + this.offset_x);\n    };\n    this.original_to_zoom_val_y = function(y){\n        return Math.floor(y*this.scale+this.offset_y);\n    };\n    this.zoom_to_original = function(punt){\n        return Array(this.zoom_to_original_val(punt[0]),this.zoom_to_original_val_y(punt[1]));\n    };\n    this.zoom_to_original_val = function(x){\n        return Math.floor((x-this.offset_x)/this.scale);\n    };\n    this.zoom_to_original_val_y = function(y){\n        //return Math.floor(y/this.scale-this.offsetY);\n        return Math.floor((y-this.offset_y)/this.scale);\n    };\n    this.zoom_changed = function(){\n        this.curr_im_w = this.im_w * this.scale;\n        this.curr_im_h = this.im_h * this.scale;\n\n    };\n    this.correct_image_offset = function(){    \n        //Ensure that image position is correct and center image\n        if ( this.curr_im_w < this.canvas_gt.width){\n            this.offset_x = (this.canvas_gt.width - this.curr_im_w)/2;\n        }else{\n            if (this.offset_x>0) this.offset_x= 0;\n            if (this.offset_x < (this.canvas_gt.width - this.curr_im_w)) this.offset_x = this.canvas_gt.width - this.curr_im_w ;\n        }\n        if (this.curr_im_h < this.canvas_gt.height){\n            this.offset_y = ( this.canvas_gt.height - this.curr_im_h)/2;\n        }else{\n            if (this.offset_y>0) this.offset_y = 0;\n            if (this.offset_y< (this.canvas_gt.height-this.curr_im_h)) this.offset_y = this.canvas_gt.height - this.curr_im_h ;\n        }\n    };\n    \n    this.refresh_canvas_position_on_mousemove = function(){    \n       var dx = self.mm_point[0]-self.initial_point[0];\n       var dy = self.mm_point[1]-self.initial_point[1];\n\n       var ox = self.initial_offset[0] + dx;\n       var oy = self.initial_offset[1] + dy;\n\n       var co = self.correct_offset(ox,oy);\n\n       if (self.mouse_clicked){\n\n           if (co[0]!=self.offset_x ||co[1]!=self.offset_y) {\n\n               self.offset_x = self.initial_offset[0] + dx;\n               self.offset_y = self.initial_offset[1] + dy;\n               self.correct_image_offset();\n               self.draw();\n           }\n           setTimeout(self.refresh_canvas_position_on_mousemove,100);\n       }\n    };\n    this.correct_offset = function(ox,oy){    \n        //Ensure that image position is correct and center image\n        if ( this.curr_im_w < this.canvas_gt.width){\n            ox = (this.canvas_gt.width - this.curr_im_w)/2;\n        }else{\n            if (ox>0) ox = 0;\n            if (ox< (this.canvas_gt.width - this.curr_im_w)) ox = this.canvas_gt.width - this.curr_im_w ;\n        }\n        if (this.curr_im_h < this.canvas_gt.height){\n            oy = (this.canvas_gt.height - this.curr_im_h)/2;\n        }else{\n            if (oy>0) oy = 0;\n            if (oy< (this.canvas_gt.height - this.curr_im_h)) oy = this.canvas_gt.height - this.curr_im_h ;\n        }\n        return Array(ox,oy);\n    };\n    this.getOffset = function(evt){      \n      var el = evt.target,\n          x = y = 0;\n\n        while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {\n          x += el.offsetLeft - el.scrollLeft;\n          y += el.offsetTop - el.scrollTop;\n          el = el.offsetParent;\n       }\n        x = evt.clientX - x;\n        y = evt.clientY - y;\n\n\n      return {x: x, y: y};\n    };\n    \n   \n    this.table_sizes = function(){\n        $(\".div_table\").scroll(function(e){\n            var pos_y = $(this).scrollTop();\n            var pos_x = $(this).scrollLeft();\n            $(\".div_table\").not(this).scrollTop(pos_y).scrollLeft(pos_x);\n        });  \n    };\n    \n    this.writeText = function(ctx,bb,text){\n        \n        var TL,TR,BL,BR;\n        \n        if (bb.length == 8){\n            //bb has 8 points, we want to find TL,TR,BL,BR\n            //1st. sort points by Y\n            var p1 = {\"x\":bb[0],\"y\":bb[1]};\n            var p2 = {\"x\":bb[2],\"y\":bb[3]};\n            var p3 = {\"x\":bb[4],\"y\":bb[5]};\n            var p4 = {\"x\":bb[6],\"y\":bb[7]};\n\n            var pointsList = [p1,p2,p3,p4];\n            pointsList = pointsList.sort(function sortPointsByY(a,b){\n                if (a.y<b.y){\n                    return 1;\n                }else if (a.y==b.y){\n                    return 0;\n                }else{\n                    return -1;\n                }\n            });\n\n            if (pointsList[0].x < pointsList[1].x){\n                 TL = pointsList[0];\n                 TR = pointsList[1];\n             }else{\n                 TL = pointsList[1];\n                 TR = pointsList[0];\n             }\n            if (pointsList[2].x < pointsList[3].x){\n                 BL = pointsList[2];\n                 BR = pointsList[3];\n             }else{\n                 BL = pointsList[3];\n                 BR = pointsList[2];\n             }\n         }else{\n             TL = {\"x\" : bb[0] , \"y\":bb[3]};\n             TR = {\"x\" : bb[2] , \"y\":bb[3]};\n             BL = {\"x\" : bb[0] , \"y\":bb[1]};\n             BR = {\"x\" : bb[2] , \"y\":bb[1]};\n         }\n        var height = Math.round(this.original_to_zoom_val_y(parseInt( Math.min(TL.y,TR.y) )+1) - this.original_to_zoom_val_y(parseInt(Math.max(BL.y,BR.y)))) - 3;\n        var width = Math.round(this.original_to_zoom_val(parseInt( Math.min(TR.x,BR.x) )+1) - this.original_to_zoom_val(parseInt(Math.max(TL.x,BL.x)))) - 3;\n\n        var fontSize = height;\n        if(fontSize<6){\n            fontSize=6;\n        }\n\n        ctx.fillStyle = \"rgba(255,255,255,1)\";\n        ctx.font= fontSize + \"px Verdana\";\n\n        var metrics = ctx.measureText(text);\n        var textWidth = metrics.width;\n\n        while(textWidth>width && fontSize>6){\n            fontSize--;\n             ctx.font= fontSize + \"px Verdana\";\n            metrics = ctx.measureText(text);\n            textWidth = metrics.width;\n        }\n        ctx.fillText(text,this.original_to_zoom_val(parseInt(BL.x)) + 3 , this.original_to_zoom_val_y(parseInt(BL.y)) + fontSize);\n  \n    };\n    \n};\n\nClassVisualization.prototype.load_visualization = function(){\n    var urlGtImg = \"/image/?sample=\" + getUrlParameter(\"sample\");\n    var extraParmasNames = [\"ch\",\"task\",\"gtv\"];\n    for(var i=0;i<extraParmasNames.length;i++){\n        var parameterValue = getUrlParameter( extraParmasNames[i] );\n        if (parameterValue!= undefined){\n            urlGtImg += \"&\" + extraParmasNames[i] + \"=\" + parameterValue;\n        }\n    }    \n    $(\"#div_sample\").append(\"<img src='\" + urlGtImg + \"'>\");\n    for (var key in visualization.sampleData){\n        $(\"#div_sample\").append(\"<br>\" + key + \" = \" + visualization.sampleData[key]);\n    }\n    //web.tancar_pantalla_espera();\n    \n};\n\nClassVisualization.prototype.draw = function(){\n    \n};\n\nvar visualization = new ClassVisualization();\n\n\n$(document).ready(function () {\n    visualization.load_sample_info();\n});\n\n$(window).resize(function(){\n    visualization.adapt_controls();\n});"
  },
  {
    "path": "static_custom/contents.txt",
    "content": "This folder are designed to store CSS and JS files for a custom visualization."
  },
  {
    "path": "static_custom/visualization_TL_iou.css",
    "content": "#div_canvas_gt{\n    position: absolute;\n    left: 0px;\n    right: 0px;\n    top: 30px;\n    bottom: 0px;    \n}\n#div_canvas_det{\n    position: absolute;\n    left: 0px;\n    right: 0px;\n    top: 30px;\n    bottom: 0px; \n}\n\ndiv.container_canvas{\n    width: 50%;\n    vertical-align: top;\n    position: relative;\n    height: 400px;\n    display: inline-block;  \n    border: 1px solid #999;\n    box-sizing: border-box;\n}\ndiv.container_canvas h3, #div_sample_info h3{\n    margin: 0px;\n    padding: 0px 10px;\n    background-color: #eee;\n    border-bottom: 1px solid #999;\n    line-height: 29px;\n}\n\n\n#img_gt_image2{\n    display: none;\n}\n\n#div_sample_info{\n    display: flex;\n}\n\n#div_matrices{\n    flex-grow: 2;\n}\n\n#div_recall{\n    flex-grow: 2;\n}\n\n#div_precision{\n    flex-grow: 2;\n}\n\n#div_char{\n    flex-grow: 2;\n}\n\n.div_table{\n    display: block;\n}\n.div_table{\n    display: block;\n    box-sizing: border-box;\n    height: 300px;\n    width: 400px;\n    overflow: auto;\n}\n\n.div_log{\n    height: 300px;\n    font-size: 15px;\n    overflow: auto;\n}\n\n.div_table table{\n    border-collapse: collapse;\n    border-style: hidden;\n    font-size: 12px;\n}\n.div_table td{\n    text-align: center;\n    width:24px;\n}\n.div_table td, th{\n    border: 1px solid black;\n}\n\n.div_table td.row_selected{\n    background-color: #ccc;\n}\n.div_table td.col_selected{\n    background-color: #ccc;\n}\n\n.div_table td.selected{\n    background-color: #888;\n}\n\n.div_table td.OO{\n    background-color: rgb(0,190,0);\n}\n.div_table td.NO{\n    background-color: rgb(38,148,232);\n    color: #c8e6c9;\n}\n.div_table td.OO.green{\n    color: #006E24;\n}"
  },
  {
    "path": "static_custom/visualization_TL_iou.js",
    "content": "/* global web, visualization, ClassVisualization */\n\nClassVisualization.prototype.load_visualization = function(){\n\n    var self = this;\n\n    var sampleData = this.sampleData;\n\n    var urlImg = \"/image/?ch=\" + getUrlParameter(\"ch\") + \"&task=\" + getUrlParameter(\"task\") + \"&sample=\" + getUrlParameter(\"sample\") +  \"&gtv=\" + getUrlParameter(\"gtv\");\n\n    var template = \"<div class='im_filters'><input type='checkbox' checked='checked' id='chk_image'><label for='chk_image'>Show Image</label></div>\"+\n                    \"<div class='container_canvas'>\" +\n                    \"<h3>Ground Truth</h3>\" +\n                    \"<div id='div_canvas_gt'></div>\" +\n                   \"</div>\"+\n                   \"<div class='container_canvas'>\" +\n                    \"<h3>Detection</h3>\" +\n                    \"<div id='div_canvas_det'></div>\" +\n                   \"</div>\"+\n                   \"<img id='img_gt_image2'>\"+\n                   \"<div id='div_sample_info'>\"+\n                   \"<div id='div_recall'><div class='div_table'><h3>Recall</h3>loading..</div></div>\"+\n                   \"<div id='div_precision'><div class='div_table'><h3>Precision</h3>loading..</div></div>\"+\n                   \"<div id='div_char'><div class='div_table'><h3>Character score</h3>loading..</div></div></div>\"+\n                   \"<div id='div_logs'><h3>Log</h3><span class='red'>loading..</span></div>\";\n    \n    $(\"#div_sample\").html(template);\n\n    if(!this.image_details_loaded){\n        this.image_details_loaded=true;\n        this.init_image_details();\n    }   \n    this.image_loaded = false;\n    this.draw();\n    \n    $(\"#chk_image\").change(function(){\n        self.draw();\n    });\n    \n    $(\"#img_gt_image2\").attr(\"src\",urlImg).one(\"load\",function(){\n        self.image_loaded = true;\n        self.im_w = this.width;\n        self.im_h = this.height;\n        self.scale = Math.min($(\"#div_canvas_gt\").width()/self.im_w,$(\"#div_canvas_det\").height()/self.im_h );\n        self.zoom_changed();\n        self.correct_image_offset();\n        self.draw();\n    });\n\n    var numGt = sampleData.gtPolPoints==undefined? 0 : sampleData.gtPolPoints.length;\n    var numDet = sampleData.detPolPoints==undefined? 0 : sampleData.detPolPoints.length;\n\n    var html_recall = \"\";\n    var html_precision = \"\";\n\n        var stylesMat = new Array();\n        for ( var j=0;j<numGt;j++){\n            stylesMat[j] = new Array();\n            for ( var i=0;i<numDet;i++){\n                stylesMat[j][i] = \"value\";\n            }\n        }\n        \n        sampleData.gtTypes = new Array();\n        sampleData.detTypes = new Array();\n        for ( var j=0;j<numGt;j++){\n            var gtDontCare = $.inArray(j,sampleData.gtDontCare)>-1;\n            sampleData.gtTypes.push( gtDontCare? 'DC' : 'NM' );\n        }\n        for ( var j=0;j<numDet;j++){\n            var detDontCare = $.inArray(j,sampleData.detDontCare)>-1;\n            sampleData.detTypes.push( detDontCare? 'DC' : 'NM' );\n        }\n\n\n        if (sampleData.pairs!=undefined){\n            for ( var k=0;k<sampleData.pairs.length;k++){\n                var pair = sampleData.pairs[k];\n                \n                var gts = new Array();\n                var dets = new Array();\n                \n                if(pair.gt.length==undefined){\n                    gts.push(pair.gt);\n                }else{\n                    gts = pair.gt;\n                }\n                if(pair.det.length==undefined){\n                    dets.push(pair.det);\n                }else{\n                    dets = pair.det;\n                }\n                for(var i=0;i<gts.length;i++){\n                    for(var j=0;j<dets.length;j++){\n                        stylesMat[gts[i]][dets[j]] += \" \" + \"OO\";\n                        sampleData.gtTypes[gts[i]] = \"OO\";\n                        sampleData.detTypes[dets[j]] = \"OO\";\n                    }\n                }\n            }\n        }\n    if(numDet>100){\n        html_recall = \"<p class='red'>The algorithm has detected more than 100 bounding boxes, the visualization are not posible</p></p>\";\n    }else{        \n        var html_recall = \"<table><thead><tr><th>GT / Det</th>\";\n        for ( var i=0;i<numDet;i++){\n            var detDontCare = $.inArray(i,sampleData.detDontCare)>-1;\n            html_recall += \"<th style='\" + (detDontCare? \"\" : \"font-weight:bold;\") + \"'>#\" + i + \"</th>\";\n        }\n        html_recall += \"</tr></thead><tbody id='tbody_recall'>\";\n\n        for ( var j=0;j<numGt;j++){\n            var gtDontCare = $.inArray(j,sampleData.gtDontCare)>-1;\n            html_recall += \"<tr>\";\n            html_recall += \"<td style='\" + (gtDontCare? \"\" : \"font-weight:bold;\") + \"'>#\" + j + \"</td>\";\n            for ( var i=0;i<numDet;i++){\n\n                var recallClass = (sampleData.recallMat[j][i]>=sampleData.evaluationParams.AREA_RECALL_CONSTRAINT ? ' green' : ' red' );\n                html_recall += \"<td data-col='\" + i + \"' data-row='\" + j + \"' class='\" + stylesMat[j][i] + \" \" + recallClass + \"'>\" + Math.round(sampleData.recallMat[j][i]*10000)/100 + \"</td>\";    \n            }\n            html_recall += \"</tr>\";\n        }\n        html_recall += \"</tbody></table>\";\n    }\n    $(\"#div_recall\").html(\"<div class='div_table'><h3>Recall</h3>\" + html_recall + \"</div>\");\n\n    if(numDet>100){\n        html_precision = \"<p class='red'>The algorithm has detected more than 100 bounding boxes, the visualization are not posible</p></p>\";\n    }else{        \n        var html_precision = \"<table><thead><tr><th>GT / Det</th>\";\n        for ( var i=0;i<numDet;i++){\n            var detDontCare = $.inArray(i,sampleData.detDontCare)>-1;\n            html_precision += \"<th style='\" + (detDontCare? \"\" : \"font-weight:bold;\") + \"'>#\" + i + \"</th>\";\n        }\n        html_precision += \"</tr></thead><tbody id='tbody_precision'>\";\n\n        for ( var j=0;j<numGt;j++){\n            var gtDontCare = $.inArray(j,sampleData.gtDontCare)>-1;\n            html_precision += \"<tr>\";\n            html_precision += \"<td style='\" + (gtDontCare? \"\" : \"font-weight:bold;\") + \"'>#\" + j + \"</td>\";\n            for ( var i=0;i<numDet;i++){\n\n                var precisionClass = (sampleData.precisionMat[j][i]>=sampleData.evaluationParams.AREA_PRECISION_CONSTRAINT ? ' green' : ' red' );\n                html_precision += \"<td data-col='\" + i + \"' data-row='\" + j + \"' class='\" + stylesMat[j][i] + \" \" + precisionClass + \"'>\" + Math.round(sampleData.precisionMat[j][i]*10000)/100 + \"</td>\";    \n            }\n            html_precision += \"</tr>\";\n        }\n        html_precision += \"</tbody></table>\";\n    }\n    $(\"#div_precision\").html(\"<div class='div_table'><h3>Precision</h3>\" + html_precision + \"</div>\");\n\n    if(numDet>100){\n        html_char = \"<p class='red'>The algorithm has detected more than 100 bounding boxes, the visualization are not posible</p></p>\";\n    }else{        \n        var html_char = \"<table><thead><tr><th>GT / Det</th>\";\n        for ( var i=0;i<numDet;i++){\n            var detDontCare = $.inArray(i,sampleData.detDontCare)>-1;\n            html_char += \"<th style='\" + (detDontCare? \"\" : \"font-weight:bold;\") + \"'>#\" + i + \"</th>\";\n        }\n        html_char += \"<th style='\" + \"font-weight:bold;\" + \"'>\" + \"Rec Score\" + \"</th>\";\n        html_char += \"</tr></thead><tbody id='tbody_char'>\";\n\n        for ( var j=0;j<numGt;j++){\n            var gtDontCare = $.inArray(j,sampleData.gtDontCare)>-1;\n            html_char += \"<tr>\";\n            html_char += \"<td style='\" + (gtDontCare? \"\" : \"font-weight:bold;\") + \"'>#\" + j + \"</td>\";\n            for ( var i=0;i<numDet;i++){\n                var charClass = 'green';\n                html_char += \"<td data-col='\" + i + \"' data-row='\" + j + \"' class='\" + stylesMat[j][i] + \" \" + charClass + \"'>\" + sampleData.charCounts[j][i] + \"</td>\";\n            }\n            html_char += \"<td data-col='\" + (i+1) + \"' data-row='\" + j + \"' class='\" + \"value\" + \" \" + charClass + \"'>\" + sampleData.recallScore[j] + \"</td>\";\n            html_char += \"</tr>\";\n        }\n\n        html_char += \"<tr>\";\n        html_char += \"<tr><td style='\" + \"font-weight:bold\" + \"'>\" + \"Prec Score\" + \"</td>\";\n        for ( var i=0;i<numDet;i++){\n            var charClass = 'green';\n            html_char += \"<td data-col='\" + i + \"' data-row='\" + (j+1) + \"' class='\" + \"value\" + \" \" + charClass + \"'>\" + sampleData.precisionScore[i] + \"</td>\";\n        }\n        html_char += \"<td data-col='\" + (i+1) + \"' data-row='\" + (j+1) + \"' class='\" + \"value\" + \" \" + charClass + \"'>\" + \"</td>\";\n        html_char += \"</tr>\";\n\n        html_char += \"</tbody></table>\";\n\n    }\n    $(\"#div_char\").html(\"<div class='div_table'><h3>Character score</h3>\" + html_char + \"</div>\");\n\n\n    var evalLog = sampleData.evaluationLog;\n    if (evalLog==undefined){\n        evalLog = \"\";\n    }else{\n        evalLog = evalLog.replace(/\\n/g, \"<br/>\")\n    }\n\n    $(\"#div_logs\").html(\"<div class='div_log'><h3>Log</h3>\" + evalLog + \"</div>\");\n\n    this.table_sizes();\n\n    $(\"#div_matrices tbody td\").mouseover(function(){\n        self.det_rect = -1;\n        self.gt_rect = -1;\n        if ( $(this).attr(\"data-col\")!=undefined && $(this).attr(\"data-row\")!=undefined){\n            self.det_rect = $(this).attr(\"data-col\");\n            self.gt_rect = $(this).attr(\"data-row\");\n            $(\"#div_matrices tbody td\").removeClass(\"selected\");\n            $(\"#div_matrices tbody td\").removeClass(\"col_selected\").removeClass(\"row_selected\");\n            $(this).addClass(\"selected\");\n            $(\"td[data-col=\" + $(this).attr(\"data-col\") + \"]\").addClass(\"col_selected\");\n            $(\"td[data-row=\" + $(this).attr(\"data-row\") + \"]\").addClass(\"row_selected\");\n        }\n        self.draw();\n    });\n\n    $(\"#div_recall tbody td\").mouseover(function(){\n        self.det_rect = -1;\n        self.gt_rect = -1;\n        if ( $(this).attr(\"data-col\")!=undefined && $(this).attr(\"data-row\")!=undefined){\n            self.det_rect = $(this).attr(\"data-col\");\n            self.gt_rect = $(this).attr(\"data-row\");\n            $(\"#div_recall tbody td\").removeClass(\"selected\");\n            $(\"#div_recall tbody td\").removeClass(\"col_selected\").removeClass(\"row_selected\");\n            $(this).addClass(\"selected\");\n            $(\"td[data-col=\" + $(this).attr(\"data-col\") + \"]\").addClass(\"col_selected\");\n            $(\"td[data-row=\" + $(this).attr(\"data-row\") + \"]\").addClass(\"row_selected\");\n        }\n        self.draw();\n    });\n\n    $(\"#div_precision tbody td\").mouseover(function(){\n        self.det_rect = -1;\n        self.gt_rect = -1;\n        if ( $(this).attr(\"data-col\")!=undefined && $(this).attr(\"data-row\")!=undefined){\n            self.det_rect = $(this).attr(\"data-col\");\n            self.gt_rect = $(this).attr(\"data-row\");\n            $(\"#div_precision tbody td\").removeClass(\"selected\");\n            $(\"#div_precision tbody td\").removeClass(\"col_selected\").removeClass(\"row_selected\");\n            $(this).addClass(\"selected\");\n            $(\"td[data-col=\" + $(this).attr(\"data-col\") + \"]\").addClass(\"col_selected\");\n            $(\"td[data-row=\" + $(this).attr(\"data-row\") + \"]\").addClass(\"row_selected\");\n        }\n        self.draw();\n    });\n\n    $(\"#div_char tbody td\").mouseover(function(){\n        self.det_rect = -1;\n        self.gt_rect = -1;\n        if ( $(this).attr(\"data-col\")!=undefined && $(this).attr(\"data-row\")!=undefined){\n            self.det_rect = $(this).attr(\"data-col\");\n            self.gt_rect = $(this).attr(\"data-row\");\n            $(\"#div_char tbody td\").removeClass(\"selected\");\n            $(\"#div_char tbody td\").removeClass(\"col_selected\").removeClass(\"row_selected\");\n            $(this).addClass(\"selected\");\n            $(\"td[data-col=\" + $(this).attr(\"data-col\") + \"]\").addClass(\"col_selected\");\n            $(\"td[data-row=\" + $(this).attr(\"data-row\") + \"]\").addClass(\"row_selected\");\n        }\n        self.draw();\n    });\n\n    this.draw();\n\n\n};\n\nClassVisualization.prototype.draw = function(){\n\n    this.ctx_gt.clearRect(0,0,this.canvas_gt.width,this.canvas_gt.height);\n    this.ctx_det.clearRect(0,0,this.canvas_gt.width,this.canvas_gt.height);\n    \n    if(!this.image_loaded){\n        this.ctx_det.fillStyle = \"rgba(255,0,0,1)\";\n        this.ctx_det.font= \"12px Verdana\";\n        this.ctx_det.fillText(\"Loading image..\", 20,60);\n        this.ctx_gt.fillStyle = \"rgba(255,0,0,1)\";\n        this.ctx_gt.font= \"12px Verdana\";\n        this.ctx_gt.fillText(\"Loading image..\", 20,60);\n        \n        return;\n    }\n    \n    \n    if( $(\"#chk_image\").is(\":checked\")){\n        this.ctx_gt.drawImage(img_gt_image2,this.offset_x,this.offset_y,this.curr_im_w,this.curr_im_h);\n    }else{\n        this.ctx_gt.strokeStyle = \"rgba(0,0,0,1)\";\n        this.ctx_gt.strokeRect(this.offset_x,this.offset_y,this.curr_im_w,this.curr_im_h);\n    }\n\n\n    if (this.sampleData==null){\n        this.ctx_gt.fillStyle = \"rgba(255,0,0,1)\";\n        this.ctx_gt.font= \"12px Verdana\";\n        this.ctx_gt.fillText(\"Loading method..\", 20,60);        \n        this.ctx_det.fillStyle = \"rgba(255,0,0,1)\";\n        this.ctx_det.font= \"12px Verdana\";\n        this.ctx_det.fillText(\"Loading method..\", 20,60);\n        return;\n    }else{\n         if (this.sampleData.gtPolPoints==undefined){\n             this.sampleData.gtPolPoints = [];\n         }\n    }\n    \n    for (var i=0;i<this.sampleData.gtPolPoints.length;i++){\n        \n        //if (bb.id_s==current_id_submit){\n\n            var opacity = 0.6;//(gt_rect==bb.i)? \"0.9\" : \"0.6\";\n            \n            var bb = this.sampleData.gtPolPoints[i];\n            var type = this.sampleData.gtTypes[i];\n            \n            var gtDontCare = $.inArray(i,this.sampleData.gtDontCare)>-1;\n            \n            if(type==\"DC\"){\n                this.ctx_gt.fillStyle = \"rgba(50,50,50,\" + opacity + \")\";\n            }else if (type==\"OO\"){\n                this.ctx_gt.fillStyle = \"rgba(0,190,0,\" + opacity + \")\";\n            }else if (type==\"NO\"){\n                this.ctx_gt.fillStyle = \"rgba(38,148,232,\" + opacity + \")\";                \n            }else{\n                this.ctx_gt.fillStyle = \"rgba(255,0,0,\" + opacity + \")\";\n            }\n\n            if (bb.length==4){\n\n                var x = this.original_to_zoom_val(parseInt(bb[0]));\n                var y = this.original_to_zoom_val_y(parseInt(bb[1]));\n                var x2 = this.original_to_zoom_val(parseInt(bb[2]));\n                var y2 = this.original_to_zoom_val_y(parseInt(bb[3]));\n                var w = x2-x+1;\n                var h = y2-y+1;\n                this.ctx_gt.fillRect(x,y,w,h);\n                if(this.gt_rect==i){\n                    this.ctx_gt.lineWidth = 2;\n                    this.ctx_gt.strokeStyle = 'red';\n                    this.ctx_gt.strokeRect(x,y,w,h);\n                }  \n            }else{\n                this.ctx_gt.beginPath();\n                this.ctx_gt.moveTo(this.original_to_zoom_val(parseInt(bb[0])), this.original_to_zoom_val_y(parseInt(bb[1])));\n                this.ctx_gt.lineTo(this.original_to_zoom_val(parseInt(bb[2])+1), this.original_to_zoom_val_y(parseInt(bb[3])));\n                this.ctx_gt.lineTo(this.original_to_zoom_val(parseInt(bb[4])+1), this.original_to_zoom_val_y(parseInt(bb[5])+1));\n                this.ctx_gt.lineTo(this.original_to_zoom_val(parseInt(bb[6])), this.original_to_zoom_val_y(parseInt(bb[7])+1));\n                this.ctx_gt.closePath();\n                this.ctx_gt.fill();\n\n                //ctx_gt.fillRect( original_to_zoom_val(parseInt(bb.x)),original_to_zoom_val_y(parseInt(bb.y)),parseInt(bb.w)*scale,parseInt(bb.h)*scale);\n                if(this.gt_rect==i){\n                    this.ctx_gt.lineWidth = 2;\n                    this.ctx_gt.strokeStyle = 'red';\n                    this.ctx_gt.stroke();\n                }            \n            }\n\n            //pseudo character centers\n            for (var k=0;k<this.sampleData.gtCharPoints[i].length;k++){\n                var center = this.sampleData.gtCharPoints[i][k];\n                var count = this.sampleData.gtCharCounts[i][k];\n\n                if(count==1){\n                    this.ctx_gt.fillStyle = \"rgba(0,190,0,\" + opacity + \")\";\n                }else{\n                    this.ctx_gt.fillStyle = \"rgba(255,0,0,\" + opacity + \")\";\n                }\n                \n\n                var x = this.original_to_zoom_val(center[0]);\n                var y = this.original_to_zoom_val_y(center[1]);\n                \n                this.ctx_gt.beginPath();\n                this.ctx_gt.arc(x, y, 5, 0, 2 * Math.PI, false);\n                this.ctx_gt.closePath();\n                this.ctx_gt.fill();\n\n                if(this.gt_rect==i){\n                    this.ctx_gt.lineWidth = 2;\n                    this.ctx_gt.strokeStyle = 'red';\n                    this.ctx_gt.stroke();\n                }else{\n                    this.ctx_gt.lineWidth = 2;\n                    this.ctx_gt.strokeStyle = \"rgba(50,50,50,\" + opacity + \")\";\n                    this.ctx_gt.stroke();\n                }\n            }\n\n        //}\n    }\n\n\n    this.ctx_det.clearRect(0,0,this.canvas_gt.width,this.canvas_gt.height);\n    if( $(\"#chk_image\").is(\":checked\")){\n        this.ctx_det.drawImage(img_gt_image2,this.offset_x,this.offset_y,this.curr_im_w,this.curr_im_h);\n    }else{\n        this.ctx_det.strokeStyle = \"rgba(0,0,0,1)\";\n        this.ctx_det.strokeRect(this.offset_x,this.offset_y,this.curr_im_w,this.curr_im_h);\n    }    \n\n    for (var i=0;i<this.sampleData.detPolPoints.length;i++){\n        var bb = this.sampleData.detPolPoints[i];\n        var type = this.sampleData.detTypes[i];\n\n            var opacity = 0.6;//(det_rect==bb.i)? \"0.9\" : \"0.6\";\n            if(type==\"DC\"){\n                this.ctx_det.fillStyle = \"rgba(50,50,50,\" + opacity + \")\";\n            }else if (type==\"OO\"){\n                this.ctx_det.fillStyle = \"rgba(0,190,0,\" + opacity + \")\";\n            }else if (type==\"NO\"){\n                this.ctx_det.fillStyle = \"rgba(38,148,232,\" + opacity + \")\";                \n            }else{\n                this.ctx_det.fillStyle = \"rgba(255,0,0,\" + opacity + \")\";\n            }\n\n            if (bb.length==4){\n                \n                var x = this.original_to_zoom_val(parseInt(bb[0]));\n                var y = this.original_to_zoom_val_y(parseInt(bb[1]));\n                var x2 = this.original_to_zoom_val(parseInt(bb[2]));\n                var y2 = this.original_to_zoom_val_y(parseInt(bb[3]));\n                var w = x2-x+1;\n                var h = y2-y+1;\n                this.ctx_det.fillRect(x,y,w,h);\n                if(this.det_rect==i){\n                    this.ctx_det.lineWidth = 2;\n                    this.ctx_det.strokeStyle = 'red';\n                    this.ctx_det.strokeRect(x,y,w,h);\n                }   \n                \n            }else{\n                this.ctx_det.beginPath();\n                this.ctx_det.moveTo(this.original_to_zoom_val(parseInt(bb[0])), this.original_to_zoom_val_y(parseInt(bb[1])));\n                this.ctx_det.lineTo(this.original_to_zoom_val(parseInt(bb[2])+1), this.original_to_zoom_val_y(parseInt(bb[3])));\n                this.ctx_det.lineTo(this.original_to_zoom_val(parseInt(bb[4])+1), this.original_to_zoom_val_y(parseInt(bb[5])+1));\n                this.ctx_det.lineTo(this.original_to_zoom_val(parseInt(bb[6])), this.original_to_zoom_val_y(parseInt(bb[7])+1));\n                this.ctx_det.closePath();\n                this.ctx_det.fill();\n\n                //ctx_gt.fillRect( original_to_zoom_val(parseInt(bb.x)),original_to_zoom_val_y(parseInt(bb.y)),parseInt(bb.w)*scale,parseInt(bb.h)*scale);\n                if(this.det_rect==i){\n                    this.ctx_det.lineWidth = 2;\n                    this.ctx_det.strokeStyle = 'red';\n                    this.ctx_det.stroke();\n                }   \n            }\n    }\n    this.draws++;\n};"
  },
  {
    "path": "views/index.tpl",
    "content": "% import json\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n    <head>\r\n        <title>{{title}}</title>\r\n        <meta charset=\"utf-8\" />\r\n        <link rel='stylesheet' href='{{ url('static', path='style.css') }}' />\r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='jquery-1.8.2.min.js') }}\" charset=\"utf-8\"></script>\r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='jquery.form-3.51.js') }}\" charset=\"utf-8\"></script>             \r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='ranking.js') }}\" charset=\"utf-8\"></script>        \r\n        <script type='text/javascript' src='https://www.google.com/jsapi'></script>\r\n    </head>\r\n    <body>\r\n        \r\n        <h1><a href=\"http://rrc.cvc.uab.es/\" target=\"_blank\"><img id='logo' src='/static/CVC.png'></a>{{title}}</h1>\r\n        \r\n        <div class='breadcrumbs'>\r\n            Methods\r\n            % if len(subm_data)>0:\r\n                <button class='ml20 button-error pure-button' onclick=\"delete_methods()\">Delete all methods</button> <span class=\"small\">(You can also delete all methods by supressing all files from the output folder)</span>\r\n            % end\r\n            \r\n            <a class=\"right\" href=\"/exit\">Exit</a>\r\n        </div>\r\n        \r\n        <form action=\"/evaluate\" method=\"post\" enctype=\"multipart/form-data\">\r\n          Upload your method: \r\n          <label for='inp_title'>Title:</label><input type='text' name='title' maxlength=\"50\" id='inp_title'>\r\n          File:\r\n          <input type=\"file\" name=\"submissionFile\" />\r\n          % for k,v in submit_params.items():\r\n                <label for='inp_{{k}}'>{{v['title']}}: </label>\r\n                <select id='inp_{{k}}' name='{{k}}'>\r\n                % for option in v['values']:\r\n                    <option value='{{option['value']}}'>{{option['name']}}</option>\r\n                % end\r\n                </select>\r\n          % end\r\n          <button class=\"pure-button pure-button-primary\" type=\"button\" onclick=\"upload_subm()\" >Evaluate</button>\r\n        </form>\r\n        <p class='info'>Dataset files of this Standalone: <a href='gt/images.zip'>Images</a> - <a href='gt/gt.{{extension}}'>Ground Truth</a> <button class=\"ml20 pure-button pure-button-secondary\" type=\"button\" onclick=\"instructions()\" >See upload instructions..</button></p>\r\n        <%\r\n        if len(subm_data)>0:\r\n            graphicRows = []\r\n            graphic2Rows = []\r\n        %>\r\n        <table class='results ib'>\r\n            <thead>\r\n                <th>Method</th>\r\n                <th>Submit date</th>\r\n            <% \r\n             row = [\"'Title'\"]\r\n             row2 = [\"'Title'\"]\r\n             num_column = -1\r\n             num_column_order = -1\r\n             show2ndGraphic = False\r\n             for k,v in method_params.items():\r\n                num_column+=1\r\n                if v['grafic'] == \"1\":\r\n                    row.append(\"'\" + v['long_name'] + \"'\")\r\n                elif v['grafic'] == \"2\":\r\n                    row2.append(\"'\" + v['long_name'] + \"'\")\r\n                end\r\n                if v['order'] != \"\":\r\n                    if v['grafic'] == \"1\":\r\n                        num_column_order = num_column\r\n                        sort_name = k\r\n                        sort_name_long = v['long_name']\r\n                        sort_order = v['order']\r\n                        sort_format = v['format']\r\n                        sort_type = v['type']\r\n                    elif v['grafic'] == \"2\":\r\n                        show2ndGraphic = True\r\n                        sort2_name = k\r\n                        sort2_name_long = v['long_name']\r\n                        sort2_order = v['order']\r\n                        sort2_format = v['format']\r\n                        sort2_type = v['type']\r\n                    end\r\n                end            \r\n            %>\r\n                <th>{{v['long_name']}}</th>\r\n            % end\r\n            <th></th>\r\n            % graphicRows.append(\"[\" + ','.join(row) + \"]\")\r\n            % graphic2Rows.append(\"[\" + ','.join(row2) + \"]\")\r\n            </thead>\r\n            <tbody>\r\n            \r\n            <% \r\n                methodsData = []\r\n                for id, title, date, methodResultJson in subm_data:\r\n                    methodData = [id, title, date]\r\n                    methodResult = json.loads(methodResultJson)\r\n                    for k,v in method_params.items():\r\n                        methodData.append(methodResult[k])\r\n                    end\r\n                    methodsData.append(methodData)\r\n                end\r\n                methodsData = sorted(methodsData, key=lambda methodData: methodData[2+num_column_order],reverse=sort_order==\"desc\")\r\n            \r\n                for methodData in methodsData:\r\n                    id = methodData[0]\r\n                    title = methodData[1]\r\n                    date = methodData[2]\r\n            %>\r\n                <tr>\r\n                    <td><a class='methodname' href='method/?m={{id}}'>{{id}}: <span class=\"title\">{{title}}</span></a></td>\r\n                <td><a href='method/?m={{id}}'>{{date}}</a></td>\r\n                <% \r\n                row = [\"'\" + title.replace(\"'\",\"\\'\") + \"'\"]\r\n                row2 = [\"'\" + title.replace(\"'\",\"\\'\") + \"'\"]\r\n                index=0\r\n                for k,v in method_params.items():\r\n                    colValue = methodData[3+index]\r\n                    if v['format'] == \"perc\" :\r\n                        value = str(round(colValue*100,2)) + \" %\"\r\n                        graphicValue = \"{v:\" + str(colValue) + \", f:'\" + str(round(colValue*100,2)) + \" %'}\";\r\n                    elif v['type'] == \"double\" :\r\n                        value = str(round(colValue*100,2))\r\n                        graphicValue =  \"{v:\" + str(colValue) + \", f:'\" + str(round(colValue*100,2)) + \"'}\";\r\n                    else:\r\n                        value = colValue\r\n                        graphicValue = colValue\r\n                    end                    \r\n                    if v['grafic'] == \"1\":\r\n                        row.append(graphicValue)\r\n                    elif v['grafic'] == \"2\":\r\n                        row2.append(graphicValue)\r\n                    end\r\n                    %>\r\n                    <td>{{value}}</td>\r\n                    <% \r\n                    index += 1\r\n                end %>\r\n                <td><button class=\"mr5 pure-button\" onclick=\"edit_method({{id}},this)\">edit</button><button class=\"pure-button button-error\"  onclick=\"delete_method({{id}})\">x</button></td>\r\n                % graphicRows.append(\"[\" + ','.join(row) + \"]\")\r\n                % graphic2Rows.append(\"[\" + ','.join(row2) + \"]\")\r\n                </tr>\r\n            <% end\r\n            graphicData = \"[\" + ','.join(graphicRows) + \"]\"\r\n            base64Data = graphicData.encode('utf-8')\r\n            graphic2Data = \"[\" + ','.join(graphic2Rows) + \"]\"\r\n            base64Data2 = graphic2Data.encode('utf-8')\r\n            \r\n            %>\r\n           </tbody>\r\n        </table>\r\n        \r\n                    \r\n        <input type=\"hidden\" id='graphic' value='{{base64Data}}'>\r\n        <input type=\"hidden\" id='graphic-sort' value='{{sort_name_long}}'>\r\n        <input type=\"hidden\" id='graphic-type' value='{{sort_type}}'>\r\n        <input type=\"hidden\" id='graphic-format' value='{{sort_format}}'>\r\n        \r\n        <div id=\"div_rankings\">\r\n            <div id='div_ranking_1' style='overflow:hidden;display:inline-block;' class='ib'></div>\r\n\r\n            % if show2ndGraphic:\r\n                <input type=\"hidden\" id='graphic-gr2' value='{{base64Data2}}'>\r\n                <input type=\"hidden\" id='graphic-gr2-sort' value='{{sort2_name_long}}'>\r\n                <input type=\"hidden\" id='graphic-gr2-type' value='{{sort2_type}}'>\r\n                <input type=\"hidden\" id='graphic-gr2-format' value='{{sort2_format}}'>        \r\n                <div id='div_ranking_2' style='overflow:hidden;display:inline-block;' class='ib'></div>\r\n            % end        \r\n        </div>\r\n        \r\n        % else:\r\n        <p class='info'>Upload your methods to see the method's ranking. </p>\r\n\r\n        % end\r\n        \r\n        <div id='div_instructions' class='hidden'><div class='wrap'><button class='close pure-button button-error'>close</button><h1>Upload instructions</h1>\r\n            <p class='info'>Note that the following instructions are for the Test Dataset, the example links may not work here if the dataset is not the Test Set.</p>\r\n            {{ !instructions }}\r\n        </div></div>\r\n    </body>\r\n</html>"
  },
  {
    "path": "views/method.tpl",
    "content": "<%\r\nimport json\r\nimport math\r\nimport web\r\n%>\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n    <head>\r\n        <title>{{title}}</title>\r\n        <meta charset=\"utf-8\" />\r\n        <link rel='stylesheet' href='{{ url('static', path='style.css') }}' />\r\n    </head>\r\n    <body>\r\n        \r\n        % submitId, methodTitle, submitDate, methodResultJson = subm_data\r\n        \r\n        <h1><a href=\"http://rrc.cvc.uab.es/\" target=\"_blank\"><img id='logo' src='/static/CVC.png'></a>{{title}}</h1>\r\n        \r\n        <div class='breadcrumbs'>\r\n            <a href='/'>Methods</a> > {{methodTitle}}\r\n        </div>        \r\n        \r\n        % result = json.loads(results.read('method.json'))\r\n        % if result==None:\r\n            <h2>Submit your method</h2>\r\n        % elif result['calculated']==False:\r\n            <h2>The method has not been calculated</h2>\r\n            <p>{{result['Message']}}</p>\r\n        % else:\r\n            <div class=\"summary\">\r\n                <h2>Method summary</h2>\r\n                <p>Title: <strong>{{methodTitle}}</strong> [{{submitId}}]</p>\r\n                <p>Submit date: {{submitDate}}</p>\r\n                <% for k,v in method_params.items():\r\n                    colValue = result['method'][k]\r\n                    if v['format'] == \"perc\" :\r\n                        value = str(round(colValue*100,2)) + \" %\"\r\n                    elif v['type'] == \"double\" :\r\n                        value = str(round(colValue*100,2))\r\n                    else:\r\n                        value = colValue\r\n                    end        \r\n                %>\r\n                    <p>{{v['long_name']}}: <strong>{{value}}</strong></p>\r\n                % end\r\n            </div>\r\n\r\n            <div class='navigation'>\r\n            %num_pages = int(math.ceil(float(len(images)) / 20))\r\n            % if page>1:\r\n                <a class=\"pure-button button-secondary\" href='?m={{submitId}}&p={{page-1}}'>< previous</a>\r\n            % end\r\n            <span class='current'>Page {{page}} of {{num_pages}}</span>\r\n            % if page<num_pages:\r\n                <a class=\"pure-button button-secondary\" href='?m={{submitId}}&p={{page+1}}'>next ></a>\r\n            % end\r\n            </div>\r\n            \r\n            <div class=\"samples_list\">\r\n            <%  \r\n                for index, name in enumerate(images[(page-1)*20:page*20]):\r\n                    sampleId = web.image_name_to_id(name)\r\n                    values = json.loads(results.read( sampleId + '.json'))\r\n                    sample = (page-1)*20+index+1\r\n            %>\r\n                    <div class='sample'>\r\n                        <a href='/sample/?m={{submitId}}&sample={{str(sample)}}'><img src='/image_thumb/?c={{acronym}}&sample={{str(sample)}}' alt='{{name}}'></a>\r\n                        <p><a href='/sample/?m={{submitId}}&sample={{str(sample)}}'>Sample: {{ str(sample)}}</a></p>\r\n                        <p><a href='/sample/?m={{submitId}}&sample={{str(sample)}}'>ID: {{sampleId}}</a></p>\r\n                        <% for k,v in sample_params.items():\r\n                            colValue = values[k]\r\n                            if v['format'] == \"perc\" :\r\n                                value = str(round(colValue*100,2)) + \" %\"\r\n                            elif v['type'] == \"double\" :\r\n                                value = str(round(colValue*100,2))\r\n                            else:\r\n                                value = colValue\r\n                            end        \r\n                            %>\r\n                            <p>{{v['long_name']}}: <strong>{{value}}</strong></p>\r\n                        % end                        \r\n                    </div>\r\n            <%\r\n                end\r\n                results.close()\r\n            %>\r\n            </div>\r\n        % end        \r\n    </body>\r\n</html>"
  },
  {
    "path": "views/sample.tpl",
    "content": "<!DOCTYPE html>\r\n<html lang=\"en\">\r\n    <head>\r\n        <title>{{title}}</title>        \r\n        <meta charset=\"utf-8\" />\r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='jquery-1.8.2.min.js') }}\" charset=\"utf-8\"></script>\r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='jquery-ui.min.js') }}\" charset=\"utf-8\"></script>\r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='jquery-mousewheel.js') }}\" charset=\"utf-8\"></script>\r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='visualization_default.js') }}\" charset=\"utf-8\"></script>\r\n        <script type=\"text/javascript\" src=\"{{ url('static', path='funcs.js') }}\" charset=\"utf-8\"></script>\r\n        <link rel='stylesheet' href='{{ url('static', path='style.css') }}' />\r\n        <link rel='stylesheet' href='{{ url('static', path='jquery-ui.min.css') }}' />\r\n        <link rel='stylesheet' href='{{ url('static', path='visualization.css') }}' />\r\n        \r\n        <script type=\"text/javascript\" src=\"{{ url('static_custom', path='visualization_TL_iou.js') }}\" charset=\"utf-8\"></script>\r\n        <link rel='stylesheet' href='{{ url('static_custom', path='visualization_TL_iou.css') }}' />\r\n        \r\n    </head>\r\n    <body>\r\n        \r\n        <h1><a href=\"http://rrc.cvc.uab.es/\" target=\"_blank\"><img id='logo' src='/static/CVC.png'></a>{{title}}</h1>\r\n        \r\n        % submitId, methodTitle, submitDate, methodResultJson = subm_data\r\n        % import math\r\n        % page = 1\r\n        % if int(sample)>1:\r\n            %page = (0 if sample % 20 == 0 else 1) + int(math.ceil(sample/20))  \r\n        % end\r\n        \r\n        <div class=\"breadcrumbs\">\r\n            <a href='/'>Methods</a> > \r\n            <a href=\"/method/?m={{submitId}}&p={{page}}\" style='margin-right: 40px;'>{{methodTitle}}</a>\r\n\r\n            % if int(sample)>1:\r\n                <a class=\"pure-button button-secondary\" href=\"/sample/?m={{submitId}}&sample={{int(sample)-1}}\">< previous</a>\r\n            % end\r\n\r\n            Sample {{sample}} of {{num_samples}}\r\n\r\n            % if int(sample) < int(num_samples):\r\n                <a class=\"pure-button button-secondary\" href=\"/sample/?m={{submitId}}&sample={{int(sample)+1}}\">next ></a>\r\n            % end        \r\n        </div>\r\n        \r\n        <div id='div_comparation'>\r\n            <table class='sample_methods'>\r\n                <thead>\r\n                    <th>Method</th>\r\n                    <% \r\n                    num_column = -1\r\n                    num_column_order = -1\r\n                    for k,v in sample_params.items():\r\n                       num_column+=1\r\n                       if v['order'] != \"\":\r\n                           num_column_order = num_column\r\n                           sort_order = v['order']\r\n                       end\r\n                       %>\r\n                       <th>{{v['long_name']}}</th>\r\n                    % end\r\n                </thead>\r\n                <tbody>\r\n                    <%\r\n                    samplesData = []\r\n                    for row in samplesValues:\r\n                        sampleData = [row['id'],row['title']]\r\n                        for k,v in sample_params.items():\r\n                            sampleData.append(row[k])\r\n                        end\r\n                        samplesData.append(sampleData)\r\n                    end\r\n                    samplesData = sorted(samplesData, key=lambda sample: sample[num_column_order],reverse=sort_order==\"desc\")                    \r\n                    for row in samplesData:\r\n                        methodClass = \"current\" if row[0]==submitId else \"other\"\r\n                    %>\r\n                        <tr class=\"{{methodClass}}\">\r\n                            <td>{{row[1]}}</td>\r\n                            <%  index = 2 #omit fields id,title\r\n                                for k,v in sample_params.items():\r\n                                    colValue = row[index]\r\n                                    if v['format'] == \"perc\" :\r\n                                        value = str(round(colValue*100,2)) + \" %\"\r\n                                    elif v['type'] == \"double\" :\r\n                                        value = str(round(colValue*100,2))\r\n                                    else:\r\n                                        value = colValue\r\n                                    end                                 \r\n                                    index = index+1\r\n                            %>\r\n                                    <td>{{value}}</td>\r\n                            %   end\r\n                        </tr>\r\n                    % end\r\n                    </tbody>\r\n            </table>\r\n        </div>\r\n        \r\n        <div id='div_sample'></div>\r\n        \r\n    </body>\r\n</html>"
  },
  {
    "path": "views/upload.tpl",
    "content": "<!DOCTYPE html>\r\n<html lang=\"en\">\r\n    <head>\r\n        <meta charset=\"utf-8\" />\r\n        <title>{{title}}</title>\r\n        <link rel='stylesheet' href='{{ url('static', path='style.css') }}' />        \r\n    </head>\r\n    <body>\r\n        \r\n        <h1><a href=\"http://rrc.cvc.uab.es/\" target=\"_blank\"><img id='logo' src='/static/CVC.png'></a>{{title}}</h1>\r\n        \r\n        <div class='breadcrumbs'>\r\n            <a href='/'>Methods</a>\r\n        </div>    \r\n        \r\n        % if resDict['calculated']==False:\r\n            <h2>The method has not been calculated</h2>\r\n            <p>{{resDict['Message']}}</p>\r\n        %else:\r\n            <h2>The method has been calculated</h2>\r\n            <p>See the <a href='/method/?m={{id}}'>method results</a></p>\r\n        % end\r\n        \r\n    </body>\r\n</html>"
  },
  {
    "path": "web.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\nimport sys\nimport os\nsys.path.append('./')\nimport json\nfrom io import BytesIO\nimport zipfile\nimport re\nfrom datetime import datetime\nimport importlib\nimport sqlite3\nimport rrc_evaluation_funcs\nfrom config.config import *\n\ntry:\n    from bottle import route, run, request, static_file, url, template, TEMPLATE_PATH,HTTPResponse,redirect\nexcept ImportError:\n    print(\"\"\"Required module not found: Bottle. Installation: pip install --user bottle\"\"\")\n    sys.exit(-1)\n\ntry:\n    from PIL import Image\nexcept ImportError:\n    print(\"\"\"Required module not found: Pillow. Installation: pip install --user Pillow\"\"\")\n    sys.exit(-1)\n\nTEMPLATE_PATH.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), \"views\")))\n\ndef image_name_to_id(name):\n    # m = re.match(image_name_to_id_str,name)\n    # if m == None:\n    #     return False\n    # id = m.group(1)\n    id = name.replace('.jpg', '').replace('.png', '').replace('.gif', '').replace('.bmp', '')\n    return id\n\n\ndef get_sample_id_from_num(num):\n    imagesFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/gt/images.zip\"\n    archive = zipfile.ZipFile(imagesFilePath,'r')\n    current = 0\n    for image in archive.namelist():\n        if image_name_to_id(image) != False:\n            current += 1\n            if (current == num):\n                return image_name_to_id(image)\n            \n    return False\n\t\ndef get_sample_from_num(num):\n    imagesFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/gt/images.zip\"\n    archive = zipfile.ZipFile(imagesFilePath,'r')\n    current = 0\n    for image in archive.namelist():\n        if image_name_to_id(image) != False:\n            current += 1\n            if (current == num):\n                return image,archive.read(image)\n            \n    return False\t\n\ndef get_samples():\n    imagesFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/gt/images.zip\"\n    archive = zipfile.ZipFile(imagesFilePath,'r')\n    num_samples = 0\n    samples_list = []\n    for image in archive.namelist():\n        if image_name_to_id(image) != False:\n            num_samples += 1\n            samples_list.append(image)\n            \n    return num_samples,samples_list\n\n@route('/static/:path#.+#', name='static')\ndef static(path):\n    return static_file(path, root=os.path.abspath(os.path.join(os.path.dirname(__file__), \"static\")))\n\n@route('/static_custom/:path#.+#', name='static_custom')\ndef static_custom(path):\n    return static_file(path, root=os.path.abspath(os.path.join(os.path.dirname(__file__), \"static_custom\")))\n\n@route('/gt/:path#.+#', name='static_gt')\ndef static_gt(path):\n    return static_file(path, root=os.path.abspath(os.path.join(os.path.dirname(__file__), \"gt\")))\n\n@route('/favicon.ico')\ndef favicon():\n    return static_file(\"cvc-ico.png\", root=os.path.abspath(os.path.join(os.path.dirname(__file__), \"static\")))\n\n\n@route('/')\ndef index():\n    \n    _,images_list = get_samples()\n\n    page = 1\n    if 'p' in request.query:\n        page = int(request.query['p'])\n        \n    subm_data = get_all_submissions()\n\n    vars = {\n            'url':url, \n            'acronym':acronym, \n            'title':title,\n            'images':images_list,\n            'method_params':method_params,\n            'page':page,\n            'subm_data':subm_data,\n            'submit_params':submit_params,\n            'instructions':instructions,\n            'extension':gt_ext\n            }\n    return template('index',vars)\n\n@route('/exit')\ndef exit():\n    sys.stderr.close()\n\n@route('/method/', methods=['GET'])\ndef method():\n    \n    _,images_list = get_samples()\n    \n    results = None\n    page = 1\n    subm_data = {}\n    \n    if 'm' in request.query:\n        id = request.query['m']\n        submFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + id   + \".zip\"\n\n        if os.path.isfile(submFilePath):\n            results = zipfile.ZipFile(submFilePath,'r')\n            \n        if 'p' in request.query:\n            page = int(request.query['p'])\n        \n        subm_data = get_submission(id)\n        \n        if results is None or subm_data is None:\n            redirect('/')\n    else:\n        redirect('/')\n\n    vars = {\n        'url':url, \n        'acronym':acronym, \n        'title':title,\n        'images':images_list,\n        'method_params':method_params,\n        'sample_params':sample_params,\n        'results':results,\n        'page':page,\n        'subm_data':subm_data\n    }\n    return template('method',vars)\n\n\n@route('/sample/')\ndef sample():\n    \n    num_samples,images_list = get_samples()    \n\n    sample = int(request.query['sample'])\n    \n    methodId = request.query['m']\n    subm_data = get_submission(methodId)\n    \n    samplesValues = []\n    \n    id = get_sample_id_from_num(int(sample))\n    sampleId = id + \".json\"\n    \n    subms = get_all_submissions()\n    for methodId,methodTitle,_,_ in subms:\n        \n        zipFolderPath = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + str(methodId)\n        sampleFilePath = zipFolderPath + \"/\" + sampleId\n        \n        if os.path.isfile(sampleFilePath) == False:\n            submFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + str(methodId) + \".zip\"\n            archive = zipfile.ZipFile(submFilePath,'r')\n        \n            if os.path.exists(zipFolderPath) == False:\n                os.makedirs(zipFolderPath)\n             \n            archive.extract(sampleId, zipFolderPath)\n            \n        file = open(sampleFilePath,\"r\")\n        results = json.loads(file.read())\n        file.close()\n        \n        #results = json.loads(archive.read(id + \".json\"))\n\n        sampleResults = {\"id\":methodId, \"title\":methodTitle}\n        for k,v in sample_params.items():\n            sampleResults[k] = results[k]\n            \n        samplesValues.append( sampleResults )\n    \n    vars = {\n                'url':url,\n                'acronym':acronym,\n                'title':title + ' - Sample ' + str(sample) + ' : ' + images_list[sample-1],\n                'sample':sample,\n                'num_samples':num_samples,\n                'subm_data':subm_data,\n                'samplesValues':samplesValues,\n                'sample_params':sample_params,\n                'customJS':customJS,\n                'customCSS':customCSS\n            }\n    return template('sample',vars)\n\n\n\n@route('/sampleInfo/', methods=['GET'])\ndef get_sample_info():\n    \n    methodId = request.query['m']    \n    submFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + methodId + \".zip\"\n    archive = zipfile.ZipFile(submFilePath,'r')\n    id = get_sample_id_from_num(int(request.query['sample']))\n    results = json.loads(archive.read(id + \".json\"))\n    return json.dumps(results)\n\n@route('/image_thumb/', methods=['GET'])\ndef image_thumb():\n\n    sample = int(request.query['sample'])\n    fileName,data = get_sample_from_num(sample)\n    ext = fileName.split('.')[-1]\n    \n    f = BytesIO(data)\t\n    image = Image.open(f)\n\n    maxsize = (205, 130)\n    image.thumbnail(maxsize)\n    output = BytesIO()\n\t\n    if ext==\"jpg\":\n            im_format = \"JPEG\"\n            header = \"image/jpeg\"\n            image.save(output,im_format, quality=80, optimize=True, progressive=True)\n    elif ext == \"gif\":\n            im_format = \"GIF\"\n            header = \"image/gif\"\n            image.save(output,im_format)\n    elif ext == \"png\":\n            im_format = \"PNG\"\n            header = \"image/png\"\n            image.save(output,im_format, optimize=True)\n    \n    contents = output.getvalue()\n    output.close()\n    \n    body = contents\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    \n    return HTTPResponse(body, **headers)    \n\n@route('/image/', methods=['GET'])\ndef image():\n    sample = int(request.query['sample'])\n    fileName,data = get_sample_from_num(sample)\n    ext = fileName.split('.')[-1]\n    if ext==\"jpg\":\n        header = \"image/jpeg\"\n    elif ext == \"gif\":\n        header = \"image/gif\"    \n    elif ext == \"png\":\n        header = \"image/png\"            \n    \n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers)    \n\n@route('/gt_image/', methods=['GET'])\ndef gt_image():\n    imagesFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/gt/gt.zip\"\n    archive = zipfile.ZipFile(imagesFilePath,'r')\n    fileName = request.query['sample']\n    ext = fileName.split('.')[-1]\n    if ext==\"jpg\":\n        header = \"image/jpeg\"\n    elif ext == \"gif\":\n        header = \"image/gif\"    \n    elif ext == \"png\":\n        header = \"image/png\"            \n    \n    data = archive.read(fileName)\n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers)   \n\n@route('/gt_file/', methods=['GET'])\ndef gt_file():\n    imagesFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/gt/gt.zip\"\n    archive = zipfile.ZipFile(imagesFilePath,'r')\n    fileName = request.query['sample']\n    ext = fileName.split('.')[-1]\n    if ext==\"xml\":\n        header = \"text/xml\"\n\n    data = archive.read(fileName)\n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers)   \n\n@route('/gt_video/', methods=['GET'])\ndef gt_video():\n    imagesFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/gt/images.zip\"\n    archive = zipfile.ZipFile(imagesFilePath,'r')\n    fileName = request.query['sample']\n    ext = fileName.split('.')[-1]\n    header = \"video/mp4\"\n\n    data = archive.read(fileName)\n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers)  \n\n@route('/subm_image/', methods=['GET'])\ndef subm_image():\n    submFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/output/subm_\" + str(request.query['m'])  + \".zip\"\n    archive = zipfile.ZipFile(submFilePath,'r')\n    fileName = request.query['sample']\n    ext = fileName.split('.')[-1]\n    if ext==\"jpg\":\n        header = \"image/jpeg\"\n    elif ext == \"gif\":\n        header = \"image/gif\"    \n    elif ext == \"png\":\n        header = \"image/png\"            \n    \n    data = archive.read(fileName)\n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers)  \n\n@route('/subm_xml/', methods=['GET'])\ndef subm_xml():\n    submFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/output/subm_\" + str(request.query['m'])  + \".zip\"\n    archive = zipfile.ZipFile(submFilePath,'r')\n    fileName = request.query['sample']\n    header = \"text/xml\"\n    data = archive.read(fileName)\n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers) \n\n\n@route('/result_image/', methods=['GET'])\ndef result_image():\n    submFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + str(request.query['m'])  + \".zip\"\n    archive = zipfile.ZipFile(submFilePath,'r')\n    fileName = request.query['name']\n    ext = fileName.split('.')[-1]\n    if ext==\"jpg\":\n        header = \"image/jpeg\"\n    elif ext == \"gif\":\n        header = \"image/gif\"    \n    elif ext == \"png\":\n        header = \"image/png\"            \n    \n    data = archive.read(fileName)\n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers)  \n\n@route('/result_xml/', methods=['GET'])\ndef result_xml():\n    submFilePath = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + str(request.query['m'])  + \".zip\"\n    archive = zipfile.ZipFile(submFilePath,'r')\n    fileName = request.query['name']\n    header = \"text/xml\"\n    data = archive.read(fileName)\n    body = data\n    headers = dict()\n    headers['Content-Type'] = header\n    if 'c' in request.query:\n        headers['Cache-Control'] = \"public, max-age=3600\"\n    return HTTPResponse(body, **headers)  \n\n@route('/evaluate', method=['POST','GET'])\ndef evaluate():\n    \n    id=0\n    submFile = request.files.get('submissionFile')\n    \n    if submFile is None:\n        resDict = {\"calculated\":False,\"Message\":\"No file selected\"}\n        if request.query['json']==\"1\":\n            return json.dumps(resDict)\n        else:        \n            vars = {'url':url, 'title':'Method Upload ' + title,'resDict':resDict}\n            return template('upload',vars)    \n    else:\n        \n        name, ext = os.path.splitext(submFile.filename)\n        if ext not in ('.' + gt_ext):\n            resDict = {\"calculated\":False,\"Message\":\"File not valid. A \" + gt_ext.upper() + \" file is required.\"}\n            if request.query['json']==\"1\":\n                return json.dumps(resDict)            \n            else:\n                vars = {'url':url, 'title':'Method Upload ' + title,'resDict':resDict}\n                return template('upload',vars)    \n    \n        p = {\n            'g': os.path.dirname(os.path.abspath(__file__)) + \"/gt/gt.\" + gt_ext, \n            's': os.path.dirname(os.path.abspath(__file__)) + \"/output/subm.\" + gt_ext, \n            'o': os.path.dirname(os.path.abspath(__file__)) + \"/output\"\n        }\n        \n        for k,_ in submit_params.items():\n            p['p'][k] = request.forms.get(k)\n\n        if os.path.isfile(p['s']):\n            os.remove(p['s'])\n\n        submFile.save(p['s'])\n\n        module = importlib.import_module(evaluation_script )\n        resDict = rrc_evaluation_funcs.main_evaluation(p,module.default_evaluation_params,module.validate_data,module.evaluate_method)\n\n        \n        if resDict['calculated']==True:\n            dbPath = os.path.dirname(os.path.abspath(__file__)) + \"/output/submits\"\n            conn = sqlite3.connect(dbPath)\n            cursor = conn.cursor()\n            \n            submTitle = request.forms.get('title')\n            if submTitle==\"\":\n                submTitle = \"unnamed\"\n                \n            cursor.execute('INSERT INTO submission(title,sumbit_date,results) VALUES(?,?,?)',(submTitle ,datetime.now().strftime(\"%Y-%m-%d %H:%M\"),json.dumps(resDict['method'])))\n            conn.commit()\n            id = cursor.lastrowid\n\n            os.rename(p['s'], p['s'].replace(\"subm.\" + gt_ext,\"subm_\" + str(id) + \".\" + gt_ext) )\n            os.rename(p['o'] + \"/results.zip\", p['o'] + \"/results_\" + str(id) + \".zip\" )\n\n            conn.close()\n\n        if request.query['json']==\"1\":\n            return json.dumps( {\"calculated\": resDict['calculated'],\"Message\": resDict['Message'],'id':id} )\n        else:\n            vars = {'url':url, 'title':'Method Upload ' + title,'resDict':resDict,'id':id}\n            return template('upload',vars)    \n\n@route('/delete_all', method='POST')\ndef delete_all():\n    output_folder = os.path.dirname(os.path.abspath(__file__)) + \"/output\"\n    try:    \n        for root, dirs, files in os.walk(output_folder, topdown=False):\n            for f in files:\n                os.remove(os.path.join(root, f))\n            for d in dirs:\n                os.rmdir(os.path.join(root, d))\n    except:\n        print(\"Unexpected error:\", sys.exc_info()[0])\n        \n@route('/delete_method', method='POST')\ndef delete_method():\n    id = request.forms.get('id')\n    \n    try:\n        output_folder = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + id\n        if os.path.isdir(output_folder):\n            for root, dirs, files in os.walk(output_folder, topdown=False):\n                for f in files:\n                    os.remove(os.path.join(root, f))\n                for d in dirs:\n                    os.rmdir(os.path.join(root, d))\n            os.rmdir(output_folder)\n        subm_file = os.path.dirname(os.path.abspath(__file__)) + \"/output/results_\" + id + \".\" + gt_ext\n        results_file = os.path.dirname(os.path.abspath(__file__)) + \"/output/subm_\" + id + \".zip\"\n        os.remove(subm_file)\n        os.remove(results_file)\n    except:\n        print(\"Unexpected error:\", sys.exc_info()[0])\n        \n    dbPath = os.path.dirname(os.path.abspath(__file__)) + \"/output/submits\"\n    conn = sqlite3.connect(dbPath)\n    cursor = conn.cursor()\n    cursor.execute('DELETE FROM submission WHERE id=?',(id))\n    conn.commit()\n    conn.close()\n    \n@route('/edit_method', method='POST')\ndef edit_method():\n    id = request.forms.get('id')\n    name = request.forms.get('name')\n    \n    dbPath = os.path.dirname(os.path.abspath(__file__)) + \"/output/submits\"\n    conn = sqlite3.connect(dbPath)\n    cursor = conn.cursor()\n    cursor.execute('UPDATE submission SET title=? WHERE id=?',(name,id))\n    conn.commit()\n    conn.close()    \n    \ndef get_all_submissions():\n    dbPath = os.path.dirname(os.path.abspath(__file__)) + \"/output/submits\"\n    conn = sqlite3.connect(dbPath)\n    cursor = conn.cursor()\n    cursor.execute(\"\"\"CREATE TABLE IF NOT EXISTS submission(id integer primary key autoincrement, title varchar(50), sumbit_date varchar(12),results TEXT)\"\"\")\n    conn.commit()\n\n    cursor.execute('SELECT id,title,sumbit_date,results FROM submission')\n    sumbData = cursor.fetchall()\n    conn.close()\n    return sumbData\n\n\ndef get_submission(id):\n    dbPath = os.path.dirname(os.path.abspath(__file__)) + \"/output/submits\"\n    conn = sqlite3.connect(dbPath)\n    cursor = conn.cursor()\n    cursor.execute(\"\"\"CREATE TABLE IF NOT EXISTS submission(id integer primary key autoincrement, title varchar(50), sumbit_date varchar(12),results TEXT)\"\"\")\n    conn.commit()\n    \n    cursor.execute('SELECT id,title,sumbit_date,results FROM submission WHERE id=?',(id,))\n    sumbData = cursor.fetchone()\n    conn.close()\n    \n    return sumbData\n\n\nif __name__=='__main__':\n    \n    evalModule = importlib.import_module(evaluation_script)\n    try:\n        for module,alias in evalModule.evaluation_imports().items():\n            importlib.import_module(module)\t\t\n    except ImportError:\n            print(\"Script \" + evaluation_script + \". Required module (\" + module + \") not found.\")\n            if module==\"Polygon\":\n                print(\"Install it with: pip3 install Polygon3\")\n            else:\n                print(\"Install it with: pip install \"\"\" + module)\n            sys.exit(101)    \n    \n    print(\"***********************************************\")\n    print(\"RRC Standalone Task\")\n    print(\"-----------------------------------------------\")\n    print('Command line client:\\ncurl -F \"submissionFile=submit.zip\" http://127.0.0.1:8080/evaluate')\n    print(\"\\nGUI client:firefox http://127.0.0.1:8080\")\n    print(\"-----------------------------------------------\")\n    run(host='0.0.0.0', port=8080, debug=True)\n"
  }
]