├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── gtm_hit ├── .DS_Store ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20161108_1322.py │ ├── 0003_auto_20161108_1544.py │ ├── 0004_auto_20161108_1607.py │ ├── 0005_auto_20161110_1512.py │ ├── 0006_auto_20161110_1558.py │ ├── 0007_auto_20161110_1654.py │ ├── 0008_auto_20161110_1705.py │ ├── 0009_remove_validationcode_workerid.py │ ├── 0010_worker_time_list.py │ ├── 0011_auto_20161202_1255.py │ └── __init__.py ├── models.py ├── static │ ├── .DS_Store │ └── gtm_hit │ │ ├── .DS_Store │ │ ├── cst.txt │ │ ├── day_2 │ │ ├── images │ │ ├── .DS_Store │ │ ├── arrows0_3D.png │ │ ├── arrows1_3D.png │ │ ├── arrows2_3D.png │ │ ├── arrows3_3D.png │ │ ├── arrows4_3D.png │ │ ├── arrows5_3D.png │ │ ├── arrows6_3D.png │ │ ├── arrows_plane.png │ │ ├── bad.png │ │ ├── epfl_logo.png │ │ ├── ex2.png │ │ ├── fns_logo.png │ │ ├── good.png │ │ └── idiap_logo.png │ │ ├── index.css │ │ ├── jquery.hotkeys.js │ │ ├── marker.js │ │ └── tuto.css ├── templates │ ├── .DS_Store │ ├── gtm_hit │ │ ├── .DS_Store │ │ ├── finish.html │ │ ├── frame.html │ │ ├── index.html │ │ ├── requestID.html │ │ └── tuto.html │ └── includes_hit │ │ ├── controller.html │ │ ├── footer.html │ │ ├── help.html │ │ ├── navbar.html │ │ └── side_menu.html ├── templatetags │ ├── __init__.py │ └── gtm_hit_extra.py ├── tests.py ├── urls.py └── views.py ├── gtmarker ├── .DS_Store ├── __init__.py ├── dump.rdb ├── settings.py ├── urls.py └── wsgi.py ├── home ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── static │ ├── epfl_logo.png │ ├── fns_logo.png │ └── idiap_logo.png ├── templates │ ├── .DS_Store │ ├── home │ │ ├── error.html │ │ ├── index.html │ │ └── login.html │ └── includes_home │ │ ├── footer.html │ │ └── navbar.html ├── tests.py └── views.py ├── manage.py └── marker ├── .DS_Store ├── __init__.py ├── admin.py ├── apps.py ├── context_processors.py ├── management ├── .DS_Store └── commands │ ├── add_data.py │ └── set_data.py ├── middleware.py ├── migrations ├── 0001_initial.py ├── 0002_auto_20161013_1455.py ├── 0003_rectanges.py ├── 0004_auto_20161018_1310.py ├── 0005_auto_20161018_1341.py ├── 0006_rectangle_xmid.py ├── 0007_remove_annotation_pom_file.py ├── 0008_delete_rectangle.py ├── 0009_worker.py ├── 0010_auto_20161110_1512.py └── __init__.py ├── models.py ├── static ├── .DS_Store └── marker │ ├── .DS_Store │ ├── arrows.gif │ ├── arrows.png │ ├── cst.txt │ ├── day_2 │ ├── find_rect.pickle │ ├── frame.css │ ├── images │ ├── .DS_Store │ ├── arrows0_3D.png │ ├── arrows1_3D.png │ ├── arrows2_3D.png │ ├── arrows3_3D.png │ ├── arrows4_3D.png │ ├── arrows5_3D.png │ ├── arrows6_3D.png │ ├── arrows_plane.png │ ├── bad.png │ ├── epfl_logo.png │ ├── ex2.png │ ├── fns_logo.png │ ├── good.png │ └── idiap_logo.png │ ├── index.css │ ├── jquery.hotkeys.js │ ├── marker.js │ ├── raphael.js │ └── rect.pickle ├── templates ├── .DS_Store ├── includes │ ├── controller.html │ ├── footer.html │ ├── help.html │ ├── load.html │ ├── navbar.html │ ├── side_menu.html │ └── tuto.html └── marker │ ├── download.html │ ├── download_worker.html │ ├── frame.html │ ├── index.html │ └── login.html ├── templatetags ├── __init__.py └── marker_extra.py ├── tests.py ├── urls.py └── views.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.json 3 | *.pom 4 | day2 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EPFL Multi-camera detection annotation tool 2 | Our web-application to annotate multi-camera detection datasets. Feel free to download and use it to annotate other datasets. 3 | 4 | ### Usage 5 | 6 | ### Dependencies 7 | Django==1.10.2 8 | django-bootstrap-form==3.2.1 9 | numpy==1.11.2 10 | pandas==0.19.0 11 | 12 | ### Acknowledgements 13 | Written by Stephane Bouquet during his MSc. Thesis at CVLab, EPFL, under the supervision of Pierre Baqué. 14 | We acknowledge the WILDTRACK project and the Swiss National Scientific Fund. 15 | -------------------------------------------------------------------------------- /gtm_hit/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/.DS_Store -------------------------------------------------------------------------------- /gtm_hit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/__init__.py -------------------------------------------------------------------------------- /gtm_hit/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import * 3 | 4 | admin.site.register(Worker) 5 | admin.site.register(ValidationCode) 6 | -------------------------------------------------------------------------------- /gtm_hit/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class Gtm_hitConfig(AppConfig): 5 | name = 'gtm_hit' 6 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-07 16:00 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Worker', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('workerID', models.PositiveIntegerField(default=0)), 21 | ('frameNB', models.PositiveSmallIntegerField(default=0)), 22 | ('validationoCode', models.PositiveIntegerField(default=0)), 23 | ('finished', models.BooleanField(default=False)), 24 | ('state', models.IntegerField(default=-1)), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0002_auto_20161108_1322.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-08 13:22 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('gtm_hit', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='worker', 17 | old_name='validationoCode', 18 | new_name='validationCode', 19 | ), 20 | migrations.AddField( 21 | model_name='worker', 22 | name='tuto', 23 | field=models.BooleanField(default=False), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0003_auto_20161108_1544.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-08 15:44 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('gtm_hit', '0002_auto_20161108_1322'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='worker', 17 | name='workerID', 18 | field=models.TextField(default='', max_length=40), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0004_auto_20161108_1607.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-08 16:07 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('gtm_hit', '0003_auto_20161108_1544'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='worker', 17 | name='frame_labeled', 18 | field=models.PositiveSmallIntegerField(default=0), 19 | ), 20 | migrations.AlterField( 21 | model_name='worker', 22 | name='frameNB', 23 | field=models.IntegerField(default=-1), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0005_auto_20161110_1512.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-10 15:12 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('gtm_hit', '0004_auto_20161108_1607'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='ValidationCode', 18 | fields=[ 19 | ('validationCode', models.PositiveIntegerField(primary_key=True, serialize=False)), 20 | ], 21 | ), 22 | migrations.RemoveField( 23 | model_name='worker', 24 | name='id', 25 | ), 26 | migrations.RemoveField( 27 | model_name='worker', 28 | name='validationCode', 29 | ), 30 | migrations.AlterField( 31 | model_name='worker', 32 | name='workerID', 33 | field=models.TextField(max_length=40, primary_key=True, serialize=False), 34 | ), 35 | migrations.AddField( 36 | model_name='validationcode', 37 | name='workerID', 38 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='gtm_hit.Worker'), 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0006_auto_20161110_1558.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-10 15:58 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('gtm_hit', '0005_auto_20161110_1512'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='validationcode', 17 | name='validationCode', 18 | field=models.TextField(primary_key=True, serialize=False), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0007_auto_20161110_1654.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-10 16:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('gtm_hit', '0006_auto_20161110_1558'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='validationcode', 18 | name='worker', 19 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='gtm_hit.Worker', unique=True), 20 | preserve_default=False, 21 | ), 22 | migrations.AlterField( 23 | model_name='validationcode', 24 | name='workerID', 25 | field=models.TextField(max_length=40, unique=True), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0008_auto_20161110_1705.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-10 17:05 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('gtm_hit', '0007_auto_20161110_1654'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='validationcode', 18 | name='worker', 19 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='gtm_hit.Worker'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0009_remove_validationcode_workerid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-10 17:06 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('gtm_hit', '0008_auto_20161110_1705'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='validationcode', 17 | name='workerID', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0010_worker_time_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-12-02 11:12 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('gtm_hit', '0009_remove_validationcode_workerid'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='worker', 17 | name='time_list', 18 | field=models.TextField(default=0), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /gtm_hit/migrations/0011_auto_20161202_1255.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-12-02 12:55 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('gtm_hit', '0010_worker_time_list'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='worker', 17 | name='time_list', 18 | field=models.TextField(default=''), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /gtm_hit/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/migrations/__init__.py -------------------------------------------------------------------------------- /gtm_hit/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.core.validators import validate_comma_separated_integer_list 3 | import json 4 | 5 | class Worker(models.Model): 6 | 7 | workerID = models.TextField(primary_key=True,max_length=40) 8 | frameNB = models.IntegerField(default=-1) 9 | frame_labeled = models.PositiveSmallIntegerField(default=0) 10 | #validationCode = models.PositiveIntegerField(default=0) 11 | finished = models.BooleanField(default=False) 12 | state = models.IntegerField(default=-1) 13 | tuto = models.BooleanField(default=False) 14 | time_list = models.TextField(default="") 15 | 16 | 17 | def increaseFrame(self,val): 18 | self.frame_labeled = self.frame_labeled + val 19 | 20 | def decreaseFrame(self,val): 21 | self.frame_labeled = self.frame_labeled - val 22 | 23 | def setTimeList(self,x): 24 | self.time_list = json.dumps(x) 25 | def getTimeList(self): 26 | return json.loads(self.time_list) 27 | def __str__(self): 28 | return 'Worker: ' + self.workerID 29 | class ValidationCode(models.Model): 30 | validationCode = models.TextField(primary_key=True) 31 | worker = models.OneToOneField('Worker',on_delete=models.CASCADE) 32 | def __str__(self): 33 | return 'Code: ' + self.worker.workerID 34 | -------------------------------------------------------------------------------- /gtm_hit/static/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/.DS_Store -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/.DS_Store -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/cst.txt: -------------------------------------------------------------------------------- 1 | WIDTH 12 2 | HEIGHT 32 3 | NB_WIDTH 480 4 | NB_HEIGHT 1280 5 | MAN_RAY 0.16 6 | MAN_HEIGHT 1.8 7 | REDUCTION 1 8 | ORIGINE_X -3.0 9 | ORIGINE_Y -6.0 10 | NB_CAMERA 7 -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/day_2: -------------------------------------------------------------------------------- 1 | /cvlabdata1/cvlab/datasets_people_tracking/ETH/day_2/ -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/.DS_Store -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows0_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows0_3D.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows1_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows1_3D.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows2_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows2_3D.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows3_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows3_3D.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows4_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows4_3D.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows5_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows5_3D.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows6_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows6_3D.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/arrows_plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/arrows_plane.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/bad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/bad.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/epfl_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/epfl_logo.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/ex2.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/fns_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/fns_logo.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/good.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/images/idiap_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/static/gtm_hit/images/idiap_logo.png -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Start Bootstrap - Landing Page (http://startbootstrap.com/) 3 | * Copyright 2013-2016 Start Bootstrap 4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | 7 | body, 8 | html { 9 | width: 100%; 10 | height: 100%; 11 | } 12 | 13 | body { 14 | font-family: "Lato","Helvetica Neue",Arial,sans-serif; 15 | font-weight: 500; 16 | } 17 | h1, 18 | h2, 19 | h3, 20 | h4, 21 | h5, 22 | h6 { 23 | font-family: "Lato","Helvetica Neue",Arial,sans-serif; 24 | font-weight: 700; 25 | } 26 | 27 | 28 | .intro-header { 29 | text-align: center; 30 | } 31 | 32 | .intro-message { 33 | position: relative; 34 | padding-top: 10%; 35 | padding-bottom: 10%; 36 | } 37 | 38 | .intro-message > h1 { 39 | margin: 0; 40 | font-size: 5em; 41 | } 42 | 43 | .intro-divider { 44 | width: 400px; 45 | border-top: 1px solid #f8f8f8; 46 | border-bottom: 1px solid rgba(0,0,0,0.2); 47 | } 48 | 49 | .button-name { 50 | text-transform: uppercase; 51 | font-size: 14px; 52 | font-weight: 400; 53 | letter-spacing: 2px; 54 | } 55 | 56 | .footer { 57 | position: relative; 58 | bottom: 0; 59 | width: 100%; 60 | } 61 | 62 | .top5 { margin-top:5px; } 63 | .top7 { margin-top:7px; } 64 | .top10 { margin-top:10px; } 65 | .top15 { margin-top:15px; } 66 | .top17 { margin-top:17px; } 67 | .top30 { margin-top:30px; } 68 | 69 | .big { 70 | line-height: 150%; 71 | } 72 | 73 | #my-row { 74 | display: table; 75 | } 76 | 77 | #my-row .panel { 78 | float: none; 79 | display: table-cell; 80 | vertical-align: top; 81 | } 82 | -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/jquery.hotkeys.js: -------------------------------------------------------------------------------- 1 | /*jslint browser: true*/ 2 | /*jslint jquery: true*/ 3 | 4 | /* 5 | * jQuery Hotkeys Plugin 6 | * Copyright 2010, John Resig 7 | * Dual licensed under the MIT or GPL Version 2 licenses. 8 | * 9 | * Based upon the plugin by Tzury Bar Yochay: 10 | * https://github.com/tzuryby/jquery.hotkeys 11 | * 12 | * Original idea by: 13 | * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ 14 | */ 15 | 16 | /* 17 | * One small change is: now keys are passed by object { keys: '...' } 18 | * Might be useful, when you want to pass some other data to your handler 19 | */ 20 | 21 | (function(jQuery) { 22 | 23 | jQuery.hotkeys = { 24 | version: "0.2.0", 25 | 26 | specialKeys: { 27 | 8: "backspace", 28 | 9: "tab", 29 | 10: "return", 30 | 13: "return", 31 | 16: "shift", 32 | 17: "ctrl", 33 | 18: "alt", 34 | 19: "pause", 35 | 20: "capslock", 36 | 27: "esc", 37 | 32: "space", 38 | 33: "pageup", 39 | 34: "pagedown", 40 | 35: "end", 41 | 36: "home", 42 | 37: "left", 43 | 38: "up", 44 | 39: "right", 45 | 40: "down", 46 | 45: "insert", 47 | 46: "del", 48 | 59: ";", 49 | 61: "=", 50 | 96: "0", 51 | 97: "1", 52 | 98: "2", 53 | 99: "3", 54 | 100: "4", 55 | 101: "5", 56 | 102: "6", 57 | 103: "7", 58 | 104: "8", 59 | 105: "9", 60 | 106: "*", 61 | 107: "+", 62 | 109: "-", 63 | 110: ".", 64 | 111: "/", 65 | 112: "f1", 66 | 113: "f2", 67 | 114: "f3", 68 | 115: "f4", 69 | 116: "f5", 70 | 117: "f6", 71 | 118: "f7", 72 | 119: "f8", 73 | 120: "f9", 74 | 121: "f10", 75 | 122: "f11", 76 | 123: "f12", 77 | 144: "numlock", 78 | 145: "scroll", 79 | 173: "-", 80 | 186: ";", 81 | 187: "=", 82 | 188: ",", 83 | 189: "-", 84 | 190: ".", 85 | 191: "/", 86 | 192: "`", 87 | 219: "[", 88 | 220: "\\", 89 | 221: "]", 90 | 222: "'" 91 | }, 92 | 93 | shiftNums: { 94 | "`": "~", 95 | "1": "!", 96 | "2": "@", 97 | "3": "#", 98 | "4": "$", 99 | "5": "%", 100 | "6": "^", 101 | "7": "&", 102 | "8": "*", 103 | "9": "(", 104 | "0": ")", 105 | "-": "_", 106 | "=": "+", 107 | ";": ": ", 108 | "'": "\"", 109 | ",": "<", 110 | ".": ">", 111 | "/": "?", 112 | "\\": "|" 113 | }, 114 | 115 | // excludes: button, checkbox, file, hidden, image, password, radio, reset, search, submit, url 116 | textAcceptingInputTypes: [ 117 | "text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", 118 | "datetime-local", "search", "color", "tel"], 119 | 120 | // default input types not to bind to unless bound directly 121 | textInputTypes: /textarea|input|select/i, 122 | 123 | options: { 124 | filterInputAcceptingElements: true, 125 | filterTextInputs: true, 126 | filterContentEditable: true 127 | } 128 | }; 129 | 130 | function keyHandler(handleObj) { 131 | if (typeof handleObj.data === "string") { 132 | handleObj.data = { 133 | keys: handleObj.data 134 | }; 135 | } 136 | 137 | // Only care when a possible input has been specified 138 | if (!handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string") { 139 | return; 140 | } 141 | 142 | var origHandler = handleObj.handler, 143 | keys = handleObj.data.keys.toLowerCase().split(" "); 144 | 145 | handleObj.handler = function(event) { 146 | // Don't fire in text-accepting inputs that we didn't directly bind to 147 | if (this !== event.target && 148 | (jQuery.hotkeys.options.filterInputAcceptingElements && 149 | jQuery.hotkeys.textInputTypes.test(event.target.nodeName) || 150 | (jQuery.hotkeys.options.filterContentEditable && jQuery(event.target).attr('contenteditable')) || 151 | (jQuery.hotkeys.options.filterTextInputs && 152 | jQuery.inArray(event.target.type, jQuery.hotkeys.textAcceptingInputTypes) > -1))) { 153 | return; 154 | } 155 | 156 | var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[event.which], 157 | character = String.fromCharCode(event.which).toLowerCase(), 158 | modif = "", 159 | possible = {}; 160 | 161 | jQuery.each(["alt", "ctrl", "shift"], function(index, specialKey) { 162 | 163 | if (event[specialKey + 'Key'] && special !== specialKey) { 164 | modif += specialKey + '+'; 165 | } 166 | }); 167 | 168 | // metaKey is triggered off ctrlKey erronously 169 | if (event.metaKey && !event.ctrlKey && special !== "meta") { 170 | modif += "meta+"; 171 | } 172 | 173 | if (event.metaKey && special !== "meta" && modif.indexOf("alt+ctrl+shift+") > -1) { 174 | modif = modif.replace("alt+ctrl+shift+", "hyper+"); 175 | } 176 | 177 | if (special) { 178 | possible[modif + special] = true; 179 | } 180 | else { 181 | possible[modif + character] = true; 182 | possible[modif + jQuery.hotkeys.shiftNums[character]] = true; 183 | 184 | // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" 185 | if (modif === "shift+") { 186 | possible[jQuery.hotkeys.shiftNums[character]] = true; 187 | } 188 | } 189 | 190 | for (var i = 0, l = keys.length; i < l; i++) { 191 | if (possible[keys[i]]) { 192 | return origHandler.apply(this, arguments); 193 | } 194 | } 195 | }; 196 | } 197 | 198 | jQuery.each(["keydown", "keyup", "keypress"], function() { 199 | jQuery.event.special[this] = { 200 | add: keyHandler 201 | }; 202 | }); 203 | 204 | })(jQuery || this.jQuery || window.jQuery); 205 | -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/marker.js: -------------------------------------------------------------------------------- 1 | var rectsID = []; 2 | var boxes = {}; 3 | var chosen_rect; 4 | var imgArray = []; 5 | var arrArray = []; 6 | var validation = {}; 7 | var identities = {}; 8 | var personID = 0; 9 | var cameras = 7; 10 | var camName = ''; 11 | var loadcount = 0; 12 | var zoomOn = false; 13 | var zoomratio = []; 14 | var rotation = [50,230,150,75,265,340,80]; 15 | var bounds = [[0,396,1193,180,1883,228,1750,1080],[0,344,1467,77,1920,82,-1,-1], 16 | [97,1080,73,273,864,202,1920,362],[0,444,1920,261,-1,-1,-1,-1], 17 | [0,435,1920,403,-1,-1,-1,-1],[0,243,29,203,656,191,1920,442], 18 | [0,244,1920,162,-1,-1,-1,-1]]; 19 | var toggle_ground; 20 | var toggle_orientation; 21 | var to_label = 9; 22 | // hashsets --> rect per camera ? rect -> id to coordinates? 23 | // store variables here? in db ? (reupload db?) 24 | window.onload = function() { 25 | toggle_ground = true; 26 | toggle_orientation = true; 27 | 28 | var d = document.getElementById("changeF"); 29 | if(d != null){ 30 | d.className = d.className + " disabled"; 31 | if(nblabeled >= to_label) { 32 | var button = document.getElementById("changeF"); 33 | button.href="/gtm_hit/" + workerID + "/processFrame"; 34 | button.text = "Finish"; 35 | } 36 | } 37 | if (nblabeled > 0) { 38 | load_prev(); 39 | } 40 | camName = cams.substring(2,cams.length-2).split("', '"); 41 | for (var i = 0; i < cameras; i++) { 42 | boxes[i] = {}; 43 | 44 | arrArray[i] = new Image(); 45 | arrArray[i].id=("arrows"+i); 46 | arrArray[i].src = '../../static/gtm_hit/images/arrows'+i+'_3D.png'; 47 | 48 | imgArray[i] = new Image(); 49 | imgArray[i].id=(i+1); 50 | imgArray[i].onload = function() { 51 | var c = document.getElementById("canv"+this.id); 52 | var ctx = c.getContext('2d'); 53 | 54 | ctx.drawImage(this,0,0); 55 | if(toggle_orientation) 56 | drawArrows(ctx,this.id-1); 57 | c.addEventListener('click',mainClick); 58 | loadcount++; 59 | if(loadcount == 7) { 60 | $("#loader").hide(); 61 | } 62 | update(); 63 | } 64 | 65 | loadcount = 0; 66 | $("#loader").show(); 67 | imgArray[i].src = '../../static/gtm_hit/day_2/annotation_final/'+ camName[i]+ '/begin/'+frame_str+'.png'; // change 00..0 by a frame variable 68 | //imgArray[i].src = '../../static/gtm_hit/frames/'+ camName[i]+frame_str+'.png'; // change 00..0 by a frame variable 69 | } 70 | 71 | $(document).bind('keydown', "backspace",backSpace); 72 | $(document).bind('keydown', "left",left); 73 | $(document).bind('keydown', "right",right); 74 | $(document).bind('keydown', "up",up); 75 | $(document).bind('keydown', "down",down); 76 | $(document).bind('keydown', "tab",tab); 77 | $(document).bind('keydown', "space",space); 78 | $(document).bind( "keydown", "e",validate); 79 | $(document).bind( "keydown", "z",zoomControl); 80 | $(document).bind( "keydown", "t",toggleGround); 81 | $(document).bind( "keydown", "h",toggleOrientation); 82 | $("#pID").bind( "keydown", "return",changeID); 83 | $("#pID").val(-1); 84 | $("#pHeight").val(-1); 85 | $("#pWidth").val(-1); 86 | 87 | }; 88 | 89 | function mainClick(e) { 90 | var canv = this; 91 | var offset = $(this).offset(); 92 | var relativeX = (e.pageX - offset.left)-15; 93 | var relativeY = (e.pageY - offset.top); 94 | var xCorr = Math.round(relativeX*1920/(this.clientWidth-30)); 95 | var yCorr = Math.round(relativeY*1080/this.clientHeight); 96 | if(relativeX >=0 && relativeX<=(this.clientWidth - 29)) { 97 | if(zoomOn) 98 | zoomOut(); 99 | //post 100 | $.ajax({ 101 | method: "POST", 102 | url: "click", 103 | data: { 104 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 105 | x: xCorr, 106 | y: yCorr, 107 | canv: this.id 108 | }, 109 | dataType: "json", 110 | success: function(msg) { 111 | var rid = msg[0].rectangleID; 112 | var indof = rectsID.indexOf(rid); 113 | if(indof == -1) { 114 | rectsID.push(rid); 115 | chosen_rect = rectsID.length-1; 116 | while(personID in validation) 117 | personID++; 118 | identities[rid] = personID; 119 | validation[personID] = true; 120 | saveRect(msg,personID); 121 | } else { 122 | chosen_rect = indof; 123 | } 124 | update(); 125 | } 126 | }); 127 | 128 | } 129 | } 130 | 131 | function backSpace() { 132 | if (rectsID.length > 0){ 133 | var rid = rectsID[chosen_rect]; 134 | rectsID.splice(chosen_rect,1); 135 | var idPers = identities[rid]; 136 | delete validation[idPers]; 137 | delete identities[rid]; 138 | //validation_dict.pop(idPers) 139 | //identities.pop(idRect) 140 | //for i in range(NB_PICTURES): 141 | // if idPers in person_rect[i]: 142 | // person_rect[i].pop(idPers) 143 | if (chosen_rect == rectsID.length) { 144 | chosen_rect--; 145 | } 146 | if(zoomOn) { 147 | zoomOut(); 148 | } 149 | update(); 150 | } 151 | return false; 152 | } 153 | 154 | function tab() { 155 | if (rectsID.length <= 1) 156 | return false; 157 | 158 | chosen_rect++; 159 | if(zoomOn) 160 | zoomOut(); 161 | update(); 162 | return false; 163 | 164 | } 165 | 166 | function space() { 167 | if (rectsID.length <= 1) 168 | return false; 169 | 170 | chosen_rect--; 171 | if(zoomOn) 172 | zoomOut(); 173 | update(); 174 | return false; 175 | } 176 | 177 | function left() { 178 | sendAJAX("move","left",rectsID[chosen_rect],moveRect); 179 | update(); 180 | return false; 181 | } 182 | 183 | function right() { 184 | sendAJAX("move","right",rectsID[chosen_rect],moveRect); 185 | update(); 186 | return false; 187 | } 188 | 189 | function up() { 190 | sendAJAX("move","up",rectsID[chosen_rect],moveRect); 191 | update(); 192 | return false; 193 | } 194 | 195 | function down() { 196 | sendAJAX("move","down",rectsID[chosen_rect],moveRect); 197 | update() 198 | return false; 199 | } 200 | 201 | function incrWidth() { 202 | var ind = getIndx(); 203 | var pid = identities[rectsID[chosen_rect]]; 204 | var rect = boxes[ind][pid]; 205 | rect.x1 = rect.x1-1; 206 | rect.x2 = rect.x2+1; 207 | boxes[ind][pid] = rect; 208 | 209 | var size = (rect.x2-rect.x1)*rect.ratio; 210 | updateSize(false,size,ind); 211 | return false; 212 | 213 | } 214 | 215 | function decrWidth() { 216 | var ind = getIndx(); 217 | var pid = identities[rectsID[chosen_rect]]; 218 | var rect = boxes[ind][pid]; 219 | rect.x1 = rect.x1+1; 220 | if(rect.x2 - rect.x1 < 1) { 221 | rect.x1 = rect.x2 -1; 222 | } else { 223 | rect.x2 = rect.x2-1; 224 | } 225 | boxes[ind][pid] = rect; 226 | var size = (rect.x2-rect.x1)*rect.ratio; 227 | updateSize(false,size,ind); 228 | return false; 229 | 230 | } 231 | 232 | function incrHeight() { 233 | 234 | var ind = getIndx(); 235 | var pid = identities[rectsID[chosen_rect]]; 236 | var rect = boxes[ind][pid]; 237 | rect.y1 = rect.y1-1; 238 | boxes[ind][pid] = rect; 239 | 240 | var size = (rect.y2-rect.y1)*rect.ratio; 241 | updateSize(true,size,ind); 242 | return false; 243 | 244 | } 245 | 246 | function decrHeight() { 247 | var ind = getIndx(); 248 | var pid = identities[rectsID[chosen_rect]]; 249 | var rect = boxes[ind][pid]; 250 | rect.y1 = rect.y1+1; 251 | if(rect.y2 - rect.y1 < 1) { 252 | rect.y1 = rect.y2-1; 253 | } 254 | boxes[ind][pid] = rect; 255 | 256 | var size = (rect.y2-rect.y1)*rect.ratio; 257 | updateSize(true,size,ind); 258 | return false; 259 | } 260 | 261 | function getIndx() { 262 | var h = -1; 263 | var retInd = -1; 264 | var pid = identities[rectsID[chosen_rect]]; 265 | for (var i = 0; i < cameras; i++) { 266 | r = boxes[i][pid]; 267 | tpH = Math.abs(r.y1 - r.y2); 268 | 269 | if(tpH > h) { 270 | h = tpH; 271 | retInd = i; 272 | } 273 | } 274 | return retInd; 275 | } 276 | 277 | function updateSize(height,size,ind) { 278 | var r = rectsID[chosen_rect]; 279 | var pid = identities[r]; 280 | for (var i = 0; i < cameras; i++) { 281 | rect = boxes[i][pid]; 282 | if(i != ind && rect.y1 != 0) { 283 | if(height) { 284 | var b = Math.round(rect.y2 - size/rect.ratio); 285 | if(rect.y2 - b < 1) 286 | b = rect.y2 - 1; 287 | rect.y1 = b; 288 | } else { 289 | var delta = size/(2*rect.ratio); 290 | var c = Math.round(rect.xMid + delta); 291 | var a = Math.round(rect.xMid - delta); 292 | if(c - a < 1) 293 | a = c - 1; 294 | rect.x1 = a; 295 | rect.x2 = c; 296 | } 297 | } 298 | boxes[i][pid] = rect; 299 | } 300 | update() 301 | } 302 | 303 | function save() { 304 | var dims = {}; 305 | var k = 0; 306 | for (var i = 0; i < rectsID.length; i++) { 307 | var rid = rectsID[i]; 308 | var pid = identities[rid]; 309 | dims[rid] = []; 310 | dims[rid].push(pid); 311 | dims[rid].push(validation[pid]); 312 | } 313 | 314 | for (var i = 0; i < cameras; i++) { 315 | for (var j = 0; j < rectsID.length; j++) { 316 | var rid = rectsID[j]; 317 | var pid = identities[rid]; 318 | 319 | var field = boxes[i][pid]; 320 | if(field.x2 != 0){ 321 | dims[rid].push(field.x1); 322 | dims[rid].push(field.y1); 323 | dims[rid].push(field.x2); 324 | dims[rid].push(field.y2); 325 | } else { 326 | dims[rid].push(-1); 327 | dims[rid].push(-1); 328 | dims[rid].push(-1); 329 | dims[rid].push(-1); 330 | } 331 | } 332 | } 333 | $.ajax({ 334 | method: "POST", 335 | url: 'save', 336 | data: { 337 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 338 | data: JSON.stringify(dims), 339 | ID: frame_str, 340 | workerID: workerID 341 | }, 342 | success: function(msg) { 343 | console.log(msg); 344 | } 345 | }); 346 | 347 | } 348 | 349 | function load() { 350 | loader('load'); 351 | } 352 | 353 | function load_prev() { 354 | loader('loadprev'); 355 | 356 | } 357 | 358 | function loader(uri) { 359 | 360 | $.ajax({ 361 | method: "POST", 362 | url: uri, 363 | data: { 364 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 365 | ID: frame_str, 366 | workerID: workerID 367 | }, 368 | dataType: 'json', 369 | success: function(msg) { 370 | clean(); 371 | var maxID = 0; 372 | for (var i = 0; i < msg.length; i++) { 373 | var rid = msg[i][0].rectangleID; 374 | var indof = rectsID.indexOf(rid); 375 | if(indof == -1) { 376 | rectsID.push(rid); 377 | saveRectLoad(msg[i]); 378 | chosen_rect = rectsID.length-1; 379 | identities[rid] = msg[i][7]; 380 | var pid = msg[i][7]; 381 | if(pid > maxID) 382 | maxID = pid; 383 | if(uri == "loadprev") 384 | validation[pid] = false; 385 | else 386 | validation[pid] = msg[i][8]; 387 | } 388 | } 389 | personID = maxID + 1; 390 | update(); 391 | 392 | } 393 | }); 394 | } 395 | function clean() { 396 | for (var i = 0; i < cameras; i++) { 397 | boxes[i] = {}; 398 | rectsID = []; 399 | validation = {}; 400 | identities = {}; 401 | personID = 0; 402 | chosen_rect = 0; 403 | } 404 | update(); 405 | } 406 | 407 | function changeFrame(order,increment) { 408 | save(); 409 | if(nblabeled >= to_label) { 410 | return true; 411 | } 412 | $.ajax({ 413 | method: "POST", 414 | url: 'changeframe', 415 | data: { 416 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 417 | order: order, 418 | frameID: frame_str, 419 | incr: increment, 420 | workerID: workerID 421 | }, 422 | dataType: "json", 423 | success: function(msg) { 424 | frame_str = msg['frame']; 425 | nblabeled = msg['nblabeled']; 426 | if(nblabeled >= to_label) { 427 | var button = document.getElementById("changeF"); 428 | button.href="/gtm_hit/" + workerID + "/processFrame"; 429 | button.text = "Finish"; 430 | } 431 | loadcount = 0; 432 | $("#loader").show(); 433 | fstr = frame_str; 434 | fstr = fstr.replace(/^0*/, ""); 435 | $("#frameID").html("Frame ID: " + fstr +"  "); 436 | for (var i = 0; i < cameras; i++) 437 | imgArray[i].src = '../../static/gtm_hit/day_2/annotation_final/'+ camName[i]+ '/begin/'+frame_str+'.png'; // change 00..0 by a frame variable 438 | //imgArray[i].src = '../../static/gtm_hit/frames/'+ camName[i]+frame_str+'.png'; // change 00..0 by a frame variable 439 | 440 | }, 441 | complete: function() { 442 | load_prev(); 443 | } 444 | }); 445 | 446 | } 447 | 448 | function next() { 449 | changeFrame('next',1); 450 | } 451 | 452 | function prev() { 453 | changeFrame('prev',1); 454 | } 455 | 456 | function nextI() { 457 | changeFrame('next',10); 458 | } 459 | 460 | function prevI() { 461 | changeFrame('prev',10); 462 | } 463 | 464 | function validate() { 465 | var rid = rectsID[chosen_rect]; 466 | var idPers = identities[rid]; 467 | validation[idPers] = true; 468 | return false; 469 | } 470 | 471 | function changeID() { 472 | var newID = parseInt($("#pID").val()); 473 | if (rectsID.length > 0 && newID >= 0) { 474 | var rid = rectsID[chosen_rect]; 475 | var pid = identities[rid]; 476 | var match = false; 477 | for (key in identities) { 478 | if (identities[key] == newID) 479 | match = true; 480 | } 481 | if(!match) { 482 | validation[newID]= validation[pid]; 483 | delete validation[pid]; 484 | identities[rid] = newID; 485 | for(key in boxes){ 486 | if(pid in boxes[key]){ 487 | var args = boxes[key][pid]; 488 | boxes[key][newID] = args; 489 | delete boxes[key][pid]; 490 | } 491 | } 492 | $("#pID").val(newID); 493 | } else { 494 | $("#pID").val(pid); 495 | } 496 | } 497 | } 498 | 499 | function sendAJAX(uri,data,id,suc) { 500 | $.ajax({ 501 | method: "POST", 502 | url: uri, 503 | data: { 504 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 505 | data: data, 506 | ID: id 507 | }, 508 | dataType: "json", 509 | success: function(msg) { 510 | if(msg.length > 0) 511 | suc(msg,id); 512 | update(); 513 | } 514 | }); 515 | } 516 | 517 | function saveRect(msg,pid) { 518 | for (var i = 0; i < msg.length; i++) { 519 | var ind = msg[i].cameraID; 520 | boxes[ind][pid] = msg[i]; 521 | } 522 | } 523 | 524 | function saveRectLoad(msg) { 525 | for (var i = 0; i < msg.length-2; i++) { 526 | var ind = msg[i].cameraID; 527 | boxes[ind][msg[7]] = msg[i]; 528 | } 529 | } 530 | 531 | function moveRect(msg,id) { 532 | var pid = identities[id]; 533 | if(typeof boxes[0][pid] == "undefined") { 534 | return false; 535 | } 536 | var index = rectsID.indexOf(id); 537 | var nextRect = msg[0].rectangleID; 538 | rectsID.splice(index,1); 539 | rectsID.push(nextRect); 540 | chosen_rect = rectsID.length-1; 541 | identities[nextRect] = pid; 542 | delete identities[id]; 543 | validation[pid] = true; 544 | 545 | for (var i = 0; i < msg.length; i++) { 546 | var f = msg[i]; 547 | var ind = f.cameraID; 548 | var oldRect = boxes[ind][pid]; 549 | 550 | var newRect = msg[i]; 551 | var heightR = Math.abs(oldRect.y1-oldRect.y2)*oldRect.ratio; 552 | var widthR = Math.abs(oldRect.x1-oldRect.x2)*oldRect.ratio; 553 | 554 | if(newRect.ratio > 0){ 555 | newRect.y1 = Math.round(newRect.y2 - (heightR/newRect.ratio)); 556 | var delta = widthR/(2*newRect.ratio); 557 | newRect.x2 = Math.round(newRect.xMid + delta); 558 | newRect.x1 = Math.round(newRect.xMid - delta); 559 | } 560 | boxes[ind][pid] = newRect; 561 | } 562 | } 563 | 564 | function update() { 565 | chosen_rect = ((chosen_rect % rectsID.length) + rectsID.length) % rectsID.length; 566 | $("#pID").val(identities[rectsID[chosen_rect]]); 567 | 568 | drawRect(); 569 | if(toggle_ground) 570 | drawGround(); 571 | 572 | var d = document.getElementById("changeF"); 573 | if(d!=null) { 574 | if (rectsID.length > 1) { 575 | if(checkRects()) 576 | d.className = d.className.replace(" disabled",""); 577 | else if(d.className.indexOf("disabled") == -1) 578 | d.className = d.className + " disabled"; 579 | } else if(d.className.indexOf("disabled") == -1) { 580 | d.className = d.className + " disabled"; 581 | } 582 | } 583 | } 584 | 585 | function drawRect() { 586 | for (var i = 0; i < cameras; i++) { 587 | var c = document.getElementById("canv"+(i+1)); 588 | var ctx=c.getContext("2d"); 589 | ctx.clearRect(0, 0, c.width, c.height); 590 | ctx.drawImage(imgArray[i],0,0); 591 | if(toggle_orientation) 592 | drawArrows(ctx,i); 593 | } 594 | var heightR = 0; 595 | var widthR = 0; 596 | var sumH = 0; 597 | for (key in boxes) { 598 | for (var r = 0; r < rectsID.length; r++) { 599 | var field = boxes[key][identities[rectsID[r]]]; 600 | if(field.y1 != -1 && field.y2 != -1 && field.x1 != -1) { 601 | var c = document.getElementById("canv"+(field.cameraID+1)); 602 | var ctx=c.getContext("2d"); 603 | var w = field.x2 - field.x1; 604 | var h = field.y2 - field.y1; 605 | if(r == chosen_rect) { 606 | ctx.strokeStyle="cyan"; 607 | ctx.lineWidth="7"; 608 | heightR += (field.y2-field.y1)*field.ratio; 609 | widthR += (field.x2-field.x1)*field.ratio; 610 | sumH += 1; 611 | } else { 612 | var pid = identities[field.rectangleID]; 613 | if(validation[pid]) 614 | ctx.strokeStyle="white"; 615 | else 616 | ctx.strokeStyle="yellow"; 617 | ctx.lineWidth="4"; 618 | } 619 | ctx.beginPath(); 620 | ctx.rect(field.x1,field.y1,w,h); 621 | ctx.stroke(); 622 | ctx.closePath(); 623 | 624 | ctx.beginPath(); 625 | ctx.fillStyle = "red"; 626 | ctx.fillRect(field.xMid-5,field.y2-5,10,10); 627 | ctx.stroke(); 628 | ctx.closePath(); 629 | 630 | } 631 | } 632 | } 633 | if(chosen_rect >= 0) { 634 | $("#pHeight").text(Math.round(heightR/sumH)); 635 | $("#pWidth").text(Math.round(widthR/sumH)); 636 | } else { 637 | $("#pHeight").text(-1); 638 | $("#pWidth").text(-1); 639 | } 640 | 641 | } 642 | 643 | function drawGround() { 644 | for (var i = 0; i < cameras; i++) { 645 | var c = document.getElementById("canv"+(i+1)); 646 | var ctx=c.getContext("2d"); 647 | ctx.strokeStyle="chartreuse"; 648 | ctx.lineWidth="2"; 649 | ctx.beginPath(); 650 | 651 | ctx.moveTo(bounds[i][0], bounds[i][1]); 652 | for (var j = 2; j < bounds[i].length; j=j+2) { 653 | if(bounds[i][j] >= 0) { 654 | ctx.lineTo(bounds[i][j], bounds[i][j+1]); 655 | } 656 | } 657 | ctx.stroke(); 658 | ctx.closePath(); 659 | 660 | } 661 | } 662 | 663 | function drawArrows(ctx, idx) { 664 | ctx.drawImage(arrArray[idx],0,0); 665 | } 666 | 667 | function zoomControl() { 668 | if(rectsID.length > 0){ 669 | if(!zoomOn) { 670 | zoomIn(); 671 | } else { 672 | zoomOut(); 673 | } 674 | 675 | } 676 | update(); 677 | } 678 | 679 | function zoomIn() { 680 | for (var i = 0; i < cameras; i++) { 681 | var pid = identities[rectsID[chosen_rect]]; 682 | var r = boxes[i][pid]; 683 | 684 | var c = document.getElementById("canv"+(i+1)); 685 | 686 | zoomratio[i] = c.height*60/(100*(r.y2-r.y1)); 687 | if(zoomratio[i] != Infinity) { 688 | 689 | var ctx = c.getContext('2d'); 690 | c.width=c.width/zoomratio[i]; 691 | c.height=c.height/zoomratio[i]; 692 | var originx = r.xMid - c.width/2; 693 | // var originx = r.xMid; 694 | var originy = r.y1-12.5*c.clientHeight/100; 695 | // ctx.scale(1.75,1.75); 696 | ctx.translate(-originx, -originy); 697 | 698 | } 699 | } 700 | zoomOn = true; 701 | return false; 702 | 703 | } 704 | 705 | function zoomOut() { 706 | for (var i = 0; i < cameras; i++) { 707 | var c = document.getElementById("canv"+(i+1)); 708 | if(zoomratio[i] != Infinity) { 709 | c.width=c.width*zoomratio[i]; 710 | c.height=c.height*zoomratio[i]; 711 | } 712 | } 713 | zoomOn = false; 714 | return false; 715 | } 716 | 717 | function toggleGround() { 718 | if(toggle_ground == false) 719 | toggle_ground = true; 720 | else 721 | toggle_ground=false; 722 | update(); 723 | return false; 724 | } 725 | 726 | function toggleOrientation() { 727 | if(toggle_orientation == false) 728 | toggle_orientation = true; 729 | else 730 | toggle_orientation=false; 731 | update(); 732 | return false; 733 | } 734 | 735 | function checkRects() { 736 | var c = 0; 737 | for (var i = 0; i < rectsID.length; i++) { 738 | var personID = identities[rectsID[i]]; 739 | if(validation[personID]) 740 | c++; 741 | } 742 | if(c > 1) 743 | return true; 744 | else 745 | return false; 746 | } 747 | -------------------------------------------------------------------------------- /gtm_hit/static/gtm_hit/tuto.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Start Bootstrap - Landing Page (http://startbootstrap.com/) 3 | * Copyright 2013-2016 Start Bootstrap 4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | 7 | body, 8 | html { 9 | width: 100%; 10 | height: 100%; 11 | } 12 | 13 | body { 14 | font-family: "Lato","Helvetica Neue",Arial,sans-serif; 15 | font-weight: 500; 16 | } 17 | h1, 18 | h2, 19 | h3, 20 | h4, 21 | h5, 22 | h6 { 23 | font-family: "Lato","Helvetica Neue",Arial,sans-serif; 24 | font-weight: 600; 25 | } 26 | 27 | .nav-tabs > li, .nav-pills > li { 28 | float:none; 29 | display:inline-block; 30 | } 31 | 32 | .nav-tabs, .nav-pills { 33 | text-align:center; 34 | } 35 | .intro-header { 36 | text-align: center; 37 | } 38 | 39 | .intro-message { 40 | position: relative; 41 | padding-top: 5%; 42 | padding-bottom: 2%; 43 | } 44 | 45 | .intro-message > h1 { 46 | margin: 0; 47 | font-size: 5em; 48 | } 49 | 50 | .intro-divider { 51 | width: 400px; 52 | border-top: 1px solid #f8f8f8; 53 | border-bottom: 1px solid rgba(0,0,0,0.2); 54 | } 55 | 56 | .button-name { 57 | text-transform: uppercase; 58 | font-size: 14px; 59 | font-weight: 400; 60 | letter-spacing: 2px; 61 | } 62 | 63 | .footer { 64 | position: relative; 65 | bottom: 0; 66 | width: 100%; 67 | } 68 | 69 | .top5 { margin-top:5px; } 70 | .top7 { margin-top:7px; } 71 | .top10 { margin-top:10px; } 72 | .top15 { margin-top:15px; } 73 | .top17 { margin-top:17px; } 74 | .top30 { margin-top:30px; } 75 | 76 | .big { 77 | line-height: 150%; 78 | } 79 | 80 | #my-row { 81 | display: table; 82 | } 83 | 84 | #my-row .panel { 85 | float: none; 86 | display: table-cell; 87 | vertical-align: top; 88 | } 89 | -------------------------------------------------------------------------------- /gtm_hit/templates/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/templates/.DS_Store -------------------------------------------------------------------------------- /gtm_hit/templates/gtm_hit/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/templates/gtm_hit/.DS_Store -------------------------------------------------------------------------------- /gtm_hit/templates/gtm_hit/finish.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 31 | 32 | 33 | {% include "includes_hit/navbar.html" %} 34 | {% csrf_token %} 35 |
36 |
37 |
38 |
39 |
40 |

