Skip to content

Commit e7793f2

Browse files
authored
stitching speedup: parallel loading of full resolution images (#49)
1 parent f0fd04c commit e7793f2

File tree

4 files changed

+72
-49
lines changed

4 files changed

+72
-49
lines changed

tests/stitcher_pipeline_test.cc

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ TEST_CASE("Stitcher pipeline defaults") {
2222
xpano::pipeline::StitcherPipeline stitcher;
2323

2424
auto result = stitcher.RunLoading(kInputs, {}, {}).get();
25-
auto progress = stitcher.LoadingProgress();
25+
auto progress = stitcher.Progress();
2626
CHECK(progress.tasks_done == progress.num_tasks);
2727

2828
CHECK(result.images.size() == 10);
@@ -33,15 +33,23 @@ TEST_CASE("Stitcher pipeline defaults") {
3333

3434
const float eps = 0.01;
3535

36+
// preview
3637
auto pano0 = stitcher.RunStitching(result, {.pano_id = 0}).get().pano;
38+
progress = stitcher.Progress();
39+
CHECK(progress.tasks_done == progress.num_tasks);
3740
REQUIRE(pano0.has_value());
3841
CHECK_THAT(pano0->rows, WithinRel(804, eps));
3942
CHECK_THAT(pano0->cols, WithinRel(2145, eps));
4043

41-
auto pano1 = stitcher.RunStitching(result, {.pano_id = 1}).get().pano;
44+
// full resolution
45+
auto pano1 = stitcher.RunStitching(result, {.pano_id = 1, .full_res = true})
46+
.get()
47+
.pano;
48+
progress = stitcher.Progress();
49+
CHECK(progress.tasks_done == progress.num_tasks);
4250
REQUIRE(pano1.has_value());
43-
CHECK_THAT(pano1->rows, WithinRel(974, eps));
44-
CHECK_THAT(pano1->cols, WithinRel(1325, eps));
51+
CHECK_THAT(pano1->rows, WithinRel(1952, eps));
52+
CHECK_THAT(pano1->cols, WithinRel(2651, eps));
4553
}
4654

4755
const std::vector<std::string> kShuffledInputs = {
@@ -63,7 +71,7 @@ TEST_CASE("Stitcher pipeline custom matching neighborhood") {
6371
auto result =
6472
stitcher.RunLoading(kShuffledInputs, {}, {.neighborhood_search_size = 3})
6573
.get();
66-
auto progress = stitcher.LoadingProgress();
74+
auto progress = stitcher.Progress();
6775
CHECK(progress.tasks_done == progress.num_tasks);
6876

6977
CHECK(result.images.size() == 10);
@@ -84,7 +92,7 @@ TEST_CASE("Stitcher pipeline larger neighborhood size") {
8492
{}, {.neighborhood_search_size = 10})
8593
.get();
8694

87-
auto progress = stitcher.LoadingProgress();
95+
auto progress = stitcher.Progress();
8896
CHECK(progress.tasks_done == progress.num_tasks);
8997

9098
REQUIRE(result.images.size() == 3);
@@ -97,7 +105,7 @@ TEST_CASE("Stitcher pipeline single image") {
97105
xpano::pipeline::StitcherPipeline stitcher;
98106

99107
auto result = stitcher.RunLoading({"data/image01.jpg"}, {}, {}).get();
100-
auto progress = stitcher.LoadingProgress();
108+
auto progress = stitcher.Progress();
101109
CHECK(progress.tasks_done == progress.num_tasks);
102110

103111
REQUIRE(result.images.size() == 1);
@@ -108,7 +116,7 @@ TEST_CASE("Stitcher pipeline no images") {
108116
xpano::pipeline::StitcherPipeline stitcher;
109117

110118
auto result = stitcher.RunLoading({}, {}, {}).get();
111-
auto progress = stitcher.LoadingProgress();
119+
auto progress = stitcher.Progress();
112120
CHECK(progress.tasks_done == progress.num_tasks);
113121

114122
REQUIRE(result.images.empty());
@@ -125,7 +133,7 @@ TEST_CASE("Stitcher pipeline loading options") {
125133
.RunLoading({"data/image05.jpg", "data/image06.jpg"},
126134
{.preview_longer_side = preview_size}, {})
127135
.get();
128-
auto progress = stitcher.LoadingProgress();
136+
auto progress = stitcher.Progress();
129137
CHECK(progress.tasks_done == progress.num_tasks);
130138

131139
REQUIRE(result.images.size() == 2);

xpano/gui/pano_gui.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ Action PanoGui::DrawSidebar() {
248248
action |= DrawActionButtons(plot_pane_.Type(), selection_.target_id);
249249
ImGui::Spacing();
250250

251-
auto progress = stitcher_pipeline_.LoadingProgress();
251+
auto progress = stitcher_pipeline_.Progress();
252252
DrawProgressBar(progress);
253253
if (progress.tasks_done < progress.num_tasks) {
254254
if (ImGui::SmallButton("Cancel")) {

xpano/pipeline/stitcher_pipeline.cc

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -80,54 +80,66 @@ std::future<StitcherData> StitcherPipeline::RunLoading(
8080

8181
std::future<StitchingResult> StitcherPipeline::RunStitching(
8282
const StitcherData &data, const StitchingOptions &options) {
83-
std::vector<cv::Mat> imgs;
8483
auto pano = data.panos[options.pano_id];
85-
8684
return pool_.submit([pano, &images = data.images, options, this]() {
87-
int num_tasks = static_cast<int>(pano.ids.size()) + 1 +
88-
static_cast<int>(options.export_path.has_value()) +
89-
static_cast<int>(options.full_res);
90-
progress_.Reset(ProgressType::kLoadingImages, num_tasks);
91-
std::vector<cv::Mat> imgs;
85+
return RunStitchingPipeline(pano, images, options);
86+
});
87+
}
88+
89+
StitchingResult StitcherPipeline::RunStitchingPipeline(
90+
const algorithm::Pano &pano, const std::vector<algorithm::Image> &images,
91+
const StitchingOptions &options) {
92+
int num_tasks = static_cast<int>(pano.ids.size()) + 1 +
93+
static_cast<int>(options.export_path.has_value()) +
94+
static_cast<int>(options.full_res);
95+
progress_.Reset(ProgressType::kLoadingImages, num_tasks);
96+
std::vector<cv::Mat> imgs;
97+
if (options.full_res) {
98+
BS::multi_future<cv::Mat> imgs_future;
99+
for (const auto &img_id : pano.ids) {
100+
imgs_future.push_back(pool_.submit([this, &image = images[img_id]]() {
101+
auto full_res_image = image.GetFullRes();
102+
progress_.NotifyTaskDone();
103+
return full_res_image;
104+
}));
105+
}
106+
imgs = imgs_future.get();
107+
} else {
92108
for (int img_id : pano.ids) {
93-
if (options.full_res) {
94-
imgs.push_back(images[img_id].GetFullRes());
95-
} else {
96-
imgs.push_back(images[img_id].GetPreview());
97-
}
109+
imgs.push_back(images[img_id].GetPreview());
98110
progress_.NotifyTaskDone();
99111
}
112+
}
100113

101-
progress_.SetTaskType(ProgressType::kStitchingPano);
102-
auto [status, pano, mask] =
103-
algorithm::Stitch(imgs, {.projection = options.projection,
104-
.return_pano_mask = options.full_res});
105-
progress_.NotifyTaskDone();
114+
progress_.SetTaskType(ProgressType::kStitchingPano);
115+
auto [status, result, mask] = algorithm::Stitch(
116+
imgs,
117+
{.projection = options.projection, .return_pano_mask = options.full_res});
118+
progress_.NotifyTaskDone();
106119

107-
if (status != cv::Stitcher::OK) {
108-
return StitchingResult{.pano_id = options.pano_id, .status = status};
109-
}
120+
if (status != cv::Stitcher::OK) {
121+
return StitchingResult{.pano_id = options.pano_id, .status = status};
122+
}
110123

111-
std::optional<utils::RectRRf> auto_crop;
112-
if (options.full_res) {
113-
progress_.SetTaskType(ProgressType::kAutoCrop);
114-
auto_crop = algorithm::FindLargestCrop(mask);
115-
progress_.NotifyTaskDone();
116-
}
124+
std::optional<utils::RectRRf> auto_crop;
125+
if (options.full_res) {
126+
progress_.SetTaskType(ProgressType::kAutoCrop);
127+
auto_crop = algorithm::FindLargestCrop(mask);
128+
progress_.NotifyTaskDone();
129+
}
117130

118-
std::optional<std::string> export_path;
119-
if (options.export_path) {
120-
progress_.SetTaskType(ProgressType::kExport);
121-
if (cv::imwrite(*options.export_path, pano,
122-
CompressionParameters(options.compression))) {
123-
export_path = options.export_path;
124-
}
125-
progress_.NotifyTaskDone();
131+
std::optional<std::string> export_path;
132+
if (options.export_path) {
133+
progress_.SetTaskType(ProgressType::kExport);
134+
if (cv::imwrite(*options.export_path, result,
135+
CompressionParameters(options.compression))) {
136+
export_path = options.export_path;
126137
}
138+
progress_.NotifyTaskDone();
139+
}
127140

128-
return StitchingResult{options.pano_id, options.full_res, status, pano,
129-
auto_crop, export_path};
130-
});
141+
return StitchingResult{options.pano_id, options.full_res, status,
142+
result, auto_crop, export_path};
131143
}
132144

133145
std::future<ExportResult> StitcherPipeline::RunExport(
@@ -152,7 +164,7 @@ std::future<ExportResult> StitcherPipeline::RunExport(
152164
});
153165
}
154166

155-
ProgressReport StitcherPipeline::LoadingProgress() const {
167+
ProgressReport StitcherPipeline::Progress() const {
156168
return progress_.Progress();
157169
}
158170

xpano/pipeline/stitcher_pipeline.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class StitcherPipeline {
112112

113113
std::future<ExportResult> RunExport(cv::Mat pano,
114114
const ExportOptions &options);
115-
ProgressReport LoadingProgress() const;
115+
ProgressReport Progress() const;
116116

117117
void Cancel();
118118

@@ -121,6 +121,9 @@ class StitcherPipeline {
121121
const std::vector<std::string> &inputs, const LoadingOptions &options);
122122
StitcherData RunMatchingPipeline(std::vector<algorithm::Image> images,
123123
const MatchingOptions &options);
124+
StitchingResult RunStitchingPipeline(
125+
const algorithm::Pano &pano, const std::vector<algorithm::Image> &images,
126+
const StitchingOptions &options);
124127

125128
ProgressMonitor progress_;
126129

0 commit comments

Comments
 (0)