00001
00002
00003
00004
00005 from shutil import copyfile, move
00006 from util import *
00007 from tempimage import *
00008 from referencefile import *
00009
00010 class FileTester:
00011 """Tests the generated files and selects which one should be output.
00012
00013 Removes other temporary files in the process.
00014 """
00015 def __init__(self):
00016 """Constructor.
00017
00018 Data members:
00019 Mode : Mode of file selection. available modes:
00020 "q" : choose the smallest file w/ at least given quality.
00021 "s" : choose the best file with at most given filesize.
00022 MinQuality : Minimum quality - used when mode is q . Specified in %,
00023 e.g. "10.1%" or in subjective units, e.g. "10.1", that
00024 are closer to JPG quality settings.
00025 MaxSize : Maximum filesize - used when mode is s . Specified in %
00026 of input filesize, e.g. "10.1%", % of filesize of the
00027 reference file, e.g. "10.1r%", kiB, MiB, GiB, e.g.
00028 "10.1kiB", in bytes, e.g. "10342" or in average bits per
00029 pixel, e.g. "2.4bpp" .
00030 FileList : List of all temporaty images. Contains their filenames,
00031 filesizes, quality values and data about how they were
00032 saved.
00033 """
00034 self.Mode = "q"
00035 self.MinQuality = "100%"
00036 self.MaxSize = "50%"
00037 self.FileList = []
00038
00039 def set_mode(self, m):
00040 """Sets mode of file selection.
00041
00042 Arguments:
00043 m: Mode to set. Valid values are "q" and "s"
00044 """
00045 if(m in("q", "s")):
00046 self.Mode = m
00047 else:
00048 warning("wrong value passed by --mode (-m): using default")
00049
00050 def set_min_quality(self, q):
00051 """Sets minimum quality used in "q" mode.
00052
00053 Argumets:
00054 q: quality to set. Valid values are floats 0-100% or 0-100.
00055 """
00056 if q[-1] == "%":
00057 self.MinQuality = valid_number_str_suffix(q, "100%", "%",
00058 "--minquality", 0, 100,
00059 True)
00060 else:
00061 self.MinQuality = valid_number_str(q, "100%", "--minquality", 0,
00062 100, True)
00063
00064 def set_max_size(self, s):
00065 """Sets maximum size used in "s" mode.
00066
00067 Arguments:
00068 s: size to set. Valid values are floats 0-100% , 0-100r% ,
00069 0-1048576kiB/MiB/GiB , 0-1073741824
00070 """
00071 if s.endswith("r%"):
00072 self.MaxSize = valid_number_str_suffix(s, "50%", "r%", "--maxsize",
00073 0, 100, True)
00074 elif s[-1]=="%":
00075 self.MaxSize = valid_number_str_suffix(s, "50%", "%", "--maxsize",
00076 0, 100, True)
00077 elif s.endswith("bpp"):
00078 self.MaxSize = valid_number_str_suffix(s, "50%", "bpp", "--maxsize"
00079 , 0, 1024, True)
00080 elif s.endswith("kiB") or s.endswith("MiB") or s.endswith("GiB"):
00081 self.MaxSize = valid_number_str_suffix(s, "50%", s[-3 :],
00082 "--maxsize", 0, 1048576,
00083 True)
00084 else:
00085 self.MaxSize = valid_number_str(s, "50%", "--maxsize", 0,
00086 1073741824, True)
00087 def abs_min_quality(self):
00088 """Returns minimum quality in %.
00089
00090 Parses stored minimum quality data and returns it's equivalent in %.
00091 """
00092 if self.MinQuality[-1] == "%":
00093 return float(self.MinQuality[:-1])
00094 else:
00095
00096 return 100.0 * sqrt(sqrt(sqrt(sqrt(sqrt(float(self.MinQuality)/100)))))
00097
00098 def abs_max_size(self, reffile):
00099 """Returns maximum size in bytes.
00100
00101 Parses stored maximum size data and returns it's equivalent in bytes.
00102
00103 Arguments:
00104 reffile : Reference file used for relative max size options (i.e. in %)
00105 """
00106 if self.MaxSize.endswith("kiB"):
00107 return int(float(self.MaxSize[:-3]) * 1024)
00108 elif self.MaxSize.endswith("MiB"):
00109 return int(float(self.MaxSize[:-3]) * 1024 * 1024)
00110 elif self.MaxSize.endswith("GiB"):
00111 return int(float(self.MaxSize[:-3]) * 1024 * 1024 * 1024)
00112 elif self.MaxSize.endswith("r%"):
00113 return int(float(self.MaxSize[:-2]) / 100.0 * reffile.Size)
00114 elif self.MaxSize[-1]=="%":
00115 return int(float(self.MaxSize[:-1]) / 100.0 * reffile.InputSize)
00116 elif self.MaxSize.endswith("bpp"):
00117 return int(reffile.Width * reffile.Height *
00118 float(self.MaxSize[:-3]) / 8.0)
00119 else:
00120 return int(float(self.MaxSize))
00121
00122 def compare_files_size(self, minqual, bestsize, file, bestfile):
00123 """Compares size of file and bestfile. Returns True if file is better.
00124
00125 If files have same filesize, file with better quality is determined
00126 to be better. If file is better than bestfile, bestfile is deleted.
00127 """
00128 fsize = file.Size
00129 fqual = file.Quality
00130 bestqual = minqual
00131 if bestfile is not None:
00132 bestqual = bestfile.Quality
00133
00134 if fsize < bestsize and fqual >= minqual:
00135 if bestfile is not None:
00136 subprocess.call("rm '" + bestfile.FileName + "'", shell = True)
00137 return True
00138
00139 elif fsize == bestsize and fqual > bestqual:
00140 if bestfile is not None:
00141 subprocess.call("rm '" + bestfile.FileName + "'", shell = True)
00142 return True
00143
00144 else:
00145 subprocess.call("rm '" + file.FileName + "'", shell = True)
00146 return False
00147
00148 def compare_files_quality(self, bestqual, maxsize, file, bestfile):
00149 """Compares quality of file and bestfile.Returns True if file is better.
00150
00151 If files have same quality, file with better filesize is determined
00152 to be better. If file is better than bestfile, bestfile is deleted.
00153 """
00154 fsize = file.Size
00155 fqual = file.Quality
00156 bestsize = maxsize
00157 if bestfile is not None:
00158 bestsize = bestfile.Size
00159
00160 if fqual > bestqual and fsize <= maxsize:
00161 if bestfile is not None:
00162 subprocess.call("rm '" + bestfile.FileName + "'", shell = True)
00163 return True
00164
00165 elif fqual == bestqual and fsize < bestsize:
00166 if bestfile is not None:
00167 subprocess.call("rm '" + bestfile.FileName + "'", shell = True)
00168 return True
00169
00170 else:
00171 subprocess.call("rm '" + file.FileName + "'", shell = True)
00172 return False
00173
00174 def select_best_file(self, filelist, reffile, outfilename):
00175 """Selects which of generated files should be output, deletes others.
00176
00177 Arguments:
00178 filelist : List of generated temporary files to select from.
00179 reffile : Data about the current reference file to compare to.
00180 outfilename : Filename the output will have.
00181 """
00182 output("selecting the best file")
00183 self.FileList = filelist
00184 bestfile = None
00185 if self.Mode == "q":
00186 bestsize = reffile.Size
00187 minquality = self.abs_min_quality()
00188 for f in self.FileList:
00189 if self.compare_files_size(minquality, bestsize, f, bestfile):
00190 bestfile = f
00191 bestsize = f.Size
00192 elif self.Mode == "s":
00193 bestquality = 0.0
00194 maxsize = self.abs_max_size(reffile)
00195 for f in self.FileList:
00196 if self.compare_files_quality(bestquality, maxsize, f, bestfile):
00197 bestfile = f
00198 bestquality = f.Quality
00199 if bestfile is not None:
00200 try:
00201 move(bestfile.FileName, outfilename + "." + bestfile.FileType)
00202 except IOError:
00203 warning("Could not output file. Maybe the user " +
00204 "does not have required permissions?")
00205 else:
00206 if reffile.Size < reffile.InputSize:
00207 warning("File " + reffile.InputName + " could not be " +
00208 "optimized according to input. Outputting reference " +
00209 "file instead.")
00210 try:
00211 copyfile(reffile.Name, outfilename + ".png")
00212 except IOError:
00213 warning("Could not output file. Maybe user " +
00214 "does not have required permissions?")
00215 else:
00216 warning("File " + reffile.InputName + " could not be "
00217 "optimized according to input. Outputting input "
00218 "file instead.")
00219 out = outfilename + \
00220 reffile.InputName[reffile.InputName.rfind("."):]
00221 try:
00222 copyfile(reffile.InputName, out)
00223 except IOError:
00224 warning("Could not output file. Maybe user " +
00225 "does not have required permissions?")