Loading autocnet/matcher/mutual_information.py +66 −64 Original line number Diff line number Diff line Loading @@ -2,42 +2,9 @@ from math import floor import numpy as np def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, bins=100, func=None): """ Applys the mutual information matcher function over a search image using a defined template. Where the search area is 2x the size of the template image Parameters ---------- template : ndarray The input search template used to 'query' the destination image image : ndarray The image or sub-image to be searched bins : int Number of bins to use when computing the histograms Returns ------- x : float The x offset y : float The y offset max_corr : float The strength of the correlation in the range [-1, 1]. corr_map : ndarray Map of corrilation coefficients when comparing the template to locations within the search area """ from scipy.ndimage.measurements import center_of_mass def mutual_information_match(t1, t2, **kwargs): def mutual_information(t1, t2, **kwargs): """ Computes the correlation coefficient between two images using a histogram comparison (Mutual information for joint histograms). The corr_map coefficient Loading @@ -58,6 +25,10 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, : float Correlation coefficient computed between the two images being compared between 0 and 4 See Also -------- numpy.histogram2d : for the kwargs that can be passed to the comparison """ hgram, x_edges, y_edges = np.histogram2d(t1.ravel(),t2.ravel(), **kwargs) Loading @@ -70,14 +41,56 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, nzs = pxy > 0 # Only non-zero pxy values contribute to the sum return np.sum(pxy[nzs] * np.log(pxy[nzs] / px_py[nzs])) def mutual_information_match(d_template, s_image, subpixel_size=3, bins=100, func=None, **kwargs): """ Applys the mutual information matcher function over a search image using a defined template Parameters ---------- d_template : ndarray The input search template used to 'query' the destination image s_image : ndarray The image or sub-image to be searched subpixel_size : int Subpixel area size to search for the center of mass calculation bins : int Number of bins to use when computing the histograms func : function Function object to be used to compute the histogram comparison Returns ------- x : float The x offset y : float The y offset max_corr : float The strength of the correlation in the range [0, 4]. corr_map : ndarray Map of corrilation coefficients when comparing the template to locations within the search area """ if func == None: func = mutual_information_match func = mutual_information image_size = s_image.shape template_size = d_template.shape y_diff = abs(template_size[0] - image_size[0]) x_diff = abs(template_size[1] - image_size[1]) y_diff = image_size[0] - template_size[0] x_diff = image_size[1] - template_size[1] max_corr = -np.inf corr_map = np.zeros((y_diff+1, x_diff+1)) Loading @@ -87,15 +100,13 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, for j in range(x_diff+1): sub_image = s_image[i:i+template_size[1], # y j:j+template_size[0]] # x corr = func(sub_image, d_template, bins=bins) corr = func(sub_image, d_template, bins=bins, **kwargs) if corr > max_corr: max_corr = corr max_i = i max_j = j corr_map[i, j] = corr # This is still operating at the pixel scale. Use the template_match_autoreg # logic to achieve submpixel weighting. y, x = np.unravel_index(np.argmax(corr_map, axis=None), corr_map.shape) upper = int(2 + floor(subpixel_size / 2)) Loading @@ -104,27 +115,18 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, area = corr_map[y-lower:y+upper, x-lower:x+upper] # Compute the y, x shift (subpixel) using scipys center_of_mass function cmass = center_of_mass(area) if area.shape != (subpixel_size+2, subpixel_size+2): print("Max correlation is too close to the boundary.") return None, None, 0, None # Find the max on the edges, scale just like autoreg (but why?) edge_max = np.max(np.vstack([area[0], area[-1], area[:,0], area[:,-1]])) internal = area[1:-1, 1:-1] mask = (internal > edge_max + max_scaler * (edge_max-max_corr)).flatten() empty = np.column_stack([np.repeat(np.arange(0,subpixel_size),subpixel_size), np.tile(np.arange(0,subpixel_size),subpixel_size), np.zeros(subpixel_size*subpixel_size)]) empty[:,-1] = internal.ravel() to_weight = empty[mask, :] # Average is the shift from y, x form average = np.average(to_weight[:,:2], axis=0, weights=to_weight[:,2]) subpixel_y_shift = subpixel_size - 1 - cmass[0] subpixel_x_shift = subpixel_size - 1 - cmass[1] # The center of the 3x3 window is 1.5,1.5, so the shift needs to be recentered to 0,0 y += (subpixel_size/2 - average[0]) x += (subpixel_size/2 - average[1]) y += subpixel_y_shift x += subpixel_x_shift # Compute the idealized shift (image center) y -= (s_image.shape[0] / 2) - (d_template.shape[0] / 2) Loading autocnet/matcher/tests/test_mutual_information.py +6 −7 Original line number Diff line number Diff line Loading @@ -11,14 +11,13 @@ from .. import mutual_information def test_good_mi(): test_image1 = np.array([[i for i in range(50)] for j in range(50)]) # test_image2 = np.ones((50, 50)) corrilation = mutual_information.mi(test_image1, test_image1) corrilation = mutual_information.mutual_information(test_image1, test_image1) assert corrilation == 2.3025850929940455 def test_bad_mi(): test_image1 = np.array([[i for i in range(50)] for j in range(50)]) test_image2 = np.ones((50, 50)) corrilation = mutual_information.mi(test_image1, test_image2) corrilation = mutual_information.mutual_information(test_image1, test_image2) assert corrilation == pytest.approx(0) def test_mutual_information(): Loading @@ -27,9 +26,9 @@ def test_mutual_information(): s_image[25:75, 25:75] = d_template x_offset, y_offset, max_corr, corr_map = mutual_information.mutual_information(d_template, s_image, bins=20) assert x_offset == 0.9633527901853505 assert y_offset == 0.5 x_offset, y_offset, max_corr, corr_map = mutual_information.mutual_information_match(d_template, s_image, bins=20) assert x_offset == 0.01711861257171421 assert y_offset == 0.0 assert max_corr == 2.9755967600033015 assert corr_map.shape == (51, 51) assert np.average(corr_map) == 1.3199548152066989 assert np.min(corr_map) >= 0.0 Loading
autocnet/matcher/mutual_information.py +66 −64 Original line number Diff line number Diff line Loading @@ -2,42 +2,9 @@ from math import floor import numpy as np def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, bins=100, func=None): """ Applys the mutual information matcher function over a search image using a defined template. Where the search area is 2x the size of the template image Parameters ---------- template : ndarray The input search template used to 'query' the destination image image : ndarray The image or sub-image to be searched bins : int Number of bins to use when computing the histograms Returns ------- x : float The x offset y : float The y offset max_corr : float The strength of the correlation in the range [-1, 1]. corr_map : ndarray Map of corrilation coefficients when comparing the template to locations within the search area """ from scipy.ndimage.measurements import center_of_mass def mutual_information_match(t1, t2, **kwargs): def mutual_information(t1, t2, **kwargs): """ Computes the correlation coefficient between two images using a histogram comparison (Mutual information for joint histograms). The corr_map coefficient Loading @@ -58,6 +25,10 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, : float Correlation coefficient computed between the two images being compared between 0 and 4 See Also -------- numpy.histogram2d : for the kwargs that can be passed to the comparison """ hgram, x_edges, y_edges = np.histogram2d(t1.ravel(),t2.ravel(), **kwargs) Loading @@ -70,14 +41,56 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, nzs = pxy > 0 # Only non-zero pxy values contribute to the sum return np.sum(pxy[nzs] * np.log(pxy[nzs] / px_py[nzs])) def mutual_information_match(d_template, s_image, subpixel_size=3, bins=100, func=None, **kwargs): """ Applys the mutual information matcher function over a search image using a defined template Parameters ---------- d_template : ndarray The input search template used to 'query' the destination image s_image : ndarray The image or sub-image to be searched subpixel_size : int Subpixel area size to search for the center of mass calculation bins : int Number of bins to use when computing the histograms func : function Function object to be used to compute the histogram comparison Returns ------- x : float The x offset y : float The y offset max_corr : float The strength of the correlation in the range [0, 4]. corr_map : ndarray Map of corrilation coefficients when comparing the template to locations within the search area """ if func == None: func = mutual_information_match func = mutual_information image_size = s_image.shape template_size = d_template.shape y_diff = abs(template_size[0] - image_size[0]) x_diff = abs(template_size[1] - image_size[1]) y_diff = image_size[0] - template_size[0] x_diff = image_size[1] - template_size[1] max_corr = -np.inf corr_map = np.zeros((y_diff+1, x_diff+1)) Loading @@ -87,15 +100,13 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, for j in range(x_diff+1): sub_image = s_image[i:i+template_size[1], # y j:j+template_size[0]] # x corr = func(sub_image, d_template, bins=bins) corr = func(sub_image, d_template, bins=bins, **kwargs) if corr > max_corr: max_corr = corr max_i = i max_j = j corr_map[i, j] = corr # This is still operating at the pixel scale. Use the template_match_autoreg # logic to achieve submpixel weighting. y, x = np.unravel_index(np.argmax(corr_map, axis=None), corr_map.shape) upper = int(2 + floor(subpixel_size / 2)) Loading @@ -104,27 +115,18 @@ def mutual_information(d_template, s_image, subpixel_size=3, max_scaler=0.2, area = corr_map[y-lower:y+upper, x-lower:x+upper] # Compute the y, x shift (subpixel) using scipys center_of_mass function cmass = center_of_mass(area) if area.shape != (subpixel_size+2, subpixel_size+2): print("Max correlation is too close to the boundary.") return None, None, 0, None # Find the max on the edges, scale just like autoreg (but why?) edge_max = np.max(np.vstack([area[0], area[-1], area[:,0], area[:,-1]])) internal = area[1:-1, 1:-1] mask = (internal > edge_max + max_scaler * (edge_max-max_corr)).flatten() empty = np.column_stack([np.repeat(np.arange(0,subpixel_size),subpixel_size), np.tile(np.arange(0,subpixel_size),subpixel_size), np.zeros(subpixel_size*subpixel_size)]) empty[:,-1] = internal.ravel() to_weight = empty[mask, :] # Average is the shift from y, x form average = np.average(to_weight[:,:2], axis=0, weights=to_weight[:,2]) subpixel_y_shift = subpixel_size - 1 - cmass[0] subpixel_x_shift = subpixel_size - 1 - cmass[1] # The center of the 3x3 window is 1.5,1.5, so the shift needs to be recentered to 0,0 y += (subpixel_size/2 - average[0]) x += (subpixel_size/2 - average[1]) y += subpixel_y_shift x += subpixel_x_shift # Compute the idealized shift (image center) y -= (s_image.shape[0] / 2) - (d_template.shape[0] / 2) Loading
autocnet/matcher/tests/test_mutual_information.py +6 −7 Original line number Diff line number Diff line Loading @@ -11,14 +11,13 @@ from .. import mutual_information def test_good_mi(): test_image1 = np.array([[i for i in range(50)] for j in range(50)]) # test_image2 = np.ones((50, 50)) corrilation = mutual_information.mi(test_image1, test_image1) corrilation = mutual_information.mutual_information(test_image1, test_image1) assert corrilation == 2.3025850929940455 def test_bad_mi(): test_image1 = np.array([[i for i in range(50)] for j in range(50)]) test_image2 = np.ones((50, 50)) corrilation = mutual_information.mi(test_image1, test_image2) corrilation = mutual_information.mutual_information(test_image1, test_image2) assert corrilation == pytest.approx(0) def test_mutual_information(): Loading @@ -27,9 +26,9 @@ def test_mutual_information(): s_image[25:75, 25:75] = d_template x_offset, y_offset, max_corr, corr_map = mutual_information.mutual_information(d_template, s_image, bins=20) assert x_offset == 0.9633527901853505 assert y_offset == 0.5 x_offset, y_offset, max_corr, corr_map = mutual_information.mutual_information_match(d_template, s_image, bins=20) assert x_offset == 0.01711861257171421 assert y_offset == 0.0 assert max_corr == 2.9755967600033015 assert corr_map.shape == (51, 51) assert np.average(corr_map) == 1.3199548152066989 assert np.min(corr_map) >= 0.0