commit cdcd870b6f9f15b1bac4541e890d7bf5657304b0 Author: Andreas Wilms Date: Mon Sep 8 16:25:55 2025 +0200 init diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d8b0236 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +PCSurvey/potree/pointclouds \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3e8dd35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +PCSurvey/potree/pointclouds +*.tar \ No newline at end of file diff --git a/DOC.md b/DOC.md new file mode 100644 index 0000000..16a58dd --- /dev/null +++ b/DOC.md @@ -0,0 +1,48 @@ + +# Interactive Point Cloud Study + +This project is the implementation of a point cloud study on QoE described in my Bachelor’s thesis. + +### Brief Summary of Study Flow: +- User and task instructions +- Trial run +- Study begins +- End / Feedback / Code issuance + + +## Documentation + +The folder **PCSurvey** contains all the code for the study. The folder **PCStatic** contains images and HTML files for the instruction and information phases of the study. + +The files *survey.html* and *survey.js* serve as the entry point and logic for the study. The *survey.js* file constructs the study flow using jsPsych. +### The "potree" folder +It contains a folder **pointclouds**, which includes all the point clouds used for the study. + +The file *paired_comparison_template.html* creates the interface for this evaluation condition. This interface was developed early on but ultimately was not used. It requires minor adaptations but is overall functional. + +The files *sequentiell_template.html* and *sequentiell_template2.html* serve as the interfaces for the sequential evaluation method. We have two versions: one is the first, and the other is the second. It is necessary for them to be separate, even though they are very similar, due to the external HTML loading process in jsPsych. + +The files *single_page_template_\*.html* are designated for each element and test condition (1, 2, and 3 elements in the cloud). Each stimulus varies in size and requires special configuration. These files manage those configurations. + +All the mentioned files are used in the *survey.js* file to dynamically generate the evaluation block for the study. + + +## Deployment +The *docker-compose.yml* file provides an example configuration for starting the Docker container with the study. It builds the container using the *Dockerfile* and mounts the volumes for the point cloud folder and the output folder for the captured user data. + +Configure mounting and ports based on your case. + +```bash + docker compose up +``` + +After deployment, it can be accessed on: + +http://localhost/survey.html?worker=0&campaign=0&randkey=0 + +The parameters are necessary to pass the requirement test for the study. + +## Authors + +- Andreas Wilms + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..86089ea --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# Verwenden Sie das offizielle PHP-Bild mit Apache +FROM php:8.1-apache + +# Aktivieren Sie Apache-Module +RUN a2enmod rewrite ssl + +# Kopieren Sie die PHP-Anwendungsdateien in das richtige Verzeichnis +COPY PCSurvey/ /var/www/html/ + +# Kopieren Sie die SSL-Zertifikate in die richtigen Verzeichnisse +COPY server.pem /etc/ssl/certs/server.pem +COPY server.key /etc/ssl/private/server.key + +# Kopieren Sie die Apache SSL-Konfigurationsdatei +COPY httpd-ssl.conf /etc/apache2/sites-available/default-ssl.conf + +# Fügen Sie eine ServerName Direktive in der Apache-Konfigurationsdatei hinzu +RUN echo "ServerName netcom-survey2.informatik.uni-augsburg.de" >> /etc/apache2/apache2.conf + +# Aktivieren Sie die Standard-SSL-Site +RUN a2ensite default-ssl + +# Exponieren Sie die Ports 80 und 443 +EXPOSE 80 443 + +# Stellen Sie sicher, dass die Berechtigungen für die Anwendungsdateien korrekt sind +RUN chown -R www-data:www-data /var/www/html +RUN chmod -R 755 /var/www/html + +# Create the /Data directory and set permissions +RUN mkdir -p /Data +RUN chown -R www-data:www-data /Data +RUN chmod -R 755 /Data + +# Starten Sie den Apache-Server +CMD ["apache2-foreground"] diff --git a/PCSurvey/.DS_Store b/PCSurvey/.DS_Store new file mode 100644 index 0000000..a273c91 Binary files /dev/null and b/PCSurvey/.DS_Store differ diff --git a/PCSurvey/PCstatic/css/instructions.css b/PCSurvey/PCstatic/css/instructions.css new file mode 100644 index 0000000..3175a45 --- /dev/null +++ b/PCSurvey/PCstatic/css/instructions.css @@ -0,0 +1,92 @@ +@font-face { + font-family: 'Cardo'; + src: url(https://jonathan-harrell.com/wp-content/themes/jonathanharrell/fonts/Cardo-Italic.woff) format('woff2'); +} + +#intro_text { + max-width: 40rem; + margin: 0 auto; + padding: 1rem; + text-align: left; + font-size: 16px; +} + +section { + margin-top: 3rem; +} + +#intro_text h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2; +} + +a { + text-decoration: none; + color: #2eec96; +} + +hr { + height: 1px; + padding: 0; + border: 0; + background-color: #ccc; +} + + +.instructions ul, +.instructions ol { + margin: 0.075rem 0; +} + +.instructions li { + margin-bottom: 0.25rem; +} + +.instructions ul { + list-style: none; + padding-left: 50px; + margin: 0; +} + +.instructions ul li { + position: relative; + padding-left: 1em; +} + +.instructions ul li::before { + position: absolute; + left: 0; + content: '•'; +} + +.instructions ol { + padding-left: 1.2rem; +} + +figure { + margin: 2rem 0; + padding: 1rem 0; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} + +#intro_text header h1 { + margin-top: 0; + text-align: center; +} + +header p { + text-align: center; +} + +p.thick { + font-weight: bold; +} + +#consent { + margin-bottom: 50px +} diff --git a/PCSurvey/PCstatic/css/jspsych.css b/PCSurvey/PCstatic/css/jspsych.css new file mode 100644 index 0000000..1e53897 --- /dev/null +++ b/PCSurvey/PCstatic/css/jspsych.css @@ -0,0 +1,125 @@ +/* + * CSS for jsPsych experiments. + * + * This stylesheet provides minimal styling to make jsPsych + * experiments look polished without any additional styles. + */ + + @import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); + +/* Container holding jsPsych content */ + + .jspsych-display-element { + display: flex; + flex-direction: column; + overflow-y: auto; + } + + .jspsych-display-element:focus { + outline: none; + } + + .jspsych-content-wrapper { + display: flex; + margin: auto; + flex: 1 1 100%; + width: 100%; + } + + .jspsych-content { + max-width: 95%; /* this is mainly an IE 10-11 fix */ + text-align: center; + margin: auto; /* this is for overflowing content */ + } + + .jspsych-top { + align-items: flex-start; + } + + .jspsych-middle { + align-items: center; + } + +/* fonts and type */ + +.jspsych-display-element { + font-family: 'Open Sans', 'Arial', sans-serif; + font-size: 18px; + line-height: 1.6em; +} + +/* Form elements like input fields and buttons */ + +.jspsych-display-element input[type="text"] { + font-family: 'Open Sans', 'Arial', sans-serif; + font-size: 14px; +} + +/* borrowing Bootstrap style for btn elements, but combining styles a bit */ +.jspsych-btn { + display: inline-block; + padding: 6px 12px; + margin: 0px; + font-size: 14px; + font-weight: 400; + font-family: 'Open Sans', 'Arial', sans-serif; + cursor: pointer; + line-height: 1.4; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; + color: #333; + background-color: #fff; + border-color: #ccc; +} + +.jspsych-btn:hover { + background-color: #ddd; + border-color: #aaa; +} + +.jspsych-btn:disabled { + background-color: #eee; + color: #aaa; + border-color: #ccc; + cursor: not-allowed; +} + +/* jsPsych progress bar */ + +#jspsych-progressbar-container { + color: #555; + border-bottom: 1px solid #dedede; + background-color: #f9f9f9; + margin-bottom: 1em; + text-align: center; + padding: 8px 0px; + width: 100%; + line-height: 1em; +} +#jspsych-progressbar-container span { + font-size: 14px; + padding-right: 14px; +} +#jspsych-progressbar-outer { + background-color: #eee; + width: 50%; + margin: auto; + height: 14px; + display: inline-block; + vertical-align: middle; + box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); +} +#jspsych-progressbar-inner { + background-color: #aaa; + width: 0%; + height: 100%; +} + +/* Control appearance of jsPsych.data.displayData() */ +#jspsych-data-display { + text-align: left; +} diff --git a/PCSurvey/PCstatic/css/main.css b/PCSurvey/PCstatic/css/main.css new file mode 100644 index 0000000..af68a89 --- /dev/null +++ b/PCSurvey/PCstatic/css/main.css @@ -0,0 +1,187 @@ +.clear_content { + all:initial; +} + +.test { + display: none; +} + +#end-trial { + +} + +#replay { + +} + +.container > div { + visibility:hidden; +} + +.debug { + display:none; + background: #000; + color: #ff2277 +} + +.hidden { + visibility: hidden; +} + +.object-fit_fill { object-fit: fill } +.object-fit_contain { object-fit: contain } +.object-fit_cover { object-fit: cover } +.object-fit_none { object-fit: none } +.object-fit_scale-down { object-fit: scale-down } + +.work_container { + display: grid; + grid-template-rows: auto; + grid-template-columns: 188px auto 188px; + grid-template-areas: + "work1 work2 work3 " +} + +.container { + font-family: "Courier New", Courier, serif; + display: grid; + grid-template-rows: 30px 100px 120px 40px 377px 377px 200px; + grid-template-columns: auto 565px 565px auto; + grid-gap: 20px 20px; + grid-template-areas: + ". blank blank . " + ". header_l header_r . " + ". title_l title_r . " + ". spacer spacer ." + ". image1 image2 ." + ". image3 image4 ." + ". about work ." +} + +.container_noimages { + font-family: "Courier New", Courier, serif; + display: grid; + grid-template-rows: 30px 100px 120px 40px 200px; + grid-template-columns: auto 565px 565px auto; + grid-gap: 20px 20px; + grid-template-areas: + ". blank blank . " + ". header_l header_r . " + ". title_l title_r . " + ". spacer spacer ." + ". about work ." +} + + +.footer { + visibility: hidden; + font-family: "Courier New", Courier, serif; + display: grid; + grid-template-rows: 30px 100px 30px; + grid-template-columns: auto 585px 565px auto; + background: #001122; + grid-template-areas: + ". . . . " + ". footer_left footer_right . " + ". . . . " +} + +.blank { + grid-area: blank; +} + +.spacer { + grid-area: spacer; + border-top: 1px solid #eee; +} + + +.left_header { + grid-area: header_l; +} + +.right_header { + grid-area: header_r; + text-align: right; + font-size: 10px +} + +.left_title { + grid-area: title_l; +} + +.left_title { + font-size: 48px; +} + +.right_title { + grid-area: title_r; +} + +.img1{ + grid-area: image1; +} + +.img2{ + grid-area: image2; +} + +.img3 { + grid-area: image3; +} + +.img4 { + grid-area: image4; +} + +.worklist { + grid-area: work; + text-align: center; +} + +.about { + grid-area: about; +} + +.worklist.work1{ + grid-area: work1; + display: inline-block; +} + +.worklist.work2{ + grid-area: work2; + display: inline-block; +} + +.worklist.work3{ + grid-area: work3; + display: inline-block; +} + +.footer_left { + grid-area: footer_left; + color: #fff +} + +.header { + background: #001122; + height: 130px +} + +img[class] { + width: 100%; +} + +.left_header > img { + width: 50px; +} + +.smallimg { + height:48px; + padding-top: 20px; + padding-bottom: 10px +} + +.experts { + font-size: 19px +} diff --git a/PCSurvey/PCstatic/css/main_3col.css b/PCSurvey/PCstatic/css/main_3col.css new file mode 100644 index 0000000..5245d4c --- /dev/null +++ b/PCSurvey/PCstatic/css/main_3col.css @@ -0,0 +1,259 @@ +#currentscore, #total_current_score { + visibility:hidden; +} + +.clear_content { + all:initial; +} + +.test { + display: none; +} + +#end-trial { + width: 110px; + height: 45px; +} + +#infotext { + width: 100%; +} + +#left-right{ + width: 45%; + height: 20px; +} + +#right-left{ + width: 45%; + height: 20px; +} + +#replay { + width: 100px; + height: 47px; +} + +.container > div { + visibility:hidden; +} + +.head > div { + visibility:hidden; +} + +.footer > div { + visibility:hidden; +} + +.debug { + display:none; + background: #000; + color: #ff2277 +} + +.hidden { + visibility: hidden; +} + +.display_toggle { + display: none; +} + +.object-fit_fill { object-fit: fill } +.object-fit_contain { object-fit: contain } +.object-fit_cover { object-fit: cover } +.object-fit_none { object-fit: none } +.object-fit_scale-down { object-fit: scale-down } + +.work_container { + display: grid; + grid-template-rows: auto; + grid-template-columns: 120px 200px 200px 200px; + grid-template-areas: + " . work1 work2 work3 " +} + +.head { + visibility: hidden; + font-family: "Courier New", Courier, serif; + display: grid; + /* grid-template-rows: 30px 100px 120px 40px 377px 377px 250 1fr; */ + grid-template-columns: auto 350px 350px 350px auto; + grid-gap: 20px 20px; + grid-template-areas: + ". blank blank blank . " + ". header_l header_l header_r . " + ". title_l title_l title_r . " + ". spacer spacer spacer ." +} + +.container { + align-items: center; + font-family: "Courier New", Courier, serif; + display: grid; + /* grid-template-rows: 30px 100px 120px 40px 377px 377px 250 1fr; */ + grid-template-columns: auto 350px 350px 350px auto; + grid-gap: 20px 20px; + grid-template-areas: + /* ". blank blank blank . " + ". header_l header_l header_r . " + ". title_l title_l title_r . " + ". spacer spacer spacer ." */ + ". text1 image1 image2 ." + ". image3 image4 text2 ." + ". image5 text3 image6 ." + ". about work work ." + ". hfh hfh hfh ." +} + +.footer { + align-items: left; + visibility: hidden; + font-family: "Courier New", Courier, serif; + display: grid; + grid-template-rows: 30px 100px 30px; + grid-gap: 20px 20px; + grid-template-columns: auto 350px 350px 350px auto; + background: #001122; + grid-template-areas: + ". . . . . " + ". footer_left footer_left footer_right . " + ". . . . . " +} + +.blank { + grid-area: blank; +} + +.spacer { + grid-area: spacer; + border-top: 1px solid #eee; + height: 30px +} + +.text1 { + grid-area: text1; +} + +.text2 { + grid-area: text2; +} + +.text3 { + grid-area: text3; +} + + +.left_header { + grid-area: header_l; +} + +.right_header { + grid-area: header_r; + text-align: right; + font-size: 10px +} + +.left_title { + grid-area: title_l; +} + +.left_title { + font-size: 48px; +} + +.right_title { + grid-area: title_r; +} + +.img1{ + grid-area: image1; +} + +.img2{ + grid-area: image2; +} + +.img3 { + grid-area: image3; +} + +.img4 { + grid-area: image4; +} + +.img5 { + grid-area: image5; +} + +.img6 { + grid-area: image6; +} + +.worklist { + grid-area: work; + text-align: center; +} + +.about { + grid-area: about; +} + +.work1{ + grid-area: work1; + display: inline-block +} + +.work2{ + grid-area: work2; + display: inline-block; +} + +.work3{ + grid-area: work3; + display: inline-block; +} + +.footer_left { + grid-area: footer_left; + color: #fff +} + +.header { + background: #001122; + height: 130px +} + +.test img { + width: 100%; +} + +.left_header > img { + width: 50px; +} + +.smallimg { + height:48px; + padding-top: 20px; + padding-bottom: 10px +} + +.experts { + font-size: 19px +} + +h4 { + font-size: 19px +} + +#screenshot { + position: auto; +} + +#slider { + width: 40%; + height: auto; + padding-top: 20px; + padding-bottom: 20px +} diff --git a/PCSurvey/PCstatic/html/feedback.html b/PCSurvey/PCstatic/html/feedback.html new file mode 100644 index 0000000..b843a74 --- /dev/null +++ b/PCSurvey/PCstatic/html/feedback.html @@ -0,0 +1,79 @@ + + + + + + Document + + + +
+
+

