adaptive_median.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
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     ## set filter window and image dimensions
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     ## create 2-D image array and initialize window
00064     image_array = reshape(array(image, type=UInt8), (ylength,xlength))
00065     filter_window = zeros((W,W))
00066     pixel_count = 0
00067 
00068     try:
00069         ## loop over image with specified window W
00070         for y in range(window, ylength-(window+1)):
00071             for x in range(window, xlength-(window+1)):
00072             ## populate window, sort, find median
00073                 filter_window = image_array[y-window:y+window+1,x-window:x+window+1]
00074                 target_vector = reshape(filter_window, ((vlength),))
00075                 ## internal sort
00076                 median = demo(target_vector, vlength)
00077                 ##median = medians_1D.quick_select(target_vector, vlength)
00078                 ## check for threshold
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         ## ,NameError,ArithmeticError,LookupError
00096 
00097     print pixel_count, "pixel(s) filtered out of", xlength*ylength
00098     ## convert array back to sequence and return
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     ## Do the right thing with boolean values for all known Python versions.
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     # Obligatory spam variable; controls verbosity of the output
00130     spam = False
00131     
00132     # window = ws, where the filter window W = 2*ws + 1, 
00133     # ie, ws = 1 is a 3x3 window (W=3)
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         ## Convert the PIL image object to a sequence (list)
00214         input_sequence = list(pil_image.getdata())
00215 
00216         try:
00217             timing.start()
00218             ## filter input image sequence
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             ## init output image
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 

Generated on Fri Dec 8 18:11:25 2006 for Median Filtering Routines by  doxygen 1.5.1