Ground truth marker

41 |
42 |

Thank you for your participation!

43 |

We hope you enjoyed the task

44 |

Please copy the following validation code and paste it in the key field of the HIT page

45 | 46 |
47 |

Validation code: {{validation_code}}

48 |
49 |
50 |
51 | 52 |
53 | 54 |
55 | {% include "includes_hit/footer.html" %} 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /gtm_hit/templates/gtm_hit/frame.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 23 | 24 | 25 | {% include "includes_hit/navbar.html" %} 26 | 27 | {% csrf_token %} 28 | 29 |
30 | 41 |
42 | 43 | 48 | 53 |
54 |
55 | 60 |
61 |
62 | {% include "includes_hit/footer.html" %} 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /gtm_hit/templates/gtm_hit/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | {% include "includes_hit/navbar.html" %} 18 | {% csrf_token %} 19 |
20 |
21 |
22 |
23 |
24 |

Ground truth marker

25 |
26 |

Task description

27 |

Hi fellow worker and welcome on our ground truth marker framework!

28 |

The task we ask you to complete will be very simple: putting bounding boxes around people while being as accurate as possible.

29 |

Please read carefully the tutorial before starting since it contains everything you need to know.

30 |
31 | 36 |
37 |
38 |
39 | 40 |
41 | 42 |
43 | {% include "includes_hit/footer.html" %} 44 | 45 | {% include "includes_hit/help.html" %} 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /gtm_hit/templates/gtm_hit/requestID.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% include "includes_hit/navbar.html" %} 14 | {% csrf_token %} 15 |
16 |
17 |
18 |
19 |
20 |

