ICP (version 2.1.1, 2017-November-25)

ICP.py
 
Version: 2.1.1
   
Author: Avinash Kak (kak@purdue.edu)
 
Date: 2017-November-25
 
 

Download Version 2.1.1:  gztar  


 
     Total number of downloads (all versions): 2472
     This count is automatically updated at every rotation of
     the weblogs (normally once every two to four days)
     Last updated: Thu Apr 18 06:11:01 EDT 2024
View the main module code file in your browser  

View the ICPImageScanner code in your browser  
 
 
 
CHANGES:

 
  Version 2.1.1:
 
    This version fixes a bug in the newly introduced scanning mode for
    applying the ICP algorithm to two large images.  This bug made itself
    evident if you chopped your large images into non-square arrays of
    subimages. I had the row and column indexing reversed in the movie
    making part of the code for demonstrating the ICP results.
 
  Version 2.1.0: 
 
    This is a significant upgrade of the ICP module: (1) The module now
    comes with a new class named ICPImageScanner for dealing with the case
    when different portions of the two images, one for the model and the
    other for the data, are related to each other with different values for
    the translational and rotational offsets.  This can happen when large
    images are recorded by aggregating the data from sensors in motion (as
    is the case with earth imaging satellites that use pushbroom cameras
    and, in some cases, with the cameras mounted in UAVs). (2) The module
    now uses a more commonly available font file for the labels shown in
    the results. The default font is now FreeSerif.ttf from the Freefont
    family.  This version also gives you a constructor option to specify
    your own font file.  And, finally, (3) this version includes a bug fix
    in the calls to the putpixel() function.  This bugfix was needed in
    order for the module to work with the more recent Pillow library for
    PIL.
 
  Version 2.0:
 
    This is a Python 3.x compliant version of the module.  You should now
    be able to execute the module code with either Python 2.7 or Python 3.
 
  Version 1.3:
 
    This version is a major rewrite of the ICP module. While the previous
    versions of this module were useful primarily for binary images, the
    new version should also work well for grayscale and color images.  The
    new module also contains improvements to the implementation code for
    the core ICP algorithm.  It should be more forgiving should there exist
    no correspondents in one image for some of the pixels chosen for ICP
    calculations in the other image.  Finally, this version gives you two
    options for applying ICP to grayscale and color images: You can carry
    out either edge-based ICP or corner-pixels based ICP.
 
  Version 1.2:
 
    This version allows for a movie-like display of the superimposed model
    and data images.  This makes it much easier to see the convergence of
    the registration between the two images.  Another change in Version 1.2
    is a size reduction of large binary images to speed up the
    computations.  The size to which all images are now reduced is set by
    the 'calculation_image_size' parameter of the constructor.  Previously,
    only the color and the grayscale images were subject to such size
    reduction.  Version 1.2 also removes a bug in the mosaicked display of
    the results.
 
  Version 1.1:
 
    This version includes a new option for the constructor that lets the
    system decide as to which image to use as the model and which image as
    the data.  In general, for color and grayscale images, you get superior
    registration between the two images if the image that results in fewer
    pixels for ICP processing is used as the data image.  Version 1.1 also
    includes a better (although still extremely primitive) data generator
    for creating simple synthetic binary images that can be used to
    experiment with the ICP algorithm.
 
 
INSTALLATION:
                                                                                                   
    The ICP class was packaged using setuptools.  For installation, execute
    the following command in the source directory (this is the directory
    that contains the setup.py file after you have downloaded and
    uncompressed the package):
                                                                                                   
            sudo python setup.py install                                                           
                                                                                                   
    and/or, for the case of Python 3,                                                               
                                                                                                   
            sudo python3 setup.py install                                                          
                                                                                                   
    On Linux distributions, this will install the module file at a location                        
    that looks like                                                                                
                                                                                                   
             /usr/local/lib/python2.7/dist-packages/                                               
                                                                                                   
    and, for the case of Python 3, at a location that looks like                                    
                                                                                                   
             /usr/local/lib/python3.5/dist-packages/
 
    If you do not have root access, you have the option of working directly
    off the directory in which you downloaded the software by simply
    placing the following statements at the top of your scripts that use
    the ICP class:
 
        import sys
        sys.path.append( "pathname_to_ICP_directory" )
 
    To uninstall the module, simply delete the source directory, locate
    where ICP was installed with "locate ICP" and delete those files.  As
    mentioned above, the full pathname to the installed version is likely
    to look like /usr/local/lib/python2.7/dist-packages/ICP*
 
    If you want to carry out a non-standard install of the ICP module,
    look up the on-line information on Disutils by pointing your
    browser to
 
          http://docs.python.org/dist/dist.html
 
 
