🔎 명세
## 주사위 읽기 ##
• 책상 위에 놓인 임의의 주사위의 눈을 읽기
• 한 이미지 내에 여러 개의 주사위 존재
• 주사위의 눈을 오름차순으로 console에 출력하기
• Color 입력
• 주사위의 색상은 밝은 색 주사위 눈의 색상은 어두운 색
• 배경은 임의의 색
• 밝은 배경 혹은 어두운 배경
🔎 구현 단계
1. 회색조로 주사위 사진 파일 열기
• imread() 함수의 인자로 cv.IMREAD_GRAYSCALE을 넣어 회색조로 사진 파일을 열었다.
2. 이진화
• threshold() 함수의 인자로 cv.THRESH_OTSU를 이용했더니 잘 안나와서 cv.THRESH_BINARY를 한 후 threshold값을 220으로 주었다.
_, dst = cv.threshold(src, 220, 255, cv.THRESH_BINARY)
3. 레이블링
• connectedComponents() 함수를 이용해 각 주사위 객체를 분리했다.
4. 외곽선 찾기
• findContours() 함수의 인자로 cv.RETR_CCOMP를 넣어 모든 외곽선을 검색하고 2단계 계층 구조를 구성하도록 했다. 또한 눈으로 외곽선이 잘 검출되었나 확인하기 위해 drawContours() 함수를 이용해 검출된 외곽선을 그렸다.
5. 객체(주사위) 당 눈금 수 세기
• 기본적인 틀은 밑에 달아둔 게시글 내용의 5번과 같다. 그치만 노이즈도 눈금으로 검출하는 경우가 많아서 노이즈는 continue하여 그냥 넘기도록 하고 눈금도 rario가 0.5보다 클 때만 검출하도록 한다.
[참고 게시글 (여러 개의 주사위 눈금 읽기)] >> https://miind.tistory.com/68
i=0
j=0
dice = [] # 읽은 눈금 저장 list
while (i!=-1): # 주사위 하나를 세는 반복문
j = hierarchy[0, i, 2] # j는 다음에 볼 인덱스
dicenum = 0
while (j!=-1): # 주사위 내부 눈금 수를 세는 반복문
pts = contours[j]
length = cv.arcLength(pts, True)
area = cv.contourArea(pts)
ratio = 4. * math.pi * area / (length * length)
if (cv.contourArea(pts)) < 400: # 노이즈는 그냥 넘기도록
j = hierarchy[0, j, 0]
continue
if (ratio > 0.5): # 확률이 0.5를 넘을 때만 검출하도록 한다.
dicenum += 1 # 눈금 세기
# cv.drawContours(src, contours, j, (0,0,255), 2, cv.LINE_8, hierarchy)
j = hierarchy[0,j,0]
i = hierarchy[0, i, 0] # 다음 객체(주사위)로 이동
dice.append(dicenum) # list에 읽은 눈금 넣어두기
6. 출력
• 눈금이 0이라면 주사위 객체가 아니므로 0인 것은 출력하지 않는다. 나머지는 똑같이 오름차순으로 리스트를 정렬한 후 출력한다.
🔎 소스코드
import random
import math
import numpy as np
import cv2 as cv
def detectDice3():
# 회색조로 파일 열기
src = cv.imread('dice3.jpg', cv.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
return
# 이진화
_, dst = cv.threshold(src, 220, 255, cv.THRESH_BINARY)
# 레이블링
cnt, lables = cv.connectedComponents(dst)
# 외곽선 찾기
contours, hierarchy = cv.findContours(dst, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE)
idx = 0
while idx >= 0:
c = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
cv.drawContours(dst, contours, idx, c, 2, cv.LINE_8, hierarchy)
idx = hierarchy[0, idx, 0]
# 객채 마다 내부 외곽선 개수 세기
i=0
j=0
dice = [] # 읽은 눈금 저장 list
while (i!=-1): # 주사위 하나를 세는 반복문
j = hierarchy[0, i, 2] # j는 다음에 볼 인덱스
dicenum = 0
while (j!=-1): # 주사위 내부 눈금 수를 세는 반복문
pts = contours[j]
length = cv.arcLength(pts, True)
area = cv.contourArea(pts)
ratio = 4. * math.pi * area / (length * length)
if (cv.contourArea(pts)) < 400: # 노이즈는 그냥 넘기도록
j = hierarchy[0, j, 0]
continue
if (ratio > 0.5): # 확률이 0.5를 넘을 때만 검출하도록 한다.
dicenum += 1 # 눈금 세기
# cv.drawContours(src, contours, j, (0,0,255), 2, cv.LINE_8, hierarchy)
j = hierarchy[0,j,0]
i = hierarchy[0, i, 0] # 다음 객체(주사위)로 이동
dice.append(dicenum) # list에 읽은 눈금 넣어두기
dice.sort() # 오름차순 정렬
for i in range(0, len(dice)):
if (dice[i]==0):
continue
print(f'주사위 눈금 : {dice[i]}')
cv.imshow('src', src)
cv.imshow('dst', dst)
cv.waitKey()
detectDice3()