Intelligente Kantenerkennung für die Bildverarbeitung (nicht maximale Unterdrückung und hohe und niedrige Schwellenwerte)

Clevere Kantenerkennungsmethode

Der Canny-Operator wird in der Arbeit „Canny J. A computational Approach to Edge Detection [J] veröffentlicht von John F. Canny im Jahr 1986. IEEE Transactions on Pattern Analysis and Machine Intelligence, 1986 (6): 679–698.“ vorgeschlagen.

Erkennungsziel:

  • geringe Fehlerquote. Alle Kanten sollten gefunden werden und es sollte keine falschen Reaktionen geben. Das heißt, die erkannten Kanten müssen möglichst echte Kanten sein.
  • Kantenpunkte sollten gut positioniert sein. Die gefundenen Kanten müssen den realen Kanten möglichst nahe kommen. Das heißt, der Abstand zwischen einem vom Detektor als Kante markierten Punkt und der Mitte der wahren Kante sollte minimal sein.
  • Einzelkantenpunktreaktion. Das bedeutet, dass der Detektor nicht auf mehrere Kantenpixel zeigen sollte, bei denen nur ein einziger Kantenpunkt vorhanden ist.

Clevere Algorithmusschritte

①Gaußsche Unschärfe – GaussianBlur
②Graustufenkonvertierung – cvtColor
③Gradient berechnen – ​​Sobel/Scharr
④Nicht maximale Signalunterdrückung
⑤Binärbild mit hoher und niedriger Schwelle ausgeben – das Verhältnis von hoher und niedriger Schwelle beträgt 2:1 oder 3:1 ist am besten

1. Graustufenkonvertierung

Klicken Sie auf Bildverarbeitungsbild- Graustufenansicht

2. Gaußsche Unschärfe

Klicken Sie zum Anzeigen auf den Gaußschen Filter der Bildverarbeitung

3. Berechnen Sie den Gradienten

Klicken Sie zum Anzeigen auf „Gradienten- und Kantenerkennungsoperatoren der Bildverarbeitung“.

4. Nicht maximale Unterdrückung

