Add conversion of PMD range to cartesian coordinates to simulation library.
[camsim.git] / camsim-pmd-example / camsim-pmd-example.cpp
1 /*
2  * Copyright (C) 2017, 2018
3  * Computer Graphics Group, University of Siegen
4  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24
25 #include <QGuiApplication>
26 #include <QOpenGLContext>
27 #include <QOffscreenSurface>
28 #include <QFile>
29
30 #include <camsim/camsim.hpp>
31
32 int main(int argc, char* argv[])
33 {
34     /* Initialize Qt and an OpenGL context */
35     QGuiApplication app(argc, argv);
36     QSurfaceFormat format;
37     format.setProfile(QSurfaceFormat::CoreProfile);
38     format.setVersion(4, 5);
39     QSurfaceFormat::setDefaultFormat(format);
40     QOffscreenSurface surface;
41     surface.create();
42     QOpenGLContext context;
43     context.create();
44     context.makeCurrent(&surface);
45
46     /* Define a simple animated scene: at the top,
47      * a quad moves from left to right through the view,
48      * and at the bottom, a teapot rotates. */
49
50     CamSim::Scene scene;
51     CamSim::Generator generator;
52
53     CamSim::Light light;
54     light.type = CamSim::SpotLight;
55     light.innerConeAngle = 90.0f;
56     light.outerConeAngle = 90.0f;
57     light.isRelativeToCamera = true;
58     light.position = QVector3D(0.0f, 0.0f, 0.0f);
59     light.color = QVector3D(9.0f, 9.0f, 9.0f); // for RGB simulation
60     light.power = 0.2f;                        // for PMD simulation
61     scene.addLight(light);
62
63     CamSim::Material backgroundMaterial;
64     backgroundMaterial.ambient = QVector3D(0.0f, 0.0f, 0.0f);
65     backgroundMaterial.diffuse = QVector3D(1.0f, 1.0f, 1.0f);
66     backgroundMaterial.specular = QVector3D(0.0f, 0.0f, 0.0f);
67     int backgroundMaterialIndex = scene.addMaterial(backgroundMaterial);
68     CamSim::Transformation backgroundTransformation;
69     backgroundTransformation.translation = QVector3D(0.0f, 0.0f, -2.0f);
70     backgroundTransformation.scaling = QVector3D(5.0f, 5.0f, 5.0f);
71     generator.addQuadToScene(scene, backgroundMaterialIndex, backgroundTransformation);
72
73     CamSim::Material quadMaterial;
74     quadMaterial.ambient = QVector3D(0.0f, 0.0f, 0.0f);
75     quadMaterial.diffuse = QVector3D(0.7f, 0.7f, 0.7f);
76     quadMaterial.specular = QVector3D(0.3f, 0.3f, 0.3f);
77     int quadMaterialIndex = scene.addMaterial(quadMaterial);
78     CamSim::Transformation quadAnimationTransformations[2];
79     quadAnimationTransformations[0].translation = QVector3D(-1.0f, 0.5f, -1.5f);
80     quadAnimationTransformations[1].translation = QVector3D(+1.0f, 0.5f, -1.5f);
81     CamSim::Animation quadAnimation;
82     quadAnimation.addKeyframe(      0, quadAnimationTransformations[0]);
83     quadAnimation.addKeyframe(5000000, quadAnimationTransformations[1]);
84     CamSim::Transformation quadTransformation;
85     quadTransformation.scaling = QVector3D(0.2f, 0.2f, 0.2f);
86     generator.addQuadToScene(scene, quadMaterialIndex, quadTransformation, quadAnimation);
87
88     CamSim::Material teapotMaterial;
89     teapotMaterial.isTwoSided = true; // because there is a gap between pot and lid
90     teapotMaterial.ambient = QVector3D(0.0f, 0.0f, 0.0f);
91     teapotMaterial.diffuse = QVector3D(0.5f, 0.5f, 0.5f);
92     teapotMaterial.specular = QVector3D(1.0f, 1.0f, 1.0f);
93     int teapotMaterialIndex = scene.addMaterial(teapotMaterial);
94     CamSim::Transformation teapotAnimationTransformations[5];
95     teapotAnimationTransformations[0].rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.5f,   0.0f);
96     teapotAnimationTransformations[0].translation = QVector3D(0.0f, -0.3f, -1.0f);
97     teapotAnimationTransformations[1].rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.5f,  60.0f);
98     teapotAnimationTransformations[1].translation = QVector3D(0.0f, -0.3f, -1.0f);
99     teapotAnimationTransformations[2].rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.5f, 120.0f);
100     teapotAnimationTransformations[2].translation = QVector3D(0.0f, -0.3f, -1.0f);
101     teapotAnimationTransformations[3].rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.5f, 180.0f);
102     teapotAnimationTransformations[3].translation = QVector3D(0.0f, -0.3f, -1.0f);
103     teapotAnimationTransformations[4].rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.5f, 240.0f);
104     teapotAnimationTransformations[4].translation = QVector3D(0.0f, -0.3f, -1.0f);
105     CamSim::Animation teapotAnimation;
106     teapotAnimation.addKeyframe(      0, teapotAnimationTransformations[0]);
107     teapotAnimation.addKeyframe(1250000, teapotAnimationTransformations[1]);
108     teapotAnimation.addKeyframe(2500000, teapotAnimationTransformations[2]);
109     teapotAnimation.addKeyframe(3750000, teapotAnimationTransformations[3]);
110     teapotAnimation.addKeyframe(5000000, teapotAnimationTransformations[4]);
111     CamSim::Transformation teapotTransformation;
112     teapotTransformation.scaling = QVector3D(0.33f, 0.33f, 0.33f);
113     generator.addTeapotToScene(scene, teapotMaterialIndex, teapotTransformation, teapotAnimation);
114
115     /* Simulate */
116     
117     CamSim::Projection projection = CamSim::Projection::fromOpeningAngle(352, 288, 70.0f);
118     qInfo("Camera intrinsic parameters: cx=%g cy=%g fx=%g fy=%g",
119             projection.centerPixel().x(), projection.centerPixel().y(),
120             projection.focalLengths().x(), projection.focalLengths().y());
121
122     CamSim::ChipTiming chipTiming;
123     chipTiming.exposureTime = 1000e-6;         // realistic: 1000e-6
124     chipTiming.readoutTime = 1000e-6;          // realistic: 1000e-6
125     chipTiming.pauseTime = 0.036;              // typically used to set a target FPS
126
127     CamSim::PMD pmd;
128     pmd.pixelSize = 12.0 * 12.0;                // 12 micrometer pixel pitch
129     pmd.pixelContrast = 0.75;                   // TODO: should be per-pixel to simulate different gains
130     pmd.modulationFrequency = 10e6;
131     pmd.wavelength = 880;
132     pmd.quantumEfficiency = 0.8f;
133     pmd.maxElectrons = 100000;
134
135     CamSim::Pipeline pipeline;
136     pipeline.thinLensVignetting = true;
137     pipeline.thinLensApertureDiameter = 8.89f;  // 0.889 cm aperture (so that F-number is 1.8)
138     pipeline.thinLensFocalLength = 16.0f;
139     pipeline.spatialSamples = QSize(5, 5);
140     pipeline.temporalSamples = 19;
141     pipeline.shotNoise = true;
142
143     CamSim::Output output;
144     output.rgb = true;
145     output.srgb = true;
146     output.pmd = true;
147     output.pmdCoordinates = true;
148     output.depthAndRange = true;
149
150     CamSim::Simulator simulator;
151     simulator.setScene(scene);
152     simulator.setProjection(projection);
153     simulator.setChipTiming(chipTiming);
154     simulator.setPMD(pmd);
155     simulator.setPipeline(pipeline);
156     simulator.setOutput(output);
157
158     CamSim::Exporter exporter;
159     int frameCounter = 0;
160     QFile::remove("rgb-subframes.ppm");
161     QFile::remove("rgb-result.ppm");
162     QFile::remove("pmd-subframes.pfs");
163     QFile::remove("groundtruth-depthrange.pfs");
164     QFile::remove("pmd-result.pfs");
165     QFile::remove("pmd-coordinates.pfs");
166     for (long long t = simulator.startTimestamp();
167             t < simulator.endTimestamp();
168             t = simulator.nextFrameTimestamp()) {
169
170         qInfo("simulating for %08lld", t);
171         simulator.simulate(t);
172
173         // Each new frame is appended to the output files, so these files
174         // contain all frames of the sequence. Alternatively, you could
175         // export each frame to its own set of files.
176
177         exporter.waitForAsyncExports();
178
179         // PMD phase images
180         exporter.asyncExportData("pmd-subframes.pfs",
181                 { simulator.getPMD(0), simulator.getPMD(1), simulator.getPMD(2), simulator.getPMD(3) });
182         // RGB results for each sub frame, i.e. each PMD phase image
183         exporter.asyncExportData("rgb-subframes.ppm",
184                 { simulator.getSRGB(0), simulator.getSRGB(1), simulator.getSRGB(2), simulator.getSRGB(3) });
185         // PMD simulation result: range, amplitude, intensity
186         exporter.asyncExportData("pmd-result.pfs", simulator.getPMD());
187         // PMD simulation results: cartesian coordinates (computed from range)
188         exporter.asyncExportData("pmd-coordinates.pfs", simulator.getPMDCoordinates());
189         // RGB simulation result
190         exporter.asyncExportData("rgb-result.ppm", simulator.getSRGB());
191         // Grount Truth depth and range values
192         exporter.asyncExportData("groundtruth-depthrange.pfs", simulator.getDepthAndRange());
193
194         frameCounter++;
195     }
196     exporter.waitForAsyncExports();
197 }