-
Notifications
You must be signed in to change notification settings - Fork 2
/
ICP_original.py
2151 lines (1838 loc) · 105 KB
/
ICP_original.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
### Modification in the original ICP script to save the data, model & superimposed co-ordinates as well as the rotation & translation matrix ###
__version__ = '2.1.1'
__author__ = "Avinash Kak (kak@purdue.edu)"
__date__ = '2017-November-25'
__url__ = 'https://engineering.purdue.edu/kak/distICP/ICP-2.1.1.html'
__copyright__ = "(C) 2017 Avinash Kak. Python Software Foundation."
__doc__ = '''
ICP.py
Version: ''' + __version__ + '''
Author: Avinash Kak (kak@purdue.edu)
Date: ''' + __date__ + '''
@title
CHANGE LOG:
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.
@title
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
@title
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.
@title
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.
@title
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.
@title
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.
@title
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.
@title
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.
@title
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.
@title
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
@title
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()
@title
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.
@title
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.
@title
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.
@title
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.
@title
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.
@title
BUGS:
Please notify the author if you encounter any bugs. When sending
email, please place the string 'ICP' in the subject line.
@title
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.
@title
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
'''
from PIL import Image
from PIL import ImageFilter