Ch4. 에지와 영역

wonnie1224·2023년 4월 5일
1

4.1 에지 검출

에지 검출 알고리즘은 아래의 명암 변화 특성 활용함

  • 물체 내부 - 명암이 서서히 변함
  • 물체 경계 - 명암이 급격히 변함

4.1.1 영상의 미분

  • 디지털 영상 : 정수 좌표 사용 → x의 최소 변화량 = 1이므로 델타 x = 1 사용

Untitled

  • 식을 영상 f에 적용 ➡️ 필터 u로 컨볼루션하여 구현
  • 필터 u의 중심점 : 왼쪽 화소
  • 필터 u = 에지 연산자(edge operator)

Untitled

4.1.2 에지 연산자

현실 세계의 영상 - 물체 경계에서 명암 변화가 계단모양이 아님

🔍 현실 세계를 반영하려면?

📌 현실 세계의 램프 에지

  • 명암이 몇 화소에 걸체 변하는 램프 에지(ramp edge) 발생
  • 에지의 정확한 위치를 정하는 위치 찾기; localization 문제 발생

Untitled

필터 u로 컨볼루션 2번 적용

⇒ [-1 -2 1] 필터 1개로 가능

Untitled

에지에서

  • 1차 미분 영상 : 봉우리 발생
  • 2차 미분 영상 : 영교차(zero crossing) 발생
    • 영교차 : 좌우에서 부호 다름 & 자신은 0인 위치

➡️ 에지 검출 : 1차 미분에서 봉우리 찾기 or 2차 미분에서 영교차 찾는 일

Untitled

📌 1차 미분에 기반한 에지 연산자

  • 1차원에서 설계한 연산자 ➡️ 2차원으로 확장하는 식
  • x & y 방향의 2개 연산자 사용

Untitled

  • 필터 3x3 크기로 확장하면 잡음 흡수하여 성능 up
  • 널리 쓰이는 에지 연산자 2 종류

Untitled

에지 강도 & 에지 방향

Untitled

Untitled

  • 에지 강도(edge strength) : 에지일 가능성 나타냄
  • 에지 방향(edge direction) : 에지의 진행 방향 나타내며, 그레이디언트 방향을 90도 회전한 방향으로 정의
    • 그레이디언트(gradient) : (f’y, f’x)
    • f’ : 프레윗 / 소벨 연산자 적용한 결과 영상
  • f’x, f’y맵 & 에지 강도 맵 & 에지 방향 맵 ; 음수 포함하는 실수 ⇒ 32비트 실수형인 cv.CV_32F로 지정

📌 소벨 연산자의 적용

import cv2 as cv

img=cv.imread('soccer.jpg')
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)

grad_x=cv.Sobel(gray,cv.CV_32F,1,0,ksize=3)	# 소벨 연산자 적용
grad_y=cv.Sobel(gray,cv.CV_32F,0,1,ksize=3)

sobel_x=cv.convertScaleAbs(grad_x)	# 절대값을 취해 양수 영상으로 변환
sobel_y=cv.convertScaleAbs(grad_y)

edge_strength=cv.addWeighted(sobel_x,0.5,sobel_y,0.5,0)	# 에지 강도 계산

# 결과 영상 윈도우에 디스플레이
cv.imshow('Original',gray)
cv.imshow('sobelx',sobel_x)
cv.imshow('sobely',sobel_y)
cv.imshow('edge strength',edge_strength)

cv.waitKey()
cv.destroyAllWindows()
  • Sobel() : Sobel(명암 영상으로 변환, 32비트 실수형, x방향 or y방향 연산자 결정, 그림 크기)
  • convertScaleAbs() 함수 : 부호 없는 8비트형인 CV_8U(numpy의 uint8과 동일) 맵 만듦
    • 0보다 작으면 0 / 225보다 크면 255로 바꾸어 기록
  • addWeighted(img1,a,img2,b,c) : img1 x a + img2 x b + c 계산
    • 이때 img1 & img2는 같은 데이터형이어야 함

🌿 결과 영상

Untitled

  • x 방향 연산자 적용 → 수직 방향의 에지 선명

Untitled

  • y 방향 연산자 적용 → 수평 방향의 에지 선명

Untitled

Untitled

4.2 케니 에지

