найти край в наклонном изображении с Canny - PullRequest
0 голосов
/ 04 июля 2018

Я пытаюсь найти угол наклона в серии изображений, которые выглядят так, как показано в примере ниже. Должен быть четкий край, который виден на глаз. Однако я до сих пор изо всех сил пытаюсь извлечь края. Является ли Canny правильным способом найти край здесь или есть лучший способ найти край?

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage.filters import gaussian_filter

# create data
xvals = np.arange(0,2000)
yvals = 10000 * np.exp((xvals - 1600)/200) + 100
yvals[1600:] = 100
blurred = gaussian_filter(yvals, sigma=20)

# create image
img = np.tile(blurred,(2000,1))
img = np.swapaxes(img,0,1)

# rotate image
rows,cols = img.shape
M = cv.getRotationMatrix2D((cols/2,rows/2),3.7,1)
img = cv.warpAffine(img,M,(cols,rows))

# convert to uint8 for Canny
img_8 = cv.convertScaleAbs(img,alpha=(255.0/65535.0))
fig,ax = plt.subplots(3)
ax[0].plot(xvals,blurred)
ax[1].imshow(img)

# find edge
ax[2].imshow(cv.Canny(img_8, 20, 100, apertureSize=5))

sample image for edge detection

Ответы [ 4 ]

0 голосов
/ 07 апреля 2019

Определение порога как предложено kavko не сработало так хорошо, так как интенсивность варьировалась от изображения к изображению (я, конечно, мог бы рассмотреть гистограмму для каждого изображения, чтобы улучшить этот подход). В итоге я взял максимум градиента в направлении у:

def rotate_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

0 голосов
/ 04 июля 2018

Очень простой способ сделать это следующим образом ... скорректировать мои цифры в соответствии с вашими знаниями о данных.

Нормализуйте ваше изображение в масштабе 0-255.

Выберите две точки A и B, где A составляет 10% ширины изображения с левой стороны, а B - 10% с правой стороны. Расстояние AB теперь 0,8 x 2000 или 1600 пикселей.

Идите на север от точки A, пока вы не достигнете разумного порога, который означает, что вы встретили наклонную линию. Обратите внимание на значение Y в этой точке, как YA.

Сделайте то же самое, двигаясь на север от точки B, пока не встретите наклонную линию. Обратите внимание на значение Y в этой точке, как YB.

Угол, который вы ищете:

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

enter image description here

0 голосов
/ 04 июля 2018

Вы можете найти угол, преобразовав свое изображение в двоичное (cv2.threshold(cv2.THRESH_BINARY)), а затем выполнить поиск контуров.

enter image description here

Когда вы находите свой контур (линию), вы можете разместить линию на вашем контуре cv2.fitLine() и получить две точки своей линии. Моя математика не очень хорошая, но я думаю, что в линейном уравнении формула имеет вид f(x) = k*x + n, и вы можете получить k из этих двух точек (k = (y2-y1)/(x2-x1)) и, наконец, угол phi = arctan(k). (Если я не прав, пожалуйста, исправьте это)

Вы также можете использовать повернутый ограничивающий прямоугольник - cv2.minAreaRect() - который уже возвращает угол прямоугольника (rect = cv2.minAreaRect() -> rect[2]). Надеюсь, поможет. Ура!

Вот пример кода:

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 < 10001 and 100 < 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)

Исходное изображение:

enter image description here

Выход:

enter image description here

-3,8493663478518627

-3,7022125720977783

0 голосов
/ 04 июля 2018

TRIBOL,

кажется, что вы можете взять градиентное изображение G = | Gx | + | Гр | (нормализуйте его до некоторого известного диапазона), рассчитайте его гистограмму и возьмите верхние ячейки. это даст вам приблизительную маску линии. Тогда вы можете сделать установку линии. Это даст вам хорошее начальное предположение.

...