-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathUtils.cpp
175 lines (166 loc) · 5.87 KB
/
Utils.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
/*
* Utils.cpp
*
* Created on: 12/feb/2015
* Author: nicola pierazzo <[email protected]>
*/
#include <cmath>
#include "Utils.hpp"
using std::vector;
using std::pair;
using std::move;
using std::max;
using std::min;
using da3d::Image;
using da3d::WeightMap;
namespace utils {
/*! \brief Compute best tiling of image in at most ntiles
*
* Returns the pair: rows, columns
*/
pair<int, int> ComputeTiling(int rows, int columns, int ntiles) {
// The objective is extract ntiles square-ish tiles
// For a square image the optimal number of rows is sqrt(ntiles)
// The ratio rows/columns permits to handle rectangular images
float best_r = sqrt(static_cast<float>(ntiles * rows) / columns);
int r_low = static_cast<int>(best_r);
int r_up = r_low + 1;
if (r_low < 1) return {1, ntiles}; // single row
if (r_up > ntiles) return {ntiles, 1}; // single column
// look for the nearest integer divisors of ntiles
while (ntiles % r_low != 0) --r_low;
while (ntiles % r_up != 0) ++r_up;
// At this point there are two possible tilings:
// {r_low, ntiles / r_low} and {r_up, ntiles / r_up}.
// We need to select the best.
// To do that, we consider the shape of the tiles.
// In the first case, the tiles are roughly
// {rows / r_low, columns * r_low / ntiles} pixels.
// In the second case, the tiles are
// {rows / r_up, columns * r_up / ntiles} pixels.
// Since r_low <= best_r <= r_up the first tile will have i
// more rows than columns and vice-versa.
//
// To select the best case we consider the ratio between the
// lengths of the longer and the shorter edge of a tile.
// The closer this ratio is to 1, the "squarer" the tile will be.
// In other words, we select the first tiling if
// (rows / r_low) / (columns * r_low / ntiles) <
// (columns * r_up / ntiles) / (rows / r_up)
// That is equivalent to (all values are > 0):
// rows * ntiles < r_up * r_low * columns
if (r_up * r_low * columns > ntiles * rows) {
return {r_low, ntiles / r_low};
} else {
return {r_up, ntiles / r_up};
}
}
/*! \brief Split image in tiles
*
* Returns a vector containing tiling.first x tiling.sencond images
* each padded by pad_* pixels. Tiles are stored in lexicographic order.
* Padding outside the image is done by symmetrization
*/
vector<Image> SplitTiles(const Image &src,
int pad_before,
int pad_after,
pair<int, int> tiling) {
vector<Image> result;
for (int tr = 0; tr < tiling.first; ++tr) {
int rstart = src.rows() * tr / tiling.first - pad_before;
int rend = src.rows() * (tr + 1) / tiling.first + pad_after;
for (int tc = 0; tc < tiling.second; ++tc) {
int cstart = src.columns() * tc / tiling.second - pad_before;
int cend = src.columns() * (tc + 1) / tiling.second + pad_after;
// copy image to tile using the above computed limits
Image tile(rend - rstart, cend - cstart, src.channels());
for (int row = rstart; row < rend; ++row) {
for (int col = cstart; col < cend; ++col) {
for (int ch = 0; ch < src.channels(); ++ch) {
tile.val(col - cstart, row - rstart, ch) = src.val(
SymmetricCoordinate(col, src.columns()),
SymmetricCoordinate(row, src.rows()),
ch);
}
}
}
result.push_back(move(tile));
}
}
return result;
}
/*! \brief Recompose tiles produced by SplitTiles
*
* Returns an image resulting of recomposing the tiling
* padded margins are averaged in the result
*/
Image MergeTiles(const vector<pair<Image, Image>> &src,
pair<int, int> shape,
int pad_before,
int pad_after,
pair<int, int> tiling) {
int channels = src[0].first.channels();
Image result(shape.first, shape.second, channels);
Image weights(shape.first, shape.second);
auto tile = src.begin();
for (int tr = 0; tr < tiling.first; ++tr) {
int rstart = shape.first * tr / tiling.first - pad_before;
int rend = shape.first * (tr + 1) / tiling.first + pad_after;
for (int tc = 0; tc < tiling.second; ++tc) {
int cstart = shape.second * tc / tiling.second - pad_before;
int cend = shape.second * (tc + 1) / tiling.second + pad_after;
// copy tile to image using the above computed limits
for (int row = max(0, rstart); row < min(shape.first, rend); ++row) {
for (int col = max(0, cstart); col < min(shape.second, cend); ++col) {
for (int ch = 0; ch < channels; ++ch) {
result.val(col, row, ch) +=
tile->first.val(col - cstart, row - rstart, ch);
}
weights.val(col, row) += tile->second.val(col - cstart, row - rstart);
}
}
++tile;
}
}
// normalize by the weight
for (int row = 0; row < shape.first; ++row) {
for (int col = 0; col < shape.second; ++col) {
for (int ch = 0; ch < channels; ++ch) {
result.val(col, row, ch) /= weights.val(col, row);
}
}
}
return result;
}
/*! \brief Test if a color image is actually monochrome */
bool isMonochrome (const Image &u) {
for (int row = 0; row < u.rows(); ++row) {
for (int col = 0; col < u.columns(); ++col) {
float v = u.val(col, row, 0);
for (int ch = 1; ch < u.channels(); ++ch) {
if (u.val(col, row, ch) != v) {
return false;
}
}
}
}
return true;
}
/*! \brief Convert image to monochrome
*
* Returns a monochrome image
*/
Image makeMonochrome (const Image &u) {
Image result(u.rows(), u.columns());
for (int row = 0; row < u.rows(); ++row) {
for (int col = 0; col < u.columns(); ++col) {
double v = 0;
for (int ch = 0; ch < u.channels(); ++ch) {
v += u.val(col, row, ch);
}
result.val(col,row) = v / u.channels();
}
}
return result;
}
} // namespace utils