이미지 분석으로 발 사이즈 측정하기 #첫 시도

재미로 말고 프로덕션 목적으로 이미지 분석은 처음이랄까..

일단 opencv 를 사용하는 것을 전제로 하고..

요구조건은 다음과 같다.

1. 핸드폰 카메라로 찍힌 발 사진의 발볼길이, 발길이를 mm단위로 추출한다.
2. 길이를 재기 위한 기준으로 사용자는 a4용지 위에 발을 올리고 사진을 찍는다.
3. x,y,z 축 즉 발의 3면을 각각 찍어 사용자별로 사진이 3개씩 주어진다.

솔직히 처음에는 여차여차 공부하며 하면 되겠지 라고 생각했는데 역시나 쉽지 않다. 

오늘은 이미지 분석의 첫 시도로 뻘글을 싼다.

1. 일단 주어진 발 사진을 a4용지의 축척에 맞추어 리사이징 한다. 이 과정에서 찍은 각도가 보정되고, 사진상으로 mm를 확인할 수 있게 된다.

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import cv2

img = cv2.imread("foot.jpeg")

blur = cv2.blur(img,(5,10))

rows,cols,ch = img.shape

point=[[170,270],[480,220],[240, 710],[540,650]]

pts1 = np.float32(point)
pts2 = np.float32([[0,0],[210,0],[0,297],[210,297]])

M = cv2.getPerspectiveTransform(pts1,pts2)

dst = cv2.warpPerspective(img,M,(210,297))

plt.subplot(121),plt.imshow(img),plt.title('Input')

plt.plot(*zip(*point), marker='.', color='r', ls='')

plt.subplot(122),plt.imshow(dst),plt.title('Output')

plt.show()


사진을 보면 a4용지의 4 귀퉁이를 기준으로 210 * 290 사이즈로 맞춘다.
a4용지의 4귀퉁이를 이미지 분석으로 알아내기는 불가능 한것으로 보인다. 바닥과 차이가 안나는 경우도 있을 것이기에..
그런데 이 각도의 사진은 발 뒤꿈치의 끝을 알아내기 어려워 발 길이를 재기에는 적절치 않다는 게 이 시점에서 생각이 들었다.
그래서 다시 측면사진으로

2. 측면 사진 분석

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import cv2
import math

img = cv2.imread("foot-side.jpeg")

imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

point=[[825,470],[780,140],[230, 460], [285,130]]

pts1 = np.float32(point)
pts2 = np.float32([[0,0],[210,0],[0,297],[210,297]])

#######

plt.figure(1)
plt.subplot(111),plt.imshow(img),plt.title('Input')
plt.plot(*zip(*point), marker='.', color='r', ls='')

#########

plt.figure(2)
M = cv2.getPerspectiveTransform(pts1,pts2)
dst = cv2.warpPerspective(imgray,M,(210,297))
plt.subplot(111),plt.imshow(dst),plt.title('Adjust Size Output')

#########

plt.figure(3);
ret,thresh = cv2.threshold(dst,127,255,0)
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
rows,cols,ch = img.shape
contourImg = cv2.drawContours(dst, contours, -1, (0,255,0), 1)
plt.subplot(111)
plt.imshow(contourImg)
plt.title('Contour Output')

#########

plt.figure(4);

newContour = [];
for c in contours:
    if is_contour_good(c):
        newContour.append(c)

newContourImg = cv2.drawContours(dst, newContour, -1, (0,255,0), 1)

h_x,h_y = highestPoint(newContour);
l_x,l_y = lowestPoint(newContour);
lowestContourImg = cv2.circle(newContourImg, (l_x,l_y), 1, (255,255,255), thickness=3, lineType=8, shift=0)
highestContourImg = cv2.circle(lowestContourImg, (h_x,h_y), 1, (255,255,255), thickness=3, lineType=8, shift=0)
plt.subplot(111),plt.imshow(highestContourImg),plt.title('Contour Lowest/Highest Point')

plt.show()

print(math.sqrt((l_y  - h_y)**2+(l_x  - h_x)**2))

4개 figure로 뽑아보았는데

figure 1


측면 사진의 a4용지 네 귀퉁이를 잡는다. 
여기서도 문제가 한 귀퉁이가 사진상에 나오지 않아서 어림짐작으로 점을 찍을 수 밖에 없었다. 
물론 a4용지의 범위를 인식해서 교차선을 그을 수 있다면 문제가 되지 않겠지만.. 그렇게 할 수 있으면 점을 수동으로 찍지도 않았겠지..


figure 2

사이즈가 보정된 상태이다.

figure 3
여기서 contour를 써보았는데 여기를 참고했다
그런데 나중에 안 사실이지만 이렇게 오브젝트의 경계가 확실하지 않은 경우에는 먼저 경계가 확실히 드러나게 해 준 후 사용해야한다. 
이거 실행할 당시에는 이 것만 해도 매우 신기했다..
그런데 문제가 있었다.
위에서 이미지를 warpPerspective 해준 것 때문인지 위 이미지의 바깥 경계 부분까지도 contour로 잡혔다. 그래서 높이 값으로 간단하게 해볼려고 했던 내 의도가 무산되었다.
그래서 짠 코드가 이것
def lowestPoint(contours):
    temp_y = 0
    temp_x = 0;
    for index, i in enumerate(contours):
        if index == 0:
            continue
        for j in i:
            x,y = j[0]
            if y > temp_y and x != 1:
                temp_y = y
                temp_x = x

    return temp_x, temp_y
    
def highestPoint(contours):
    temp_y = 999
    temp_x = 0;
    for index, i in enumerate(contours):
        if index == 0:
            continue
        for j in i:
            x,y = j[0]
            if y < temp_y and x > 30 and y > 30:
                temp_y = y
                temp_x = x

    return temp_x, temp_y

def is_contour_good(c):
 peri = cv2.arcLength(c, True)
 approx = cv2.approxPolyDP(c, 0.02 * peri, True)
 return len(approx) > 4

허접하다...
lowestPoint 와 highestPoint 함수를 보명 for 문의 가장 안쪽에 if 문이 있는데 이 것으로 이미지의 테두리에 찍혀있는 contour 점들을 걸러낸다.
그런데 highestPoint 에  if y < temp_y and x > 30 and y > 30: 이게 있는 덕택에 고점이 잘못 찍힌 것은 함정..
밑에 이미지를 보면 우연찮게 발뒤꿈치 쪽에 점이 찍혀있는 걸 볼 수 있는데 순전히 저 30 상수 때문에 찍힌 것이다. 실제로 발뒷꿈치의 끝부분에 점을 찍을 수 있는지는 아직 모른다.. 안될거같으....

figure 4

그래도 여차저차 점 두개를 구해서 그 길이를 재보니 왠걸..

210.02142747824567 


이 값이 나왔다..

지수 발사이즈는 230인데...

ㅎㅎㅎ


댓글

  1. 안녕하세요 혹시 궁금한 게 있는데 물어봐도 될까요?

    답글삭제

댓글 쓰기