INTRODUCTION:
 
    ICP stands for the Iterative Closest Point algorithm. ICP algorithms
    are used to align two datasets in a multi-dimensional space by
    iteratively applying rotations and translations to one dataset until it
    is aligned with the other dataset.
 
    In image processing and computer vision, ICP can be used to align a
    data image recorded through a sensor with a model image that is
    produced by a geographic information system (GIS).  A typical
    application would be a UAV recording images as it flies over a terrain.
    A successful alignment between such sensor produced images and the 
    model images produced by an on-board or satellite-connected GIS system
    would enable precise computation of the position and the orientation 
    of the UAV vis-a-vis the terrain.
 
    The main goal of the pure-Python implementation of ICP presented here
    is to make it easier to experiment with the different aspects of such
    algorithms.
 
 
USAGE:
 
    You have two modes for applying the ICP algorithm to grayscale and color
    images: You can carry out either edge-based ICP or corner-pixels based
    ICP.  For edge-based ICP, a typical usage example would look like
 
        import ICP
        icp = ICP.ICP(
               binary_or_color = "color",
               corners_or_edges = "edges",
               auto_select_model_and_data = 1,
               calculation_image_size = 200,
               max_num_of_pixels_used_for_icp = 300,
               pixel_correspondence_dist_threshold = 20,
               iterations = 24,
               model_image =  "SydneyOpera.jpg",
               data_image = "SydneyOpera2.jpg",
               font_file = "/usr/share/fonts/truetype/freefont/FreeSerif.ttf",
             )
        icp.extract_pixels_from_color_image("model")
        icp.extract_pixels_from_color_image("data")
        icp.icp()
        icp.display_images_used_for_edge_based_icp()
        icp.display_results_as_movie()
        icp.cleanup_directory()
 
    On the other hand, for corner-pixels based ICP, your usage of this
    module is likely to be:
 
        import ICP
        icp = ICP.ICP(
               binary_or_color = "color",
               corners_or_edges = "corners",
               calculation_image_size = 200,
               image_polarity = -1,
               smoothing_low_medium_or_high = "medium",
               corner_detection_threshold = 0.2,
               pixel_correspondence_dist_threshold = 40,
               auto_select_model_and_data = 1,
               max_num_of_pixels_used_for_icp = 100,
               iterations = 16,
               model_image =  "textured.jpg",
               data_image = "textured2.jpg",
               font_file = "/usr/share/fonts/truetype/freefont/FreeSerif.ttf",
            )
        icp.extract_pixels_from_color_image("model")
        icp.extract_pixels_from_color_image("data")
        icp.icp()
        icp.display_images_used_for_corner_based_icp()
        icp.display_results_as_movie()
        icp.cleanup_directory()
 
    When applying this ICP module to binary images, your usage is likely to
    be:
 
        import ICP
        icp = ICP.ICP(
               binary_or_color = "binary",
               pixel_correspondence_dist_threshold = 40,
               auto_select_model_and_data = 1,
               calculation_image_size = 200,
               iterations = 16,
               model_image = "triangle1.jpg",
               data_image = "triangle2.jpg",
               font_file = "/usr/share/fonts/truetype/freefont/FreeSerif.ttf",
            )
        icp.extract_pixels_from_binary_image("model")
        icp.extract_pixels_from_binary_image("data")
        icp.icp()
        icp.display_images_used_for_binary_image_icp()
        icp.display_results_as_movie()
        icp.cleanup_directory()
 
 
    In the calls shown above, the parameter calculation_image_size controls
    the size of the image that will actually be used for ICP calculations.
    Color (and grayscale) images as output by sensors can be very large and
    it would be impractical to process all of the pixel data in the images.
    This module first smoothly reduces the size of the images so that the
    maximum dimension does not exceed calculation_image_size and then
    carries out ICP processing on the reduced-size images.  The
    pixel_correspondence_dist_threshold parameter controls how wide the net
    will be cast, so to speak, in seeking a model image correspondent for a
    data image pixel.  You will generally get better results if you choose
    the image with a larger number of candidate pixels for ICP calculations
    as the model image. The parameter auto_select_model_and_data, when set
    to 1, lets the module decide as to which image to use for the model and
    which to use as data.
 
    The other constructor parameters shown in the calls shown above are
    explained further down on this documentation page.
 
    The module also includes a static method gendata() to illustrate
    how one can create simple synthetic images to experiment with this
    ICP module.  A call to this method looks like
 
        import ICP
        ICP.ICP.gendata( "triangle", (80,80), (10,10), 30, "newtriangle2.jpg" )
 
    As currently programmed, the gendata() method constructs images with 
    triangles and straight lines segments.
 
 