Die Unterdrückung nicht maximaler Werte ist ein wichtiger Schritt bei der Kantenerkennung. Im allgemeinen Sinne bezieht sie sich auf das Ermitteln des lokalen Maximalwerts von Pixeln. Vergleichen Sie entlang der Gradientenrichtung den Gradientenwert davor und danach und entfernen Sie ihn, wenn es sich nicht um ein lokales Maximum handelt.
Fügen Sie hier eine Bildbeschreibung ein
In dem von John Canny vorgeschlagenen Canny-Operatorpapier liegt die nicht maximale Unterdrückung nur bei 0 ∘ , 9 0 ∘ , 4 5 ∘ , 13 5 ∘ 0^\circ, 90^\circ, 45^\circ, 135^ \circ09045135 Es wird in vier Gradientenrichtungen ausgeführt, und die Gradientenrichtung jedes Pixels wird entsprechend der Ähnlichkeit durch diese vier Richtungen ersetzt. Diese vier Fälle stellen auch vier verschiedene Gradienten dar, nämlich
G y > ​​​​G x G_y>G_xGy>Gx, und beide haben die gleiche Nummer.
G y > ​​​​G x G_y>G_xGy>Gx, und die beiden haben unterschiedliche Vorzeichen.
G y < ​​​​G x G_y<G_xGy<Gx, und beide haben die gleiche Nummer.
G y < ​​​​G x G_y<G_xGy<Gx, und die beiden haben unterschiedliche Vorzeichen.
Wie in der Abbildung oben gezeigt, kann anhand der Größe des Gradienten in X-Richtung und Y-Richtung beurteilt werden, ob Punkt A nahe an der X-Achse oder der Y-Achse liegt. Der Subpixelwert von Punkt A kann berechnet werden durch die Pixelwerte von A1 und A2. Dasselbe gilt für Punkt B, nicht mehr wiederholen. Die beiden Bilder oben zeigen, dass der Farbverlauf in der Nähe der Y-Achse groß ist, und die beiden Bilder unten zeigen, dass die Pixel in der Nähe der X-Achse groß sind.
Da die Positionen der Punkte A und B durch den Gradienten bestimmt werden, können die Gradientenwerte der Punkte A und B auch anhand des Gradienten des Punkts Q berechnet werden, sodass davon ausgegangen wird, dass die Gradienten des Punkts Q in den vier Richtungen liegen sind G 1 G_1G1G 2 G_2G2G 3 G_3G3G 4 G_4G4
G y > ​​G x G_y>G_xGy>Gx时,w = G x G y , G 1 = ( i − 1 , j ) , G 2 = ( i + 1 , j ) w=\frac{G_x}{G_y},G_1=(i-1,j) ,G_2=(i+1,j)w=GyGx,G1=( ich1 ,j ) ,G2=( ich+1 ,j )
Wenn beide das gleiche Vorzeichen haben:G 3 = ( i − 1 , j − 1 ) , G 4 = ( i + 1 , j + 1 ) G_3=(i-1,j-1),G_4=(i+ 1,j+1)G3=( ich1 ,J1 ) ,G4=( ich+1 ,J+1 )
Wenn die beiden unterschiedliche Vorzeichen haben:G 3 = ( i − 1 , j + 1 ) , G 4 = ( i + 1 , j − 1 ) G_3=(i-1,j+1),G_4=(i+ 1,j-1)G3=( ich1 ,J+1 ) ,G4=( ich+1 ,J1 )
G y < ​​G x G_y<G_xGy<Gx时,w = G y G x , G 1 = ( i , j − 1 ) , G 2 = ( i , j + 1 ) w=\frac{G_y}{G_x},G_1=(i,j-1) ,G_2=(i,j+1)w=GxGy,G1=( ich ,J1 ) ,G2=( ich ,J+1 )
Wenn beide das gleiche Vorzeichen haben:G 3 = ( i + 1 , j − 1 ) , G 4 = ( i + 1 , j − 1 ) G_3=(i+1,j-1),G_4=(i+ 1,j-1)G3=( ich+1 ,J1 ) ,G4=( ich+1 ,J1 )
Wenn die beiden unterschiedliche Vorzeichen haben:G 3 = ( i − 1 , j − 1 ) , G 4 = ( i + 1 , j + 1 ) G_3=(i-1,j-1),G_4=(i+ 1,j+1)G3=( ich1 ,J1 ) ,G4=( ich+1 ,J+1 )
Auf diese Weise kann der Gradientenwert g A = w ∗ G 1 + ( 1 − w ) ∗ G 3 g B = w ∗ G 2 + ( 1 − w ) ∗ G 4 g_A zweier benachbarter Subpixelpunkte ermittelt werden berechnet werden
=w*G_1+(1-w)*G_3\\ g_B=w*G_2+(1-w)*G_4GA=wG1+( 1w )G3GB=wG2+( 1w )G4
Vergleichen Sie die Pixelwerte der drei. Wenn der Pixelwert von Punkt Q größer als der der anderen beiden ist, behalten Sie Punkt Q als Punkt am Rand bei, andernfalls betrachten Sie Punkt Q als redundanten Punkt.
Python-Code:

ef NMS(gradients, direction):
    """ Non-maxima suppression

    Args:
        gradients: the gradients of each pixel
        direction: the direction of the gradients of each pixel

    Returns:
        the output image
    """
    W, H = gradients.shape
    nms = np.copy(gradients[1:-1, 1:-1])

    for i in range(1, W - 1):
        for j in range(1, H - 1):
            theta = direction[i, j]
            weight = np.tan(theta)
            if theta > np.pi / 4:
                d1 = [0, 1]
                d2 = [1, 1]
                weight = 1 / weight
            elif theta >= 0:
                d1 = [1, 0]
                d2 = [1, 1]
            elif theta >= - np.pi / 4:
                d1 = [1, 0]
                d2 = [1, -1]
                weight *= -1
            else:
                d1 = [0, -1]
                d2 = [1, -1]
                weight = -1 / weight

            g1 = gradients[i + d1[0], j + d1[1]]
            g2 = gradients[i + d2[0], j + d2[1]]
            g3 = gradients[i - d1[0], j - d1[1]]
            g4 = gradients[i - d2[0], j - d2[1]]

            grade_count1 = g1 * weight + g2 * (1 - weight)
            grade_count2 = g3 * weight + g4 * (1 - weight)

            if grade_count1 > gradients[i, j] or grade_count2 > gradients[i, j]:
                nms[i - 1, j - 1] = 0

    return nms

