4 #include <iostream> // cout and cerr streams
5 #include <memory> // auto_ptr (because of exceptions)
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) {
33 QImage
image(inpName
);
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
);
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
) );
45 if ( !root
->allSettingsFromFile(confName
) )
46 throw tr("Error while reading configuration file \"%1\"") .arg(confName
);
48 confName
= "<default>";
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());
56 throw tr("Can't write output file \"%1\"") .arg(outName
);
58 // decode the image and measure the PSNR
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
;
87 /* Declared and commented in main.cpp */
88 int batchRun(const vector
<const char*> &names
) {
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();
97 while (nextStart
<length
) { // process a block of files
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");
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
]);
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
);
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])
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
]);
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" );
165 outNameStart
+= "_%1.fci";
166 for (int confID
=confStart
; confID
<outpStart
; ++confID
) {
167 QString cName
= QFileInfo(QString(names
[confID
]))
169 encodeFile( names
[inputID
], outNameStart
.arg(cName
)
176 throw tr("The compression output \"%1\" should be"
177 " either fractal image or a directory") .arg(names
[outpStart
]);
178 } // switch (types[outpStart])
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
;