CONSTRUCTOR PARAMETERS:
 
    auto_select_model_and_data: Must be set to 0 or 1. When set to 1, the
                         system decides as to which of the two images you
                         supplied to the constructor will actually be used
                         as model and which as data. You generally get
                         better results if the image that yields a larger
                         number of pixels for ICP calculations is used as a
                         model. (DEFAULTS TO 0)
 
    binary_or_color:     Must be set to 'binary' for binary images and to
                         'color' for grayscale and color images. (REQUIRED)
 
    calculation_image_size:   The size to which a large image will be reduced
                         for ICP processing.  (DEFAULTS TO 200)
 
    corner_detection_threshold: When corner pixels are needed for ICP
                         calculations, the module uses the Harris Corner
                         Detector.  To detect a corner, we sum the squares
                         of the x-derivatives, the squares of the
                         y-derivatives, and the product of the two in a 5x5
                         window. We find the trace and the determinant of
                         the 2x2 matrix formed in this manner. This
                         parameter is a threshold for testing the ratio of
                         the determinant to the square of the trace.
                         (DEFAULTS TO 0.2)
 
    corners_or_edges:    To be used only when binary_or_color is set to 'color'.
                         It must be set to either 'edges' or 'corners'. 
                         (DEFAULTS TO 'edges')
 
    data_image:          The name of the data image file    (REQUIRED)
 
    image_polarity:      When the corners_or_edges parameter is set to 'corners',
                         you must specify the image polarity.  The polarity is
                         1 if the object pixels are generally brighter than the
                         background pixels.  Otherwise, it is -1.  (REQUIRED
                         when corners_or_edges is set to "corners")
 
    iterations:          The maximum number of iterations to try (DEFAULTS
                         TO 24)
 
    max_num_of_pixels_used_for_icp: Although, in general, as the number of
                         pixels you use for ICP goes up, the quality of the
                         registration improves on account of the averaging
                         effect created by the pixels.  But this works only
                         up to a point, beyond which you only increase the
                         time it takes for each ICP iteration without any
                         additional accuracy in registration.  This
                         parameter lets you set an upper bound on the number
                         of pixels that will be chosen for ICP calculations.
                         (DEFAULTS TO 100)
 
    model_image:         The name of the model image file   (REQUIRED)    
 
    pixel_correspondence_dist_threshold:  This parameter controls how far the 
                         the data image will be searched for a corresponding 
                         pixel for a model image pixel. (DEFAULTS TO 100).
 
    smoothing_low_medium_or_high: A useful parameter when you are applying
                         ICP to color (or grayscale) images in the "corner"
                         mode.  This parameter controls the degree of
                         smoothing that is applied to the two images to
                         segment out the object pixels from the background
                         pixels.  Its value must be either 'low', or
                         'medium', or 'high'.  (DEFAULTS TO 'medium')
 
    font_file:           For displaying labels on the results, the module 
                         uses the font file 'FreeSerif.ttf' by default. The 
                         module assumes that this file can be located through 
                         the paths stored in 'sys.path', or via the paths 
                         available through your environment variables. If 
                         you don't like the FreeSerif true-type font for 
                         some reason, starting with Version 2.1 you can 
                         specify your own font file through the "font_file" 
                         constructor option.
 
METHODS:
 
    (1) extract_pixels_from_color_image()
 
        This method extracts the pixels to use for ICP calculation for the
        case of color and grayscale images.  It chooses the most prominent
        edge pixels when called with the argument 'edges'.  And it chooses
        the most prominent corner pixels when called with the argument
        'corners'.
 
    (2) extract_pixels_from_binary_image()
 
        This method extracts the pixels to use for ICP calculations for the
        case of binary images.
 
    (3) icp() 
 
        You must call the method icp() for the basic ICP calculations.
 
    (4) display_images_used_for_edge_based_icp()
        display_images_used_for_corner_based_icp()
        display_images_used_for_binary_image_icp()
 
        Since the model and the data images are processed differently for
        the three different cases of edge-based color, corner-pixels based
        color, and the binary images, the three display methods listed above
        are customized to what needs to be shown in each case.
 
    (5) display_results_as_movie()  
 
        The different iterations of the ICP algorithm are displayed through
        a movie by calling this method.
 
    (6) cleanup_directory()
 
        The data image as transformed by the rotation and the translation at
        each iteration is stored in a file whose name begins with the
        '__result' prefix.  These files, stored in the directory in which
        you invoke this module, are used subsequently for the movie
        depiction of the registration process.  By calling this method, you
        can delete these files.
 
 
HOW THE RESULTS ARE DISPLAYED:
 
    The results are displayed using Tkinter graphics (meaning, actually, Tk
    graphics via the Tkinter interface).  To understand the results, you
    must first call one of the three display_images_used_for_xxxx() methods
    listed in item (4) under Methods above in order to see which pixels are
    being used for ICP calculations.  Subsequently, you call the method
    display_results_as_movie() to see an iteration-by-iteration movie of the
    registration between the model image and the data image.
 
 