Incorrect! You selected the wrong letter.

+

Don't worry, you can try again!

+
+ + +
+ + + diff --git a/PCSurvey/PCstatic/html/feedback_no_emphasis_quality.html b/PCSurvey/PCstatic/html/feedback_no_emphasis_quality.html new file mode 100644 index 0000000..c0b0144 --- /dev/null +++ b/PCSurvey/PCstatic/html/feedback_no_emphasis_quality.html @@ -0,0 +1,74 @@ + + + + + + Document + + + +
+
+

Incorrect! You selected the wrong letter.

+

Don't worry, you can try again!

+
+ + +
+ + + diff --git a/PCSurvey/PCstatic/html/instructions.html b/PCSurvey/PCstatic/html/instructions.html new file mode 100644 index 0000000..ca9f1cf --- /dev/null +++ b/PCSurvey/PCstatic/html/instructions.html @@ -0,0 +1,44 @@ + + + + + + + + + +
+
+

Test Instructions

+
+
+
+

Your Task

+

You will first see a picture in original quality (left).

+
+ + +
+

After that you see a picture of a reduced quality (right).

+

Your Task:

+
    +
  • Rate the quality of the picture.
  • +
      +
    • Good quality is given if the surfaces of objects are smooth and easily recognisable.
    • +
    • A picture of bad quality has less recognisable surfaces.
    • +
    +
  • Rate the difference in quality between the original picture and the reduced picture.
  • +
  • In addition on each picture there will be a red letter in the top right corner. Memorize it and Tick/Check the corresponding box your rating.
  • +
