1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup freestyle
19 * \brief Convenient access to the steerable ViewMap to which any element of the ViewMap belongs
20 * to.
21 */
22
23 #include <sstream>
24
25 #include "Silhouette.h"
26 #include "SteerableViewMap.h"
27
28 #include "../geometry/Geom.h"
29
30 #include "../image/Image.h"
31 #include "../image/ImagePyramid.h"
32
33 #include "BKE_global.h"
34 #include "BLI_math.h"
35
36 #include "IMB_imbuf.h"
37 #include "IMB_imbuf_types.h"
38
39 namespace Freestyle {
40
41 using namespace Geometry;
42
SteerableViewMap(unsigned int nbOrientations)43 SteerableViewMap::SteerableViewMap(unsigned int nbOrientations)
44 {
45 _nbOrientations = nbOrientations;
46 _bound = cos(M_PI / (float)_nbOrientations);
47 for (unsigned int i = 0; i < _nbOrientations; ++i) {
48 _directions.push_back(Vec2d(cos((float)i * M_PI / (float)_nbOrientations),
49 sin((float)i * M_PI / (float)_nbOrientations)));
50 }
51 Build();
52 }
53
Build()54 void SteerableViewMap::Build()
55 {
56 _imagesPyramids =
57 new ImagePyramid *[_nbOrientations + 1]; // one more map to store the complete visible VM
58 memset((_imagesPyramids), 0, (_nbOrientations + 1) * sizeof(ImagePyramid *));
59 }
60
SteerableViewMap(const SteerableViewMap & iBrother)61 SteerableViewMap::SteerableViewMap(const SteerableViewMap &iBrother)
62 {
63 _nbOrientations = iBrother._nbOrientations;
64 unsigned int i;
65 _bound = iBrother._bound;
66 _directions = iBrother._directions;
67 _mapping = iBrother._mapping;
68 _imagesPyramids =
69 new ImagePyramid *[_nbOrientations + 1]; // one more map to store the complete visible VM
70 for (i = 0; i <= _nbOrientations; ++i) {
71 _imagesPyramids[i] = new GaussianPyramid(
72 *(dynamic_cast<GaussianPyramid *>(iBrother._imagesPyramids[i])));
73 }
74 }
75
~SteerableViewMap()76 SteerableViewMap::~SteerableViewMap()
77 {
78 Clear();
79 }
80
Clear()81 void SteerableViewMap::Clear()
82 {
83 unsigned int i;
84 if (_imagesPyramids) {
85 for (i = 0; i <= _nbOrientations; ++i) {
86 if (_imagesPyramids[i]) {
87 delete (_imagesPyramids)[i];
88 }
89 }
90 delete[] _imagesPyramids;
91 _imagesPyramids = 0;
92 }
93 if (!_mapping.empty()) {
94 for (map<unsigned int, double *>::iterator m = _mapping.begin(), mend = _mapping.end();
95 m != mend;
96 ++m) {
97 delete[](*m).second;
98 }
99 _mapping.clear();
100 }
101 }
102
Reset()103 void SteerableViewMap::Reset()
104 {
105 Clear();
106 Build();
107 }
108
ComputeWeight(const Vec2d & dir,unsigned i)109 double SteerableViewMap::ComputeWeight(const Vec2d &dir, unsigned i)
110 {
111 double dotp = fabs(dir * _directions[i]);
112 if (dotp < _bound) {
113 return 0.0;
114 }
115 if (dotp > 1.0) {
116 dotp = 1.0;
117 }
118
119 return cos((float)_nbOrientations / 2.0 * acos(dotp));
120 }
121
AddFEdge(FEdge * iFEdge)122 double *SteerableViewMap::AddFEdge(FEdge *iFEdge)
123 {
124 unsigned i;
125 unsigned id = iFEdge->getId().getFirst();
126 map<unsigned int, double *>::iterator o = _mapping.find(id);
127 if (o != _mapping.end()) {
128 return (*o).second;
129 }
130 double *res = new double[_nbOrientations];
131 for (i = 0; i < _nbOrientations; ++i) {
132 res[i] = 0.0;
133 }
134 Vec3r o2d3 = iFEdge->orientation2d();
135 Vec2r o2d2(o2d3.x(), o2d3.y());
136 real norm = o2d2.norm();
137 if (norm < 1.0e-6) {
138 return res;
139 }
140 o2d2 /= norm;
141
142 for (i = 0; i < _nbOrientations; ++i) {
143 res[i] = ComputeWeight(o2d2, i);
144 }
145 _mapping[id] = res;
146 return res;
147 }
148
getSVMNumber(Vec2f dir)149 unsigned SteerableViewMap::getSVMNumber(Vec2f dir)
150 {
151 // soc unsigned res = 0;
152 real norm = dir.norm();
153 if (norm < 1.0e-6) {
154 return _nbOrientations + 1;
155 }
156 dir /= norm;
157 double maxw = 0.0f;
158 unsigned winner = _nbOrientations + 1;
159 for (unsigned int i = 0; i < _nbOrientations; ++i) {
160 double w = ComputeWeight(dir, i);
161 if (w > maxw) {
162 maxw = w;
163 winner = i;
164 }
165 }
166 return winner;
167 }
168
getSVMNumber(unsigned id)169 unsigned SteerableViewMap::getSVMNumber(unsigned id)
170 {
171 map<unsigned int, double *>::iterator o = _mapping.find(id);
172 if (o != _mapping.end()) {
173 double *wvalues = (*o).second;
174 double maxw = 0.0;
175 unsigned winner = _nbOrientations + 1;
176 for (unsigned i = 0; i < _nbOrientations; ++i) {
177 double w = wvalues[i];
178 if (w > maxw) {
179 maxw = w;
180 winner = i;
181 }
182 }
183 return winner;
184 }
185 return _nbOrientations + 1;
186 }
187
buildImagesPyramids(GrayImage ** steerableBases,bool copy,unsigned iNbLevels,float iSigma)188 void SteerableViewMap::buildImagesPyramids(GrayImage **steerableBases,
189 bool copy,
190 unsigned iNbLevels,
191 float iSigma)
192 {
193 for (unsigned int i = 0; i <= _nbOrientations; ++i) {
194 ImagePyramid *svm = (_imagesPyramids)[i];
195 delete svm;
196 if (copy) {
197 svm = new GaussianPyramid(*(steerableBases[i]), iNbLevels, iSigma);
198 }
199 else {
200 svm = new GaussianPyramid(steerableBases[i], iNbLevels, iSigma);
201 }
202 _imagesPyramids[i] = svm;
203 }
204 }
205
readSteerableViewMapPixel(unsigned iOrientation,int iLevel,int x,int y)206 float SteerableViewMap::readSteerableViewMapPixel(unsigned iOrientation, int iLevel, int x, int y)
207 {
208 ImagePyramid *pyramid = _imagesPyramids[iOrientation];
209 if (!pyramid) {
210 if (G.debug & G_DEBUG_FREESTYLE) {
211 cout << "Warning: this steerable ViewMap level doesn't exist" << endl;
212 }
213 return 0.0f;
214 }
215 if ((x < 0) || (x >= pyramid->width()) || (y < 0) || (y >= pyramid->height())) {
216 return 0;
217 }
218 // float v = pyramid->pixel(x, pyramid->height() - 1 - y, iLevel) * 255.0f;
219 // We encode both the directionality and the lines counting on 8 bits (because of frame buffer).
220 // Thus, we allow until 8 lines to pass through the same pixel, so that we can discretize the
221 // Pi/_nbOrientations angle into 32 slices. Therefore, for example, in the vertical direction, a
222 // vertical line will have the value 32 on each pixel it passes through.
223 float v = pyramid->pixel(x, pyramid->height() - 1 - y, iLevel) / 32.0f;
224 return v;
225 }
226
readCompleteViewMapPixel(int iLevel,int x,int y)227 float SteerableViewMap::readCompleteViewMapPixel(int iLevel, int x, int y)
228 {
229 return readSteerableViewMapPixel(_nbOrientations, iLevel, x, y);
230 }
231
getNumberOfPyramidLevels() const232 unsigned int SteerableViewMap::getNumberOfPyramidLevels() const
233 {
234 if (_imagesPyramids[0]) {
235 return _imagesPyramids[0]->getNumberOfLevels();
236 }
237 return 0;
238 }
239
saveSteerableViewMap() const240 void SteerableViewMap::saveSteerableViewMap() const
241 {
242 for (unsigned int i = 0; i <= _nbOrientations; ++i) {
243 if (_imagesPyramids[i] == 0) {
244 cerr << "SteerableViewMap warning: orientation " << i
245 << " of steerable View Map whas not been computed yet" << endl;
246 continue;
247 }
248 int ow = _imagesPyramids[i]->width(0);
249 int oh = _imagesPyramids[i]->height(0);
250
251 // soc QString base("SteerableViewMap");
252 string base("SteerableViewMap");
253 stringstream filename;
254
255 for (int j = 0; j < _imagesPyramids[i]->getNumberOfLevels(); ++j) { // soc
256 float coeff = 1.0f; // 1 / 255.0f; // 100 * 255; // * pow(2, j);
257 // soc QImage qtmp(ow, oh, QImage::Format_RGB32);
258 ImBuf *ibuf = IMB_allocImBuf(ow, oh, 32, IB_rect);
259 int rowbytes = ow * 4;
260 char *pix;
261
262 for (int y = 0; y < oh; ++y) { // soc
263 for (int x = 0; x < ow; ++x) { // soc
264 int c = (int)(coeff * _imagesPyramids[i]->pixel(x, y, j));
265 if (c > 255) {
266 c = 255;
267 }
268 // int c = (int)(_imagesPyramids[i]->pixel(x, y, j));
269
270 // soc qtmp.setPixel(x, y, qRgb(c, c, c));
271 pix = (char *)ibuf->rect + y * rowbytes + x * 4;
272 pix[0] = pix[1] = pix[2] = c;
273 }
274 }
275
276 // soc qtmp.save(base+QString::number(i)+"-"+QString::number(j)+".png", "PNG");
277 filename << base;
278 filename << i << "-" << j << ".png";
279 ibuf->ftype = IMB_FTYPE_PNG;
280 IMB_saveiff(ibuf, const_cast<char *>(filename.str().c_str()), 0);
281 }
282 #if 0
283 QString base("SteerableViewMap");
284 for (unsigned j = 0; j < _imagesPyramids[i]->getNumberOfLevels(); ++j) {
285 GrayImage *img = _imagesPyramids[i]->getLevel(j);
286 int ow = img->width();
287 int oh = img->height();
288 float coeff = 1.0f; // 100 * 255; // * pow(2, j);
289 QImage qtmp(ow, oh, 32);
290 for (unsigned int y = 0; y < oh; ++y) {
291 for (unsigned int x = 0; x < ow; ++x) {
292 int c = (int)(coeff * img->pixel(x, y));
293 if (c > 255) {
294 c = 255;
295 }
296 //int c = (int)(_imagesPyramids[i]->pixel(x, y, j));
297 qtmp.setPixel(x, y, qRgb(c, c, c));
298 }
299 }
300 qtmp.save(base + QString::number(i) + "-" + QString::number(j) + ".png", "PNG");
301 }
302 #endif
303 }
304 }
305
306 } /* namespace Freestyle */
307