📌 케니 알고리즘

비최대 억제 (NMS : Non-Maximum Suppression)

  • 한 두께 에지 출력 위해 이용
  • 에지 방향에 수직인 2개 이웃 화소와 비교해 자신이 최대일 때만 에지로 살아남음

Untitled

  • 거짓 긍정(false positive) : 실제 에지가 아닌데 에지로 검출된 화소
  • 거짓 긍정 줄이기 위해 2개의 이력 임계값(T_low, T_high) 이용한 에지 추적을 추가로 적용함

🌿 절차

1) 에지 추적 시작 화소 설정 : 에지 강도가 T_high 이상인 에지 화소

⇒ 실제 에지일 가능성이 높은 곳에서 추적 시작하기 위해서임

2) T_low 임곗값 넘는 에지 대상으로 추적 진행

⇒ 추적 이력이 있는(= T_low 임곗값 넘는 에지) 이웃 가진 화소는 에지 강도 낮아도 실제 에지로 인정하는 전략!

💡 T_high : T_low의 2~3배로 설정 권고

  • OpenCV에서 Canny 함수 제공

📌 케니 에지 실험

import cv2 as cv

img=cv.imread('soccer.jpg')	# 영상 읽기

gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)

canny1=cv.Canny(gray,50,150)	# Tlow=50, Thigh=150으로 설정
canny2=cv.Canny(gray,100,200)	# Tlow=100, Thigh=200으로 설정

cv.imshow('Original',gray)
cv.imshow('Canny1',canny1)
cv.imshow('Canny2',canny2)

cv.waitKey()
cv.destroyAllWindows()

Untitled

Untitled

📌 에지 검출의 한계

명암 변화에만 의존해서

⇒ 물체 경계에 나타난 에지 & 그림자로 인해 발생한 가짜 에지 구분 불가능

⇒ 인접한 두 물체가 비슷한 명암 가져 명암 변화가 적으면 에지 발생 X

+) 사람 - 에지 검출 시 물체 모양 표현한 3차원 모델 & 눈에 비치는 2차원 겉모습 모델 동시 사용

4.3 직선 검출

에지맵 - 에지의 연결 관계가 명시적으로 표현 X

⇒ 에지 연결 ➡️ 경계선 ➡️ 직선으로 변환하면 물체 표현 / 인식에 유리

4.3.1 경계선 찾기

📌 에지 맵에서 경계선 검출

  • 8-연결 에지 화소 연결해 경계선(contour) 구성 가능
  • findContours 함수가 수행

Untitled

ex 4-3 : 에지 맵에서 경계선 검출 후 길이가 임계값 이상인 경계선만 취함

import cv2 as cv
import numpy as np

img=cv.imread('soccer.jpg')	 # 영상 읽기
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
canny=cv.Canny(gray,100,200)

contour,hierarchy=cv.findContours(canny,cv.RETR_LIST,cv.CHAIN_APPROX_NONE)

lcontour=[]   
for i in range(len(contour)):
    if contour[i].shape[0]>100:	# 길이가 100보다 크면
        lcontour.append(contour[i])
    
cv.drawContours(img,lcontour,-1,(0,255,0),3)
             
cv.imshow('Original with contours',img)    
cv.imshow('Canny',canny)    

cv.waitKey()
cv.destroyAllWindows()

Untitled

🌿 코드 설명

1) cv.Canny() : Canny 함수로 에지맵 구하기

2) cv.findContours(canny,cv.RETR_LIST,cv.CHAIN_APPROX_NONE) : 경계선 찾아 contour 객체에 저장

  • 인수1 : 경계선 찾을 에지 영상
  • 인수2 : 구멍 있는 경우에 바깥쪽 경계선과 안의 구멍 경계선을 계층적으로 찾는 방식 지정 cv.RETER_LIST : 맨 바깥쪽 경계선만 찾는 방식
  • 인수3 : 경계선을 표현하는 방식 지정 cv.CHAIN_APPROX_NONE : [그림 4-9]처럼 모든 점을 기록 cv.CHAIN_APPROX_SIMPLE : 직선에 대 해서는 양 끝점만 기록 cv. CHAIN_APPROX_TC89_L1 or cv.CHAIN_APPROX_ TC89_KCOS : Teh-Chin 알고리즘으로 굴곡이 심한 점을 찾아 그들만 기록

