OpenCV Review 06

2400·2025년 3월 15일
post-thumbnail

opencv의 시간효율성

import cv2 as cv
import numpy as np
import time

def my_cvtGray1(bgr_img):
    g = np.zeros([bgr_img.shape[0], bgr_img.shape[1]])
    for r in range(bgr_img.shape[0]):
        for c in range(bgr_img.shape[1]):
            g[r, c] = 0.114 * bgr_img[r,c,0] + 0.587 * bgr_img[r,c,1] + 0.299 * bgr_img[r,c,2]
    return np.uint8(g)

def my_cvtGray2(bgr_img):
    g = np.zeros([bgr_img.shape[0], bgr_img.shape [1]])
    g = 0.114 * bgr_img[:,:,0] + 0.587 * bgr_img[:,:,1] + 0.299 * bgr_img[:,:,2]
    return np.uint8(g)

img = cv.imread("./.data/frieren.png")

start = time.time()
my_cvtGray1(img)
print('My time1:', time.time()-start)

start = time.time()
my_cvtGray2(img)
print('My time2:', time.time()-start)

start = time.time()
cv.cvtColor(img,cv.COLOR_BGR2GRAY)
print('OPENCV time:', time.time()-start)

OpenCV는 C와 C++로 함수를 작성하고 인텔 마이크로프로세서에 최적화하는등의 노력으로 비교적 빠른 속도를 보여준다. 근데 2와 opencv에서 2가 더 빨랐음ㅇㅇ.. 상황에 맞게 쓰면 될 것 같다.

에지 검출 알고리즘에 대한 정리

에지 검출은 영상 처리에서 중요한 과정으로, 물체의 경계를 찾아내는 기술이다.

  • 물체 내부: 명암(intensity)이 서서히 변함
  • 물체 경계(에지): 명암이 급격히 변함

에지 연산자

에지 검출을 위한 다양한 연산자가 있다

  1. 1차 미분 연산자

    • Sobel 연산자
    • Prewitt 연산자
    • Roberts 연산자
  2. 2차 미분 연산자

    • Laplacian 연산자
    • LoG (Laplacian of Gaussian)
    • DoG (Difference of Gaussian)
  3. 최적 에지 검출기

    • Canny 에지 검출기

소벨 연산자의 적용

소벨(Sobel) 연산자는 가장 널리 사용되는 에지 검출 방법 중 하나다.

  1. 원리: 이미지의 각 픽셀에 대해 x방향과 y방향의 기울기(gradient)를 계산

  2. 소벨 마스크:

    Gx = [[-1, 0, 1],       Gy = [[ 1,  2,  1],
          [-2, 0, 2],             [ 0,  0,  0],
          [-1, 0, 1]]             [-1, -2, -1]]
  3. 적용 방법:

    • 각 픽셀에 x방향 마스크(Gx)와 y방향 마스크(Gy)를 컨볼루션
    • 기울기 크기 계산: G = √(Gx² + Gy²)
    • 기울기 방향 계산: θ = tan⁻¹(Gy/Gx)
  4. 장점:

    • 노이즈에 비교적 강함
    • 대각선 에지 검출에 효과적
    • 계산이 간단하고 빠름
  1. 이미지 로드 및 전처리:

    import cv2 as cv
    img = cv.imread('./.data/seulgi3.jpg')
    gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    • 이미지를 로드하고 그레이스케일로 변환. 에지 검출은 보통 컬러 정보보다 명암 정보가 중요하기 때문에 그레이스케일로 변환함.
  2. 소벨 연산자 적용:

    grad_x = cv.Sobel(gray,cv.CV_32F,1,0,ksize=3)
    grad_y = cv.Sobel(gray,cv.CV_32F,0,1,ksize=3)
    • cv.Sobel 함수는 이미지에 소벨 연산자를 적용함.
    • cv.CV_32F: 출력 이미지의 데이터 타입을 32비트 부동소수점으로 지정함.
    • 1,0: x방향의 미분(가로 방향 에지 검출)
    • 0,1: y방향의 미분(세로 방향 에지 검출)
    • ksize=3: 3x3 크기의 소벨 커널을 사용
  3. 결과 변환:

    sobel_x = cv.convertScaleAbs(grad_x)
    sobel_y = cv.convertScaleAbs(grad_y)
    • cv.convertScaleAbs 함수는 음수 값을 포함할 수 있는 기울기 값을 절대값으로 변환하고 0-255 사이의 값으로 변환
    • 이 과정이 필요한 이유는 OpenCV에서 이미지를 표시할 때 8비트 부호 없는 정수(0-255) 형식을 사용하기 때문임
  4. 에지 강도 계산:

    edge_strength = cv.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
    • 각 방향(x, y)의 에지 강도를 결합하여 최종 에지 강도를 계산
    • 각 방향에 0.5의 가중치를 부여하여 두 방향을 균등하게 고려
    • 마지막 파라미터 0은 추가적인 값을 더하지 않는다는 의미

