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