THE EXAMPLES DIRECTORY:
 
    The best way to become familiar with this module is by executing the
    following scripts in the Examples subdirectory:
 
    1.  color_image_registration_with_edge_pixels_example1.py
 
            This script shows registration results with edge-based ICP on
            two color images of Sydney Opera House.  (The registration
            itself is carried out on the grayscale versions of the color
            images.)
 
    2.  color_image_registration_with_edge_pixels_example2.py 
 
            This script shows registration results with edge-based ICP on
            overhead photos of a highway interchange.
 
    3.  color_image_registration_with_edge_pixels_example3.py 
 
            This script shows registration results on a pair of generic
            images with square blocks.
 
    4.  color_image_registration_with_corner_pixels_example1.py
 
            This script shows registration results with corner-pixels based
            ICP on two photos of my wife's earrings.  The model photo is
            from the www.etsy.com website where these earrings are sold.  To
            the best of what I can tell, there are no copyright issues
            related to the use of this photo here.
 
    5.  binary_image_registration_example1.py
 
            This is an example of registering two binary images of a
            triangular shape.
 
    6.  binary_image_registration_example2.py
 
            This is another example of binary image registration that only
            involves a single straight line in the images.
 
    It is highly recommended that you play with these example scripts before
    using the module on your own images.
 
 
SHOULD YOU CHOOSE THE 'edges' MODE OR THE 'corners' MODE FOR COLOR
AND GRAYSCALE IMAGES:
 
    That obviously depends on what your images look like.  For example, the
    Sydney Opera House images used in the script
    color_image_registration_with_edge_pixels_example1.py have strong edges
    and the 'edges' mode works fine for this case.  On the other hand, the
    edges in the earrings images used in the script
    color_image_registration_with_corner_pixels_example1.py are not so
    strong.  The objects in these images are more textured than anything
    else and the 'corners' mode works well in this case.  In general, you
    can expect the 'corners' mode to work well when the images have
    relatively confined objects with textured surfaces.
 
 
THEORETICAL BASIS:   
 
    The first ICP algorithm was proposed by Paul Besl and Neil McKay in
    a now celebrated paper that appeared in 1992 in IEEE Transactions
    on PAMI.  Since then various versions of the algorithm have been
    published by other folks for either speeding up the performance of
    the algorithm or for improving its accuracy.
 
    The algorithm implemented here is as simple as it can be.  We model
    the relationship between model and data as
 
          R x_d   +   T    =    x_m 
 
    where x_d denote the data points and x_m the model points, each a
    two-element vector.  R is a 2x2 rotation matrix and T a 2-element
    translation vector.  Since two planar figures may not be related by
    the above transformation (even when one figure "appears" to be a
    rotated version of the other figure) for an arbitrary location of
    the origin, move the origin to the mean point of the model by
 
          x_m   =   x_m  -   mean( x_m )
                                                       
          x_d   =   x_d  -   mean( x_m )
 
    With regard to the calculation of R and T, let's express the data
    points and the CORRESPONDING model points as
 
         [x_d1, x_d2, ....., x_dn]  
 
    and their CORRESPONDING model points
 
         [x_m1, x_m2, ....., x_mn]
 
    We can now write the following for estimating the rotation matrix:
 
         R . A   =  B
 
    where
 
         A  =  [x_d1, x_d2, ....., x_dn]  
 
         B  =  [x_m1 - T, x_m2 - T, ....., x_mn - T]
 
    Both A and B are 2xn matrices.  We can now write
 
         R . A . A^t =  B . A^t
 
    where A^t is the transpose of A.  So, we have the following as a
    least mean squares estimate for R:
 
         R    =    B . A^t . ( A . A^t )^-1
 
    Since such an R may not obey the strict properties that must apply
    to a rotation matrix (it must be orthonormal), we condition it by
    first subjecting it to a singular value decomposition:
 
        U.S.V^t  =  svd( R )
 
    and then writing the following for a better estimate for R:
 
        R    =   U . V^t
 
    We can now estimate T by
 
        T    =  mean( x_m )   -   mean( R x_d )        
 
    The above assumes that we are carrying out an one-shot calculation
    of R and T.  But ICP is iterative.  The following applies to an
    iterative implementation of the above:
 
    For an iterative assessment of R and T, let's assume that we can
    decompose R as
 
        R = \deltaR . R_0
 
    where we have previously calculated R_0 and now we wish to refine
    the estimate with the calculation of \deltaR.  Plugging the above
    in the previous formulation:
 
        \deltaR . R_0 . A . A^t =  B . A^t
 
    implying
 
        \deltaR . R_0   =  B . A^t . ( A . A^t )^-1
 
    which is to say:
 
        \deltaR   =  B . A^t . ( A . A^t )^-1 .  R_0^t
 
    After you have calculated \deltaR, you can update the rotation
    matrix by
 
        R = \deltaR . R_0
 
    At the end of each such update of the rotation matrix, the
    calculation of the translation vector remains the same as before
 
        T    =  mean( x_m )   -   mean( R .  x_d )               
 
    We can therefore write down the following steps for ICP
    computation:
 
    STEP 1:
               x_m   =   x_m  -  mean(x_m)
               x_d   =   x_d  -  mean(x_m)
 
 
    STEP 2:
               Initialize R and T:
 
               R  =    1  0
                       0  1
 
               T  =    0
                       0
 
               old_error = inf
 
 
    STEP 3:     error = (1/N) \sum dist( x_m - (R * x_d + T) )
 
 
    STEP 4:     diff_error = abs(old_error - error)
                if (diff_error > threshold):
                    old_error = error
                else:
                    break
 
    STEP 5:    for each x_d find its closest x_m by finding that x_m which
               minimizes the squared difference between the two sides
               of
 
                      R . x_d  +   T  =   x_m
 
    STEP 6:     A  =  [x_d1, x_d2, ....., x_dn]  
                B  =  [x_m1 - T, x_m2 - T, ....., x_mn - T]
 
                Note that A will remain the same for ICP iterations,
                but B can change with each iteration depending on the
                what corresponding pixels are found for each data
                pixel.
 
    STEP 7:     AATI =  A^t inverse(A * A^t)
 
    STEP 8:     R_update =  B * AATI * R.T
 
    STEP 9:     U,S,VT =  svd(R_update)
                deter =   determinant(U * VT)
                U[0,1] = U[0,1] * determinant
                U[1,1] = U[1,1] * determinant
 
                R_update = U * VT
 
    STEP 10:    R =  R_update * R
 
    STEP 11:    T  =  mean(x_m) - mean( R * x_d )
 
    STEP 12:    Back to Step 3
 
 
