# # Image to graph utility # # For the given data we have: # dip_hw_3.mat["d1a"] # [MN x MN] affinity matrix # dip_hw_3.mat["d2a"] # [M x N x 3] RGB image # dip_hw_3.mat["d2b"] # [M x N x 3] RGB image # # # author: Christos Choutouridis # date: 05/07/2025 # try: import numpy as np from numpy._typing import NDArray from sklearn.metrics import pairwise_distances # Testing requirements import matplotlib.pyplot from scipy.io import loadmat except ImportError as e: print("Missing package: ", e) print("Run: pip install -r requirements.txt to install.") exit(1) def image_to_graph( img_array: NDArray[np.floating] ) -> NDArray[np.float64]: """ Converts an input image into a fully connected graph represented by an affinity matrix. Parameters: ---------- img_array : np.ndarray of shape (M, N, C), dtype=float The input image with C channels (e.g., 3 for RGB), with values normalized in [0, 1]. Returns: ------- affinity_mat : np.ndarray of shape (M*N, M*N), dtype=float Symmetric affinity matrix representing the fully connected graph. A(i, j) = 1 / ||pixel_i - pixel_j||_2 """ if not np.issubdtype(img_array.dtype, np.floating): raise ValueError("img_array must be of float type with values in [0, 1].") M, N, C = img_array.shape pixels = img_array.reshape(-1, C) # shape (M*N, C) # Compute Euclidean distances between all pixel vectors distances = pairwise_distances(pixels, metric='euclidean') # shape (MN, MN) # Avoid division by zero on the diagonal np.fill_diagonal(distances, 1e-10) # Affinity = 1 / e^d(i,j) affinity_mat = 1.0 / np.exp(distances) return affinity_mat def _test_1(plot : bool = False): """ Test image_to_graph() with a small 4x4 RGB random value array """ print(f" === Test 1 === ") print(f"") # Small 4x4 RGB with random values at [0, 1] img_array = np.random.rand(4, 4, 3).astype(np.float32) # affinity matrix calculation A = image_to_graph(img_array) # Print specs print("Shape of affinity matrix:", A.shape) # (16, 16) print("Is symmetric:", np.allclose(A, A.T)) # True print("Max value:", np.max(A)) print("Min value:", np.min(A)) if plot: matplotlib.use("TkAgg") matplotlib.pyplot.imshow(A, cmap='hot') matplotlib.pyplot.colorbar() matplotlib.pyplot.title("Affinity Matrix Heatmap") matplotlib.pyplot.show() def _test_2(plot : bool = False): """ Test image_to_graph() with d2b matrix """ print(f" === Test 2 === ") print(f"") data = loadmat("dip_hw_3.mat") img_array = data["d2b"] # shape (M, N, 3), dtype float, values in [0, 1] # Check shape and type print("Input image shape:", img_array.shape) print("dtype:", img_array.dtype) # affinity matrix calculation A = image_to_graph(img_array) # Print specs print("Affinity matrix shape:", A.shape) print("Is symmetric:", np.allclose(A, A.T)) print("Max value:", np.max(A)) print("Min value:", np.min(A)) if plot: matplotlib.use("TkAgg") matplotlib.pyplot.imshow(A, cmap='hot') matplotlib.pyplot.title("Affinity Matrix Heatmap") matplotlib.pyplot.colorbar() matplotlib.pyplot.show() if __name__ == '__main__': # If you have TkAgg you can pass True, otherwise pass False _test_1(False) _test_2(False)