3) for문 ; 길이가 50 이상인 경계선만 골라 Icontour 객체에 저장

findContours 함수 - 시작점부터 끝점까지 추적한 후, 역추적하여 시작점으로 돌아오도록 경계선을 표현

⇒ if문에서 길이 기준 100으로 설정 → 실제로는 길이가 50 이상인 경계선만 남김

4) drawContours(img,lcontour,-1,(0,255,0),3) : 영상에 경계선 그림

  • 인수1 : 경계선을 그려 넣을 영상
  • 인수2 : 경계선, 여기선 Icontour 객체로 설정
  • 인수3
    • -1로 설정 ; 모든 경계선 그려줌
    • 양수로 설정 ; 해당 번호의 경계선 하나만 그려줌
  • 인수4 & 인수5 : 경계선 색 & 두께 지정

4.3.2 허프 변환

이웃한 에지 연결하여 경계선 검출 ⇒ 같은 물체 구성하는 에지 자잘하게 끊겨 나타나는 경우 문제 발생

➡️ 허프 변환 적용 시 끊긴 에지 모아 선분 / 원 등 검출 가능!

📌 원리

Untitled

📌 구현 ; 실제 상황에서 구현 시 고려 사항

  1. 현실엔 점 #개, 점들이 완벽히 일직선을 이루지 X

    ⇒ (a,b) 공간을 이산화하여 해결

    ex)

    (1) a와 b의 범위를 [-1000,1000]으로 설정

    (2) 각각을 크기가 20인 구간 50개로 나누어 칸이 50×50=2500인 2차원 누적 배열 v 생성

    (3) v를 0으로 초기화한 후 각각의 직선은 자신이 지나는 모든 칸에 1씩 투표

  1. 투표가 이뤄진 누적 배열에 잡음 #

Untitled

[그림 4-11] 가상의 누적 배열

  • 비최대 억제(non-maximum suppression) : 잡음이 많은 상태에서 한 점을 결정할 때 컴퓨터 비전이 사용하는 방법, 지역 최대가 아닌 점을 억제하고 지역 최대(local maximum)만 남기는 전략
    • 지역 최대 점 = 극점(extreme point)
    • 캐니 에지에서는 [그림 4-8]의 비최대 억제를 사용
  • 극점만 남기더라도 잡음이 있어 보통 임곗값을 같이 적용
  • [그림 4-11] ; 8-이웃 사용 시 색 칠한 점이 극점
    • 비최대 억제로 점이 3개 남음 ⇒ 추가로 임곗값을 5로 설정하면 노란색으로 칠한 점 2개만 최종 선택됨
  1. 직선 방정식 y=ax+b를 사용하면 기울기 a가 무한대인 경우 투표 불가능

    ⇒ 극좌표에서의 직선의 방정식 도입해서 해결

    Untitled

    • 허프 변환 - 직선의 방정식은 알려주지만, 직선의 양 끝점은 모름

    ⇒ 양 끝점을 알아 내려면 비최대 억제 과정에서 극점을 형성한 화소를 찾아 가장 먼 곳에 있는 두 화소를 계산하는 과정 추가로 필요

    • 허프 변환은 직선뿐만 아니라 어떤 도형이라도 검출가능

4.3.3 RANSAC

  • 강인한 추정robust estimation : 아웃라이어를 걸러내는 과정을 가진 추정 기법

ex) 평균X, 중앙값 : 아웃라이어를 배제 → 강인한 추정 기법

  • RANSAC (Random Sample Consensus) : 인라이어와 아웃라이어가 섞여 있는 상황에서 인라이어를 찾아 최적 근사하는 기법

💡 에지 영상에서 선분을 추정하는 문제에 RANSAC 을 적용한 경우

Untitled

1) 랜덤하게 두 점을 선택하고 두 점을 지나는 직선을 계산

2) 일정한 양의 오차 허용 → 직선에 일치하는 점의 개수 count

3) 개수가 임곗값 d를 넘지 못하면 가능성이 없다고 보고 버림

  • 1차 시도 : 직선에 일치하는 점이 2개 → d=5라면 첫 번째 시도는 버림
  • 2차 시도 : 직선에 일치하는 점이 7개 → 임곗값 d 넘음 ⇒ 점 7개로 최적 직선 추정 후, 추정 오류가 임곗값 e보다 작으면 후보군에 추가

