forked from sxyu/volrend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main_headless.cpp
235 lines (202 loc) · 7.09 KB
/
main_headless.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include <fstream>
#include <iomanip>
#include "volrend/internal/auto_filesystem.hpp"
#include "volrend/common.hpp"
#include "volrend/n3tree.hpp"
#include "volrend/internal/opts.hpp"
#include "volrend/cuda/common.cuh"
#include "volrend/cuda/renderer_kernel.hpp"
#include "volrend/internal/imwrite.hpp"
namespace {
std::string path_basename(const std::string &str) {
for (size_t i = str.size() - 1; ~i; --i) {
const char c = str[i];
if (c == '/' || c == '\\') {
return str.substr(i);
}
}
return str;
}
std::string remove_ext(const std::string &str) {
for (size_t i = str.size() - 1; ~i; --i) {
const char c = str[i];
if (c == '.') {
return str.substr(0, i);
}
}
return str;
}
int read_transform_matrices(const std::string &path,
std::vector<glm::mat4x3> &out) {
std::ifstream ifs(path);
int cnt = 0;
if (!ifs) {
fprintf(stderr, "ERROR: '%s' does not exist\n", path.c_str());
std::exit(1);
}
while (ifs) {
glm::mat4x3 tmp;
float garb;
// Recall GL is column major
ifs >> tmp[0][0] >> tmp[1][0] >> tmp[2][0] >> tmp[3][0];
if (!ifs) break;
ifs >> tmp[0][1] >> tmp[1][1] >> tmp[2][1] >> tmp[3][1];
ifs >> tmp[0][2] >> tmp[1][2] >> tmp[2][2] >> tmp[3][2];
if (ifs) {
ifs >> garb >> garb >> garb >> garb;
}
++cnt;
out.push_back(std::move(tmp));
}
return cnt;
}
void read_intrins(const std::string &path, float &fx, float &fy) {
std::ifstream ifs(path);
if (!ifs) {
fprintf(stderr, "ERROR: intrin '%s' does not exist\n", path.c_str());
std::exit(1);
}
float _; // garbage
ifs >> fx >> _ >> _ >> _;
ifs >> _ >> fy;
}
} // namespace
int main(int argc, char *argv[]) {
using namespace volrend;
cxxopts::Options cxxoptions(
"volrend_headless",
"Headless PlenOctree volume rendering (c) PlenOctree authors 2021");
internal::add_common_opts(cxxoptions);
// clang-format off
cxxoptions.add_options()
("o,write_images", "output directory of images; "
"if empty, DOES NOT save (for timing only)",
cxxopts::value<std::string>()->default_value(""))
("i,intrin", "intrinsics matrix 4x4; if set, overrides the fx/fy",
cxxopts::value<std::string>()->default_value(""))
("r,reverse_yz", "use OpenCV camera space convention instead of NeRF",
cxxopts::value<bool>())
("scale", "scaling to apply to image",
cxxopts::value<float>()->default_value("1.0"))
("max_imgs", "max images to render, default no limit",
cxxopts::value<int>()->default_value("0"))
;
// clang-format on
cxxoptions.allow_unrecognised_options();
// Pass a list of camera pose *.txt files after npz file
// each file should have 4x4 c2w pose matrix
cxxoptions.positional_help("npz_file [c2w_txt_4x4...]");
cxxopts::ParseResult args = internal::parse_options(cxxoptions, argc, argv);
const int device_id = args["gpu"].as<int>();
if (~device_id) {
cuda(SetDevice(device_id));
}
// Load all transform matrices
std::vector<glm::mat4x3> trans;
std::vector<std::string> basenames;
for (auto path : args.unmatched()) {
int cnt = read_transform_matrices(path, trans);
std::string fname = remove_ext(path_basename(path));
if (cnt == 1) {
basenames.push_back(fname);
} else {
for (int i = 0; i < cnt; ++i) {
std::string tmp = std::to_string(i);
while (tmp.size() < 6) tmp = "0" + tmp;
basenames.push_back(fname + "_" + tmp);
}
}
}
if (args["reverse_yz"].as<bool>()) {
puts("INFO: Use OpenCV camera convention\n");
// clang-format off
glm::mat4x4 cam_trans(1, 0, 0, 0,
0, -1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1);
// clang-format on
for (auto &transform : trans) {
transform = transform * cam_trans;
}
} else {
puts("INFO: Use NeRF camera convention\n");
}
if (trans.size() == 0) {
fputs("WARNING: No camera poses specified, quitting\n", stderr);
return 1;
}
std::string out_dir = args["write_images"].as<std::string>();
N3Tree tree(args["file"].as<std::string>());
int width = args["width"].as<int>(), height = args["height"].as<int>();
float fx = args["fx"].as<float>();
if (fx < 0) fx = 1111.11f;
float fy = args["fy"].as<float>();
if (fy < 0) fy = fx;
{
// Load intrin matrix
std::string intrin_path = args["intrin"].as<std::string>();
if (intrin_path.size()) {
read_intrins(intrin_path, fx, fy);
}
}
{
float scale = args["scale"].as<float>();
if (scale != 1.f) {
int owidth = width, oheight = height;
width *= scale;
height *= scale;
fx *= (float)width / owidth;
fy *= (float)height / oheight;
}
}
{
int max_imgs = args["max_imgs"].as<int>();
if (max_imgs > 0 && trans.size() > (size_t)max_imgs) {
trans.resize(max_imgs);
basenames.resize(max_imgs);
}
}
Camera camera(width, height, fx, fy);
cudaArray_t array;
cudaStream_t stream;
cudaChannelFormatDesc channelDesc =
cudaCreateChannelDesc(8, 8, 8, 8, cudaChannelFormatKindUnsigned);
std::vector<uint8_t> buf;
if (out_dir.size()) {
std::filesystem::create_directories(out_dir);
buf.resize(4 * width * height);
}
cuda(MallocArray(&array, &channelDesc, width, height));
cuda(StreamCreateWithFlags(&stream, cudaStreamDefault));
cudaArray_t depth_arr = nullptr; // Not using depth buffer
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start);
for (size_t i = 0; i < trans.size(); ++i) {
camera.transform = trans[i];
camera._update(false);
RenderOptions options = internal::render_options_from_args(args);
launch_renderer(tree, camera, options, array, depth_arr, stream, true);
if (out_dir.size()) {
cuda(Memcpy2DFromArrayAsync(buf.data(), 4 * width, array, 0, 0,
4 * width, height,
cudaMemcpyDeviceToHost, stream));
std::string fpath = out_dir + "/" + basenames[i] + ".png";
internal::write_png_file(fpath, buf.data(), width, height);
}
}
cudaEventRecord(stop);
cudaEventSynchronize(stop);
float milliseconds = 0;
cudaEventElapsedTime(&milliseconds, start, stop);
milliseconds = milliseconds / trans.size();
printf("%.10f ms per frame\n", milliseconds);
printf("%.10f fps\n", 1000.f / milliseconds);
cuda(FreeArray(array));
cuda(StreamDestroy(stream));
}