+

+

Further informations on the topic are at the end of the survey.

+
+ +
+
+ + + + + diff --git a/PCSurvey/PCstatic/html/instructions_P0.html b/PCSurvey/PCstatic/html/instructions_P0.html new file mode 100644 index 0000000..6387d37 --- /dev/null +++ b/PCSurvey/PCstatic/html/instructions_P0.html @@ -0,0 +1,150 @@ + + + + + + Point Clouds and VR: Research Introduction + + + +
+
+

Point Clouds and Virtual Reality: Essential Research Introduction

+ +
+ Please read the following introduction as it can be helpful for understanding the research. +
+ +

What is a Point Cloud?

+

A point cloud is a set of data points in 3D space, typically representing the external surface of an object or environment. Each point has X, Y, and Z coordinates and may include additional information like color or intensity.

+ +
+ Key Features of Point Clouds: +
    +
  • Detailed 3D representation of objects and environments
  • +
  • Created using 3D scanners or LiDAR technology
  • +
  • Can contain millions of points for high-resolution models
  • +
+
+ + Point Cloud Visualization + +

Connection to Virtual Reality (VR)

+

Point clouds play a crucial role in creating immersive VR experiences by bridging the gap between physical and virtual worlds.

+ +
+ How Point Clouds Enhance VR: +
    +
  • Create detailed and accurate 3D models of real-world environments
  • +
  • Enable the generation of immersive VR experiences
  • +
  • Allow users to explore digitally recreated spaces
  • +
  • Provide a data-driven foundation for realistic environmental representations
  • +
