00001
00002 """Module doctsring
00003
00004 Routine Name: adaptive_median.py
00005
00006 Desc: This routine is run from the command line with one or more arguments
00007 and one or more input image filenames. Optional arguments include
00008 Help, Verbose, and the filter parameters Window and Threshold.
00009
00010 Window is specified as the window size (ws) where the width of the
00011 square window (W) equals 2*ws + 1 and the range is 1..5.
00012
00013 Threshold is defined as t*S (where S is the adaptive filter parameter)
00014 such that t = 0 is the most aggresive (a standard median filter) and
00015 higher values of 't' will reduce the probability of pixel replacement.
00016 This effectively filters out the more outlying pixels.
00017
00018 Requires Python Imaging Library (PIL) of recent vintage, and numarray.
00019
00020 Arguments:
00021
00022 Name I/O Description
00023 ---- --- -----------
00024 -h|--help N/A Prints module docstring
00025 -v|--verbose N/A Prints extra more verbose messages
00026 -w|--window N/A Sets the filter window size (must be a scalar
00027 between 1 and 5). Window size (ws) is defined as
00028 W = 2*ws + 1 so that W = 3 is a 3x3 filter window.
00029 -t|--threshold N/A Sets the adaptive threshold (0=normal median
00030 behavior). Higher values reduce the "aggresiveness"
00031 of the filter.
00032
00033 filename(s) In One or more gray-scale image files
00034
00035 usage:
00036
00037 adaptive_median.py [-hvwt|--help --verbose --window=[1..5] --threshold=[N]] <filename> [<filename>...]
00038
00039 Revision History:
00040 Date Name Description
00041 ---- ---- -----------
00042 08-28-2005 S.L. Arnold Initial implementation with internal (numarray) sort.
00043 09-18-2005 S.L. Arnold Added timing routine and prepped for calling SWIG-
00044 wrapped functions.
00045 """
00046
00047
00048 import sys
00049 import medians_1D
00050 from numarray import *
00051
00052 def process(image, size, window, threshold, spam):
00053
00054
00055 W = 2*window + 1
00056 xlength, ylength = size
00057 vlength = W*W
00058 if spam:
00059 print "Image length in X direction:", xlength
00060 print "Image length in Y direction:", ylength
00061 print "Filter window size:", W, "x", W
00062
00063
00064 image_array = reshape(array(image, type=UInt8), (ylength,xlength))
00065 filter_window = zeros((W,W))
00066 pixel_count = 0
00067
00068 try:
00069
00070 for y in range(window, ylength-(window+1)):
00071 for x in range(window, xlength-(window+1)):
00072
00073 filter_window = image_array[y-window:y+window+1,x-window:x+window+1]
00074 target_vector = reshape(filter_window, ((vlength),))
00075
00076 median = demo(target_vector, vlength)
00077
00078
00079 if not threshold > 0:
00080 image_array[y,x] = median
00081 pixel_count += 1
00082 else:
00083 scale = zeros(vlength)
00084 for n in range(vlength):
00085 scale[n] = abs(target_vector[n] - median)
00086 scale = sort(scale)
00087 Sk = 1.4826 * (scale[vlength/2])
00088 if abs(image_array[y,x] - median) > (threshold * Sk):
00089 image_array[y,x] = median
00090 pixel_count += 1
00091
00092 except (TypeError), err:
00093 print "Error in processing function:", err
00094 sys.exit(2)
00095
00096
00097 print pixel_count, "pixel(s) filtered out of", xlength*ylength
00098
00099 return reshape(image_array, (xlength*ylength,)).tolist()
00100
00101 def demo(target_array, array_length):
00102
00103 sorted = sort(target_array)
00104 median = sorted[array_length/2]
00105 return median
00106
00107 def main(argv):
00108
00109 import os, getopt, sys, Image
00110 import timing
00111
00112 global filename
00113
00114
00115 try:
00116 True, False
00117 except NameError:
00118 (True, False) = (1, 0)
00119
00120 try:
00121 args, filenames = getopt.getopt(argv[1:], "hvwt", ["help", "verbose", "window=", "threshold="])
00122 except getopt.error, msg:
00123 args = "dummy"
00124 print msg
00125 print "Usage: %s [-h|v|--window=[1..5]|--threshold=[0..N]] <filename>" % (argv[0],)
00126 print "Demonstrates adaptive median filtering on gray-scale images."
00127 sys.exit(2)
00128
00129
00130 spam = False
00131
00132
00133
00134 window = 1
00135 threshold = 0.
00136
00137 for o, a in args:
00138 if o in ("-h", "--help"):
00139 print __doc__
00140 sys.exit(0)
00141 if o in ("-v", "--verbose"):
00142 spam = True
00143 if spam:
00144 print "options =", args
00145 print "filenames =", filenames
00146
00147 try:
00148 for o in args[:]:
00149 if o[0] == '--threshold' and o[1] != '':
00150 threshold = float(o[1])
00151 args.remove(o)
00152 break
00153 elif o[0] == '--threshold' and o[1] == '':
00154 print "The --threshold option requires an argument."
00155 sys.exit(2)
00156 for o in args[:]:
00157 if o[0] == '--window' and o[1] != '':
00158 window = int(o[1])
00159 args.remove(o)
00160 break
00161 elif o[0] == '--window' and o[1] == '':
00162 print "The --window option requires an argument."
00163 sys.exit(2)
00164 except ValueError:
00165 print "Incompatible parameter", o[1],".", " Option must be a number."
00166 sys.stderr.write
00167 sys.exit(2)
00168 except TypeError, err:
00169 print "Parameter error:", err
00170 sys.exit(2)
00171
00172 if threshold < 0.:
00173 print "The threshold must be a non-negative real value (default=0)."
00174 sys.exit(2)
00175
00176 if not (1 <= window <= 5):
00177 print "The window size must be an integer between 1 and 5 (default=1)."
00178 sys.exit(2)
00179
00180 if not filenames:
00181 print "Please specify one or more gray-scale input files."
00182
00183 if spam:
00184 print "window =", window
00185 print "threshold =", threshold
00186
00187 image_count = 0
00188 filter_time = 0.
00189
00190 for filename in filenames:
00191 try:
00192 infile = open(filename, "rb")
00193 except IOError, err:
00194 print "Input file error:", err
00195 if spam:
00196 print "Please check the name(s) of your input file(s)."
00197 os.close(sys.stderr.fileno())
00198 sys.exit(2)
00199
00200 try:
00201 pil_image = Image.open(infile)
00202 if pil_image.mode == 'P':
00203 if spam:
00204 print "Original image mode: ",pil_image.mode
00205 pil_image = pil_image.convert("L")
00206 except IOError:
00207 print "Cannot parse input image format.", pil_image
00208 if spam:
00209 print "Input image format: ", pil_image.format
00210 print "Input image size: ", pil_image.size
00211 print "Working image mode: ",pil_image.mode
00212
00213
00214 input_sequence = list(pil_image.getdata())
00215
00216 try:
00217 timing.start()
00218
00219 output_sequence = process(input_sequence, pil_image.size, window, threshold, spam)
00220 timing.finish()
00221 filter_time = filter_time + float(timing.micro()) / 1000000
00222
00223
00224 file, ext = os.path.splitext(filename)
00225 outfile = "new_" + file + ext
00226 try:
00227 output_image = Image.new(pil_image.mode, pil_image.size, None)
00228 output_image.putdata(output_sequence)
00229 output_image.save(outfile, pil_image.format)
00230 if spam:
00231 print "Output image name: ", outfile
00232
00233 except IOError, err:
00234 print "Output file error:", err
00235 if spam:
00236 print "Cannot create output image for ", input_image, "."
00237 print " Continuing with next available file..."
00238 continue
00239
00240 except MemoryError, err:
00241 sys.stderr.write(err)
00242 if spam:
00243 print "Not enough memory to create output image for ", input_image, "."
00244 print " Continuing with next available file..."
00245 continue
00246
00247 infile.close()
00248 image_count += 1
00249
00250 print image_count, "image(s) filtered in", filter_time, "seconds."
00251
00252 if __name__ == "__main__":
00253 main(sys.argv)
00254