Fixed wrong prediction for incomplete ranges.
[fic.git] / batch.cpp
blob0ff7e9200f1c99b5378c7072fb9d15b83ffd6b73
1 #include "headers.h"
2 #include "imageUtil.h"
4 #include <iostream> // cout and cerr streams
5 #include <memory> // auto_ptr (because of exceptions)
7 #include <QDir>
8 #include <QFileInfo>
9 #include <QImage>
10 #include <QObject>
11 #include <QString>
12 #include <QTime>
14 using namespace std;
16 /** A shortcut for Qt's QObject::tr */
17 inline QString tr(const char *str) { return QObject::tr(str); }
19 /** Decodes a fractal image into a bitmap image */
20 void decodeFile(const char *inpName,QString outName) {
21 auto_ptr<IRoot> root( IRoot::compatiblePrototype().clone(Module::ShallowCopy) );
22 if ( !root->fromFile(inpName) )
23 throw tr("Error while reading file \"%1\"") .arg(inpName);
24 root->decodeAct(MTypes::Clear);
25 root->decodeAct(MTypes::Iterate,10); //< \todo constant 10
26 if ( !root->toImage().save(outName) )
27 throw tr("Error while writing file \"%1\"") .arg(outName);
29 /** Encodes a bitmap image into a fractal image using specified configuration file.
30 * It also measures times, PSNRs, compression ratios, etc. and outputs the information. */
31 void encodeFile(const char *inpName,QString outName,const char *confName=0) {
32 // load the bitmap
33 QImage image(inpName);
34 if (image.isNull())
35 throw tr("Can't read bitmap image \"%1\"") .arg(inpName);
36 if ( image.format() != QImage::Format_RGB32 ) // convert to 24-bits
37 image= image.convertToFormat(QImage::Format_RGB32);
39 QTime time;
40 time.start();
41 // configure the module tree, using auto_ptr to release memory on exception
42 Module::CloneMethod clMethod= confName ? Module::ShallowCopy : Module::DeepCopy;
43 auto_ptr<IRoot> root( IRoot::compatiblePrototype().clone(clMethod) );
44 if (confName) {
45 if ( !root->allSettingsFromFile(confName) )
46 throw tr("Error while reading configuration file \"%1\"") .arg(confName);
47 } else
48 confName= "<default>";
49 // encode the image
50 if ( !root->encode(image) )
51 throw tr("Error while encoding file \"%1\" with %2 configuration")
52 .arg(inpName) .arg( tr(confName) );
53 float encTime= time.elapsed()/1000.0;
54 int outSize= root->toFile(outName.toStdString().c_str());
55 if (!outSize)
56 throw tr("Can't write output file \"%1\"") .arg(outName);
58 // decode the image and measure the PSNR
59 time.restart();
60 root->decodeAct(MTypes::Clear);
61 root->decodeAct(MTypes::Iterate,10); //< \todo constant 10
62 float decTime= time.elapsed()/1000.0;
63 vector<Real> psnr= Color::getPSNR( root->toImage(), image );
64 // output the information
65 cout << inpName << " " << confName << " "; //< the input and config name
66 for (int i=0; i<4; ++i) // the PSNRs
67 cout << psnr[i] << " ";
68 Real grayRatio= image.width()*image.height() / Real(outSize);
69 cout << grayRatio << " " << 3*grayRatio << " "; //< gray and color compression ratio
70 cout << encTime << " " << decTime << endl; //< encoding and decoding time
73 /** A functor providing filename classification into one of FileClassifier::FileType */
74 struct FileClassifier {
75 /** The used file types */
76 enum FileType { Fractal, Bitmap, Config, Directory };
78 FileType operator()(const char *name) const {
79 QString suffix= QFileInfo(QString(name)).suffix();
80 if (suffix=="") return Directory;
81 if (suffix=="fci") return Fractal;
82 if (suffix=="fcs") return Config;
83 else return Bitmap;
87 /* Declared and commented in main.cpp */
88 int batchRun(const vector<const char*> &names) {
89 try {
90 // classify the types of the parameters
91 vector<FileClassifier::FileType> types;
92 transform( names.begin(), names.end(), back_inserter(types), FileClassifier() );
94 int inpStart, confStart, outpStart, nextStart;
95 int length= names.size();
96 nextStart= 0;
97 while (nextStart<length) { // process a block of files
98 inpStart= nextStart;
100 FileClassifier::FileType inpType= types[inpStart];
101 // find the end of input-file list
102 for ( confStart= inpStart+1;
103 confStart<length && types[confStart]==inpType;
104 ++confStart ) /* no body */;
105 // find the end of config-file list
106 for ( outpStart= confStart;
107 outpStart<length && types[outpStart]==FileClassifier::Config;
108 ++outpStart ) /* no body */;
109 nextStart= outpStart+1; //< exactly one output per block
111 if (nextStart>length)
112 throw tr("Missing output at the end of the parameter list");
114 switch (inpType) {
116 // decompression
117 case FileClassifier::Fractal:
118 if (confStart<outpStart)
119 throw tr("No config file should be specified for decompression"
120 " (parameter %1)") .arg(confStart+1);
121 switch (types[outpStart]) {
122 case FileClassifier::Bitmap: { // the output is a single specified file
123 if (confStart-inpStart!=1) //< checking the input is single
124 throw tr("A single output file (\"%1\")"
125 " can only be used with single input") .arg(names[outpStart]);
126 decodeFile(names[inpStart],names[outpStart]);
128 break;
129 case FileClassifier::Directory: // the output is a directory
130 for (int inputID=inpStart; inputID<confStart; ++inputID) {
131 // test input's existence and permissions
132 QFileInfo inputInfo(names[inputID]);
133 if ( !inputInfo.isReadable() )
134 throw tr("Can't open file \"%1\"") .arg(names[inputID]);
135 // construct output's name and decode the file
136 QString outName= QString("%1%2%3.png") .arg(names[outpStart])
137 .arg(QDir::separator()) .arg(inputInfo.completeBaseName());
138 decodeFile( names[inputID], outName );
140 break;
141 default: // the output is *.fci
142 throw tr("The decompression output \"%1\" shouldn't be fractal image")
143 .arg(names[outpStart]);
144 } // switch (types[outpStart])
145 break;
147 // compression
148 case FileClassifier::Bitmap:
149 switch (types[outpStart]) {
150 case FileClassifier::Fractal: { // the output is a single specified file
151 if (confStart-inpStart!=1) //< checking the input is single
152 throw tr("A single output file (\"%1\")"
153 " can only be used with single input") .arg(names[outpStart]);
154 encodeFile(names[inpStart],names[outpStart]);
156 break;
157 case FileClassifier::Directory: // the output is a directory
158 for (int inputID=inpStart; inputID<confStart; ++inputID) {
159 QString outNameStart= names[outpStart] + ( QDir::separator()
160 + QFileInfo(names[inputID]).completeBaseName() );
162 if (confStart==outpStart) // using default configuration
163 encodeFile( names[inputID], outNameStart+".fci" );
164 else {
165 outNameStart+= "_%1.fci";
166 for (int confID=confStart; confID<outpStart; ++confID) {
167 QString cName= QFileInfo(QString(names[confID]))
168 .completeBaseName();
169 encodeFile( names[inputID], outNameStart.arg(cName)
170 , names[confID] );
174 break;
175 default:
176 throw tr("The compression output \"%1\" should be"
177 " either fractal image or a directory") .arg(names[outpStart]);
178 } // switch (types[outpStart])
179 break;
181 default:
182 throw tr("Invalid input file \"%1\"").arg(names[inpStart]);
183 } // switch (inpType)
185 } // while - block-of-files processing
186 } catch (QString &message) {
187 cerr << message.toStdString() << endl;
188 return 1;
190 return 0;