Ground truth marker

21 |
22 |

No Worker ID detected

23 |

Please enter your amazon mechanical turk worker ID. This will allow us to pay you after the completion of the HIT

24 |

If you do not come from amazon mechanical turk, enter any ID you want

25 |
26 |
27 | {% csrf_token %} 28 |
29 | 30 |
31 |
32 | 33 |
34 |

Your worker ID should only contain CAPITAL letters and numbers.

35 | 36 | 37 |
38 |
39 |
40 | 41 |
42 | 43 |
44 | {% include "includes_hit/footer.html" %} 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /gtm_hit/templates/gtm_hit/tuto.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 56 | 57 | 58 | {% include "includes_hit/navbar.html" %} 59 | {% csrf_token %} 60 |
61 |
62 |
63 |
64 |
65 |

Tutorial

66 |
67 | 73 |
74 |
75 |

Your task

76 |

As previously mentionned, you will be asked to label images, i.e. to place bounding boxes around people.

You will see 7 images at the same time which corresponds to multiple views of a scene. 77 | Try to put bounding boxes around all the people you can (some of them are not in the ground area, don't worry about them). 78 |

After having labeled a frame, the bounding boxes you placed will stay on the next frame and turn yellow. 79 | Since the people do not move a lot between two consecutive frames, you will only have to switch between the bounding boxes ( with tab ) and adjust their position using the arrow keys. 80 |
The task will end once you have labeled 10 frames. 81 |

Use your left click to place a bounding box on an image. The bottom of the bounding box will be centered on the point you click on, so aim at the feets. 82 |

The red dot at the bottom of the bouding box corresponds to a point on the ground. To correctly place a bounding box, this point should be placed at the center of the person, on the ground. (See Example 1) 83 |

Use the arrow keys to move a bounding box on the images. Each view has a different orientation. Look at the small keys on the top left of each image to know in which direction goes each arrow. (See Example 2) 84 |

Take a look at all the tutorial tabs before being allowed to start. The second example contains and summarizes everything you need to know

85 | Note: Since accuracy is very important, do not hesitate to use the z key to zoom on the selected bounding box. 86 |
87 | 88 |
89 |
90 |

Left click (on an image) : place a bounding box.

91 |

Arrows : move the selected bounding box.

92 |

z : zoom on the selected bounding box (press again to zoom out).

93 |

Backspace : remove the selected bounding box.

94 |

Space / Tab : select the next/previous the bounding box.

95 |

e : validate the placement of a yellow bounding box.

96 |

h : hide/show the orientation helper.

97 |

t : hide/show the ground area limits.

98 |

Cyan box : selected bounding box

99 |

White box : standard bounding box

100 |

Yellow box : Unvalidated bounding box

101 |

The bounding box should surround the person on all images it appears. (see Example 1 tab)

102 |
103 | 104 |
105 |
106 |

The bounding box should surround the person on all images it appears.

107 | 108 |

Good

109 |

All the bounding boxes sourround the lady

110 | 111 |

Bad

112 |

The box seems correctly placed on the top right image, but not in the other images. 113 | In this case, you have to adjust the position using the arrows to obtain the results seen on the "Good" example

114 | 115 | 116 |
117 | 118 |
119 |
120 |

The bounding box should surround the person on all images it appears.

121 | 122 |

Note that the orientation helper and the blue arrows have another orientation on every frame since the angle changes.

123 | 124 |
125 | 126 | 127 |
128 | 129 |
130 |
    131 |
  • 132 | Start 133 |
  • 134 |
135 |
136 |
137 |
138 | 139 |
140 | 141 |
142 | {% include "includes_hit/footer.html" %} 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /gtm_hit/templates/includes_hit/controller.html: -------------------------------------------------------------------------------- 1 | {% load marker_extra%} 2 | 3 | 4 |
5 |
6 |

Selection

7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 |

Position

20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 |
30 |
31 | 53 | 54 |
55 | -------------------------------------------------------------------------------- /gtm_hit/templates/includes_hit/footer.html: -------------------------------------------------------------------------------- 1 | 8 | 12 | -------------------------------------------------------------------------------- /gtm_hit/templates/includes_hit/help.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 83 | -------------------------------------------------------------------------------- /gtm_hit/templates/includes_hit/navbar.html: -------------------------------------------------------------------------------- 1 | 17 | {% include "includes_hit/help.html" %} 18 | -------------------------------------------------------------------------------- /gtm_hit/templates/includes_hit/side_menu.html: -------------------------------------------------------------------------------- 1 | {% load gtm_hit_extra%} 2 | 3 | 4 |
5 |

Infos

6 |
7 | 8 |
-1 9 |
10 |
11 |
12 | 13 |
-1 14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /gtm_hit/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtm_hit/templatetags/__init__.py -------------------------------------------------------------------------------- /gtm_hit/templatetags/gtm_hit_extra.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from marker.models import * 3 | from django.utils import timezone 4 | import json 5 | from django.core import serializers 6 | 7 | register = template.Library() 8 | 9 | @register.filter(name='next') 10 | def next(value): 11 | return int(value)+1 12 | 13 | @register.filter(name='prev') 14 | def prev(value): 15 | f = int(value) 16 | if f > 0: 17 | return f-1 18 | else: 19 | return f 20 | 21 | @register.filter(name='toJSON') 22 | def toJSON(value): 23 | return serializers.serialize("json",value) 24 | -------------------------------------------------------------------------------- /gtm_hit/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /gtm_hit/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url(r'^&MID=(?P[A-Z0-9]+)$', views.index, name="index"), 7 | url(r'^$', views.requestID, name="requestID"), 8 | url(r'^(?P[A-Z0-9]+)/processInit$', views.processInit, name="processInit"), 9 | url(r'^(?P[A-Z0-9]+)/processIndex$', views.processIndex, name="processIndex"), 10 | url(r'^(?P[A-Z0-9]+)/processTuto$', views.processTuto, name="processTuto"), 11 | url(r'^(?P[A-Z0-9]+)$', views.dispatch, name="dispatch"), 12 | url(r'^(?P[A-Z0-9]+)/index$', views.index, name="index"), 13 | url(r'^(?P[A-Z0-9]+)/tuto$', views.tuto, name="tuto"), 14 | url(r'^(?P[A-Z0-9]+)/frame$', views.frame, name='frame'), 15 | url(r'^(?P[A-Z0-9]+)/processFrame$', views.processFrame, name="processFrame"), 16 | url(r'^.*click',views.click,name="click"), 17 | url(r'^.*move',views.move,name="move"), 18 | url(r'^.*changeframe$', views.changeframe, name='changeframe'), 19 | url(r'^.*save$',views.save,name='save'), 20 | url(r'^.*load$',views.load,name='load'), 21 | url(r'^.*loadprev$',views.load_previous,name='loadprev'), 22 | url(r'^.*processFinish$',views.processFinish,name='processFinish'), 23 | 24 | url(r'^(?P[A-Z0-9]+)/finish$',views.finish,name='finish'), 25 | ] 26 | -------------------------------------------------------------------------------- /gtm_hit/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.shortcuts import get_object_or_404,render, redirect 3 | from django.contrib.auth import authenticate, login, logout 4 | from django.template.loader import render_to_string 5 | from django.contrib.auth.decorators import login_required 6 | from django.http import HttpResponseRedirect, HttpResponse 7 | from django.core import serializers 8 | from django.core.urlresolvers import reverse 9 | from django.views import generic 10 | from django.utils import timezone 11 | from django.conf import settings 12 | from .models import Worker, ValidationCode 13 | from django.template import RequestContext 14 | import re 15 | import json 16 | import os 17 | import random as rand 18 | from threading import Thread 19 | 20 | def requestID(request): 21 | context = RequestContext(request) 22 | if request.method == "POST": 23 | if 'wID' in request.POST: 24 | workerID = request.POST['wID'] 25 | pattern = re.compile("^[A-Z0-9]+$") 26 | if pattern.match(workerID): 27 | return redirect("/gtm_hit/"+workerID+"/processInit") 28 | return render(request, 'gtm_hit/requestID.html',{},context) 29 | 30 | def processInit(request, workerID): 31 | context = RequestContext(request) 32 | try: 33 | w = Worker.objects.get(pk = workerID) 34 | if w.state == -1: 35 | w.state = 0 36 | w.save() 37 | return redirect("/gtm_hit/"+workerID) 38 | except Worker.DoesNotExist: 39 | return redirect("/gtm_hit/"+workerID) 40 | 41 | def index(request,workerID): 42 | context = RequestContext(request) 43 | try: 44 | w = Worker.objects.get(pk = workerID) 45 | if w.state != 0: 46 | return redirect("/gtm_hit/"+workerID) 47 | return render(request, 'gtm_hit/index.html',{'workerID' : workerID},context) 48 | 49 | except Worker.DoesNotExist: 50 | return redirect("/gtm_hit/"+workerID) 51 | 52 | def processIndex(request, workerID): 53 | context = RequestContext(request) 54 | try: 55 | w = Worker.objects.get(pk = workerID) 56 | if w.state == 0: 57 | w.state = 3 58 | w.save() 59 | except Worker.DoesNotExist: 60 | return redirect("/gtm_hit/"+workerID) 61 | return redirect("/gtm_hit/"+workerID) 62 | 63 | def dispatch(request,workerID): 64 | context = RequestContext(request) 65 | try: 66 | w = Worker.objects.get(pk = workerID) 67 | try: 68 | code = ValidationCode.objects.get(worker_id = w) 69 | stop = False 70 | i = 2 71 | while not stop: 72 | try: 73 | w2 = Worker.objects.get(pk = workerID+str(i)) 74 | c2 = ValidationCode.objects.get(worker_id = w2) 75 | i = i + 1 76 | except Worker.DoesNotExist: 77 | stop = True 78 | except ValidationCode.DoesNotExist: 79 | return redirect("/gtm_hit/"+workerID+str(i)) 80 | return redirect("/gtm_hit/"+workerID+str(i)) 81 | except ValidationCode.DoesNotExist: 82 | pass 83 | except Worker.DoesNotExist: 84 | w = registerWorker(workerID) 85 | 86 | state = w.state 87 | if state == 0: 88 | return redirect(workerID+'/index') 89 | #return render(request, 'gtm_hit/frame.html',{'frame_number': frame_number, 'workerID' : workerID},context) 90 | elif state == 1: 91 | return redirect(workerID+'/frame') 92 | # return render(request, 'gtm_hit/finish.html',{'workerID' : workerID, 'validation_code' : validation_code},context) 93 | elif state == 2: 94 | return redirect(workerID+'/finish') 95 | # return render(request, 'gtm_hit/finish.html',{'workerID' : workerID, 'validation_code' : validation_code},context) 96 | elif state == 3: 97 | return redirect(workerID+'/tuto') 98 | elif state == -1: 99 | return redirect(workerID+'/processInit') 100 | #return render(request, 'gtm_hit/index.html',{'workerID' : workerID},context) 101 | else: 102 | return redirect(workerID+'/index') 103 | #return render(request, 'gtm_hit/index.html',{'workerID' : workerID},context) 104 | 105 | def frame(request,workerID): 106 | context = RequestContext(request) 107 | try: 108 | w = Worker.objects.get(pk = workerID) 109 | if w.state != 1: 110 | return redirect("/gtm_hit/"+workerID) 111 | if w.frameNB < 0: 112 | w.frameNB = settings.STARTFRAME 113 | w.save() 114 | frame_number = w.frameNB 115 | nblabeled = w.frame_labeled 116 | return render(request, 'gtm_hit/frame.html',{'frame_number': frame_number, 'workerID': workerID,'cams': settings.CAMS, 'path': settings.SERVER_PATH, 'nblabeled' : nblabeled},context) 117 | except Worker.DoesNotExist: 118 | return redirect("/gtm_hit/"+workerID) 119 | 120 | def processFrame(request,workerID): 121 | context = RequestContext(request) 122 | try: 123 | w = Worker.objects.get(pk = workerID) 124 | if w.state == 1 and w.frame_labeled >= 9: 125 | w.state = 2 126 | timelist = w.getTimeList() 127 | timelist.append(timezone.now().isoformat()) 128 | w.setTimeList(timelist) 129 | w.save() 130 | return redirect("/gtm_hit/"+workerID) 131 | except Worker.DoesNotExist: 132 | return redirect("/gtm_hit/"+workerID) 133 | 134 | def finish(request,workerID): 135 | context = RequestContext(request) 136 | try: 137 | w = Worker.objects.get(pk = workerID) 138 | if w.state == 2: 139 | validation_code = generate_code(w) 140 | startframe = w.frameNB - (w.frame_labeled*5) 141 | try: 142 | settings.UNLABELED.remove(startframe) 143 | except ValueError: 144 | pass 145 | return render(request, 'gtm_hit/finish.html',{'workerID': workerID, 'validation_code': validation_code},context) 146 | except Worker.DoesNotExist: 147 | return redirect("/gtm_hit/"+workerID) 148 | return redirect("/gtm_hit/"+workerID) 149 | 150 | def click(request): 151 | if request.is_ajax(): 152 | try: 153 | x = int(request.POST['x']) 154 | y = int(request.POST['y']) 155 | cam = request.POST['canv'] 156 | cam = int(re.findall('\d+',cam)[0]) - 1 157 | if 0 <= cam < settings.NB_CAMS: 158 | closest = -1 159 | vbest = 1000 160 | for y_temp in range(y - settings.DELTA_SEARCH, y + settings.DELTA_SEARCH): 161 | if 0 <= y_temp < 1080: 162 | for x_temp in settings.FIND_RECT[cam][y_temp]: 163 | vtemp = abs(y - y_temp) + abs(x - x_temp) 164 | if vtemp < vbest: 165 | vbest, closest = vtemp, settings.FIND_RECT[cam][y_temp][x_temp] 166 | if closest != -1: 167 | rects = get_rect(closest) 168 | rect_json = json.dumps(rects) 169 | return HttpResponse(rect_json,content_type="application/json") 170 | 171 | return HttpResponse("OK") 172 | except KeyError: 173 | return HttpResponse("Error") 174 | return HttpResponse("No") 175 | 176 | def move(request): 177 | if request.is_ajax(): 178 | try: 179 | if request.POST['data'] == "down": 180 | rectID = request.POST['ID'] 181 | if int(rectID) // settings.NB_WIDTH > 0: 182 | nextID = int(rectID) - settings.NB_WIDTH 183 | else: 184 | return HttpResponse(json.dumps([]),content_type="application/json") 185 | 186 | elif request.POST['data'] == "up": 187 | rectID = request.POST['ID'] 188 | if int(rectID) // settings.NB_WIDTH < settings.NB_HEIGHT - 1: 189 | nextID = int(rectID) + settings.NB_WIDTH 190 | else: 191 | return HttpResponse(json.dumps([]),content_type="application/json") 192 | 193 | elif request.POST['data'] == "right": 194 | rectID = request.POST['ID'] 195 | if int(rectID) % settings.NB_WIDTH < settings.NB_WIDTH - 1: 196 | nextID = int(rectID) + 1 197 | else: 198 | return HttpResponse(json.dumps([]),content_type="application/json") 199 | 200 | elif request.POST['data'] == "left": 201 | rectID = request.POST['ID'] 202 | if int(rectID) % settings.NB_WIDTH > 0: 203 | nextID = int(rectID) - 1 204 | else: 205 | return HttpResponse(json.dumps([]),content_type="application/json") 206 | 207 | else: 208 | return HttpResponse("Error") 209 | next_rect = get_rect(nextID) 210 | next_rect_json = json.dumps(next_rect) 211 | return HttpResponse(next_rect_json,content_type="application/json") 212 | 213 | except KeyError: 214 | return HttpResponse("Error") 215 | return HttpResponse("Error") 216 | 217 | def save(request): 218 | if request.is_ajax(): 219 | try: 220 | data = json.loads(request.POST['data']) 221 | frameID = request.POST['ID'] 222 | wid = request.POST['workerID'] 223 | annotations = [] 224 | cols = ["rectID","personID","modified","a1","b1","c1","d1","a2","b2","c2","d2","a3","b3","c3","d3","a4","b4","c4","d4","a5","b5","c5","d5","a6","b6","c6","d6","a7","b7","c7","d7"] 225 | annotations.append(cols) 226 | for r in data: 227 | row = data[r] 228 | row.insert(0,int(r)) 229 | annotations.append(row) 230 | with open("gtm_hit/labels/"+ wid + "_" + frameID + '.json', 'w') as outFile: 231 | json.dump(annotations, outFile, sort_keys=True, indent=4, separators=(',', ': ')) 232 | with open("gtm_hit/static/gtm_hit/day_2/annotation_final/labels/"+ wid + "_" + frameID + '.json', 'w') as outFile: 233 | json.dump(annotations, outFile, sort_keys=True, indent=4, separators=(',', ': ')) 234 | return HttpResponse("Saved") 235 | except KeyError: 236 | return HttpResponse("Error") 237 | else: 238 | return("Error") 239 | 240 | def load(request): 241 | if request.is_ajax(): 242 | try: 243 | frameID = request.POST['ID'] 244 | wid = request.POST['workerID'] 245 | rect_json = read_save(frameID,wid) 246 | return HttpResponse(rect_json,content_type="application/json") 247 | except (FileNotFoundError, KeyError): 248 | return HttpResponse("Error") 249 | return HttpResponse("Error") 250 | 251 | def load_previous(request): 252 | if request.is_ajax(): 253 | try: 254 | 255 | frameID = request.POST['ID'] 256 | wid = request.POST['workerID'] 257 | current_frame = int(frameID) 258 | closest = float('inf') 259 | diff = float('inf') 260 | 261 | for f in os.listdir("gtm_hit/labels/"): 262 | if f.endswith(".json"): 263 | nb_frame = int((f.split('.')[0]).split('_')[1]) 264 | if nb_frame < current_frame: 265 | if current_frame - nb_frame < diff: 266 | diff = current_frame - nb_frame 267 | closest = nb_frame 268 | if closest != float('inf'): 269 | frame = "0" * (8 - len(str(closest))) + str(closest) 270 | rect_json = read_save(frame,wid) 271 | return HttpResponse(rect_json,content_type="application/json") 272 | except (FileNotFoundError, KeyError): 273 | return HttpResponse("Error") 274 | return HttpResponse("Error") 275 | 276 | def read_save(frameID,workerID): 277 | with open("gtm_hit/labels/"+ workerID + "_" + frameID + '.json','r') as loadFile: 278 | annotations = json.load(loadFile) 279 | rects = [] 280 | for i in annotations[1:]: 281 | r = get_rect(i[0]) 282 | for j in range(settings.NB_CAMS): 283 | r[j]['x1'] = i[j*4+3] 284 | r[j]['y1'] = i[j*4+4] 285 | r[j]['x2'] = i[j*4+5] 286 | r[j]['y2'] = i[j*4+6] 287 | r.append(i[1]) 288 | r.append(i[2]) 289 | rects.append(r) 290 | return json.dumps(rects) 291 | 292 | def changeframe(request): 293 | context = RequestContext(request) 294 | if request.is_ajax(): 295 | frame = 0 296 | try: 297 | wID = request.POST['workerID'] 298 | order = request.POST['order'] 299 | frame_number = request.POST['frameID'] 300 | increment = request.POST['incr'] 301 | 302 | worker = Worker.objects.get(pk = wID) 303 | worker.increaseFrame(1) 304 | worker.save() 305 | timelist = worker.getTimeList() 306 | timelist.append(timezone.now().isoformat()) 307 | worker.setTimeList(timelist) 308 | #validation_code = generate_code() 309 | #return render(request, 'gtm_hit/finish.html',{'workerID' : wID, 'validation_code': validation_code},context) 310 | if order == "next": 311 | frame = int(frame_number) + int(increment) 312 | elif order == "prev" and (int(frame_number) - int(increment)) >= 0: 313 | frame = int(frame_number) - int(increment) 314 | else: 315 | return HttpResponse("Requested frame not existing") 316 | frame = "0" * (8 - len(str(frame))) + str(frame) 317 | response = {} 318 | response['frame'] = frame 319 | response['nblabeled'] = worker.frame_labeled 320 | worker.frameNB = frame 321 | worker.save() 322 | return HttpResponse(json.dumps(response)) 323 | except KeyError: 324 | return HttpResponse("Error") 325 | else: 326 | return HttpResponse("Error") 327 | 328 | def get_rect(closest): 329 | rects = [] 330 | for i in range(settings.NB_CAMS): 331 | rdic = {} 332 | rdic['rectangleID'] = closest 333 | if closest in settings.RECT[i]: 334 | a,b,c,d,ratio = settings.RECT[i][closest] 335 | else: 336 | a,b,c,d,ratio = 0,0,0,0,0 337 | rdic['x1'] = a 338 | rdic['y1'] = b 339 | rdic['x2'] = c 340 | rdic['y2'] = d 341 | rdic['cameraID'] = i 342 | rdic['ratio'] = ratio 343 | rdic['xMid'] = (a + c) // 2 344 | rects.append(rdic) 345 | return rects 346 | 347 | def registerWorker(workerID): 348 | w = Worker() 349 | w.workerID = workerID 350 | w.frameNB = settings.STARTFRAME % settings.NBFRAMES 351 | settings.STARTFRAME = settings.STARTFRAME + 50 352 | w.save() 353 | return w 354 | 355 | def updateWorker(workerID, state): 356 | w = Worker.objects.get(pk = workerID) 357 | 358 | def generate_code(worker): 359 | try: 360 | code = ValidationCode.objects.get(worker_id = worker) 361 | except ValidationCode.DoesNotExist: 362 | random_code = int(16777215 * rand.random()) 363 | random_code = "{0:0>8}".format(random_code) 364 | while(random_code in settings.VALIDATIONCODES): 365 | random_code = int(16777215 * rand.random()) 366 | random_code = "{0:0>8}".format(random_code) 367 | settings.VALIDATIONCODES.append(random_code) 368 | code = ValidationCode() 369 | code.validationCode = random_code 370 | code.worker = worker 371 | code.save() 372 | return code.validationCode 373 | 374 | def tuto(request,workerID): 375 | context = RequestContext(request) 376 | try: 377 | w = Worker.objects.get(pk = workerID) 378 | if w.state != 3: 379 | return redirect("/gtm_hit/"+workerID) 380 | return render(request, 'gtm_hit/tuto.html',{'workerID' : workerID},context) 381 | 382 | except Worker.DoesNotExist: 383 | return redirect("/gtm_hit/"+workerID) 384 | 385 | def processTuto(request, workerID): 386 | context = RequestContext(request) 387 | try: 388 | w = Worker.objects.get(pk = workerID) 389 | if w.state == 3: 390 | w.state = 1 391 | timelist = [timezone.now().isoformat()] 392 | w.setTimeList(timelist) 393 | w.save() 394 | except Worker.DoesNotExist: 395 | return redirect("/gtm_hit/"+workerID) 396 | return redirect("/gtm_hit/"+workerID) 397 | 398 | def processFinish(request): 399 | context = RequestContext(request) 400 | if request.is_ajax(): 401 | try: 402 | wID = request.POST['workerID'] 403 | 404 | w = Worker.objects.get(pk = wID) 405 | startframe = w.frameNB - w.frame_labeled 406 | #delete_and_load(startframe) 407 | return HttpResponse("ok") 408 | except KeyError: 409 | return HttpResponse("Error") 410 | else: 411 | return HttpResponse("Error") 412 | 413 | 414 | 415 | def delete_and_load(startframe): 416 | toload = settings.LASTLOADED + 10 417 | #1. remove frames 418 | sframe = startframe 419 | #2. copy next frames 420 | for i in range(10): 421 | rm_frame = "0" * (8 - len(str(sframe))) + str(sframe) 422 | cp_frame = "0" * (8 - len(str(toload))) + str(toload) 423 | sframe = sframe + 1 424 | toload = toload + 1 425 | for j in range(settings.NB_CAMS): 426 | command = os.system("rm gtm_hit/static/gtm_hit/frames/"+ settings.CAMS[j] + "/" + rm_frame + ".png") 427 | command = os.system("cp gtm_hit/static/gtm_hit/day_2/annotation_final/"+ settings.CAMS[j] + "/begin/" + cp_frame + ".png gtm_hit/static/gtm_hit/frames/"+ settings.CAMS[j] + "/") 428 | 429 | settings.LASTLOADED = settings.LASTLOADED + 10 430 | -------------------------------------------------------------------------------- /gtmarker/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtmarker/.DS_Store -------------------------------------------------------------------------------- /gtmarker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtmarker/__init__.py -------------------------------------------------------------------------------- /gtmarker/dump.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/gtmarker/dump.rdb -------------------------------------------------------------------------------- /gtmarker/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for gtmarker project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.10.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.10/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '%z1g%^3%nf-k3sf$i^qra_d*0m4745c57f&(su(2=&nuwt#=z1' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = False 27 | 28 | #ALLOWED_HOSTS = ['127.0.0.1'] 29 | ALLOWED_HOSTS = ['pedestriantag.epfl.ch'] 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'marker.apps.MarkerConfig', 35 | 'gtm_hit.apps.Gtm_hitConfig', 36 | 'home', 37 | 'django.contrib.admin', 38 | 'django.contrib.auth', 39 | 'django.contrib.contenttypes', 40 | 'django.contrib.sessions', 41 | 'django.contrib.messages', 42 | 'django.contrib.staticfiles', 43 | 'bootstrapform', 44 | ] 45 | 46 | MIDDLEWARE_CLASSES = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | 55 | 'marker.middleware.RequireLoginMiddleware', 56 | ] 57 | LOGIN_REQUIRED_URLS = ( 58 | r'/marker/(.*)$', 59 | ) 60 | LOGIN_REQUIRED_URLS_EXCEPTIONS = ( 61 | r'/marker/login(.*)$', 62 | r'/marker/logout(.*)$', 63 | ) 64 | 65 | LOGIN_URL = '/login/' 66 | 67 | ROOT_URLCONF = 'gtmarker.urls' 68 | 69 | TEMPLATES = [ 70 | { 71 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 72 | 'DIRS': [], 73 | 'APP_DIRS': True, 74 | 'OPTIONS': { 75 | 'context_processors': [ 76 | 'django.template.context_processors.debug', 77 | 'django.template.context_processors.request', 78 | 'django.contrib.auth.context_processors.auth', 79 | 'django.contrib.messages.context_processors.messages', 80 | ], 81 | }, 82 | }, 83 | ] 84 | 85 | WSGI_APPLICATION = 'gtmarker.wsgi.application' 86 | 87 | 88 | # Database 89 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases 90 | 91 | # DATABASES = { 92 | # 'default': { 93 | # 'ENGINE': 'django.db.backends.sqlite3', 94 | # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 95 | # } 96 | # } 97 | # 98 | # DATABASES = { 99 | # 'default': { 100 | # 'ENGINE': 'django.db.backends.postgresql_psycopg2', 101 | # 'NAME': 'gtmarker', 102 | # 'USER': 'admin', 103 | # 'PASSWORD': '', 104 | # 'HOST': 'localhost', 105 | # 'PORT': '', 106 | # } 107 | # } 108 | 109 | DATABASES = { 110 | 'default': { 111 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 112 | 'NAME': 'pedestriantag', 113 | 'USER': 'pedestriantag', 114 | 'PASSWORD': 'lAzyLift96', 115 | 'HOST': 'localhost', 116 | 'PORT': '', 117 | } 118 | } 119 | 120 | # Password validation 121 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators 122 | 123 | AUTH_PASSWORD_VALIDATORS = [ 124 | { 125 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 126 | }, 127 | { 128 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 129 | }, 130 | { 131 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 132 | }, 133 | { 134 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 135 | }, 136 | ] 137 | 138 | 139 | # Internationalization 140 | # https://docs.djangoproject.com/en/1.10/topics/i18n/ 141 | 142 | LANGUAGE_CODE = 'en-us' 143 | 144 | TIME_ZONE = 'UTC' 145 | 146 | USE_I18N = True 147 | 148 | USE_L10N = True 149 | 150 | USE_TZ = True 151 | 152 | 153 | # Static files (CSS, JavaScript, Images) 154 | # https://docs.djangoproject.com/en/1.10/howto/static-files/ 155 | 156 | STATIC_URL = '/static/' 157 | 158 | SAVES = '/labels/' 159 | 160 | # Constants 161 | # 162 | # f_rect = open('./marker/static/marker/cst.txt', 'r') 163 | # lines = f_rect.readlines() 164 | # f_rect.close() 165 | # NB_WIDTH = int(lines[2].split()[1]) 166 | # NB_HEIGHT = int(lines[3].split()[1]) 167 | # NB_RECT = NB_WIDTH * NB_HEIGHT 168 | # MAN_RAY = float(lines[4].split()[1]) 169 | # MAN_HEIGHT = float(lines[5].split()[1]) 170 | # REDUCTION = float(lines[6].split()[1]) 171 | # NB_CAMS = int(lines[9].split()[1]) 172 | 173 | DELTA_SEARCH = 5 174 | 175 | #TEMPLATES[0]['OPTIONS']['context_processors'].append("marker.context_processors.rectangles_processor") 176 | 177 | try: 178 | rectangles_file = './marker/static/marker/rectangles.pom'#480x1440.pom' 179 | f_rect = open(rectangles_file, 'r') 180 | lines = f_rect.readlines() 181 | f_rect.close() 182 | if lines[0].split()[0] != "WIDTH": 183 | messagebox.showerror("Error","Incorrect file header") 184 | else: 185 | NB_WIDTH = int(lines[2].split()[1]) 186 | NB_HEIGHT = int(lines[3].split()[1]) 187 | NB_RECT = NB_WIDTH * NB_HEIGHT 188 | MAN_RAY = float(lines[4].split()[1]) 189 | MAN_HEIGHT = float(lines[5].split()[1]) 190 | REDUCTION = float(lines[6].split()[1]) 191 | NB_CAMS = int(lines[9].split()[1]) 192 | incr = 0 193 | test = [] 194 | FIND_RECT = [[{} for _ in range(2913)] for _ in range(NB_CAMS)] 195 | RECT = [{} for _ in range(NB_CAMS)] 196 | for line in lines[10:]: 197 | l = line.split() 198 | cam = int(l[1]) 199 | id_rect = int(l[2]) 200 | if l[3] != "notvisible": 201 | a, b, c, d = l[3:] 202 | a = int(a) 203 | b = int(b) 204 | c = int(c) 205 | d = int(d) 206 | ratio = 180/(d-b) 207 | if d < 5000: 208 | if abs(c - a) < abs(d - b): 209 | RECT[cam][id_rect] = (a, b, c, d,ratio) 210 | FIND_RECT[cam][d][(a + c) // 2] = id_rect 211 | except FileNotFoundError: 212 | print("Error: Rectangle file not found") 213 | 214 | VALIDATIONCODES = [] 215 | STARTFRAME = 550 216 | NBFRAMES = 18000 217 | UNLABELED = list(range(0,NBFRAMES,10)) 218 | LASTLOADED = 990 219 | 220 | SERVER_PATH = "/Volumes/cvlabdata1/cvlab/datasets_people_tracking/ETH/day_2/" 221 | CAMS = [] 222 | for i in range(4): 223 | CAMS.append("cvlab_camera" + str(i+1)) 224 | for i in range(3): 225 | CAMS.append("idiap_camera" + str(i+1)) 226 | -------------------------------------------------------------------------------- /gtmarker/urls.py: -------------------------------------------------------------------------------- 1 | """gtmarker URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import include, url, handler404, handler403, handler400,handler500 17 | from django.contrib import admin 18 | from home import views as v 19 | 20 | urlpatterns = [ 21 | url(r'^$', v.index), 22 | url(r'^marker/', include('marker.urls')), 23 | url(r'^admin/', admin.site.urls), 24 | url(r'^gtm_hit/', include('gtm_hit.urls')), 25 | url(r'^login/$', v.login), 26 | ] 27 | 28 | handler400 = 'home.views.bad_request' 29 | handler403 = 'home.views.permission_denied' 30 | handler404 = 'home.views.page_not_found' 31 | handler500 = 'home.views.server_error' 32 | -------------------------------------------------------------------------------- /gtmarker/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for gtmarker project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gtmarker.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/home/__init__.py -------------------------------------------------------------------------------- /home/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /home/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class HomeConfig(AppConfig): 5 | name = 'home' 6 | -------------------------------------------------------------------------------- /home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/home/migrations/__init__.py -------------------------------------------------------------------------------- /home/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /home/static/epfl_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/home/static/epfl_logo.png -------------------------------------------------------------------------------- /home/static/fns_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/home/static/fns_logo.png -------------------------------------------------------------------------------- /home/static/idiap_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/home/static/idiap_logo.png -------------------------------------------------------------------------------- /home/templates/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/home/templates/.DS_Store -------------------------------------------------------------------------------- /home/templates/home/error.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | {% include "includes_home/navbar.html" %} 17 |
18 |
19 |
20 |
21 |
22 |

