λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

AI/Computer Vision

[Computer Vision] 에지(Edge) κ²€μΆœ

728x90
λ°˜μ‘ν˜•
πŸ‘€ λ³Έ μ˜ˆμ œλŠ” Window10의 VSCode, Python3.11.0둜 μž‘μ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

 

μ˜μƒμ—μ„œ 에지(edge)λŠ” ν•œμͺ½ λ°©ν–₯으둜 ν”½μ…€ 값이 κΈ‰κ²©ν•˜κ²Œ λ°”λ€ŒλŠ” 뢀뢄을 가리킨닀.

 

즉, μ–΄λ‘μš΄ μ˜μ—­μ—μ„œ κ°‘μžκΈ° λ°μ•„μ§€κ±°λ‚˜ λ˜λŠ” λ°˜λŒ€λ‘œ 밝은 μ˜μ—­μ—μ„œ κΈ‰κ²©ν•˜κ²Œ μ–΄λ‘μ›Œμ§€λŠ” 뢀뢄을 에지라고 ν•œλ‹€.

 

일반적으둜 객체와 배경의 경계, λ˜λŠ” 객체와 λ‹€λ₯Έ 객체의 κ²½κ³„μ—μ„œ 에지가 λ°œμƒν•œλ‹€.

 

μ΄λŸ¬ν•œ 에지λ₯Ό μ°Ύμ•„λ‚΄λŠ” μž‘μ—…μ€ μ˜μƒ λ‚΄ 객체의 μœ€κ³½μ„ μ•Œμ•„λ‚Ό 수 μžˆλŠ” μœ μš©ν•œ 방법이며, λ‹€μ–‘ν•œ Computer Vision μ‹œμŠ€ν…œμ—μ„œ 객체 νŒλ³„μ„ μœ„ν•œ μ „μ²˜λ¦¬λ‘œ 에지 κ²€μΆœμ΄ μ‚¬μš©λœλ‹€.

 

기본적으둜 μ˜μƒμ—μ„œ 에지λ₯Ό μ°Ύμ•„λ‚΄λ €λ©΄ ν”½μ…€ κ°’μ˜ λ³€ν™”μœ¨μ΄ 큰 픽셀을 선택해야 ν•œλ‹€.

  • μˆ˜ν•™μ—μ„œ ν•¨μˆ˜ λ˜λŠ” λ°μ΄ν„°μ˜ λ³€ν™”μœ¨μ„ λ―ΈλΆ„(derivative)이라고 ν•œλ‹€.
  • μ˜μƒμ€ 2차원 ν‰λ©΄μ—μ„œ μ •μ˜λœ ν•¨μˆ˜μ΄κΈ° λ•Œλ¬Έμ— μ˜μƒμ„ κ°€λ‘œ, μ„Έλ‘œ λ°©ν–₯으둜 각각 λ―ΈλΆ„ν•΄μ•Ό ν•œλ‹€.

 

μœ„ 마슀크둜 쀑앙 차뢄을 μ΄μš©ν•œ μ˜μƒμ˜ λ―ΈλΆ„ κ·Όμ‚¬λŠ” 마슀크 연산을 μ΄μš©ν•˜μ—¬ ꡬ할 수 μžˆλ‹€.

(β­νŽΈλ―ΈλΆ„ 근사 μˆ˜μ‹μ„ κ·ΈλŒ€λ‘œ μ μš©ν•˜μ—¬ κ³„μ‚°ν•˜λ €λ©΄ ν•„ν„° 마슀크 값에 1/2λ₯Ό κ³±ν•΄μ•Ό ν•˜μ§€λ§Œ 보톡 λ―ΈλΆ„ κ°’μ˜ μƒλŒ€μ  크기λ₯Ό μ€‘μš”μ‹œ ν•˜κΈ° λ•Œλ¬Έμ— μœ„μ™€ 같이 λ‹¨μˆœν™”μ‹œν‚¨ 마슀크λ₯Ό 주둜 μ‚¬μš©ν•œλ‹€.)

 

이 λ§ˆμŠ€ν¬λ“€μ„ μ‚¬μš©ν•˜μ—¬ ν•„ν„°λ§ν•˜λ©΄ κ°€λ‘œ λ°©ν–₯κ³Ό μ„Έλ‘œ λ°©ν–₯으둜 νŽΈλ―ΈλΆ„ν•œ 정보λ₯Ό λ‹΄κ³  μžˆλŠ” 행렬을 얻을 수 μžˆλ‹€.

import cv2
import numpy as np