5. Duale Schwellenwert-Tracking-Grenze

Legen Sie zwei Schwellenwerte fest: minVal und maxVal. Jede Kante mit einem Gradienten größer als maxVal ist eine echte Kante, während eine Kante unter minVal keine Kante ist. Kanten, die zwischen diesen beiden Schwellenwerten liegen, werden aufgrund ihrer Konnektivität als Kanten oder Nichtkanten klassifiziert und gelten als Teil einer Kante, wenn sie mit einem „zuverlässigen Kanten“-Pixel verbunden sind. Andernfalls ist dies nicht der Fall.
Code wie folgt anzeigen:

def double_threshold(nms, threshold1, threshold2):
    """ Double Threshold
    Use two thresholds to compute the edge.

    Args:
        nms: the input image
        threshold1: the low threshold
        threshold2: the high threshold

    Returns:
        The binary image.
    """

    visited = np.zeros_like(nms)
    output_image = nms.copy()
    W, H = output_image.shape

    def dfs(i, j):
        if i >= W or i < 0 or j >= H or j < 0 or visited[i, j] == 1:
            return
        visited[i, j] = 1
        if output_image[i, j] > threshold1:
            output_image[i, j] = 255
            dfs(i-1, j-1)
            dfs(i-1, j)
            dfs(i-1, j+1)
            dfs(i, j-1)
            dfs(i, j+1)
            dfs(i+1, j-1)
            dfs(i+1, j)
            dfs(i+1, j+1)
        else:
            output_image[i, j] = 0


    for w in range(W):
        for h in range(H):
            if visited[w, h] == 1:
                continue
            if output_image[w, h] >= threshold2:
                dfs(w, h)
            elif output_image[w, h] <= threshold1:
                output_image[w, h] = 0
                visited[w, h] = 1

    for w in range(W):
        for h in range(H):
            if visited[w, h] == 0:
                output_image[w, h] = 0
    return output_image

Der Gesamtcode lautet wie folgt:

# -*- coding: utf-8 -*-
import numpy as np
import cv2
import imgShow as iS

def smooth(image, sigma = 1.4, length = 5):
    """ Smooth the image
    Compute a gaussian filter with sigma = sigma and kernal_length = length.
    Each element in the kernal can be computed as below:
        G[i, j] = (1/(2*pi*sigma**2))*exp(-((i-k-1)**2 + (j-k-1)**2)/2*sigma**2)
    Then, use the gaussian filter to smooth the input image.

    Args:
        image: array of grey image
        sigma: the sigma of gaussian filter, default to be 1.4
        length: the kernal length, default to be 5

    Returns:
        the smoothed image
    """
    # Compute gaussian filter
    k = length // 2
    gaussian = np.zeros([length, length])
    for i in range(length):
        for j in range(length):
            gaussian[i, j] = np.exp(-((i-k) ** 2 + (j-k) ** 2) / (2 * sigma ** 2))
    gaussian /= 2 * np.pi * sigma ** 2
    # Batch Normalization
    gaussian = gaussian / np.sum(gaussian)

    # Use Gaussian Filter
    W, H = image.shape
    new_image = np.zeros([W - k * 2, H - k * 2])

    for i in range(W - 2 * k):
        for j in range(H - 2 * k):
            new_image[i, j] = np.sum(image[i:i+length, j:j+length] * gaussian)

    new_image = np.uint8(new_image)

    return new_image


def get_gradient_and_direction(image):
    """ Compute gradients and its direction
    Use Sobel filter to compute gradients and direction.
         -1 0 1        -1 -2 -1
    Gx = -2 0 2   Gy =  0  0  0
         -1 0 1         1  2  1

    Args:
        image: array of grey image

    Returns:
        gradients: the gradients of each pixel
        direction: the direction of the gradients of each pixel
    """
    Gx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
    Gy = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])

    W, H = image.shape
    gradients = np.zeros([W - 2, H - 2])
    direction = np.zeros([W - 2, H - 2])

    for i in range(W - 2):
        for j in range(H - 2):
            dx = np.sum(image[i:i+3, j:j+3] * Gx)
            dy = np.sum(image[i:i+3, j:j+3] * Gy)
            gradients[i, j] = np.sqrt(dx ** 2 + dy ** 2)
            if dx == 0:
                direction[i, j] = np.pi / 2
            else:
                direction[i, j] = np.arctan(dy / dx)

    gradients = np.uint8(gradients)

    return gradients, direction