+
+ + +

By utilizing point clouds in VR, researchers and developers can create highly realistic and interactive virtual environments, revolutionizing fields such as architecture, gaming, virtual tourism, and more.

+ +

Understanding these concepts is essential for delving into advanced research topics in 3D visualization, computer vision, and virtual reality applications.

+
+
+ + + + \ No newline at end of file diff --git a/PCSurvey/PCstatic/html/instructions_P1.html b/PCSurvey/PCstatic/html/instructions_P1.html new file mode 100644 index 0000000..c528970 --- /dev/null +++ b/PCSurvey/PCstatic/html/instructions_P1.html @@ -0,0 +1,155 @@ + + + + + + Instructions + + + + +
+ +
+

Research Experiment: Point Cloud Quality Evaluation

+ +
+

Key Focus: Evaluate the QUALITY of point clouds in virtual reality environments.

+
+ +

Experiment Structure

+

You will participate in 4 blocks of evaluations. Each block consists of the following:

+
    +
  1. Viewing and evaluating an initial point cloud
  2. +
  3. Viewing and evaluating a second point cloud
  4. +
  5. Rating the difference between the two point clouds
  6. +
+

Your Task

+
+ 1. + You will be immersed in a point cloud environment. +
+
+ 2. + Explore the point cloud to familiarize yourself with it for later evaluation. +
+ Point Cloud Image +
+ 3. + As a control measure, there are three letters/numbers hidden in the cloud which you must identify. You will be asked about it later during the evaluation. +
+ Letter Point Cloud Image +
+ 4. + Please rate the quality of the point cloud on a scale and enter the corresponding control letters/numbers that you observed. +
+ Rating Scale Image +
+ 5. + You will then experience a second point cloud. +
+
+ 6. + Repeat steps 2 and 3 for the new cloud, then rate its quality, enter the control letters/numbers from the cloud, and assess the difference in quality compared to the previous one. +
+ Different Compression Settings Image + +
+

