운동 자동기록 서비스를 계획하면서 초기에는 load cell을 이용해 센서로 무게를 재려고 하였다.
허나 load cell에서 예상하지 못했던 치명적인 문제가 발생했고 발표까지 3일이 남은 상황에서 무게를 잴 수 있는 새로운 솔루션을 생각해보았다.

컨설턴트님, 프로님들의 도움으로 삼성화재 연수원 내의 헬스장을 사용할 수 있었고, 아래와 같이 통일된 규격의 운동기구들을 보자 영상처리를 통해 이를 해결할 수 있을거라는 생각이 들었다.

초기 계획으로는 해당 이미지에서 ROI를 설정하고 해당 파트에서 노란색을 findContour를 통해 윤곽을 추출하고 갯수를 세어 들어올린 중량을 측정하는 것을 목표로 했다.

ROI는 Range Of Interest의 약자로 관심영역을 의미한다.

원본관심영역
import cv2
import numpy as np

img=cv2.imread('capture.jpg')

img=cv2.resize(img,(480,640)) # 사용할 웹캠의 해상도에 맞게 리사이징함
roi=img[190:630,140:260] # 관심영역을 설정함
cv2.rectangle(roi, (0,0),(119,439),(0,255,0)) # 관심영역을 초록색으로 테두리를 침
cv2.imshow('img',img) 

key=cv2.waitKey(0)
print(key)
cv2.destroyAllWindows()

이후 해당 영역에서 노란색을 추출하는 작업이 필요하다.
BGR값이 아닌 HSV값으로 변경 후 노란색애서 해당 부위를 추출하면 된다.

원본HSV노란색 영역 추출
import cv2
import numpy as np

img=cv2.imread('capture.jpg',cv2.IMREAD_COLOR)

img=cv2.resize(img,(480,640))
roi=img[190:630,140:260]
roi_hsv=cv2.cvtColor(roi,cv2.COLOR_BGR2HSV) # HSV로 변경 후 
hsv_down=np.array([30,0,100]) #추출할 노란색의 하한선
hsv_up=np.array([40,255,240]) #추출할 노란색의 상한선

hsv_mask=cv2.inRange(roi_hsv,hsv_down,hsv_up) # 마스크를 만들고
result=cv2.bitwise_and(roi,roi,mask=hsv_mask) # 비트연산으로 해당하는 위치를 추춯한다.
key=cv2.waitKey(0)
cv2.destroyAllWindows()

위의 HSV 이미지는 hsv로 변환된 후 해당 값을 기반으로 BGR출력이 되서 뭔가 사이버펑크적인 색이 되었다.
노랑색은 일반적으로 H값이 30~90이므로 초기에는 이를 기반으로 테스트 후, 범위를 세부적으로 조정해 위의 값을 찾아냈다.
범위를 기반으로 제작된 마스크와 이미지를 비트연산으로 겹치는 부분만 추출하면 가장 우측의 이미지를 얻을 수 있는데, 잘 보면 흰색의 노이즈가 존재하는 것을 알 수 있다.

따라서 이를 보정하기 위해 medianBlur를 사용하였다.

원본MedianBlur
 blur=cv2.medianBlur(result,3)
 

위의 과정을 통해 구한 이미지에 findContour를 통해 외곽선을 구해 노란색의 수를 구해주려고 했다.
원본 이미지를 보면 알 수 있듯이 노란색 26개+ 위의 1개 = 총 27개의 결과가 나오면 정상적으로 구한것이라 추정해볼 수 있다.

원본thresholdContour
import cv2
import numpy as np

img=cv2.imread('capture.jpg',cv2.IMREAD_COLOR)

img=cv2.resize(img,(480,640))
roi=img[190:630,140:260]
roi_hsv=cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
hsv_down=np.array([30,0,100])
hsv_up=np.array([40,255,240])

hsv_mask=cv2.inRange(roi_hsv,hsv_down,hsv_up)
result=cv2.bitwise_and(roi,roi,mask=hsv_mask)

blur=cv2.medianBlur(result,3)

blur_gray=cv2.cvtColor(blur,cv2.COLOR_BGR2GRAY)
ret,img_threshold=cv2.threshold(blur_gray,127,255,cv2.THRESH_BINARY)#127을 기준으로 흑/백으로 나눔
contour,hierarchy=cv2.findContours(img_threshold,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) #contour를 찾음

print(len(contour)) #contour의 갯수를 구함
cv2.drawContours(blur, contour,-1,(0,0,255),2) #외곽선을 빨간색으로 그림

key=cv2.waitKey(0)
cv2.destroyAllWindows()


27개라는 결과가 나왔지만, contour 결과를 보면 떨어지면 안되는 것이 떨어져 있고, 다른 묶음이 붙어서 정상적으로 추적이 안되었음을 알 수 있다.

따라서 이를 해결하기 위해 새로운 방법을 구상했다.

0개의 댓글