4) 1~3의 과정 반복 마치면 후보군에서 최적을 찾아 출력한다.

➡️ 반복 횟수 많을수록 진짜 직선 찾을 가능성 up, 시간은 # 소요

➡️ 난수 사용 → 매 실행마다 다른 결과 출력

4.4 영역 분할

  • 영역 분할 (region segmentation) : 물체가 점유한 영역을 구분하는 작업

물체의 경계를 지정하는 에지가 완벽하다면 영역 분할이 따로 필요 X

but 명암 변화가 낮은 곳에서 뚫려 폐곡선을 형성 못하는 경우 #

사람 - 의미 있는 단위로 분할하는 방식인 의미 분할 사용

여기선 의미 전혀 고려 X, 명암 또는 컬러의 변화만 보고 영역을 분할하는 고전적인 기법만 다룸

4.4.1 배경이 단순한 영상의 영역 분할

  • 3.2절의 이진화 알고리즘을 확장해 영역 분할에 쓸 수 O

ex1) 오츄 이진화에서 여러 임곗값을 사용하도록 확장한 알고리즘

ex2) 군집화 알고리즘 : (r,g,b) 3개 값으로 표현된 화소를 샘플로 보고, 3차원 공간에서 클러스터링 수행하여 화소 각각에 클러스터 번호를 부여

⇒ 이렇게 구성된 영상에서 연결 요소를 찾아 영역으로 간주

  • 워터셰드(watershed) : 비가 오면 오목한 곳에 웅덩이가 생기는 현상을 모방하는 연산, 즉 낮은 곳부터 물 채우는 연산
    • 워터 셰드를 확장해 영역 분할에 활용 가능

Untitled

4.4.2 슈퍼 화소 분할

영상을 아주 작은 영역으로 분할해 다른 알고리즘의 입력으로 사용

  • 슈퍼 화소 (super-pixel) : 화소보다 크지만 물체보다 작은 영역

슈퍼 화소 알고리즘 - SLIC (Simple Linear Iterative Clustering) 알고리즘

  • k-means clustering 알고리즘과 비슷하게 작동, but 처리 과정이 단순 & 성능 좋아 인기

1) 입력 영상 - k개 화소를 군집 중심으로 지정

  • 등간격으로 패치로 분할 후, 패치 중심을 군집 중심으로 간주

Untitled

  • 화소 ; 색상을 나타내는 3개 값 + 위치 나타내는 2개 값 ⇒ 5차원 벡터 (R,G,B.x,y)로 표현
    • C_1의 컬러 값이 (100.120.50) + 위치 정보 (x,y)=(1.2) 결합 → (100,120,50,1,2)

화소를 가장 가까운 군집 중심에 할당 단계 & 군집 중심 갱신 단계 반복

2) 화소 할당 단계 : 화소 각각에 대해 주위 4개 군집 중심 ~ 자신까지 거리 계산 ⇒ 가장 유사한 군집 중심에 할당

3) 화소 할당이 끝나면 각 군집 중심은 자신에게 할당된 화소를 평균해 군집 중심을 갱신

4) 모든 군집 중심의 이동량의 평균 < 임계치 ⇒ 수렴했다고 판단하고 알고리즘 멈춤

📌 SLIC 알고리즘으로 슈퍼 화소 분할

  • skimage 라이브러리의 slic 함수 사용
import skimage
import numpy as np
import cv2 as cv

img=skimage.data.coffee()
cv.imshow('Coffee image',cv.cvtColor(img,cv.COLOR_RGB2BGR))

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)

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)

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))

cv.waitKey()
cv.destroyAllWindows()

🌿 코드 설명

두 라이브러리(skimage, OpenCV) 모두 numpy 배열로 영상을 표현하기 때문에 호환 가능

cv.imshow('Coffee image',cv.cvtColor(img,cv.COLOR_RGB2BGR)) : skimage - RGB 순서 / OpenCV - BGR 순서로 저장 → cvtColor 함수로 BGR로 변환하여 출력