Ground truth marker

23 |
24 |

Oops! Looks like an error ({{error}}) happened

25 | 26 |
27 |
28 |
29 |
30 |
31 | {% include "includes_hit/footer.html" %} 32 | 33 | {% include "includes_hit/tuto.html" %} 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /home/templates/home/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | {% include "includes_home/navbar.html" %} 17 |
18 |
19 |
20 |
21 |
22 |

Ground truth marker

23 |
24 |

Welcome to our ground truth marker app!

25 |

This simple app will help us to label frames by adding bounding boxes around people.

26 | 27 |
28 | 36 |
37 |
38 |
39 |
40 |
41 | {% include "includes_hit/footer.html" %} 42 | 43 | {% include "includes_hit/tuto.html" %} 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /home/templates/home/login.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | {% include "includes/navbar.html" %} 18 | {% csrf_token %} 19 |
20 |
21 |
22 |
23 |
24 |

Ground truth marker

25 |
26 |

Login

27 |

Please log in to access this part of our website!

28 | 29 |
30 |
31 | {% csrf_token %} 32 | Username: 33 |

34 | Password : 35 |

36 | 37 | 38 |
39 |
40 |
41 |
42 |
43 |
44 | {% include "includes/footer.html" %} 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /home/templates/includes_home/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 | 12 | -------------------------------------------------------------------------------- /home/templates/includes_home/navbar.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /home/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /home/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.template import RequestContext 3 | from django.contrib.auth.decorators import login_required 4 | 5 | def index(request): 6 | context = RequestContext(request) 7 | return render(request, 'home/index.html',{},context) 8 | 9 | def page_not_found(request): 10 | context = RequestContext(request) 11 | return render(request, 'home/error.html',{'error': 'Page Not Found'},context,status=404) 12 | 13 | def bad_request(request): 14 | return render(request, 'home/error.html',{'error': 'Bad Request'},status=400) 15 | 16 | def permission_denied(request): 17 | context = RequestContext(request) 18 | return render(request, 'home/error.html',{'error': 'Permission Denied'},context,status=403) 19 | 20 | def server_error(request): 21 | context = RequestContext(request) 22 | return render(request, 'home/error.html',{'error': 'Server Error'},context,status=500) 23 | 24 | def login(request): 25 | context = RequestContext(request) 26 | error = False 27 | active = True 28 | if request.method == 'POST': 29 | username = request.POST['username'] 30 | password = request.POST['password'] 31 | 32 | user = authenticate(username=username, password=password) 33 | 34 | if user: 35 | if user.is_active: 36 | login(request, user) 37 | return HttpResponseRedirect('/') 38 | else: 39 | active = False 40 | else: 41 | error = True 42 | 43 | return render(request,'home/login.html', {'error': error, 'active':active}, context) 44 | 45 | @login_required 46 | def logout(request): 47 | logout(request) 48 | return HttpResponseRedirect('/') 49 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gtmarker.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /marker/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/.DS_Store -------------------------------------------------------------------------------- /marker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/__init__.py -------------------------------------------------------------------------------- /marker/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import * 3 | -------------------------------------------------------------------------------- /marker/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MarkerConfig(AppConfig): 5 | name = 'marker' 6 | -------------------------------------------------------------------------------- /marker/context_processors.py: -------------------------------------------------------------------------------- 1 | from .models import Annotation,Rectangle 2 | from django.core import serializers 3 | import json 4 | 5 | def rectangles_processor(request): 6 | rectangles = Rectangle.objects.filter(x1__gt = 0, y1__gt = 0, x2__gt = 0) 7 | return {'rectangles': rectangles} 8 | -------------------------------------------------------------------------------- /marker/management/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/management/.DS_Store -------------------------------------------------------------------------------- /marker/management/commands/add_data.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | from marker.models import Rectangle 3 | import pandas as pd 4 | import json 5 | import os 6 | class Command(BaseCommand): 7 | help = 'Populate database' 8 | 9 | def handle(self, *args, **options): 10 | rect = open('../data/rectangles480x1280.pom', 'r') 11 | lines = rect.readlines() 12 | rect.close() 13 | total = len(lines) 14 | i = 0 15 | j = 1 16 | for line in lines: 17 | box = Rectangle() 18 | l = line.split() 19 | box.cameraID = int(l[1]) 20 | box.rectangleID = int(l[2]) 21 | if l[3] != "notvisible": 22 | a, b, c, d = l[3:] 23 | else: 24 | a = b = c = d = 0 25 | box.x1 = int(a) 26 | box.y1 = int(b) 27 | box.x2 = int(c) 28 | box.y2 = int(d) 29 | box.xMid = (int(a)+int(c))//2 30 | box.save() 31 | i = i + 1 32 | if i % round(total/10) == 0: 33 | print(j*10,'%') 34 | j = j + 1 35 | print("Done") 36 | -------------------------------------------------------------------------------- /marker/management/commands/set_data.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | from marker.models import Rectangle 3 | 4 | class Command(BaseCommand): 5 | help = 'Populate database' 6 | 7 | def handle(self, *args, **options): 8 | candidates = Rectangle.objects.all() 9 | total = len(candidates) 10 | i = 0 11 | j = 1 12 | for c in candidates: 13 | c.xMid = (c.x1 + c.x2) // 2 14 | c.save() 15 | i = i + 1 16 | if i % round(total/10) == 0: 17 | print(j*10,'%') 18 | j = j + 1 19 | print("Done") 20 | -------------------------------------------------------------------------------- /marker/middleware.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django.conf import settings 4 | from django.contrib.auth.decorators import login_required 5 | 6 | class RequireLoginMiddleware(object): 7 | """ 8 | Middleware component that wraps the login_required decorator around 9 | matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and 10 | define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your 11 | settings.py. For example: 12 | ------ 13 | LOGIN_REQUIRED_URLS = ( 14 | r'/topsecret/(.*)$', 15 | ) 16 | LOGIN_REQUIRED_URLS_EXCEPTIONS = ( 17 | r'/topsecret/login(.*)$', 18 | r'/topsecret/logout(.*)$', 19 | ) 20 | ------ 21 | LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must 22 | be a valid regex. 23 | 24 | LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly 25 | define any exceptions (like login and logout URLs). 26 | """ 27 | def __init__(self): 28 | self.required = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS) 29 | self.exceptions = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS) 30 | 31 | def process_view(self, request, view_func, view_args, view_kwargs): 32 | # No need to process URLs if user already logged in 33 | if request.user.is_authenticated(): 34 | return None 35 | 36 | # An exception match should immediately return None 37 | for url in self.exceptions: 38 | if url.match(request.path): 39 | return None 40 | 41 | # Requests matching a restricted URL pattern are returned 42 | # wrapped with the login_required decorator 43 | for url in self.required: 44 | if url.match(request.path): 45 | return login_required(view_func)(request, *view_args, **view_kwargs) 46 | 47 | # Explicitly return None for all non-matching requests 48 | return None 49 | -------------------------------------------------------------------------------- /marker/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-13 14:50 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Annotation', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('pom_file', models.CharField(max_length=30)), 21 | ('frame_number', models.PositiveIntegerField(default=0)), 22 | ('rectangleID', models.PositiveIntegerField(default=0)), 23 | ('personID', models.PositiveSmallIntegerField(default=0)), 24 | ('modified_flag', models.BooleanField(default=False)), 25 | ('coordinates', models.CommaSeparatedIntegerField(max_length=180)), 26 | ], 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /marker/migrations/0002_auto_20161013_1455.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-13 14:55 3 | from __future__ import unicode_literals 4 | 5 | import django.core.validators 6 | from django.db import migrations, models 7 | import re 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('marker', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name='annotation', 19 | name='coordinates', 20 | field=models.CharField(max_length=180, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:\\,\\d+)*\\Z', 32), code='invalid', message='Enter only digits separated by commas.')]), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /marker/migrations/0003_rectanges.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-18 13:04 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0002_auto_20161013_1455'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Rectanges', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('cameraID', models.PositiveSmallIntegerField(default=0)), 20 | ('rectangleID', models.PositiveIntegerField(default=0)), 21 | ('x1', models.PositiveSmallIntegerField(default=0)), 22 | ('y1', models.PositiveSmallIntegerField(default=0)), 23 | ('x2', models.PositiveSmallIntegerField(default=0)), 24 | ('y2', models.PositiveSmallIntegerField(default=0)), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /marker/migrations/0004_auto_20161018_1310.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-18 13:10 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0003_rectanges'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameModel( 16 | old_name='Rectanges', 17 | new_name='Rectangles', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /marker/migrations/0005_auto_20161018_1341.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-18 13:41 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0004_auto_20161018_1310'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameModel( 16 | old_name='Rectangles', 17 | new_name='Rectangle', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /marker/migrations/0006_rectangle_xmid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-19 12:53 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0005_auto_20161018_1341'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='rectangle', 17 | name='xMid', 18 | field=models.PositiveSmallIntegerField(default=0), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /marker/migrations/0007_remove_annotation_pom_file.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-21 13:21 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0006_rectangle_xmid'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='annotation', 17 | name='pom_file', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /marker/migrations/0008_delete_rectangle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-10-26 10:02 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0007_remove_annotation_pom_file'), 12 | ] 13 | 14 | operations = [ 15 | migrations.DeleteModel( 16 | name='Rectangle', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /marker/migrations/0009_worker.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-04 14:23 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0008_delete_rectangle'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Worker', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('workerID', models.PositiveIntegerField(default=0)), 20 | ('frameNB', models.PositiveSmallIntegerField(default=0)), 21 | ('validationoCode', models.PositiveIntegerField(default=0)), 22 | ('finished', models.BooleanField(default=False)), 23 | ('state', models.IntegerField(default=-1)), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /marker/migrations/0010_auto_20161110_1512.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-11-10 15:12 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('marker', '0009_worker'), 12 | ] 13 | 14 | operations = [ 15 | migrations.DeleteModel( 16 | name='Annotation', 17 | ), 18 | migrations.DeleteModel( 19 | name='Worker', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /marker/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/migrations/__init__.py -------------------------------------------------------------------------------- /marker/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.core.validators import validate_comma_separated_integer_list 3 | 4 | # class Annotation(models.Model): 5 | # 6 | # frame_number = models.PositiveIntegerField(default=0) 7 | # rectangleID = models.PositiveIntegerField(default=0) 8 | # personID = models.PositiveSmallIntegerField(default=0) 9 | # modified_flag = models.BooleanField(default=False) 10 | # coordinates = models.CharField(max_length=180,validators=[validate_comma_separated_integer_list]) 11 | # 12 | # # class Rectangle(models.Model): 13 | # # cameraID = models.PositiveSmallIntegerField(default=0) 14 | # # rectangleID = models.PositiveIntegerField(default=0) 15 | # # x1 = models.PositiveSmallIntegerField(default=0) 16 | # # y1 = models.PositiveSmallIntegerField(default=0) 17 | # # x2 = models.PositiveSmallIntegerField(default=0) 18 | # # y2 = models.PositiveSmallIntegerField(default=0) 19 | # # xMid = models.PositiveSmallIntegerField(default=0) 20 | # 21 | # 22 | # class Worker(models.Model): 23 | # 24 | # workerID = models.PositiveIntegerField(primary_key=True) 25 | # frameNB = models.PositiveSmallIntegerField(default=0) 26 | # #validationoCode = models.PositiveIntegerField(default=0) 27 | # finished = models.BooleanField(default=False) 28 | # state = models.IntegerField(default=-1) 29 | # 30 | # def increaseFrame(self,val): 31 | # frameNB = frameNB + val 32 | # 33 | # def decreaseFrame(self,val): 34 | # frameNB = frameNB - val 35 | # 36 | # class ValidationCode(models.Model): 37 | # validationCode = models.PositiveIntegerField(primary_key=True) 38 | # workerID = models.ForeignKey('Worker', on_delete=models.CASCADE) 39 | -------------------------------------------------------------------------------- /marker/static/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/.DS_Store -------------------------------------------------------------------------------- /marker/static/marker/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/.DS_Store -------------------------------------------------------------------------------- /marker/static/marker/arrows.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/arrows.gif -------------------------------------------------------------------------------- /marker/static/marker/arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/arrows.png -------------------------------------------------------------------------------- /marker/static/marker/cst.txt: -------------------------------------------------------------------------------- 1 | WIDTH 12 2 | HEIGHT 32 3 | NB_WIDTH 480 4 | NB_HEIGHT 1280 5 | MAN_RAY 0.16 6 | MAN_HEIGHT 1.8 7 | REDUCTION 1 8 | ORIGINE_X -3.0 9 | ORIGINE_Y -6.0 10 | NB_CAMERA 7 -------------------------------------------------------------------------------- /marker/static/marker/day_2: -------------------------------------------------------------------------------- 1 | /cvlabdata1/cvlab/datasets_people_tracking/ETH/day_2/ -------------------------------------------------------------------------------- /marker/static/marker/find_rect.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/find_rect.pickle -------------------------------------------------------------------------------- /marker/static/marker/frame.css: -------------------------------------------------------------------------------- 1 | .top5 { margin-top:5px; } 2 | .top7 { margin-top:7px; } 3 | .top10 { margin-top:10px; } 4 | .top15 { margin-top:15px; } 5 | .top17 { margin-top:17px; } 6 | .top30 { margin-top:30px; } 7 | 8 | .loadersmall { 9 | border: 5px solid #f3f3f3; 10 | -webkit-animation: spin 1s linear infinite; 11 | animation: spin 1s linear infinite; 12 | border-top: 5px solid #555; 13 | border-radius: 50%; 14 | width: 30px; 15 | height: 30px; 16 | } 17 | 18 | @-webkit-keyframes spin { 19 | 0% { -webkit-transform: rotate(0deg); } 20 | 100% { -webkit-transform: rotate(360deg); } 21 | } 22 | 23 | @keyframes spin { 24 | 0% { transform: rotate(0deg); } 25 | 100% { transform: rotate(360deg); } 26 | } 27 | 28 | .big { 29 | line-height: 150%; 30 | } 31 | 32 | body { 33 | font-family: "Lato","Helvetica Neue",Arial,sans-serif; 34 | font-weight: 500; 35 | } 36 | -------------------------------------------------------------------------------- /marker/static/marker/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/.DS_Store -------------------------------------------------------------------------------- /marker/static/marker/images/arrows0_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows0_3D.png -------------------------------------------------------------------------------- /marker/static/marker/images/arrows1_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows1_3D.png -------------------------------------------------------------------------------- /marker/static/marker/images/arrows2_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows2_3D.png -------------------------------------------------------------------------------- /marker/static/marker/images/arrows3_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows3_3D.png -------------------------------------------------------------------------------- /marker/static/marker/images/arrows4_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows4_3D.png -------------------------------------------------------------------------------- /marker/static/marker/images/arrows5_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows5_3D.png -------------------------------------------------------------------------------- /marker/static/marker/images/arrows6_3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows6_3D.png -------------------------------------------------------------------------------- /marker/static/marker/images/arrows_plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/arrows_plane.png -------------------------------------------------------------------------------- /marker/static/marker/images/bad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/bad.png -------------------------------------------------------------------------------- /marker/static/marker/images/epfl_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/epfl_logo.png -------------------------------------------------------------------------------- /marker/static/marker/images/ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/ex2.png -------------------------------------------------------------------------------- /marker/static/marker/images/fns_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/fns_logo.png -------------------------------------------------------------------------------- /marker/static/marker/images/good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/good.png -------------------------------------------------------------------------------- /marker/static/marker/images/idiap_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/images/idiap_logo.png -------------------------------------------------------------------------------- /marker/static/marker/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Start Bootstrap - Landing Page (http://startbootstrap.com/) 3 | * Copyright 2013-2016 Start Bootstrap 4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | 7 | body, 8 | html { 9 | width: 100%; 10 | height: 100%; 11 | } 12 | 13 | body { 14 | font-family: "Lato","Helvetica Neue",Arial,sans-serif; 15 | font-weight: 500; 16 | } 17 | h1, 18 | h2, 19 | h3, 20 | h4, 21 | h5, 22 | h6 { 23 | font-family: "Lato","Helvetica Neue",Arial,sans-serif; 24 | font-weight: 700; 25 | } 26 | 27 | 28 | .intro-header { 29 | text-align: center; 30 | } 31 | 32 | .intro-message { 33 | position: relative; 34 | padding-top: 10%; 35 | padding-bottom: 10%; 36 | } 37 | 38 | .intro-message > h1 { 39 | margin: 0; 40 | font-size: 5em; 41 | } 42 | 43 | .intro-divider { 44 | width: 400px; 45 | border-top: 1px solid #f8f8f8; 46 | border-bottom: 1px solid rgba(0,0,0,0.2); 47 | } 48 | 49 | .button-name { 50 | text-transform: uppercase; 51 | font-size: 14px; 52 | font-weight: 400; 53 | letter-spacing: 2px; 54 | } 55 | 56 | .footer { 57 | position: relative; 58 | bottom: 0; 59 | width: 100%; 60 | } 61 | 62 | .top5 { margin-top:5px; } 63 | .top7 { margin-top:7px; } 64 | .top10 { margin-top:10px; } 65 | .top15 { margin-top:15px; } 66 | .top17 { margin-top:17px; } 67 | .top30 { margin-top:30px; } 68 | 69 | .big { 70 | line-height: 150%; 71 | } 72 | 73 | #my-row { 74 | display: table; 75 | } 76 | 77 | #my-row .panel { 78 | float: none; 79 | display: table-cell; 80 | vertical-align: top; 81 | } 82 | -------------------------------------------------------------------------------- /marker/static/marker/jquery.hotkeys.js: -------------------------------------------------------------------------------- 1 | /*jslint browser: true*/ 2 | /*jslint jquery: true*/ 3 | 4 | /* 5 | * jQuery Hotkeys Plugin 6 | * Copyright 2010, John Resig 7 | * Dual licensed under the MIT or GPL Version 2 licenses. 8 | * 9 | * Based upon the plugin by Tzury Bar Yochay: 10 | * https://github.com/tzuryby/jquery.hotkeys 11 | * 12 | * Original idea by: 13 | * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ 14 | */ 15 | 16 | /* 17 | * One small change is: now keys are passed by object { keys: '...' } 18 | * Might be useful, when you want to pass some other data to your handler 19 | */ 20 | 21 | (function(jQuery) { 22 | 23 | jQuery.hotkeys = { 24 | version: "0.2.0", 25 | 26 | specialKeys: { 27 | 8: "backspace", 28 | 9: "tab", 29 | 10: "return", 30 | 13: "return", 31 | 16: "shift", 32 | 17: "ctrl", 33 | 18: "alt", 34 | 19: "pause", 35 | 20: "capslock", 36 | 27: "esc", 37 | 32: "space", 38 | 33: "pageup", 39 | 34: "pagedown", 40 | 35: "end", 41 | 36: "home", 42 | 37: "left", 43 | 38: "up", 44 | 39: "right", 45 | 40: "down", 46 | 45: "insert", 47 | 46: "del", 48 | 59: ";", 49 | 61: "=", 50 | 96: "0", 51 | 97: "1", 52 | 98: "2", 53 | 99: "3", 54 | 100: "4", 55 | 101: "5", 56 | 102: "6", 57 | 103: "7", 58 | 104: "8", 59 | 105: "9", 60 | 106: "*", 61 | 107: "+", 62 | 109: "-", 63 | 110: ".", 64 | 111: "/", 65 | 112: "f1", 66 | 113: "f2", 67 | 114: "f3", 68 | 115: "f4", 69 | 116: "f5", 70 | 117: "f6", 71 | 118: "f7", 72 | 119: "f8", 73 | 120: "f9", 74 | 121: "f10", 75 | 122: "f11", 76 | 123: "f12", 77 | 144: "numlock", 78 | 145: "scroll", 79 | 173: "-", 80 | 186: ";", 81 | 187: "=", 82 | 188: ",", 83 | 189: "-", 84 | 190: ".", 85 | 191: "/", 86 | 192: "`", 87 | 219: "[", 88 | 220: "\\", 89 | 221: "]", 90 | 222: "'" 91 | }, 92 | 93 | shiftNums: { 94 | "`": "~", 95 | "1": "!", 96 | "2": "@", 97 | "3": "#", 98 | "4": "$", 99 | "5": "%", 100 | "6": "^", 101 | "7": "&", 102 | "8": "*", 103 | "9": "(", 104 | "0": ")", 105 | "-": "_", 106 | "=": "+", 107 | ";": ": ", 108 | "'": "\"", 109 | ",": "<", 110 | ".": ">", 111 | "/": "?", 112 | "\\": "|" 113 | }, 114 | 115 | // excludes: button, checkbox, file, hidden, image, password, radio, reset, search, submit, url 116 | textAcceptingInputTypes: [ 117 | "text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", 118 | "datetime-local", "search", "color", "tel"], 119 | 120 | // default input types not to bind to unless bound directly 121 | textInputTypes: /textarea|input|select/i, 122 | 123 | options: { 124 | filterInputAcceptingElements: true, 125 | filterTextInputs: true, 126 | filterContentEditable: true 127 | } 128 | }; 129 | 130 | function keyHandler(handleObj) { 131 | if (typeof handleObj.data === "string") { 132 | handleObj.data = { 133 | keys: handleObj.data 134 | }; 135 | } 136 | 137 | // Only care when a possible input has been specified 138 | if (!handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string") { 139 | return; 140 | } 141 | 142 | var origHandler = handleObj.handler, 143 | keys = handleObj.data.keys.toLowerCase().split(" "); 144 | 145 | handleObj.handler = function(event) { 146 | // Don't fire in text-accepting inputs that we didn't directly bind to 147 | if (this !== event.target && 148 | (jQuery.hotkeys.options.filterInputAcceptingElements && 149 | jQuery.hotkeys.textInputTypes.test(event.target.nodeName) || 150 | (jQuery.hotkeys.options.filterContentEditable && jQuery(event.target).attr('contenteditable')) || 151 | (jQuery.hotkeys.options.filterTextInputs && 152 | jQuery.inArray(event.target.type, jQuery.hotkeys.textAcceptingInputTypes) > -1))) { 153 | return; 154 | } 155 | 156 | var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[event.which], 157 | character = String.fromCharCode(event.which).toLowerCase(), 158 | modif = "", 159 | possible = {}; 160 | 161 | jQuery.each(["alt", "ctrl", "shift"], function(index, specialKey) { 162 | 163 | if (event[specialKey + 'Key'] && special !== specialKey) { 164 | modif += specialKey + '+'; 165 | } 166 | }); 167 | 168 | // metaKey is triggered off ctrlKey erronously 169 | if (event.metaKey && !event.ctrlKey && special !== "meta") { 170 | modif += "meta+"; 171 | } 172 | 173 | if (event.metaKey && special !== "meta" && modif.indexOf("alt+ctrl+shift+") > -1) { 174 | modif = modif.replace("alt+ctrl+shift+", "hyper+"); 175 | } 176 | 177 | if (special) { 178 | possible[modif + special] = true; 179 | } 180 | else { 181 | possible[modif + character] = true; 182 | possible[modif + jQuery.hotkeys.shiftNums[character]] = true; 183 | 184 | // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" 185 | if (modif === "shift+") { 186 | possible[jQuery.hotkeys.shiftNums[character]] = true; 187 | } 188 | } 189 | 190 | for (var i = 0, l = keys.length; i < l; i++) { 191 | if (possible[keys[i]]) { 192 | return origHandler.apply(this, arguments); 193 | } 194 | } 195 | }; 196 | } 197 | 198 | jQuery.each(["keydown", "keyup", "keypress"], function() { 199 | jQuery.event.special[this] = { 200 | add: keyHandler 201 | }; 202 | }); 203 | 204 | })(jQuery || this.jQuery || window.jQuery); 205 | -------------------------------------------------------------------------------- /marker/static/marker/marker.js: -------------------------------------------------------------------------------- 1 | var rectsID = []; 2 | var boxes = {}; 3 | var chosen_rect; 4 | var imgArray = []; 5 | var arrArray = []; 6 | var validation = {}; 7 | var identities = {}; 8 | var personID = 0; 9 | var cameras = 7; 10 | var camName = ''; 11 | var loadcount = 0; 12 | var zoomOn = false; 13 | var zoomratio = []; 14 | var rotation = [50,230,150,75,265,340,80]; 15 | var bounds = [[0,396,1193,180,1883,228,1750,1080],[0,344,1467,77,1920,82,-1,-1], 16 | [97,1080,73,273,864,202,1920,362],[0,444,1920,261,-1,-1,-1,-1], 17 | [0,435,1920,403,-1,-1,-1,-1],[0,243,29,203,656,191,1920,442], 18 | [0,244,1920,162,-1,-1,-1,-1]]; 19 | var toggle_ground; 20 | var toggle_orientation; 21 | // hashsets --> rect per camera ? rect -> id to coordinates? 22 | // store variables here? in db ? (reupload db?) 23 | window.onload = function() { 24 | toggle_ground = true; 25 | toggle_orientation = true; 26 | camName = cams.substring(2,cams.length-2).split("', '"); 27 | for (var i = 0; i < cameras; i++) { 28 | boxes[i] = {}; 29 | 30 | arrArray[i] = new Image(); 31 | arrArray[i].id=("arrows"+i); 32 | arrArray[i].src = '../../static/gtm_hit/images/arrows'+i+'_3D.png'; 33 | 34 | imgArray[i] = new Image(); 35 | imgArray[i].id=(i+1); 36 | imgArray[i].onload = function() { 37 | var c = document.getElementById("canv"+this.id); 38 | var ctx = c.getContext('2d'); 39 | 40 | ctx.drawImage(this,0,0); 41 | if(toggle_orientation) 42 | drawArrows(ctx,this.id-1); 43 | c.addEventListener('click',mainClick); 44 | loadcount++; 45 | if(loadcount == 7) { 46 | $("#loader").hide(); 47 | } 48 | update(); 49 | } 50 | 51 | loadcount = 0; 52 | $("#loader").show(); 53 | imgArray[i].src = '../../static/marker/day_2/annotation_final/'+ camName[i]+ '/begin/'+frame_str+'.png'; // change 00..0 by a frame variable 54 | //imgArray[i].src = '../../static/gtm_hit/frames/'+ camName[i]+frame_str+'.png'; // change 00..0 by a frame variable 55 | } 56 | $(document).bind('keydown', "backspace",backSpace); 57 | $(document).bind('keydown', "left",left); 58 | $(document).bind('keydown', "right",right); 59 | $(document).bind('keydown', "up",up); 60 | $(document).bind('keydown', "down",down); 61 | $(document).bind('keydown', "tab",tab); 62 | $(document).bind('keydown', "space",space); 63 | $(document).bind('keydown', "Shift+right",incrWidth); 64 | $(document).bind('keydown', "Shift+left",decrWidth); 65 | $(document).bind('keydown', "Shift+up",incrHeight); 66 | $(document).bind('keydown', "Shift+down",decrHeight); 67 | $(document).bind('keydown', "s",save); 68 | $(document).bind('keydown', "l",load); 69 | $(document).bind('keydown', "n",next); 70 | $(document).bind('keydown', "p",prev); 71 | $(document).bind( "keydown", "z",zoomControl); 72 | $(document).bind( "keydown", "t",toggleGround); 73 | $(document).bind( "keydown", "h",toggleOrientation); 74 | $(document).bind('keydown', "Shift+n",nextI); 75 | $(document).bind('keydown', "Shift+p",prevI); 76 | $(document).bind( "keydown", "e",validate); 77 | $("#pID").bind( "keydown", "return",changeID); 78 | $("#pID").val(-1); 79 | $("#pHeight").val(-1); 80 | $("#pWidth").val(-1); 81 | }; 82 | 83 | function mainClick(e) { 84 | var canv = this; 85 | var offset = $(this).offset(); 86 | var relativeX = (e.pageX - offset.left)-15; 87 | var relativeY = (e.pageY - offset.top); 88 | var xCorr = Math.round(relativeX*1920/(this.clientWidth-30)); 89 | var yCorr = Math.round(relativeY*1080/this.clientHeight); 90 | if(relativeX >=0 && relativeX<=(this.clientWidth - 29)) { 91 | if(zoomOn) 92 | zoomOut(); 93 | //post 94 | $.ajax({ 95 | method: "POST", 96 | url: "click", 97 | data: { 98 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 99 | x: xCorr, 100 | y: yCorr, 101 | canv: this.id 102 | }, 103 | dataType: "json", 104 | success: function(msg) { 105 | var rid = msg[0].rectangleID; 106 | var indof = rectsID.indexOf(rid); 107 | if(indof == -1) { 108 | rectsID.push(rid); 109 | chosen_rect = rectsID.length-1; 110 | while(personID in validation) 111 | personID++; 112 | identities[rid] = personID; 113 | validation[personID] = true; 114 | saveRect(msg,personID); 115 | } else { 116 | chosen_rect = indof; 117 | } 118 | update(); 119 | } 120 | }); 121 | 122 | } 123 | } 124 | 125 | function backSpace() { 126 | if (rectsID.length > 0){ 127 | var rid = rectsID[chosen_rect]; 128 | rectsID.splice(chosen_rect,1); 129 | var idPers = identities[rid]; 130 | delete validation[idPers]; 131 | delete identities[rid]; 132 | //validation_dict.pop(idPers) 133 | //identities.pop(idRect) 134 | //for i in range(NB_PICTURES): 135 | // if idPers in person_rect[i]: 136 | // person_rect[i].pop(idPers) 137 | if (chosen_rect == rectsID.length) { 138 | chosen_rect--; 139 | } 140 | if(zoomOn) { 141 | zoomOut(); 142 | } 143 | update(); 144 | } 145 | return false; 146 | } 147 | 148 | function space() { 149 | if (rectsID.length <= 1) 150 | return false; 151 | 152 | chosen_rect--; 153 | if(zoomOn) 154 | zoomOut(); 155 | update(); 156 | return false; 157 | 158 | } 159 | 160 | function tab() { 161 | if (rectsID.length <= 1) 162 | return false; 163 | 164 | chosen_rect++; 165 | if(zoomOn) 166 | zoomOut(); 167 | update(); 168 | return false; 169 | } 170 | 171 | function left() { 172 | sendAJAX("move","left",rectsID[chosen_rect],moveRect); 173 | update(); 174 | return false; 175 | } 176 | 177 | function right() { 178 | sendAJAX("move","right",rectsID[chosen_rect],moveRect); 179 | update(); 180 | return false; 181 | } 182 | 183 | function up() { 184 | sendAJAX("move","up",rectsID[chosen_rect],moveRect); 185 | update(); 186 | return false; 187 | } 188 | 189 | function down() { 190 | sendAJAX("move","down",rectsID[chosen_rect],moveRect); 191 | update() 192 | return false; 193 | } 194 | 195 | function incrWidth() { 196 | var ind = getIndx(); 197 | var pid = identities[rectsID[chosen_rect]]; 198 | var rect = boxes[ind][pid]; 199 | rect.x1 = rect.x1-1; 200 | rect.x2 = rect.x2+1; 201 | boxes[ind][pid] = rect; 202 | 203 | var size = (rect.x2-rect.x1)*rect.ratio; 204 | updateSize(false,size,ind); 205 | return false; 206 | 207 | } 208 | 209 | function decrWidth() { 210 | var ind = getIndx(); 211 | var pid = identities[rectsID[chosen_rect]]; 212 | var rect = boxes[ind][pid]; 213 | rect.x1 = rect.x1+1; 214 | if(rect.x2 - rect.x1 < 1) { 215 | rect.x1 = rect.x2 -1; 216 | } else { 217 | rect.x2 = rect.x2-1; 218 | } 219 | boxes[ind][pid] = rect; 220 | var size = (rect.x2-rect.x1)*rect.ratio; 221 | updateSize(false,size,ind); 222 | return false; 223 | 224 | } 225 | 226 | function incrHeight() { 227 | 228 | var ind = getIndx(); 229 | var pid = identities[rectsID[chosen_rect]]; 230 | var rect = boxes[ind][pid]; 231 | rect.y1 = rect.y1-1; 232 | boxes[ind][pid] = rect; 233 | 234 | var size = (rect.y2-rect.y1)*rect.ratio; 235 | updateSize(true,size,ind); 236 | return false; 237 | 238 | } 239 | 240 | function decrHeight() { 241 | var ind = getIndx(); 242 | var pid = identities[rectsID[chosen_rect]]; 243 | var rect = boxes[ind][pid]; 244 | rect.y1 = rect.y1+1; 245 | if(rect.y2 - rect.y1 < 1) { 246 | rect.y1 = rect.y2-1; 247 | } 248 | boxes[ind][pid] = rect; 249 | 250 | var size = (rect.y2-rect.y1)*rect.ratio; 251 | updateSize(true,size,ind); 252 | return false; 253 | 254 | } 255 | 256 | function getIndx() { 257 | var h = -1; 258 | var retInd = -1; 259 | var pid = identities[rectsID[chosen_rect]]; 260 | for (var i = 0; i < cameras; i++) { 261 | r = boxes[i][pid]; 262 | tpH = Math.abs(r.y1 - r.y2); 263 | 264 | if(tpH > h) { 265 | h = tpH; 266 | retInd = i; 267 | } 268 | } 269 | return retInd; 270 | } 271 | 272 | function updateSize(height,size,ind) { 273 | var r = rectsID[chosen_rect]; 274 | var pid = identities[r]; 275 | for (var i = 0; i < cameras; i++) { 276 | rect = boxes[i][pid]; 277 | if(i != ind && rect.y1 != 0) { 278 | if(height) { 279 | var b = Math.round(rect.y2 - size/rect.ratio); 280 | if(rect.y2 - b < 1) 281 | b = rect.y2 - 1; 282 | rect.y1 = b; 283 | } else { 284 | var delta = size/(2*rect.ratio); 285 | var c = Math.round(rect.xMid + delta); 286 | var a = Math.round(rect.xMid - delta); 287 | if(c - a < 1) 288 | a = c - 1; 289 | rect.x1 = a; 290 | rect.x2 = c; 291 | } 292 | } 293 | boxes[i][pid] = rect; 294 | } 295 | update() 296 | } 297 | 298 | function save() { 299 | var dims = {}; 300 | var k = 0; 301 | for (var i = 0; i < rectsID.length; i++) { 302 | var rid = rectsID[i]; 303 | var pid = identities[rid]; 304 | dims[rid] = []; 305 | dims[rid].push(pid); 306 | dims[rid].push(validation[pid]); 307 | } 308 | 309 | for (var i = 0; i < cameras; i++) { 310 | for (var j = 0; j < rectsID.length; j++) { 311 | var rid = rectsID[j]; 312 | var pid = identities[rid]; 313 | 314 | var field = boxes[i][pid]; 315 | if(field.x2 != 0){ 316 | dims[rid].push(field.x1); 317 | dims[rid].push(field.y1); 318 | dims[rid].push(field.x2); 319 | dims[rid].push(field.y2); 320 | } else { 321 | dims[rid].push(-1); 322 | dims[rid].push(-1); 323 | dims[rid].push(-1); 324 | dims[rid].push(-1); 325 | } 326 | } 327 | } 328 | $.ajax({ 329 | method: "POST", 330 | url: 'save', 331 | data: { 332 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 333 | data: JSON.stringify(dims), 334 | ID: frame_str 335 | }, 336 | success: function(msg) { 337 | console.log(msg); 338 | } 339 | }); 340 | 341 | } 342 | 343 | function load() { 344 | loader('load'); 345 | } 346 | 347 | function load_prev() { 348 | loader('loadprev') 349 | } 350 | 351 | function loader(uri) { 352 | $.ajax({ 353 | method: "POST", 354 | url: uri, 355 | data: { 356 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 357 | ID: frame_str 358 | }, 359 | dataType: 'json', 360 | success: function(msg) { 361 | clean(); 362 | var maxID = 0; 363 | for (var i = 0; i < msg.length; i++) { 364 | var rid = msg[i][0].rectangleID; 365 | var indof = rectsID.indexOf(rid); 366 | if(indof == -1) { 367 | rectsID.push(rid); 368 | saveRectLoad(msg[i]); 369 | chosen_rect = rectsID.length-1; 370 | identities[rid] = msg[i][7]; 371 | var pid = msg[i][7]; 372 | if(pid > maxID) 373 | maxID = pid; 374 | if(uri == "loadprev") 375 | validation[pid] = false; 376 | else 377 | validation[pid] = msg[i][8]; 378 | } 379 | personID = maxID + 1; 380 | update(); 381 | } 382 | } 383 | }); 384 | } 385 | function clean() { 386 | for (var i = 0; i < cameras; i++) { 387 | boxes[i] = {}; 388 | rectsID = []; 389 | validation = {}; 390 | identities = {}; 391 | personID = 0; 392 | chosen_rect = 0; 393 | } 394 | update(); 395 | } 396 | 397 | function changeFrame(order,increment) { 398 | $.ajax({ 399 | method: "POST", 400 | url: 'changeframe', 401 | data: { 402 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 403 | order: order, 404 | frameID: frame_str, 405 | incr: increment, 406 | }, 407 | dataType: "json", 408 | success: function(msg) { 409 | frame_str = msg; 410 | loadcount = 0; 411 | $("#loader").show(); 412 | fstr = frame_str; 413 | fstr = fstr.replace(/^0*/, ""); 414 | $("#frameID").html("Frame ID: " + fstr +"  "); 415 | for (var i = 0; i < cameras; i++) 416 | imgArray[i].src = '../../static/marker/day_2/annotation_final/'+ camName[i]+ '/begin/'+frame_str+'.png'; // change 00..0 by a frame variable 417 | clean(); 418 | update(); 419 | } 420 | }); 421 | 422 | } 423 | 424 | function next() { 425 | changeFrame('next',1); 426 | } 427 | 428 | function prev() { 429 | changeFrame('prev',1); 430 | } 431 | 432 | function nextI() { 433 | changeFrame('next',10); 434 | } 435 | 436 | function prevI() { 437 | changeFrame('prev',10); 438 | } 439 | 440 | function validate() { 441 | var rid = rectsID[chosen_rect]; 442 | var idPers = identities[rid]; 443 | validation[idPers] = true; 444 | return false; 445 | } 446 | 447 | function changeID() { 448 | var newID = parseInt($("#pID").val()); 449 | if (rectsID.length > 0 && newID >= 0) { 450 | var rid = rectsID[chosen_rect]; 451 | var pid = identities[rid]; 452 | var match = false; 453 | for (key in identities) { 454 | if (identities[key] == newID) 455 | match = true; 456 | } 457 | if(!match) { 458 | validation[newID]= validation[pid]; 459 | delete validation[pid]; 460 | identities[rid] = newID; 461 | for(key in boxes){ 462 | if(pid in boxes[key]){ 463 | var args = boxes[key][pid]; 464 | boxes[key][newID] = args; 465 | delete boxes[key][pid]; 466 | } 467 | } 468 | $("#pID").val(newID); 469 | } else { 470 | $("#pID").val(pid); 471 | } 472 | } 473 | } 474 | 475 | 476 | function sendAJAX(uri,data,id,suc) { 477 | $.ajax({ 478 | method: "POST", 479 | url: uri, 480 | data: { 481 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 482 | data: data, 483 | ID: id 484 | }, 485 | dataType: "json", 486 | success: function(msg) { 487 | if(msg.length > 0) 488 | suc(msg,id); 489 | update(); 490 | } 491 | }); 492 | } 493 | 494 | function saveRect(msg,pid) { 495 | for (var i = 0; i < msg.length; i++) { 496 | var ind = msg[i].cameraID; 497 | boxes[ind][pid] = msg[i]; 498 | } 499 | } 500 | 501 | function saveRectLoad(msg) { 502 | for (var i = 0; i < msg.length-2; i++) { 503 | var ind = msg[i].cameraID; 504 | boxes[ind][msg[7]] = msg[i]; 505 | } 506 | } 507 | 508 | function moveRect(msg,id) { 509 | var pid = identities[id]; 510 | if(typeof boxes[0][pid] == "undefined") { 511 | return false; 512 | } 513 | var index = rectsID.indexOf(id); 514 | var nextRect = msg[0].rectangleID; 515 | rectsID.splice(index,1); 516 | rectsID.push(nextRect); 517 | chosen_rect = rectsID.length-1; 518 | identities[nextRect] = pid; 519 | delete identities[id]; 520 | validation[pid] = true; 521 | 522 | for (var i = 0; i < msg.length; i++) { 523 | var f = msg[i]; 524 | var ind = f.cameraID; 525 | var oldRect = boxes[ind][pid]; 526 | 527 | var newRect = msg[i]; 528 | if(oldRect.y1 != 0) { 529 | var heightR = Math.abs(oldRect.y1-oldRect.y2)*oldRect.ratio; 530 | var widthR = Math.abs(oldRect.x1-oldRect.x2)*oldRect.ratio; 531 | } else { 532 | var heightR = parseInt($("#pHeight").text()); 533 | var widthR = parseInt($("#pWidth").text()); 534 | } 535 | if(newRect.ratio > 0){ 536 | newRect.y1 = Math.round(newRect.y2 - (heightR/newRect.ratio)); 537 | var delta = widthR/(2*newRect.ratio); 538 | newRect.x2 = Math.round(newRect.xMid + delta); 539 | newRect.x1 = Math.round(newRect.xMid - delta); 540 | } 541 | boxes[ind][pid] = newRect; 542 | } 543 | } 544 | 545 | function update() { 546 | chosen_rect = ((chosen_rect % rectsID.length) + rectsID.length) % rectsID.length; 547 | $("#pID").val(identities[rectsID[chosen_rect]]); 548 | drawRect(); 549 | if(toggle_ground) 550 | drawGround(); 551 | 552 | } 553 | 554 | function drawRect() { 555 | for (var i = 0; i < cameras; i++) { 556 | var c = document.getElementById("canv"+(i+1)); 557 | var ctx=c.getContext("2d"); 558 | ctx.clearRect(0, 0, c.width, c.height); 559 | ctx.drawImage(imgArray[i],0,0); 560 | if(toggle_orientation) 561 | drawArrows(ctx,i); 562 | } 563 | var heightR = 0; 564 | var widthR = 0; 565 | var sumH = 0; 566 | for (key in boxes) { 567 | for (var r = 0; r < rectsID.length; r++) { 568 | var field = boxes[key][identities[rectsID[r]]]; 569 | if(field.y1 > 0) { 570 | var c = document.getElementById("canv"+(field.cameraID+1)); 571 | var ctx=c.getContext("2d"); 572 | var w = field.x2 - field.x1; 573 | var h = field.y2 - field.y1; 574 | if(r == chosen_rect) { 575 | ctx.strokeStyle="cyan"; 576 | ctx.lineWidth="7"; 577 | heightR += (field.y2-field.y1)*field.ratio; 578 | widthR += (field.x2-field.x1)*field.ratio; 579 | sumH += 1; 580 | } else { 581 | var pid = identities[field.rectangleID]; 582 | if(validation[pid]) 583 | ctx.strokeStyle="white"; 584 | else 585 | ctx.strokeStyle="yellow"; 586 | 587 | ctx.lineWidth="4"; 588 | 589 | } 590 | 591 | 592 | ctx.beginPath(); 593 | ctx.rect(field.x1,field.y1,w,h); 594 | ctx.stroke(); 595 | ctx.closePath(); 596 | 597 | ctx.beginPath(); 598 | ctx.fillStyle = "red"; 599 | ctx.fillRect(field.xMid-5,field.y2-5,10,10); 600 | ctx.stroke(); 601 | ctx.closePath(); 602 | } 603 | } 604 | } 605 | if(chosen_rect >= 0) { 606 | $("#pHeight").text(Math.round(heightR/sumH)); 607 | $("#pWidth").text(Math.round(widthR/sumH)); 608 | } else { 609 | $("#pHeight").text(-1); 610 | $("#pWidth").text(-1); 611 | } 612 | } 613 | 614 | function drawGround() { 615 | for (var i = 0; i < cameras; i++) { 616 | var c = document.getElementById("canv"+(i+1)); 617 | var ctx=c.getContext("2d"); 618 | ctx.strokeStyle="chartreuse"; 619 | ctx.lineWidth="2"; 620 | ctx.beginPath(); 621 | 622 | ctx.moveTo(bounds[i][0], bounds[i][1]); 623 | for (var j = 2; j < bounds[i].length; j=j+2) { 624 | if(bounds[i][j] >= 0) { 625 | ctx.lineTo(bounds[i][j], bounds[i][j+1]); 626 | } 627 | } 628 | ctx.stroke(); 629 | ctx.closePath(); 630 | 631 | } 632 | } 633 | 634 | function drawArrows(ctx, idx) { 635 | ctx.drawImage(arrArray[idx],0,0); 636 | } 637 | 638 | function zoomControl() { 639 | if(rectsID.length > 0){ 640 | if(!zoomOn) { 641 | zoomIn(); 642 | } else { 643 | zoomOut(); 644 | } 645 | 646 | } 647 | update(); 648 | } 649 | 650 | function zoomIn() { 651 | for (var i = 0; i < cameras; i++) { 652 | var pid = identities[rectsID[chosen_rect]]; 653 | var r = boxes[i][pid]; 654 | 655 | var c = document.getElementById("canv"+(i+1)); 656 | 657 | zoomratio[i] = c.height*60/(100*(r.y2-r.y1)); 658 | if(zoomratio[i] != Infinity) { 659 | 660 | var ctx = c.getContext('2d'); 661 | c.width=c.width/zoomratio[i]; 662 | c.height=c.height/zoomratio[i]; 663 | var originx = r.xMid - c.width/2; 664 | // var originx = r.xMid; 665 | var originy = r.y1-12.5*c.clientHeight/100; 666 | // ctx.scale(1.75,1.75); 667 | ctx.translate(-originx, -originy); 668 | 669 | } 670 | } 671 | zoomOn = true; 672 | return false; 673 | 674 | } 675 | 676 | 677 | function zoomOut() { 678 | for (var i = 0; i < cameras; i++) { 679 | var c = document.getElementById("canv"+(i+1)); 680 | if(zoomratio[i] != Infinity) { 681 | c.width=c.width*zoomratio[i]; 682 | c.height=c.height*zoomratio[i]; 683 | } 684 | } 685 | zoomOn = false; 686 | return false; 687 | 688 | } 689 | 690 | 691 | function toggleGround() { 692 | if(toggle_ground == false) 693 | toggle_ground = true; 694 | else 695 | toggle_ground=false; 696 | update(); 697 | return false; 698 | } 699 | 700 | function toggleOrientation() { 701 | if(toggle_orientation == false) 702 | toggle_orientation = true; 703 | else 704 | toggle_orientation=false; 705 | update(); 706 | return false; 707 | } 708 | 709 | function load_file(f){ 710 | var re = f.match(/_(.*)\./); 711 | if(re == null) 712 | var frame_string = f.split(".")[0]; 713 | else 714 | var frame_string = f.match(/_(.*)\./).pop(); 715 | $.ajax({ 716 | method: "POST", 717 | url: "loadfile", 718 | data: { 719 | csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, 720 | ID: f 721 | }, 722 | dataType: 'json', 723 | success: function(msg) { 724 | clean(); 725 | load_frame(frame_string); 726 | var maxID = 0; 727 | for (var i = 0; i < msg.length; i++) { 728 | var rid = msg[i][0].rectangleID; 729 | var indof = rectsID.indexOf(rid); 730 | if(indof == -1) { 731 | rectsID.push(rid); 732 | saveRectLoad(msg[i]); 733 | chosen_rect = rectsID.length-1; 734 | identities[rid] = msg[i][7]; 735 | var pid = msg[i][7]; 736 | if(pid > maxID) 737 | maxID = pid; 738 | 739 | validation[pid] = msg[i][8]; 740 | } 741 | personID = maxID + 1; 742 | update(); 743 | } 744 | } 745 | }); 746 | 747 | } 748 | 749 | function load_frame(frame_string) { 750 | loadcount = 0; 751 | $("#loader").show(); 752 | fstr = frame_string; 753 | fstr = fstr.replace(/^0*/, ""); 754 | frame_str = frame_string; 755 | $("#frameID").html("Frame ID: " + fstr +"  "); 756 | for (var i = 0; i < cameras; i++) 757 | imgArray[i].src = '../../static/marker/day_2/annotation_final/'+ camName[i]+ '/begin/'+frame_string+'.png'; // change 00..0 by a frame variable 758 | clean(); 759 | update(); 760 | } 761 | -------------------------------------------------------------------------------- /marker/static/marker/rect.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/static/marker/rect.pickle -------------------------------------------------------------------------------- /marker/templates/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/templates/.DS_Store -------------------------------------------------------------------------------- /marker/templates/includes/controller.html: -------------------------------------------------------------------------------- 1 | {% load marker_extra%} 2 | 3 | 4 |
5 |
6 |

