<jupyter_start><jupyter_text>Straighten an image using the Hough transform We'll write our own Hough transform to compute the Hough transform and use it to straighten a wonky image.  Package inclusion for Python<jupyter_code>import copy
import math
import numpy as np
import cv2<jupyter_output><empty_output><jupyter_text>Read the image from a file on the disk and return a new matrix ![../wonky.png](../img/wonky.png)<jupyter_code>image = cv2.imread("../img/wonky.png");<jupyter_output>libpng warning: iCCP: CRC error<jupyter_text>Check for errors<jupyter_code># Check for failure
if image is None: 
    raise Exception("Could not open or find the image");<jupyter_output><empty_output><jupyter_text>Convert degrees to gradients<jupyter_code>def deg2rad(anAngleInDegrees: float) -> float:
    return anAngleInDegrees * math.pi / 180.0<jupyter_output><empty_output><jupyter_text>Apply the Canny edge detector<jupyter_code>def cannyEdgeDetector(anInputImage: np.array,
                      aCannyThreshold: int) -> np.array:
    
    # Find edges using Canny
    ratio = 3
    kernel_size = 3
    
    edge_image = cv2.Canny(anInputImage,
          aCannyThreshold,
          aCannyThreshold * ratio,
          kernel_size)
    
    return edge_image
blurred_image = cv2.blur( image, (3,3) )
edge = cannyEdgeDetector(blurred_image, 60)