THE ICPImageScanner CLASS:
 
    Starting with Version 2.1.0, you can first chop large model and data
    images into subimages and then apply the ICP algorithm separately to
    each corresponding pair of subimages. When large images are recorded by
    aggregating the data from sensors in motion (as is the case with earth
    imaging satellites that use pushbroom cameras and, in some cases, with
    the cameras mounted in UAVs), the different portions of a recorded
    image may not be characterizable with the same translational and
    rotational offsets vis-a-vis a model image extracted for a GIS
    database.
 
    The ICPImageScanner class is programmed as a subclass of the main ICP
    class.  Therefore, it inherits all of the functionality of the parent
    ICP class.
 
    Here is how you'd class the constructor of the ICPImageScanner class if
    you want to chop the original model and data images into a collection
    of non-overlapping subimages:
 
            model_image_file = "my_large_model_image.jpg"
            data_image_file = "my_large_data_image.jpg"
            scanner = ICPImageScanner.ICPImageScanner(
                           model_image_file = model_image_file,
                           data_image_file = data_image_file,
                           calculation_image_size = 200,
                           max_num_of_pixels_used_for_icp = 300, 
                           binary_or_color = "color",
                           corners_or_edges = "edges",
                           scanning_window_width = 250,
                           scanning_window_height = 225,
                      )
            scanner.chop_model_and_data_images_into_tiles_interactive()
    
    The code shown above will deposit the subimages into two scanner dump
    directories, one for the model and the other for the data, whose names
    are keyed to the names of the image files.  Subsequently, you can test
    the application of the ICP algorithm to just one pair of corresponding
    subimages through the following constructor call and the invocation
    shown below that:
 
            model_image_file = "my_large_model_image.jpg"
            data_image_file = "my_large_data_image.jpg"
            subimage_index = 0
            scanner = ICPImageScanner.ICPImageScanner(
                           model_image_file = model_image_file,
                           data_image_file = data_image_file,
                           subimage_index = subimage_index,
                           binary_or_color = "color",
                           corners_or_edges = "edges",
                           calculation_image_size = 200,
                           max_num_of_pixels_used_for_icp = 300,
                           pixel_correspondence_dist_threshold = 20,
                           iterations = 24,
                           scanning_window_width = 250,
                           scanning_window_height = 225,
                   )
            scanner.calculate_icp_for_one_pair_of_subimages_and_display_results()
            
    Note the additional parameter subimage_index in the call to the
    constructor of the ICPImageScanner class. This parameter identifies the
    subimages to use from the model-image scanner dump and the data-image
    scanner dump A call such as the one depicted above outputs the final
    results of ICP registration through a "movie" that shows the data
    subimage being incrementally rotated and translated as it is registered
    with the model subimage.
 
    After you have tested image scanning with the call shown above, you can
    make the following constructor call and the two invocations shown below
    that for registering all of the corresponding pairs of subimages in the
    model and data scanner dump directories:
 
            model_image_file = "my_large_model_image.jpg"
            data_image_file = "my_large_data_image.jpg"
            scanner = ICPImageScanner.ICPImageScanner(
                           model_image_file = model_image_file,
                           data_image_file = data_image_file,
                           binary_or_color = "color",
                           corners_or_edges = "edges",
                           calculation_image_size = 200,
                           max_num_of_pixels_used_for_icp = 300,
                           pixel_correspondence_dist_threshold = 20,
                           iterations = 24,
                           scanning_window_width = 250,
                           scanning_window_height = 225,
                      )
            scanner.apply_icp_to_model_and_data_scanner_dumps_fast()
            scanner.display_results_for_all_subimage_pairs_together_as_a_movie_with_colorization()
 
    Note the substring "_fast" in the name of the method called in the
    first of the two invocations shown. This method is fast because it does
    NOT show separately the ICP registration for each pair of corresponding
    subimages. On the other hand, the final result that you see is through
    the second invocation shown above, which displays all of the subimage
    based ICP registrations in the form of a composite movie.  If you do
    want to see the ICP registrations for each of the subimage pairs
    individually, you would need to replace the two invocations on the
    scanner object shown above with:
 
            scanner.apply_icp_to_model_and_data_scanner_dumps_and_show_intermediate_results()
            scanner.display_results_for_all_subimage_pairs_together_as_a_movie()
 
 