def NMS(gradients, direction):
    """ Non-maxima suppression

    Args:
        gradients: the gradients of each pixel
        direction: the direction of the gradients of each pixel

    Returns:
        the output image
    """
    W, H = gradients.shape
    nms = np.copy(gradients[1:-1, 1:-1])

    for i in range(1, W - 1):
        for j in range(1, H - 1):
            theta = direction[i, j]
            weight = np.tan(theta)
            if theta > np.pi / 4:
                d1 = [0, 1]
                d2 = [1, 1]
                weight = 1 / weight
            elif theta >= 0:
                d1 = [1, 0]
                d2 = [1, 1]
            elif theta >= - np.pi / 4:
                d1 = [1, 0]
                d2 = [1, -1]
                weight *= -1
            else:
                d1 = [0, -1]
                d2 = [1, -1]
                weight = -1 / weight

            g1 = gradients[i + d1[0], j + d1[1]]
            g2 = gradients[i + d2[0], j + d2[1]]
            g3 = gradients[i - d1[0], j - d1[1]]
            g4 = gradients[i - d2[0], j - d2[1]]

            grade_count1 = g1 * weight + g2 * (1 - weight)
            grade_count2 = g3 * weight + g4 * (1 - weight)

            if grade_count1 > gradients[i, j] or grade_count2 > gradients[i, j]:
                nms[i - 1, j - 1] = 0

    return nms


def double_threshold(nms, threshold1, threshold2):
    """ Double Threshold
    Use two thresholds to compute the edge.

    Args:
        nms: the input image
        threshold1: the low threshold
        threshold2: the high threshold

    Returns:
        The binary image.
    """

    visited = np.zeros_like(nms)
    output_image = nms.copy()
    W, H = output_image.shape

    def dfs(i, j):
        if i >= W or i < 0 or j >= H or j < 0 or visited[i, j] == 1:
            return
        visited[i, j] = 1
        if output_image[i, j] > threshold1:
            output_image[i, j] = 255
            dfs(i-1, j-1)
            dfs(i-1, j)
            dfs(i-1, j+1)
            dfs(i, j-1)
            dfs(i, j+1)
            dfs(i+1, j-1)
            dfs(i+1, j)
            dfs(i+1, j+1)
        else:
            output_image[i, j] = 0


    for w in range(W):
        for h in range(H):
            if visited[w, h] == 1:
                continue
            if output_image[w, h] >= threshold2:
                dfs(w, h)
            elif output_image[w, h] <= threshold1:
                output_image[w, h] = 0
                visited[w, h] = 1

    for w in range(W):
        for h in range(H):
            if visited[w, h] == 0:
                output_image[w, h] = 0

    return output_image


if __name__ == "__main__":
    # code to read image
    img=cv2.imread('./originImg/Lena.tif')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    smoothed_image = smooth(img)
    gradients, direction = get_gradient_and_direction(smoothed_image)
    nms = NMS(gradients, direction)
    output_image = double_threshold(nms, 40, 100)
    imageList = []
    origin_img = [img, 'origin_img']
    imageList.append(origin_img)
    # smoothed= [smoothed_image, ' smoothed_image']
    # imageList.append(smoothed)
    gradient = [gradients, 'gradients']
    imageList.append(gradient)
    nms = [nms, 'nms']
    imageList.append(nms)
    output_images = [output_image, 'output_image']
    imageList.append(output_images)
    iS.showMultipleimages(imageList, 25, 25, './ProcessedImg/canny.jpg')

Testergebnisse:
Fügen Sie hier eine Bildbeschreibung ein

Ich denke du magst

Origin blog.csdn.net/weixin_42491648/article/details/131348643
Empfohlen
Rangfolge