diff --git a/assignment3/solution.py b/assignment3/solution.py index 3eb9d05..7dba86d 100644 --- a/assignment3/solution.py +++ b/assignment3/solution.py @@ -10,9 +10,9 @@ import os ############################################## def ex1(): - #one_a() - #one_b() - #one_c() + one_a() + one_b() + one_c() one_e() def one_a() -> None: @@ -153,11 +153,11 @@ def one_e(): """ hello """ - museum = uz_image.imread('./images/museum.jpg', uz_image.ImageType.float64) + #museum = uz_image.imread('./images/museum.jpg', uz_image.ImageType.float64) #uz_image.get_image_bins_gradient_magnitude_and_angles(museum) - #ex2_naive('../assignment2/data/dataset/', 8) + ex2_naive('../assignment2/data/dataset/', 8) ex2_optimized('../assignment2/data/dataset/', 8) @@ -270,11 +270,10 @@ def ex2_optimized(directory: str, n_bins: int): ############################################ def ex2(): - #two_a() + two_a() two_b() two_c() - def two_a(): """ Firstly, create a function findedges that accepts an image I, and the parameters @@ -346,9 +345,10 @@ def ex3(): #three_a() #three_b() #three_c() - #three_d() - #three_e() + three_d() + three_e() three_f() + #three_g() def three_a(): """ @@ -507,33 +507,30 @@ def three_d(): fig, axs = plt.subplots(1, 3) - - def select_best_pairs(image_line_params: npt.NDArray[np.float64], n =10): - image_line_params = np.array(image_line_params) - # Sorts just kth element so every eleement before kth element is lower than kth element - # and every element after kth element is higher than kth element - partition = np.argpartition(image_line_params, kth=len(image_line_params) - n - 1, axis=0)[-n:] - image_line_params = image_line_params[partition.T[0]] - return image_line_params - # Plot synthetic image axs[0].imshow(synthetic_image, cmap='gray') - neighbour_pairs = uz_image.retrieve_hough_pairs(synthetic_image, synthetic_image_hough_nonmax, np.max(synthetic_image_hough_nonmax)*0.99, N_BINS_THETA, N_BINS_RHO) - for neighbour in neighbour_pairs: + neighbour_pairs = uz_image.retrieve_hough_pairs(synthetic_image, synthetic_image_hough_nonmax, np.max(synthetic_image_hough_nonmax)*0.80, N_BINS_THETA, N_BINS_RHO) + + best_paris = uz_image.select_best_pairs(neighbour_pairs) + + for neighbour in best_paris: xs, ys = uz_image.get_line_to_plot(neighbour[0], neighbour[1], synthetic_image.shape[0], synthetic_image.shape[1]) axs[0].plot(xs, ys, 'r', linewidth=0.7) # Plot oneline image axs[1].imshow(oneline_image, cmap='gray') - neighbour_pairs = uz_image.retrieve_hough_pairs(oneline_image, oneline_image_hough_nonmax, np.max(oneline_image_hough_nonmax)*0.5, N_BINS_THETA, N_BINS_RHO) - for neighbour in neighbour_pairs: + neighbour_pairs = uz_image.retrieve_hough_pairs(oneline_image, oneline_image_hough_nonmax, np.max(oneline_image_hough_nonmax)*0.4, N_BINS_THETA, N_BINS_RHO) + + best_paris = uz_image.select_best_pairs(neighbour_pairs) + + for neighbour in best_paris: xs, ys = uz_image.get_line_to_plot(neighbour[0], neighbour[1], oneline_image.shape[0], oneline_image.shape[1]) axs[1].plot(xs, ys, 'r', linewidth=0.7) # Plot rectangle image axs[2].imshow(rectangle_image, cmap='gray') neighbour_pairs = uz_image.retrieve_hough_pairs(rectangle_image, rectangle_image_hough_nonmax, np.max(rectangle_image_hough_nonmax)*0.35, N_BINS_THETA, N_BINS_RHO) - best_paris = select_best_pairs(neighbour_pairs) + best_paris = uz_image.select_best_pairs(neighbour_pairs) for neighbour in best_paris: xs, ys = uz_image.get_line_to_plot(neighbour[0], neighbour[1], rectangle_image.shape[0], rectangle_image.shape[1]) @@ -646,37 +643,82 @@ def three_f(): the [−π/2, π/2] interval. Test the modified function on several images and compare the results with the original implementation. """ - rectangle_image = uz_image.imread_gray('./images/rectangle.png', uz_image.ImageType.float64) + SIGMA = 1 + THETA = 0.02 + T_LOW = 0.04 + T_HIGH = 0.16 - image_with_edges_n, derivative_magnitude_n, gradient_angles_n, hough_image_n, hough_image_nms_n, pairs_n, best_pairs_n = uz_image.find_lines_in_image_naive( - rectangle_image - ) + img = cv2.imread('images/rectangle.png') - image_with_edges_i, derivative_magnitude_i, gradient_angles_i, hough_image_i, hough_image_nms_i, pairs_i, best_pairs_i = uz_image.find_lines_in_image_improved( - rectangle_image - ) + # Get gradient magntude and gradient angle + + t_lower = 70 + t_upper = 200 + + edge_detected_image = cv2.Canny(img, t_lower, t_upper) + gm, ga = uz_image.gradient_magnitude(img, 1) + + print('Edge detected:', edge_detected_image.shape) + + # Transform image into hough space + image_transformed_into_hough_space = uz_image.hough_find_lines_i(edge_detected_image, ga, gm, 360, 360, 0.2) + print('Hough lines drawn:', image_transformed_into_hough_space.shape) + + hugh_pairs = uz_image.retrieve_hough_pairs(img, image_transformed_into_hough_space, np.max(image_transformed_into_hough_space) * 0.2, 360, 360) + best_pairs = hugh_pairs fig, axs = plt.subplots(2, 2) - axs[0, 0].imshow(hough_image_n) + axs[0, 0].imshow(edge_detected_image) axs[0, 0].set(title='normal') - axs[0, 1].imshow(hough_image_i) + axs[0, 1].imshow(image_transformed_into_hough_space) axs[0, 1].set(title='normal') - axs[1, 0].imshow(rectangle_image, cmap='gray') - for param in best_pairs_n: - xs, ys = uz_image.get_line_to_plot(param[0], param[1], rectangle_image.shape[0], rectangle_image.shape[1]) + axs[1, 0].imshow(edge_detected_image, cmap='gray') + for param in best_pairs: + xs, ys = uz_image.get_line_to_plot(param[0], param[1], img.shape[0], img.shape[1]) axs[1, 0].plot(xs, ys, 'r', linewidth=0.7) - axs[1,1].imshow(rectangle_image, cmap='gray') - for param in best_pairs_i: - xs, ys = uz_image.get_line_to_plot(param[0], param[1], rectangle_image.shape[0], rectangle_image.shape[1]) - axs[1, 1].plot(xs, ys, 'r', linewidth=0.7) + axs[1, 1].imshow(image_transformed_into_hough_space) + + #axs[1,1].imshow(rectangle_image, cmap='gray') + #for param in best_pairs_i: + # xs, ys = uz_image.get_line_to_plot(param[0], param[1], rectangle_image.shape[0], rectangle_image.shape[1]) + # axs[1, 1].plot(xs, ys, 'r', linewidth=0.7) + + #plt.show() plt.show() +def three_g(): + """ + F (5 points) Implement a Hough transform that detects circles of a fixed radius. + You can test the algorithm on image eclipse.jpg. Try using a radius somewhere + between 45 and 50 pixels. + """ + circle_image = uz_image.imread('images/eclipse.jpg', uz_image.ImageType.uint8) + #img = cv2.imread('images/rectangle.png') + t_lower = 100 + t_upper = 150 + edge_detected_image = cv2.Canny(circle_image, t_lower, t_upper) + + hugh_transformed_circle = uz_image.hough_transform_a_circle(edge_detected_image, 45, 50, 0.2) + + fig, axs = plt.subplots(1, 2) + + axs[0].imshow(circle_image, cmap='gray') + axs[0].set(title='Original') + + axs[1].imshow(edge_detected_image, cmap='gray') + axs[1].set(title='Edges') + + plt.show() + + for i in range(hugh_transformed_circle.shape[2]): + plt.imshow(hugh_transformed_circle[:, :, i]) + plt.show() # ######## # @@ -684,9 +726,9 @@ def three_f(): # ######## # def main(): - ex1() - #ex2() - #ex3() + #ex1() # everything K + #ex2() # everything OK + ex3() if __name__ == '__main__': main() diff --git a/assignment3/uz_framework/image.py b/assignment3/uz_framework/image.py index e0d9828..90fbd64 100644 --- a/assignment3/uz_framework/image.py +++ b/assignment3/uz_framework/image.py @@ -241,7 +241,13 @@ def get_image_bins_ND(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint return hist / np.sum(hist) -def get_image_bins_gradient_magnitude_and_angles(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]) -> npt.NDArray[np.float64]: +def get_image_bins_gradient_magnitude_and_angles(image: Union[npt.NDArray[np.float64], + npt.NDArray[np.uint8]]) -> npt.NDArray[np.float64]: + """ + Accepts: image, + Returns: 1D histogram of image using gradient magnitude and gradient angles + Works OK on many dimensions + """ WIDTH = image.shape[0] HEIGHT = image.shape[1] WIDTH_8 = WIDTH // 8 @@ -720,6 +726,30 @@ def hough_transform_a_point(x: int, y: int, n_bins: int) -> npt.NDArray[np.float accumlator[int(r), i] += 1 return accumlator + +def hough_transform_a_circle(edged_image: Union[npt.NDArray[np.float64] , npt.NDArray[np.uint8]], + r_start: int, r_end: int, treshold: float) -> npt.NDArray[np.float64]: + + """ + Accepts: image, r_start, r_end + Returns: hough space + """ + image = edged_image.copy() + image[image < treshold] = 0 + accumlator = np.zeros((edged_image.shape[0], edged_image.shape[1], r_end - r_start)) + indices = np.argwhere(image) + sine_value = np.sin(np.linspace(0, np.pi, 360)) + cosine_value = np.cos(np.linspace(0, np.pi, 360)) + + # Loop through all nonzero pixels above treshold + for i in tqdm(range(len(indices)), desc='Hough transform'): + for r in range(0, r_end - r_start): + x, y = indices[i] + for svcv in range(sine_value.shape[0]): + a = x - r * cosine_value[svcv] + b = y - r * sine_value[svcv] + accumlator[int(a), int(b), r ] += 1 + return accumlator def hough_find_lines(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], @@ -758,38 +788,37 @@ def hough_find_lines(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8] return accumulator -def hough_find_lines_i(image_with_lines: npt.NDArray[np.float64], gradient_angles: npt.NDArray[np.float64], - gradient_magnitude: npt.NDArray[np.float64], - n_bins_theta: int, n_bins_rho: int, treshold: float) -> npt.NDArray[np.uint64]: +def hough_find_lines_i(image_with_lines: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], gradient_angles: npt.NDArray[np.float64], + gradient_magnitude: npt.NDArray[np.float64], + n_bins_theta: int, n_bins_rho: int, treshold: float) -> Union[npt.NDArray[np.uint64], npt.NDArray[np.float64]]: """" Accepts: bw image with lines, n_bins_theta, n_bins_rho, treshold Returns: image points above treshold transformed into hough space """ image = image_with_lines.copy() - #image[image < treshold] = 0 + image[image < treshold] = 0 theta_values = np.linspace(-np.pi/2, np.pi/2, n_bins_theta) D = np.sqrt(image.shape[0]**2 + image.shape[1]**2) rho_values = np.linspace(-D, D, n_bins_rho) - accumulator = np.zeros((n_bins_rho, n_bins_theta), dtype=np.uint64) + accumulator = np.zeros((n_bins_rho, n_bins_theta), dtype=np.float64) cos_precalculated = np.cos(theta_values) sin_precalculated = np.sin(theta_values) indices = np.argwhere(image) - # Loop through all nonzero pixels above treshold for i in tqdm(range(len(indices)), desc='Hough transform'): - y, x= indices[i] - - theta = np.digitize(gradient_angles[y, x] / 2, theta_values) -1 - rho = np.round(x* cos_precalculated[theta] + y* sin_precalculated[theta]).astype(np.int64) + y, x = indices[i] + angle = (np.mod(gradient_angles[y, x] + np.pi/2 , np.pi)) - np.pi/2 + + theta = np.digitize(angle, theta_values) -1 + rho = np.round(x* cos_precalculated[theta] + y* sin_precalculated[theta]).astype(np.float64) binned_rho = np.digitize(rho, rho_values) - 1 # cuz digitize is returning bin number + 1 # Add to accumulator - print(gradient_magnitude[y, x]) - accumulator[binned_rho, theta] += 1 + accumulator[binned_rho, theta] += gradient_magnitude[y, x] return accumulator @@ -818,7 +847,6 @@ def nonmaxima_suppression_box(image: npt.NDArray[np.uint64]) -> npt.NDArray[np.u break return image - def retrieve_hough_pairs(original_image: npt.NDArray[np.float64], hough_image: npt.NDArray[np.uint64], treshold: int, n_bins_theta: int, n_bins_rho: int) -> list[tuple[int, int]]: """ @@ -856,61 +884,6 @@ def select_best_pairs(image_line_params, n =10): partition = np.argpartition(image_line_params, kth=len(image_line_params) - n - 1, axis=0)[-n:] image_line_params = image_line_params[partition.T[0]] return image_line_params - -def find_lines_in_image_naive(image: npt.NDArray[np.float64], SIGMA=1, THETA=0.02, T_LOW=0.04, - T_HIGH=0.16, N_BINS_THETA=360, N_BINS_RHO=360, TRESHOLD=0.2): - """ - Aplies all methods to transform image into hough space and find lines - """ - - image = image.copy() - - # First step: apply canny edge detector - image_with_edges = find_edges_canny(image, SIGMA, THETA, T_LOW, T_HIGH) - - # Second step: Retrieve gradient angles - derivative_magnitude, gradient_angles = gradient_magnitude(image, SIGMA) - - # Third step: Transform image into hough space - hough_image = hough_find_lines(image_with_edges, int(N_BINS_THETA), int(N_BINS_RHO), TRESHOLD) - - # Fourth step: Apply nonmaxima suppression - hough_image_nms = nonmaxima_suppression_box(hough_image) - - # Fifth step: Retrieve sigma and theta pairs - pairs = retrieve_hough_pairs(image, hough_image_nms, np.max(hough_image_nms) *0.5, int(N_BINS_THETA), int(N_BINS_RHO)) - - # Sixth step: select best pairs - best_pairs = select_best_pairs(pairs, 10) - - return image_with_edges, derivative_magnitude, gradient_angles, hough_image, hough_image_nms, pairs, best_pairs - -def find_lines_in_image_improved(image: npt.NDArray[np.float64], SIGMA=1, THETA=0.02, T_LOW=0.04, T_HIGH=0.16, N_BINS_THETA=360, N_BINS_RHO=360, TRESHOLD=0.2): - """ - Aplies all methods to transform image into hough space and find lines - """ - - image = image.copy() - - # First step: apply canny edge detector - image_with_edges = find_edges_canny(image, SIGMA, THETA, T_LOW, T_HIGH) - - # Second step: Retrieve gradient angles - derivative_magnitude, gradient_angles = gradient_magnitude(image, SIGMA) - - # Third step: Transform image into hough space - hough_image = hough_find_lines_i(image_with_edges, gradient_angles, derivative_magnitude, N_BINS_THETA, N_BINS_RHO, TRESHOLD) - - # Fourth step: Apply nonmaxima suppression - hough_image_nms = nonmaxima_suppression_box(hough_image) - - # Fifth step: Retrieve sigma and theta pairs - pairs = retrieve_hough_pairs(image, hough_image_nms, 0, N_BINS_THETA, N_BINS_RHO) - # Sixth step: select best pairs - #best_pairs = select_best_pairs(pairs, 10) - best_pairs = pairs - - return image_with_edges, derivative_magnitude, gradient_angles, hough_image, hough_image_nms, pairs, best_pairs def get_line_to_plot(rho, theta, h, w): """