skimage.segmentation.slic(img,compactness=20,n_segments=600) : skimage의 slic 함수로 슈퍼 화소 분할을 수행 결과를 slic1 객체에 저장

  • 인수1 : 분할할 영상
  • compactness 인수 : 슈퍼 화소의 모양을 조절 값이 클수록 네모에 가까운 모양이 만들어지는 대신 슈퍼 화소의 색상 균일성은 희생
  • n_segments 인수 : 슈퍼 화소의 개수 지정 (알고리즘 설명에서 사용한 k)

sp_img1=skimage.segmentation.mark_boundaries(img,slic1) : slic1 객체의 분할 정보를 img 영상에 표시하고 결과를 sp_img1 객체에 저장

sp_img1=np.uint8(sp_img1*255.0) : 0~1 사이의 실수로 표현된 sp_img1을 0~255 사이로 바꾸고 uint8 형으로 변환

Untitled

  • compactness 인수 클수록 네모 모양 유지 잘 됨, but 슈퍼 화소의 색상 균일성 감소

4.4.3 최적화 분할

이전의 분할 알고리즘 - 지역적 명암 변화만 고려

ex) 물체 색 & 배경색 비슷 → 경계 제대로 형성 X

  • 지역적 명암 변화 & 전역적 정보 함께 고려

: 지역적으로 아주 약한 색상 변화지만, 전역적으로 유리한 측면 있으면 그곳을 물체 경계로 간주

➡️ 영상 그래프 표현 후 분할을 최적화 문제로 해결

📌 영상의 그래프 표현

Untitled

📌 정규화 절단 알고리즘

  • 화소 : 노드
  • f(v) : 색상 & 위치 결합한 5차원 벡터
  • 유사도 : 에지 가중치

원래 영역 C → 2개의 영역 C1, C2로 분할했을 때 cut

cut : 영역 분할의 좋은 정도를 측정해주는 목적 함수

  • C1, C2 분할 굿 → C1에 속한 v_p & C2에 속한 v_q의 유사도 작음 → cut(C1,C2) 작음

⇒ 작을 수록 분할 잘 된 거임

  • but C1, C2 영역 클수록 둘 사이에 에지 # → cut(C1,C2) 커짐

⇒ 영역을 자잘하게 분할하는 경향

Untitled

ncut : cut을 정규화하여 영역 크기에 중립 되게 한 식

Untitled

ncut 함수 - 목적 함수로 사용 → 분할을 최적화 문제로 해결 가능

ncut이 작을 수록 좋은 분할 ⇒ 최소화 문제임!

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

import skimage
import numpy as np
import cv2 as cv
import time

coffee=skimage.data.coffee()  # coffee 영상을 읽어

start=time.time()  # 분할하는 데 걸리는 시간을 측정해 출력
slic=skimage.segmentation.slic(coffee,compactness=20,n_segments=600,start_label=1)
g=skimage.future.graph.rag_mean_color(coffee,slic,mode='similarity') 
ncut=skimage.future.graph.cut_normalized(slic,g)	# 정규화 절단
print(coffee.shape,' Coffee 영상을 분할하는데 ',time.time()-start,'초 소요')

marking=skimage.segmentation.mark_boundaries(coffee,ncut)  # 원래 영상인 coffee에 영역 분할 정보를 담은 ncut 맵을 이용하여 영역 경계를 표 시하고 marking 객체에 저장
ncut_coffee=np.uint8(marking*255.0)  # 0~1 사이의 실수를 가진 marking을 0~255 사이의 uint8 형으로 변환

cv.imshow('Normalized cut',cv.cvtColor(ncut_coffee,cv.COLOR_RGB2BGR))  # RGB로 표현된 분할 영상을 BGR로 변환하여 윈도우에 디스플레이

cv.waitKey()
cv.destroyAllWindows()

slic 함수는 영상을 600개 슈퍼 화소로 분할해 slic1 객체에 저장한다.

rag_mean_color 함수 : 슈퍼 화소를 노드로 사용하고 'similarity'를 에지 가중치로 사용한 그래프를 구성하여 8 객체에 저장

cut_normalized 함수 : slic1과 g 객체 정보를 이용하여 정규화 절단을 수행하고 결과를 ncut 객체에 저장

ncut : 화소에 영역의 번호를 부여한 맵

