const-fixing functions
[mldemos:baraks-mldemos.git] / _AlgorithmsPlugins / Projections / interfaceKPCAProjection.cpp
1 #include "interfaceKPCAProjection.h"
2 #include "projectorKPCA.h"
3 #include <QDebug>
4 #include <QImage>
5 #include <QClipboard>
6 #include <qcontour.h>
7 #include <algorithm>
8
9 using namespace std;
10
11 KPCAProjection::KPCAProjection()
12     : widget(new QWidget()), contourLabel(0), pcaPointer(0), contourWidget(new QWidget()),
13       xIndex(0), yIndex(1)
14 {
15     params = new Ui::paramsKPCA();
16     params->setupUi(widget);
17     contours = new Ui::ContourWidget();
18     contours->setupUi(contourWidget);
19     contourWidget->layout()->setSizeConstraint( QLayout::SetFixedSize );
20     contourWidget->setWindowTitle("Kernel Eigenvector Projections");
21
22     connect(params->kernelTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(ChangeOptions()));
23     connect(params->contourButton, SIGNAL(clicked()), this, SLOT(ShowContours()));
24     connect(contours->dimSpin, SIGNAL(valueChanged(int)), this, SLOT(DrawContours(int)));
25     connect(contours->displayCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(ShowContours()));
26     connect(contours->clipboardButton, SIGNAL(clicked()), this, SLOT(SaveScreenshot()));
27     connect(contours->spinX1, SIGNAL(valueChanged(int)), this, SLOT(ContoursChanged()));
28     connect(contours->spinX2, SIGNAL(valueChanged(int)), this, SLOT(ContoursChanged()));
29     connect(contours->spinZoom, SIGNAL(valueChanged(int)), this, SLOT(ContoursChanged()));
30 }
31
32 // virtual functions to manage the algorithm creation
33 Projector *KPCAProjection::GetProjector()
34 {
35     return new ProjectorKPCA(params->dimCountSpin->value());
36 }
37
38 void KPCAProjection::SetParams(Projector *projector)
39 {
40     if(!projector) return;
41     ProjectorKPCA *kpca = dynamic_cast<ProjectorKPCA*>(projector);
42     if(!kpca) return;
43     // we add 1 to the kernel type because we have taken out the linear kernel
44     kpca->SetParams(params->kernelTypeCombo->currentIndex()+1, params->kernelDegSpin->value(), params->kernelWidthSpin->value());
45 }
46
47 fvec KPCAProjection::GetParams()
48 {
49     int kernelType = params->kernelTypeCombo->currentIndex();
50     float kernelGamma = params->kernelWidthSpin->value();
51     float kernelDegree = params->kernelDegSpin->value();
52
53     fvec par(3);
54     par[0] = kernelType;
55     par[1] = kernelGamma;
56     par[2] = kernelDegree;
57     return par;
58 }
59
60 void KPCAProjection::SetParams(Projector *projector, fvec parameters)
61 {
62     if(!projector) return;
63     int kernelType = parameters.size() > 0 ? parameters[0] : 0;
64     float kernelGamma = parameters.size() > 1 ? parameters[1] : 0.1;
65     int kernelDegree = parameters.size() > 2 ? parameters[2] : 1;
66
67     ProjectorKPCA *kpca = dynamic_cast<ProjectorKPCA*>(projector);
68     if(!kpca) return;
69     // we add 1 to the kernel type because we have taken out the linear kernel
70     kpca->SetParams(kernelType+1, kernelDegree, kernelGamma);
71 }
72
73 void KPCAProjection::GetParameterList(std::vector<QString> &parameterNames,
74                                 std::vector<QString> &parameterTypes,
75                                 std::vector< std::vector<QString> > &parameterValues)
76 {
77     parameterNames.push_back("Kernel Type");
78     parameterNames.push_back("Kernel Width");
79     parameterNames.push_back("Kernel Degree");
80     parameterTypes.push_back("List");
81     parameterTypes.push_back("Real");
82     parameterTypes.push_back("Integer");
83     parameterValues.push_back(vector<QString>());
84     parameterValues.back().push_back("Poly");
85     parameterValues.back().push_back("RBF");
86     parameterValues.push_back(vector<QString>());
87     parameterValues.back().push_back("0.00000001f");
88     parameterValues.back().push_back("9999999");
89     parameterValues.push_back(vector<QString>());
90     parameterValues.back().push_back("1");
91     parameterValues.back().push_back("150");
92 }
93
94 void KPCAProjection::SaveScreenshot()
95 {
96     const QPixmap *screenshot = contours->plotLabel->pixmap();
97     if(screenshot->isNull()) return;
98
99     QClipboard *clipboard = QApplication::clipboard();
100     clipboard->setImage(screenshot->toImage());
101     clipboard->setPixmap(*screenshot);
102 }
103
104 void KPCAProjection::ContoursChanged()
105 {
106     contourPixmaps.clear();
107     ShowContours();
108 }
109
110 void KPCAProjection::ShowContours()
111 {
112     PCA *pca = dynamic_cast<PCA*> (pcaPointer);
113     if(!pca) return;
114     DrawContours(contours->dimSpin->value());
115     contourWidget->show();
116 }
117
118 void KPCAProjection::ChangeOptions()
119 {
120     switch(params->kernelTypeCombo->currentIndex())
121     {
122     case 0: // poly
123         params->kernelDegSpin->setEnabled(true);
124         params->kernelDegSpin->setVisible(true);
125         params->kernelWidthSpin->setEnabled(true);
126         params->kernelWidthSpin->setVisible(true);
127         params->kernelDegSpin->setDecimals(0);
128         params->kernelDegSpin->setRange(1,999);
129         params->kernelDegSpin->setSingleStep(1);
130         params->kernelWidthSpin->setRange(-999,999);
131         params->param1Label->setText("Degree");
132         params->param2Label->setText("Offset");
133         break;
134     case 1: // RBF
135         params->kernelDegSpin->setEnabled(false);
136         params->kernelDegSpin->setVisible(false);
137         params->param1Label->setText("");
138         params->param2Label->setText("Width");
139         params->kernelWidthSpin->setRange(0.001,999);
140         params->kernelWidthSpin->setEnabled(true);
141         params->kernelWidthSpin->setVisible(true);
142         break;
143     case 2: // TANH
144         params->kernelDegSpin->setEnabled(true);
145         params->kernelDegSpin->setVisible(true);
146         params->kernelWidthSpin->setEnabled(true);
147         params->kernelWidthSpin->setVisible(true);
148         params->kernelDegSpin->setDecimals(3);
149         params->kernelDegSpin->setRange(0.01,100);
150         params->kernelDegSpin->setSingleStep(0.1);
151         params->kernelWidthSpin->setRange(-999,999);
152         params->param1Label->setText("Scale");
153         params->param2Label->setText("Offset");
154         break;
155
156     }
157 }
158
159 void KPCAProjection::DrawInfo(Canvas *canvas, QPainter &painter, Projector *projector)
160 {
161     if(!canvas || !projector) return;
162 }
163
164 void KPCAProjection::GetContoursPixmap(int index)
165 {
166     PCA *pca = dynamic_cast<PCA*>(pcaPointer);
167     if(!pca) return;
168     if(contourPixmaps.count(index)) return; // nothing to be done here, moving on!
169
170     // we compute the density map
171     int w = 65;
172     int h = 65;
173     int hmo = h-1; // we will drop one line at the edges to avoid weird border conditions
174     int wmo = w-1;
175     QImage image(wmo,hmo,QImage::Format_RGB32);
176     int dim = pca->sourcePoints.rows();
177     float zoom = 1;
178     // use [xy]Index given in the constuctor if spinX1 [ov]
179     if (contours->spinX1->isVisible()) {
180         xIndex = contours->spinX1->value()-1;
181         yIndex = contours->spinX2->value()-1;
182         zoom = (contours->spinZoom->value()-1)*0.25 + 1;
183     }
184     //qDebug() << "KPCAProjection::GetContoursPixmap - xIndex:" << xIndex << ", yIndex:" << yIndex << ", zoom:" << zoom << ".";
185     double multiplier = 1000.; // this is used to avoid numerical instabilities when computing the contour lines
186     VectorXd point(dim);
187     FOR(d,dim) point(d) = 0.0;
188     double xdiff = xmax - xmin;
189     double ydiff = ymax - ymin;
190     //qDebug() << "KPCAProjection::GetContoursPixmap - xmin:" << xmin << ", ymin:" << ymin << ", xman:" << xmax << ", ymax:" << ymax << ".";
191     double *values = new double[w*h];
192     double vmin = DBL_MAX, vmax = -DBL_MAX;
193
194     double zxmin = xmin - xdiff*0.5*(zoom-1);
195     double zxmax = xmax + xdiff*0.5*(zoom-1);
196     double zxdiff = zxmax - zxmin;
197     double zymin = ymin - ydiff*0.5*(zoom-1);
198     double zymax = ymax + ydiff*0.5*(zoom-1);
199     double zydiff = zymax - zymin;
200     FOR(i, w)
201     {
202         FOR(j, h)
203         {
204             if ( xIndex < dim ) point( xIndex ) = i * zxdiff / (double)w + zxmin;
205             if ( yIndex < dim ) point( yIndex ) = j * zydiff / (double)h + zymin;
206             double value = pcaPointer->test( point, index-1, multiplier ); // indices start from 1 in params.dimCountSpin
207             vmin = min(value, vmin);
208             vmax = max(value, vmax);
209             values[j*w + i] = value;
210         }
211     }
212     double vdiff=vmax-vmin;
213     //qDebug() << "KPCAProjection::GetContoursPixmap - vmin:" << vmin << ", vmax:" << vmax << " - vdiff: " << vdiff << ".";
214     if(vdiff == 0) vdiff = 1.f;
215     FOR(i, wmo)
216     {
217         FOR(j, hmo)
218         {
219             int value = (int)((values[j*w + i]-vmin)/vdiff*255);
220             image.setPixel(i,j, qRgb((int)value,value,value));
221         }
222     }
223     QPixmap contourPixmap = QPixmap::fromImage(image).scaled(512,512, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
224
225     int W = contourPixmap.width();
226     int H = contourPixmap.height();
227     QPainter painter(&contourPixmap);
228     painter.setRenderHint(QPainter::Antialiasing);
229
230     // we plot the samples
231     painter.setPen(Qt::black);
232     painter.setBrush(Qt::white);
233     painter.setOpacity(1);
234     FOR(i, contourSamples.size())
235     {
236         fvec &sample = contourSamples[i];
237         //qDebug() << "drawing sample at " << sample[0] << sample[1];
238         float x = (sample[xIndex]-zxmin)/(zxmax-zxmin)*wmo;
239         float y = (sample[yIndex]-zymin)/(zymax-zymin)*hmo;
240         x = (x)*W/wmo;
241         y = (y)*H/hmo;
242         Canvas::drawSample(painter, QPointF(x,y), 10, contourSampleLabels[i]);
243     }
244
245     // we plot the contour lines
246     if(contourSamples.size())
247     {
248         QContour contour(values, w, h);
249         contour.bDrawColorbar = true;
250         //contour.SetLimits(zvmin, zvmax);
251         contour.Paint(painter, 20, zoom);
252     }
253
254     contourPixmaps[index] = contourPixmap;
255     delete [] values;
256 }
257
258 void KPCAProjection::DrawContours(int index)
259 {
260     PCA *pca = dynamic_cast<PCA*>(pcaPointer);
261     if(!pca) return;
262     int displayType = contours->displayCombo->currentIndex();
263     if ( ( displayType < 0 ) or ( displayType > 1 ) ) {
264         qDebug() << "KPCAProjection::DrawContours - displayType out of range :" << displayType;
265         displayType = 0;
266     }
267
268     switch(displayType)
269     {
270     case 0: // single
271     {
272         // ensure that we have the right pixmap
273         GetContoursPixmap(index);
274         contours->plotLabel->setPixmap(contourPixmaps[index]);
275     }
276         break;
277     case 1: // take all the values and draw them
278     {
279         int maximum = contours->dimSpin->maximum();
280         for(int i=1; i<=contours->dimSpin->maximum(); i++)
281         {
282             GetContoursPixmap(i);
283         }
284         int gridX = std::ceil(sqrtf(maximum));
285         //int gridY = std::ceil(maximum / (float)gridX);
286         int gridY = gridX;
287
288         int w = contourPixmaps[1].width();
289         int h = contourPixmaps[1].height();
290         QPixmap bigPixmap(gridX*w, gridX*h);
291         QBitmap bitmap(bigPixmap.width(), bigPixmap.height());
292         bitmap.clear();
293         bigPixmap.setMask(bitmap);
294         bigPixmap.fill(Qt::transparent);
295         QPainter painter(&bigPixmap);
296         for(int i=1; i<=contours->dimSpin->maximum(); i++)
297         {
298             int x = ((i-1)%gridX)*w;
299             int y = ((i-1)/gridX)*h;
300             QRect rect(x,y,w,h);
301             painter.drawPixmap(rect, contourPixmaps[i], QRect(0,0,w,h));
302         }
303         contours->plotLabel->setPixmap(bigPixmap.scaled(QSize(w,h), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
304     }
305         break;
306     }
307     if(contourWidget->isVisible()) contours->plotLabel->repaint();
308 }
309
310 void DrawEigenvals(QPainter &painter, fvec eigs, bool bSkipFirstEigenvector)
311 {
312     int w=painter.window().width();
313     int h=painter.window().height();
314     int pad = 5;
315
316     int dim = eigs.size();
317     float maxEigVal = 1.f;
318     if(dim > 2) maxEigVal = bSkipFirstEigenvector ? eigs[1] : eigs[0];
319     else if(dim) maxEigVal = eigs[0];
320
321     painter.setPen(Qt::gray);
322     painter.drawLine(QPointF(pad, h-2*pad), QPointF(w-pad, h-2*pad));
323     painter.drawLine(QPointF(pad, pad), QPointF(pad, h-2*pad));
324     painter.setRenderHint(QPainter::Antialiasing);
325     painter.setPen(Qt::NoPen);
326     painter.setBrush(Qt::black);
327     int rectW = (w-2*pad) / dim - 2;
328     FOR(i, dim)
329     {
330         float eigval = eigs[i];
331
332         int x = dim==1 ? w/2 : i * (w-2*pad) / dim + pad;
333         int y = (int)(eigval/maxEigVal * (h-2*pad));
334         y = min(y, h-2*pad);
335         painter.drawRect(x,h-2*pad,rectW,-y);
336     }
337     painter.setBrush(Qt::NoBrush);
338     //painter.drawLine(point, QPoint(w-2*pad, h-2*pad));
339     QFont font = painter.font();
340     font.setPointSize(8);
341     painter.setFont(font);
342     painter.setPen(Qt::black);
343     painter.drawText(0,0,w,2*pad,Qt::AlignCenter, "eigenvalues");
344     int step = 1;
345     while((dim/step > 8)) step++;
346     for(int i=0; i<dim; i+=step)
347     {
348         int x = dim==1? w/2 : (i+0.5f) * (w-2*pad) / (dim) + pad+(!i?1:0);
349         if(i==dim-1) x -= 4;
350         //        int x = dim==1? w/2 : i*(w-2*pad)/(dim-1);
351         painter.drawText(x - 4, h-1, QString("l%1").arg(i+1));
352     }
353 }
354 void KPCAProjection::DrawModel(Canvas *canvas, QPainter &painter, Projector *projector)
355 {
356     contourPixmaps.clear();
357     if(!canvas || !projector) return;
358     ProjectorKPCA *kpca = dynamic_cast<ProjectorKPCA*>(projector);
359     if(!kpca) return;
360     contourPca = *kpca->pca;
361     pcaPointer = &contourPca;
362     vector<fvec> samples = projector->source;
363     contourSamples = samples;
364     contourSampleLabels = canvas->data->GetLabels();
365     if(contourSampleLabels.size() != contourSamples.size()) contourSampleLabels = ivec(contourSamples.size(), 0);
366
367     // changed FLT_MAX -> DBL_MAX [ov]
368     xmin=ymin=DBL_MAX;
369     xmax=ymax=-DBL_MAX;
370     int dim = samples.size() ? samples[0].size() : 2;
371     contours->spinX1->setRange(1, dim);
372     contours->spinX2->setRange(1, dim);
373     FOR(i, samples.size())
374     {
375         contourSamples[i] -= kpca->mean;
376         xmin=min((double)samples[i][xIndex]-kpca->mean[xIndex], xmin);
377         xmax=max((double)samples[i][xIndex]-kpca->mean[xIndex], xmax);
378         ymin=min((double)samples[i][yIndex]-kpca->mean[yIndex], ymin);
379         ymax=max((double)samples[i][yIndex]-kpca->mean[yIndex], ymax);
380     }
381
382     double xdiff = (xmax - xmin);
383     double ydiff = (ymax - ymin);
384
385     double diff = max(xdiff, ydiff);
386     double xmid = (xmax-xmin)/2 + xmin;
387     double ymid = (ymax-ymin)/2 + ymin;
388     xmin = xmid - diff;
389     xmax = xmid + diff;
390     ymin = ymid - diff;
391     ymax = ymid + diff;
392     /*
393     if(xdiff <= ydiff)
394     {
395         xdiff = ydiff;
396         double xmid = ((xmax - xmin)/2.0) + xmin;
397         xmin = xmid - xdiff/2.0;
398         xmax = xmid + xdiff/2.0;
399     }
400     else if(ydiff < xdiff)
401     {
402         ydiff = xdiff;
403         double ymid = ((ymax - ymin)/2.0) + ymin;
404         ymin = ymid - (ydiff/2.0);
405         ymax = ymid + (ydiff/2.0);
406     }
407     if(xdiff == 0) xdiff = .5f;
408     if(ydiff == 0) ydiff = .5f;
409     */
410
411     /*
412     xmin -= xdiff;
413     xmax += xdiff;
414     ymin -= ydiff;
415     ymax += ydiff;
416     */
417     if(samples.size() < 3)
418     {
419         xmin -= diff;
420         xmax += diff;
421         ymin -= diff;
422         ymax += diff;
423     }
424
425     // we get the eigenvectors
426     fvec eigenvalues;
427     VectorXd eigs = kpca->pca->eigenvalues;
428     FOR(i, eigs.rows())
429     {
430         eigenvalues.push_back(eigs(i));
431     }
432     sort(eigenvalues.begin(), eigenvalues.end(), std::greater<float>());
433     eigenvalues.resize(params->dimCountSpin->value());
434     //FOR(i, eigenvalues.size()) qDebug() << "eigs" << i << eigenvalues[i];
435
436     float accumulator = 0;
437     float maxEigVal = 0;
438     FOR(i, eigenvalues.size()) if(eigenvalues[i] == eigenvalues[i] && eigenvalues[i] >= 0) maxEigVal += eigenvalues[i];
439
440     params->eigenList->clear();
441     FOR(i, eigenvalues.size())
442     {
443         float eigval = eigenvalues[i];
444         if(eigval == eigval && eigval >= 0)
445         {
446             accumulator += eigval / maxEigVal;
447         }
448         else eigval = 0;
449         params->eigenList->addItem(QString("%1: %2 %3%%").arg(i+1).arg(eigval, 0, 'f', 2).arg(eigval/maxEigVal*100, 0, 'f', 1));
450     }
451
452     QPixmap pixmap(params->eigenGraph->size());
453     QBitmap bitmap(params->eigenGraph->size());
454     pixmap.setMask(bitmap);
455     pixmap.fill(Qt::transparent);
456     QPainter eigenPainter(&pixmap);
457     DrawEigenvals(eigenPainter, eigenvalues, true);
458     params->eigenGraph->setPixmap(pixmap);
459
460     contours->dimSpin->setRange(1, kpca->targetDims);
461     DrawContours(contours->dimSpin->value());
462
463     if(canvas->canvasType) return;
464     if(!canvas->data->bProjected) // We are displaying a Manifold to 1D
465     {
466         painter.setRenderHint(QPainter::Antialiasing);
467
468         // we need to sort the list of points
469         vector< pair<float, int> > points(projector->projected.size());
470         FOR(i, projector->projected.size())
471         {
472             points[i] = make_pair(projector->projected[i][0], i);
473         }
474         sort(points.begin(), points.end());
475         float minVal = points.front().first;
476         float maxVal = points.back().first;
477
478         // now we go through the points and compute the back projection
479         int steps = min((int)points.size(), 64);
480         int index = 0;
481         vector<QPointF> pointList;
482         FOR(i, steps)
483         {
484             float val = (i+1)/(float)steps*(maxVal-minVal) + minVal;
485             int nextIndex = (i+1)/(float)steps*points.size();
486             fvec mean(canvas->data->GetDimCount());
487             float meanVal = 0;
488             int count = 0;
489             while(index < points.size() && index < nextIndex)
490             //while(index < points.size() && points[index].first < val)
491             {
492                 meanVal += points[index].first;
493                 mean += canvas->data->GetSample(points[index].second);
494                 count++;
495                 index++;
496             }
497             mean /= count;
498             meanVal /= count;
499             // we look for the closest point to the value in projected space
500             int closest = 0;
501             float closestDist = FLT_MAX;
502             FOR(p, points.size())
503             {
504                 float dist = (meanVal-points[p].first)*(meanVal-points[p].first);
505                 if(dist < closestDist)
506                 {
507                     closestDist = dist;
508                     closest = p;
509                 }
510             }
511             QPointF point = canvas->toCanvasCoords(mean);
512             //QPointF point = canvas->toCanvasCoords(canvas->data->GetSample(points[closest].second));
513             if(!count) pointList.push_back(pointList.back());
514             else pointList.push_back(point);
515         }
516         // and we draw it
517         FOR(i, pointList.size()-1)
518         {
519             painter.setPen(QPen(Qt::black, 2));
520             painter.drawLine(pointList[i], pointList[i+1]);
521         }
522         FOR(i, pointList.size())
523         {
524             painter.setPen(QPen(Qt::black, 3));
525             painter.setBrush(QBrush(QColor(255*i/pointList.size(), 255*i/pointList.size(), 255*i/pointList.size())));
526             painter.drawEllipse(pointList[i], 7, 7);
527         }
528     }
529
530 }
531
532 // virtual functions to manage the GUI and I/O
533 QString KPCAProjection::GetAlgoString()
534 {
535     return QString("KPCA");
536 }
537
538 void KPCAProjection::SaveOptions(QSettings &settings)
539 {
540     settings.setValue("kernelTypeCombo", params->kernelTypeCombo->currentIndex());
541     settings.setValue("kernelDegSpin", params->kernelDegSpin->value());
542     settings.setValue("kernelWidthSpin", params->kernelWidthSpin->value());
543     settings.setValue("dimCountSpin", params->dimCountSpin->value());
544 }
545
546 bool KPCAProjection::LoadOptions(QSettings &settings)
547 {
548     if(settings.contains("kernelTypeCombo")) params->kernelTypeCombo->setCurrentIndex(settings.value("kernelTypeCombo").toInt());
549     if(settings.contains("kernelDegSpin")) params->kernelDegSpin->setValue(settings.value("kernelDegSpin").toFloat());
550     if(settings.contains("kernelWidthSpin")) params->kernelWidthSpin->setValue(settings.value("kernelWidthSpin").toFloat());
551     if(settings.contains("dimCountSpin")) params->dimCountSpin->setValue(settings.value("dimCountSpin").toInt());
552     ChangeOptions();
553     return true;
554 }
555
556 void KPCAProjection::SaveParams(QTextStream &file)
557 {
558     file << "projectOptions" << ":" << "kernelTypeCombo" << " " << params->kernelTypeCombo->currentIndex() << "\n";
559     file << "projectOptions" << ":" << "kernelDegSpin" << " " << params->kernelDegSpin->value() << "\n";
560     file << "projectOptions" << ":" << "kernelWidthSpin" << " " << params->kernelWidthSpin->value() << "\n";
561     file << "projectOptions" << ":" << "dimCountSpin" << " " << params->dimCountSpin->value() << "\n";
562 }
563
564 bool KPCAProjection::LoadParams(QString name, float value)
565 {
566     if(name.endsWith("kernelTypeCombo")) params->kernelTypeCombo->setCurrentIndex((int)value);
567     if(name.endsWith("kernelDegSpin")) params->kernelDegSpin->setValue(value);
568     if(name.endsWith("kernelWidthSpin")) params->kernelWidthSpin->setValue(value);
569     if(name.endsWith("dimCountSpin")) params->dimCountSpin->setValue((int)value);
570     ChangeOptions();
571     return true;
572 }