Skip to content Skip to sidebar Skip to footer

Finding Edge In Tilted Image With Canny

I'm trying to find the tilt angle in a series of images which look like the created example data below. There should be a clear edge which is visible by eye. However I'm struggling

Solution 1:

You can find the angle by transforming your image to binary (cv2.threshold(cv2.THRESH_BINARY)) then search for contours.

enter image description here

When you locate your contour (line) then you can fit a line on your contour cv2.fitLine() and get two points of your line. My math is not very good but I think that in linear equation the formula goes f(x) = k*x + n and you can get k out of those two points (k = (y2-y1)/(x2-x1)) and finally the angle phi = arctan(k). (If I'm wrong please correct it)

You can also use the rotated bounding rectangle - cv2.minAreaRect() - which already returns the angle of the rectangle (rect = cv2.minAreaRect() --> rect[2]). Hope it helps. Cheers!

Here is an example code:

import cv2
import numpy as np
import math

img = cv2.imread('angle.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, threshold = cv2.threshold(gray,170,255,cv2.THRESH_BINARY)
im, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
for c in contours:
    area = cv2.contourArea(c)
    perimeter = cv2.arcLength(c, False)
    if area < 10001and100 < perimeter < 1000:
        # first approach - fitting line and calculate with y=kx+n --> angle=tan^(-1)k
        rows,cols = img.shape[:2]
        [vx,vy,x,y] = cv2.fitLine(c, cv2.DIST_L2,0,0.01,0.01)
        lefty = int((-x*vy/vx) + y)
        righty = int(((cols-x)*vy/vx)+y)
        cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)
        (x1, y1) = (cols-1, righty)
        (x2, y2) = (0, lefty)
        k = (y2-y1)/(x2-x1)
        angle = math.atan(k)*180/math.pi
        print(angle)
        #second approch - cv2.minAreaRect --> returns center (x,y), (width, height), angle of rotation )
        rect = cv2.minAreaRect(c)
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        cv2.drawContours(img,[box],0,(0,0,255),2)
        print(rect[2])

cv2.imshow('img2', img)

Original image:

enter image description here

Output:

enter image description here

-3.8493663478518627

-3.7022125720977783

Solution 2:

tribol,

it seems like you can take the gradient image G = |Gx| + |Gy| (normalize it to some known range), calc its Histogram and take the top bins of it. it will give you approx mask of the line. Then you can do line fitting. It'll give you a good initial guess.

Solution 3:

A very simple way of doing it is as follows... adjust my numbers to suit your knowledge of the data.

Normalise your image to a scale of 0-255.

Choose two points A and B, where A is 10% of the image width in from the left side and B is 10% in from the right side. The distance AB is now 0.8 x 2000, or 1600 px.

Go North from point A sampling your image till you exceed some sensible threshold that means you have met the tilted line. Note the Y value at this point, as YA.

Do the same, going North from point B till you meet the tilted line. Note the Y value at this point, as YB.

The angle you seek is:

tan-1((YB-YA)/1600) 

enter image description here

Solution 4:

Thresholding as suggested by kavko didn't work that well, as the intensity varied from image to image (I could of course consider the histogram for each image to imrove this approach). I ended up with taking the maximum of the gradient in the y-direction:

defrotate_image(image):
    blur = ndimage.gaussian_filter(image, sigma=10)    # blur image first
    grad = np.gradient(blur, axis= 0)    # take gradient along y-axis
    grad[grad>10000]=0# filter unreasonable high values
    idx_maxline = np.argmax(grad, axis=0)    # get y-indices of max slope = indices of edge

    mean = np.mean(idx_maxline)
    std = np.std(idx_maxline)
    idx = np.arange(idx_maxline.shape[0])
    idx_filtered = idx[(idx_maxline < mean+std) & (idx_maxline > mean - std)]    # filter positions where highest slope is at different position(blobs)
    slope, intercept, r_value, p_value, std_err = stats.linregress(idx_filtered, idx_maxline[idx_filtered])
    out = ndimage.rotate(image,slope*180/np.pi, reshape = False)
    return out
out = rotate_image(img)
plt.imshow(out)

final rotated image

Post a Comment for "Finding Edge In Tilted Image With Canny"