CONSTRUCTOR PARAMETERS OF THE ICPImageScanner CLASS:
 
    model_image_file: This is the name of the original image to be used as
                    the model image for ICP registration.  IMPORTANT NOTE:
                    Even when you are doing ICP registration on just a
                    subimage pair, the value of this parameter must be the
                    name of the original model image from which the
                    subimage was extracted.
 
    data_image_file: This is the name of the data image to be used for ICP
                    registration. IMPORTANT NOTE: As with the previous
                    parameter, the value of this parameter must be the
                    original data image even when you are doing ICP
                    registration on just a pair of subimages extracted from
                    the original model and the data images.
 
    binary_or_color: For now you must set it to 'color' for grayscale and
                    color images. (I have not yet allowed for 'binary" in
                    the ICPImageScanner class.  Will add that functionality
                    in a later version.)
 
    corners_or_edges: For now you must set it to 'edges'.  I have not yet
                    added the 'corners' mode to the ICPImageScanner class.
 
    calculation_image_size: This is the size to which the subimages will be
                    reduced if it turns out that they are larger than the
                    value of this parameter.
 
    max_num_of_pixels_used_for_icp: The value of this parameter is passed
                    to the parent instance of the ICP class.  See the
                    documentation on this parameter as presented earlier in
                    the context of the ICP class.
 
    pixel_correspondence_dist_threshold: The value of this parameter is
                    passed to the parent instance of the ICP class.  See
                    the documentation on this parameter as presented
                    earlier in the context of the ICP class.
 
    iterations: The maximum number of iterations to try for ICP based
                    registration.
 
    scanning_window_width: As should be obvious by its name, this parameter
                    specifies the width of the scanning window for chopping
                    a large image into subimages.
 
    scanning_window_height: As with the previous entry, this parameter
                    specifies the height of the scanning window for
                    chopping a large image into subimages.
 
            