Important: While finding the hidden letters/numbers is part of the mechanism to validate the interaction, the primary focus is on evaluating the overall quality of the point cloud.

+
+ +

This sequence of two point clouds and their evaluations will be repeated 4 times (4 blocks).

+

After completing all blocks, you will have the opportunity to provide feedback, which we greatly appreciate for further improvement of our research.

+

To help you familiarize yourself with the survey, you will complete a trial run beforehand.

+ +
+
+ + + + diff --git a/PCSurvey/PCstatic/html/instructions_P1_no_ emphasis_quality.html b/PCSurvey/PCstatic/html/instructions_P1_no_ emphasis_quality.html new file mode 100644 index 0000000..f3c4948 --- /dev/null +++ b/PCSurvey/PCstatic/html/instructions_P1_no_ emphasis_quality.html @@ -0,0 +1,151 @@ + + + + + + Instructions + + + + +
+ +
+

Research Experiment: Point Cloud Quality Evaluation

+ +
+

Key Focus: Evaluate the QUALITY of point clouds in virtual reality environments.

+
+ +

Experiment Structure

+

You will participate in 4 blocks of evaluations. Each block consists of the following:

+
    +
  1. Viewing and evaluating an initial point cloud
  2. +
  3. Viewing and evaluating a second point cloud
  4. +
  5. Rating the difference between the two point clouds
  6. +
+

Your Task

+
+ 1. + You will be immersed in a point cloud environment. +
+
+ 2. + Explore the point cloud to familiarize yourself with it for later evaluation. +
+ Point Cloud Image +
+ 3. + As a control measure, there are one letter/number hidden in the cloud which you must identify. You will be asked about it later during the evaluation. +
+ Letter Point Cloud Image +
+ 4. + Please rate the quality of the point cloud on a scale and enter the corresponding control letter/number that you observed. +
+ Rating Scale Image +
+ 5. + You will then experience a second point cloud. +
+
+ 6. + Repeat steps 2 and 3 for the new cloud, then rate its quality, enter the control letter/number from the cloud, and assess the difference in quality compared to the previous one. +
+ Different Compression Settings Image + +

This sequence of two point clouds and their evaluations will be repeated 4 times (4 blocks).

+

After completing all blocks, you will have the opportunity to provide feedback, which we greatly appreciate for further improvement of our research.

+

To help you familiarize yourself with the survey, you will complete a trial run beforehand.

+ +
+
+ + + + diff --git a/PCSurvey/PCstatic/html/instructions_P2.html b/PCSurvey/PCstatic/html/instructions_P2.html new file mode 100644 index 0000000..a48fafc --- /dev/null +++ b/PCSurvey/PCstatic/html/instructions_P2.html @@ -0,0 +1,109 @@ + + + + + + Instructions + + + +
+
+

Controls

+

Hold the left mouse button and move the mouse to adjust your perspective.

+ +

Use the W, A, S, and D keys to change your position:

+
    +
  • W - Move forward
  • +
  • S - Move backward
  • +
  • D - Move right
  • +
  • A - Move left
  • +
+ +

Combine mouse and keyboard controls to navigate through the cloud.

+ +
+
+ + + + diff --git a/PCSurvey/PCstatic/html/instructions_V.html b/PCSurvey/PCstatic/html/instructions_V.html new file mode 100644 index 0000000..c71bbb3 --- /dev/null +++ b/PCSurvey/PCstatic/html/instructions_V.html @@ -0,0 +1,47 @@ + + + + + + + + + +
+
+

Test Instructions

+
+
+
+

Your Task

+

You will first see a video in original quality:

+
+ +
+

After that, you see the same video in reduced quality:

+
+ +
+

Your Task:

+
    +
  • Rate the quality of the videos.
  • +
      +
    • Good quality is given if the surfaces of objects are smooth and easily recognisable.
    • +
    • A video of bad quality has less recognisable surfaces.
    • +
    +
  • Rate the difference in quality between the original video and the reduced video.
  • +
  • Even in the original video you might see areas that are not fully scanned (Areas are missing e.g. a roof). This should not influence your rating!
  • +
  • In addition at the end of each video there will be a red letter in the top right corner. Memorize it and tick/check the corresponding box below your rating.
  • +
+

+

Further informations on the topic are at the end of the survey.

