이미지 분류 머신러닝을 위한 OpenCV 기초 실습 2

신 영·2024년 4월 1일
1

Python

목록 보기
4/4
post-thumbnail

✅가장자리 검출

  • 가장자리 : 이미지상의 개체의 가장 바깥 둘레를 의미 (= 테두리)
  • 전경과 배경이 구분되는 지점, 즉 밝기가 큰 폭으로 변하는 지점이 가장자리가 됨
  • 픽셀 밝기 변화율이 높은 경계선을 찾으려면, 가장자리를 찾기 위해 미분, 기울기 계산이 필요함
  • 크게 소벨(Sobel), 라플라시안(Laplacian), 캐니(Canny)의 3가지 함수가 존재

📌소벨(Sobel) & 라플라시안(Laplacian)

  • 사용법 : cv2.Sobel(이미지, ddepth, dx, dy, ksize, scale, delta, borderType)
  • ddepth : 출력 이미지 깊이
    • CV_8U : 8비트 정수, CV_16U : 16비트 정수, CV_32F : 32비트 실수, CV_64F : 64비트 실수
    • 보통 CV_32F를 많이 쓰지만 본인이 쓸 사진에 적합한 걸로 고르면 된다
  • dx : 가로 방향 몇 차 미분 할지, dy : 세로 방향으로 몇 차 미분 할지
    • 밝기 값에 대한 미분
    • 기본값은 X
    • 미분 많이 할수록 윤곽 유해지지만 너무 많이 하면 뿌얘진다.
    • 라플라시안은 dx, dy가 2로 고정되어 있어 설정 X
  • ksize : 커널(필터) 크기, 기본값 3, 1~31까지의 정수, 홀수
    • 대부분 안 건드린다(건드려도 모델 성능에 도움 별로 안됨)
    • CNN에서 필터와 동일한 의미
  • scale : 전체적인 보정 값
  • delta : 윤곽선, 높게 잡으면 전체적으로 밝게 나옴
  • borderType : 다 넣어보고 제일 잘 나오는 거 써라, 보통 constant 씀

📌캐니(Canny)

  • 셋 중 가장 많이 사용(사실상 가장자리 검출 시에는 캐니만 쓴다고 봐도 됨)
  • 소벨의 변형, 사람 윤곽 볼 때 좋다.
  • 사용법 : cv2.Canny(이미지, threshold1, threshold2, apertureSize, L2Gradient)
  • threshold1 : 하위 임계값, threshold2 : 상위 임계값
    • 밝기 변화 < threshold1 : 가장자리X
    • threshold1 < 밝기 변화 < threshold2 : 주변 밝기 감안해서 가장자리로 판단
    • 밝기 변화 > threshold2 : 가장자리O
  • apertureSize : 소벨 연산자 마스크 크기(소벨의 ksize와 동일한 개념)
  • L2Gradient : L1-norm(덜 정확하지만 빠름), L2-norm(느리지만 정확) 중 선택
  • 보통 L1 안쓰고 L2 쓴다
# ※ 일반적으로 그레이스케일화된 이미지로 수행한다

# 물체의 색보다 형체를 알아볼 때 사용
# 엣지 디텍션은 항상 그레이스케일화를 먼저 해야 한다

sobel = cv2.Sobel(gray_img, cv2.CV_8U, 1, 0, 3)
laplacian = cv2.Laplacian(gray_img, cv2.CV_8U, ksize = 3)
canny = cv2.Canny(gray_img, 100, 255)

# 이미지 시각화
plt.figure(figsize = (10, 10))

# 원본 이미지
plt.subplot(2,2,1) 
plt.imshow(img_rgb)
plt.title('Original')
plt.axis('off')

# 소벨
plt.subplot(2,2,2)
plt.imshow(sobel)
plt.title('Sobel')
plt.axis('off')

# 라플라시안
plt.subplot(2,2,3)
plt.imshow(laplacian)
plt.title('Laplacian')
plt.axis('off')

# 캐니
plt.subplot(2,2,4)
plt.imshow(canny)
plt.title('Canny')
plt.axis('off')

# 이미지 표시
plt.show()

✅이진화

  • 0 또는 1로만 된 1비트 이미지를 생성
  • 윤곽선 검출 시 input image로 사용
  • cv2.threshold(이미지, thresh, maxval, type) 함수 사용
  • Thresh : 임계값, maxval : 최댓값, type : 임계값 형식
  • 원하는 결과 이미지에 따라 다양한 임계값 형식을 적용할 수 있으며, 마스킹에도 적용 가능
    • 마스킹이 꼭 좋은 것만은 아니다. 배경을 마스킹한 데이터로 학습시키면 과적합 될 수 있어서 주의해야 한다.
  • 만약 코드가 cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY) 로 구성되어 있다면: 픽셀 값이 100 이상이면 255로 변경, 미만이라면 0으로 변경
  • ThresholdTypes
text = cv2.imread('짱구는 못말려 텍스트.jpg')
# 그레이스케일화 필요
text = cv2.cvtColor(text, cv2.COLOR_RGB2GRAY)
ret, binary = cv2.threshold(text, 100, 255, cv2.THRESH_BINARY)
# ret는 수동으로 100을 줘버려서 안 쓰는 값, binary만 쓴다고 생각하면 된다
plt.imshow(binary, cmap = 'gray')

✅윤곽선 검출

# 윤곽선 검출을 위해서는 반드시 이진화 후 진행해야 함
# 색 반전
binary = cv2.bitwise_not(binary) # 흰 ↔ 검

# 최외곽 윤곽선 검출
# 윤곽선 검출 시에는 binary를 넣어준다
contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

# binary를 넣어줘도 되는데 윤곽선과 번호 잘 보이도록 흰 배경을 다시 넣어주자 
# text가 원래 1차원이었기 때문에 빨간색 윤곽선이 나오지 않는 이슈 발생
# 3차원으로 바꿔준다
text_3 = cv2.cvtColor(text, cv2.COLOR_GRAY2RGB)
# text_3.shape → (648, 1152, 3)

# 차례로 윤곽선과 번호 그리기
for i in range(len(contours)):
    # 윤곽선 그리기
    cv2.drawContours(text_3, [contours[i]], 0, (0,0,255), 1)
    cv2.putText(text_3, str(i), tuple(contours[i][0][0]), cv2.FONT_HERSHEY_COMPLEX, 0.8, (40,40,112), 1)
    
# 시각화
plt.figure(figsize=(10,10))
plt.imshow(cv2.cvtColor(text_3, cv2.COLOR_BGR2RGB))
plt.show()

[같은 계층에서의 다음 윤곽선, 같은 계층에서의 이전 윤곽선, 하위 계층(내측) 윤곽선, 상위 계층(외측) 윤곽선]
정수 : 해당 윤곽선 번호, -1 : 존재하지 않음

0개의 댓글