11#!/usr/bin/env python
22
3+ import os
4+ import tempfile
35import unittest
46
57import numpy as np
@@ -15,118 +17,103 @@ def test_copy(self):
1517 img = Image .Read ("monalisa.png" )
1618 img_copy = img .copy ()
1719 self .assertEqual (img_copy .shape , img .shape )
18- nt .assert_array_equal (img_copy .A , img .A )
20+ nt .assert_array_equal (img_copy .array , img .array )
1921
2022 def test_write_read_roundtrip (self ):
21- """Test writing and reading back an image"""
23+ """Test writing and reading back an image (PNG is lossless) """
2224 img = Image .Read ("monalisa.png" , dtype = "uint8" )
2325
24- try :
25- # Write to file
26- import tempfile
27-
28- with tempfile .NamedTemporaryFile (suffix = ".png" , delete = False ) as f :
29- fname = f .name
30-
26+ with tempfile .TemporaryDirectory () as tmpdir :
27+ fname = os .path .join (tmpdir , "roundtrip.png" )
3128 img .write (fname )
32-
33- # Read back
3429 img_read = Image .Read (fname )
35- self .assertEqual (img_read .shape , img .shape )
3630
37- # Clean up
38- import os
39-
40- os .unlink (fname )
41- except Exception :
42- pass
31+ self .assertEqual (img_read .shape , img .shape )
32+ nt .assert_array_equal (img_read .array , img .array )
4333
4434 def test_colorspace_conversion_roundtrip (self ):
4535 """Test colorspace conversions"""
4636 img = Image .Read ("flowers1.png" )
4737
48- # RGB to HSV and back
49- try :
50- hsv = img .colorspace ("hsv" , src = "rgb" )
51- self .assertEqual (hsv .nplanes , 3 )
52- except :
53- pass
38+ # RGB to HSV
39+ hsv = img .colorspace ("hsv" , src = "rgb" )
40+ self .assertEqual (hsv .nplanes , 3 )
41+ self .assertEqual (hsv .shape [:2 ], img .shape [:2 ])
5442
5543 # RGB to lab
56- try :
57- lab = img .colorspace ("lab" )
58- self .assertEqual (lab .nplanes , 3 )
59- except :
60- pass
44+ lab = img .colorspace ("lab" )
45+ self .assertEqual (lab .nplanes , 3 )
46+ self .assertEqual (lab .shape [:2 ], img .shape [:2 ])
6147
6248 def test_cast_operations (self ):
6349 """Test image type casting"""
50+ # float to uint8
6451 img_float = Image (np .random .rand (10 , 10 ))
52+ img_uint8 = img_float .array_as ("uint8" )
53+ self .assertEqual (img_uint8 .dtype , np .uint8 )
6554
66- try :
67- # Cast to uint8
68- img_uint8 = img_float .array_as ("uint8" )
69- self .assertEqual (img_uint8 .dtype , np .uint8 )
70- except :
71- pass
72-
73- try :
74- # Cast to float
75- img_uint8 = Image (np .random .rand (10 , 10 ) * 255 , dtype = "uint8" )
76- img_float2 = img_uint8 .array_as ("float32" )
77- self .assertTrue (np .issubdtype (img_float2 .dtype , np .floating ))
78- except :
79- pass
55+ # uint8 to float
56+ img_uint8 = Image (np .random .rand (10 , 10 ) * 255 , dtype = "uint8" )
57+ img_float2 = img_uint8 .array_as ("float32" )
58+ self .assertEqual (img_float2 .dtype , np .float32 )
8059
8160 def test_matrix_conversion (self ):
8261 """Test matrix/array conversion"""
8362 img = Image (np .random .rand (10 , 10 , 3 ))
8463
8564 # To array
86- arr = img .A
65+ arr = img .array
8766 self .assertEqual (arr .shape , img .shape )
8867
89- def test_concat (self ):
90- """Test image concatenation"""
68+ def test_hstack (self ):
69+ """Test horizontal concatenation"""
70+ img1 = Image (np .random .rand (10 , 10 ))
71+ img2 = Image (np .random .rand (10 , 10 ))
72+
73+ stacked = Image .Hstack ([img1 , img2 ], sep = 0 )
74+ self .assertEqual (stacked .shape , (10 , 20 ))
75+ nt .assert_array_equal (stacked .array [:, :10 ], img1 .array )
76+ nt .assert_array_equal (stacked .array [:, 10 :], img2 .array )
77+
78+ # a separator adds columns between the images
79+ stacked = Image .Hstack ([img1 , img2 ], sep = 2 )
80+ self .assertEqual (stacked .shape , (10 , 22 ))
81+
82+ def test_vstack (self ):
83+ """Test vertical concatenation"""
9184 img1 = Image (np .random .rand (10 , 10 ))
9285 img2 = Image (np .random .rand (10 , 10 ))
9386
94- try :
95- # Horizontal concatenation
96- concat_h = img1 .concat (img2 , "h" )
97- self .assertEqual (concat_h .shape [0 ], img1 .shape [0 ])
98- self .assertEqual (concat_h .shape [1 ], img1 .shape [1 ] + img2 .shape [1 ])
99- except :
100- pass
101-
102- try :
103- # Vertical concatenation
104- concat_v = img1 .concat (img2 , "v" )
105- self .assertEqual (concat_v .shape [0 ], img1 .shape [0 ] + img2 .shape [0 ])
106- self .assertEqual (concat_v .shape [1 ], img1 .shape [1 ])
107- except :
108- pass
87+ stacked = Image .Vstack ([img1 , img2 ], sep = 0 )
88+ self .assertEqual (stacked .shape , (20 , 10 ))
89+ nt .assert_array_equal (stacked .array [:10 , :], img1 .array )
90+ nt .assert_array_equal (stacked .array [10 :, :], img2 .array )
91+
92+ # a separator adds rows between the images
93+ stacked = Image .Vstack ([img1 , img2 ], sep = 2 )
94+ self .assertEqual (stacked .shape , (22 , 10 ))
10995
11096 def test_interp2d (self ):
111- """Test 2D interpolation"""
97+ """Test 2D interpolation: sampling at integer pixel coordinates must
98+ reproduce the image values"""
11299 img = Image .Read ("monalisa.png" , mono = True , dtype = "float32" )
113100
114- try :
115- # Interpolate at specific points
116- interp = img .interp2d (np . array ([ 100 , 200 ]), np . array ([ 150 , 250 ]) )
117- self . assertIsNotNone ( interp )
118- except :
119- pass
101+ # U, V are (Ho, Wo) coordinate arrays for the output image
102+ U , V = np . meshgrid ( np . arange ( 100 , 110 ), np . arange ( 150 , 160 ))
103+ interp = img .interp2d (U , V )
104+
105+ self . assertEqual ( interp . shape , ( 10 , 10 ))
106+ nt . assert_allclose ( interp . array , img . array [ 150 : 160 , 100 : 110 ], rtol = 1e-5 )
120107
121108 def test_get_pixel (self ):
122- """Test getting pixel values"""
123- img = Image (np .random .rand (10 , 10 ))
109+ """Test getting pixel values; the arguments are (u, v), i.e. (column,
110+ row), the opposite order to NumPy indexing"""
111+ img = Image (np .random .rand (10 , 12 ))
112+ self .assertEqual (img .pixel (5 , 3 ), img .array [3 , 5 ])
124113
125- try :
126- val = img .getpixel (5 , 5 )
127- self .assertIsNotNone (val )
128- except :
129- pass
114+ # color image: result is a vector over planes
115+ img3 = Image (np .random .rand (10 , 12 , 3 ))
116+ nt .assert_array_equal (img3 .pixel (5 , 3 ), img3 .array [3 , 5 , :])
130117
131118
132119class TestImagePointFeatures (unittest .TestCase ):
@@ -135,31 +122,26 @@ def test_sift(self):
135122 """Test SIFT feature detection"""
136123 img = Image .Read ("monalisa.png" , mono = True )
137124
138- try :
139- sift = img .SIFT ()
140- self .assertGreater (len (sift ), 0 )
141- except :
142- pass
125+ sift = img .SIFT ()
126+ self .assertGreater (len (sift ), 0 )
143127
128+ @unittest .skip (
129+ "Image.SURF() is not implemented, and SURF is non-free in pip OpenCV "
130+ "builds; see https://github.com/petercorke/machinevision-toolbox-python/issues/113"
131+ )
144132 def test_surf (self ):
145133 """Test SURF feature detection"""
146134 img = Image .Read ("flowers1.png" , mono = True )
147135
148- try :
149- surf = img .SURF ()
150- self .assertGreater (len (surf ), 0 )
151- except :
152- pass
136+ surf = img .SURF ()
137+ self .assertGreater (len (surf ), 0 )
153138
154139 def test_orb (self ):
155140 """Test ORB feature detection"""
156141 img = Image .Read ("monalisa.png" , mono = True )
157142
158- try :
159- orb = img .ORB ()
160- self .assertGreater (len (orb ), 0 )
161- except :
162- pass
143+ orb = img .ORB ()
144+ self .assertGreater (len (orb ), 0 )
163145
164146 def test_match_orb_auto_metric (self ):
165147 """match() must auto-select hamming distance for binary (ORB)
@@ -187,15 +169,12 @@ def test_match_orb_auto_metric(self):
187169 m_sift_l2 = sift1 .match (sift2 , metric = "L2" )
188170 self .assertEqual (len (m_sift_auto ), len (m_sift_l2 ))
189171
190- def test_corners (self ):
191- """Test corner detection"""
172+ def test_harris (self ):
173+ """Test Harris corner detection"""
192174 img = Image .Read ("monalisa.png" , mono = True )
193175
194- try :
195- corners = img .corners ()
196- self .assertGreater (len (corners ), 0 )
197- except :
198- pass
176+ corners = img .Harris ()
177+ self .assertGreater (len (corners ), 0 )
199178
200179 def test_draw2 (self ):
201180 """draw2() with a named color and a colorized (colororder-bearing)
@@ -212,21 +191,16 @@ def test_draw2(self):
212191 def test_features_list_operations (self ):
213192 """Test feature list operations"""
214193 img = Image .Read ("monalisa.png" , mono = True )
194+ sift = img .SIFT ()
195+ self .assertGreater (len (sift ), 5 )
215196
216- try :
217- sift = img .SIFT ()
218-
219- # Test slicing
220- if len (sift ) > 5 :
221- slice_sift = sift [:5 ]
222- self .assertEqual (len (slice_sift ), 5 )
197+ # Test slicing
198+ slice_sift = sift [:5 ]
199+ self .assertEqual (len (slice_sift ), 5 )
223200
224- # Test indexing
225- if len (sift ) > 0 :
226- first_feature = sift [0 ]
227- self .assertIsNotNone (first_feature )
228- except :
229- pass
201+ # Test indexing
202+ first_feature = sift [0 ]
203+ self .assertEqual (len (first_feature ), 1 )
230204
231205 def test_gridify_scalar_nbins (self ):
232206 """gridify() with a scalar nbins must not raise (regression: numpy
@@ -266,20 +240,19 @@ def test_akaze(self):
266240 def test_feature_properties (self ):
267241 """Test feature properties"""
268242 img = Image .Read ("monalisa.png" , mono = True )
269-
270- try :
271- sift = img .SIFT ()
272-
273- if len (sift ) > 0 :
274- # Get properties
275- uv = sift .uv
276- self .assertIsNotNone (uv )
277-
278- # Get strength
279- strength = sift .strength
280- self .assertIsNotNone (strength )
281- except :
282- pass
243+ sift = img .SIFT ()
244+ n = len (sift )
245+ self .assertGreater (n , 0 )
246+
247+ # coordinates: u, v are per-feature lists, p is a 2xN array
248+ self .assertEqual (len (sift .u ), n )
249+ self .assertEqual (len (sift .v ), n )
250+ self .assertEqual (sift .p .shape , (2 , n ))
251+ nt .assert_array_equal (sift .p [0 , :], sift .u )
252+ nt .assert_array_equal (sift .p [1 , :], sift .v )
253+
254+ # strength
255+ self .assertEqual (len (sift .strength ), n )
283256
284257
285258if __name__ == "__main__" :
0 commit comments