Writing about ISquareEncoder, started optimal-SE computations.
[fic.git] / debug.cpp
blob062f98a37876f77539c2fb6dedb82175804e9d02
1 #include "interfaces.h"
2 #ifndef NDEBUG
4 #include "modules/root.h"
5 #include "modules/squarePixels.h"
6 #include "modules/stdEncoder.h"
7 #include "modules/quadTree.h"
8 #include "modules/stdDomains.h"
9 #include "modules/saupePredictor.h"
11 #include <QBoxLayout>
12 #include <QDialog>
13 #include <QLabel>
14 #include <QTabWidget>
15 #include <QTableWidget>
17 using namespace std;
19 int pos;
21 QWidget* MRoot::debugModule(QPixmap &pixmap,const QPoint &click) {
22 // create a new modeless dialog window
23 QDialog *dlg= new QDialog;
24 dlg->setModal(false);
25 // add a laid-out single widget created by IShapeTransformer
26 ( new QVBoxLayout(dlg) )->addWidget( moduleShape()->debugModule(pixmap,click) );
27 return dlg;
30 namespace NOSPACE {
31 struct PlaneFinder {
32 typedef const IColorTransformer::PlaneSettings CPlSet;
34 CPlSet* const plSet;
36 PlaneFinder(CPlSet *plSet2find): plSet(plSet2find) {}
37 bool operator()(const IColorTransformer::Plane &plane)
38 { return plane.settings==plSet; }
41 QWidget* MSquarePixels::debugModule(QPixmap &pixmap,const QPoint &click) {
42 ASSERT( !planeList.empty() );
43 // create a tab widget
44 QTabWidget *tabs= new QTabWidget;
45 bool isInside= pixmap.rect().contains(click);
46 // fill one tab for every job by three sub-tabs with the three modules
47 for (int i=0; i<(int)jobs.size(); ++i) {
48 PlaneBlock &job= jobs[i];
49 if ( job.ranges->getRangeList().size() == 1 )
50 continue;
51 // find the plane which is the job from
52 PlaneList::const_iterator plane=
53 find_if( planeList.begin(), planeList.end(), PlaneFinder(job.settings) );
54 ASSERT( plane != planeList.end() );
55 // find out the position of the job in the plane
56 QPoint jobClick= click;
57 if (isInside) {
58 int xShift=-1, yShift;
59 plane->pixels.getPosition(job.pixels.start,xShift,yShift);
60 ASSERT(xShift>=0);
61 jobClick-= QPoint(xShift,yShift);
62 if ( jobClick.x()<0 || jobClick.y()<0
63 || jobClick.x()>=job.width || jobClick.y()>=job.height )
64 continue;
66 // make children do the work
67 QTabWidget *tabs2= new QTabWidget;
68 tabs2->addTab( job.encoder->debugModule(pixmap,jobClick), "Encoder" );
69 tabs2->addTab( job.ranges->debugModule(pixmap,jobClick), "Ranges" );
70 tabs2->addTab( job.domains->debugModule(pixmap,jobClick), "Domains" );
72 tabs->addTab( tabs2, QString("Job %1").arg(i+1) );
74 return tabs;
78 namespace NOSPACE {
79 struct RangeInfoAccumulator {
80 const ISquareDomains::PoolList *pools;
81 int rotCounts[9];
82 vector<int> poolCounts, levelCounts;
84 RangeInfoAccumulator(const ISquareDomains::PoolList &poolList,int maxLevel)
85 : pools( &poolList ), poolCounts( pools->size(), 0 ), levelCounts( maxLevel+1, 0 ) {
86 for (int i=0; i<9; ++i)
87 rotCounts[i]= 0;
90 void operator()(const ISquareRanges::RangeNode *range) {
91 const MStandardEncoder::RangeInfo &info
92 = static_cast<MStandardEncoder::RangeInfo&>( *range->encoderData );
93 // increment the counts of ranges on this rotation and level
94 ++rotCounts[info.rotation+1];
95 ++levelCounts.at(range->level);
96 // find and increment the pool of the range's domain (if exists)
97 if ( info.rotation>=0 ) {
98 int index= info.decAccel.pool - &*pools->begin() ;
99 ASSERT( index>=0 && index<(int)pools->size() );
100 ++poolCounts[index];
103 }; // RangeAccumulator struct
105 const ISquareRanges::RangeNode* findRangeOnPoint
106 ( const ISquareRanges::RangeList &ranges, const QPoint &click ) {
107 int x= click.x(), y= click.y();
108 ISquareRanges::RangeList::const_iterator it;
109 for (it=ranges.begin(); it!=ranges.end(); ++it) {
110 const Block &b= **it;
111 if ( b.x0<=x && b.y0<=y && b.xend>x && b.yend>y )
112 return *it;
114 ASSERT(false); return 0;
118 namespace NOSPACE {
119 static void addFramedImage(QLayout *layout,const QImage &image) {
120 QLabel *label= new QLabel;
121 label->setPixmap( QPixmap::fromImage(image) );
122 label->setFrameStyle( QFrame::Plain | QFrame::Box ); // to have a thin frame
123 label->setSizePolicy( QSizePolicy() ); // to have the frame exatly around the image
124 layout->addWidget(label);
126 template<class Assigner>
127 QImage imageFromMatrix(CSMatrix matrix,Block block,int rotation,Assigner assigner) {
128 QImage image(block.width(),block.height(),QImage::Format_RGB32);
130 using namespace MatrixWalkers;
131 // changing rotation because of transposed CheckedImage
132 rotation= Rotation::compose(1,rotation);
134 walkOperateCheckRotate( CheckedImage<QImage,QRgb>(image), assigner
135 , matrix, block, rotation );
137 return image;
139 struct GrayImageAssigner {
140 void operator()(QRgb &rgb,const SReal &pixel) {
141 int gray= Float2int<8,Real>::convertCheck(pixel);
142 rgb= qRgb(gray,gray,255); // not gray colour (intentionally)
144 void innerEnd() const {}
146 struct GrayImageMulAddAssigner: public GrayImageAssigner {
147 Real mul, add;
149 GrayImageMulAddAssigner(Real toMul,Real toAdd)
150 : mul(toMul), add(toAdd) {}
151 void operator()(QRgb &rgb,const SReal &pixel)
152 { GrayImageAssigner::operator()( rgb, pixel*mul+add ); }
155 QWidget* MStandardEncoder::debugModule(QPixmap &pixmap,const QPoint &click) {
156 const ISquareRanges::RangeList &ranges= planeBlock->ranges->getRangeList();
158 QWidget *widget= new QWidget;
159 QBoxLayout *layout= new QVBoxLayout(widget);
161 if ( pixmap.rect().contains(click) ) { // info about range clicked on
162 const RangeNode &range= *findRangeOnPoint( ranges, click );
163 const RangeInfo &info= static_cast<RangeInfo&>(*range.encoderData);
165 QString msg= QString("Quantized average: %1\nQuantized deviation: %2\n\n")
166 .arg((double)info.qrAvg) .arg((double)sqrt(info.qrDev2));
168 msg+= QString("Encoding SE: %1\n") .arg(info.bestSE);
170 if (info.qrDev2) {
171 int poolIndex= info.decAccel.pool - &*planeBlock->domains->getPools().begin();
172 const Block &domBlock= info.decAccel.domBlock;
173 msg+= QString("Domain pool: %1\nDomain block top-left corner coordinates:\n"
174 "\t %2 %3\n") .arg(poolIndex) .arg(domBlock.x0) .arg(domBlock.y0);
176 msg+= QString("Rotation: %1\nInversion: %2")
177 .arg((int)info.rotation) .arg(info.inverted);
178 } else
179 msg+= "Only using solid color";
181 layout->addWidget( new QLabel(msg) );
183 if (info.qrDev2) {
184 layout->addWidget( new QLabel("Domain block:") );
185 addFramedImage( layout
186 , imageFromMatrix( info.decAccel.pool->pixels, info.decAccel.domBlock
187 , 0, GrayImageAssigner() )
190 layout->addWidget( new QLabel("Domain block, transformed (encode-mode-only):") );
191 addFramedImage( layout
192 , imageFromMatrix( info.decAccel.pool->pixels, info.decAccel.domBlock
193 , info.rotation, GrayImageMulAddAssigner(info.exact.linCoeff,info.exact.constCoeff) )
197 layout->addWidget( new QLabel("Range block:") );
198 addFramedImage( layout
199 , imageFromMatrix( planeBlock->pixels, range, 0, GrayImageAssigner() )
202 } else { // provide general info
203 int maxLevel= 1 +log2ceil( max(planeBlock->width,planeBlock->height) );
204 RangeInfoAccumulator info( planeBlock->domains->getPools(), maxLevel );
205 info= for_each( ranges.begin(), ranges.end(), info );
207 {// create a label with various counts info
208 QString msg= "Range count: %1\nRotation counts: %2\nDomain pool counts: %3";
209 // fill in the total range count
210 msg= msg.arg( ranges.size() );
211 // fill in the rotation counts
212 QString rots= QString::number(info.rotCounts[0]);
213 for (int i=1; i<9; ++i)
214 (rots+= ", ")+= QString::number(info.rotCounts[i]);
215 msg= msg.arg(rots);
216 // fill in the domain counts
217 QString doms= QString::number(info.rotCounts[0]);
218 vector<int>::const_iterator it;
219 for (it= info.poolCounts.begin(); it!=info.poolCounts.end(); ++it)
220 (doms+= ", ")+= QString::number(*it);
221 msg= msg.arg(doms);
222 // create the label and add it to the layout
223 layout->addWidget( new QLabel(msg) );
226 QTableWidget *table= new QTableWidget( maxLevel-2, 3 );
227 table->setHorizontalHeaderLabels
228 ( QStringList() << "Level" << "Ranges" << "Domain count" );
229 for (int level=2; level<maxLevel; ++level) {
230 // the number of the level
231 table->setItem( level-2, 0, new QTableWidgetItem(QString::number(level)) );
232 // the count of the ranges on the level
233 table->setItem( level-2, 1, new QTableWidgetItem(
234 QString::number( info.levelCounts[level] )
235 ) );
236 // the maximum allowed SE for the level
237 //float maxSE= planeBlock->moduleQ2SE->rangeSE( planeBlock->quality, powers[2*level] );
238 //table->setItem( level-2, 1.5, new QTableWidgetItem(QString::number(maxSE)) );
239 // the domain count for the level
240 int domCount= levelPoolInfos[level].empty() ? -1
241 : levelPoolInfos[level].back().indexBegin;
242 table->setItem( level-2, 2, new QTableWidgetItem(
243 QString("2 ^ %1").arg( log2(domCount) )
244 ) );
246 // resize the label and add it to the layout
247 table->resizeColumnsToContents();
248 table->resizeRowsToContents();
249 layout->addWidget(table);
251 if ( modulePredictor() )
252 layout->addWidget( modulePredictor()->debugModule(pixmap,click) );
253 } // if-then-else
255 return widget;
256 } // MStandardEncoder::debugModule method
259 QWidget* MQuadTree::debugModule(QPixmap &pixmap,const QPoint &click) {
261 if ( pixmap.rect().contains(click) ) { // info about range clicked on
262 const ISquareRanges::RangeNode &range= *findRangeOnPoint( fringe, click );
264 Real rSum, r2Sum;
265 planeBlock->summers_makeValid(); /// \todo only approximation
266 planeBlock->getSums(range).unpack(rSum,r2Sum);
267 float estSE= estimateSE(rSum,r2Sum,range.size(),range.level);
269 QString msg= QString("Level: %1\nRegular: %2\n"
270 "Top-left corner: %3 %4\nWidth: %5\nHeight: %6\n"
271 "Estimated SE: %7 - %8% of the encoded value")
272 .arg(range.level) .arg(range.isRegular())
273 .arg(range.x0) .arg(range.y0)
274 .arg(range.width()) .arg(range.height())
275 .arg(estSE) .arg(100*estSE/range.encoderData->bestSE);
276 return new QLabel(msg);
278 } else { // provide general info
279 QString msg;
280 // add info about heuristic dividing (if it's allowed)
281 if ( settingsInt(HeuristicAllowed) ) {
282 msg+= "Heuristic dividing info:\n"
283 "\tBad tries (forced to divide): %1\n"
284 "\tBad divides (successful merges): %2\n"
285 "\tUnsuccessful merges: %3\n";
286 msg= msg.arg(badTries).arg(badDivides).arg(triedMerges);
288 return new QLabel(msg);
289 } // if-then-else
291 } // MQuadTree::debugModule method
295 #include <QImage>
296 void debugPool(const ISquareDomains::Pool &pool) {
297 // create and fill the image
298 QImage image( pool.width, pool.height, QImage::Format_RGB32 );
300 for (int y=0; y<pool.height; ++y) {
301 QRgb *line= (QRgb*)image.scanLine(y);
302 for (int x=0; x<pool.width; ++x) {
303 int c= checkBoundsFunc( 0, (int)(pool.pixels[x][y]*256), 255 );
304 line[x]= qRgb(c,c,c);
308 image.save("pool.png");
310 return;
313 QWidget* MStdDomains::debugModule(QPixmap &pixmap,const QPoint &click) {
315 if ( pixmap.rect().contains(click) ) { // info about range clicked on
318 } else { // provide general info
321 } // if-then-else
322 return 0;
325 QWidget* MSaupePredictor::debugModule(QPixmap &pixmap,const QPoint &click) {
326 if ( pixmap.rect().contains(click) )
327 return 0;
328 return new QLabel( QString("Predicted %1/%2 (%3%)")
329 .arg(predicted) .arg(maxpred) .arg(double(100)*predicted/(double)maxpred) );
332 #endif