Ex5를 진행하다가, 어떤 얼굴이 가장 닮은 얼굴인지 고르는 게임이 있으면 재밌겠다는 생각이 들어서, 만들어보았습니다. 만들면서 개선한 점에 대해 중점으로 적어보겠습니다.
깃헙 링크를 참고해주세요. 종합하여 거리 순으로 나열하기 부분까지의 내용입니다.
matplotlib
으로 시각화한다set_something()
, get_something()
을 정의해본다모든 목표를 달성하지 못 했지만, 이러한 아이디어로 진행했습니다.
get_nearest_face_mod
: 타깃과 가까운 순서대로 top개, 혹은 먼 순서대로 top 개의 (이름, 타깃과의 거리) 순서쌍을 갖는 리스트를 반환합니다. 먼 쪽을 반환하는지 가까운 쪽을 반환하는지는 키워드 인자 closest
로 제어합니다. (mod는 함수를 임의로 수정했다는 의미로 넣었습니다)# 먼 이미지를 골라내기 위해 closest라는 키워드 인자를 추가한다. closest가 False이면 "먼" 이라는 문자열
# 과 key에 거리에 -1을 곱한 것이 기준이 되어 가장 먼 사람이 앞에 오게 된다.
def get_nearest_face_mod(name, top=5, *, closest=True):
near_faces = []
sort_name_func = get_sort_key_func(name)
if closest:
sorted_list = sorted(embedding_dict.keys(), key=lambda x: sort_name_func(x))
else:
sorted_list = sorted(embedding_dict.keys(), key=lambda x: -sort_name_func(x))
for idx, name in enumerate(sorted_list, start=1):
if idx > 1:
near_faces.append((name, sort_name_func(name)))
if idx > top:
return near_faces
plot_closest_mod
:get_nearest_face_mod(타깃, top=10)
이 반환하는 리스트leaderboard
를 입력으로 받아, 순서대로 이미지를 표시합니다. x_tick 과 y_tick을 없애고, xlabel은 사람의 (이름, 거리)로 설정합니다. 제목을 추가합니다.# x, y축 틱을 없애고, 이름을 표시합니다.
def plot_closest_mod(leaderboard, title):
#Set figsize here
fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(24,10))
# flatten axes for easy iterating
for i, ax in enumerate(axes.flatten()):
ax.axes.xaxis.set_ticks([])
ax.axes.yaxis.set_ticks([])
ax.set_xlabel(leaderboard[i])
image = mapping[leaderboard[i][0]]
image_path = img.imread(os.path.join(dir_path, image),0) # png
ax.imshow(image_path)
plt.suptitle(f"{title[0]}과 {title[1]} 상위 10명", fontweight="bold")
plt.show()
fig.tight_layout()
풀입스쿨에서 배웠던 private member를 사용하여 게임의 중요한 정보를 감추어보았습니다.
파이썬 클래스에서 private 변수 및 함수 사용하기
name
: 타깃의 이름입니다. (ex. 장동건.jpg -> name=='장동건')rounds
: 게임이 진행될 라운드입니다closest
: Boolean으로, True
라면 가까운 것을 찾는 게임으로, False
라면 먼 것을 찾는 게임으로 초기화합니다self.filename
: 타깃의 파일명입니다. 미리 정의해둔 mapping을 통해 접근합니다 (ex. '장동건.jpg')self.mode
: closest
의 값에 따라 "먼"과 "가까운"으로 정해집니다private attributes
self.__cand_list
: 게임에 사용될 이미지 인덱스의 배열입니다. np.random.randint
로 구현했습니다. (self.rounds
, 3) 의 형태로, 매 라운드마다 하나(axis=0)씩 쓰입니다
self.__rank
: 타깃과 거리가 가까운(먼) 200위까지의 (이름, 거리) 순서쌍의 리스트입니다.
self.__point
: 현재 점수입니다
self.__leaderboard
: 타깃과 거리가 가까운(먼) 10위까지의 (이름, 거리) 순서쌍의 리스트입니다
methods
set_val()
: private variable을 초기화합니다
get_val()
: 현재 라운드에 사용될 (이름, 거리) 순서쌍의 리스트를 반환합니다
cands도 누출되지 않도록 했어야 하는지 의문
earn_point()
: 점수를 초기화하고, 정답을 맞춘 경우 점수를 추가합니다
play()
: 게임을 시작합니다. 선택지를 그림으로 시각화하고, input으로 사용자로부터 받은 문자열과 정답을 비교합니다. 게임이 끝나면 plot_closest_mod
를 실행합니다.
개선할 점
정답을 찾아내는 알고리즘에 오류가 있어 self.closest
와 무관하게 가장 먼 사람을 정답으로 선정했습니다
타깃이 객체 생성시에만 보여, 문제를 푸는것이 불편합니다. 타깃의 사진을 매 라운드 보여주면 좋겠습니다
self.__point
의 값을 반환하는 함수를 만드는 것이 좋겠습니다
동일한 사람이 중복되어 나오는 경우가 있는데, 해결해야 할 것 같습니다
from time import sleep
class GuessingGame():
def __init__(self, name, *, rounds=8, closest=True):
self.__cand_list = None
self.__rank = None
self.__point = None
self.closest = closest
self.name = name
self.filename = mapping[name]
self.rounds = rounds
mode_list = ["먼", "가까운"]
self.mode = mode_list[closest]
fig_0, ax_0 = plt.subplots(figsize=(4,5))
ax_0.axes.xaxis.set_ticks([])
ax_0.axes.yaxis.set_ticks([])
plt.xlabel(name)
image_path = img.imread(os.path.join(dir_path, self.filename),0) # png
ax_0.imshow(image_path)
print(f"{name}과 {self.mode} 사람 맞추기 게임이 준비되었습니다. ({rounds} 라운드)")
print("시작하시려면 .play()를 실행하세요")
self.earn_point()
def play(self):
self.set_val()
print(f"게임 시작")
for i in range(1, self.rounds+1):
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(12,5))
candidates = self.get_val(i)
for j, ax in enumerate(axes.flatten()):
ax.axes.xaxis.set_ticks([])
ax.axes.yaxis.set_ticks([])
ax.set_xlabel(f"{candidates[j][0]}")
img_path = mapping[candidates[j][0]]
image_path = img.imread(os.path.join(dir_path, img_path),0) # png
ax.imshow(image_path)
plt.suptitle(f"라운드 {i}: 3명 중 가장 {self.name}과 {self.mode} 사람을 골라주세요.", fontweight="bold")
plt.show()
sleep(8)
picked = input("정답이라고 생각되는 이름을 정확히 입력해주세요.")
correct = sorted(candidates, key=lambda candidates: -candidates[1])[0][0]
if picked == correct:
print("정답입니다. 포인트 1점을 얻으셨습니다.")
self.earn_point()
print(f"현재 포인트: {self.__point}.")
else:
print(f"오답입니다... 정답: {correct}")
# 게임 끝
print(f'게임이 끝났습니다. 최종 스코어는 {self.__point}점 입니다')
plot_closest_mod(self.__leaderboard, (self.name, self.mode))
def set_val(self):
self.__cand_list = np.random.randint(1, 201 , (self.rounds,3))
self.__rank = get_nearest_face_mod(name=self.name, top=200, closest=self.closest)
self.__leaderboard = self.__rank[:10]
def get_val(self, cur_round):
cands = []
for i in range(3):
cands.append(self.__rank[self.__cand_list[cur_round-1][i]])
return cands
def earn_point(self):
if self.__point == None:
self.__point = 0
else:
self.__point += 1
등수, 이름, 거리를 표시하도록 수정했습니다
# x, y축 틱을 없애고, 이름을 표시한다.
def plot_closest(leaderboard, title):
# Set figsize here
fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(24,10))
# flatten axes for easy iterating
for i, ax in enumerate(axes.flatten()):
ax.axes.xaxis.set_ticks([])
ax.axes.yaxis.set_ticks([])
ax.set_xlabel(f"{i+1}등: {leaderboard[i][0]}, 거리: {leaderboard[i][1]}")
image = mapping[leaderboard[i][0]]
image_path = img.imread(os.path.join(dir_path, image),0) # png
ax.imshow(image_path)
plt.suptitle(f"{title[0]}와(과) {title[1]} 상위 10명", fontweight="bold")
plt.show()
fig.tight_layout()
self.closest
에 따라 정답이 제대로 골라지게 수정했습니다
우측에 타깃의 이미지가 표시되도록 수정했습니다
정답 입력 문구를 수정하고, 오답이더라도 현재 점수를 출력합니다
현재 점수를 반환하는 get_point()
를 정의하여 사용했습니다
from time import sleep
#우측에 비교할 이미지를 표시한다.
#비복원추출로 변경한다.
#정답을 골라내는데 문제가 있어 수정했다.
class GuessingGameV3():
def __init__(self, name, *, rounds=8, closest=True):
self.__cand_list = None
self.__rank = None
self.__point = None
self.__leaderboard = None
self.closest = closest
self.name = name
self.filename = mapping[name]
self.rounds = rounds
mode_list = ["먼", "가까운"]
self.mode = mode_list[closest]
fig, ax = plt.subplots(figsize=(4,5))
ax.axes.xaxis.set_ticks([])
ax.axes.yaxis.set_ticks([])
plt.xlabel(name)
image_path = img.imread(os.path.join(dir_path, self.filename),0) # png
ax.imshow(image_path)
print(f"{name}와(과) {self.mode} 사람 맞추기 게임이 준비되었습니다. ({rounds} 라운드)")
print("시작하시려면 .play()를 실행하세요")
self.earn_point()
def play(self):
self.set_val()
print(f"게임 시작")
for cur_round in range(1, self.rounds+1):
self.__round_cand_list = None
self.__correct = None
self.__round_cand_list = self.get_val(cur_round)
self.plot_candidates(cur_round)
print("정답이라고 생각되는 이름을 정확히 입력해주세요.")
picked = input("당신의 선택: --------->")
self.__correct = self.get_correct_answer(self.closest, self.__round_cand_list)
if picked == self.__correct:
print("정답입니다. 포인트 1점을 얻으셨습니다.")
self.earn_point()
print(f"현재 포인트: {self.get_point()}.")
else:
print(f"오답입니다... 정답: {self.__correct}")
print(f"현재 포인트: {self.get_point()}.")
# 게임 끝
print(f'게임이 끝났습니다. 최종 스코어는 {self.get_point()}점 입니다')
plot_closest(self.__leaderboard, (self.name, self.mode)) # 변경
def get_correct_answer(self, closest, round_cand_list):
if closest: return sorted(round_cand_list, key=lambda round_cand_list: round_cand_list[1])[0][0]
else: return sorted(round_cand_list, key=lambda round_cand_list: -round_cand_list[1])[0][0]
def plot_candidates(self, cur_round):
fig, axes = plt.subplots(nrows=1, ncols=4, figsize=(16,5))
for j, ax in enumerate(axes.flatten()):
ax.axes.xaxis.set_ticks([])
ax.axes.yaxis.set_ticks([])
if j != 3:
ax.set_xlabel(f"{self.__round_cand_list[j][0]}")
img_path = mapping[self.__round_cand_list[j][0]]
else:
ax.set_xlabel(f"{self.name}")
img_path = self.filename
image_path = img.imread(os.path.join(dir_path, img_path),0) # png
ax.imshow(image_path)
plt.suptitle(f"라운드 {cur_round}: 3명 중 가장 {self.name}와(과) {self.mode} 사람을 골라주세요.", fontweight="bold")
plt.show()
sleep(4)
def set_val(self):
# self.__cand_list = np.random.randint(1, 201 , (self.rounds,3))
self.__cand_list = np.random.choice(200, size=(self.rounds,3), replace=False )
self.__rank = get_nearest_face_mod(name=self.name, top=200, closest=self.closest)
self.__leaderboard = self.__rank[:10]
def get_val(self, cur_round):
cands = []
for i in range(3):
cands.append(self.__rank[self.__cand_list[cur_round-1][i]])
return cands
def earn_point(self):
if self.__point == None:
self.__point = 0
else:
self.__point += 1
def get_point(self):
return self.__point