4.5 대화식 분할

4.5.1 능동 외곽선

능동 외곽선(active contour) 알고리즘 : 사용자가 물체 내부에 초기 곡선을 지정하면 곡선을 점점 확장하면서 물체 외곽선으로 접근하는 방법

  • 곡선이 꿈틀대면서 에너지 최소인 상태를 찾아가서 스네이크snake라는 별명

스네이크를 구현 위한 곡선 표현 방법 ➡️ 스프라인 곡선(spline curve)

  • 스프라인 곡선; 2차원 상의 곡선이므로 g(1) = (y(1), x(1))로 표현
  • 매개변수 1은 [0.1] 범위의 실수인데 폐곡선을 이루기 위해 g(0) g(1)이다. 디지털 영상은 이산 공간이기 때문에 1을 0, 1, ..., n으로 표현 Untitled

스네이크 곡선 g의 에너지 E = 영상 에너지 + 내부 에너지 + 도메인 에너지

Untitled

Untitled

  • 영상 E : 곡선이 에지에 위치하도록 유도
  • 내부 E : 곡선이 매끄러운 모양 되도록 유도
  • 도메인 E : 분할하려는 특정 물체의 모양 정보 잘 유지하도록 유도

➡️ 스네이크 알고리즘이 풀어야 하는 문제 : 에너지 최소로 하는 최적 곡선 찾는 최적화 문제

Untitled

💡 Kass의 탐색 방법을 Williams가 개선한 알고리즘

Kass의 탐색 방법 : 사용자 지정한 초기 곡선 g0

g0에서 g1, g1~g2, …, gt~gt+1 찾아가는 과정을 수렴할 때까지 반복

Untitled

4.5.2 GrabCut

GrabCut 알고리즘 : 사용자가 붓으로 물체와 배경을 초기 지정

1) 파란 화소 - 물체 & 빨간 화소 - 배경

⇒ 화소를 가지고 물체 히스토그램과 배경 히스토그램 만듦

2) 나머지 화소들 - 두 히스토그램과 유사성을 따져 물체일 확률 & 배경일 확률 추정

⇒ 물체 영역과 배경 영역을 갱신

3) 새로운 정보로 히스토그램을 다시 만들고 물체 영역과 배경 영역을 갱신 반복

⇒ 영역이 거의 변하지 않으면 수렴한 것으로 간주하고 멈춘다.

📌 GrabCut으로 물체 분할

[ex 4-7] 사용자가 붓칠한 정보를 이용하여 GrabCut으로 물체를 분할

import cv2 as cv 
import numpy as np

img=cv.imread('soccer.jpg')	# 영상 읽기
img_show=np.copy(img)		# 붓 칠을 디스플레이할 목적의 영상

mask=np.zeros((img.shape[0],img.shape[1]),np.uint8) 
mask[:,:]=cv.GC_PR_BGD		# 모든 화소를 배경일 것 같음으로 초기화

BrushSiz=9				# 붓의 크기
LColor,RColor=(255,0,0),(0,0,255)	# 파란색(물체)과 빨간색(배경)

def painting(event,x,y,flags,param):
    if event==cv.EVENT_LBUTTONDOWN:   
        cv.circle(img_show,(x,y),BrushSiz,LColor,-1)	# 왼쪽 버튼 클릭하면 파란색
        cv.circle(mask,(x,y),BrushSiz,cv.GC_FGD,-1)
    elif event==cv.EVENT_RBUTTONDOWN: 
        cv.circle(img_show,(x,y),BrushSiz,RColor,-1)	# 오른쪽 버튼 클릭하면 빨간색
        cv.circle(mask,(x,y),BrushSiz,cv.GC_BGD,-1)
    elif event==cv.EVENT_MOUSEMOVE and flags==cv.EVENT_FLAG_LBUTTON:
        cv.circle(img_show,(x,y),BrushSiz,LColor,-1)# 왼쪽 버튼 클릭하고 이동하면 파란색
        cv.circle(mask,(x,y),BrushSiz,cv.GC_FGD,-1)
    elif event==cv.EVENT_MOUSEMOVE and flags==cv.EVENT_FLAG_RBUTTON:
        cv.circle(img_show,(x,y),BrushSiz,RColor,-1)	# 오른쪽 버튼 클릭하고 이동하면 빨간색
        cv.circle(mask,(x,y),BrushSiz,cv.GC_BGD,-1)

    cv.imshow('Painting',img_show)
    