cv2.namedWindow("edge", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("edge", edge)

cv2.namedWindow("image", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("image", image)

cv2.waitKey(0) # Wait for any keystroke in the window
cv2.destroyAllWindows() # Destroy all the created windows<jupyter_output><empty_output><jupyter_text>| Original image | canny ||----------------|--------||![image](../img/wonky_original.png) |  ![canny](../img/wonky_canny.png) |  Compute the accumulator<jupyter_code>def houghTransform(anInputImage: np.array,
                   aCannyThreshold: int) -> np.array:

    # Blur the input image
    blurred_image = cv2.blur( anInputImage, (3,3) )

    # Find edges using Canny
    edge_image = cannyEdgeDetector(blurred_image, aCannyThreshold)

    width  = 180;
    diagonal = math.sqrt(edge_image.shape[1] * edge_image.shape[1] + edge_image.shape[0] * edge_image.shape[0])
    height = math.floor(2.0 * diagonal)
    half_height = height / 2.0
    accumulator = np.zeros((height, width), np.single)

    # Process all the pixels of the edge image
    for j in range(edge_image.shape[0]):

        for i in range(edge_image.shape[1]):

            # The pixel is on an edge
            if edge_image[j,i] > 0:

                # Process all the angles
                for theta in range(180):

                    angle = deg2rad(theta);
                    r = i * math.cos(angle) + j * math.sin(angle)

                    v = math.floor(r + half_height)
                    accumulator[v, theta] += 1

    return accumulator
accumulator = houghTransform(image, 60)<jupyter_output><empty_output><jupyter_text>Visualise the accumulatorLook for dots. Every dot represents a line in the original image. There are for of them<jupyter_code>vis_accumulator = cv2.normalize(accumulator, None, 0, 1, cv2.NORM_MINMAX, cv2.CV_32FC1)
    
cv2.namedWindow("accumulator", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("accumulator", vis_accumulator)

cv2.waitKey(0) # Wait for any keystroke in the window
cv2.destroyAllWindows() # Destroy all the created windows<jupyter_output><empty_output><jupyter_text>![accumulator](../img/wonky_accumulator.png)  Draw the lines<jupyter_code>Scalar = [float, float, float]

def drawLines(anImage: np.array,
              anAccumulator: np.array,
              aHoughThreshold: float,
              aLineWidth: int,
              aLineColour: Scalar) -> np.array:

    # Copy the input image into the output image
    output = copy.deepcopy(anImage)

    # Process all the pixels of the accumulator image
    for j in range(anAccumulator.shape[0]):

        for i in range(anAccumulator.shape[1]):
        
            # The pixel value in the accumulator is greater than the threshold
            # Display the corresponding line
            if anAccumulator[j, i] >= aHoughThreshold:

                # The pixel location
                location = (i, j)

                # The two corners of the image
                pt1 = [               0, 0]
                pt2 = [anImage.shape[1] - 1, anImage.shape[0] - 1]

                # Get theta in radian
                theta = deg2rad(location[0])

                # Get r
                r = location[1]
                r -= anAccumulator.shape[0] / 2.0

                # How to retrieve the line from theta and r:
                #      x = (r - y * sin(theta)) / cos(theta);
                #      y = (r - x * cos(theta)) / sin(theta);

                # sin(theta) != 0
                if location[0] != 0 and location[0] != 180:
                    pt1[1] = math.floor((r - pt1[0] * math.cos(theta)) / math.sin(theta))
                    pt2[1] = math.floor((r - pt2[0] * math.cos(theta)) /math.sin(theta))
                # math.sin(theta) == 0 && math.cos(theta) != 0
                else:
                    pt1[0] = math.floor((r - pt1[1] * sin(theta)) / math.cos(theta))
                    pt2[0] = math.floor((r - pt2[1] * sin(theta)) / math.cos(theta))

                # Draw the line
                output = cv2.line(output, pt1, pt2, aLineColour, aLineWidth)

    return output
# Get tne min and max in the accumulator
min_value, max_value, min_loc, max_loc = cv2.minMaxLoc(accumulator)

hough_threshold = min_value + 0.6 * (max_value - min_value)

image_with_lines = drawLines(image, accumulator, hough_threshold, 4, (0, 0, 255))
cv2.namedWindow("image", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("image", image)

cv2.namedWindow("edge", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("edge", edge)

cv2.namedWindow("image_with_lines", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("image_with_lines", image_with_lines)

cv2.waitKey(0) # Wait for any keystroke in the window
cv2.destroyAllWindows() # Destroy all the created windows<jupyter_output><empty_output><jupyter_text>| Original image | canny | lines ||----------------|--------|--------||![image](../img/wonky_original.png) |  ![canny](../img/wonky_canny.png) | ![ines](../img/wonky_with_lines.png)  Extract the angle from the accumulator<jupyter_code>min_value, max_value, min_loc, max_loc = cv2.minMaxLoc(accumulator)
print("Max value: ", max_value, " Location: ", (90 - max_loc[0], max_loc[1]))<jupyter_output>Max value:  418.0  Location:  (9, 1632)<jupyter_text>We must convert the position along the horizontal axis into an angle. It's simple. [9, 1632] Tells us that the image is rotated by 9 degrees. To straighten it, we must rotate it by -9 degrees.<jupyter_code>def rotate(anImage: np.array, angle: float) -> np.array:

    /# Point from where to rotate (centre of rotation), here the centre of the image
    pt = (anImage.shape[1] / 2.0, anImage.shape[0] / 2.0)          
    
    # Create a rotation matrix
    rotation_matrix = cv2.getRotationMatrix2D(pt, angle, 1.0)
    
    # Apply the transforation to the image
    rotated_image = cv2. warpAffine(anImage, rotation_matrix, (anImage.shape[0], anImage.shape[1])) 
    
    return rotated_image
print(90 - max_loc[0])
rotated = rotate(image, -(90 - max_loc[0]))
cv2.namedWindow("image", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("image", image)

cv2.namedWindow("rotated", cv2.WINDOW_GUI_EXPANDED)
cv2.imshow("rotated", rotated)

cv2.waitKey(0) # Wait for any keystroke in the window
cv2.destroyAllWindows() # Destroy all the created windows<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12073732718894009, 'fraction_numerical': 0.02317314022383147, 'mean_word_length': 2.8953846153846152, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '9392', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2643, 'n_tokens_neox': 2481, 'n_words': 626, 'path': 'Lectures/Week05-Edge_n_line_detection/notebooks/4-python-straighten-image-using-hough-transform.ipynb', 'repo_name': 'effepivi/ICE-3111-Computer_Vision', 'size': '7595'}