Selection

7 |
8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |

Position

16 |
17 | 18 | 19 | 20 | 21 |
22 |
23 | 24 |
25 |

Size

26 |
27 |
28 |
Height
29 |
30 | 31 | 32 |
33 |
34 |
35 |
Width
36 |
37 | 38 | 39 |
40 |
41 |
42 |
43 | 44 |
45 | -------------------------------------------------------------------------------- /marker/templates/includes/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 | 12 | -------------------------------------------------------------------------------- /marker/templates/includes/help.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 70 | -------------------------------------------------------------------------------- /marker/templates/includes/load.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load marker_extra %} 3 | 4 | 5 | 42 | -------------------------------------------------------------------------------- /marker/templates/includes/navbar.html: -------------------------------------------------------------------------------- 1 | 23 | {% include "includes/help.html" %} 24 | -------------------------------------------------------------------------------- /marker/templates/includes/side_menu.html: -------------------------------------------------------------------------------- 1 | {% load marker_extra%} 2 | 3 | 4 |
5 |

Infos

6 |
7 | 8 |
-1 9 |
10 |
11 |
12 | 13 |
-1 14 |
15 |
16 | 17 |
18 | 19 | 20 |
21 | 22 |
23 |
24 | -------------------------------------------------------------------------------- /marker/templates/includes/tuto.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 61 | -------------------------------------------------------------------------------- /marker/templates/marker/download.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load marker_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% include "includes/navbar.html" %} 15 |
16 |
17 |
18 |
19 |
20 |

