Fixed a bug: clearing the module tree on image read.
[fic.git] / debug.cpp
blob614e25031d2b647fdb7a4acb756cc9b1c7495857
1 #include "headers.h"
3 #ifndef NDEBUG
5 #include "modules/root.h"
6 #include "modules/squarePixels.h"
7 #include "modules/stdEncoder.h"
8 #include "modules/quadTree.h"
9 #include "modules/stdDomains.h"
10 #include "modules/saupePredictor.h"
12 #include <QBoxLayout>
13 #include <QDialog>
14 #include <QLabel>
15 #include <QTabWidget>
16 #include <QTableWidget>
18 using namespace std;
20 int pos; ///< modified by STREAM_POS macro - stores the current position (to ease debugging)
25 QWidget* MRoot::debugModule(QPixmap &pixmap,const QPoint &click) {
26 // create a new modeless dialog window
27 QDialog *dlg= new QDialog;
28 dlg->setModal(false);
29 // add a laid-out single widget created by IShapeTransformer
30 ( new QVBoxLayout(dlg) )->addWidget( moduleShape()->debugModule(pixmap,click) );
31 return dlg;
34 namespace NOSPACE {
35 struct PlaneFinder {
36 typedef const IColorTransformer::PlaneSettings CPlSet;
38 CPlSet* const plSet;
40 PlaneFinder(CPlSet *plSet2find): plSet(plSet2find) {}
41 bool operator()(const IColorTransformer::Plane &plane)
42 { return plane.settings==plSet; }
45 QWidget* MSquarePixels::debugModule(QPixmap &pixmap,const QPoint &click) {
46 ASSERT( !planeList.empty() );
47 // create a tab widget
48 QTabWidget *tabs= new QTabWidget;
49 bool isInside= pixmap.rect().contains(click);
50 // fill one tab for every job by three sub-tabs with the three modules
51 for (int i=0; i<(int)jobs.size(); ++i) {
52 PlaneBlock &job= jobs[i];
53 if ( job.ranges->getRangeList().size() == 1 )
54 continue;
55 // find the plane which is the job from
56 PlaneList::const_iterator plane=
57 find_if( planeList.begin(), planeList.end(), PlaneFinder(job.settings) );
58 ASSERT( plane != planeList.end() );
59 // find out the position of the job in the plane
60 QPoint jobClick= click;
61 if (isInside) {
62 int xShift=-1, yShift;
63 plane->pixels.getPosition(job.pixels.start,xShift,yShift);
64 ASSERT(xShift>=0);
65 jobClick-= QPoint(xShift,yShift);
66 if ( jobClick.x()<0 || jobClick.y()<0
67 || jobClick.x()>=job.width || jobClick.y()>=job.height )
68 continue;
70 // make children do the work
71 QTabWidget *tabs2= new QTabWidget;
72 tabs2->addTab( job.encoder->debugModule(pixmap,jobClick), "Encoder" );
73 tabs2->addTab( job.ranges->debugModule(pixmap,jobClick), "Ranges" );
74 tabs2->addTab( job.domains->debugModule(pixmap,jobClick), "Domains" );
76 tabs->addTab( tabs2, QString("Job %1").arg(i+1) );
78 return tabs;
82 namespace NOSPACE {
83 struct RangeInfoAccumulator {
84 const ISquareDomains::PoolList *pools;
85 int rotCounts[9];
86 vector<int> poolCounts, levelCounts;
88 RangeInfoAccumulator(const ISquareDomains::PoolList &poolList,int maxLevel)
89 : pools( &poolList ), poolCounts( pools->size(), 0 ), levelCounts( maxLevel+1, 0 ) {
90 for (int i=0; i<9; ++i)
91 rotCounts[i]= 0;
94 void operator()(const ISquareRanges::RangeNode *range) {
95 const MStdEncoder::RangeInfo &info= *MStdEncoder::RangeInfo::get(range);
96 // increment the counts of ranges on this rotation and level
97 ++rotCounts[info.rotation+1];
98 ++levelCounts.at(range->level);
99 // find and increment the pool of the range's domain (if exists)
100 if ( info.rotation>=0 ) {
101 int index= info.decAccel.pool - &*pools->begin() ;
102 ASSERT( index>=0 && index<(int)pools->size() );
103 ++poolCounts[index];
106 }; // RangeAccumulator struct
108 const ISquareRanges::RangeNode* findRangeOnPoint
109 ( const ISquareRanges::RangeList &ranges, const QPoint &click ) {
110 int x= click.x(), y= click.y();
111 ISquareRanges::RangeList::const_iterator it;
112 for (it=ranges.begin(); it!=ranges.end(); ++it) {
113 const Block &b= **it;
114 if ( b.x0<=x && b.y0<=y && b.xend>x && b.yend>y )
115 return *it;
117 ASSERT(false); return 0;
121 namespace NOSPACE {
122 static void addFramedImage(QLayout *layout,const QImage &image) {
123 QLabel *label= new QLabel;
124 label->setPixmap( QPixmap::fromImage(image) );
125 label->setFrameStyle( QFrame::Plain | QFrame::Box ); // to have a thin frame
126 label->setSizePolicy( QSizePolicy() ); // to have the frame exatly around the image
127 layout->addWidget(label);
129 template<class Assigner>
130 QImage imageFromMatrix(CSMatrix matrix,Block block,int rotation,Assigner assigner) {
131 QImage image(block.width(),block.height(),QImage::Format_RGB32);
133 using namespace MatrixWalkers;
134 // changing rotation because of transposed CheckedImage
135 rotation= Rotation::compose(1,rotation);
137 walkOperateCheckRotate( CheckedImage<QImage,QRgb>(image), assigner
138 , matrix, block, rotation );
140 return image;
142 struct GrayImageAssigner {
143 void operator()(QRgb &rgb,const SReal &pixel) {
144 int gray= Float2int<8,Real>::convertCheck(pixel);
145 rgb= qRgb(gray,gray,255); // not gray colour (intentionally)
147 void innerEnd() const {}
149 struct GrayImageMulAddAssigner: public GrayImageAssigner {
150 Real mul, add;
152 GrayImageMulAddAssigner(Real toMul,Real toAdd)
153 : mul(toMul), add(toAdd) {}
154 void operator()(QRgb &rgb,const SReal &pixel)
155 { GrayImageAssigner::operator()( rgb, pixel*mul+add ); }
158 QWidget* MStdEncoder::debugModule(QPixmap &pixmap,const QPoint &click) {
159 const ISquareRanges::RangeList &ranges= planeBlock->ranges->getRangeList();
161 QWidget *widget= new QWidget;
162 QBoxLayout *layout= new QVBoxLayout(widget);
164 if ( pixmap.rect().contains(click) ) { // info about range clicked on
165 const RangeNode &range= *findRangeOnPoint( ranges, click );
166 const RangeInfo &info= *RangeInfo::get(&range);
168 QString msg= QString("Quantized average: %1\nQuantized deviation: %2\n\n")
169 .arg((double)info.qrAvg) .arg((double)sqrt(info.qrDev2));
171 msg+= QString("Encoding SE: %1\n") .arg(info.bestSE);
173 if (info.qrDev2) {
174 int poolIndex= info.decAccel.pool - &*planeBlock->domains->getPools().begin();
175 const Block &domBlock= info.decAccel.domBlock;
176 msg+= QString("Domain pool: %1\nDomain block top-left corner coordinates:\n"
177 "\t %2 %3\n") .arg(poolIndex) .arg(domBlock.x0) .arg(domBlock.y0);
179 msg+= QString("Rotation: %1\nInversion: %2")
180 .arg((int)info.rotation) .arg(info.inverted);
181 } else
182 msg+= "Only using solid color";
184 layout->addWidget( new QLabel(msg) );
186 if (info.qrDev2) {
187 layout->addWidget( new QLabel("Domain block:") );
188 addFramedImage( layout
189 , imageFromMatrix( info.decAccel.pool->pixels, info.decAccel.domBlock
190 , 0, GrayImageAssigner() )
193 layout->addWidget( new QLabel("Domain block, transformed (encode-mode-only):") );
194 addFramedImage( layout
195 , imageFromMatrix( info.decAccel.pool->pixels, info.decAccel.domBlock
196 , info.rotation, GrayImageMulAddAssigner(info.exact.linCoeff,info.exact.constCoeff) )
200 layout->addWidget( new QLabel("Range block:") );
201 addFramedImage( layout
202 , imageFromMatrix( planeBlock->pixels, range, 0, GrayImageAssigner() )
205 } else { // provide general info
206 int maxLevel= 1 +log2ceil( max(planeBlock->width,planeBlock->height) );
207 RangeInfoAccumulator info( planeBlock->domains->getPools(), maxLevel );
208 info= for_each(ranges,info);
210 {// create a label with various counts info
211 QString msg= "Range count: %1\nRotation counts: %2\nDomain pool counts: %3";
212 // fill in the total range count
213 msg= msg.arg( ranges.size() );
214 // fill in the rotation counts
215 QString rots= QString::number(info.rotCounts[0]);
216 for (int i=1; i<9; ++i)
217 (rots+= ", ")+= QString::number(info.rotCounts[i]);
218 msg= msg.arg(rots);
219 // fill in the domain counts
220 QString doms= QString::number(info.rotCounts[0]);
221 vector<int>::const_iterator it;
222 for (it= info.poolCounts.begin(); it!=info.poolCounts.end(); ++it)
223 (doms+= ", ")+= QString::number(*it);
224 msg= msg.arg(doms);
225 // create the label and add it to the layout
226 layout->addWidget( new QLabel(msg) );
229 QTableWidget *table= new QTableWidget( maxLevel-2, 3 );
230 table->setHorizontalHeaderLabels
231 ( QStringList() << "Level" << "Ranges" << "Domain count" );
232 for (int level=2; level<maxLevel; ++level) {
233 // the number of the level
234 table->setItem( level-2, 0, new QTableWidgetItem(QString::number(level)) );
235 // the count of the ranges on the level
236 table->setItem( level-2, 1, new QTableWidgetItem(
237 QString::number( info.levelCounts[level] )
238 ) );
239 // the maximum allowed SE for the level
240 //float maxSE= planeBlock->moduleQ2SE->rangeSE( planeBlock->quality, powers[2*level] );
241 //table->setItem( level-2, 1.5, new QTableWidgetItem(QString::number(maxSE)) );
242 // the domain count for the level
243 int domCount= levelPoolInfos[level].empty() ? -1
244 : levelPoolInfos[level].back().indexBegin;
245 table->setItem( level-2, 2, new QTableWidgetItem(
246 QString("2 ^ %1").arg( log2(domCount) )
247 ) );
249 // resize the label and add it to the layout
250 table->resizeColumnsToContents();
251 table->resizeRowsToContents();
252 layout->addWidget(table);
254 if ( modulePredictor() )
255 layout->addWidget( modulePredictor()->debugModule(pixmap,click) );
256 } // if-then-else
258 return widget;
259 } // MStdEncoder::debugModule method
262 QWidget* MQuadTree::debugModule(QPixmap &pixmap,const QPoint &click) {
264 if ( pixmap.rect().contains(click) ) { // info about range clicked on
265 const ISquareRanges::RangeNode &range= *findRangeOnPoint( fringe, click );
267 Real rSum, r2Sum;
268 planeBlock->summers_makeValid(); // \todo only approximation
269 planeBlock->getSums(range).unpack(rSum,r2Sum);
270 float estSE= estimateSE(rSum,r2Sum,range.size(),range.level);
271 float realSE= range.encoderData->bestSE;
272 if (realSE==-1)
273 realSE= numeric_limits<float>::infinity();
275 QString msg= QString("Level: %1\nRegular: %2\n"
276 "Top-left corner: %3 %4\nWidth: %5\nHeight: %6\n"
277 "Estimated SE: %7 (%8% of the encoded value)")
278 .arg(range.level) .arg(range.isRegular())
279 .arg(range.x0) .arg(range.y0)
280 .arg(range.width()) .arg(range.height())
281 .arg(estSE) .arg(100*estSE/realSE);
282 return new QLabel(msg);
284 } else { // provide general info
285 QString msg;
286 // add info about heuristic dividing (if it's allowed)
287 if ( settingsInt(HeuristicAllowed) ) {
288 msg+= "Heuristic dividing info:\n"
289 "\tBad tries (forced to divide): %1\n"
290 "\tBad divides (successful merges): %2\n"
291 "\tUnsuccessful merges: %3\n";
292 msg= msg.arg(badTries).arg(badDivides).arg(triedMerges);
294 return new QLabel(msg);
295 } // if-then-else
297 } // MQuadTree::debugModule method
301 #include <QImage>
302 void debugPool(const ISquareDomains::Pool &pool) {
303 // create and fill the image
304 QImage image( pool.width, pool.height, QImage::Format_RGB32 );
306 for (int y=0; y<pool.height; ++y) {
307 QRgb *line= (QRgb*)image.scanLine(y);
308 for (int x=0; x<pool.width; ++x) {
309 int c= checkBoundsFunc( 0, (int)(pool.pixels[x][y]*256), 255 );
310 line[x]= qRgb(c,c,c);
314 image.save("pool.png");
316 return;
319 QWidget* MStdDomains::debugModule(QPixmap &pixmap,const QPoint &click) {
321 if ( pixmap.rect().contains(click) ) { // info about range clicked on
324 } else { // provide general info
327 } // if-then-else
328 return 0;
331 QWidget* MSaupePredictor::debugModule(QPixmap &pixmap,const QPoint &click) {
332 if ( pixmap.rect().contains(click) )
333 return 0;
334 return new QLabel( QString("Predicted %1/%2 (%3%)")
335 .arg(predicted) .arg(maxpred) .arg(double(100)*predicted/(double)maxpred) );
338 #endif