scroll-count / src /streamlit_app.py
SSEONG's picture
Update src/streamlit_app.py
6fae35d verified
# app.py
import streamlit as st
import cv2
import numpy as np
from PIL import Image
import easyocr
from streamlit_paste_button import paste_image_button as pbutton
color_ranges = {
'fire': 'B50B0E',
'water': '015AB6',
'wind': '1F6A0B',
'earth': '623F23',
'light': 'DA8D09',
'dark': '502181'
}
def hex_to_rgb(hex_code):
return tuple(int(hex_code[i:i+2], 16) for i in (0, 2, 4))
# ์บ์‹ฑ์œผ๋กœ Reader๋ฅผ ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œ
@st.cache_resource
def load_reader():
return easyocr.Reader(['en'], gpu=False, verbose=False)
def extract_number(region, reader):
try:
gray = cv2.cvtColor(region, cv2.COLOR_BGR2GRAY)
resized = cv2.resize(gray, None, fx=6, fy=6, interpolation=cv2.INTER_CUBIC)
results = reader.readtext(resized, allowlist='0123456789', paragraph=False)
if results:
text = results[0][1]
return int(text.strip())
except Exception as e:
st.error(f"์ˆซ์ž ์ถ”์ถœ ์˜ค๋ฅ˜: {e}")
return 0
def find_items(img_array, color_range, reader):
img_rgb = cv2.cvtColor(img_array, cv2.COLOR_BGR2RGB)
results = {}
for type, hex_color in color_range.items():
target_rgb = hex_to_rgb(hex_color)
lower_c = np.array([max(0, c - 10) for c in target_rgb])
upper_c = np.array([min(255, c + 10) for c in target_rgb])
mask = cv2.inRange(img_rgb, lower_c, upper_c)
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
largest = max(contours, key=cv2.contourArea)
if cv2.contourArea(largest) > 100:
x, y, w, h = cv2.boundingRect(largest)
number_region = img_rgb[y+h:y+int(h*2), x+w:x+int(w*2.3)]
count = extract_number(number_region, reader)
results[type] = count
else:
results[type] = 0
else:
results[type] = 0
return results
def process_image(img):
st.image(cv2.cvtColor(img, cv2.COLOR_BGR2RGB), caption='์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€', use_container_width=True)
with st.spinner('์•„์ดํ…œ ๊ฐœ์ˆ˜๋ฅผ ์„ธ๋Š” ์ค‘... (์ฒ˜์Œ ์‹คํ–‰์‹œ ๋ชจ๋ธ ๋กœ๋”ฉ์œผ๋กœ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)'):
try:
reader = load_reader()
results = find_items(img, color_ranges, reader)
# ๊ฒฐ๊ณผ ํ‘œ์‹œ
st.success('โœ… ๋ถ„์„ ์™„๋ฃŒ!')
col1, col2, col3 = st.columns(3)
emoji_map = {
'fire': '๐Ÿ”ฅ',
'water': '๐Ÿ’ง',
'wind': '๐Ÿ’จ',
'earth': '๐ŸŒ',
'light': 'โœจ',
'dark': '๐ŸŒ™'
}
korean_map = {
'fire': '๋ถˆ',
'water': '๋ฌผ',
'wind': '๋ฐ”๋žŒ',
'earth': '๋Œ€์ง€',
'light': '๋น›',
'dark': '์–ด๋‘ '
}
# ๋‹จ์ผ ์—ด๋กœ ํ‘œ์‹œ
for type, count in results.items():
st.metric(
label=f"{emoji_map.get(type, '')} {korean_map.get(type, type)}",
value=f"{count}๊ฐœ"
)
# cols = [col1, col2, col3]
# counts_str = ''
# for idx, (type, count) in enumerate(results.items()):
# col = cols[idx % 3]
# with col:
# st.metric(
# label=f"{emoji_map.get(type, '')} {korean_map.get(type, type)}",
# value=f"{count}๊ฐœ"
# )
counts_str += f"{count}/"
st.markdown(f'{counts_str}')
except Exception as e:
st.error(f"๋ถ„์„ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
st.info("EasyOCR ๋ชจ๋ธ ๋กœ๋”ฉ์— ์‹คํŒจํ–ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Streamlit Cloud์˜ ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ ๋•Œ๋ฌธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
# Streamlit UI
st.set_page_config(page_title="๊ฒŒ์ž„ ์•„์ดํ…œ ์นด์šดํ„ฐ", page_icon="๐ŸŽฎ")
st.title('๐ŸŽฎ ๊ฒŒ์ž„ ์•„์ดํ…œ ์นด์šดํ„ฐ')
st.write('ํš๋“ํ•œ ์†์„ฑ ์•„์ดํ…œ ๊ฐœ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์„ธ์–ด๋“œ๋ฆฝ๋‹ˆ๋‹ค!')
tab1, tab2 = st.tabs(["๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ", "๐Ÿ“‹ ๋ถ™์—ฌ๋„ฃ๊ธฐ"])
with tab1:
st.write('์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜์„ธ์š”')
uploaded_file = st.file_uploader("์Šคํฌ๋ฆฐ์ƒท์„ ์—…๋กœ๋“œํ•˜์„ธ์š”", type=['png', 'jpg', 'jpeg'])
if uploaded_file is not None:
file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
process_image(img)
with tab2:
st.write('์ด๋ฏธ์ง€๋ฅผ ๋ถ™์—ฌ๋„ฃ์œผ์„ธ์š”')
paste_result = pbutton(
label="๐Ÿ“‹ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•˜๊ณ  Ctrl+V",
background_color="#FF4B4B",
hover_background_color="#FF6B6B",
)
if paste_result.image_data is not None:
pil_image = paste_result.image_data
# st.markdown(f'{type(pil_image)}')
img = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
process_image(img)
st.markdown('---')
st.caption('Made by โค๏ธsseong')