if __name__ == "__main__":
    img = cv2.imread("test.jpg",cv2.IMREAD_GRAYSCALE)
    cv2.imshow("origin",img)
    x_mask = np.array([-1,0,1])
    y_mask = np.array([[-1],[0],[1]])

    x_edge = cv2.filter2D(img,-1,x_mask)
    cv2.imshow("x_edge",x_edge)
    y_edge = cv2.filter2D(img,-1,y_mask)
    cv2.imshow("y_edge",y_edge)
    # edge ν•©μΉ˜κΈ°
    edges = cv2.magnitude(x_edge.astype(np.float32), y_edge.astype(np.float32))

    cv2.imshow("edge",edges)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

 

 

μœ„μ˜ κ²°κ³Όλ₯Ό 보면 μ΅œμ’… 결과에 작음이 많이 μžˆμ–΄ μ œλŒ€λ‘œ 에지가 κ²€μΆœμ΄ λ˜μ§€ μ•Šμ•˜λ‹€.

 

κ·Έλž˜μ„œ μ‹€μ œ μ˜μƒμ—μ„œλŠ” 1x3, 3x1 마슀크 λŒ€μ‹  작음의 영ν–₯을 쀄일 수 μžˆλ„λ‘ μ’€ 더 큰 크기의 마슀크λ₯Ό μ΄μš©ν•œλ‹€.

 

μ—¬λŸ¬κ°€μ§€μ˜ λ―ΈλΆ„ 근사 λ§ˆμŠ€ν¬κ°€ κ°œλ°œλ˜μ—ˆμ§€λ§Œ 그쀑 κ°€μž₯ 널리 μ‚¬μš©λ˜λŠ” 것은 μ†Œλ²¨ ν•„ν„°(Sobel Filter) 마슀크 이닀.

import cv2
import numpy as np

if __name__ == "__main__":
    img = cv2.imread("test.jpg",cv2.IMREAD_GRAYSCALE)
    cv2.imshow("origin",img)

    # sobel ν•„ν„° 적용
    sobel_x = cv2.Sobel(img,cv2.CV_32F,1,0,ksize=3) # μˆ˜ν‰ 에지
    cv2.imshow("x_edge",sobel_x)
    sobel_y = cv2.Sobel(img,cv2.CV_32F,0,1,ksize=3) # 수직 에지
    cv2.imshow("y_edge",sobel_y)
    # edge ν•©μΉ˜κΈ°
    edges = cv2.magnitude(sobel_x,sobel_y)
    # μ •κ·œν™”
    edges = cv2.normalize(edges, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)

    # Thresholding (에지 κ°•μ‘°)
    _, edges = cv2.threshold(edges, 50, 255, cv2.THRESH_BINARY)
    
    cv2.imshow("edge",edges)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

 

μž„κ³„κ°’μ„ μ‘°μ •ν•˜μ—¬ λΆˆν•„μš”ν•œ μž‘μŒμ„ μ œκ±°ν•˜λ©΄ 쒀더 에지가 잘 κ²€μΆœλœλ‹€.

 

 

Sobel 마슀크λ₯Ό μ‚¬μš©ν•˜λŠ” 방법은 κ΅¬ν˜„μ΄ κ°„λ‹¨ν•˜κ³  λΉ λ₯΄κ²Œ λ™μž‘ν•˜κΈ° λ•Œλ¬Έμ— 많이 μ‚¬μš©λ˜λ‚˜, κ·Έλž˜λ””μ–ΈνŠΈ ν¬κΈ°λ§Œμ„ κΈ°μ€€μœΌλ‘œ 에지 픽셀을 κ²€μΆœν•˜κΈ° λ•Œλ¬Έμ— μž„κ³„κ°’μ— λ―Όκ°ν•˜κ³  에지 픽셀이 λ‘κ»κ²Œ ν‘œν˜„λ˜λŠ” 문제점이 μžˆλ‹€.

 

이런 단점을 ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ‹€μŒμ˜ 쑰건을 λ§Œμ‘±ν•˜λŠ” Canny Edgeκ°€ λ‚˜μ™”λ‹€.

  • μ •ν™•ν•œ κ²€μΆœ : 에지λ₯Ό κ²€μΆœν•˜μ§€ λͺ»ν•˜κ±°λ‚˜ λ˜λŠ” 에지가 μ•„λ‹Œλ° μ—μ§€λ‘œ κ²€μΆœν•˜λŠ” ν™•λ₯  μ΅œμ†Œν™”.
  • μ •ν™•ν•œ μœ„μΉ˜ : μ‹€μ œ μ—μ§€μ˜ 쀑심을 찾아야함.
  • 단일 에지 : ν•˜λ‚˜μ˜ μ—μ§€λŠ” ν•˜λ‚˜μ˜ 점으둜 ν‘œν˜„.

 