Ground truth marker

21 |
22 |

Download

23 |

Please select the annotations you want to download

24 | 25 |
26 | {% csrf_token %} 27 |
28 | 29 |
    30 | 31 |
    32 |

    Saved files

    33 |
    34 | 35 |
    36 | {% for col in files|columns:"2" %} 37 | {% for f in col %} 38 |
    39 | {% if f in todl %} 40 | 41 | {% else %} 42 | 43 |
    44 | {% endif %} 45 | {% endfor %} 46 | {% endfor %} 47 | 48 |
    49 |
    50 |
  • 51 | 52 |
  • 53 |
  • 54 | 55 |
  • 56 |
57 |
58 | 59 |
60 | 61 |
62 |
63 |
64 | 65 |
66 | 67 |
68 | 69 | {% include "includes/footer.html" %} 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /marker/templates/marker/download_worker.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load marker_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% include "includes/navbar.html" %} 15 |
16 |
17 |
18 |
19 |
20 |

Ground truth marker

21 |
22 |

Download

23 |

Please select the annotations you want to download

24 | 25 |
26 | {% csrf_token %} 27 |
28 | 29 |
    30 | 31 |
    32 |

    Saved files

    33 |
    34 | 35 |
    36 | {% for col in files|columns:"2" %} 37 | {% for f in col %} 38 |
    39 | {% if f in todl %} 40 | 41 | {% else %} 42 | 43 |
    44 | {% endif %} 45 | {% endfor %} 46 | {% endfor %} 47 | 48 |
    49 |
    50 |
  • 51 | 52 |
  • 53 |
  • 54 | 55 |
  • 56 |
