스크랩_Edge detection
title : Types of Edge detection algorithms
스크랩 출처 : 미디엄, Medium
Medium is a place to write, read, and connect
참고) 개인적인 해석과 기록으로 오류가 있을 수 있습니다.
+ Python OpenCV 강좌 : 제 14강 - 가장자리 검출, https://076923.github.io/posts/Python-opencv-14/
Edge detection, 윤곽선 검출
Edge
만화에서 물체를 선(Edge)으로 표현하여 사람들에게 정보를 전달하는 것과 같이,이미지 영상에서도 Edge는 중요한 역할을 함. 영상에서 Edge는 밝기가 갑자기 변한다는 점을 이용하여 찾음. 이상적인 경우에는, 물체의 경계선과 같은 부분을 알아내어 데이터의 양을 줄일 수 있으며, 이후의 일을 간단화할 수 있음.
Sonka, Milan, Vaclav Hlavac, and Roger Boyle. Image processing, analysis, and machine vision. 4th ed. Cengage Learning, 2014. p135.
ex) Roberst operato
ex) Roberts, Sobel, Prewiit 비교
출처 : https://jstar0525.tistory.com/53
- 이미지처리 및 패턴 인식에서는 연속성, 불연속성 및 전반적인 구조를 이해하기 위해 윤곽과 경계를 찾는 것이 항상 중요함.
- edges(= 테두리, 가장자리)는 개체가 분리되는 부분.
- gray scale(회색조, “흑백사진”처럼 밝기 정보만으로 구성된 영상, 밝기 정보를 256 단계로 구분하여서 표현 * 0: 검은색 255 : 흰색) levels 또는 강도의 중요하거나 명시적인 변화는 edges가 존재함을 나타낸다.
-
Edges는 이미지에서 형상을 식별할 수 있게 함.
- Hubel과 Wiesel의 시각체계 연구에서의 발견으로, 우리의 시각적 지각에는 이미지를 다른 패턴으로 나뉘는 메커니즘이 있음을 추론할 수 있음.
Hubel과 Wiesel 관련
- 이미지를 데이터로 간주할 때는 이미지에서 특징을 추출하는 것이 중요함.
- 특징을 추출하기 위해서 다양한 종류의 필터/커널을 사용하여 이미지에 컨볼루션 연산을 수행하고 특징맵(feature map, 합성곱 계산으로 만들어진 행렬)을 얻음.
- 우리가 만약 처음부터 시작한다면, 사전 훈련된 모델을 사용하지 않는 경우로 모델은 이미지에서 모든 것을 배우고 모든 기능을 처음부터 배워야 함을 의미함. 그러기 위해서 커널(= filter, which will identify all features from images)을 설정해야 함.
- 평균 이미지의 초기 단계에서 기본적으로 features는 가장자리, 윤곽선이라고 할 수 있음. 따라서 이미지에서 가장자리를 식별하는 것은 항상 중요함.
1) Canny edge detection
- 1986년 John F. Canny가 개발.
- 먼저 처리 편의를 위해 이미지를 그레이 스케일 이미지로 변환, 모든 에지 감지 알고리즘에서 첫 번째 작업이 될 것.
- 그런 다음 가우스 블러 필터를 사용, 단순히 가우스 함수의 도움으로 이미지를 흐리게 하는 방법
- 블러가 적용된 이미지는 가장자리가 강해짐.
Visualizing gaussian blur
import cv2
import numpy as np
import matplotlib.pyplot as plt
# image = cv2.imread("Your image address with extension")
image = cv2.imread('/content/image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
plt.imshow(gray, cmap='gray')
blur = cv2.GaussianBlur(gray, (1,1), 0)
plt.imshow(blur, cmap='gray')
- 블러로 이미지의 노이즈를 줄인 후 마지막으로 임계값 수준을 지정할 수 있는 Canny 필터를 사용.
canny = cv2.Canny(blur, 100, 150, 3)
plt.imshow(canny, cmap='gray')
- 캐니 함수(cv2.Canny)로 입력 이미지에서 가장자리를 검출.
- 노이즈에 민감하지 않아 강한 가장자리를 검출하는 데 목적을 둔 알고리즘.
- dst = cv2.Canny(src, threshold1, threshold2, apertureSize, L2gradient)는 입력 이미지(src)를 하위 임곗값(threshold1), 상위 임곗값(threshold2), 소벨 연산자 마스크 크기(apertureSize), L2 그레이디언트(L2gradient)을 설정하여 결과 이미지(dst)를 반환.
- 하위 임곗값과 상위 임곗값으로 픽셀이 갖는 최솟값과 최댓값을 설정해 검출을 진행.
- 픽셀이 상위 임곗값보다 큰 기울기를 가지면 픽셀을 가장자리로 간주하고, 하위 임곗값보다 낮은 경우 가장자리로 고려하지 않는다.
- 소벨 연산자 마스크 크기는 소벨 연산을 활용하므로, 소벨 마스크의 크기를 설정.
- L2 그레이디언트는 L2-norm으로 방향성 그레이디언트를 정확하게 계산할지, 정확성은 떨어지지만 속도가 더 빠른 L1-norm으로 계산할지를 선택.
L1그라디언트 :
L2그라디언트 :
2) Sobel edge detection
- 스탠포드 인공 지능 연구소(SAIL)의 동료인 Irwin Sobel과 Gary Feldman의 이름을 따서 명명됨.
- Sobel 엣지 감지에는 수평 및 수직의 두 커널이 있음, 먼저 두 커널은 개별적으로 동작하고 그 다음에 전체적인 결과를 합산.
- 수평 커널은 수평 방향의 가장자리를 포함하고 수직 커널은 수직 방향으로 가장자리를 포함.
Kernels for X and Y direction
# sobel edge detection
# we can provide minimum and maximum threshold according to our need
import cv2
image = cv2.imread('/content/img1.jpeg')
original = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(original)
# Convert to graycsale
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Blur the image for better edge detection
img_blur = cv2.GaussianBlur(img_gray, (3,3), 0)
plt.imshow(img_blur, cmap='gray')
# Sobel Edge Detection
# Sobel Edge Detection on the X axis
sobelx = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=1, dy=0, ksize=7)
# Sobel Edge Detection on the Y axis
sobely = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=0, dy=1, ksize=7)
# Combined X and Y Sobel Edge Detection
sobelxy = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=1, dy=1, ksize=7)
# Display Sobel Edge Detection Images
fig = plt.figure(figsize=(18,28))
rows = 1
cols = 3
ax1 = fig.add_subplot(rows, cols, 1)
ax1.imshow(sobelx, cmap='gray')
ax1.set_title('sobel_x')
ax1.axis("off")
ax2 = fig.add_subplot(rows, cols, 2)
ax2.imshow(sobely, cmap='gray')
ax2.set_title('sobel_y')
ax2.axis("off")
ax3 = fig.add_subplot(rows, cols, 3)
ax3.imshow(sobelxy, cmap='gray')
ax3.set_title('sobelx_y')
ax3.axis("off")
plt.show()
- 소벨 함수(cv2.Sobel)로 입력 이미지에서 가장자리를 검출
- 미분 값을 구할 때 가장 많이 사용되는 연산자이며, 인접한 픽셀들의 차이로 기울기(Gradient)의 크기를 구함, 인접한 픽셀들의 기울기를 계산하기 위해 컨벌루션 연산을 수행.
- dst = cv2.Sobel(src, ddepth, dx, dy, ksize, scale, delta, borderType)
- 입력 이미지(src)에 출력 이미지 정밀도(ddepth)를 설정하고 dx(X 방향 미분 차수), dy(Y 방향 미분 차수), 커널 크기(ksize), 비율(scale), 오프셋(delta), 테두리 외삽법(borderType)을 설정하여 결과 이미지(dst)를 반환.
- 커널 크기(=소벨 마스크의 크기)를 설정합니다. 1, 3, 5, 7 등의 홀수 값을 사용하며, 최대 31까지 설정할 수 있음.
- 출력 이미지 정밀도는 반환되는 결과 이미지의 정밀도를 설정.
- X 방향 미분 차수는 이미지에서 X 방향으로 미분할 차수를 설정.
- Y 방향 미분 차수는 이미지에서 Y 방향으로 미분할 차수를 설정.
- Tip : X 방향 미분 차수와 Y 방향 미분 차수는 합이 1 이상이여야 하며, 0의 값은 해당 방향으로 미분하지 않음을 의미.
- 비율과 오프셋은 출력 이미지를 반환하기 전에 적용되며, 주로 시각적으로 확인하기 위해 사용.
- 픽셀 외삽법은 이미지 가장자리 부분의 처리 방식을 설정.
#If we want to scratch code Sobel edge detection,
import numpy as np
import cv2
import matplotlib.pyplot as plt
vertical_filter = [[-1,-2,-1],[0,0,0],[1,2,1]]
horizontal_filter = [[-1,0,1],[-2,0,2],[-1,0,1]]
img = plt.imread("enter your image address")
n,m,d = img.shape
edges_img = np.zeros_like(img)
for row in range(3,n-2):
for col in range(3,m-2):
local_pixels = img[row-1:row+2,col-1:col+2, 0]
vertical_transformed_pixels = vertical_filter*local_pixels
vertical_score = vertical_transformed_pixels.sum()/4
horizontal_transformed_pixels = horizontal_filter*local_pixels
horizontal_score = horizontal_transformed_pixels.sum()/4
edge_score = (vertical_score**2 + horizontal_score**2)**.5
edges_img[row,col] = [edge_score]*3
edges_img = edges_img/edges_img.max()
plt.imshow(edges_img)
3) By ROI
- ROI : Region Of Interest
- 평소와 같이 먼저 이미지를 그레이스케일로 변환.
- 경계를 정의하고 제거하는 것이 코드의 주 목적.
- 윤곽 인식을 위해 cv2.findContours 함수 활용.
+ cv2.findContours
- 영상이나 이미지의 윤곽선(컨투어)을 검출하기 위해 사용
- 영상이나 이미지에서 외곽과 내곽의 윤곽선(컨투어)을 검출할 수 있음.
- cv2.findContours(이진화 이미지, 검색 방법, 근사화 방법)을 의미.
- 반환값으로 윤곽선, 계층 구조를 반환
- 윤곽선은 Numpy 구조의 배열로 검출된 윤곽선의 지점들이 담겨있음.
- 계층 구조는 윤곽선의 계층 구조를 의미, 각 윤곽선에 해당하는 속성 정보들이 담김.
출처 : https://076923.github.io/posts/Python-opencv-21/
# 제가 시도했을 떄 이미지가 잘 출력되지 않아 다른 코드를 조금 수정해 보았습니다.
# edge detection on the basis of ROI
# bgr = cv2.imread('your image path with extension')
# gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY)
# _, roi = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
# cv2.imwrite('roi1.png', roi)
import cv2
bgr = cv2.imread('/content/img1.jpeg')
gray = cv2.cvtColor(bgr, cv2.COLOR_RGB2GRAY)
ret, binary = cv2.threshold(gray, 121, 255, cv2.THRESH_BINARY)
binary = cv2.bitwise_not(binary)
plt.imshow(binary, cmap='gray')
plt.show()
#cv2.imshow("roi", binary)
cont = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
output = np.zeros(gray.shape, dtype=np.uint8)
cv2.drawContours(output, cont[0], -1, (255, 255, 255))
plt.imshow(output, cmap='gray')
plt.show()
# removing boundary
boundary = 255*np.ones(gray.shape, dtype=np.uint8)
boundary[1:boundary.shape[0]-1, 1:boundary.shape[1]-1] = 0
toremove = output & boundary
output2 = output ^ toremove
plt.imshow(output2, cmap='gray')
plt.show()
ROI
관심영역 ROI(Region Of Interest)는 뜻 그대로 이미지나 영상 내에서 내가 관심있는 부분. 이미지 상의 특정 오브젝트나 특이점을 찾는 것을 목표로 할 때 씀. 비슷한 용어로는 COI(Channel Of Interest)라는 관심 채널이 있다.
관심영역을 지정하는 것은 불필요한 영역에 대한 이미지 처리를 방지할 수 있어 여러 리소스를 절역할 수 있다. OpenCV 에서는 selectROI 함수와 selectROIs 함수를 주로 이용하여 관심영역을 지정함.
ex) ROI 평균값으로 이미지 나타내기
import cv2
import numpy as np
src = cv2.imread("enter your image address")
dst = np.zeros(src.shape, dtype=src.dtype)
N = 256
height, width, _ = src.shape
h = height // N
w = width // N
for i in range(N):
for j in range(N):
y = i*h
x = j*w
roi = src[y: y+h, x: x+w]
dst[y: y+h, x: x+w] = cv2.mean(roi)[0:3]
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
roi = src[y: y+h, x: x+w] 는 원본 영상의 ROI를 계산해 그 평균값 계산함. 평균값을 나타내는 것으로 N의 값이 커질수록 원본 영상과 비슷해지고 N이 작을수록 원본 영상의 전체 평균값과 비슷해져 구분하기 어려워짐.
N = 32
N = 256
출처 : https://eusun0830.tistory.com/42
+ cv2 윈도우창 관리
-
cv2.namedWindow(winname, flags) 함수는 winname이라는 이름을 갖는 창을 생성해줍니다. 파라미터는 아래와 같습니다. winname: 창 구분자로 활용될 창 이름 flags: 창 옵션 (cv2.WINDOW_NORMAL: 사용자가 창 크기를 조정할 수 있음, cv2.WINDOW_AUTOSIZE: 이미지와 동일한 크기로 창 크기를 재조정할 수 없음)
-
cv2.moveWindow(winname, x, y) 함수를 호출하면 원하는 위치로 창을 옮길 수 있습니다. winname: 위치를 변경할 창 이름 x, y: 변경할 위치 (x, y 좌표)
- cv2.resizeWindow(winname, width, hegith) 함수는 winname 창의 크기를 (width, height) 크기로 변경해줍니다.
- cv2.destroyWindow(winname) 함수를 호출하면 winname에 해당하는 창을 닫습니다.
- cv2.destroyAllwindows() 함수는 열린 모든 창을 닫습니다.
+ 키보드 이벤트 처리
- cv2.waitKey(delay) 함수는 delay 밀리초만큼 프로그램을 멈추고 있다가 키보드의 눌린 키에 대응하는 값을 반환
- dalay 시간만큼 키보드 입력이 없다면 -1을 반환
- delay의 default값은 0인데, 이 경우 키보드 입력이 있을 때까지 영원히 대기
+ 마우스 이벤트 처리
-
마우스 이벤트는 cv2.setMouseCallback(windowName, onMouse, param=None) 함수 windowName: 이벤트를 등록할 윈도우 이름 onMouse: 이벤트 처리를 위해 미리 선언해 놓은 마우스 콜백 함수 콜백 함수인 onMouse(evnet, x, y, flags, param) 함수는 마우스의 이벤트와 마우스 좌표를 처리
-
여기서 event에는 마우스의 움직임, 왼쪽 버튼 누름, 왼쪽 버튼 뗌, 오른쪽 버튼 누름, 오른쪽 버튼 뗌, 왼쪽 버튼 더블 클릭, 휠 스크롤 등 cv2.EVENT_로 시작하는 12가지 이벤트 (ex. cv2.EVENT_MOSEMOVE: 마우스 움직임, cv2.EVENT_LBUTTONDOWN: 왼쪽 버튼 누름) flags는 컨트롤, 쉬프트, 알트와 같은 키를 함께 누른 상태처럼 이벤트를 처리 flags와 param을 사용하지 않는다 하더라도 콜백 함수 선언부에 flags와 param을 기재, 그렇지 않으면 오류가 발생
-
flags 활용 cv2.EVENT_FLAG_CTRLKEY(8): Ctrl 키를 누름 cv2.EVENT_FLAG_SHIFTKEY(16): Shift 키를 누름 cv2.EVENT_FLAG_ALTKEY(32): Alt 키를 누름
출처 : https://bkshin.tistory.com/entry/OpenCV-5-%EC%B0%BD-%EA%B4%80%EB%A6%AC-%EB%B0%8F-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B2%98%EB%A6%AC
4) Using PIL
- PIL: Python Imaging Library
- Python에 많은 이미지 처리 기능을 추가함.
- Pillow는 사용자 친화적인 기능을 추가한 PIL의 포크(fork, 소프트웨어 개발에서 “여러 갈래로 나누고 개개의 길을 가다, 하나의 소프트웨어 소스 코드를 통째로 복사하여 독립적인 새로운 소프트웨어를 개발하는 것”라는 뜻)
- 다양한 종류의 이미지 조작을 위한 PIL의 많은 흥미로운 기능이 있음.
- PIL에서 ImageFilter 임포트 하고 “ImageFilter.FIND_EDGES” 활용.
#Edges detection using PIL library
from PIL import Image, ImageFilter
image = Image.open(r"/content/img1.jpeg")
#In PIL grayscale is denoted by "L"
image = image.convert("L")
# Detecting Edges on the Image using the argument ImageFilter.FIND_EDGES
image = image.filter(ImageFilter.FIND_EDGES)
# Saving the Image Under the name Edges.png
image.save(r"Edges.png")
Conclusion
- Each algorithms stands by its unique mathematical approach for detecting edges. Single algorithm wont work for every case but you can decide algorithm on the basis of test case you have.
- 각 알고리즘은 가장자기를 감지하기 위한 고유한 수학적 접근 방식을 고수함.
- 각자의 test case를 기준으로 알고리즘 결정할 수 있음.
- Edge detection is also dependent on what next steps and processing you want to do for your application so for that more deeper mathematical intuition is needed.
- Edge detection은 또한 당신의 어플리케이션에 원하는 다음 단계와 처리에 따라 달라지므로 더 깊은 수학적 직관력을 필요로 함.
- Drastic ups and drops of intensities in images are termed as edges and all kernels are designed accordingly.
- images의 급격한 강도 상승과 하락을 가장자리라고 하며, 그에 따라 모든 커널이 설계됨.