YOLOV5로 Hangman 게임을 만들어보자 #3-문제 보여주기

이영준·2022년 6월 6일
0
post-thumbnail

📌행맨 캔버스를 띄우기

우선 그림판을 통하여 퀴즈의 초기화면을 만들었다.

위 그림에서 우측에는 모자이크된 사진을 띄울 것이고, 퀴즈가 틀릴 때마다 행맨을 걸 것이다.

먼저 이 초기화면을 띄우는 show_quiz()함수를 정의했다.

def show_quiz():  # hangman 캔버스를 띄움
    img = cv2.imread(CANVAS)  # width = 640, height = 425
    cv2.namedWindow("Hangman", cv2.WND_PROP_FULLSCREEN)
    cv2.setWindowProperty("Hangman", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
    cv2.imshow("Hangman", img)
    # img = np.full((640,480,3),(255,255,255),np.uint8)
    # cv2.imshow("quiz",img)
    # cv2.waitKey(0)
    return img

📌캔버스에 문제를 보여주기

def show_question(img, hangman):  # hangman 캔버스에 문제 보여줌
    height, width, channel = img.shape
    hangman[100:height + 100, 576:576 + width] = img
    cv2.imshow("Hangman", hangman)

show_question함수는 우측에 문제를 띄워주는 함수이다. show_quiz와 show_question함수의 차이라면 show_question함수에서는 cv2.imshow를 통하여 캔버스를 띄워주는 코드가 함수 내에 이미 있어서 따로 imshow를 쓸 필요가 없다. 어찌보면 일관성이 없는 함수들이라고 볼 수 있다,,ㅎㅎ

지금까지 어떠한 사진이 주어지면, 그 사진 속 객체들을 Yolov5를 통하여 탐지해내고, 객체들을 둘러싸는 바운딩 박스를 파란색 선으로 표시하고, 바운딩 박스 내의 객체들을 모두 모자이크 시켜서 사용자에게 보여지는 것까지를 완료 하였다.

📌키보드 입력을 통하여 객체를 선택하기

🔑마우스 클릭을 통한 객체 선택?

단순히 출력된 화면을 보기만 하는 것이 아닌, 사용자가 게임을 하는 프로젝트가 되게 하기 위해서는, 입력을 받아야 한다.

모자이크 처리가 된 여러가지 객체 중에서 사용자가 풀고 싶어하는 객체를 선택하도록 하려면 어떻게 해야 할까 고민했는데, 마우스로 클릭하는 것이 가장 직관적이고 쉬울 듯 보였으나,,

def on_mouse(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        value = param[y,x,:]
        # print("왼쪽 버튼 눌림 \t x : {} y : {} 좌표의 픽셀값은 : {}".format(x,y, value) )
        global shrinked_cords
        global hangman

        for val in shrinked_cords:
             if x>=val['xmin']+576 and x<=val['xmax']+576 and y>=val['ymin']+100 and y<=val['ymax']+100:
                 hangman = cv2.rectangle(hangman, (int(val['xmin']) + 576, int(val['ymin']) + 100),
                                         (int(val['xmax']) + 576, int(val['ymax']) + 100), (255, 255, 255), 2)
                 print(shrinked_cords.index(val))
                 cv2.imshow("Hangman",hangman)

    return

위처럼 좌클릭으로 이벤트 처리를 하여 함수를 만들어봤으나 생각대로 클릭이 되지 않고 디버깅을 아무리 해봐도 고쳐지지가 않았다. 게다가 어떠한 크기가 큰 객체가 크기가 작은 객체를 완전히 둘러싸고 있는 형태의 사진이라면? 한 마우스 클릭의 구역에 해당되는 객체가 2개 이상인 문제가 생길 수도 있다.

결국 반 합리화,,반 회피 식으로 키보드 좌우 입력을 통하여 원하는 객체를 탐색할 수 있도록 하였다.

def keyboard_chooseobj():
    global shrinked_cords
    global hangman
    index_num = 0

    while shrinked_cords:
        key = cv2.waitKeyEx(30)
        if key == 0x270000:
            index_num += 1
            if index_num > len(shrinked_cords) - 1:
                index_num = 0
        elif key == 0x250000:
            index_num -= 1
            if index_num < 0:
                index_num = len(shrinked_cords) - 1
        elif key == 13:  # enter
            for val in shrinked_cords:
                if shrinked_cords.index(val) == index_num:
                    # print(val['name'])
                    return val
        elif key == 0x1B:  # esc
            break
        for val in shrinked_cords:
            if shrinked_cords.index(val) == index_num:
                hangman = cv2.rectangle(hangman, (int(val['xmin']) + 576, int(val['ymin']) + 100),
                                        (int(val['xmax']) + 576, int(val['ymax']) + 100), (255, 255, 255), 2)
            else:
                hangman = cv2.rectangle(hangman, (int(val['xmin']) + 576, int(val['ymin']) + 100),
                                        (int(val['xmax']) + 576, int(val['ymax']) + 100), (255, 0, 0), 2)
            cv2.imshow("Hangman", hangman)

키보드 ←, →를 통해 입력마다 인덱스를 바꿔가면서 해당 인덱스인 경우에 객체의 박스를 흰색으로 쳐줬으며, enter키를 누른 경우에 현재 흰색 박스가 된 객체의 레이블 값을 반환하도록 하였다.

📌객체 레이블 길이의 빈칸 박스 만들기

이제 문제가 나오고, 객체 선택까지 할 수 있다. 다음을 선택된 객체의 문제 풀이를 위한 빈칸을 마련해주는 것이다.

Hangman게임은 기본적으로 문자열의 길이가 얼만큼인지는 알려준다. 따라서 선택된 객체의 길이에 따라 빈칸박스를 만들어주는 함수를 정의했다.


def draw_blank_rects(word, char_rects, img):
    for i in range(len(char_rects)):
        top_left, bottom_right = char_rects[i]
        if not word[i].isalpha() or \
                ord(word[i]) < 65 or \
                ord(word[i]) > 122 or \
                (ord(word[i]) > 90 and \
                 ord(word[i]) < 97):
            cv2.putText(img, word[i], (top_left[0], \
                                       bottom_right[1]), \
                        cv2.FONT_HERSHEY_SIMPLEX, \
                        1, (0, 0, 255), 2)
            continue
        cv2.rectangle(img, top_left, \
                      bottom_right, \
                      (0, 0, 255), thickness=2, \
                      lineType=cv2.LINE_8)
    cv2.imshow("Hangman", img)
    return img

여기서 ord함수는 해당 문자의 유니코드 정수를 반환해주는 함수로, 각 문자가 알파벳이 맞는지 확인하는 절차를 추가하는 코드를 작성하였다. 또한
그 문자열마다 박스를 치는 좌표값을 알아야 하는데, 이를 위해 get_char_coords함수를 작성하였다.


def get_char_coords(word):
    x_coord = 230
    y_coord = 440

    char_ws = []
    char_hs = []

    for i in word:
        char_width, char_height = cv2.getTextSize(i, \
                                                  cv2.FONT_HERSHEY_SIMPLEX, 1, 2)[0]
        char_ws.append(char_width)
        char_hs.append(char_height)

    max_char_h = max(char_hs)
    max_char_w = max(char_ws)

    char_rects = []

    for i in range(len(char_ws)):
        rect_coord = [(x_coord, y_coord - max_char_h), \
                      (x_coord + max_char_w, y_coord)]
        char_rects.append(rect_coord)
        x_coord = x_coord + max_char_w

    return char_rects

결과적으로 enter을 누를 시 draw_blank_rects를 실행하여 위와 같은 화면을 띄울 수 있다.

profile
컴퓨터와 교육 그사이 어딘가

0개의 댓글