Edge : 픽셀 값의 변화율 측정
→ 변화율이 큰 픽셀 선택
미분(derivative) : 데이터의 변화율, 순간 변화율
급격하게 바뀌는 부분
2차원 평면
미분 근사화 방법
1차원 이산함수(영상) 에서 미분 구하는 방법
(I(x + 1) - I(x - 1)) / 2
바로 앞 뒤에 있는 픽셀값 이용
→ 근사화 오류 가장 적음, 널리 사용
y좌표 고정
x축 방향으로만 미분 근사 계산
→ x축 방향으로의 편미분(partial derivative) = Ix
1 x 3 필터 마스크
미분 결과 시각적 분석 = 미분값 + 128
0 ~ 255 정수 형 변환 → 그레이스케일 영상으로
원본, x좌표 증가함에 따른 변화, y좌표 증가함에 따른 변화
2차원 : x축 방향, y축 방향 편미분 모두 사용
→ gradient : x축, y축 방향 미분을 한 번에 벡터로 표현한 것(합성, 방향성)
벡터
Example>
threshold
현재 행에 대한 중앙 차분 연산 2회
→ 더 큰 가중치 목적
mx = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]], dtype = np.float32)
my = np.array([[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]], dtype = np.float32)
dx = cv2.filter2D(src, -1, mx, delta = 128)
dy = cv2.filter2D(src, - 1, my, delta = 128)
x(y)방향으로 편미분한 결과 dx(dy) 행렬에 저장
Mat dx, dy;
Sobel(src, dx, CV_32FC1, 1, 0);
Sobel(src, dy, CV_32FC1, 0, 1);
def sobel_edge():
# src, ksize, scale, delta
dx = cv2.Sobel(src, cv2.CV_32F, 1, 0)
dy = cv2.Sobel(src, cv2.CV_32F, 0, 1)
fmag = cv2.magnitude(dx, dy) # 실수형 행렬
mag = np.uint8(np.clip(fmag, 0, 255)) # 그레이스케일로 변환
# clip : 255 넘어가면 잘라내기
_, edge = cv2.threshold(mag, 150, 255, cv2.THRESH_BINARY)
cv2.imshow('src', src)
cv2.imshow('mag', mag)
cv2.imshow('edge', edge)
Sobel().ksize = -1 || FILTER_SCHARR : 샤르 마스크 사용
weak edge를 최종적으로 판별
strong edge와 연결되어있는지가 기준 : edge direction
def canny_edge():
dst1 = cv2.Canny(src, 50, 100) # 두 개의 임계값 사용
dst2 = cv2.Canny(src, 50, 150)
cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
→ 임계값 낮출수록 : 더 많은 에지 픽셀 검출, 잡음에 해당하는 픽셀도 검출
hough transform
edge 검출 → 일직선상에 배열되어있는지 확인
2차원 xy좌표 직선의 방정식 → 파라미터 공간으로 변환
직선이 많이 교차되는 점(교점) 찾기
축적 배열 (accumulation array) 사용
0으로 초기화된 2차원 배열에서 직선이 지나가는 위치의 배열 원소값++
한계 : x = 3 (기울기 : 무한대) → 표현 불가
# math.pi = 180도, math.pi/180 -> 1도 단위
edge = cv2.Canny(src, 50, 150)
lines = cv2.HoughLines(edge, 1, math.pi/180, 250)
dst = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)
if lines is not None:
for i in range(lines.shape[0]):
rho = lines[i][0][0]
theta = lines[i][0][1]
cos_t = math.cos(theta)
sin_t = math.sin(theta)
x0, y0 = rho * cos_t, rho * sin_t
alpha = 1000 # 충분히 크게 설정 필요
pt1 = (int(x0 - alpha * sin_t), int(y0 + alpha * cos_t))
pt2 = (int(x0 + alpha * sin_t), int(y0 - alpha * cos_t))
cv2.line(dst, pt1, pt2, (0, 0, 255), 2, cv2.LINE_AA)
minLineLength : 검출할 선분의 최소 길이
maxLineGap : 직선으로 간주할 최대 에지 점 간격
edge = cv2.Canny(src, 50, 150)
lines = cv2.HoughLinesP(edge, 1, math.pi / 180, minLineLength=50, maxLineGap=5)
dst = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)
if lines is not None:
for i in range(lines.shape[0]):
pt1 = (lines[i][0][0], lines[i][0][1])
pt2 = (lines[i][0][2], lines[i][0][3])
cv2.line(dst, pt1, pt2, (0,0,255), 2, cv2.LINE_AA)
방법
영상에 존재하는 모든 원의 중심 좌표 찾기
원의 중심으로부터 적합한 반지름 구하기
HoughCircles(image, circles, method, dp, minDist, param1 = 100, param2 = 100, minRadius = 0, maxRadius = 0)
blurred = cv2.blur(src, (3, 3)) # 잡음제거용
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, 1, 50,
param1=150, param2=30)
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
if circles is not None:
for i in range(circles.shape[1]):
cx, cy, radius = circles[0][i]
cv2.circle(dst, (cx, cy), radius, (0,0,255), 2, cv2.LINE_AA)