PUBLIC METHODS OF THE ICPImageScanner CLASS:
 
    (1) apply_icp_to_model_and_data_scanner_dumps_and_show_intermediate_results()
 
        The purpose of this method is to apply the ICP algorithm to ALL the
        subimage pairs extracted from the supplied model and data images.
        The scanning function (see the fifth method listed below) in the
        ICPImageScanner class creates two dump directories containing the
        subimages, one for the model image and the other for the data
        image.  See the script
        "ICPforScannerDump_show_intermediate_results.py" in the
        ExamplesICPImageScanner directory that illustrates how you can
        invoke this method.
 
 
    (2) apply_icp_to_model_and_data_scanner_dumps_fast():
 
        This is a faster version of the previous method.  It is faster
        because it does not show ICP registration results separately for
        each pair of subimages.  See the script
        "ICPforScannerDump_no_intermediate_results.py" in the
        ExamplesICPImageScanner directory that illustrates how you can use
        this method.
 
 
    (3) calculate_icp_for_one_pair_of_subimages_and_display_results()
 
        If you want to carry out ICP registration only for a specific pair
        of subimages, then this is the method to invoke.  Note that the
        identity of the subimage pair is supplied as an integer index to
        the constructor for the ICPImageScanner class. Let us say you
        chopped up the two original images into a 3x3 array of subimages
        and you want to see the result of ICP registration on the subimage
        pair that corresponds to the upper left hand corner of the original
        images, the value of the subimage identity index would be 0 in the
        constructor call. And if you wanted to see ICP registration for the
        subimage at the lower right-hand corner, the subimage identity
        index for that would be 8 for the case of a 3x3 array of subimages.
        See the script "ICPforOneSubimagePair.py" in the
        ExamplesICPImageScanner directory for how to use this method.
 
 
    (4) calculate_icp_for_one_pair_of_subimages_fast()
 
        This is a faster version of the previous method.  It is faster
        because it does not create visual representations of ICP
        registration.  In the current code, it is called by the second
        method listed above.
 
 
    (5) chop_model_and_data_images_into_tiles_interactive():
 
        This is the method to call for chopping your model and data images
        into subimages.  The subimages are dumped in two separate
        directories, one for the model image and the other for the data
        subimage. The names of these directories is keyed to the names of
        the original images.  See the script "RunICPImageScanner.py" in the
        ExamplesICPImageScanner directory for how to invoke this method.
 
 
    (6) cleanup_scanner_examples_directory():
 
        Several of the methods listed here create directories for the
        intermediate results that are subsequently used for a "movie"
        presentation of ICP registration. This is the method to call if you
        want to get rid of those directories, as made evident by the
        example scripts in the ExamplesICPImageScanner directory.  The dump
        directories produced by the scanner are NOT touched by this cleanup
        method.
      
 
    (7) display_subimage_pair_used_for_edge_based_icp():
 
        If you are working with just one pair of corresponding subimages in
        the two scanner dump directories and you want to display the
        subimages, the edges extracted from them, and the pixels retained
        for ICP registration, this is the method you want to invoke.  In
        the current code, this functionality is called by the third method
        listed above for displaying the intermediate results on individual
        pairs of subimages.
 
 
    (8) display_results_for_all_subimage_pairs_together_as_a_movie():
 
        This method displays ICP registration for ALL the subimages in the
        form of a composite "movie".  You should invoke this method only if
        your ICP registration was carried out by the invoked
        "apply_icp_to_model_and_data_scanner_dumps_and_show_intermediate_results()".
        The reason for that is that it is the production of the
        intermediate results that colorizes the pixels needed for making
        the movies.  See the script
        "ICPforScannerDump_show_intermediate_results.py" in the
        ExamplesICPImageScanner directory for how to use this method for
        presenting the results in the form of a a movie.
 
 
    (9) display_results_for_all_subimage_pairs_together_as_a_movie_with_colorization():
 
        If you have opted for the fast (meaning no display of intermediate
        results) methods for ICP registration of the corresponding
        subimages, you need to call this method for creating a composite
        movie of the results.  This method differs from the previous movie
        making method in only one aspect: it also colorizes the pixels for
        the movie.  See the script
        "ICPforScannerDump_no_intermediate_results.py" in the
        ExamplesICPImageScanner directory for how to use this method for
        presenting the results in the form of a composite movie.
 
 
THE ExamplesICPImageScanner DIRECTORY:
 
    This directory contains the following scripts:
 
 
    (1) RunICPImageScanner.py
 
        This example script illustrates how to use the ICPImageScanner
        class in the ICP module.  This script scans two images, one for the
        model and the other for the data, in an interactive mode.  What
        that means is that each subimage is shown to the user before moving
        on to the next subimage.  The subimages extracted from the large
        model and data images are dumped in scanner dump directories whose
        names are keyed to the names of the images.
 
 
    (2) ICPforOneSubimagePair.py
 
        This script demonstrates ICP registration of one subimage extracted
        from the large model image with the corresponding subimage
        extracted from the large data image.  For this script to work, you
        have to have previously run the image scanner that chops the large
        images into subimages and dumps them in two scanner dump
        directories, one for the model image and the other for the data
        image.  The names of the dump directories are keyed to the names of
        the images.
 
 
    (3) ICPforScannerDump_show_intermediate_results.py
 
        This script applies the ICP registration algorithm to ALL the
        subimage pairs extracted from the large model and data images.
        Since this script also shows you ICP registrations separately for
        each subimage pair, it is slower than the next script. The final
        output of this script is in the form of a composite movie that
        shows ICP registrations simultaneously for ALL subimage pairs.
 
 
    (4) ICPforScannerDump_no_intermediate_results.py
 
        Like the previous script, this script applies the ICP algorithm to
        ALL of the subimage pairs extracted from the large model and data
        images.  This script should be much faster than the previous script
        listed above.  As for the previous script, the final output of this
        script is in the form of a composite movie that shows ICP
        registrations for ALL subimage pairs simultaneously.
 
 
    (5) cleanup_scanner_directory.py
 
        Ordinarily, the scripts in this directory should clean up after
        themselves.  However, should you want to kill a script midstream or
        should it abort for some reason, you can run this script to clean
        up the directory.  The dump directories produced by the scanner are
        not touched by this cleanup script.
 
 
