빠른 속도
낮은 자원 사용
사용하기 쉬움
전처리 기능 풍부
아주 살짝 그림자가 지고, 아주 살짝 빛 반사가 된 약간 어려운 task 를 가져와 봤다.
import cv2
import seaborn as sns
src = cv2.imread(IMG_PATH)
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
sns.histplot(src_gray.reshape(-1,))
확실히 이전 post 보다 훨씬 어려워진 분포가 된다.
src = cv2.imread(IMG_PATH)
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
th, src_bin = cv2.threshold(src_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print("Auto threshold:", th)
fig = plt.figure(figsize=(10,10))
ax1 = fig.add_subplot(1, 2, 1)
ax1.imshow(cv2.cvtColor(src_gray, cv2.COLOR_BGR2RGB))
ax1.set_title("1st pic", fontsize=10)
ax2 = fig.add_subplot(1, 2, 2)
ax2.imshow(cv2.cvtColor(src_bin, cv2.COLOR_BGR2RGB))
ax2.set_title("2th pic", fontsize=10)
Auto threshold: 150.0
으음.. 150 정말 맞나..
다음 번 예시는 아예 빛 반사와 흰 배경으로 가져와야지.. 오른쪽만 좀 침범 해서 현재 코드로는 탐지하기 어려울 듯, 앞단에 좀 더 preprocessing 이 들어가고 minAreaRect
을 넣어 그려볼 예정.
# Contour detection
contours, _ = cv2.findContours(src_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for contour in contours:
# Ignore small object
if cv2.contourArea(contour) < 20000:
continue
# Contour approximation
epsilon = 0.02 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# If approximated as a rectangle, draw the contour.
if len(approx) == 4:
cv2.polylines(src, [approx],
isClosed=True,
color=(0, 255, 0),
thickness=30,
lineType=cv2.LINE_AA
)
plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
plt.xticks([])
plt.yticks([])
plt.show()
잡힐 턱이 있나..
if cv2.contourArea(contour) < 2000:
임계치를 줄이면,
이런 식으로 나온다.
src_gray = cv2.medianBlur(src_gray, 21)
src_gray = cv2.bilateralFilter(src_gray, -1, 50, 10)
GaussianBlur
를 일반적으로 사용하긴 하지만, 세밀하게 작업하는 목적이 아니라서..
bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None)
d
: 필터링에 사용할 각 픽셀 이웃의 지름. d
가 음수이면 sigmaSpace
값에 의해 자동 계산.sigmaColor
: 색 공간에서 필터의 sigma.sgmaSpace
: 좌표 공간에서 filter 의 sigma.를 추가하고, contourArea 를 20000
으로 다시 원복하고,
if cv2.contourArea(contour) < 20000:
일단 무엇을 그리는지 확인하기 위해서
if len(approx) == 4:
의 조건을 빼고 그려보면,
그림자의 문제가 되면 gamma 를 추가 하여 좀더 밝게 해보면
gamma = 1.1
src_gray = src_gray.astype(np.float32)
src_gray = ((src_gray / 255) ** (1 / gamma)) * 255
src_gray = src_gray.astype(np.uint8)
minAreaRect
이렇게 선을 approximation 하지 말고, minAreaRect
를 사용하여 사각을 그려보고 그 box 들을 활용하여 그려보는 것이 더 알맞을 것으로 판단.
rotated bounding box
을 계산한다.# Contour detection
contours, _ = cv2.findContours(src_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for contour in contours:
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.intp(box)
# Ignore small object
if cv2.contourArea(box) < 20000:
continue
cv2.drawContours(src, [box], 0, (0, 255, 0), 2)
아무것도 그려지지 않는다. 1st pic
에서 edge 만 걸러보자.
canny 알고리즘
min_val = 5
max_val = 50
edge_canny = cv2.Canny(src_gray, min_val, max_val)
를 이용 하면
fig = plt.figure(figsize=(10,10))
ax1 = fig.add_subplot(1, 3, 1)
ax1.imshow(cv2.cvtColor(edge_canny, cv2.COLOR_BGR2RGB))
ax1.set_title("1st pic", fontsize=10)
ax2 = fig.add_subplot(1, 3, 2)
edge_canny = cv2.bitwise_not(edge_canny)
ax2.imshow(cv2.cvtColor(edge_canny, cv2.COLOR_BGR2RGB))
ax2.set_title("2nd pic", fontsize=10)
ax2 = fig.add_subplot(1, 3, 3)
ax2.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
ax2.set_title("3th pic", fontsize=10)
edge detection 에서 edge 가 너무 옅게 나와서 dilate
를 사용.
kernel = np.ones((3, 3), np.uint8)
edge_canny = cv2.dilate(edge_canny, kernel, iterations=3)
중간에 끊어짐이 있으니 gamma 를 다시 내려서 볼까..
gamma = 1
오 ... 그렇네..
자세히 보면, 정확한 fit 은 되지 않았다. 일단 넘어가고, 좀 더 어려운 task 가져오자.
이것이 일반화 시키려는 방법인데, 앞서 언급했지만, 제한된 범용성 이것이 정말 어렵다.
Just with 'openCV' alone....