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