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가 더 빨랐음ㅇㅇ.. 상황에 맞게 쓰면 될 것 같다.
에지 검출은 영상 처리에서 중요한 과정으로, 물체의 경계를 찾아내는 기술이다.
에지 검출을 위한 다양한 연산자가 있다
1차 미분 연산자
2차 미분 연산자
최적 에지 검출기
소벨(Sobel) 연산자는 가장 널리 사용되는 에지 검출 방법 중 하나다.
원리: 이미지의 각 픽셀에 대해 x방향과 y방향의 기울기(gradient)를 계산
소벨 마스크:
Gx = [[-1, 0, 1], Gy = [[ 1, 2, 1],
[-2, 0, 2], [ 0, 0, 0],
[-1, 0, 1]] [-1, -2, -1]]
적용 방법:
장점:
이미지 로드 및 전처리:
import cv2 as cv
img = cv.imread('./.data/seulgi3.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)
cv.Sobel 함수는 이미지에 소벨 연산자를 적용함.cv.CV_32F: 출력 이미지의 데이터 타입을 32비트 부동소수점으로 지정함.1,0: x방향의 미분(가로 방향 에지 검출)0,1: y방향의 미분(세로 방향 에지 검출)ksize=3: 3x3 크기의 소벨 커널을 사용결과 변환:
sobel_x = cv.convertScaleAbs(grad_x)
sobel_y = cv.convertScaleAbs(grad_y)
cv.convertScaleAbs 함수는 음수 값을 포함할 수 있는 기울기 값을 절대값으로 변환하고 0-255 사이의 값으로 변환에지 강도 계산:
edge_strength = cv.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
이미지 로드 및 전처리:
import cv2 as cv
img = cv.imread('./.data/seulgi4.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
Canny 에지 검출 적용
canny1 = cv.Canny(gray,50,150)
canny2 = cv.Canny(gray,100,200)
cv.Canny 함수는 입력 이미지에 Canny 에지 검출 알고리즘을 적용
이 함수는 두 개의 임계값(threshold)을 사용
canny1은 더 낮은 임계값(50,150)을 사용하므로 더 많은 에지를 감지
canny2는 더 높은 임계값(100,200)을 사용하므로 더 중요한 에지만 감지
결과 표시
cv.imshow('gray',gray)
cv.imshow('canny1',canny1)
cv.imshow('canny2',canny2)
Canny 에지 검출기 작동 단계
1. 노이즈 제거(가우시안 블러 적용)
2. 기울기 계산(소벨 연산자 사용)
3. 비최대 억제(Non-maximum suppression)
4. 이중 임계값 처리(Double thresholding)
5. 에지 연결(Hysteresis)
이 코드는 이미지에서 에지를 검출한 후 윤곽선(contours)을 찾고 그리는 과정을 수행.
이미지 로드 및 전처리
img = cv.imread("./.data/seulgi4.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)
cv.findContours 함수는 이진 이미지(에지 이미지)에서 윤곽선을 찾는다.cv.RETR_LIST: 모든 윤곽선을 계층 구조 없이 리스트로 검출한다.cv.CHAIN_APPROX_NONE: 윤곽선의 모든 점을 저장한다(압축 없음).윤곽선 필터링
lcontour = []
for i in range(len(contour)):
if contour[i].shape[0] > 100:
lcontour.append(contour[i])
윤곽선 그리기
cv.drawContours(img,lcontour,-1,(0,255,0),3)
-1: 모든 윤곽선을 그린다.(0,255,0): 녹색으로 윤곽선을 그린다.3: 윤곽선의 두께를 3픽셀로 설정한다.결과 표시
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픽셀임
원형 허프 변환은 다음과 같은 분야에서 유용하게 사용된다:
라이브러리를 임포트
skimage: scikit-image 라이브러리로, 이미지 처리 기능을 제공numpycv2샘플 이미지를 로드
img = skimage.data.coffee()
cv.imshow("coffee", cv.cvtColor(img, cv.COLOR_RGB2BGR))
skimage.data.coffee()는 scikit-image에 내장된 커피 이미지를 로드함첫 번째 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 원본 이미지에 슈퍼 픽셀 경계를 표시한다.두 번째 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)
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))
필요한 라이브러리 임포트
import skimage # 이미지 처리 라이브러리
import numpy as np # 수치 연산 라이브러리
import cv2 as cv # OpenCV 이미지 처리 라이브러리
import time # 실행 시간 측정을 위한 라이브러리
샘플 이미지 로드
coffee = skimage.data.coffee() # scikit-image에 내장된 커피 이미지 로드
세그멘테이션 시간 측정 시작
start = time.time() # 현재 시간 기록
SLIC 슈퍼픽셀 분할:
slic = skimage.segmentation.slic(coffee, compactness=20, n_segments=600, start_label=1)
compactness=20: 슈퍼픽셀의 모양 규칙성을 조절(값이 클수록 더 정규적인 모양)n_segments=600: 생성할 슈퍼픽셀의 대략적인 개수start_label=1: 레이블 번호의 시작값영역 인접 그래프(RAG) 생성
g = skimage.graph.rag_mean_color(coffee, slic, mode='similarity')
정규화 컷 알고리즘 적용
ncut = skimage.graph.cut_normalized(slic, g)
처리 시간 출력
print(coffee.shape, 'Coffee:', time.time()-start, '초 소요')
세그멘테이션 결과 시각화
marking = skimage.segmentation.mark_boundaries(coffee, ncut)
ncut_coffee = np.uint8(marking * 255.0)
결과 표시 및 종료
cv.imshow('Normalized cut', cv.cvtColor(ncut_coffee, cv.COLOR_RGB2BGR))
cv.waitKey()
cv.destroyAllWindows()