web-dev-qa-db-fra.com

Comment détecter les lignes dans l'opencv?

J'essaie de détecter les lignes dans le parking comme indiqué ci-dessous

Empty parking lot

Ce que j'espère obtenir, ce sont les lignes nettes et la position (x, y) dans la ligne croisée, mais le résultat n'est pas très prometteur

Parking lot with Hough Lines drawn

Je suppose que cela est dû à deux raisons principales

  1. certaines lignes sont très cassées ou manquent même les yeux humains peuvent clairement les identifier. (Même HoughLine peut aider à connecter certaines lignes manquantes car HoughLine connectait parfois des lignes inutiles ensemble, je préférerais donc le faire manuellement)

  2. il y a quelques lignes répétées

Le pipeline général des travaux est présenté ci-dessous.

1. sélectionnez les couleurs spécifiques (blanc ou jaune)

import cv2
import numpy as np
import matplotlib
from matplotlib.pyplot import imshow
from matplotlib import pyplot as plt

# white color mask
img = cv2.imread(filein)
#converted = convert_hls(img)
image = cv2.cvtColor(img,cv2.COLOR_BGR2HLS)
lower = np.uint8([0, 200, 0])
upper = np.uint8([255, 255, 255])
white_mask = cv2.inRange(image, lower, upper)
# yellow color mask
lower = np.uint8([10, 0,   100])
upper = np.uint8([40, 255, 255])
yellow_mask = cv2.inRange(image, lower, upper)
# combine the mask
mask = cv2.bitwise_or(white_mask, yellow_mask)
result = img.copy()
cv2.imshow("mask",mask) 

Binary image

2. répéter la dilatation et l'érosion jusqu'à ce que l'image ne puisse plus être modifiée ( référence )

height,width = mask.shape
skel = np.zeros([height,width],dtype=np.uint8)      #[height,width,3]
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
temp_nonzero = np.count_nonzero(mask)
while(np.count_nonzero(mask) != 0 ):
    eroded = cv2.erode(mask,kernel)
    cv2.imshow("eroded",eroded)   
    temp = cv2.dilate(eroded,kernel)
    cv2.imshow("dilate",temp)
    temp = cv2.subtract(mask,temp)
    skel = cv2.bitwise_or(skel,temp)
    mask = eroded.copy()

cv2.imshow("skel",skel)
#cv2.waitKey(0)

 After the erosion and dialation

3. appliquer le filtre pour filtrer les lignes et utiliser HoughLinesP pour obtenir les lignes

edges = cv2.Canny(skel, 50, 150)
cv2.imshow("edges",edges)
lines = cv2.HoughLinesP(edges,1,np.pi/180,40,minLineLength=30,maxLineGap=30)
i = 0
for x1,y1,x2,y2 in lines[0]:
    i+=1
    cv2.line(result,(x1,y1),(x2,y2),(255,0,0),1)
print i

cv2.imshow("res",result)
cv2.waitKey(0)

After Canny

Je me demande après la première étape de sélection de certaines couleurs, les lignes sont fracturées et les bruits, je pense que dans cette étape, nous devrions faire quelque chose pour que la ligne brisée soit complète, moins bruyante, puis essayer d'appliquer quelque chose pour faire la Canny et Hough lignes, des idées?

25
user824624

Voici mon pipeline, il peut peut-être vous aider.

D'abord, récupérez l'image grise et traitez GaussianBlur.

img = cv2.imread('src.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

Deuxièmement, la détection de processus de processus utilise Canny.

low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

Ensuite, utilisez HoughLinesP pour obtenir les lignes. Vous pouvez ajuster les paramètres pour obtenir de meilleures performances.

rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 15  # minimum number of votes (intersections in Hough grid cell)
min_line_length = 50  # minimum number of pixels making up a line
max_line_gap = 20  # maximum gap in pixels between connectable line segments
line_image = np.copy(img) * 0  # creating a blank to draw lines on

# Run Hough on Edge detected image
# Output "lines" is an array containing endpoints of detected line segments
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)

for line in lines:
    for x1,y1,x2,y2 in line:
    cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),5)

Enfin, tracez les lignes sur votre srcImage.

# Draw the lines on the  image
lines_edges = cv2.addWeighted(img, 0.8, line_image, 1, 0)

Voici ma dernière performance.

Image finale:

enter image description here

23
veraposeidon

Je ne sais pas exactement ce que vous demandez, puisqu'il n'y a aucune question dans votre message.

Une technique agréable et robuste pour détecter les segments de ligne est le LSD (détecteur de segment de ligne), disponible en openCV depuis openCV 3.

Voici un code C++ de base simple, qui peut probablement être converti en python facilement:

int main(int argc, char* argv[])
{
    cv::Mat input = cv::imread("C:/StackOverflow/Input/parking.png");
    cv::Mat gray;
    cv::cvtColor(input, gray, CV_BGR2GRAY);


    cv::Ptr<cv::LineSegmentDetector> det;
    det = cv::createLineSegmentDetector();



    cv::Mat lines;
    det->detect(gray, lines);

    det->drawSegments(input, lines);

    cv::imshow("input", input);
    cv::waitKey(0);
    return 0;
}

Donnant ce résultat:

enter image description here

Ce qui est plus esthétique que votre image pour un traitement ultérieur (pas de doublons de lignes, etc.)

14
Micka

Il y a d'excellentes réponses ici à la première partie de votre question, mais en ce qui concerne la deuxième partie (trouver les intersections de lignes), je ne vois pas grand-chose.

Je vous suggère de jeter un coup d'œil à l'algorithme Bentley-Ottmann .

Il y a quelques implémentations de python de l'algorithme ici et ici .

Edit: En utilisant l’implémentation de VeraPoseidon Houghlines et la deuxième bibliothèque liée ici, j’ai réussi à obtenir le résultat suivant pour la détection d’intersection. Nous remercions Vera et l'auteur de la bibliothèque pour leur bon travail. Les carrés verts représentent une intersection détectée. Il y a quelques erreurs, mais cela me semble un très bon point de départ. Il semble que plusieurs intersections soient détectées dans la plupart des emplacements où vous souhaitez réellement détecter une intersection. Vous pouvez donc probablement afficher une fenêtre de taille appropriée sur l'image qui recherche plusieurs intersections et qui est considérée comme une intersection réelle comme étant celle où cette fenêtre est activée.

Bentley-Ottmann applied to Houghlines

Voici le code que j'ai utilisé pour produire ce résultat:

import cv2
import numpy as np
import isect_segments_bentley_ottmann.poly_point_isect as bot


img = cv2.imread('parking.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 15  # minimum number of votes (intersections in Hough grid cell)
min_line_length = 50  # minimum number of pixels making up a line
max_line_gap = 20  # maximum gap in pixels between connectable line segments
line_image = np.copy(img) * 0  # creating a blank to draw lines on

# Run Hough on Edge detected image
# Output "lines" is an array containing endpoints of detected line segments
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)
print(lines)
points = []
for line in lines:
    for x1, y1, x2, y2 in line:
        points.append(((x1 + 0.0, y1 + 0.0), (x2 + 0.0, y2 + 0.0)))
        cv2.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5)

lines_edges = cv2.addWeighted(img, 0.8, line_image, 1, 0)
print(lines_edges.shape)
#cv2.imwrite('line_parking.png', lines_edges)

print points
intersections = bot.isect_segments(points)
print intersections

for inter in intersections:
    a, b = inter
    for i in range(3):
        for j in range(3):
            lines_edges[int(b) + i, int(a) + j] = [0, 255, 0]

cv2.imwrite('line_parking.png', lines_edges)

Vous pouvez utiliser quelque chose comme ce bloc de code pour une stratégie visant à supprimer plusieurs intersections dans une petite zone:

for idx, inter in enumerate(intersections):
    a, b = inter
    match = 0
    for other_inter in intersections[idx:]:
        c, d = other_inter
        if abs(c-a) < 15 and abs(d-b) < 15:
            match = 1
            intersections[idx] = ((c+a)/2, (d+b)/2)
            intersections.remove(other_inter)

    if match == 0:
        intersections.remove(inter)

Image de sortie: Cleaned Output

Vous devrez cependant jouer avec la fonction de fenêtrage.

10
Saedeas

que se passe-t-il si vous ajustez maxLineGap ou la taille de votre noyau d'érosion. Alternativement, vous pouvez trouver la distance entre les lignes. Vous devez aller si les paires de lignes disent ax1, ay1 à ax2, ay2 c.f. bx1, by1 à bx2, by2 vous pouvez trouver le point où le dégradé à angle droit (-1 sur le dégradé de ligne) à une ligne de croix b. Géométrie scolaire de base et équations simultanées, quelque chose comme:

x = (ay1 - by1) / ((by2 - by1) / (bx2 - bx1) + (ax2 - ax1) / (ay2 - ay1))
# then
y = by1 + x * (by2 - by1) / (bx2 - bx1)

et comparez x, y avec ax1, ay1

PS: vous devrez peut-être ajouter une vérification de la distance entre ax1, ay1 et bx1, by1, car certaines de vos lignes semblent être des suites d'autres lignes et peuvent être éliminées par la technique du point le plus proche.

1
paddyg