캐니 에지

  1. 이미지 로드 및 전처리:

    import cv2 as cv
    img = cv.imread('./.data/seulgi4.jpg')
    gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    • 이미지를 로드하고 그레이스케일로 변환함. Canny 에지 검출은 그레이스케일 이미지에서 수행된다.
  2. Canny 에지 검출 적용

    canny1 = cv.Canny(gray,50,150)
    canny2 = cv.Canny(gray,100,200)
    • cv.Canny 함수는 입력 이미지에 Canny 에지 검출 알고리즘을 적용

    • 이 함수는 두 개의 임계값(threshold)을 사용

      • 첫 번째 임계값(낮은 임계값): 에지의 시작점을 결정
      • 두 번째 임계값(높은 임계값): 확실한 에지를 결정
    • canny1은 더 낮은 임계값(50,150)을 사용하므로 더 많은 에지를 감지

    • canny2는 더 높은 임계값(100,200)을 사용하므로 더 중요한 에지만 감지

  3. 결과 표시

    cv.imshow('gray',gray)
    cv.imshow('canny1',canny1)
    cv.imshow('canny2',canny2)
    • 원본 그레이스케일 이미지와 두 가지 다른 임계값 설정으로 생성된 Canny 에지 검출 결과를 표시

Canny 에지 검출기 작동 단계
1. 노이즈 제거(가우시안 블러 적용)
2. 기울기 계산(소벨 연산자 사용)
3. 비최대 억제(Non-maximum suppression)
4. 이중 임계값 처리(Double thresholding)
5. 에지 연결(Hysteresis)

에지 맵에서 경계선 찾기

이 코드는 이미지에서 에지를 검출한 후 윤곽선(contours)을 찾고 그리는 과정을 수행.

  1. 이미지 로드 및 전처리

    img = cv.imread("./.data/seulgi4.jpg")
    gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    • 컬러 이미지를 로드하고 그레이스케일로 변환한다.
  2. 캐니 에지 검출

    canny = cv.Canny(gray,100,200)
    • 그레이스케일 이미지에 캐니 에지 검출 알고리즘을 적용한다.
    • 임계값 100, 200을 사용하여 에지를 감지한다.
  3. 윤곽선 찾기

    contour, hierarchy = cv.findContours(canny, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
    • cv.findContours 함수는 이진 이미지(에지 이미지)에서 윤곽선을 찾는다.
    • cv.RETR_LIST: 모든 윤곽선을 계층 구조 없이 리스트로 검출한다.
    • cv.CHAIN_APPROX_NONE: 윤곽선의 모든 점을 저장한다(압축 없음).
  4. 윤곽선 필터링

    lcontour = []
    for i in range(len(contour)):
        if contour[i].shape[0] > 100:
            lcontour.append(contour[i])
    • 윤곽선의 점 개수가 100개 이상인 것만 선택한다.
    • 이 과정은 작은 노이즈나 불필요한 세부 윤곽선을 제거하는 데 도움이 된다.
  5. 윤곽선 그리기

    cv.drawContours(img,lcontour,-1,(0,255,0),3)
    • 원본 이미지 위에 필터링된 윤곽선을 그린다.
    • -1: 모든 윤곽선을 그린다.
    • (0,255,0): 녹색으로 윤곽선을 그린다.
    • 3: 윤곽선의 두께를 3픽셀로 설정한다.
  6. 결과 표시

    cv.imshow('Original',img)
    cv.imshow('Canny',canny)
    • 윤곽선이 그려진 원본 이미지와 캐니 에지 이미지를 표시한다.

허프 변환

허프 변환은 이미지에서 직선, 원, 타원과 같은 기하학적 형태를 검출하는 데 사용되는 알고리즘이다. 이 알고리즘은 이미지 공간의 점들을 매개변수 공간으로 변환함으로써 작동한다.

원형 허프 변환

원은 중심점(x₀, y₀)과 반지름(r)에 의해 정의된다: (x - x₀)² + (y - y₀)² = r²

원형 허프 변환에서는 이미지의 각 에지 픽셀이 매개변수 공간(중심점과 반지름으로 정의된 3차원 공간)에서 가능한 모든 원의 중심들의 집합으로 변환된다.

코드 분석

circle = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, 200, param1=150, param2=20, minRadius=50, maxRadius=120)
  • gray: 입력 그레이스케일 이미지
  • cv.HOUGH_GRADIENT: 검출 방법
  • 1: 해상도 비율 (입력과 출력 이미지 크기의 비율)
  • 200: 검출된 원들 사이의 최소 거리 (픽셀 단위)
  • param1=150: 내부적으로 사용되는 캐니 에지 검출기의 높은 임계값
  • param2=20: 원 중심 검출을 위한 축적 임계값 (값이 작을수록 더 많은 원이 검출됨)
  • minRadius=50: 검출할 원의 최소 반지름
  • maxRadius=120: 검출할 원의 최대 반지름

그 후에는 검출된 각 원을 원본 이미지에 그린다

for i in circle[0]:
    cv.circle(img, (int(i[0]), int(i[1])), int(i[2]), (255,0,0), 2)

여기서 i[0]i[1]은 원의 중심 좌표이고, i[2]는 반지름이다. 원은 파란색(255,0,0)으로 그려지며 선의 두께는 2픽셀임

