Parcourir la source

Merge branch 'release/v0.0.4'

Jérôme BUISINE il y a 4 ans
Parent
commit
8aee9a23fb

+ 1 - 1
.gitignore

@@ -115,5 +115,5 @@ GitHub.sublime-settings
 !.vscode/extensions.json 
 .history
 
-expe_results
+# exclude media folder
 media

+ 10 - 3
expe/config.py

@@ -5,6 +5,7 @@ GET_SCENE_IMAGE_API_URL     = DIRAN_DOMAIN_NAME + "api/getImage?sceneName={0}&im
 GET_SCENES_API_URL          = DIRAN_DOMAIN_NAME + "api/listScenes"
 
 # folder variables
+model_expe_folder           = "expes_models/{0}/"
 output_expe_folder          = "expes_results/{0}/"
 output_tmp_folder           = "tmp"
 
@@ -12,9 +13,15 @@ output_tmp_folder           = "tmp"
 expe_name_list              = ["quest_one_image"]
 
 # configure experiences labels
-expe_questions              = {
+expes_configuration         = {
     'quest_one_image':{
-        'question': "Do you see one image or a composition of more than one?",
-        'indication': "press left if you see one image, right if not"
+        'text':{
+            'question': "Do you see one image or a composition of more than one?",
+            'indication': "press left if you see one image, right if not",
+            'end_text': "Experience is finished. Thanks for your participation",
+        },
+        'params':{
+            'iterations': 5
+        }
     }
 }

expe/quest/__init__.py → expe/expes/__init__.py


+ 25 - 0
expe/quest/quest_plus.py

@@ -7,6 +7,11 @@ import pandas as pd
 # TODO : Currently `weibull` is not used as default function
 # from psychometric import weibull
 
+
+# PARAMETERS of the psychometric function
+chance_level = 0 #e.g. chance_level should be 0.5 for 2AFC (Two-alternative forced choice) procedure
+threshold_prob = 1.-(1.-chance_level)/2.0 #the probability level at the threshold
+
 def reformat_params(params):
     '''Unroll multiple lists into array of their products.'''
     if isinstance(params, list):
@@ -18,6 +23,26 @@ def reformat_params(params):
     return params
 
 
+# quest_plus.py comes also with psychometric.py wich includes the definition of the weibull and weibull_db function
+# here I define the logistic function using the same template that works with the quest_plus implementation
+def logistic(x, params, corr_at_thresh=threshold_prob, chance_level=chance_level):
+        # unpack params
+        if len(params) == 3:
+            THRESHOLD, SLOPE, lapse = params
+        else:
+            THRESHOLD, SLOPE = params
+            lapse = 0.
+
+        b = 4 * SLOPE
+        a = -b * THRESHOLD
+
+        return chance_level + (1 - lapse - chance_level) / (1 + np.exp(-(a + b*x)))
+    
+
+# that's a wrapper function to specify wich  psychometric function one we want to use for the QUEST procedure
+def psychometric_fun( x , params ):
+    return logistic(x , params ,  corr_at_thresh=threshold_prob, chance_level=chance_level)
+
 # TODO:
 # - [ ] highlight lowest point in entropy in plot
 class QuestPlus(object):

+ 106 - 0
expe/expes/run.py

@@ -0,0 +1,106 @@
+# main imports
+import os
+import time
+import numpy as np
+import pickle
+
+# module imports
+from ..utils import api
+
+from ..utils.processing import crop_images
+from .. import config as cfg
+
+# expe imports
+from .quest_plus import QuestPlus
+from .quest_plus import psychometric_fun
+
+
+def run_quest_one_image(request, model_filepath, output_file):
+
+    # get parameters
+    qualities = request.session.get('qualities')
+    scene_name = request.session.get('scene')
+    expe_name = request.session.get('expe')
+
+    # by default
+    iterations = 0
+
+    # first time only init `quest`
+    # if experience is started we can save data
+    if request.session.get('expe_started'):
+        answer = int(request.GET.get('answer'))
+        iterations = int(request.GET.get('iteration'))
+
+        answer_time = time.time() - request.session['answer_time']
+        print("Answer time is ", answer_time)
+        previous_percentage = request.session.get('expe_percentage')
+        previous_orientation = request.session.get('expe_orientation')
+        previous_position = request.session.get('expe_position')
+
+    # default params
+    thresholds = np.arange(50, 10000, 50)
+    stim_space=np.asarray(qualities)
+    slopes = np.arange(0.0001, 0.001, 0.00003)
+
+    # check if necessary to construct `quest` object
+    if not os.path.exists(model_filepath):
+        qp = QuestPlus(stim_space, [thresholds, slopes], function=psychometric_fun)
+    else:
+        print('Load `qp` model')
+        filehandler = open(model_filepath, 'rb') 
+        qp = pickle.load(filehandler)
+    
+    # construct image and update `quest` only if necessary
+    if iterations < cfg.expes_configuration[expe_name]['params']['iterations']:
+        # process `quest`
+        next_stim = qp.next_contrast()
+        print("Next quality ", next_stim)
+
+        # construct new image
+        noisy_image = api.get_image(scene_name, next_stim)
+
+        # reconstruct reference image from list stored into session
+        ref_image = api.get_image(scene_name, 'max')
+        img_merge, percentage, orientation, position = crop_images(noisy_image, ref_image)
+    else:
+        request.session['expe_finished'] = True
+        return None
+    
+    # if experience is already begin
+    if request.session.get('expe_started'):
+
+        # TODO : check `i` variable 
+        # update of `quest`
+        # qp.update(qualities[i], answer)
+        qp.update(qualities[iterations], answer) 
+        entropy = qp.get_entropy()
+
+        line = str(next_stim) 
+        line += ";" + scene_name 
+        line += ";" + str(previous_percentage)
+        line += ";" + str(previous_orientation) 
+        line += ";" + str(previous_position) 
+        line += ";" + str(answer) 
+        line += ";" + str(answer_time) 
+        line += ";" + str(entropy) 
+        line += '\n'
+
+        print(line)
+        # TODO : add answer time from javascript
+        output_file.write(line)
+        output_file.flush()
+
+    # save `quest` model
+    file_pi = open(model_filepath, 'wb') 
+    pickle.dump(qp, file_pi)
+
+    # set current step data
+    request.session['expe_percentage'] = percentage
+    request.session['expe_orientation'] = orientation
+    request.session['expe_position'] = position
+    request.session['answer_time'] = time.time()
+    
+    # expe is now started
+    request.session['expe_started'] = True
+
+    return img_merge

+ 0 - 141
expe/quest/expe.py

@@ -1,141 +0,0 @@
-# main imports 
-import numpy as np
-import os
-import time
-from datetime import datetime
-import re
-
-# image processing imports
-from .processing import crop_images
-
-# expe imports
-from .quest_plus import QuestPlus
-
-# load `config` variables
-from .. import config as cfg
-
-# PARAMETERS of the psychometric function
-chance_level = 0 #e.g. chance_level should be 0.5 for 2AFC (Two-alternative forced choice) procedure
-threshold_prob = 1.-(1.-chance_level)/2.0 #the probability level at the threshold
-
-# quest_plus.py comes also with psychometric.py wich includes the definition of the weibull and weibull_db function
-# here I define the logistic function using the same template that works with the quest_plus implementation
-def logistic(x, params, corr_at_thresh=threshold_prob, chance_level=chance_level):
-        # unpack params
-        if len(params) == 3:
-            THRESHOLD, SLOPE, lapse = params
-        else:
-            THRESHOLD, SLOPE = params
-            lapse = 0.
-
-        b = 4 * SLOPE
-        a = -b * THRESHOLD
-
-        return chance_level + (1 - lapse - chance_level) / (1 + np.exp(-(a + b*x)))
-    
-
-# that's a wrapper function to specify wich  psychometric function one we want to use for the QUEST procedure
-def psychometric_fun( x , params ):
-    return logistic(x , params ,  corr_at_thresh=threshold_prob, chance_level=chance_level )
-
-
-
-#create results directory if not exist
-if not os.path.exists(cfg.output_expe_folder):
-    os.makedirs(cfg.output_expe_folder)
-  
-timestamp = datetime.strftime(datetime.utcnow(), "%Y-%m-%d_%Hh%Mm%Ss")
-filename += "online_ans" + timestamp +".csv"
-f = open(filename,"w")
-
-#orientation : 0 = vertical, 1 = horizontal
-#image_ref_position : 0 = right/bottom, 1 = left/up
-#answer : left = 1, right = 0
-f.write('stimulus' + ";" + "name_stimulus" + ";" + 'cropping_percentage' + ";" + 'orientation' + ';' 
-         + 'image_ref_position' + ';' + 'answer' + ';' + 'time_reaction' + ';' + 'entropy' + '\n')
-#k=np.linspace(50,20000,50)
-#k=k.astype(int)
-dd_sam=[]
-
-for i in range(len(files)):
-    ff = [int(s) for s in re.findall(r'\d+', files[i])]
-    dd_sam.append(ff[0])
-
-
-dd_sam=np.array(dd_sam)
-    
-    
-thresholds = np.arange(50, 10000, 50)
-stim_space=np.asarray(dd_sam)
-slopes = np.arange(0.0001, 0.001, 0.00003)
-
-#mywin = visual.Window([800,600], monitor="testMonitor", screen = 1, units="deg",fullscr=True)
-qp = QuestPlus(stim_space, [thresholds, slopes], function=psychometric_fun)
-answerTime=[]
-r=[]
-
-dataset = "contemporary"
-
-#image_ref = Image.open(data_folder + files[-1])
-image_ref = get_image(diran_domain_name, dataset, 10000)
-for i in range(5):
-    next_stim = qp.next_contrast()
-    print(next_stim)
-    #next_idx = np.where(dd_sam==next_stim)
-    #print(files[next_idx[0][0]])
-    #image_path = data_folder + files[next_idx[0][0]]
-    #current_image = Image.open(image_path)
-    current_image= get_image(diran_domain_name, dataset, next_stim)
-    crop_image, percentage, orientation, position = crop_images(image_ref, current_image)
-
-    answerTime.append(end-start)
-    if key_answer == ['left']:
-        answer = 1 #one image
-    else: 
-        answer = 0  #two images
-    r.append(answer)
-    qp.update(dd_sam[i], answer) 
-    
-    entropy = qp.get_entropy()
-
-    print(entropy)
-    
-    f.write(str(next_stim) + ";" + dataset + ";" + str(percentage) + ";" + str(orientation) + ";" 
-            + str(position) + ";" + str(answer) + ";" + str(answerTime[-1]) + ";" + str(entropy) +'\n')
-    f.flush()
-
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    

+ 17 - 7
expe/templates/expe/expe.html

@@ -8,21 +8,31 @@
 
 {% block content %}
     
-    {% if request.session.begin %}
-        <h3>{{ question }}</h3>
-        <p>{{ indication }}</p>
+    {% if request.session.expe_finished %}
+        <h3>{{end_text}}</h3>
 
         <br />
-        <p>Press enter to begin experience</p>
+        <p>You will be redirected in <span id="refreshTime">5</span> sec to home page</p>
     {% endif %}
-    
+
     <!-- TODO : Load    img from bitmap with javascript `loadImg.js` -->
-    {% if not request.session.begin %}
+    {% if not request.session.expe_finished %}
         <img id="expeImg" src="/{{img_merged_path}}" data-img="{{request.session.img_merged}}"/>
     {% endif %}
 
     {% block javascripts %}
-        <script src="{% static "js/pnglib.js" %}"></script>
+        <script type="text/javascript"> 
+            // Utils informations
+            var host     = window.location.host
+            var pathname = window.location.pathname
+
+            var baseUrl      = location.protocol + "//" + host
+            var baseExpeUrl  = location.protocol + "//" + host + pathname
+
+            // get access to django variables
+            var BEGIN_EXPE = "{{request.session.expe_started}}"
+            var END_EXPE   = "{{request.session.expe_finished}}"
+        </script>
         <script src="{% static "js/loadImg.js" %}"></script>
         <script src="{% static "js/keyEvents.js" %}"></script>
     {% endblock %}

+ 20 - 0
expe/templates/expe/expe_indications.html

@@ -0,0 +1,20 @@
+{% extends 'base.html' %}
+
+{% load staticfiles %}
+
+{% block title %}
+    Expe {{ expe_name }}
+{% endblock %}
+
+{% block content %}
+    
+    <h3>{{question}}</h3>
+    <p>{{indication}}</p>
+
+    <br />
+    <p>Press enter to begin experience</p>
+    
+    {% block javascripts %}
+        <script src="{% static "js/indications.js" %}"></script>
+    {% endblock %}
+{% endblock %}

+ 1 - 1
expe/templates/expe/expe_list.html

@@ -11,7 +11,7 @@
 
     <div class="row">
         <div class="col-md-4 col-md-offset-4">
-            <form action="/expe" id="expeChoice">
+            <form action="/indications" id="expeChoice">
 
                 <div class="form-group">
                     <label for="scene">Select scene:</label>

+ 1 - 0
expe/urls.py

@@ -8,6 +8,7 @@ from . import views
 urlpatterns = [
     path('', views.expe_list, name='expe_list'),
     path('expe', views.expe, name='expe'),
+    path('indications', views.indications, name='indications'),
 ]
 
 if settings.DEBUG is True:

+ 1 - 0
expe/utils/functions.py

@@ -1,3 +1,4 @@
+# main imports
 import random
 
 def uniqueID():

+ 4 - 2
expe/quest/processing.py

@@ -1,9 +1,11 @@
-
-from PIL import Image
+# main imports
 import os
 import numpy as np
 import random
 
+# processing imports
+from PIL import Image
+
 def crop_images(img1, img2, per=None, orien=None, swap_img=None):
     '''
     crop and gather reference image and a noisy one randomly

+ 84 - 56
expe/views.py

@@ -10,16 +10,24 @@ import base64
 import random
 import numpy as np
 from datetime import datetime
+import pickle 
+import time
+
+# expe imports
+from .expes.quest_plus import QuestPlus
+from .expes.quest_plus import psychometric_fun
+
+from .expes.run import run_quest_one_image
 
 # image processing imports
 import io
 from PIL import Image
 
-# api imports
+# module imports
 from .utils import api
 from .utils import functions
 
-from .quest.processing import crop_images
+from .utils.processing import crop_images
 from . import config as cfg
 
 
@@ -33,6 +41,20 @@ def expe_list(request):
 
     return render(request, 'expe/expe_list.html', {'scenes': scenes, 'expes': expes})
 
+def indications(request):
+
+    # get param 
+    expe_name = request.GET.get('expe')
+
+    # expe parameters
+    data = {
+        'expe_name': expe_name,
+        'question': cfg.expes_configuration[expe_name]['text']['question'],
+        'indication': cfg.expes_configuration[expe_name]['text']['indication']
+    }
+
+    return render(request, 'expe/expe_indications.html', data)
+
 
 # Create your views here.
 def expe(request):
@@ -40,86 +62,92 @@ def expe(request):
     # get param 
     expe_name = request.GET.get('expe')
     scene_name = request.GET.get('scene')
+    
+    # default filepath name
+    filepath_img = ''
 
     # unique user ID during session (user can launch multiple exeperiences)
     if 'id' not in request.session:
         request.session['id'] = functions.uniqueID()
 
     # first time expe is launched add expe information
-    if 'expe' not in request.session:
-        request.session['expe'] = expe_name
-        request.session['begin'] = True
-    else:
-        request.session['begin'] = False
-
-    print(request.session.get('begin'))
-
-    # update ref img at first time or expe changed
-    if expe_name != request.session.get('expe'):
-    #if 'ref_img' not in request.session or expe_name != request.session.get('expe'):
-        request.session['begin'] = True
-        request.session['qualities'] = api.get_scene_qualities(scene_name)
-        # update unique timestamp each time new experience is launched
-        request.session['timestamp'] = datetime.strftime(datetime.utcnow(), "%Y-%m-%d_%Hh%Mm%Ss")
-
-        # TODO : add in cache ref_image
-        # get reference image
-        #ref_image = api.get_image(scene_name, 'max')
-        # save ref image as list (can't save python object)
-        #request.session['ref_img'] = np.array(ref_image).tolist()
+    if 'expe' not in request.session or expe_name != request.session.get('expe'):
+        refresh_data(request, expe_name, scene_name)
 
-    # construct new image
-    quality = random.choice(request.session.get('qualities'))
-    noisy_image = api.get_image(scene_name, quality)
-
-    # reconstruct reference image from list stored into session
-    # ref_image = Image.fromarray(np.array(request.session.get('ref_img')))
-    ref_image = api.get_image(scene_name, 'max')
-    img_merge, per, orien, swap_img = crop_images(noisy_image, ref_image)
-
-    # create output folder for tmp files if necessary
-    tmp_folder = os.path.join(settings.MEDIA_ROOT, cfg.output_tmp_folder)
-
-    if not os.path.exists(tmp_folder):
-        os.makedirs(tmp_folder)
-
-    # generate tmp merged image (pass as BytesIO was complicated..)
-    # TODO : add crontab task to erase generated img
-    filepath_img = os.path.join(tmp_folder, request.session.get('id') + '_' + scene_name + '' + expe_name + '.png')
-    img_merge.save(filepath_img)
+    # refresh if scene_name changed
+    if 'scene' not in request.session or scene_name != request.session.get('scene'):
+        refresh_data(request, expe_name, scene_name)
 
     # create output folder for expe_result
     current_day = datetime.strftime(datetime.utcnow(), "%Y-%m-%d")
-    results_folder = os.path.join(cfg.output_expe_folder.format(current_day))
+    results_folder = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder.format(current_day))
 
     if not os.path.exists(results_folder):
         os.makedirs(results_folder)
 
-    result_filename = expe_name + '_' + request.session.get('id') + '_' + request.session.get('timestamp') +".csv"
+    result_filename = expe_name + '_' + scene_name + '_' + request.session.get('id') + '_' + request.session.get('timestamp') +".csv"
     results_filepath = os.path.join(results_folder, result_filename)
 
     if not os.path.exists(results_filepath):
-        f = open(results_filepath, 'w')
-        functions.write_header_expe(f, expe_name)
+        output_file = open(results_filepath, 'w')
+        functions.write_header_expe(output_file, expe_name)
     else:
-        f = open(results_filepath, 'a')
+        output_file = open(results_filepath, 'a')
 
-    #orientation : 0 = vertical, 1 = horizontal
-    #image_ref_position : 0 = right/bottom, 1 = left/up
-    #answer : left = 1, right = 0
+    # create `quest` object if not exists    
+    models_folder = os.path.join(settings.MEDIA_ROOT, cfg.model_expe_folder.format(current_day))
+
+    if not os.path.exists(models_folder):
+        os.makedirs(models_folder)
+
+    model_filename = result_filename.replace('.csv', '.obj')
+    model_filepath = os.path.join(models_folder, model_filename)
+
+    # run `quest` expe
+    img_merge = run_quest_one_image(request, model_filepath, output_file)
+
+    if not request.session.get('expe_finished'):
+        # create output folder for tmp files if necessary
+        tmp_folder = os.path.join(settings.MEDIA_ROOT, cfg.output_tmp_folder)
+
+        if not os.path.exists(tmp_folder):
+            os.makedirs(tmp_folder)
+
+        # generate tmp merged image (pass as BytesIO was complicated..)
+        # TODO : add crontab task to erase generated img
+        filepath_img = os.path.join(tmp_folder, request.session.get('id') + '_' + scene_name + '' + expe_name + '.png')
+        img_merge.save(filepath_img)
+    else:
+        # reinit session as default value
+        del request.session['expe']
+        del request.session['scene']
+        del request.session['qualities']
+        del request.session['timestamp']
 
-    print("here")
-    
     # expe parameters
     data = {
         'expe_name': expe_name,
         'img_merged_path': filepath_img,
-        'question': cfg.expe_questions[expe_name]['question'],
-        'indication': cfg.expe_questions[expe_name]['indication']
+        'end_text': cfg.expes_configuration[expe_name]['text']['end_text']
     }
 
     return render(request, 'expe/expe.html', data)
 
 
-def run_quest_one_image():
-    pass
+def refresh_data(request, expe_name, scene_name):
+
+    request.session['expe'] = expe_name
+    request.session['scene'] = scene_name
+
+    request.session['expe_started'] = False
+    request.session['expe_finished'] = False
+
+    request.session['qualities'] = api.get_scene_qualities(scene_name)
+    # update unique timestamp each time new experience is launched
+    request.session['timestamp'] = datetime.strftime(datetime.utcnow(), "%Y-%m-%d_%Hh%Mm%Ss")
+
+    # TODO : add in cache ref_image
+    # get reference image
+    #ref_image = api.get_image(scene_name, 'max')
+    # save ref image as list (can't save python object)
+    #request.session['ref_img'] = np.array(ref_image).tolist()

+ 4 - 0
static/css/expe.css

@@ -5,4 +5,8 @@ body {
 .container{
     margin-top: 2%;
     text-align: center;
+}
+
+#expeImg{
+    display: none;
 }

+ 32 - 0
static/js/indications.js

@@ -0,0 +1,32 @@
+// implement `key` events
+document.onkeydown = checkKey;
+
+var host     = window.location.host
+var expe_url = '/expe'     
+var baseUrl  = location.protocol + "//" + host
+
+// Utils informations
+var KEYCODE_Q           = '81'
+var KEYCODE_ENTER       = '13'
+
+urlParams = new URLSearchParams(window.location.search);
+
+var scene = urlParams.get('scene')
+var expe  = urlParams.get('expe')
+
+function checkKey(e) {
+
+   e = e || window.event;
+
+   if (e.keyCode == KEYCODE_Q) {
+        // `q` for quit expe
+        console.log('`q` key is pressed')
+        window.location = ''
+   }
+   else if (e.keyCode == KEYCODE_ENTER) {
+
+        // right arrow
+        var params = "?scene=" + scene + "&expe=" + expe + "&iteration=0"
+        window.location = baseUrl + expe_url + params
+   }
+}

+ 65 - 22
static/js/keyEvents.js

@@ -1,29 +1,72 @@
 // implement `key` events
 document.onkeydown = checkKey;
 
+urlParams = new URLSearchParams(window.location.search);
+
+// Utils informations
+var KEYCODE_Q           = '81'
+var KEYCODE_ENTER       = '13'
+var KEYCODE_LEFT_ARROW  = '37'
+var KEYCODE_RIGHT_ARROW = '39'
+
+// get params if exists
+if (urlParams.has('scene')){
+
+   var scene = urlParams.get('scene')
+   var expe  = urlParams.get('expe')
+}
+
 function checkKey(e) {
 
-    e = e || window.event;
-
-
-    if (e.keyCode == '81') {
-        // `q` for quit expe
-       console.log('`q` key is pressed');
-       window.location = ''
-    }
-    else if (e.keyCode == '37') {
-       // left arrow
-       console.log('left arrow is pressed');
-    }
-    else if (e.keyCode == '39') {
-       // right arrow
-       console.log('right arrow is pressed');
-    }
-    else if (e.keyCode == '13') {
-       // right arrow
-       // TODO : avoid refresh with `enter` key when it is not necessary
-       console.log('right arrow is pressed');
-       window.location = window.location.href
-    }
+   e = e || window.event;
+
+   if (e.keyCode == '81') {
+      // `q` for quit expe
+      console.log('`q` key is pressed')
+      window.location = baseUrl
+   }
+   else if (e.keyCode == '13') {
+
+      console.log("Here")
+      // check if experience is begin
+      if (!BEGIN_EXPE){
+
+         console.log("And Here")
+         // right arrow
+         window.location = window.location.href + "&begin=true"
+      } 
+   }
+   else if (e.keyCode == '37' || e.keyCode == '39'){
+
+      // only do something is experience is begin
+      if (BEGIN_EXPE){
+         var answer;
+
+         // left arrow key
+         if (e.keyCode == '37'){
+            console.log('left arrow is pressed')
+            answer = '1'
+         }
+
+         // right arrow key
+         if (e.keyCode == '39'){
+            console.log('right arrow is pressed')
+            answer = '0'
+         }
+
+         var iteration = 0;
+
+         // update of iteration if exists
+         if (urlParams.has('iteration')){
+            iteration = urlParams.get('iteration')
 
+            // increment step
+            iteration++;
+         }
+         
+         // construct url with params for experience
+         var params = "?scene=" + scene + "&expe=" + expe + "&iteration=" + iteration + "&answer=" + answer
+         window.location = baseExpeUrl + params
+      }
+   }
 }

+ 21 - 20
static/js/loadImg.js

@@ -1,26 +1,27 @@
-// https://stackoverflow.com/questions/20756042/javascript-how-to-display-image-from-byte-array-using-javascript-or-servlet
-
 window.onload = function () {
-    console.log('Load img here...');
-
-
-    /*img_data = document.getElementById('expeImg').getAttribute('data-img');
-    img_data = JSON.parse(img_data);
 
-    var p = new PNGlib(800, 800, 256); // construcor takes height, weight and color-depth
-    var background = p.color(0, 0, 0, 0); // set the background transparent
+    console.log("End expe " + END_EXPE)
 
-    for (var i = 0; i < 800; i++){
-        for (var j = 0; j < 800; j++){
-
-            let r = img_data[i][j][0]
-            let g = img_data[i][j][1]
-            let b = img_data[i][j][2]
-
-            p.buffer[i, j] = p.color(r, g, b)
-        }
+    // only if not end of expe
+    if (END_EXPE == "False"){
+        setTimeout(function(){ 
+            document.getElementById("expeImg").style.display = "inline";
+        }, 500);
     }
-    console.log('done')
 
-    document.getElementById('expeImg').src = "data:image/png;base64,"+p.getBase64();*/
+    // redirect if end of expe after 5 sec
+    if (END_EXPE == "True"){
+        
+        for(var i=0; i<6; i++){
+            ((x)=>{
+                setTimeout(()=> 
+                    document.getElementById("refreshTime").textContent = 5 - x
+                ,1000 * i)
+            })(i);
+        } 
+
+        setTimeout(function(){ 
+            window.location = baseUrl
+        }, 5000);
+    }
 }

+ 0 - 214
static/js/pnglib.js

@@ -1,214 +0,0 @@
-/**
-* A handy class to calculate color values.
-*
-* @version 1.0
-* @author Robert Eisele <robert@xarg.org>
-* @copyright Copyright (c) 2010, Robert Eisele
-* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/
-* @license http://www.opensource.org/licenses/bsd-license.php BSD License
-*
-*/
-
-(function() {
-
-	// helper functions for that ctx
-	function write(buffer, offs) {
-		for (var i = 2; i < arguments.length; i++) {
-			for (var j = 0; j < arguments[i].length; j++) {
-				buffer[offs++] = arguments[i].charAt(j);
-			}
-		}
-	}
-
-	function byte2(w) {
-		return String.fromCharCode((w >> 8) & 255, w & 255);
-	}
-
-	function byte4(w) {
-		return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255);
-	}
-
-	function byte2lsb(w) {
-		return String.fromCharCode(w & 255, (w >> 8) & 255);
-	}
-
-	window.PNGlib = function(width,height,depth) {
-
-		this.width   = width;
-		this.height  = height;
-		this.depth   = depth;
-
-		// pixel data and row filter identifier size
-		this.pix_size = height * (width + 1);
-
-		// deflate header, pix_size, block headers, adler32 checksum
-		this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4;
-
-		// offsets and sizes of Png chunks
-		this.ihdr_offs = 0;									// IHDR offset and size
-		this.ihdr_size = 4 + 4 + 13 + 4;
-		this.plte_offs = this.ihdr_offs + this.ihdr_size;	// PLTE offset and size
-		this.plte_size = 4 + 4 + 3 * depth + 4;
-		this.trns_offs = this.plte_offs + this.plte_size;	// tRNS offset and size
-		this.trns_size = 4 + 4 + depth + 4;
-		this.idat_offs = this.trns_offs + this.trns_size;	// IDAT offset and size
-		this.idat_size = 4 + 4 + this.data_size + 4;
-		this.iend_offs = this.idat_offs + this.idat_size;	// IEND offset and size
-		this.iend_size = 4 + 4 + 4;
-		this.buffer_size  = this.iend_offs + this.iend_size;	// total PNG size
-
-		this.buffer  = new Array();
-		this.palette = new Object();
-		this.pindex  = 0;
-
-		var _crc32 = new Array();
-
-		// initialize buffer with zero bytes
-		for (var i = 0; i < this.buffer_size; i++) {
-			this.buffer[i] = "\x00";
-		}
-
-		// initialize non-zero elements
-		write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03");
-		write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE');
-		write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS');
-		write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT');
-		write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND');
-
-		// initialize deflate header
-		var header = ((8 + (7 << 4)) << 8) | (3 << 6);
-		header+= 31 - (header % 31);
-
-		write(this.buffer, this.idat_offs + 8, byte2(header));
-
-		// initialize deflate block headers
-		for (var i = 0; (i << 16) - 1 < this.pix_size; i++) {
-			var size, bits;
-			if (i + 0xffff < this.pix_size) {
-				size = 0xffff;
-				bits = "\x00";
-			} else {
-				size = this.pix_size - (i << 16) - i;
-				bits = "\x01";
-			}
-			write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size));
-		}
-
-		/* Create crc32 lookup table */
-		for (var i = 0; i < 256; i++) {
-			var c = i;
-			for (var j = 0; j < 8; j++) {
-				if (c & 1) {
-					c = -306674912 ^ ((c >> 1) & 0x7fffffff);
-				} else {
-					c = (c >> 1) & 0x7fffffff;
-				}
-			}
-			_crc32[i] = c;
-		}
-
-		// compute the index into a png for a given pixel
-		this.index = function(x,y) {
-			var i = y * (this.width + 1) + x + 1;
-			var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i;
-			return j;
-		}
-
-		// convert a color and build up the palette
-		this.color = function(red, green, blue, alpha) {
-
-			alpha = alpha >= 0 ? alpha : 255;
-			var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue;
-
-			if (typeof this.palette[color] == "undefined") {
-				if (this.pindex == this.depth) return "\x00";
-
-				var ndx = this.plte_offs + 8 + 3 * this.pindex;
-
-				this.buffer[ndx + 0] = String.fromCharCode(red);
-				this.buffer[ndx + 1] = String.fromCharCode(green);
-				this.buffer[ndx + 2] = String.fromCharCode(blue);
-				this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha);
-
-				this.palette[color] = String.fromCharCode(this.pindex++);
-			}
-			return this.palette[color];
-		}
-
-		// output a PNG string, Base64 encoded
-		this.getBase64 = function() {
-
-			var s = this.getDump();
-
-			// If the current browser supports the Base64 encoding
-			// function, then offload the that to the browser as it
-			// will be done in native code.
-			if ((typeof window.btoa !== 'undefined') && (window.btoa !== null)) {
-				return window.btoa(s);
-			}
-
-			var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-			var c1, c2, c3, e1, e2, e3, e4;
-			var l = s.length;
-			var i = 0;
-			var r = "";
-
-			do {
-				c1 = s.charCodeAt(i);
-				e1 = c1 >> 2;
-				c2 = s.charCodeAt(i+1);
-				e2 = ((c1 & 3) << 4) | (c2 >> 4);
-				c3 = s.charCodeAt(i+2);
-				if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); }
-				if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; }
-				r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4);
-			} while ((i+= 3) < l);
-			return r;
-		}
-
-		// output a PNG string
-		this.getDump = function() {
-
-			// compute adler32 of output pixels + row filter bytes
-			var BASE = 65521; /* largest prime smaller than 65536 */
-			var NMAX = 5552;  /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
-			var s1 = 1;
-			var s2 = 0;
-			var n = NMAX;
-
-			for (var y = 0; y < this.height; y++) {
-				for (var x = -1; x < this.width; x++) {
-					s1+= this.buffer[this.index(x, y)].charCodeAt(0);
-					s2+= s1;
-					if ((n-= 1) == 0) {
-						s1%= BASE;
-						s2%= BASE;
-						n = NMAX;
-					}
-				}
-			}
-			s1%= BASE;
-			s2%= BASE;
-			write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1));
-
-			// compute crc32 of the PNG chunks
-			function crc32(png, offs, size) {
-				var crc = -1;
-				for (var i = 4; i < size-4; i += 1) {
-					crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
-				}
-				write(png, offs+size-4, byte4(crc ^ -1));
-			}
-
-			crc32(this.buffer, this.ihdr_offs, this.ihdr_size);
-			crc32(this.buffer, this.plte_offs, this.plte_size);
-			crc32(this.buffer, this.trns_offs, this.trns_size);
-			crc32(this.buffer, this.idat_offs, this.idat_size);
-			crc32(this.buffer, this.iend_offs, this.iend_size);
-
-			// convert PNG to string
-			return "\211PNG\r\n\032\n"+this.buffer.join('');
-		}
-	}
-
-})();