CannyλŠ” λ‹€μŒκ³Ό 같은 μˆ˜ν–‰κ³Όμ •μ„ 톡해 κ²°κ³Όκ°€ λ‚˜μ˜¨λ‹€.

  1. κ°€μš°μ‹œμ•ˆ 필터링
  2. κ·Έλž˜λ””μ–ΈνŠΈ 계산(3x3 Sobel 마슀크λ₯Ό μ‚¬μš©)
  3. λΉ„μ΅œλŒ€ μ–΅μ œ
  4. 이쀑 μž„κ³„κ°’μ„ μ΄μš©ν•œ νžˆμŠ€ν…Œλ¦¬μ‹œμŠ€ 에지 νŠΈλž˜ν‚Ή

 

μ΄λ ‡κ²Œ λ³΅μž‘ν•œ 과정을 λ”°λ‘œ 거치치 μ•Šκ³  Canny ν•¨μˆ˜λ‘œ μ‰½κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€.

import cv2

if __name__ == "__main__":
    img = cv2.imread("test.jpg",cv2.IMREAD_GRAYSCALE)
    cv2.imshow("origin",img)
    canny = cv2.Canny(img,threshold1=100,threshold2=200)
    cv2.imshow("canny",canny)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

 

μž„κ³„κ°’μ„ μ‘°μ ˆν•΄ μ’€ 더 μ„¬μ„Έν•˜κ²Œ κ²€μΆœ ν•  수 μžˆλ‹€.

 

 

μ΄λ ‡κ²Œ κ²€μΆœν•œ 에지λ₯Ό 가지고 직선 κ²€μΆœμ„ ν•  수 μžˆλ‹€.

 

μ˜μƒμ—μ„œ 직선을 μ°ΎλŠ” 방법은 ν—ˆν”„ λ³€ν™˜(Hough Transform) 기법이 널리 μ‚¬μš©λœλ‹€.

  • 2차원 xy μ’Œν‘œμ—μ„œ μ§μ„ μ˜  방정식을 νŒŒλΌλ―Έν„° κ³΅κ°„μœΌλ‘œ λ³€ν˜Έλ‚˜ν•˜μ—¬ 직선을 μ°ΎλŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€.
 

Hough transform - Wikipedia

From Wikipedia, the free encyclopedia Method of detecting shapes within images The Hough transform is a feature extraction technique used in image analysis, computer vision, pattern recognition, and digital image processing.[1] [2] The purpose of the techn

en.wikipedia.org

 

import cv2
import numpy as np
if __name__ == "__main__":
    img = cv2.imread("test.jpg")
    cv2.imshow("origin",img)

    img_g = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(img_g,50,150)

    lines = cv2.HoughLinesP(edges,1,np.pi / 180, threshold=100,minLineLength=50,maxLineGap=10)

    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line[0]
            cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)  # μ΄ˆλ‘μƒ‰μœΌλ‘œ 직선 그리기
    
    cv2.imshow("Canny Edges", edges)
    cv2.imshow("Detected Lines", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

 

 

ν—ˆν”„λ³€ν™˜ 방법은 μ§μ„ λΏλ§Œ μ•„λ‹ˆλΌ 원도 κ²€μΆœν•  수 μžˆλ‹€.

import cv2
import numpy as np

if __name__ == "__main__":
    # 이미지 읽기
    img = cv2.imread("test3.jpg")
    cv2.imshow("Original Image", img)

    # κ·Έλ ˆμ΄μŠ€μΌ€μΌ λ³€ν™˜
    img_g = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Canny 엣지 κ²€μΆœ
    edges = cv2.Canny(img_g, 50, 150)

    # ν—ˆν”„ λ³€ν™˜μ„ μ‚¬μš©ν•˜μ—¬ 원 κ²€μΆœ (엣지 이미지 μ‚¬μš©)
    circles = cv2.HoughCircles(edges, 
                                cv2.HOUGH_GRADIENT, 
                                dp=1, 
                                minDist=30,  # 원 κ°„μ˜ μ΅œμ†Œ 거리 μ‘°μ •
                                param1=50,  # Canny 엣지 κ²€μΆœμ˜ μƒν•œκ°’ μ‘°μ •
                                param2=30,   # 원 κ²€μΆœμ˜ μž„κ³„κ°’ μ‘°μ •
                                minRadius=10, # μ΅œμ†Œ λ°˜μ§€λ¦„ μ‘°μ •
                                maxRadius=100) # μ΅œλŒ€ λ°˜μ§€λ¦„ μ‘°μ •

    # 원 그리기
    if circles is not None:
        circles = np.uint16(np.around(circles))
        for i in circles[0, :]:
            # μ›μ˜ 쀑심 그리기
            cv2.circle(img, (i[0], i[1]), 5, (0, 255, 0), -1)
            # 원 그리기
            cv2.circle(img, (i[0], i[1]), i[2], (255, 0, 0), 2)

    # κ²°κ³Ό 이미지 ν‘œμ‹œ
    cv2.imshow("Canny Edges", edges)
    cv2.imshow("Detected Circles", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

 

 

728x90
λ°˜μ‘ν˜•