diff --git a/assignment3/solution.py b/assignment3/solution.py index c6207c1..e0d6797 100644 --- a/assignment3/solution.py +++ b/assignment3/solution.py @@ -11,8 +11,9 @@ import uz_framework.text as uz_text def ex1(): #one_a() - #two_b() - two_c() + #one_b() + #one_c() + one_d() def one_a() -> None: """ @@ -21,7 +22,7 @@ def one_a() -> None: Ixy(x, y) """ -def two_b() -> None: +def one_b() -> None: """ Implement a function that computes the derivative of a 1-D Gaussian kernel Implement the function gaussdx(sigma) that works the same as function gauss @@ -35,7 +36,7 @@ def two_b() -> None: kernel = uz_image.gaussdx(sigma) print(kernel) -def two_c() -> None: +def one_c() -> None: """ The properties of the filter can be analyzed by using an impulse response function. This is performed as a convolution of the filter with a Dirac delta function. The @@ -103,6 +104,51 @@ def two_c() -> None: plt.show() + +def one_d() -> None: + """ + Implement a function that uses functions gauss and gaussdx to compute both + partial derivatives of a given image with respect to x and with respect to y. + Similarly, implement a function that returns partial second order derivatives of a + given image. + Additionally, implement the function gradient_magnitude that accepts a grayscale + image I and returns both derivative magnitudes and derivative angles. Magnitude + is calculated as m(x, y) = sqrt(Ix(x,y)^2 + Iy(x, y)^2) and angles are calculated as + φ(x, y) = arctan(Iy(x, y)/Ix(x, y)) + Hint: Use function np.arctan2 to avoid division by zero for calculating the arctangent function. + Use all the implemented functions on the same image and display the results in the + same window. + """ + + museum = uz_image.imread_gray('./images/museum.jpg', uz_image.ImageType.float64) + + museum_x, museum_y = uz_image.derive_image_first_order(museum, 1) + (museum_xx, museum_xy) , (_, museum_yy) = uz_image.derive_image_second_order(museum, 1) + derivative_magnitude, derivative_angle = uz_image.gradient_magnitude(museum, 1) + + fig, axs = plt.subplots(2, 4) + fig.suptitle('Museum') + + axs[0,0].imshow(museum, cmap='gray') + axs[0,0].set_title('Original') + axs[0, 1].imshow(museum_x, cmap='gray') + axs[0, 1].set_title('I_x') + axs[0, 2].imshow(museum_y, cmap='gray') + axs[0, 2].set_title('I_y') + axs[1, 0].imshow(museum_xx, cmap='gray') + axs[1, 0].set_title('I_xx') + axs[1, 1].imshow(museum_xy, cmap='gray') + axs[1, 1].set_title('I_xy') + axs[1, 2].imshow(museum_yy, cmap='gray') + axs[1, 2].set_title('I_yy') + axs[0, 3].imshow(derivative_magnitude, cmap='gray') + axs[0, 3].set_title('I_mag') + axs[1, 3].imshow(derivative_angle, cmap='gray') + axs[1, 3].set_title('I_dir') + + plt.show() + + # ######## # # SOLUTION # # ######## # diff --git a/assignment3/uz_framework/image.py b/assignment3/uz_framework/image.py index 8a039f7..81c36ce 100644 --- a/assignment3/uz_framework/image.py +++ b/assignment3/uz_framework/image.py @@ -498,3 +498,57 @@ def generate_dirac_impulse(size: int) -> npt.NDArray[np.float64]: dirac_impulse[int(size/2), int(size/2)] = 1 return dirac_impulse + +def derive_image_by_x(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], sigma: float) -> Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]: + """ + Accepts: image + Returns: image derived by x + """ + image = image.copy() + gaussd = np.array([gaussdx(sigma)]) + gauss = np.array([get_gaussian_kernel(sigma)]) + gaussd = np.flip(gaussd, axis=1) + + applied_by_y = cv2.filter2D(image, cv2.CV_64F, gauss.T) + applied_by_x = cv2.filter2D(applied_by_y, cv2.CV_64F, gaussd) + + return applied_by_x + +def derive_image_by_y(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], sigma: float) -> Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]: + """ + Accepts: image + Returns: image derived by y + """ + gaussd = np.array([gaussdx(sigma)]) + gauss = np.array([get_gaussian_kernel(sigma)]) + gaussd = np.flip(gaussd, axis=1) + + applied_by_x = cv2.filter2D(image, cv2.CV_64F, gauss) + applied_by_y = cv2.filter2D(applied_by_x, cv2.CV_64F, gaussd.T) + + return applied_by_y + +def derive_image_first_order(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], sigma: float) -> tuple[Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]]: + """ + Accepts: image + returns: image derived by x, image derived by y + """ + return derive_image_by_x(image, sigma), derive_image_by_y(image, sigma) + +def derive_image_second_order(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], sigma: float) -> tuple[tuple[Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]], tuple[Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]]]: + """ + Accepts: image + Returns: Ixx, Ixy, Iyx, Iyy + """ + derived_by_x = derive_image_by_x(image, sigma) + derived_by_y = derive_image_by_y(image, sigma) + + return derive_image_first_order(derived_by_x, sigma), derive_image_first_order(derived_by_y, sigma) + +def gradient_magnitude(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], sigma: float) -> tuple[Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]]: + """ + Accepts: image + Returns: gradient magnitude of image and derivative angles + """ + Ix, Iy = derive_image_first_order(image, sigma) + return np.sqrt(Ix**2 + Iy**2), np.arctan2(Iy, Ix)