57 |
58 | 59 |
60 | 61 |
62 |
63 |
64 | 65 |
66 | 67 |
68 | 69 | {% include "includes/footer.html" %} 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /marker/templates/marker/frame.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load marker_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | {% include "includes/navbar.html" %} 23 | 24 | {% csrf_token %} 25 | 26 |
27 |
    28 |
    29 |
    30 | Loading ... please wait until this text disappear
    31 | 32 | 33 |
    34 | 37 | 42 |
    43 | 44 | 45 |
    46 |
47 |
48 | 49 |
    50 | 51 | 52 | 53 |
54 |
    55 | 56 | 57 | 58 |
59 |
60 |
61 |
    62 | 63 |
    {% include "includes/side_menu.html" %}
    64 |
    {% include "includes/controller.html" %}
    65 |
66 |
67 |
68 | {% include "includes/footer.html" %} 69 | {% include "includes/load.html" %} 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /marker/templates/marker/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | {% include "includes/navbar.html" %} 18 | {% csrf_token %} 19 |
20 |
21 |
22 |
23 |
24 |

Ground truth marker

25 |
26 |

Task description

27 |

Hi fellow worker and welcome on our ground truth marker framework!

28 |

The task we ask you to complete will be very simple: putting bounding boxes around people whilst being as accurate as possible.