FOR MORE ADVANCED READING ON ICP:
 
    The reader might want to look up the research publication "UAV Vision:
    Feature Based Accurate Ground Target Localization Through Propagated
    Initializations and Interframe Homographies" by Han, Aeschliman, Park,
    and Kak that appeared in the Proceedings of 2012 Conference on Robotics
    and Automation.  You can download it from
 
    https://engineering.purdue.edu/RVL/Publications/chad_avi_han_2012.pdf

 
    The ICP algorithm used in the work described in this publication was
    custom designed for that project.
 
 
CAVEATS:
 
    As to what sort of results you'll get for your images depends a great
    deal on what values you choose for the various constructor parameters
    listed earlier in this documentation.  As a case in point, if the
    parameter pixel_correspondence_dist_threshold is set to 20 for the case
    of the highway interchange images in the script
    color_image_registration_with_edge_pixels_example2.py, the ICP
    algorithm gets stuck in a local minimum.  The good result that the
    script produces is for the value 40 for this parameter.  On the other
    hand, the value of 20 for the same parameter works fine for the Sydney
    Opera House images in the script
    color_image_registration_with_edge_pixels_example1.py.  Note that the
    extent of misregistration between the two images for both scripts is
    roughly the same.
 
 
BUGS:                                                                                              
                                                                                               
    Please notify the author if you encounter any bugs.  When sending
    email, please place the string 'ICP' in the subject line.
 
 
ABOUT THE AUTHOR:  
 
    Avi Kak (kak@purdue.edu) recently completed his multi-year "Objects
    Trilogy" project.  See his web page at Purdue for what this project is
    all about.  If nothing else, you will get to enjoy Harry Potter all
    over again.
 
THANKS:
 
    Bharath Comandur found a bug in Version 2.0 that caused this module to
    not work with the more recent versions of the Pillow library for PIL.
    Bharath also supplied a fix for the problem, which was to cast to int
    the argument provided to the putpixel function.  This fix was made in
    Version 2.1 of the module.
 
@endofdocs

 
Imported Modules
       
PIL.Image
PIL.ImageChops
PIL.ImageDraw
PIL.ImageFilter
PIL.ImageFont
PIL.ImageTk
Tkinter
functools
glob
math
numpy
os
re
sys
tkFont

 
Classes
       
__builtin__.object
ICP

 
class ICP(__builtin__.object)
     Methods defined here:
__init__(self, *args, **kwargs)
callbak(self, arg)
cleanup_directory(self)
condition_data(self)
If your model and data images are such that the pixel extraction
functions yield very different number of pixels for ICP computations for
the model and the data images, you may be able to improve your results
by calling this method before you call icp().
displayImage6(self, argimage, title='')
This does the same thing as displayImage3() except that it also provides for
"save" and "exit" buttons. Note that 'argimage' must be of type Image.
display_array_as_image(self, numpy_arr)
display_images_used_for_binary_image_icp(self)
display_images_used_for_corner_based_icp(self)
display_images_used_for_edge_based_icp(self)
display_pixel_list_as_image(self, pixel_list)
display_results_as_movie(self)
extract_pixels_from_binary_image(self, model_or_data)
extract_pixels_from_color_image(self, model_or_data)
icp(self)
move_to_model_origin(self)
Since two patterns that are situated at different places in a plane may not be
related by a Euclidean transform for an arbitrary placement of the origin
even when one pattern appears to be a rotated version of the other, we will
assume that the origin for ICP calculations will be at the "center" of the
model image.  Now our goal becomes to find an R and a T that will make the
data pattern congruent with the model pattern with respect to this origin.
save_array_as_image(self, numpy_arr, label)
save_pixel_list_as_image(self, pixel_list, filename)

Static methods defined here:
gendata(feature, imagesize, position, orientation, output_image_name)
Permissible values for feature:  'line', 'triangle'
 
Permissible values for imagesize: (m,n) tuple for the size of the output image
 
Permissible values for position:  (x,y) pixel coordinates
 
Permissible values for orientation:  integer value for degrees
 
The code here is just the simplest example of synthetic data
generation for experimenting with ICP.  You can obviously 
construct more complex model and data images by calling on the
other shape drawing primitives of the ImageDraw class.  When
specifying coordinates, note the following
 
       .----------> positive x
       |
       |
       |        
       V
     positive y
 
A line is drawn from the first pair (x,y) coordinates to the
second pair.

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
p
        __author__ = 'Avinash Kak (kak@purdue.edu)'
__copyright__ = '(C) 2017 Avinash Kak. Python Software Foundation.'
__date__ = '2017-November-25'
__url__ = 'https://engineering.purdue.edu/kak/distICP/ICP-2.1.1.html'
__version__ = '2.1.1'
 
Author
        Avinash Kak (kak@purdue.edu)