mosshoon's picture
update: 2026.04.13
70ce021
Raw
History Blame Contribute Delete
28.9 kB
import gradio as gr
import cv2
import numpy as np
import pandas as pd
import json
import os
import threading
import gc
from datetime import datetime
try:
from pyngrok import ngrok
NGROK_AVAILABLE = True
except ImportError:
NGROK_AVAILABLE = False
try:
from skimage.metrics import structural_similarity as ssim
from skimage.metrics import peak_signal_noise_ratio as psnr
except ImportError:
# scikit-image๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋Œ€์ฒด ๋ฐฉ๋ฒ• ์‚ฌ์šฉ
def ssim(img1, img2):
# ๊ฐ„๋‹จํ•œ MSE ๊ธฐ๋ฐ˜ ์œ ์‚ฌ๋„ ๊ณ„์‚ฐ
mse = np.mean((img1 - img2) ** 2)
return 1 / (1 + mse / 1000)
def psnr(img1, img2):
mse = np.mean((img1 - img2) ** 2)
if mse == 0:
return 100
return 20 * np.log10(255.0 / np.sqrt(mse))
import io
from PIL import Image
import torch
import torchvision.models as models
import torchvision.transforms as transforms
import ssl
# Fix SSL error for model download (macOS specific)
ssl._create_default_https_context = ssl._create_unverified_context
# ========== ์—ฌ๊ธฐ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ํƒญ์ด ๋™์ ์œผ๋กœ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค ==========
# ์˜ˆ์‹œ: ["Response๋ฐ˜", "Challenge๋ฐ˜"] โ†’ ์—ฐ์Šต ํƒญ + 2๊ฐœ ๋ฐ˜ = ์ด 3๊ฐœ ํƒญ
# ์˜ˆ์‹œ: ["206ํ˜ธ", "207ํ˜ธ", "305ํ˜ธ"] โ†’ ์—ฐ์Šต ํƒญ + 3๊ฐœ ๋ฐ˜ = ์ด 4๊ฐœ ํƒญ
# CLASSES = ["Respect๋ฐ˜", "Challenge๋ฐ˜", "Originality๋ฐ˜", "BCE๋ฐ˜"]
CLASSES = ["Respect๋ฐ˜", "Challenge๋ฐ˜", "Originality๋ฐ˜", "BCE๋ฐ˜"]
# ============================================================
# ํƒญ ์•„์ด์ฝ˜ ์ž๋™ ๋ฐฐ์ •
_TAB_ICONS = ["๐ŸŽฏ", "โšก", "๐ŸŽจ", "๐Ÿ”ฅ", "๐Ÿ’Ž", "๐ŸŒŸ", "๐Ÿš€", "๐Ÿ’ก", "๐Ÿ…", "โœจ"]
def _class_key(name):
"""๋ฐ˜ ์ด๋ฆ„์„ json ํŒŒ์ผ๋ช…/๋”•์…”๋„ˆ๋ฆฌ ํ‚ค๋กœ ๋ณ€ํ™˜ (์˜ˆ: 'Respect๋ฐ˜' โ†’ 'respect')"""
# ํ•œ๊ธ€ '๋ฐ˜' ์ œ๊ฑฐ, ๊ณต๋ฐฑโ†’์–ธ๋”์Šค์ฝ”์–ด, ์†Œ๋ฌธ์ž
return name.replace("๋ฐ˜", "").replace("ํ˜ธ", "").strip().lower().replace(" ", "_")
class ImageSimilarityLeaderboard:
def __init__(self, reference_image_path="label2.jpg", data_file="leaderboard.json"):
self.reference_image_path = reference_image_path
self.data_file = data_file
self.admin_password = "9900" # ๊ด€๋ฆฌ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ
self.admin_authenticated = False # ๊ด€๋ฆฌ์ž ์ธ์ฆ ์ƒํƒœ
# ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ ์บ์‹œ ๋ฐ ๋ฝ (๋จผ์ € ์ดˆ๊ธฐํ™”)
self._ref_image_cache = None
self._ref_embedding = None # ResNet ์ž„๋ฒ ๋”ฉ ์บ์‹œ
self._cache_loaded = False
self._file_lock = threading.Lock() # ํŒŒ์ผ I/O ๋™์‹œ์„ฑ ์ œ์–ด
self._processing_lock = threading.Lock() # ์ฒ˜๋ฆฌ ๋™์‹œ์„ฑ ์ œ์–ด
# ๋ฝ ์ดˆ๊ธฐํ™” ํ›„ ๋ฐ์ดํ„ฐ ๋กœ๋“œ
self.leaderboard_data = self.load_leaderboard()
self.last_modified = self.get_file_modified_time()
# ResNet ๋ชจ๋ธ ์ดˆ๊ธฐํ™” (ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œ)
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
try:
# ResNet50 (ImageNet weights) - ๋งˆ์ง€๋ง‰ FC ๋ ˆ์ด์–ด ์ œ์™ธ
resnet = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
self.resnet_model = torch.nn.Sequential(*(list(resnet.children())[:-1])).to(self.device)
self.resnet_model.eval()
# ์ „์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ
self.preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
print(f"โœ… ResNet ๋ชจ๋ธ ๋กœ๋“œ ์™„๋ฃŒ (Device: {self.device})")
except Exception as e:
print(f"โš ๏ธ ResNet ๋ชจ๋ธ ๋กœ๋“œ ์‹คํŒจ: {e}")
self.resnet_model = None
# macOS ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•œ ๊ฒฝ๊ณ  ์–ต์ œ
import warnings
warnings.filterwarnings("ignore", category=UserWarning, module="cv2")
def load_leaderboard(self):
"""๋ฆฌ๋”๋ณด๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค."""
with self._file_lock: # ํŒŒ์ผ I/O ๋™์‹œ์„ฑ ์ œ์–ด
if os.path.exists(self.data_file):
try:
with open(self.data_file, 'r', encoding='utf-8') as f:
return json.load(f)
except:
return []
return []
def get_file_modified_time(self):
"""ํŒŒ์ผ ์ˆ˜์ • ์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."""
if os.path.exists(self.data_file):
return os.path.getmtime(self.data_file)
return 0
def save_leaderboard(self):
"""๋ฆฌ๋”๋ณด๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค."""
with self._file_lock: # ํŒŒ์ผ I/O ๋™์‹œ์„ฑ ์ œ์–ด
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(self.leaderboard_data, f, ensure_ascii=False, indent=2)
self.last_modified = self.get_file_modified_time()
def check_for_updates(self):
"""๋ฆฌ๋”๋ณด๋“œ ์—…๋ฐ์ดํŠธ ํ™•์ธ"""
current_modified = self.get_file_modified_time()
if current_modified > self.last_modified:
self.leaderboard_data = self.load_leaderboard()
self.last_modified = current_modified
return True
return False
def _load_reference_image(self):
"""์ฐธ์กฐ ์ด๋ฏธ์ง€๋ฅผ ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œํ•˜๊ณ  ์บ์‹œํ•ฉ๋‹ˆ๋‹ค."""
if not self._cache_loaded:
if os.path.exists(self.reference_image_path):
ref_image = cv2.imread(self.reference_image_path)
if ref_image is not None:
# macOS ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”
if ref_image.shape[0] > 1024 or ref_image.shape[1] > 1024:
# ํฐ ์ด๋ฏธ์ง€๋Š” ๋ฏธ๋ฆฌ ๋ฆฌ์‚ฌ์ด์ฆˆํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๊ฐ์†Œ
scale = min(1024 / ref_image.shape[0], 1024 / ref_image.shape[1])
if scale < 1:
new_width = int(ref_image.shape[1] * scale)
new_height = int(ref_image.shape[0] * scale)
ref_image = cv2.resize(ref_image, (new_width, new_height))
self._ref_image_cache = cv2.cvtColor(ref_image, cv2.COLOR_BGR2RGB)
# ResNet ์ž„๋ฒ ๋”ฉ ๊ณ„์‚ฐ ๋ฐ ์บ์‹œ
if self.resnet_model is not None:
try:
pil_img = Image.fromarray(self._ref_image_cache)
img_t = self.preprocess(pil_img).unsqueeze(0).to(self.device)
with torch.no_grad():
self._ref_embedding = self.resnet_model(img_t).flatten()
except Exception as e:
print(f"์ฐธ์กฐ ์ด๋ฏธ์ง€ ์ž„๋ฒ ๋”ฉ ์‹คํŒจ: {e}")
self._cache_loaded = True
# ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ
del ref_image
gc.collect()
return self._ref_image_cache
def _get_memory_usage(self):
"""ํ˜„์žฌ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."""
try:
import psutil
process = psutil.Process()
memory_info = process.memory_info()
return memory_info.rss / 1024 / 1024 # MB ๋‹จ์œ„
except (ImportError, AttributeError):
# macOS๋‚˜ ๋‹ค๋ฅธ ํ™˜๊ฒฝ์—์„œ psutil์ด ์—†๊ฑฐ๋‚˜ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ
try:
import resource
# getrusage๋ฅผ ํ†ตํ•œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ธก์ •
usage = resource.getrusage(resource.RUSAGE_SELF)
return usage.ru_maxrss / 1024 # KB -> MB
except:
return 0
def calculate_similarity(self, image1, image2):
try:
# 1) ResNet Feature Similarity (Semantic Similarity) - ๊ฐ€์žฅ ์ค‘์š”
resnet_score = 0.0
if self.resnet_model is not None and self._ref_embedding is not None:
try:
# ์‚ฌ์šฉ์ž ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
pil_img = Image.fromarray(image2) # image2 is RGB numpy array
img_t = self.preprocess(pil_img).unsqueeze(0).to(self.device)
with torch.no_grad():
user_emb = self.resnet_model(img_t).flatten()
# Cosine Similarity
cos_sim = torch.nn.functional.cosine_similarity(
self._ref_embedding.unsqueeze(0),
user_emb.unsqueeze(0)
).item()
# Sigmoid Scoring Formula
# Sim 0.61 (Bad) -> Score 14
# Sim 0.77 (Good) -> Score 80
# Sim 0.92 (Perfect) -> Score 99
# Formula: 100 / (1 + exp(-20 * (sim - 0.7)))
resnet_score = 100 / (1 + np.exp(-20 * (cos_sim - 0.7)))
except Exception as e:
print(f"ResNet ๊ณ„์‚ฐ ์˜ค๋ฅ˜: {e}")
resnet_score = 0.0
# 2) ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜ (๊ธฐ์กด ๋กœ์ง ์œ ์ง€)
if image1.ndim == 3:
gray1 = cv2.cvtColor(image1, cv2.COLOR_RGB2GRAY)
else:
gray1 = image1.copy()
if image2.ndim == 3:
gray2 = cv2.cvtColor(image2, cv2.COLOR_RGB2GRAY)
else:
gray2 = image2.copy()
# 3) ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ํ‘œ์ค€ ํฌ๊ธฐ๋กœ ๋ฆฌ์‚ฌ์ด์ฆˆ (512x512)
target_size = (512, 512)
gray1 = cv2.resize(gray1, target_size, interpolation=cv2.INTER_LINEAR)
gray2 = cv2.resize(gray2, target_size, interpolation=cv2.INTER_LINEAR)
# 4) SSIM ๊ณ„์‚ฐ (๊ตฌ์กฐ์  ์œ ์‚ฌ๋„)
try:
ssim_score = ssim(gray1.astype(np.float32), gray2.astype(np.float32))
except:
# SSIM ๊ณ„์‚ฐ ์‹คํŒจ์‹œ MSE ๊ธฐ๋ฐ˜ ๋Œ€์ฒด
mse = np.mean((gray1.astype(np.float32) - gray2.astype(np.float32)) ** 2)
ssim_score = 1 / (1 + mse / 10000)
# 5) PSNR ๊ณ„์‚ฐ (ํ”ฝ์…€ ๋‹จ์œ„ ์œ ์‚ฌ๋„)
mse = np.mean((gray1.astype(np.float32) - gray2.astype(np.float32)) ** 2)
if mse == 0:
psnr_score = 1.0
else:
psnr_score = 20 * np.log10(255.0 / np.sqrt(mse))
# PSNR์„ 0-1 ๋ฒ”์œ„๋กœ ๋” ๊ด€๋Œ€ํ•˜๊ฒŒ ์ •๊ทœํ™” (๋ณดํ†ต PSNR์€ 20-40 ๋ฒ”์œ„)
psnr_score = min(psnr_score / 40.0, 1.0)
# 6) ํžˆ์Šคํ† ๊ทธ๋žจ ์œ ์‚ฌ๋„
hist1 = cv2.calcHist([gray1], [0], None, [256], [0, 256])
hist2 = cv2.calcHist([gray2], [0], None, [256], [0, 256])
hist_corr = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
hist_score = (hist_corr + 1) / 2 # -1~1 โ†’ 0~1
# 7) ์ตœ์ข… ์ ์ˆ˜ ๊ณ„์‚ฐ (ResNet ๋น„์ค‘ ๋Œ€ํญ ๊ฐ•ํ™”)
# ResNet ๋ชจ๋ธ์ด ์žˆ์œผ๋ฉด ResNet 80%, SSIM 10%, Hist 10%
if self.resnet_model is not None:
final_score = (resnet_score * 0.8) + (ssim_score * 100 * 0.1) + (hist_score * 100 * 0.1)
else:
print(f"ResNet ๋ชจ๋ธ์ด ์—†์–ด์„œ SSIM 70%, Hist 30%๋กœ ๊ณ„์‚ฐ")
final_score = (ssim_score * 0.7 + hist_score * 0.3) * 100
# 8) PSNR์ด ๋†’์œผ๋ฉด ์•ฝ๊ฐ„์˜ ๋ณด๋„ˆ์Šค (์ตœ๋Œ€ 5์ )
if psnr_score > 0.8:
bonus = min((psnr_score - 0.8) * 25, 5)
final_score = min(final_score + bonus, 100)
return {
'ssim': float(ssim_score),
'psnr': float(psnr_score * 100),
'histogram': float(hist_score),
'resnet': float(resnet_score), # ๊ฒฐ๊ณผ์— ํฌํ•จ
'final_score': float(final_score)
}
except Exception as e:
print(f"์œ ์‚ฌ๋„ ๊ณ„์‚ฐ ์˜ค๋ฅ˜: {e}")
return {'ssim':0.0,'psnr':0.0,'histogram':0.0,'resnet':0.0,'final_score':0.0}
def process_image(self, uploaded_image, username):
"""์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค."""
if uploaded_image is None:
return "๐Ÿ“ค ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", self.get_leaderboard_df()
# ์‚ฌ์šฉ์ž๋ช… ๊ฒ€์ฆ ์ œ๊ฑฐ - ์–ด๋–ค ์ด๋ฆ„์ด๋“  ํ—ˆ์šฉ
if not username or not username.strip():
return "โŒ ์‚ฌ์šฉ์ž ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", self.get_leaderboard_df()
username = username.strip()
# ๋™์‹œ์„ฑ ์ œ์–ด - ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋งŒ ์ฒ˜๋ฆฌ
with self._processing_lock:
try:
# ์บ์‹œ๋œ ์ฐธ์กฐ ์ด๋ฏธ์ง€ ์‚ฌ์šฉ
ref_image = self._load_reference_image()
if ref_image is None:
return f"์ฐธ์กฐ ์ด๋ฏธ์ง€({self.reference_image_path})๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", None
# ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ numpy ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜
if isinstance(uploaded_image, str):
# ํŒŒ์ผ ๊ฒฝ๋กœ์ธ ๊ฒฝ์šฐ
user_image = cv2.imread(uploaded_image)
if user_image is None:
return "์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", None
user_image = cv2.cvtColor(user_image, cv2.COLOR_BGR2RGB)
else:
# PIL Image์ธ ๊ฒฝ์šฐ
user_image = np.array(uploaded_image)
if user_image is None or user_image.size == 0:
return "์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", None
# ์ด๋ฏธ์ง€ ํฌ๊ธฐ ํ™•์ธ
if user_image.shape[0] < 10 or user_image.shape[1] < 10:
return "์ด๋ฏธ์ง€๊ฐ€ ๋„ˆ๋ฌด ์ž‘์Šต๋‹ˆ๋‹ค. ์ตœ์†Œ 10x10 ํ”ฝ์…€ ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.", None
# ์œ ์‚ฌ๋„ ๊ณ„์‚ฐ
similarity_scores = self.calculate_similarity(ref_image, user_image)
# ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ (macOS ์ตœ์ ํ™”)
del user_image
if 'ref_image' in locals():
del ref_image
gc.collect()
# macOS์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๊ฐ•์ œ ์ •๋ฆฌ
import platform
if platform.system() == 'Darwin': # macOS
import ctypes
try:
libc = ctypes.CDLL('libc.dylib')
libc.malloc_trim(0)
except:
pass
# ๋ฆฌ๋”๋ณด๋“œ์— ์ถ”๊ฐ€ (JSON ์ง๋ ฌํ™”๋ฅผ ์œ„ํ•ด float๋กœ ๋ณ€ํ™˜)
entry = {
'username': username,
'score': float(round(similarity_scores['final_score'], 2)),
'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'ssim': float(round(similarity_scores['ssim'], 4)),
'psnr': float(round(similarity_scores['psnr'], 2)),
'histogram': float(round(similarity_scores['histogram'], 4)),
'resnet': float(round(similarity_scores.get('resnet', 0.0), 2))
}
# ๊ฐ™์€ ์ด๋ฆ„์˜ ๊ธฐ์กด ๊ธฐ๋ก์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋” ๋†’์€ ์ ์ˆ˜๋งŒ ์œ ์ง€
existing_indices = [i for i, data in enumerate(self.leaderboard_data) if data['username'] == username]
if existing_indices:
# ๊ธฐ์กด ๊ธฐ๋ก ์ค‘ ๊ฐ€์žฅ ๋†’์€ ์ ์ˆ˜ ์ฐพ๊ธฐ
existing_scores = [self.leaderboard_data[i]['score'] for i in existing_indices]
max_existing_score = max(existing_scores)
# ์ƒˆ ์ ์ˆ˜๊ฐ€ ๋” ๋†’์œผ๋ฉด ๊ธฐ์กด ๊ธฐ๋ก๋“ค์„ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๊ณ  ์ƒˆ ๊ธฐ๋ก ์ถ”๊ฐ€
if entry['score'] > max_existing_score:
# ๊ธฐ์กด ๊ธฐ๋ก๋“ค์„ ์—ญ์ˆœ์œผ๋กœ ์ œ๊ฑฐ (์ธ๋ฑ์Šค ๋ณ€๊ฒฝ ๋ฐฉ์ง€)
for i in sorted(existing_indices, reverse=True):
del self.leaderboard_data[i]
self.leaderboard_data.append(entry)
self.save_leaderboard()
# ๊ฐฑ์‹ ๋œ ๊ฒฝ์šฐ์˜ ๋ฉ”์‹œ์ง€
result_message = f"""๐ŸŽ‰ ๋Œ€๋‹จํ•ด์š”! ์ƒˆ๋กœ์šด ์ตœ๊ณ  ๊ธฐ๋ก์ž…๋‹ˆ๋‹ค!
๐Ÿ‘ค {username}๋‹˜
๐Ÿ† ์ ์ˆ˜: {entry['score']:.0f}์ 
๐Ÿ“ˆ ์ด์ „ ์ตœ๊ณ : {max_existing_score:.0f}์ 
โœ… ๋ฆฌ๋”๋ณด๋“œ์— ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
๐Ÿ“… {entry['date']}"""
else:
# ์ƒˆ ์ ์ˆ˜๊ฐ€ ๋” ๋‚ฎ๊ฑฐ๋‚˜ ๊ฐ™์œผ๋ฉด ๋ฆฌ๋”๋ณด๋“œ๋Š” ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š์Œ
result_message = f"""๐ŸŽฏ ์ ์ˆ˜๊ฐ€ ๊ณ„์‚ฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
๐Ÿ‘ค {username}๋‹˜
๐Ÿ† ํ˜„์žฌ ์ ์ˆ˜: {entry['score']:.0f}์ 
๐Ÿ“ˆ ์ตœ๊ณ  ์ ์ˆ˜: {max_existing_score:.0f}์ 
๐Ÿ’ช ์กฐ๊ธˆ๋งŒ ๋” ๋…ธ๋ ฅํ•˜๋ฉด ์ตœ๊ณ  ๊ธฐ๋ก์„ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•„์š”!
๋‹ค์‹œ ์‹œ๋„ํ•ด๋ณด์„ธ์š”!"""
else:
# ๊ธฐ์กด ๊ธฐ๋ก์ด ์—†์œผ๋ฉด ์ƒˆ๋กœ ์ถ”๊ฐ€
self.leaderboard_data.append(entry)
self.save_leaderboard()
# ์ƒˆ๋กœ ๋“ฑ๋ก๋œ ๊ฒฝ์šฐ์˜ ๋ฉ”์‹œ์ง€
result_message = f"""๐ŸŽ‰ ์ฒซ ๋“ฑ๋ก์„ ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค!
๐Ÿ‘ค {username}๋‹˜
๐Ÿ† ์ ์ˆ˜: {entry['score']:.0f}์ 
โœ… ๋ฆฌ๋”๋ณด๋“œ์— ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
๐Ÿ“… {entry['date']}"""
return result_message, self.get_leaderboard_df()
except Exception as e:
import traceback
error_msg = f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}\n\n์ƒ์„ธ ์ •๋ณด:\n{traceback.format_exc()}"
return error_msg, None
def get_leaderboard_df(self):
"""๋ฆฌ๋”๋ณด๋“œ๋ฅผ DataFrame์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."""
if not self.leaderboard_data:
return pd.DataFrame(columns=['์ˆœ์œ„', '์‚ฌ์šฉ์ž๋ช…', '์ ์ˆ˜', '๋‚ ์งœ'])
# ์ ์ˆ˜ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ
sorted_data = sorted(self.leaderboard_data, key=lambda x: x['score'], reverse=True)
# DataFrame ์ƒ์„ฑ
df_data = []
for i, entry in enumerate(sorted_data, 1):
df_data.append({
'์ˆœ์œ„': i,
'์‚ฌ์šฉ์ž๋ช…': entry['username'],
'์ ์ˆ˜': entry['score'],
'๋‚ ์งœ': entry['date']
})
return pd.DataFrame(df_data)
def authenticate_admin(self, password):
"""๊ด€๋ฆฌ์ž ์ธ์ฆ"""
if password == self.admin_password:
self.admin_authenticated = True
return "โœ… ๊ด€๋ฆฌ์ž ์ธ์ฆ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", True
else:
self.admin_authenticated = False
return "โŒ ์ž˜๋ชป๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ์ž…๋‹ˆ๋‹ค.", False
def update_reference_image(self, new_image):
"""์ฐธ์กฐ ์ด๋ฏธ์ง€ ์—…๋ฐ์ดํŠธ"""
if not self.admin_authenticated:
return "โŒ ๊ด€๋ฆฌ์ž ์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
try:
if new_image is None:
return "โŒ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
# ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅ
new_image.save(self.reference_image_path)
return f"โœ… ์ฐธ์กฐ ์ด๋ฏธ์ง€๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ({self.reference_image_path})"
except Exception as e:
return f"โŒ ์ด๋ฏธ์ง€ ์ €์žฅ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
def clear_leaderboard(self):
"""๋ฆฌ๋”๋ณด๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค."""
if not self.admin_authenticated:
return "โŒ ๊ด€๋ฆฌ์ž ์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.", self.get_leaderboard_df()
self.leaderboard_data = []
self.save_leaderboard()
return "โœ… ๋ฆฌ๋”๋ณด๋“œ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", pd.DataFrame(columns=['์ˆœ์œ„', '์‚ฌ์šฉ์ž๋ช…', '์ ์ˆ˜', '๋‚ ์งœ'])
# โ”€โ”€ ์ „์—ญ ๋ฆฌ๋”๋ณด๋“œ ์ธ์Šคํ„ด์Šค (CLASSES ๊ธฐ๋ฐ˜ ๋™์  ์ƒ์„ฑ + ์—ฐ์Šต) โ”€โ”€
leaderboards = {}
for _cls in CLASSES:
leaderboards[_class_key(_cls)] = ImageSimilarityLeaderboard("label2.jpg", f"{_class_key(_cls)}.json")
leaderboards['practice'] = ImageSimilarityLeaderboard("label2.jpg", "practice.json")
# โ”€โ”€ ๋ฒ”์šฉ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ โ”€โ”€
def process_user_image(class_key, image, username):
"""์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ (๋ชจ๋“  ๋ฐ˜ ๊ณต์šฉ)"""
return leaderboards[class_key].process_image(image, username)
def get_current_leaderboard(class_key):
"""ํ˜„์žฌ ๋ฆฌ๋”๋ณด๋“œ ๋ฐ˜ํ™˜ (๋ชจ๋“  ๋ฐ˜ ๊ณต์šฉ)"""
return leaderboards[class_key].get_leaderboard_df()
def check_and_update_leaderboard(class_key):
"""๋ฆฌ๋”๋ณด๋“œ ์—…๋ฐ์ดํŠธ ํ™•์ธ ๋ฐ ๋ฐ˜ํ™˜ (๋ชจ๋“  ๋ฐ˜ ๊ณต์šฉ)"""
leaderboards[class_key].check_for_updates()
return leaderboards[class_key].get_leaderboard_df()
def create_interface():
"""Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ (CLASSES ๊ธฐ๋ฐ˜ ๋™์  ํƒญ)"""
with gr.Blocks(title="๋น„์Šทํ•œ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”!", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# ๐Ÿ† ๋กฏ๋ฐ ๋น„์ „ ์ŠคํŠœ๋””์˜ค ๋ฆฌ๋”๋ณด๋“œ
""")
# ๋ชจ๋“  ๋ฆฌ๋”๋ณด๋“œ ์ถœ๋ ฅ ์œ„์ ฏ์„ ๋ชจ์•„๋‘˜ ๋”•์…”๋„ˆ๋ฆฌ
leaderboard_outputs = {}
# ํƒญ ์ƒ์„ฑ
with gr.Tabs():
# โ”€โ”€ ์—ฐ์Šต ํƒญ (ํ•ญ์ƒ ์ฒซ ๋ฒˆ์งธ) โ”€โ”€
with gr.Tab("๐ŸŽ“ ์—ฐ์Šต"):
gr.Markdown("### ๐Ÿ’ก ์—ฐ์Šต์šฉ ํƒญ (๋ฉ”์ธ)")
gr.Markdown("์—…๋กœ๋“œ ๋ฐ ์ด๋ฏธ์ง€ ์œ ์‚ฌ๋„๋ฅผ ํ…Œ์ŠคํŠธํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ์—ฐ์Šต ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค.")
gr.Markdown("---")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### ๐Ÿ“ค ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ (์—ฐ์Šต)")
image_input_practice = gr.Image(
label="๋น„๊ตํ•  ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜์„ธ์š”",
type="pil",
height=300
)
username_input_practice = gr.Textbox(
label="์‚ฌ์šฉ์ž ์ด๋ฆ„ (์—ฐ์Šต์šฉ)",
placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”",
max_lines=1
)
submit_btn_practice = gr.Button("๐Ÿš€ ์œ ์‚ฌ๋„ ํ…Œ์ŠคํŠธ", variant="primary", size="lg")
with gr.Column(scale=1):
gr.Markdown("### ๐Ÿ“Š ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ")
result_output_practice = gr.Textbox(
label="์œ ์‚ฌ๋„ ๋ถ„์„ ๊ฒฐ๊ณผ",
lines=12,
interactive=False
)
gr.Markdown("### ๐Ÿ… ์—ฐ์Šต ๋ฆฌ๋”๋ณด๋“œ")
leaderboard_output_practice = gr.Dataframe(
headers=["์ˆœ์œ„", "์‚ฌ์šฉ์ž๋ช…", "์ ์ˆ˜", "๋‚ ์งœ"],
datatype=["number", "str", "number", "str"],
interactive=False
)
leaderboard_outputs['practice'] = leaderboard_output_practice
# ์—ฐ์Šต ํƒญ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
submit_btn_practice.click(
fn=lambda img, name: process_user_image('practice', img, name),
inputs=[image_input_practice, username_input_practice],
outputs=[result_output_practice, leaderboard_output_practice]
)
# โ”€โ”€ ๋ฐ˜๋ณ„ ํƒญ (CLASSES ๊ธฐ๋ฐ˜ ๋™์  ์ƒ์„ฑ) โ”€โ”€
for idx, class_name in enumerate(CLASSES):
icon = _TAB_ICONS[idx % len(_TAB_ICONS)]
key = _class_key(class_name)
with gr.Tab(f"{icon} {class_name}"):
with gr.Row():
with gr.Column(scale=1):
gr.Markdown(f"### ๐Ÿ“ค ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ({class_name})")
image_input = gr.Image(
label="๋น„๊ตํ•  ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜์„ธ์š”",
type="pil",
height=300
)
username_input = gr.Textbox(
label="์‚ฌ์šฉ์ž ์ด๋ฆ„",
placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”",
max_lines=1
)
submit_btn = gr.Button("๐Ÿš€ ์ ์ˆ˜ ๊ณ„์‚ฐ ๋ฐ ๋“ฑ๋ก", variant="primary", size="lg")
with gr.Column(scale=1):
gr.Markdown(f"### ๐Ÿ“Š ๊ฒฐ๊ณผ ({class_name})")
result_output = gr.Textbox(
label="๊ณ„์‚ฐ ๊ฒฐ๊ณผ",
lines=10,
interactive=False
)
gr.Markdown(f"### ๐Ÿ… {class_name} ๋ฆฌ๋”๋ณด๋“œ")
leaderboard_output = gr.Dataframe(
headers=["์ˆœ์œ„", "์‚ฌ์šฉ์ž๋ช…", "์ ์ˆ˜", "๋‚ ์งœ"],
datatype=["number", "str", "number", "str"],
interactive=False
)
leaderboard_outputs[key] = leaderboard_output
# ํด๋กœ์ €์—์„œ key ๊ฐ’์„ ์บก์ฒ˜ํ•˜๊ธฐ ์œ„ํ•ด default argument ์‚ฌ์šฉ
submit_btn.click(
fn=lambda img, name, k=key: process_user_image(k, img, name),
inputs=[image_input, username_input],
outputs=[result_output, leaderboard_output]
)
# โ”€โ”€ ๋ชจ๋“  ํ‚ค ์ˆœ์„œ (practice ๋จผ์ €, ์ดํ›„ CLASSES ์ˆœ์„œ) โ”€โ”€
all_keys = ['practice'] + [_class_key(c) for c in CLASSES]
all_outputs = [leaderboard_outputs[k] for k in all_keys]
# ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ๋ชจ๋“  ๋ฆฌ๋”๋ณด๋“œ ํ‘œ์‹œ
demo.load(
fn=lambda: tuple(get_current_leaderboard(k) for k in all_keys),
outputs=all_outputs
)
# ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ (10์ดˆ๋งˆ๋‹ค - ๋ชจ๋“  ํƒญ ์—…๋ฐ์ดํŠธ)
timer = gr.Timer(value=10)
timer.tick(
fn=lambda: tuple(check_and_update_leaderboard(k) for k in all_keys),
outputs=all_outputs
)
return demo
def main():
"""๋ฉ”์ธ ํ•จ์ˆ˜"""
print("๐Ÿ† ์ด๋ฏธ์ง€ ์œ ์‚ฌ๋„ ๋ฆฌ๋”๋ณด๋“œ ์‹œ์Šคํ…œ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค...")
print("๐Ÿ“ ์ฐธ์กฐ ์ด๋ฏธ์ง€: label2.jpg")
print("๐Ÿ’พ ๋ฐ์ดํ„ฐ ํŒŒ์ผ:")
for cls in CLASSES:
key = _class_key(cls)
print(f" โ€ข {cls}: {key}.json")
print(f" โ€ข ์—ฐ์Šต: practice.json")
# ๊ฐ ๋ฐ˜๋ณ„ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฒดํฌ
for class_name, lb in leaderboards.items():
initial_memory = lb._get_memory_usage()
print(f"๐Ÿ’พ {class_name} ์ดˆ๊ธฐ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰: {initial_memory:.1f}MB")
if not os.path.exists("label2.jpg"):
print("โš ๏ธ ๊ฒฝ๊ณ : ์ฐธ์กฐ ์ด๋ฏธ์ง€(label2.jpg)๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
print(" label2.jpg ํŒŒ์ผ์„ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— ๋ฐฐ์น˜ํ•ด์ฃผ์„ธ์š”.")
# Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
demo = create_interface()
print("๐Ÿš€ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค...")
print("๐Ÿ“Š ์ตœ์ ํ™” ์‚ฌํ•ญ:")
print(" โ€ข ์ฐธ์กฐ ์ด๋ฏธ์ง€ ์บ์‹ฑ ํ™œ์„ฑํ™”")
print(" โ€ข ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ œํ•œ: 512x512")
print(" โ€ข ๋™์‹œ์„ฑ ์ œ์–ด ํ™œ์„ฑํ™”")
print(" โ€ข ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ ์ž๋™ํ™”")
print(" โ€ข ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ: 10์ดˆ ๊ฐ„๊ฒฉ")
print(f" โ€ข {len(CLASSES)+1}๊ฐœ ํƒญ๋ณ„ ๋…๋ฆฝ ๋ฆฌ๋”๋ณด๋“œ ์šด์˜ ({len(CLASSES)}๊ฐœ ๋ฐ˜ + ์—ฐ์Šต)")
# ์™ธ๋ถ€ ๊ณต์œ  ์šฐ์„  ์‹œ๋„
print("\n๐ŸŒ ์™ธ๋ถ€ ์ ‘๊ทผ ์‹œ๋„ ์ค‘...")
try:
demo.launch(
# server_name="0.0.0.0",
# server_port=9900,
# share=False, # ๋กœ์ปฌ์—์„œ share=True๋Š” ๋ถˆํ•„์š”ํ•œ ๊ฒฝ๊ณ ๋ฅผ ์œ ๋ฐœํ•˜๋ฏ€๋กœ False๋กœ ๋ณ€๊ฒฝ
# show_error=False,
# quiet=False,
# ssr_mode=False # SSR ๋ชจ๋“œ ๋น„ํ™œ์„ฑํ™”๋กœ ์‹คํ—˜์  ๊ธฐ๋Šฅ ๊ฒฝ๊ณ  ์ œ๊ฑฐ
)
except Exception as e:
print(f"โŒ ์™ธ๋ถ€ ๊ณต์œ  ์‹คํŒจ: {e}")
print("๐Ÿ”„ ๋กœ์ปฌ ๋„คํŠธ์›Œํฌ ๋ชจ๋“œ๋กœ ์ „ํ™˜...")
demo.launch(
server_name="0.0.0.0",
server_port=9900,
share=False, # ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ๋Š” share=False๋กœ ์œ ์ง€
show_error=True,
quiet=False,
ssr_mode=False # SSR ๋ชจ๋“œ ๋น„ํ™œ์„ฑํ™”
)
if __name__ == "__main__":
main()