29 | 30 |
31 | 39 |
40 |
41 |
42 |
43 |
44 | {% include "includes/footer.html" %} 45 | 46 | {% include "includes/tuto.html" %} 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /marker/templates/marker/login.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load gtm_hit_extra %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | {% include "includes/navbar.html" %} 18 | {% csrf_token %} 19 |
20 |
21 |
22 |
23 |
24 |

Ground truth marker

25 |
26 |

Login

27 |

Please log in to access this part of our website!

28 | 29 |
30 |
31 | {% csrf_token %} 32 | Username: 33 |

34 | Password : 35 |

36 | 37 | 38 |
39 |
40 |
41 |
42 |
43 |
44 | {% include "includes_home/footer.html" %} 45 | 46 | {% include "includes/tuto.html" %} 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /marker/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvlab-epfl/multicam-gt/29486f0bc6bcbffa1924b4b53466a654093e38e8/marker/templatetags/__init__.py -------------------------------------------------------------------------------- /marker/templatetags/marker_extra.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from marker.models import * 3 | from django.utils import timezone 4 | import json 5 | from django.core import serializers 6 | 7 | register = template.Library() 8 | 9 | @register.filter(name='next') 10 | def next(value): 11 | return int(value)+1 12 | 13 | @register.filter(name='prev') 14 | def prev(value): 15 | f = int(value) 16 | if f > 0: 17 | return f-1 18 | else: 19 | return f 20 | 21 | @register.filter(name='toJSON') 22 | def toJSON(value): 23 | return serializers.serialize("json",value) 24 | 25 | 26 | @register.filter(name='rows') 27 | def rows(thelist, n): 28 | try: 29 | n = int(n) 30 | thelist = list(thelist) 31 | except (ValueError, TypeError): 32 | return [thelist] 33 | list_len = len(thelist) 34 | split = list_len // n 35 | 36 | if list_len % n != 0: 37 | split += 1 38 | return [thelist[split*i:split*(i+1)] for i in range(n)] 39 | 40 | @register.filter(name='rows_distributed') 41 | def rows_distributed(thelist, n): 42 | 43 | try: 44 | n = int(n) 45 | thelist = list(thelist) 46 | except (ValueError, TypeError): 47 | return [thelist] 48 | list_len = len(thelist) 49 | split = list_len // n 50 | 51 | remainder = list_len % n 52 | offset = 0 53 | rows = [] 54 | for i in range(n): 55 | if remainder: 56 | start, end = (split+1)*i, (split+1)*(i+1) 57 | else: 58 | start, end = split*i+offset, split*(i+1)+offset 59 | rows.append(thelist[start:end]) 60 | if remainder: 61 | remainder -= 1 62 | offset += 1 63 | return rows 64 | 65 | @register.filter(name='columns') 66 | def columns(thelist, n): 67 | """ 68 | Break a list into ``n`` columns, filling up each column to the maximum equal 69 | length possible. For example:: 70 | 71 | >>> from pprint import pprint 72 | >>> for i in range(7, 11): 73 | ... print '%sx%s:' % (i, 3) 74 | ... pprint(columns(range(i), 3), width=20) 75 | 7x3: 76 | [[0, 3, 6], 77 | [1, 4], 78 | [2, 5]] 79 | 8x3: 80 | [[0, 3, 6], 81 | [1, 4, 7], 82 | [2, 5]] 83 | 9x3: 84 | [[0, 3, 6], 85 | [1, 4, 7], 86 | [2, 5, 8]] 87 | 10x3: 88 | [[0, 4, 8], 89 | [1, 5, 9], 90 | [2, 6], 91 | [3, 7]] 92 | 93 | # Note that this filter does not guarantee that `n` columns will be 94 | # present: 95 | >>> pprint(columns(range(4), 3), width=10) 96 | [[0, 2], 97 | [1, 3]] 98 | """ 99 | try: 100 | n = int(n) 101 | thelist = list(thelist) 102 | except (ValueError, TypeError): 103 | return [thelist] 104 | list_len = len(thelist) 105 | split = list_len // n 106 | if list_len % n != 0: 107 | split += 1 108 | return [thelist[i::split] for i in range(split)] 109 | 110 | register.filter(rows) 111 | register.filter(rows_distributed) 112 | register.filter(columns) 113 | 114 | def _test(): 115 | import doctest 116 | doctest.testmod() 117 | 118 | if __name__ == "__main__": 119 | _test() 120 | -------------------------------------------------------------------------------- /marker/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /marker/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url(r'^$', views.index, name="index"), 7 | url(r'^frame$', views.frame, name='frame'), 8 | url(r'^frame/(?P[0-9]+)$', views.framenb, name='framenb'), 9 | url(r'^download$',views.download,name='download'), 10 | url(r'^downloadwk$',views.download_worker,name='downloadwk'), 11 | url(r'^.*click',views.click,name="click"), 12 | url(r'^.*move',views.move,name="move"), 13 | url(r'^.*changeframe$', views.changeframe, name='changeframe'), 14 | url(r'^.*save$',views.save,name='save'), 15 | url(r'^.*load$',views.load,name='load'), 16 | url(r'^.*loadfile$',views.loadfile,name='loadfile'), 17 | url(r'^.*loadprev$',views.load_previous,name='loadprev'), 18 | url(r'^login/$', views.user_login, name='login'), 19 | url(r'^logout/$', views.user_logout, name='logout'), 20 | ] 21 | -------------------------------------------------------------------------------- /marker/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.shortcuts import get_object_or_404,render, redirect 3 | from django.contrib.auth import authenticate, login, logout 4 | from django.contrib.auth.decorators import login_required 5 | from django.http import HttpResponseRedirect, HttpResponse 6 | from django.core import serializers 7 | from django.core.urlresolvers import reverse 8 | from django.views import generic 9 | from django.utils import timezone 10 | from django.conf import settings 11 | from django.template import RequestContext 12 | import re 13 | import json 14 | import os 15 | import zipfile 16 | from io import BytesIO 17 | 18 | 19 | def index(request): 20 | context = RequestContext(request) 21 | return render(request, 'marker/index.html',{},context) 22 | 23 | def framenb(request,frame_number): 24 | context = RequestContext(request) 25 | files = list_files() 26 | return render(request, 'marker/frame.html',{'frame_number': frame_number,'cams': settings.CAMS, 'files':files},context) 27 | 28 | def frame(request): 29 | context = RequestContext(request) 30 | frame_number = 0 31 | files = list_files() 32 | return render(request, 'marker/frame.html',{'frame_number': frame_number,'cams': settings.CAMS,'files':files},context) 33 | 34 | def list_files(): 35 | worker_p = "gtm_hit/labels" 36 | files = os.listdir(worker_p) 37 | marker_p = "marker/labels" 38 | files = files + os.listdir(marker_p) 39 | return files 40 | def click(request): 41 | if request.is_ajax(): 42 | try: 43 | x = int(request.POST['x']) 44 | y = int(request.POST['y']) 45 | cam = request.POST['canv'] 46 | cam = int(re.findall('\d+',cam)[0]) - 1 47 | if 0 <= cam < settings.NB_CAMS: 48 | closest = -1 49 | vbest = 1000 50 | for y_temp in range(y - settings.DELTA_SEARCH, y + settings.DELTA_SEARCH): 51 | if 0 <= y_temp < 1080: 52 | for x_temp in settings.FIND_RECT[cam][y_temp]: 53 | vtemp = abs(y - y_temp) + abs(x - x_temp) 54 | if vtemp < vbest: 55 | vbest, closest = vtemp, settings.FIND_RECT[cam][y_temp][x_temp] 56 | if closest != -1: 57 | rects = get_rect(closest) 58 | rect_json = json.dumps(rects) 59 | return HttpResponse(rect_json,content_type="application/json") 60 | 61 | return HttpResponse("OK") 62 | except KeyError: 63 | return HttpResponse("Error") 64 | return HttpResponse("No") 65 | 66 | def move(request): 67 | if request.is_ajax(): 68 | try: 69 | if request.POST['data'] == "down": 70 | rectID = request.POST['ID'] 71 | if int(rectID) // settings.NB_WIDTH > 0: 72 | nextID = int(rectID) - settings.NB_WIDTH 73 | else: 74 | return HttpResponse(json.dumps([]),content_type="application/json") 75 | 76 | elif request.POST['data'] == "up": 77 | rectID = request.POST['ID'] 78 | if int(rectID) // settings.NB_WIDTH < settings.NB_HEIGHT - 1: 79 | nextID = int(rectID) + settings.NB_WIDTH 80 | else: 81 | return HttpResponse(json.dumps([]),content_type="application/json") 82 | 83 | elif request.POST['data'] == "right": 84 | rectID = request.POST['ID'] 85 | if int(rectID) % settings.NB_WIDTH < settings.NB_WIDTH - 1: 86 | nextID = int(rectID) + 1 87 | else: 88 | return HttpResponse(json.dumps([]),content_type="application/json") 89 | 90 | elif request.POST['data'] == "left": 91 | rectID = request.POST['ID'] 92 | if int(rectID) % settings.NB_WIDTH > 0: 93 | nextID = int(rectID) - 1 94 | else: 95 | return HttpResponse(json.dumps([]),content_type="application/json") 96 | 97 | else: 98 | return HttpResponse("Error") 99 | next_rect = get_rect(nextID) 100 | next_rect_json = json.dumps(next_rect) 101 | return HttpResponse(next_rect_json,content_type="application/json") 102 | 103 | except KeyError: 104 | return HttpResponse("Error") 105 | return HttpResponse("Error") 106 | 107 | def save(request): 108 | if request.is_ajax(): 109 | try: 110 | data = json.loads(request.POST['data']) 111 | frameID = request.POST['ID'] 112 | annotations = [] 113 | cols = ["rectID","personID","modified","a1","b1","c1","d1","a2","b2","c2","d2","a3","b3","c3","d3","a4","b4","c4","d4","a5","b5","c5","d5","a6","b6","c6","d6","a7","b7","c7","d7"] 114 | annotations.append(cols) 115 | for r in data: 116 | row = data[r] 117 | row.insert(0,int(r)) 118 | annotations.append(row) 119 | with open("marker/labels/"+frameID + '.json', 'w') as outFile: 120 | json.dump(annotations, outFile, sort_keys=True, indent=4, separators=(',', ': ')) 121 | return HttpResponse("Saved") 122 | except KeyError: 123 | return HttpResponse("Error") 124 | else: 125 | return("Error") 126 | 127 | def load(request): 128 | if request.is_ajax(): 129 | try: 130 | frameID = request.POST['ID'] 131 | rect_json = read_save(frameID) 132 | return HttpResponse(rect_json,content_type="application/json") 133 | except (FileNotFoundError, KeyError): 134 | return HttpResponse("Error") 135 | return HttpResponse("Error") 136 | 137 | def load_previous(request): 138 | if request.is_ajax(): 139 | try: 140 | frameID = request.POST['ID'] 141 | current_frame = int(frameID) 142 | closest = float('inf') 143 | diff = float('inf') 144 | 145 | for f in os.listdir("marker/labels/"): 146 | if f.endswith(".json"): 147 | nb_frame = int(f.split('.')[0]) 148 | 149 | if nb_frame < current_frame: 150 | if current_frame - nb_frame < diff: 151 | diff = current_frame - nb_frame 152 | closest = nb_frame 153 | if closest != float('inf'): 154 | frame = "0" * (8 - len(str(closest))) + str(closest) 155 | rect_json = read_save(frame) 156 | return HttpResponse(rect_json,content_type="application/json") 157 | except (FileNotFoundError, KeyError): 158 | return HttpResponse("Error") 159 | return HttpResponse("Error") 160 | 161 | def read_save(frameID,fullpath=False): 162 | if(fullpath): 163 | if frameID[0].isdecimal(): 164 | filename = "marker/labels/" + frameID 165 | else: 166 | filename = "gtm_hit/labels/" + frameID 167 | else: 168 | filename = "marker/labels/"+ frameID + '.json' 169 | with open(filename,'r') as loadFile: 170 | annotations = json.load(loadFile) 171 | rects = [] 172 | for i in annotations[1:]: 173 | r = get_rect(i[0]) 174 | for j in range(settings.NB_CAMS): 175 | r[j]['x1'] = i[j*4+3] 176 | r[j]['y1'] = i[j*4+4] 177 | r[j]['x2'] = i[j*4+5] 178 | r[j]['y2'] = i[j*4+6] 179 | r.append(i[1]) 180 | r.append(i[2]) 181 | rects.append(r) 182 | return json.dumps(rects) 183 | 184 | def changeframe(request): 185 | if request.is_ajax(): 186 | frame = 0 187 | try: 188 | order = request.POST['order'] 189 | frame_number = request.POST['frameID'] 190 | increment = request.POST['incr'] 191 | if order == "next": 192 | frame = int(frame_number) + int(increment) 193 | elif order == "prev" and (int(frame_number) - int(increment)) >= 0: 194 | frame = int(frame_number) - int(increment) 195 | else: 196 | return HttpResponse("Requested frame not existing") 197 | frame = "0" * (8 - len(str(frame))) + str(frame) 198 | return HttpResponse(json.dumps(frame)) 199 | except KeyError: 200 | return HttpResponse("Error") 201 | else: 202 | return HttpResponse("Error") 203 | 204 | def get_rect(closest): 205 | rects = [] 206 | for i in range(settings.NB_CAMS): 207 | rdic = {} 208 | rdic['rectangleID'] = closest 209 | if closest in settings.RECT[i]: 210 | a,b,c,d,ratio = settings.RECT[i][closest] 211 | else: 212 | a,b,c,d,ratio = 0,0,0,0,0 213 | rdic['x1'] = a 214 | rdic['y1'] = b 215 | rdic['x2'] = c 216 | rdic['y2'] = d 217 | rdic['cameraID'] = i 218 | rdic['ratio'] = ratio 219 | rdic['xMid'] = (a + c) // 2 220 | rects.append(rdic) 221 | return rects 222 | 223 | def download(request): 224 | context = RequestContext(request) 225 | fpath = "marker/labels" 226 | files = os.listdir(fpath) 227 | todl = [] 228 | if request.method == "POST": 229 | 230 | zip_dir = "annotations" 231 | zip_name = zip_dir + ".zip" 232 | s = BytesIO() 233 | zf = zipfile.ZipFile(s,"w") 234 | 235 | if 'dlselect' in request.POST: 236 | for r in request.POST: 237 | if r in files: 238 | zipath = os.path.join(zip_dir,r) 239 | zf.write(fpath + "/" + r,zipath) 240 | zf.close() 241 | resp = HttpResponse(s.getvalue(), content_type = "application/x-zip-compressed") 242 | resp['Content-Disposition'] = 'attachment; filename=' + zip_name 243 | return resp 244 | 245 | elif 'dlall' in request.POST: 246 | for r in files: 247 | zipath = os.path.join(zip_dir,r) 248 | zf.write(fpath + "/" + r,zipath) 249 | zf.close() 250 | resp = HttpResponse(s.getvalue(), content_type = "application/x-zip-compressed") 251 | resp['Content-Disposition'] = 'attachment; filename=' + zip_name 252 | return resp 253 | 254 | return render(request, 'marker/download.html',{'files': files},context) 255 | 256 | def download_worker(request): 257 | context = RequestContext(request) 258 | fpath = "gtm_hit/labels" 259 | files = os.listdir(fpath) 260 | todl = [] 261 | if request.method == "POST": 262 | 263 | zip_dir = "annotations" 264 | zip_name = zip_dir + ".zip" 265 | s = BytesIO() 266 | zf = zipfile.ZipFile(s,"w") 267 | 268 | if 'dlselect' in request.POST: 269 | for r in request.POST: 270 | if r in files: 271 | zipath = os.path.join(zip_dir,r) 272 | zf.write(fpath + "/" + r,zipath) 273 | zf.close() 274 | resp = HttpResponse(s.getvalue(), content_type = "application/x-zip-compressed") 275 | resp['Content-Disposition'] = 'attachment; filename=' + zip_name 276 | return resp 277 | 278 | elif 'dlall' in request.POST: 279 | for r in files: 280 | zipath = os.path.join(zip_dir,r) 281 | zf.write(fpath + "/" + r,zipath) 282 | zf.close() 283 | resp = HttpResponse(s.getvalue(), content_type = "application/x-zip-compressed") 284 | resp['Content-Disposition'] = 'attachment; filename=' + zip_name 285 | return resp 286 | 287 | return render(request, 'marker/download.html',{'files': files},context) 288 | 289 | 290 | def user_login(request): 291 | context = RequestContext(request) 292 | error = False 293 | active = True 294 | if request.method == 'POST': 295 | username = request.POST['username'] 296 | password = request.POST['password'] 297 | 298 | user = authenticate(username=username, password=password) 299 | 300 | if user: 301 | if user.is_active: 302 | login(request, user) 303 | return HttpResponseRedirect('/marker/') 304 | else: 305 | active = False 306 | else: 307 | error = True 308 | 309 | return render(request,'marker/login.html', {'error': error, 'active':active}, context) 310 | 311 | @login_required 312 | def user_logout(request): 313 | logout(request) 314 | return HttpResponseRedirect('/') 315 | 316 | 317 | def loadfile(request): 318 | if request.is_ajax(): 319 | try: 320 | fileID = request.POST['ID'] 321 | rect_json = read_save(fileID,True) 322 | return HttpResponse(rect_json,content_type="application/json") 323 | except (FileNotFoundError, KeyError): 324 | return HttpResponse("Error") 325 | return HttpResponse("Error") 326 | --------------------------------------------------------------------------------