+
+ +
+
+ + + + + diff --git a/PCSurvey/PCstatic/html/welcome.html b/PCSurvey/PCstatic/html/welcome.html new file mode 100644 index 0000000..f07537e --- /dev/null +++ b/PCSurvey/PCstatic/html/welcome.html @@ -0,0 +1,98 @@ + + + + + + + + + + +
+
+

Thank you for your participation in this Survey.

+

Before starting the survey, please read the test instructions on the next page carefully.

+
+

Important!

+ Please follow the following rules to ensure your participation is rated as successful: +
    +
  • Please do NOT Switch tabs or programs during the test.
  • +
  • Please do NOT Zoom in or out during the test.
  • +
  • Please do NOT Reload the page once you have begun the test.
  • +
+
+ +
+ + + diff --git a/PCSurvey/PCstatic/img/Octree.png b/PCSurvey/PCstatic/img/Octree.png new file mode 100644 index 0000000..5047521 Binary files /dev/null and b/PCSurvey/PCstatic/img/Octree.png differ diff --git a/PCSurvey/PCstatic/img/letter_pointcloud.png b/PCSurvey/PCstatic/img/letter_pointcloud.png new file mode 100644 index 0000000..238be59 Binary files /dev/null and b/PCSurvey/PCstatic/img/letter_pointcloud.png differ diff --git a/PCSurvey/PCstatic/img/movement.gif b/PCSurvey/PCstatic/img/movement.gif new file mode 100644 index 0000000..640e528 Binary files /dev/null and b/PCSurvey/PCstatic/img/movement.gif differ diff --git a/PCSurvey/PCstatic/img/perspective.gif b/PCSurvey/PCstatic/img/perspective.gif new file mode 100644 index 0000000..c01cf97 Binary files /dev/null and b/PCSurvey/PCstatic/img/perspective.gif differ diff --git a/PCSurvey/PCstatic/img/pointcloud.png b/PCSurvey/PCstatic/img/pointcloud.png new file mode 100644 index 0000000..c1a8e56 Binary files /dev/null and b/PCSurvey/PCstatic/img/pointcloud.png differ diff --git a/PCSurvey/PCstatic/img/pointcloud_example.png b/PCSurvey/PCstatic/img/pointcloud_example.png new file mode 100644 index 0000000..a6313e2 Binary files /dev/null and b/PCSurvey/PCstatic/img/pointcloud_example.png differ diff --git a/PCSurvey/PCstatic/img/position.gif b/PCSurvey/PCstatic/img/position.gif new file mode 100644 index 0000000..d8d790f Binary files /dev/null and b/PCSurvey/PCstatic/img/position.gif differ diff --git a/PCSurvey/PCstatic/img/seq_1.png b/PCSurvey/PCstatic/img/seq_1.png new file mode 100644 index 0000000..2ab48a6 Binary files /dev/null and b/PCSurvey/PCstatic/img/seq_1.png differ diff --git a/PCSurvey/PCstatic/img/seq_2.png b/PCSurvey/PCstatic/img/seq_2.png new file mode 100644 index 0000000..e5ff4b8 Binary files /dev/null and b/PCSurvey/PCstatic/img/seq_2.png differ diff --git a/PCSurvey/PCstatic/js/external/json2.min.js b/PCSurvey/PCstatic/js/external/json2.min.js new file mode 100644 index 0000000..273eca0 --- /dev/null +++ b/PCSurvey/PCstatic/js/external/json2.min.js @@ -0,0 +1 @@ +if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i tag and the entire page + if(typeof opts.display_element == 'undefined'){ + // check if there is a body element on the page + var body = document.querySelector('body'); + if (body === null) { + document.documentElement.appendChild(document.createElement('body')); + } + // using the full page, so we need the HTML element to + // have 100% height, and body to be full width and height with + // no margin + document.querySelector('html').style.height = '100%'; + document.querySelector('body').style.margin = '0px'; + document.querySelector('body').style.height = '100%'; + document.querySelector('body').style.width = '100%'; + opts.display_element = document.querySelector('body'); + } else { + // make sure that the display element exists on the page + var display; + if (opts.display_element instanceof Element) { + var display = opts.display_element; + } else { + var display = document.querySelector('#' + opts.display_element); + } + if(display === null) { + console.error('The display_element specified in jsPsych.init() does not exist in the DOM.'); + } else { + opts.display_element = display; + } + } + opts.display_element.innerHTML = '
'; + DOM_container = opts.display_element; + DOM_target = document.querySelector('#jspsych-content'); + + + // add tabIndex attribute to scope event listeners + opts.display_element.tabIndex = 0; + + // add CSS class to DOM_target + if(opts.display_element.className.indexOf('jspsych-display-element') == -1){ + opts.display_element.className += ' jspsych-display-element'; + } + DOM_target.className += 'jspsych-content'; + + // set experiment_width if not null + if(opts.experiment_width !== null){ + DOM_target.style.width = opts.experiment_width + "px"; + } + + // create experiment timeline + timeline = new TimelineNode({ + timeline: opts.timeline + }); + + // initialize audio context based on options and browser capabilities + jsPsych.pluginAPI.initAudio(); + + // below code resets event listeners that may have lingered from + // a previous incomplete experiment loaded in same DOM. + jsPsych.pluginAPI.reset(opts.display_element); + // create keyboard event listeners + jsPsych.pluginAPI.createKeyboardEventListeners(opts.display_element); + // create listeners for user browser interaction + jsPsych.data.createInteractionListeners(); + + // add event for closing window + window.addEventListener('beforeunload', opts.on_close); + + // check exclusions before continuing + checkExclusions(opts.exclusions, + function(){ + // success! user can continue... + // start experiment, with or without preloading + if(opts.auto_preload){ + jsPsych.pluginAPI.autoPreload(timeline, startExperiment, opts.preload_images, opts.preload_audio, opts.preload_video, opts.show_preload_progress_bar); + if(opts.max_load_time > 0){ + setTimeout(function(){ + if(!loaded && !loadfail){ + core.loadFail(); + } + }, opts.max_load_time); + } + } else { + startExperiment(); + } + }, + function(){ + // fail. incompatible user. + + } + ); + }; + + core.progress = function() { + + var percent_complete = typeof timeline == 'undefined' ? 0 : timeline.percentComplete(); + + var obj = { + "total_trials": typeof timeline == 'undefined' ? undefined : timeline.length(), + "current_trial_global": global_trial_index, + "percent_complete": percent_complete + }; + + return obj; + }; + + core.startTime = function() { + return exp_start_time; + }; + + core.totalTime = function() { + if(typeof exp_start_time == 'undefined'){ return 0; } + return (new Date()).getTime() - exp_start_time.getTime(); + }; + + core.getDisplayElement = function() { + return DOM_target; + }; + + core.getDisplayContainerElement = function(){ + return DOM_container; + } + + core.finishTrial = function(data) { + + if(current_trial_finished){ return; } + current_trial_finished = true; + + // write the data from the trial + data = typeof data == 'undefined' ? {} : data; + jsPsych.data.write(data); + + // get back the data with all of the defaults in + var trial_data = jsPsych.data.get().filter({trial_index: global_trial_index}); + + // for trial-level callbacks, we just want to pass in a reference to the values + // of the DataCollection, for easy access and editing. + var trial_data_values = trial_data.values()[0]; + + // handle callback at plugin level + if (typeof current_trial.on_finish === 'function') { + current_trial.on_finish(trial_data_values); + } + + // handle callback at whole-experiment level + opts.on_trial_finish(trial_data_values); + + // after the above callbacks are complete, then the data should be finalized + // for this trial. call the on_data_update handler, passing in the same + // data object that just went through the trial's finish handlers. + opts.on_data_update(trial_data_values); + + // wait for iti + if (typeof current_trial.post_trial_gap === null || typeof current_trial.post_trial_gap === 'undefined') { + if (opts.default_iti > 0) { + setTimeout(nextTrial, opts.default_iti); + } else { + nextTrial(); + } + } else { + if (current_trial.post_trial_gap > 0) { + setTimeout(nextTrial, current_trial.post_trial_gap); + } else { + nextTrial(); + } + } + } + + core.endExperiment = function(end_message) { + timeline.end_message = end_message; + timeline.end(); + jsPsych.pluginAPI.cancelAllKeyboardResponses(); + jsPsych.pluginAPI.clearAllTimeouts(); + core.finishTrial(); + } + + core.endCurrentTimeline = function() { + timeline.endActiveNode(); + } + + core.currentTrial = function() { + return current_trial; + }; + + core.initSettings = function() { + return opts; + }; + + core.currentTimelineNodeID = function() { + return timeline.activeID(); + }; + + core.timelineVariable = function(varname, execute){ + if(execute){ + return timeline.timelineVariable(varname); + } else { + return function() { return timeline.timelineVariable(varname); } + } + } + + core.addNodeToEndOfTimeline = function(new_timeline, preload_callback){ + timeline.insert(new_timeline); + if(typeof preload_callback !== 'undefinded'){ + if(opts.auto_preload){ + jsPsych.pluginAPI.autoPreload(timeline, preload_callback); + } else { + preload_callback(); + } + } + } + + core.pauseExperiment = function(){ + paused = true; + } + + core.resumeExperiment = function(){ + paused = false; + if(waiting){ + waiting = false; + nextTrial(); + } + } + + core.loadFail = function(message){ + message = message || '

The experiment failed to load.

'; + loadfail = true; + DOM_target.innerHTML = message; + } + + function TimelineNode(parameters, parent, relativeID) { + + // a unique ID for this node, relative to the parent + var relative_id; + + // store the parent for this node + var parent_node; + + // parameters for the trial if the node contains a trial + var trial_parameters; + + // parameters for nodes that contain timelines + var timeline_parameters; + + // stores trial information on a node that contains a timeline + // used for adding new trials + var node_trial_data; + + // track progress through the node + var progress = { + current_location: -1, // where on the timeline (which timelinenode) + current_variable_set: 0, // which set of variables to use from timeline_variables + current_repetition: 0, // how many times through the variable set on this run of the node + current_iteration: 0, // how many times this node has been revisited + done: false + } + + // reference to self + var self = this; + + // recursively get the next trial to run. + // if this node is a leaf (trial), then return the trial. + // otherwise, recursively find the next trial in the child timeline. + this.trial = function() { + if (typeof timeline_parameters == 'undefined') { + // returns a clone of the trial_parameters to + // protect functions. + return jsPsych.utils.deepCopy(trial_parameters); + } else { + if (progress.current_location >= timeline_parameters.timeline.length) { + return null; + } else { + return timeline_parameters.timeline[progress.current_location].trial(); + } + } + } + + this.markCurrentTrialComplete = function() { + if(typeof timeline_parameters == 'undefined'){ + progress.done = true; + } else { + timeline_parameters.timeline[progress.current_location].markCurrentTrialComplete(); + } + } + + this.nextRepetiton = function() { + this.setTimelineVariablesOrder(); + progress.current_location = -1; + progress.current_variable_set = 0; + progress.current_repetition++; + for (var i = 0; i < timeline_parameters.timeline.length; i++) { + timeline_parameters.timeline[i].reset(); + } + } + + // set the order for going through the timeline variables array + this.setTimelineVariablesOrder = function() { + + // check to make sure this node has variables + if(typeof timeline_parameters === 'undefined' || typeof timeline_parameters.timeline_variables === 'undefined'){ + return; + } + + var order = []; + for(var i=0; i'+ + '

The minimum width is '+mw+'px. Your current width is '+w+'px.

'+ + '

The minimum height is '+mh+'px. Your current height is '+h+'px.

'; + core.getDisplayElement().innerHTML = msg; + } else { + clearInterval(interval); + core.getDisplayElement().innerHTML = ''; + checkExclusions(exclusions, success, fail); + } + }, 100); + return; // prevents checking other exclusions while this is being fixed + } + } + + // WEB AUDIO API + if(typeof exclusions.audio !== 'undefined' && exclusions.audio) { + if(window.hasOwnProperty('AudioContext') || window.hasOwnProperty('webkitAudioContext')){ + // clear + } else { + clear = false; + var msg = '

Your browser does not support the WebAudio API, which means that you will not '+ + 'be able to complete the experiment.

Browsers that support the WebAudio API include '+ + 'Chrome, Firefox, Safari, and Edge.

'; + core.getDisplayElement().innerHTML = msg; + fail(); + return; + } + } + + // GO? + if(clear){ success(); } + } + + function drawProgressBar(msg) { + document.querySelector('.jspsych-display-element').insertAdjacentHTML('afterbegin', + '
'+ + ''+ + msg+ + ''+ + '
'+ + '
'+ + '
'); + } + + function updateProgressBar() { + var progress = jsPsych.progress().percent_complete; + core.setProgressBar(progress / 100); + } + + var progress_bar_amount = 0; + + core.setProgressBar = function(proportion_complete){ + proportion_complete = Math.max(Math.min(1,proportion_complete),0); + document.querySelector('#jspsych-progressbar-inner').style.width = (proportion_complete*100) + "%"; + progress_bar_amount = proportion_complete; + } + + core.getProgressBarCompleted = function(){ + return progress_bar_amount; + } + + //Leave a trace in the DOM that jspsych was loaded + document.documentElement.setAttribute('jspsych', 'present'); + + return core; +})(); + +jsPsych.plugins = (function() { + + var module = {}; + + // enumerate possible parameter types for plugins + module.parameterType = { + BOOL: 0, + STRING: 1, + INT: 2, + FLOAT: 3, + FUNCTION: 4, + KEYCODE: 5, + SELECT: 6, + HTML_STRING: 7, + IMAGE: 8, + AUDIO: 9, + VIDEO: 10, + OBJECT: 11, + COMPLEX: 12 + } + + module.universalPluginParameters = { + data: { + type: module.parameterType.OBJECT, + pretty_name: 'Data', + default: {}, + description: 'Data to add to this trial (key-value pairs)' + }, + on_start: { + type: module.parameterType.FUNCTION, + pretty_name: 'On start', + default: function() { return; }, + description: 'Function to execute when trial begins' + }, + on_finish: { + type: module.parameterType.FUNCTION, + pretty_name: 'On finish', + default: function() { return; }, + description: 'Function to execute when trial is finished' + }, + on_load: { + type: module.parameterType.FUNCTION, + pretty_name: 'On load', + default: function() { return; }, + description: 'Function to execute after the trial has loaded' + }, + post_trial_gap: { + type: module.parameterType.INT, + pretty_name: 'Post trial gap', + default: null, + description: 'Length of gap between the end of this trial and the start of the next trial' + } + } + + return module; +})(); + +jsPsych.data = (function() { + + var module = {}; + + // data storage object + var allData = DataCollection(); + + // browser interaction event data + var interactionData = DataCollection(); + + // data properties for all trials + var dataProperties = {}; + + // cache the query_string + var query_string; + + // DataCollection + function DataCollection(data){ + + var data_collection = {}; + + var trials = typeof data === 'undefined' ? [] : data; + + data_collection.push = function(new_data){ + trials.push(new_data); + return data_collection; + } + + data_collection.join = function(other_data_collection){ + trials = trials.concat(other_data_collection.values()); + return data_collection; + } + + data_collection.top = function(){ + if(trials.length <= 1){ + return data_collection; + } else { + return DataCollection([trials[trials.length-1]]); + } + } + + data_collection.first = function(n){ + if(typeof n=='undefined'){ n = 1 } + var out = []; + for(var i=0; i