580ceb852102e3b9a619b709ecd72abe5dc62177
[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.depthAndRange = true;
148
149     CamSim::Simulator simulator;
150     simulator.setScene(scene);
151     simulator.setProjection(projection);
152     simulator.setChipTiming(chipTiming);
153     simulator.setPMD(pmd);
154     simulator.setPipeline(pipeline);
155     simulator.setOutput(output);
156
157     CamSim::Exporter exporter;
158     int frameCounter = 0;
159     QFile::remove("rgb-subframes.ppm");
160     QFile::remove("rgb-result.ppm");
161     QFile::remove("pmd-subframes.pfs");
162     QFile::remove("groundtruth-depthrange.pfs");
163     QFile::remove("pmd-result.pfs");
164     QFile::remove("pmd-coordinates.pfs");
165     for (long long t = simulator.startTimestamp();
166             t < simulator.endTimestamp();
167             t = simulator.nextFrameTimestamp()) {
168
169         qInfo("simulating for %08lld", t);
170         simulator.simulate(t);
171
172         // Each new frame is appended to the output files, so these files
173         // contain all frames of the sequence. Alternatively, you could
174         // export each frame to its own set of files.
175
176         exporter.waitForAsyncExports();
177
178         // PMD phase images
179         exporter.asyncExportData("pmd-subframes.pfs",
180                 { simulator.getPMD(0), simulator.getPMD(1), simulator.getPMD(2), simulator.getPMD(3) });
181         // RGB results for each sub frame, i.e. each PMD phase image
182         exporter.asyncExportData("rgb-subframes.ppm",
183                 { simulator.getSRGB(0), simulator.getSRGB(1), simulator.getSRGB(2), simulator.getSRGB(3) });
184         // PMD simulation result: range, amplitude, intensity
185         exporter.asyncExportData("pmd-result.pfs", simulator.getPMD());
186         // RGB simulation result
187         exporter.asyncExportData("rgb-result.ppm", simulator.getSRGB());
188         // Grount Truth depth and range values
189         exporter.asyncExportData("groundtruth-depthrange.pfs", simulator.getDepthAndRange());
190
191         // PMD simulation produces range data, i.e. the distance to the sensor center.
192         // You can compute 3D cartesian coordinates from range data by using the camera
193         // intrinsic parameters:
194         CamSim::TexData pmdResult = simulator.getPMD();
195         const float* pmdResultData = static_cast<const float*>(pmdResult.packedData());
196         QVector<QVector3D> pmdCoordData(pmdResult.width() * pmdResult.height());
197         for (int y = 0; y < pmdResult.height(); y++) {
198             float v = (y - projection.centerPixel().y()) / projection.focalLengths().y();
199             for (int x = 0; x < pmdResult.width(); x++) {
200                 float u = (x - projection.centerPixel().x()) / projection.focalLengths().x();
201                 float w = 1.0f;
202                 float range = pmdResultData[3 * (y * pmdResult.width() + x) + 0];
203                 QVector3D xyz = QVector3D(u, v, w).normalized() * range;
204                 pmdCoordData[y * pmdResult.width() + x] = xyz;
205             }
206         }
207         exporter.exportData("pmd-coordinates.pfs", CamSim::TexData(
208                     pmdResult.width(), pmdResult.height(), 3, GL_FLOAT, QByteArray::fromRawData(
209                         reinterpret_cast<const char*>(pmdCoordData.constData()),
210                         pmdCoordData.size() * sizeof(QVector3D))));
211
212         frameCounter++;
213     }
214     exporter.waitForAsyncExports();
215 }