cv.namedWindow('Painting')
cv.setMouseCallback('Painting',painting)

while(True):				# 붓 칠을 끝내려면 'q' 키를 누름
    if cv.waitKey(1)==ord('q'): 
        break

# 여기부터 GrabCut 적용하는 코드
background=np.zeros((1,65),np.float64)	# 배경 히스토그램 0으로 초기화
foreground=np.zeros((1,65),np.float64)	# 물체 히스토그램 0으로 초기화

cv.grabCut(img,mask,None,background,foreground,5,cv.GC_INIT_WITH_MASK)
mask2=np.where((mask==cv.GC_BGD)|(mask==cv.GC_PR_BGD),0,1).astype('uint8')
grab=img*mask2[:,:,np.newaxis]
cv.imshow('Grab cut image',grab)  

cv.waitKey()
cv.destroyAllWindows()

1) 영상을 읽어 img 객체에 저장

2) 붓칠한 정보를 표시하는 데 쓸 img_ show 객체를 만듦

3) 사용자의 붓칠에 따라 물체인지 배경인지에 대한 정보를 기록할 배열 mask만듦

: 원본 영상과 크기가 같은 mask 배열을 생성 & 모든 화소를 배경일 것 같음(CV.GC_PR_BGD)으로 초기화

💡 OpenCV는 배경으로 판정된 화소 - cv. GCBGD, 물체로 판정된 화소 - cv. GC FGD, 배경일 것 같은 화소 - cv. GC_PR_BGD, 물체일 것 같은 화소 - cv.GC_PR_FGD로 표시

(해당 값은 순서대로 0, 1, 2, 3)

4) 사용자가 붓칠을 마치고 q를 누르면 37행으로 이동해 분할 시도

: grabCut 함수가 내부에서 사용할 배경 히스토그램 & 물체 히스토그램 생성 후 0으로 초기화

  • 히스토그램은 실수로 표현하며 65개 칸 가짐

5) grabCut 함수 호출 → 실제 분할을 수행

cv.grabCut(img,mask,None,background,foreground,5,cv.GC_INIT_WITH_MASK)

  • 인수1 : 원본 영상
  • 인수2 : 사용자가 지정한 물체와 배경 정보를 가진 맵
    • 배경으로 지정한 화소는 0(cv.GC_BGD), 물체로 지정한 화 소는 1(cv.GC_FGD), 나머지 화소는 배경일 것 같음에 해당하는 2(cv.GC_PR_BGD) 가짐
  • 인수3 : 관심 영역을 지정하는 ROI
    • None으로 설정해 전체 영상을 대상
  • 인수4,5 : 배경, 물체 히스토그램
  • 인수6 : 반복횟수 지정 (여기선 5번)
  • 인수7 : 배경&물체 표시한 맵 사용 지시

6) 배경 / 배경일 것 같음 표시된 화소 : 0

물체 / 물체일 것 같음 표시된 화소 : 1

로 바꿔서 mask2 객체에 저장

7) 원본 영상 img x mask2 ⇒ 배경 화소 검게 바꿔서 grab 객체에 저장

8) grab를 윈도우에 디스플레이

Untitled

4.6 영역 특징

📌 특징의 불변성 & 등변성

Untitled

1) 불변성 : 변환해도 특징 값 안 변함

  • 면적 : 회전에 불변, 축소에 불변 X
  • 물체의 중심축(주축) : 회전에 불변 X, 축소에 불변

2) 등변성 : 특징이 어떤 벼노한에 대해 따라 변함

  • 면적 : 회전에 등변 X, 축소에 등변

➡️ 상황에 맞게 특징 선택 중요!

📌 모멘트 & 모양 특징

Untitled

(y,x) : 영역 R에 속하는 화소

📌 텍스처 특징

영역의 면적 & 중점

Untitled

열 분산 : 화소들이 수직 방향으로 퍼진 정도

행 분산 : 화소들이 수평 방향으로 퍼진 정도

profile
안녕하세요😊 컴퓨터비전을 공부하고 있습니다 🙌

0개의 댓글