From 802cd208dbea7743a779b058478b6bb061e0d62d Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Tue, 18 Oct 2022 22:01:06 -0300 Subject: [PATCH 01/24] Create haralick_descriptors --- computer_vision/haralick_descriptors | 245 +++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 computer_vision/haralick_descriptors diff --git a/computer_vision/haralick_descriptors b/computer_vision/haralick_descriptors new file mode 100644 index 000000000000..6f8d6bd66b80 --- /dev/null +++ b/computer_vision/haralick_descriptors @@ -0,0 +1,245 @@ +def rmse(original: np.ndarray, reference: np.ndarray) -> float: + """Simple implementation of Root Mean Squared Error for two N dimensional numpy arrays.""" + return np.sqrt(((original - reference) ** 2).mean()) + + +def normalize_image(image: np.ndarray, cap: float = 255) -> np.ndarray: + """Normalizes image in Numpy 2D array format, between ranges 0-cap, as to fit uint8 type. + + Args: + image: 2D numpy array representing image as matrix, with values in any range + cap: Maximum cap amount for normalization + Returns: + return 2D numpy array of type uint8, corresponding to limited range matrix + """ + normalized = (image - np.min(image)) / (np.max(image) - np.min(image)) * cap + return normalized.astype(np.uint8) + + +def normalize_array(array, cap: float = 1): + """Normalizes a 1D array, between ranges 0-cap. + + Args: + array: List containing values to be normalized between cap range. + cap: Maximum cap amount for normalization. + Returns: + return 1D numpy array , corresponding to limited range array + """ + normalized = (array - np.min(array)) / (np.max(array) - np.min(array)) * cap + return normalized + + +def euclidean(point_1: np.ndarray, point_2: np.ndarray): + """Simple method for calculating the euclidean distance between two points, with type np.ndarray.""" + return np.sqrt(np.sum(np.square(point_1 - point_2))) + + +def grayscale(image: np.ndarray) -> np.ndarray: + """Uses luminance weights to transform RGB channel to greyscale, by + taking the dot product between the channel and the weights.""" + return np.dot(image[:, :, 0:3], [0.299, 0.587, 0.114]).astype(np.uint8) + + +def binarize(image, threshold_value): + """Binarizes a grayscale image based on a given threshold value, setting values to 1 or 0 accordingly.""" + binarized = np.where(image > threshold_value, 1, 0) + + return binarized + + +def transform(image, kind, kernel=np.ones((3, 3))): + """Simple image transformation using one of two available filter functions: Erosion and Dilation. + + Args: + image: + kind: Can be either 'erosion', in which case the :func:np.max function is called, + or 'dilation', when :func:np.min is used instead. + kernel: n x n kernel with shape < :attr:image.shape, to be used when applying convolution to original image + + Returns: + + """ + if kind == "erosion": + constant = 1 + apply = np.max + else: + constant = 0 + apply = np.min + + center_x, center_y = (x // 2 for x in kernel.shape) + + # Use padded image when applying convolotion to not go out of bounds of the original the image + transformed = np.zeros(image.shape, dtype=np.uint8) + padded = np.pad(image, 1, 'constant', constant_values=constant) + + for x in range(center_x, padded.shape[0] - center_x): + for y in range(center_y, padded.shape[1] - center_y): + + center = padded[x - center_x: x + center_x + 1, y - center_y: y + center_y + 1] + # Apply transformation method to the centered section of the image + transformed[x - center_x, y - center_y] = apply(center[kernel == 1]) + + return transformed + + +def opening_filter(image, kernel=np.ones((3, 3))): + """Opening filter, defined as the sequence of erosion and then a dilation filter on the same image.""" + return transform( + transform(image, 'dilation', kernel), + 'erosion', kernel + ) + + +def closing_filter(image, kernel=np.ones((3, 3))): + """Opening filter, defined as the sequence of dilation and then erosion filter on the same image.""" + return transform( + transform(image, 'erosion', kernel), + 'dilation', kernel + ) + + +def binary_mask(image_gray, image_map): + """Apply binary mask, or thresholding based on bit mask value (mapping mask is 1 or 0).""" + true_mask, false_mask = np.array(image_gray, copy=True), np.array(image_gray, copy=True) + true_mask[image_map == 1] = 1 + false_mask[image_map == 0] = 0 + + return true_mask, false_mask + + +def matrix_concurrency(image, coordinate): + """Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. + Implementation is made using basic iteration, as function to be performed (np.max) is non-linear and therefore + not usable on the Fourier Transform domain.""" + matrix = np.zeros([np.max(image) + 1, np.max(image) + 1]) + + offset_x, offset_y = coordinate[0], coordinate[1] + + for x in range(1, image.shape[0] - 1): + for y in range(1, image.shape[1] - 1): + + base_pixel = image[x, y] + offset_pixel = image[x + offset_x, y + offset_y] + + matrix[base_pixel, offset_pixel] += 1 + + return matrix / np.sum(matrix) + + +def haralick_descriptors(matrix): + """Calculates all 8 Haralick descriptors based on co-occurence input matrix. + All descriptors are as follows: + Maximum probability, Inverse Difference, Homogeneity, Entropy, Energy, Dissimilarity, Contrast and Correlation + + Args: + matrix: Co-occurence matrix to use as base for calculating descriptors. + + Returns: + Reverse ordered list of resulting descriptors + """ + # Function np.indices could be used for bigger input types, but np.ogrid works just fine + i, j = np.ogrid[0:matrix.shape[0], 0:matrix.shape[1]] # np.indices() + + # Pre-calculate frequent multiplication and subtraction + prod = np.multiply(i, j) + sub = np.subtract(i, j) + + # Calculate numerical value of Maximum Probability + maximum_prob = np.max(matrix) + # Using the definition for each descriptor individually to calculate its matrix + correlation = prod * matrix + energy = np.power(matrix, 2) + contrast = matrix * np.power(sub, 2) + + dissimilarity = matrix * np.abs(sub) + inverse_difference = matrix / (1 + np.abs(sub)) + homogeneity = matrix / (1 + np.power(sub, 2)) + entropy = -(matrix[matrix > 0] * np.log(matrix[matrix > 0])) + + # Sum values for descriptors ranging from the first one to the last, as all are their respective origin matrix + # and not the resulting value yet. + descriptors = [maximum_prob, correlation.sum(), energy.sum(), contrast.sum(), + dissimilarity.sum(), inverse_difference.sum(), homogeneity.sum(), entropy.sum()] + return descriptors + + +def get_descriptors(masks, coordinate): + """Calculate all Haralick descriptors for a sequence of different co-occurrence matrices, given input + masks and coordinates.""" + + descriptors = list() + for mask in masks: + descriptors.append(haralick_descriptors( + matrix_concurrency(mask, coordinate)) + ) + # Concatenate each individual descriptor into one single list containing sequence of descriptors + return np.concatenate(descriptors, axis=None) + + +def euclidean(point_1: np.ndarray, point_2: np.ndarray): + """Simple method for calculating the euclidean distance between two points, with type np.ndarray.""" + return np.sqrt(np.sum(np.square(point_1 - point_2))) + + +def get_distances(descriptors, base): + """Calculate all Euclidian distances between a selected base descriptor and all other Haralick descriptors + The resulting comparison is return in decreasing order, showing which descriptor is the most similar to the + selected base. + + Args: + descriptors: Haralick descriptors to compare with base index + base: Haralick descriptor index to use as base when calculating respective euclidean distance to other descriptors. + + Returns: + Ordered distances between descriptors + + """ + distances = [] + + for description in descriptors: + distances.append(euclidean(description, descriptors[base])) + # Normalize distances between range [0, 1] + distances = normalize_array(distances, 1) + return sorted(enumerate(distances), key=lambda tup: tup[1]) + + +def main(): + # Index to compare haralick descriptors to + index = int(input()) + q_value = [int(value) for value in input().split()] + + # Format is the respective filter to apply, can be either 1 for the opening filter or else for the closing + parameters = {'format': int(input()), 'threshold': int(input())} + + # Number of images to perform methods on + b_number = int(input()) + + files, descriptors = (list(), list()) + + for _ in range(b_number): + file = input().rstrip() + files.append(file) + + # Open given image and calculate morphological filter, respective masks and correspondent Harralick Descriptors. + image = imageio.imread(file).astype(np.float32) + gray = grayscale(image) + threshold = binarize(gray, parameters['threshold']) + + morphological = opening_filter(threshold) if parameters['format'] == 1 else closing_filter(threshold) + masks = binary_mask(gray, morphological) + descriptors.append(get_descriptors(masks, q_value)) + + # Transform ordered distances array into a sequence of indexes corresponding to original file position + distances = get_distances(descriptors, index) + indexed_distances = np.array(distances).astype(np.uint8)[:, 0] + + # Finally, print distances considering the Haralick descriptions from the base file to + # all other images using the morphology method of choice. + print(f"Query: {files[index]}") + print("Ranking:") + for idx, file_idx in enumerate(indexed_distances): + print(f"({idx}) {files[file_idx]}", end="\n") + + +if __name__ == "__main__": + main() From 88b014f7ab6ee9d8b50c4e6b6a02e38d3a8e7d52 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Wed, 19 Oct 2022 01:25:19 -0300 Subject: [PATCH 02/24] Working on creating Unit Testing for Haralick Descriptors module --- ...ck_descriptors => haralick_descriptors.py} | 199 ++++++++++++------ 1 file changed, 131 insertions(+), 68 deletions(-) rename computer_vision/{haralick_descriptors => haralick_descriptors.py} (52%) diff --git a/computer_vision/haralick_descriptors b/computer_vision/haralick_descriptors.py similarity index 52% rename from computer_vision/haralick_descriptors rename to computer_vision/haralick_descriptors.py index 6f8d6bd66b80..1006f4db9435 100644 --- a/computer_vision/haralick_descriptors +++ b/computer_vision/haralick_descriptors.py @@ -1,16 +1,36 @@ +import imageio.v2 as imageio +import numpy as np + + def rmse(original: np.ndarray, reference: np.ndarray) -> float: - """Simple implementation of Root Mean Squared Error for two N dimensional numpy arrays.""" + """Simple implementation of Root Mean Squared Error + for two N dimensional numpy arrays. + >>> rmse(np.array([1, 2, 3]), np.array([1, 2, 3])) + 0.0 + >>> rmse(np.array([1, 2, 3]), np.array([2, 2, 2])) + 1.0 + >>> rmse(np.array([1, 2, 3]), np.array([6, 4, 2])) + 30.0 + """ return np.sqrt(((original - reference) ** 2).mean()) def normalize_image(image: np.ndarray, cap: float = 255) -> np.ndarray: - """Normalizes image in Numpy 2D array format, between ranges 0-cap, as to fit uint8 type. + """ + Normalizes image in Numpy 2D array format, between ranges 0-cap, + as to fit uint8 type. Args: image: 2D numpy array representing image as matrix, with values in any range cap: Maximum cap amount for normalization Returns: return 2D numpy array of type uint8, corresponding to limited range matrix + + >>> normalize_image(np.array([[1, 2, 3], [4, 5, 10]]), cap=1) + array([[ 0, 0.111111, 0.222222], + [ 0.333333, 0.444444, 1. ]], dtype=float32) + >>> normalize_image(np.array([[4, 4, 3], [1, 7, 2]])) + """ normalized = (image - np.min(image)) / (np.max(image) - np.min(image)) * cap return normalized.astype(np.uint8) @@ -29,11 +49,6 @@ def normalize_array(array, cap: float = 1): return normalized -def euclidean(point_1: np.ndarray, point_2: np.ndarray): - """Simple method for calculating the euclidean distance between two points, with type np.ndarray.""" - return np.sqrt(np.sum(np.square(point_1 - point_2))) - - def grayscale(image: np.ndarray) -> np.ndarray: """Uses luminance weights to transform RGB channel to greyscale, by taking the dot product between the channel and the weights.""" @@ -41,24 +56,33 @@ def grayscale(image: np.ndarray) -> np.ndarray: def binarize(image, threshold_value): - """Binarizes a grayscale image based on a given threshold value, setting values to 1 or 0 accordingly.""" + """ + Binarizes a grayscale image based on a given threshold value, + setting values to 1 or 0 accordingly. + """ binarized = np.where(image > threshold_value, 1, 0) return binarized -def transform(image, kind, kernel=np.ones((3, 3))): - """Simple image transformation using one of two available filter functions: Erosion and Dilation. +def transform(image, kind, kernel=None): + """ + Simple image transformation using one of two available filter functions: + Erosion and Dilation. Args: image: - kind: Can be either 'erosion', in which case the :func:np.max function is called, - or 'dilation', when :func:np.min is used instead. - kernel: n x n kernel with shape < :attr:image.shape, to be used when applying convolution to original image + kind: Can be either 'erosion', in which case the :func:np.max + function is called, or 'dilation', when :func:np.min is used instead. + kernel: n x n kernel with shape < :attr:image.shape, + to be used when applying convolution to original image Returns: """ + if kernel is None: + kernel = np.ones((3, 3)) + if kind == "erosion": constant = 1 apply = np.max @@ -68,39 +92,52 @@ def transform(image, kind, kernel=np.ones((3, 3))): center_x, center_y = (x // 2 for x in kernel.shape) - # Use padded image when applying convolotion to not go out of bounds of the original the image + # Use padded image when applying convolotion + # to not go out of bounds of the original the image transformed = np.zeros(image.shape, dtype=np.uint8) - padded = np.pad(image, 1, 'constant', constant_values=constant) + padded = np.pad(image, 1, "constant", constant_values=constant) for x in range(center_x, padded.shape[0] - center_x): for y in range(center_y, padded.shape[1] - center_y): - - center = padded[x - center_x: x + center_x + 1, y - center_y: y + center_y + 1] + center = padded[ + x - center_x : x + center_x + 1, y - center_y : y + center_y + 1 + ] # Apply transformation method to the centered section of the image transformed[x - center_x, y - center_y] = apply(center[kernel == 1]) return transformed -def opening_filter(image, kernel=np.ones((3, 3))): - """Opening filter, defined as the sequence of erosion and then a dilation filter on the same image.""" - return transform( - transform(image, 'dilation', kernel), - 'erosion', kernel - ) +def opening_filter(image, kernel=None): + """ + Opening filter, defined as the sequence of + erosion and then a dilation filter on the same image. + """ + if kernel is None: + np.ones((3, 3)) + return transform(transform(image, "dilation", kernel), "erosion", kernel) -def closing_filter(image, kernel=np.ones((3, 3))): - """Opening filter, defined as the sequence of dilation and then erosion filter on the same image.""" - return transform( - transform(image, 'erosion', kernel), - 'dilation', kernel - ) + +def closing_filter(image, kernel=None): + """ + Opening filter, defined as the sequence of + dilation and then erosion filter on the same image. + """ + if kernel is None: + np.ones((3, 3)) + + return transform(transform(image, "erosion", kernel), "dilation", kernel) def binary_mask(image_gray, image_map): - """Apply binary mask, or thresholding based on bit mask value (mapping mask is 1 or 0).""" - true_mask, false_mask = np.array(image_gray, copy=True), np.array(image_gray, copy=True) + """ + Apply binary mask, or thresholding based + on bit mask value (mapping mask is 1 or 0). + """ + true_mask, false_mask = np.array(image_gray, copy=True), np.array( + image_gray, copy=True + ) true_mask[image_map == 1] = 1 false_mask[image_map == 0] = 0 @@ -108,16 +145,19 @@ def binary_mask(image_gray, image_map): def matrix_concurrency(image, coordinate): - """Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. - Implementation is made using basic iteration, as function to be performed (np.max) is non-linear and therefore - not usable on the Fourier Transform domain.""" + """ + Calculate sample co-occurrence matrix based on input image + as well as selected coordinates on image. + Implementation is made using basic iteration, + as function to be performed (np.max) is non-linear and therefore + not usable on the Fourier Transform domain. + """ matrix = np.zeros([np.max(image) + 1, np.max(image) + 1]) offset_x, offset_y = coordinate[0], coordinate[1] for x in range(1, image.shape[0] - 1): for y in range(1, image.shape[1] - 1): - base_pixel = image[x, y] offset_pixel = image[x + offset_x, y + offset_y] @@ -129,7 +169,8 @@ def matrix_concurrency(image, coordinate): def haralick_descriptors(matrix): """Calculates all 8 Haralick descriptors based on co-occurence input matrix. All descriptors are as follows: - Maximum probability, Inverse Difference, Homogeneity, Entropy, Energy, Dissimilarity, Contrast and Correlation + Maximum probability, Inverse Difference, Homogeneity, Entropy, + Energy, Dissimilarity, Contrast and Correlation Args: matrix: Co-occurence matrix to use as base for calculating descriptors. @@ -137,8 +178,9 @@ def haralick_descriptors(matrix): Returns: Reverse ordered list of resulting descriptors """ - # Function np.indices could be used for bigger input types, but np.ogrid works just fine - i, j = np.ogrid[0:matrix.shape[0], 0:matrix.shape[1]] # np.indices() + # Function np.indices could be used for bigger input types, + # but np.ogrid works just fine + i, j = np.ogrid[0 : matrix.shape[0], 0 : matrix.shape[1]] # np.indices() # Pre-calculate frequent multiplication and subtraction prod = np.multiply(i, j) @@ -156,48 +198,62 @@ def haralick_descriptors(matrix): homogeneity = matrix / (1 + np.power(sub, 2)) entropy = -(matrix[matrix > 0] * np.log(matrix[matrix > 0])) - # Sum values for descriptors ranging from the first one to the last, as all are their respective origin matrix - # and not the resulting value yet. - descriptors = [maximum_prob, correlation.sum(), energy.sum(), contrast.sum(), - dissimilarity.sum(), inverse_difference.sum(), homogeneity.sum(), entropy.sum()] + # Sum values for descriptors ranging from the first one to the last, + # as all are their respective origin matrix and not the resulting value yet. + descriptors = [ + maximum_prob, + correlation.sum(), + energy.sum(), + contrast.sum(), + dissimilarity.sum(), + inverse_difference.sum(), + homogeneity.sum(), + entropy.sum(), + ] return descriptors def get_descriptors(masks, coordinate): - """Calculate all Haralick descriptors for a sequence of different co-occurrence matrices, given input - masks and coordinates.""" + """ + Calculate all Haralick descriptors for a sequence of + different co-occurrence matrices, given input masks and coordinates. + """ + descriptors = np.zeros((len(masks), 8)) + for idx, mask in enumerate(masks): + descriptors[idx] = haralick_descriptors(matrix_concurrency(mask, coordinate)) - descriptors = list() - for mask in masks: - descriptors.append(haralick_descriptors( - matrix_concurrency(mask, coordinate)) - ) - # Concatenate each individual descriptor into one single list containing sequence of descriptors + # Concatenate each individual descriptor into + # one single list containing sequence of descriptors return np.concatenate(descriptors, axis=None) def euclidean(point_1: np.ndarray, point_2: np.ndarray): - """Simple method for calculating the euclidean distance between two points, with type np.ndarray.""" + """ + Simple method for calculating the euclidean distance between two points, + with type np.ndarray. + """ return np.sqrt(np.sum(np.square(point_1 - point_2))) def get_distances(descriptors, base): - """Calculate all Euclidian distances between a selected base descriptor and all other Haralick descriptors - The resulting comparison is return in decreasing order, showing which descriptor is the most similar to the - selected base. + """ + Calculate all Euclidean distances between a selected base descriptor + and all other Haralick descriptors + The resulting comparison is return in decreasing order, + showing which descriptor is the most similar to the selected base. Args: descriptors: Haralick descriptors to compare with base index - base: Haralick descriptor index to use as base when calculating respective euclidean distance to other descriptors. + base: Haralick descriptor index to use as base when calculating respective + euclidean distance to other descriptors. Returns: Ordered distances between descriptors - """ - distances = [] + distances = np.zeros(descriptors.shape[0]) - for description in descriptors: - distances.append(euclidean(description, descriptors[base])) + for idx, description in enumerate(descriptors): + distances[idx] = euclidean(description, descriptors[base]) # Normalize distances between range [0, 1] distances = normalize_array(distances, 1) return sorted(enumerate(distances), key=lambda tup: tup[1]) @@ -208,33 +264,40 @@ def main(): index = int(input()) q_value = [int(value) for value in input().split()] - # Format is the respective filter to apply, can be either 1 for the opening filter or else for the closing - parameters = {'format': int(input()), 'threshold': int(input())} + # Format is the respective filter to apply, + # can be either 1 for the opening filter or else for the closing + parameters = {"format": int(input()), "threshold": int(input())} # Number of images to perform methods on b_number = int(input()) - files, descriptors = (list(), list()) + files, descriptors = (np.array([]), np.array([])) for _ in range(b_number): file = input().rstrip() files.append(file) - # Open given image and calculate morphological filter, respective masks and correspondent Harralick Descriptors. + # Open given image and calculate morphological filter, + # respective masks and correspondent Harralick Descriptors. image = imageio.imread(file).astype(np.float32) gray = grayscale(image) - threshold = binarize(gray, parameters['threshold']) + threshold = binarize(gray, parameters["threshold"]) - morphological = opening_filter(threshold) if parameters['format'] == 1 else closing_filter(threshold) + morphological = ( + opening_filter(threshold) + if parameters["format"] == 1 + else closing_filter(threshold) + ) masks = binary_mask(gray, morphological) descriptors.append(get_descriptors(masks, q_value)) - # Transform ordered distances array into a sequence of indexes corresponding to original file position + # Transform ordered distances array into a sequence of indexes + # corresponding to original file position distances = get_distances(descriptors, index) indexed_distances = np.array(distances).astype(np.uint8)[:, 0] - # Finally, print distances considering the Haralick descriptions from the base file to - # all other images using the morphology method of choice. + # Finally, print distances considering the Haralick descriptions from the base + # file to all other images using the morphology method of choice. print(f"Query: {files[index]}") print("Ranking:") for idx, file_idx in enumerate(indexed_distances): From bc6a1894f84173910e95ba8544ca64f42788970c Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Wed, 19 Oct 2022 01:59:19 -0300 Subject: [PATCH 03/24] Type hinting for Haralick descriptors --- computer_vision/haralick_descriptors.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 1006f4db9435..6c64632f8cf1 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -36,7 +36,7 @@ def normalize_image(image: np.ndarray, cap: float = 255) -> np.ndarray: return normalized.astype(np.uint8) -def normalize_array(array, cap: float = 1): +def normalize_array(array: np.ndarray, cap: float = 1) -> np.ndarray: """Normalizes a 1D array, between ranges 0-cap. Args: @@ -55,7 +55,7 @@ def grayscale(image: np.ndarray) -> np.ndarray: return np.dot(image[:, :, 0:3], [0.299, 0.587, 0.114]).astype(np.uint8) -def binarize(image, threshold_value): +def binarize(image: np.ndarray, threshold_value) -> np.ndarray: """ Binarizes a grayscale image based on a given threshold value, setting values to 1 or 0 accordingly. @@ -65,7 +65,7 @@ def binarize(image, threshold_value): return binarized -def transform(image, kind, kernel=None): +def transform(image: np.ndarray, kind, kernel=None) -> np.ndarray: """ Simple image transformation using one of two available filter functions: Erosion and Dilation. @@ -108,7 +108,7 @@ def transform(image, kind, kernel=None): return transformed -def opening_filter(image, kernel=None): +def opening_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: """ Opening filter, defined as the sequence of erosion and then a dilation filter on the same image. @@ -119,7 +119,7 @@ def opening_filter(image, kernel=None): return transform(transform(image, "dilation", kernel), "erosion", kernel) -def closing_filter(image, kernel=None): +def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: """ Opening filter, defined as the sequence of dilation and then erosion filter on the same image. @@ -130,7 +130,7 @@ def closing_filter(image, kernel=None): return transform(transform(image, "erosion", kernel), "dilation", kernel) -def binary_mask(image_gray, image_map): +def binary_mask(image_gray: np.ndarray, image_map: np.ndarray) -> np.ndarray: """ Apply binary mask, or thresholding based on bit mask value (mapping mask is 1 or 0). @@ -144,7 +144,7 @@ def binary_mask(image_gray, image_map): return true_mask, false_mask -def matrix_concurrency(image, coordinate): +def matrix_concurrency(image: np.ndarray, coordinate) -> np.ndarray: """ Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. @@ -166,7 +166,7 @@ def matrix_concurrency(image, coordinate): return matrix / np.sum(matrix) -def haralick_descriptors(matrix): +def haralick_descriptors(matrix: np.ndarray) -> list: """Calculates all 8 Haralick descriptors based on co-occurence input matrix. All descriptors are as follows: Maximum probability, Inverse Difference, Homogeneity, Entropy, @@ -213,7 +213,7 @@ def haralick_descriptors(matrix): return descriptors -def get_descriptors(masks, coordinate): +def get_descriptors(masks, coordinate) -> np.ndarray: """ Calculate all Haralick descriptors for a sequence of different co-occurrence matrices, given input masks and coordinates. @@ -227,7 +227,7 @@ def get_descriptors(masks, coordinate): return np.concatenate(descriptors, axis=None) -def euclidean(point_1: np.ndarray, point_2: np.ndarray): +def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: """ Simple method for calculating the euclidean distance between two points, with type np.ndarray. @@ -235,7 +235,7 @@ def euclidean(point_1: np.ndarray, point_2: np.ndarray): return np.sqrt(np.sum(np.square(point_1 - point_2))) -def get_distances(descriptors, base): +def get_distances(descriptors, base) -> np.ndarray: """ Calculate all Euclidean distances between a selected base descriptor and all other Haralick descriptors From 6e23cc094f661b5ec21a1e9d9e4cfef2c5c0e8b8 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Sun, 27 Nov 2022 13:44:28 -0300 Subject: [PATCH 04/24] Fixed docstrings, unit testing and formatting choices --- computer_vision/haralick_descriptors.py | 160 ++++++++++++++++++------ 1 file changed, 119 insertions(+), 41 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 6c64632f8cf1..8cf0d702ce32 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -1,21 +1,29 @@ +""" +https://en.wikipedia.org/wiki/Image_texture +https://en.wikipedia.org/wiki/Co-occurrence_matrix#Application_to_image_analysis +""" +from typing import Any, Union + import imageio.v2 as imageio import numpy as np -def rmse(original: np.ndarray, reference: np.ndarray) -> float: +def root_mean_square_error(original: np.ndarray, reference: np.ndarray) -> float: """Simple implementation of Root Mean Squared Error for two N dimensional numpy arrays. - >>> rmse(np.array([1, 2, 3]), np.array([1, 2, 3])) - 0.0 - >>> rmse(np.array([1, 2, 3]), np.array([2, 2, 2])) - 1.0 - >>> rmse(np.array([1, 2, 3]), np.array([6, 4, 2])) - 30.0 + + Examples: + >>> root_mean_square_error(np.array([1, 2, 3]), np.array([1, 2, 3])) + 0.0 + >>> root_mean_square_error(np.array([1, 2, 3]), np.array([2, 2, 2])) + 0.816496580927726 + >>> root_mean_square_error(np.array([1, 2, 3]), np.array([6, 4, 2])) + 3.1622776601683795 """ return np.sqrt(((original - reference) ** 2).mean()) -def normalize_image(image: np.ndarray, cap: float = 255) -> np.ndarray: +def normalize_image(image: np.ndarray, cap: float = 255.0, data_type=np.uint8) -> np.ndarray: """ Normalizes image in Numpy 2D array format, between ranges 0-cap, as to fit uint8 type. @@ -23,17 +31,20 @@ def normalize_image(image: np.ndarray, cap: float = 255) -> np.ndarray: Args: image: 2D numpy array representing image as matrix, with values in any range cap: Maximum cap amount for normalization + data_type: numpy data type to set output variable to Returns: return 2D numpy array of type uint8, corresponding to limited range matrix - >>> normalize_image(np.array([[1, 2, 3], [4, 5, 10]]), cap=1) - array([[ 0, 0.111111, 0.222222], - [ 0.333333, 0.444444, 1. ]], dtype=float32) - >>> normalize_image(np.array([[4, 4, 3], [1, 7, 2]])) - + Examples: + >>> normalize_image(np.array([[1, 2, 3], [4, 5, 10]]), cap=1.0, data_type=np.float64) + array([[0. , 0.11111111, 0.22222222], + [0.33333333, 0.44444444, 1. ]]) + >>> normalize_image(np.array([[4, 4, 3], [1, 7, 2]])) + array([[127, 127, 85], + [ 0, 255, 42]], dtype=uint8) """ normalized = (image - np.min(image)) / (np.max(image) - np.min(image)) * cap - return normalized.astype(np.uint8) + return normalized.astype(data_type) def normalize_array(array: np.ndarray, cap: float = 1) -> np.ndarray: @@ -43,44 +54,75 @@ def normalize_array(array: np.ndarray, cap: float = 1) -> np.ndarray: array: List containing values to be normalized between cap range. cap: Maximum cap amount for normalization. Returns: - return 1D numpy array , corresponding to limited range array + return 1D numpy array, corresponding to limited range array + + Examples: + >>> normalize_array(np.array([2, 3, 5, 7])) + array([0. , 0.2, 0.6, 1. ]) + >>> normalize_array(np.array([[5], [7], [11], [13]])) + array([[0. ], + [0.25], + [0.75], + [1. ]]) """ - normalized = (array - np.min(array)) / (np.max(array) - np.min(array)) * cap - return normalized + return (array - np.min(array)) / (np.max(array) - np.min(array)) * cap def grayscale(image: np.ndarray) -> np.ndarray: - """Uses luminance weights to transform RGB channel to greyscale, by - taking the dot product between the channel and the weights.""" + """ + Uses luminance weights to transform RGB channel to greyscale, by + taking the dot product between the channel and the weights. + + Example: + >>> grayscale(np.array([[[108, 201, 72], [255, 11, 127]], [[56, 56, 56], [128, 255, 107]]])) + array([[158, 97], + [ 56, 200]], dtype=uint8) + """ return np.dot(image[:, :, 0:3], [0.299, 0.587, 0.114]).astype(np.uint8) -def binarize(image: np.ndarray, threshold_value) -> np.ndarray: +def binarize(image: np.ndarray, threshold: float = 127.0) -> np.ndarray: """ Binarizes a grayscale image based on a given threshold value, setting values to 1 or 0 accordingly. - """ - binarized = np.where(image > threshold_value, 1, 0) - return binarized + Examples: + >>> binarize(np.array([[128, 255], [101, 156]])) + array([[1, 1], + [0, 1]]) + >>> binarize(np.array([[0.07, 1], [0.51, 0.3]]), threshold=0.5) + array([[0, 1], + [1, 0]]) + """ + return np.where(image > threshold, 1, 0) -def transform(image: np.ndarray, kind, kernel=None) -> np.ndarray: +def transform(image: np.ndarray, kind: str, kernel=None) -> np.ndarray: """ Simple image transformation using one of two available filter functions: Erosion and Dilation. Args: - image: + image: binarized input image, onto which to apply transformation kind: Can be either 'erosion', in which case the :func:np.max function is called, or 'dilation', when :func:np.min is used instead. kernel: n x n kernel with shape < :attr:image.shape, to be used when applying convolution to original image Returns: - + returns a numpy array with same shape as input image, corresponding to applied binary transformation. + + Examples: + >>> img = np.array([[1, 0.5], [0.2, 0.7]]) + >>> img = binarize(img, threshold=0.5) + >>> transform(img, 'erosion') + array([[1, 1], + [1, 1]], dtype=uint8) + >>> transform(img, 'dilation') + array([[0, 0], + [0, 0]], dtype=uint8) """ - if kernel is None: + if not kernel: kernel = np.ones((3, 3)) if kind == "erosion": @@ -100,8 +142,8 @@ def transform(image: np.ndarray, kind, kernel=None) -> np.ndarray: for x in range(center_x, padded.shape[0] - center_x): for y in range(center_y, padded.shape[1] - center_y): center = padded[ - x - center_x : x + center_x + 1, y - center_y : y + center_y + 1 - ] + x - center_x: x + center_x + 1, y - center_y: y + center_y + 1 + ] # Apply transformation method to the centered section of the image transformed[x - center_x, y - center_y] = apply(center[kernel == 1]) @@ -112,8 +154,15 @@ def opening_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: """ Opening filter, defined as the sequence of erosion and then a dilation filter on the same image. + + Examples: + >>> img = np.array([[1, 0.5], [0.2, 0.7]]) + >>> img = binarize(img, threshold=0.5) + >>> opening_filter(img) + array([[1, 1], + [1, 1]], dtype=uint8) """ - if kernel is None: + if not kernel: np.ones((3, 3)) return transform(transform(image, "dilation", kernel), "erosion", kernel) @@ -123,6 +172,13 @@ def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: """ Opening filter, defined as the sequence of dilation and then erosion filter on the same image. + + Examples: + >>> img = np.array([[1, 0.5], [0.2, 0.7]]) + >>> img = binarize(img, threshold=0.5) + >>> closing_filter(img) + array([[0, 0], + [0, 0]], dtype=uint8) """ if kernel is None: np.ones((3, 3)) @@ -130,10 +186,24 @@ def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: return transform(transform(image, "erosion", kernel), "dilation", kernel) -def binary_mask(image_gray: np.ndarray, image_map: np.ndarray) -> np.ndarray: +def binary_mask( + image_gray: np.ndarray, image_map: np.ndarray +) -> tuple[np.ndarray, np.ndarray]: """ Apply binary mask, or thresholding based - on bit mask value (mapping mask is 1 or 0). + on bit mask value (mapping mask is binary). + + Returns the mapped true value mask and its complementary false value mask. + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> binary_mask(gray, morphological) + (array([[1, 1], + [1, 1]], dtype=uint8), array([[158, 97], + [ 56, 200]], dtype=uint8)) """ true_mask, false_mask = np.array(image_gray, copy=True), np.array( image_gray, copy=True @@ -144,13 +214,16 @@ def binary_mask(image_gray: np.ndarray, image_map: np.ndarray) -> np.ndarray: return true_mask, false_mask -def matrix_concurrency(image: np.ndarray, coordinate) -> np.ndarray: +def matrix_concurrency( + image: np.ndarray, coordinate +) -> np.ndarray: """ Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. + Implementation is made using basic iteration, as function to be performed (np.max) is non-linear and therefore - not usable on the Fourier Transform domain. + not callable on the frequency domain. """ matrix = np.zeros([np.max(image) + 1, np.max(image) + 1]) @@ -180,7 +253,7 @@ def haralick_descriptors(matrix: np.ndarray) -> list: """ # Function np.indices could be used for bigger input types, # but np.ogrid works just fine - i, j = np.ogrid[0 : matrix.shape[0], 0 : matrix.shape[1]] # np.indices() + i, j = np.ogrid[0: matrix.shape[0], 0: matrix.shape[1]] # np.indices() # Pre-calculate frequent multiplication and subtraction prod = np.multiply(i, j) @@ -200,7 +273,7 @@ def haralick_descriptors(matrix: np.ndarray) -> list: # Sum values for descriptors ranging from the first one to the last, # as all are their respective origin matrix and not the resulting value yet. - descriptors = [ + return [ maximum_prob, correlation.sum(), energy.sum(), @@ -210,10 +283,9 @@ def haralick_descriptors(matrix: np.ndarray) -> list: homogeneity.sum(), entropy.sum(), ] - return descriptors -def get_descriptors(masks, coordinate) -> np.ndarray: +def get_descriptors(masks: tuple[np.ndarray, np.ndarray], coordinate) -> np.ndarray: """ Calculate all Haralick descriptors for a sequence of different co-occurrence matrices, given input masks and coordinates. @@ -231,11 +303,17 @@ def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: """ Simple method for calculating the euclidean distance between two points, with type np.ndarray. + + Example: + >>> a = np.array([1, 0, -2]) + >>> b = np.array([2, -1, 1]) + >>> euclidean(a, b) + 3.3166247903554 """ return np.sqrt(np.sum(np.square(point_1 - point_2))) -def get_distances(descriptors, base) -> np.ndarray: +def get_distances(descriptors, base) -> list[Any]: """ Calculate all Euclidean distances between a selected base descriptor and all other Haralick descriptors @@ -271,7 +349,7 @@ def main(): # Number of images to perform methods on b_number = int(input()) - files, descriptors = (np.array([]), np.array([])) + files, descriptors = ([], []) for _ in range(b_number): file = input().rstrip() @@ -293,7 +371,7 @@ def main(): # Transform ordered distances array into a sequence of indexes # corresponding to original file position - distances = get_distances(descriptors, index) + distances = get_distances(np.array(descriptors), index) indexed_distances = np.array(distances).astype(np.uint8)[:, 0] # Finally, print distances considering the Haralick descriptions from the base From eca7118ed98f92afa8b932def0712677de873f92 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 27 Nov 2022 16:49:55 +0000 Subject: [PATCH 05/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- computer_vision/haralick_descriptors.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 8cf0d702ce32..44c23e3f05a8 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -23,7 +23,9 @@ def root_mean_square_error(original: np.ndarray, reference: np.ndarray) -> float return np.sqrt(((original - reference) ** 2).mean()) -def normalize_image(image: np.ndarray, cap: float = 255.0, data_type=np.uint8) -> np.ndarray: +def normalize_image( + image: np.ndarray, cap: float = 255.0, data_type=np.uint8 +) -> np.ndarray: """ Normalizes image in Numpy 2D array format, between ranges 0-cap, as to fit uint8 type. @@ -142,8 +144,8 @@ def transform(image: np.ndarray, kind: str, kernel=None) -> np.ndarray: for x in range(center_x, padded.shape[0] - center_x): for y in range(center_y, padded.shape[1] - center_y): center = padded[ - x - center_x: x + center_x + 1, y - center_y: y + center_y + 1 - ] + x - center_x : x + center_x + 1, y - center_y : y + center_y + 1 + ] # Apply transformation method to the centered section of the image transformed[x - center_x, y - center_y] = apply(center[kernel == 1]) @@ -187,7 +189,7 @@ def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: def binary_mask( - image_gray: np.ndarray, image_map: np.ndarray + image_gray: np.ndarray, image_map: np.ndarray ) -> tuple[np.ndarray, np.ndarray]: """ Apply binary mask, or thresholding based @@ -214,9 +216,7 @@ def binary_mask( return true_mask, false_mask -def matrix_concurrency( - image: np.ndarray, coordinate -) -> np.ndarray: +def matrix_concurrency(image: np.ndarray, coordinate) -> np.ndarray: """ Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. @@ -253,7 +253,7 @@ def haralick_descriptors(matrix: np.ndarray) -> list: """ # Function np.indices could be used for bigger input types, # but np.ogrid works just fine - i, j = np.ogrid[0: matrix.shape[0], 0: matrix.shape[1]] # np.indices() + i, j = np.ogrid[0 : matrix.shape[0], 0 : matrix.shape[1]] # np.indices() # Pre-calculate frequent multiplication and subtraction prod = np.multiply(i, j) From 9d2cca7441d9e5351ed5b915b89afc87ad9ca989 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Sun, 27 Nov 2022 13:54:33 -0300 Subject: [PATCH 06/24] Fixed line size formatting --- computer_vision/haralick_descriptors.py | 30 ++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 8cf0d702ce32..b85058bc7b60 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -2,7 +2,7 @@ https://en.wikipedia.org/wiki/Image_texture https://en.wikipedia.org/wiki/Co-occurrence_matrix#Application_to_image_analysis """ -from typing import Any, Union +from typing import Any import imageio.v2 as imageio import numpy as np @@ -23,7 +23,9 @@ def root_mean_square_error(original: np.ndarray, reference: np.ndarray) -> float return np.sqrt(((original - reference) ** 2).mean()) -def normalize_image(image: np.ndarray, cap: float = 255.0, data_type=np.uint8) -> np.ndarray: +def normalize_image( + image: np.ndarray, cap: float = 255.0, data_type=np.uint8 +) -> np.ndarray: """ Normalizes image in Numpy 2D array format, between ranges 0-cap, as to fit uint8 type. @@ -36,7 +38,8 @@ def normalize_image(image: np.ndarray, cap: float = 255.0, data_type=np.uint8) - return 2D numpy array of type uint8, corresponding to limited range matrix Examples: - >>> normalize_image(np.array([[1, 2, 3], [4, 5, 10]]), cap=1.0, data_type=np.float64) + >>> normalize_image(np.array([[1, 2, 3], [4, 5, 10]]), + ... cap=1.0, data_type=np.float64) array([[0. , 0.11111111, 0.22222222], [0.33333333, 0.44444444, 1. ]]) >>> normalize_image(np.array([[4, 4, 3], [1, 7, 2]])) @@ -74,7 +77,8 @@ def grayscale(image: np.ndarray) -> np.ndarray: taking the dot product between the channel and the weights. Example: - >>> grayscale(np.array([[[108, 201, 72], [255, 11, 127]], [[56, 56, 56], [128, 255, 107]]])) + >>> grayscale(np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]])) array([[158, 97], [ 56, 200]], dtype=uint8) """ @@ -110,7 +114,8 @@ def transform(image: np.ndarray, kind: str, kernel=None) -> np.ndarray: to be used when applying convolution to original image Returns: - returns a numpy array with same shape as input image, corresponding to applied binary transformation. + returns a numpy array with same shape as input image, + corresponding to applied binary transformation. Examples: >>> img = np.array([[1, 0.5], [0.2, 0.7]]) @@ -142,8 +147,8 @@ def transform(image: np.ndarray, kind: str, kernel=None) -> np.ndarray: for x in range(center_x, padded.shape[0] - center_x): for y in range(center_y, padded.shape[1] - center_y): center = padded[ - x - center_x: x + center_x + 1, y - center_y: y + center_y + 1 - ] + x - center_x : x + center_x + 1, y - center_y : y + center_y + 1 + ] # Apply transformation method to the centered section of the image transformed[x - center_x, y - center_y] = apply(center[kernel == 1]) @@ -187,7 +192,7 @@ def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: def binary_mask( - image_gray: np.ndarray, image_map: np.ndarray + image_gray: np.ndarray, image_map: np.ndarray ) -> tuple[np.ndarray, np.ndarray]: """ Apply binary mask, or thresholding based @@ -196,7 +201,8 @@ def binary_mask( Returns the mapped true value mask and its complementary false value mask. Example: - >>> img = np.array([[[108, 201, 72], [255, 11, 127]], [[56, 56, 56], [128, 255, 107]]]) + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) >>> gray = grayscale(img) >>> binary = binarize(gray) >>> morphological = opening_filter(binary) @@ -214,9 +220,7 @@ def binary_mask( return true_mask, false_mask -def matrix_concurrency( - image: np.ndarray, coordinate -) -> np.ndarray: +def matrix_concurrency(image: np.ndarray, coordinate) -> np.ndarray: """ Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. @@ -253,7 +257,7 @@ def haralick_descriptors(matrix: np.ndarray) -> list: """ # Function np.indices could be used for bigger input types, # but np.ogrid works just fine - i, j = np.ogrid[0: matrix.shape[0], 0: matrix.shape[1]] # np.indices() + i, j = np.ogrid[0 : matrix.shape[0], 0 : matrix.shape[1]] # np.indices() # Pre-calculate frequent multiplication and subtraction prod = np.multiply(i, j) From 5e73aa6cc20e01827a0fee673c0df06770502583 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Sun, 27 Nov 2022 14:21:10 -0300 Subject: [PATCH 07/24] Added final doctests --- computer_vision/haralick_descriptors.py | 61 +++++++++++++++++++++---- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index b85058bc7b60..19272673412c 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -24,7 +24,7 @@ def root_mean_square_error(original: np.ndarray, reference: np.ndarray) -> float def normalize_image( - image: np.ndarray, cap: float = 255.0, data_type=np.uint8 + image: np.ndarray, cap: float = 255.0, data_type: np.dtype = np.uint8 ) -> np.ndarray: """ Normalizes image in Numpy 2D array format, between ranges 0-cap, @@ -68,7 +68,8 @@ def normalize_array(array: np.ndarray, cap: float = 1) -> np.ndarray: [0.75], [1. ]]) """ - return (array - np.min(array)) / (np.max(array) - np.min(array)) * cap + diff = np.max(array) - np.min(array) + return (array - np.min(array)) / (1 if diff == 0 else diff) * cap def grayscale(image: np.ndarray) -> np.ndarray: @@ -101,7 +102,7 @@ def binarize(image: np.ndarray, threshold: float = 127.0) -> np.ndarray: return np.where(image > threshold, 1, 0) -def transform(image: np.ndarray, kind: str, kernel=None) -> np.ndarray: +def transform(image: np.ndarray, kind: str, kernel: np.ndarray = None) -> np.ndarray: """ Simple image transformation using one of two available filter functions: Erosion and Dilation. @@ -220,7 +221,7 @@ def binary_mask( return true_mask, false_mask -def matrix_concurrency(image: np.ndarray, coordinate) -> np.ndarray: +def matrix_concurrency(image: np.ndarray, coordinate: list) -> np.ndarray: """ Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. @@ -228,6 +229,17 @@ def matrix_concurrency(image: np.ndarray, coordinate) -> np.ndarray: Implementation is made using basic iteration, as function to be performed (np.max) is non-linear and therefore not callable on the frequency domain. + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> mask_1 = binary_mask(gray, morphological)[0] + >>> matrix_concurrency(mask_1, [0, 1]) + array([[0., 0.], + [0., 0.]]) """ matrix = np.zeros([np.max(image) + 1, np.max(image) + 1]) @@ -239,8 +251,8 @@ def matrix_concurrency(image: np.ndarray, coordinate) -> np.ndarray: offset_pixel = image[x + offset_x, y + offset_y] matrix[base_pixel, offset_pixel] += 1 - - return matrix / np.sum(matrix) + matrix_sum = np.sum(matrix) + return matrix / (1 if matrix_sum == 0 else matrix_sum) def haralick_descriptors(matrix: np.ndarray) -> list: @@ -254,6 +266,17 @@ def haralick_descriptors(matrix: np.ndarray) -> list: Returns: Reverse ordered list of resulting descriptors + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> mask_1 = binary_mask(gray, morphological)[0] + >>> concurrency = matrix_concurrency(mask_1, [0, 1]) + >>> haralick_descriptors(concurrency) + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] """ # Function np.indices could be used for bigger input types, # but np.ogrid works just fine @@ -289,10 +312,21 @@ def haralick_descriptors(matrix: np.ndarray) -> list: ] -def get_descriptors(masks: tuple[np.ndarray, np.ndarray], coordinate) -> np.ndarray: +def get_descriptors( + masks: tuple[np.ndarray, np.ndarray], coordinate: list +) -> np.ndarray: """ Calculate all Haralick descriptors for a sequence of different co-occurrence matrices, given input masks and coordinates. + + Example: + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> get_descriptors(binary_mask(gray, morphological), [0, 1]) + array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) """ descriptors = np.zeros((len(masks), 8)) for idx, mask in enumerate(masks): @@ -317,7 +351,7 @@ def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: return np.sqrt(np.sum(np.square(point_1 - point_2))) -def get_distances(descriptors, base) -> list[Any]: +def get_distances(descriptors: np.ndarray, base: int) -> list[Any]: """ Calculate all Euclidean distances between a selected base descriptor and all other Haralick descriptors @@ -331,6 +365,17 @@ def get_distances(descriptors, base) -> list[Any]: Returns: Ordered distances between descriptors + + Example: + >>> index = 1 + >>> img = np.array([[[108, 201, 72], [255, 11, 127]], + ... [[56, 56, 56], [128, 255, 107]]]) + >>> gray = grayscale(img) + >>> binary = binarize(gray) + >>> morphological = opening_filter(binary) + >>> get_distances(get_descriptors(binary_mask(gray, morphological), [0, 1]), index) + [(0, 0.0), (1, 0.0), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), (6, 0.0), (7, 0.0), (8, 0.0), \ +(9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), (13, 0.0), (14, 0.0), (15, 0.0)] """ distances = np.zeros(descriptors.shape[0]) From aacb27e201d54445f073c41354456edbc042989a Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Sun, 27 Nov 2022 14:28:33 -0300 Subject: [PATCH 08/24] Changed main callable --- computer_vision/haralick_descriptors.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 19272673412c..945de2d97023 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -373,9 +373,12 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[Any]: >>> gray = grayscale(img) >>> binary = binarize(gray) >>> morphological = opening_filter(binary) - >>> get_distances(get_descriptors(binary_mask(gray, morphological), [0, 1]), index) - [(0, 0.0), (1, 0.0), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), (6, 0.0), (7, 0.0), (8, 0.0), \ -(9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), (13, 0.0), (14, 0.0), (15, 0.0)] + >>> get_distances(get_descriptors( + ... binary_mask(gray, morphological), [0, 1]), + ... index) + [(0, 0.0), (1, 0.0), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), \ +(6, 0.0), (7, 0.0), (8, 0.0), (9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), \ +(13, 0.0), (14, 0.0), (15, 0.0)] """ distances = np.zeros(descriptors.shape[0]) @@ -386,7 +389,7 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[Any]: return sorted(enumerate(distances), key=lambda tup: tup[1]) -def main(): +if __name__ == "__main__": # Index to compare haralick descriptors to index = int(input()) q_value = [int(value) for value in input().split()] @@ -429,7 +432,3 @@ def main(): print("Ranking:") for idx, file_idx in enumerate(indexed_distances): print(f"({idx}) {files[file_idx]}", end="\n") - - -if __name__ == "__main__": - main() From 5fb933d057e4f28daa711dae4b947dd39d2fa551 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Sun, 27 Nov 2022 14:29:21 -0300 Subject: [PATCH 09/24] Updated requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index a1d607df07e1..038e1cb3dbf1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,3 +20,4 @@ texttable tweepy xgboost yulewalker +imageio From c9ec63ef7dce4448021a3d0a46b77a2fa0f9db59 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 27 Nov 2022 17:30:39 +0000 Subject: [PATCH 10/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 038e1cb3dbf1..bb060edb5eba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ beautifulsoup4 fake_useragent +imageio keras lxml matplotlib @@ -20,4 +21,3 @@ texttable tweepy xgboost yulewalker -imageio From bfd0fd7e173e562201f887ec5fe64b3b914b8f56 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Tue, 13 Dec 2022 17:51:14 -0300 Subject: [PATCH 11/24] Update computer_vision/haralick_descriptors.py No! What if the Kernel is empty? Example: >>> kernel = np.zeros((1)) >>> kernel or np.ones((3, 3)) array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) Co-authored-by: Christian Clauss --- computer_vision/haralick_descriptors.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 945de2d97023..2e1036a3e236 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -186,9 +186,7 @@ def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: array([[0, 0], [0, 0]], dtype=uint8) """ - if kernel is None: - np.ones((3, 3)) - + kernel = kernel or np.ones((3, 3)) return transform(transform(image, "erosion", kernel), "dilation", kernel) From 6f52bff54a76f7e9572d25a68e6a3860ddeb0455 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Tue, 13 Dec 2022 17:52:38 -0300 Subject: [PATCH 12/24] Undone wrong commit --- computer_vision/haralick_descriptors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 2e1036a3e236..db9c7a7e869d 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -186,7 +186,8 @@ def closing_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: array([[0, 0], [0, 0]], dtype=uint8) """ - kernel = kernel or np.ones((3, 3)) + if kernel is None: + kernel = np.ones((3, 3)) return transform(transform(image, "erosion", kernel), "dilation", kernel) From fc2fa0bbad51e1b479c75fc96f2e8bf63a617094 Mon Sep 17 00:00:00 2001 From: Rafael Zimmer Date: Mon, 19 Dec 2022 22:43:29 -0300 Subject: [PATCH 13/24] Update haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index db9c7a7e869d..8c9afb4d0e88 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -128,7 +128,7 @@ def transform(image: np.ndarray, kind: str, kernel: np.ndarray = None) -> np.nda array([[0, 0], [0, 0]], dtype=uint8) """ - if not kernel: + if kernel is None: kernel = np.ones((3, 3)) if kind == "erosion": @@ -168,7 +168,7 @@ def opening_filter(image: np.ndarray, kernel: np.ndarray = None) -> np.ndarray: array([[1, 1], [1, 1]], dtype=uint8) """ - if not kernel: + if kernel is None: np.ones((3, 3)) return transform(transform(image, "dilation", kernel), "erosion", kernel) From 40da555a70b5e57c7047ddc0ef788a281d0703b5 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:12:40 -0400 Subject: [PATCH 14/24] Apply suggestions from code review --- computer_vision/haralick_descriptors.py | 41 ++++++++++++------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 8c9afb4d0e88..5a0a6baaa7bb 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -2,8 +2,6 @@ https://en.wikipedia.org/wiki/Image_texture https://en.wikipedia.org/wiki/Co-occurrence_matrix#Application_to_image_analysis """ -from typing import Any - import imageio.v2 as imageio import numpy as np @@ -211,16 +209,14 @@ def binary_mask( [1, 1]], dtype=uint8), array([[158, 97], [ 56, 200]], dtype=uint8)) """ - true_mask, false_mask = np.array(image_gray, copy=True), np.array( - image_gray, copy=True - ) + true_mask, false_mask = image_gray.copy(), image_gray.copy() true_mask[image_map == 1] = 1 false_mask[image_map == 0] = 0 return true_mask, false_mask -def matrix_concurrency(image: np.ndarray, coordinate: list) -> np.ndarray: +def matrix_concurrency(image: np.ndarray, coordinate: tuple[int, int]) -> np.ndarray: """ Calculate sample co-occurrence matrix based on input image as well as selected coordinates on image. @@ -236,13 +232,13 @@ def matrix_concurrency(image: np.ndarray, coordinate: list) -> np.ndarray: >>> binary = binarize(gray) >>> morphological = opening_filter(binary) >>> mask_1 = binary_mask(gray, morphological)[0] - >>> matrix_concurrency(mask_1, [0, 1]) + >>> matrix_concurrency(mask_1, (0, 1)) array([[0., 0.], [0., 0.]]) """ matrix = np.zeros([np.max(image) + 1, np.max(image) + 1]) - offset_x, offset_y = coordinate[0], coordinate[1] + offset_x, offset_y = coordinate for x in range(1, image.shape[0] - 1): for y in range(1, image.shape[1] - 1): @@ -254,7 +250,7 @@ def matrix_concurrency(image: np.ndarray, coordinate: list) -> np.ndarray: return matrix / (1 if matrix_sum == 0 else matrix_sum) -def haralick_descriptors(matrix: np.ndarray) -> list: +def haralick_descriptors(matrix: np.ndarray) -> list[float]: """Calculates all 8 Haralick descriptors based on co-occurence input matrix. All descriptors are as follows: Maximum probability, Inverse Difference, Homogeneity, Entropy, @@ -273,7 +269,7 @@ def haralick_descriptors(matrix: np.ndarray) -> list: >>> binary = binarize(gray) >>> morphological = opening_filter(binary) >>> mask_1 = binary_mask(gray, morphological)[0] - >>> concurrency = matrix_concurrency(mask_1, [0, 1]) + >>> concurrency = matrix_concurrency(mask_1, (0, 1)) >>> haralick_descriptors(concurrency) [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] """ @@ -312,7 +308,7 @@ def haralick_descriptors(matrix: np.ndarray) -> list: def get_descriptors( - masks: tuple[np.ndarray, np.ndarray], coordinate: list + masks: tuple[np.ndarray, np.ndarray], coordinate: tuple[int, int] ) -> np.ndarray: """ Calculate all Haralick descriptors for a sequence of @@ -324,12 +320,13 @@ def get_descriptors( >>> gray = grayscale(img) >>> binary = binarize(gray) >>> morphological = opening_filter(binary) - >>> get_descriptors(binary_mask(gray, morphological), [0, 1]) + >>> get_descriptors(binary_mask(gray, morphological), (0, 1)) array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) """ - descriptors = np.zeros((len(masks), 8)) - for idx, mask in enumerate(masks): - descriptors[idx] = haralick_descriptors(matrix_concurrency(mask, coordinate)) + descriptors = np.array([ + haralick_descriptors(matrix_concurrency(mask, coordinate)) + for mask in masks + ]) # Concatenate each individual descriptor into # one single list containing sequence of descriptors @@ -350,7 +347,7 @@ def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: return np.sqrt(np.sum(np.square(point_1 - point_2))) -def get_distances(descriptors: np.ndarray, base: int) -> list[Any]: +def get_distances(descriptors: np.ndarray, base: int) -> list[float]: """ Calculate all Euclidean distances between a selected base descriptor and all other Haralick descriptors @@ -373,16 +370,16 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[Any]: >>> binary = binarize(gray) >>> morphological = opening_filter(binary) >>> get_distances(get_descriptors( - ... binary_mask(gray, morphological), [0, 1]), + ... binary_mask(gray, morphological), (0, 1)), ... index) [(0, 0.0), (1, 0.0), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), \ (6, 0.0), (7, 0.0), (8, 0.0), (9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), \ (13, 0.0), (14, 0.0), (15, 0.0)] """ - distances = np.zeros(descriptors.shape[0]) - - for idx, description in enumerate(descriptors): - distances[idx] = euclidean(description, descriptors[base]) + distances = np.array([ + euclidean(descriptor, descriptors[base]) + for descriptor in descriptors + ]) # Normalize distances between range [0, 1] distances = normalize_array(distances, 1) return sorted(enumerate(distances), key=lambda tup: tup[1]) @@ -400,7 +397,7 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[Any]: # Number of images to perform methods on b_number = int(input()) - files, descriptors = ([], []) + files, descriptors = [], [] for _ in range(b_number): file = input().rstrip() From ba2f47420e4c4f132f96eb83559fbac0d95d5546 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 03:13:06 +0000 Subject: [PATCH 15/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- computer_vision/haralick_descriptors.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 5a0a6baaa7bb..38675bf348c7 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -323,10 +323,9 @@ def get_descriptors( >>> get_descriptors(binary_mask(gray, morphological), (0, 1)) array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) """ - descriptors = np.array([ - haralick_descriptors(matrix_concurrency(mask, coordinate)) - for mask in masks - ]) + descriptors = np.array( + [haralick_descriptors(matrix_concurrency(mask, coordinate)) for mask in masks] + ) # Concatenate each individual descriptor into # one single list containing sequence of descriptors @@ -376,10 +375,9 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: (6, 0.0), (7, 0.0), (8, 0.0), (9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), \ (13, 0.0), (14, 0.0), (15, 0.0)] """ - distances = np.array([ - euclidean(descriptor, descriptors[base]) - for descriptor in descriptors - ]) + distances = np.array( + [euclidean(descriptor, descriptors[base]) for descriptor in descriptors] + ) # Normalize distances between range [0, 1] distances = normalize_array(distances, 1) return sorted(enumerate(distances), key=lambda tup: tup[1]) From 9faef3687ac5b956742b3e573ea594c746765dc0 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:19:52 -0400 Subject: [PATCH 16/24] Fix ruff errors in haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 38675bf348c7..bc9498112d05 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -380,13 +380,13 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: ) # Normalize distances between range [0, 1] distances = normalize_array(distances, 1) - return sorted(enumerate(distances), key=lambda tup: tup[1]) + return sorted(distances) if __name__ == "__main__": # Index to compare haralick descriptors to index = int(input()) - q_value = [int(value) for value in input().split()] + q_value = tuple(int(value) for value in input().split()) # Format is the respective filter to apply, # can be either 1 for the opening filter or else for the closing From 7eda2b8c8bce2c2355cd28fc66bed40c2b19e341 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:21:49 -0400 Subject: [PATCH 17/24] Add type hint to haralick_descriptors.py to fix ruff error --- computer_vision/haralick_descriptors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index bc9498112d05..fae2accef074 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -386,7 +386,7 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: if __name__ == "__main__": # Index to compare haralick descriptors to index = int(input()) - q_value = tuple(int(value) for value in input().split()) + q_value: tuple[int, int] = tuple(int(value) for value in input().split()) # Format is the respective filter to apply, # can be either 1 for the opening filter or else for the closing From fd085df02be97b2de241fc94d0ac439c89fb95e4 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:24:55 -0400 Subject: [PATCH 18/24] Update haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index fae2accef074..f2eeb931e89e 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -386,7 +386,7 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: if __name__ == "__main__": # Index to compare haralick descriptors to index = int(input()) - q_value: tuple[int, int] = tuple(int(value) for value in input().split()) + q_value: tuple[int, int] = tuple([int(value) for value in input().split()][:2]) # Format is the respective filter to apply, # can be either 1 for the opening filter or else for the closing From 40daa68f8c693c28f6e3bad4f44eef385f31e2d3 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:34:14 -0400 Subject: [PATCH 19/24] Update haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index f2eeb931e89e..9176cb52fe64 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -380,13 +380,14 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: ) # Normalize distances between range [0, 1] distances = normalize_array(distances, 1) - return sorted(distances) + return sorted(list(enumerate(distances)), key=lambda tup: tup[1]) if __name__ == "__main__": # Index to compare haralick descriptors to index = int(input()) - q_value: tuple[int, int] = tuple([int(value) for value in input().split()][:2]) + q_value = [int(value) for value in input().split()] + q_value = (q_value[0], q_value[1]) # Format is the respective filter to apply, # can be either 1 for the opening filter or else for the closing From 8da35ebb2c5de00f2b983139f3f40334628fafd2 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:37:53 -0400 Subject: [PATCH 20/24] Update haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 9176cb52fe64..54fb5289d6e0 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -380,7 +380,8 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: ) # Normalize distances between range [0, 1] distances = normalize_array(distances, 1) - return sorted(list(enumerate(distances)), key=lambda tup: tup[1]) + distances = list(enumerate(distances)) + return sorted(distances, key=lambda tup: tup[1]) if __name__ == "__main__": From 5795fc4b5146d5f5e36045d4cbb58514cb856572 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:41:47 -0400 Subject: [PATCH 21/24] Update haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 54fb5289d6e0..955ca51657df 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -380,15 +380,15 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: ) # Normalize distances between range [0, 1] distances = normalize_array(distances, 1) - distances = list(enumerate(distances)) - return sorted(distances, key=lambda tup: tup[1]) + enum_distances = list(enumerate(distances)) + return sorted(enum_distances, key=lambda tup: tup[1]) if __name__ == "__main__": # Index to compare haralick descriptors to index = int(input()) - q_value = [int(value) for value in input().split()] - q_value = (q_value[0], q_value[1]) + q_value_list = [int(value) for value in input().split()] + q_value = (q_value_list[0], q_value_list[1]) # Format is the respective filter to apply, # can be either 1 for the opening filter or else for the closing From 44d6bd14cdae79316da295f887139c1e32f5bc94 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:48:07 -0400 Subject: [PATCH 22/24] Try to fix mypy errors in haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 955ca51657df..7bf6035aa90c 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -379,7 +379,7 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: [euclidean(descriptor, descriptors[base]) for descriptor in descriptors] ) # Normalize distances between range [0, 1] - distances = normalize_array(distances, 1) + distances: list[float] = normalize_array(distances, 1).tolist() enum_distances = list(enumerate(distances)) return sorted(enum_distances, key=lambda tup: tup[1]) From 21efc62e481fcb9d5ed2533b2e005499a691c734 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:55:23 -0400 Subject: [PATCH 23/24] Update haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 7bf6035aa90c..6f1f252e2766 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -379,9 +379,10 @@ def get_distances(descriptors: np.ndarray, base: int) -> list[float]: [euclidean(descriptor, descriptors[base]) for descriptor in descriptors] ) # Normalize distances between range [0, 1] - distances: list[float] = normalize_array(distances, 1).tolist() - enum_distances = list(enumerate(distances)) - return sorted(enum_distances, key=lambda tup: tup[1]) + normalized_distances: list[float] = normalize_array(distances, 1).tolist() + enum_distances = list(enumerate(normalized_distances)) + enum_distances.sort(key=lambda tup: tup[1], reverse=True) + return enum_distances if __name__ == "__main__": From 5fa7520fcec21fddd7c0f53a42baca23ee2c33c5 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 4 Sep 2023 23:58:35 -0400 Subject: [PATCH 24/24] Fix type hint in haralick_descriptors.py --- computer_vision/haralick_descriptors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/computer_vision/haralick_descriptors.py b/computer_vision/haralick_descriptors.py index 6f1f252e2766..1a86d84ea14b 100644 --- a/computer_vision/haralick_descriptors.py +++ b/computer_vision/haralick_descriptors.py @@ -346,7 +346,7 @@ def euclidean(point_1: np.ndarray, point_2: np.ndarray) -> np.float32: return np.sqrt(np.sum(np.square(point_1 - point_2))) -def get_distances(descriptors: np.ndarray, base: int) -> list[float]: +def get_distances(descriptors: np.ndarray, base: int) -> list[tuple[int, float]]: """ Calculate all Euclidean distances between a selected base descriptor and all other Haralick descriptors