허프 변환의 응용

원형 허프 변환은 다음과 같은 분야에서 유용하게 사용된다:

  • 동전 검출 및 계수
  • 과일이나 둥근 물체 검출
  • 홍채 검출
  • 시계 및 원형 게이지 인식
  • 공 추적(스포츠 영상 분석)

슈퍼 화소 분할

  1. 라이브러리를 임포트

    • skimage: scikit-image 라이브러리로, 이미지 처리 기능을 제공
    • numpy
    • cv2
  2. 샘플 이미지를 로드

    img = skimage.data.coffee()
    cv.imshow("coffee", cv.cvtColor(img, cv.COLOR_RGB2BGR))
    • skimage.data.coffee()는 scikit-image에 내장된 커피 이미지를 로드함
    • RGB 형식의 이미지를 OpenCV의 BGR 형식으로 변환하여 표시
  3. 첫 번째 SLIC 슈퍼 픽셀 분할을 수행

    slic1 = skimage.segmentation.slic(img, compactness=20, n_segments=600)
    sp_img1 = skimage.segmentation.mark_boundaries(img, slic1)
    sp_img1 = np.uint8(sp_img1 * 255.0)
    • compactness=20: 슈퍼 픽셀의 모양을 조절하는 매개변수로 값이 클수록 더 정규적인 모양이 된다.
    • n_segments=600: 생성할 슈퍼 픽셀의 대략적인 개수이다.
    • mark_boundaries 원본 이미지에 슈퍼 픽셀 경계를 표시한다.
    • 경계가 표시된 이미지를 0-255 범위의 정수로 변환한다.
  4. 두 번째 SLIC 슈퍼 픽셀 분할을 수행한다.

slic2 = skimage.segmentation.slic(img, compactness=40, n_segments=600)
sp_img2 = skimage.segmentation.mark_boundaries(img, slic2)
sp_img2 = np.uint8(sp_img2 * 255.0)
  1. 결과 이미지를 표시
cv.imshow('Super pixels (compact 20)', cv.cvtColor(sp_img1, cv.COLOR_RGB2BGR))
cv.imshow('Super pixels (compact 40)', cv.cvtColor(sp_img2, cv.COLOR_RGB2BGR))
  • 두 슈퍼 픽셀 이미지를 RGB에서 BGR로 변환하여 OpenCV로 표시

정규화 절단 알고리즘을 이용한 영역 분할

  1. 필요한 라이브러리 임포트

    import skimage    # 이미지 처리 라이브러리
    import numpy as np    # 수치 연산 라이브러리 
    import cv2 as cv    # OpenCV 이미지 처리 라이브러리
    import time    # 실행 시간 측정을 위한 라이브러리
  2. 샘플 이미지 로드

    coffee = skimage.data.coffee()    # scikit-image에 내장된 커피 이미지 로드
  3. 세그멘테이션 시간 측정 시작

    start = time.time()    # 현재 시간 기록
  4. SLIC 슈퍼픽셀 분할:

    slic = skimage.segmentation.slic(coffee, compactness=20, n_segments=600, start_label=1)
    • SLIC(Simple Linear Iterative Clustering) 알고리즘으로 이미지를 슈퍼픽셀로 분할
    • compactness=20: 슈퍼픽셀의 모양 규칙성을 조절(값이 클수록 더 정규적인 모양)
    • n_segments=600: 생성할 슈퍼픽셀의 대략적인 개수
    • start_label=1: 레이블 번호의 시작값
  5. 영역 인접 그래프(RAG) 생성

    g = skimage.graph.rag_mean_color(coffee, slic, mode='similarity')
    • 슈퍼픽셀을 노드로 하는 그래프를 생성
    • 각 노드(슈퍼픽셀)간의 색상 유사도에 기반한 가중치를 계산
  6. 정규화 컷 알고리즘 적용

    ncut = skimage.graph.cut_normalized(slic, g)
    • 생성된 그래프에 정규화 컷 알고리즘을 적용하여 최종 세그멘테이션을 수행
    • 이 알고리즘은 그래프를 의미적으로 일관된 부분으로 분할
  7. 처리 시간 출력

    print(coffee.shape, 'Coffee:', time.time()-start, '초 소요')
    • 이미지 크기와 세그멘테이션에 소요된 시간을 출력
  8. 세그멘테이션 결과 시각화

    marking = skimage.segmentation.mark_boundaries(coffee, ncut)
    ncut_coffee = np.uint8(marking * 255.0)
    • 원본 이미지에 세그멘테이션 결과의 경계를 표시
    • 결과 이미지를 8비트 정수형으로 변환
  9. 결과 표시 및 종료

    cv.imshow('Normalized cut', cv.cvtColor(ncut_coffee, cv.COLOR_RGB2BGR))
    cv.waitKey()
    cv.destroyAllWindows()
    • 결과 이미지를 OpenCV로 표시(RGB를 BGR로 변환 필요)
profile
시즌 2의 공부기록 - Artificial Intelligence & AeroSpace

0개의 댓글