Spaces:
Build error
Build error
Kota Takahashi commited on
Commit ·
4b34522
1
Parent(s): f5b649b
ファーストコミット
Browse files- .gitignore +40 -0
- README.md +6 -4
- app.py +93 -0
- emotion_analyzer.py +49 -0
- imgs/for_unzipped_file.txt +0 -0
- imgs/sample_photo/1569373_s.jpg +0 -0
- imgs/sample_photo/28978173_s.jpg +0 -0
- imgs/sample_photo/4024998_s.jpg +0 -0
- imgs/sample_photo/4191057_s.jpg +0 -0
- requirements.txt +82 -0
.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# Caches and logs
|
| 7 |
+
*.log
|
| 8 |
+
logs/
|
| 9 |
+
*.cache/
|
| 10 |
+
|
| 11 |
+
# Environment variables
|
| 12 |
+
.env
|
| 13 |
+
|
| 14 |
+
# Static files (usually collected by Django's collectstatic)
|
| 15 |
+
/static/
|
| 16 |
+
|
| 17 |
+
# Media files
|
| 18 |
+
/media/
|
| 19 |
+
|
| 20 |
+
# Database
|
| 21 |
+
*.sqlite3
|
| 22 |
+
|
| 23 |
+
# IDE specific files
|
| 24 |
+
.idea/
|
| 25 |
+
.vscode/
|
| 26 |
+
|
| 27 |
+
# Dependency directories
|
| 28 |
+
venv/
|
| 29 |
+
env/
|
| 30 |
+
|
| 31 |
+
# Compiled Python files
|
| 32 |
+
*.pyc
|
| 33 |
+
*.pyo
|
| 34 |
+
*.pyd
|
| 35 |
+
|
| 36 |
+
# macOS
|
| 37 |
+
.DS_Store
|
| 38 |
+
|
| 39 |
+
# Ignore __MACOSX in img directory
|
| 40 |
+
__MACOSX/
|
README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
| 1 |
---
|
| 2 |
-
title: Perfect Shot
|
| 3 |
-
|
| 4 |
-
|
|
|
|
| 5 |
colorTo: gray
|
| 6 |
sdk: streamlit
|
| 7 |
-
sdk_version: 1.
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
| 12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Perfect Shot
|
| 3 |
+
python_version: 3.8.2
|
| 4 |
+
emoji: 🐢
|
| 5 |
+
colorFrom: yellow
|
| 6 |
colorTo: gray
|
| 7 |
sdk: streamlit
|
| 8 |
+
sdk_version: 1.35.0
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 14 |
+
[README.md](README.md)
|
app.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
import glob
|
| 4 |
+
|
| 5 |
+
import streamlit as st
|
| 6 |
+
from PIL import Image
|
| 7 |
+
import zipfile
|
| 8 |
+
from emotion_analyzer import EmotionAnalyzer
|
| 9 |
+
|
| 10 |
+
st.title('ベストショット判定アプリ')
|
| 11 |
+
|
| 12 |
+
## スライダーの定義
|
| 13 |
+
st.sidebar.markdown('### 条件設定')
|
| 14 |
+
sld_anger_deg = st.sidebar.slider('anger', 1, 5, 1)
|
| 15 |
+
sld_disgust_deg = st.sidebar.slider('disgust', 1, 5, 1)
|
| 16 |
+
sld_fear_deg = st.sidebar.slider('fear', 1, 5, 1)
|
| 17 |
+
sld_happiness_deg = st.sidebar.slider('happiness', 1, 5, 5)
|
| 18 |
+
sld_sadness_deg = st.sidebar.slider('sadness', 1, 5, 1)
|
| 19 |
+
sld_surprise_deg = st.sidebar.slider('surprise', 1, 5, 1)
|
| 20 |
+
sld_neutral_deg = st.sidebar.slider('neutral', 1, 5, 1)
|
| 21 |
+
|
| 22 |
+
# 各変数の比率を計算
|
| 23 |
+
sld_total_deg = (sld_anger_deg + sld_disgust_deg + sld_fear_deg +
|
| 24 |
+
sld_happiness_deg + sld_sadness_deg +
|
| 25 |
+
sld_surprise_deg + sld_neutral_deg)
|
| 26 |
+
|
| 27 |
+
emotion_ratios = {
|
| 28 |
+
'anger': sld_anger_deg / sld_total_deg,
|
| 29 |
+
'disgust': sld_disgust_deg / sld_total_deg,
|
| 30 |
+
'fear': sld_fear_deg / sld_total_deg,
|
| 31 |
+
'happiness': sld_happiness_deg / sld_total_deg,
|
| 32 |
+
'sadness': sld_sadness_deg / sld_total_deg,
|
| 33 |
+
'surprise': sld_surprise_deg / sld_total_deg,
|
| 34 |
+
'neutral': sld_neutral_deg / sld_total_deg
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.bmp']
|
| 38 |
+
extract_dir = 'imgs'
|
| 39 |
+
|
| 40 |
+
# アップローダー
|
| 41 |
+
uploaded_file = st.file_uploader('ZIPファイルをアップロードしてください', type='zip')
|
| 42 |
+
|
| 43 |
+
# イメージファイルのパスを格納するリスト
|
| 44 |
+
img_file_path_list = []
|
| 45 |
+
|
| 46 |
+
if uploaded_file:
|
| 47 |
+
# 一時ディレクトリを作成してZIPファイルを解凍
|
| 48 |
+
with zipfile.ZipFile(uploaded_file, 'r') as zip_ref:
|
| 49 |
+
zip_ref.extractall(extract_dir)
|
| 50 |
+
|
| 51 |
+
# 解凍したファイル・ディレクトリの一覧を取得
|
| 52 |
+
extracted_items = os.listdir(extract_dir)
|
| 53 |
+
# zipファイルのベースファイルを取得
|
| 54 |
+
uploaded_file_base_name = uploaded_file.name.split('.zip')[0]
|
| 55 |
+
# 解凍したディレクトリのパスを取得
|
| 56 |
+
extract_dir_path = os.path.join(extract_dir, uploaded_file_base_name)
|
| 57 |
+
# 各拡張子に対してglobを使用してファイルを取得
|
| 58 |
+
for ext in image_extensions:
|
| 59 |
+
img_file_path_list.extend(glob.glob(os.path.join(extract_dir_path, ext)))
|
| 60 |
+
|
| 61 |
+
if img_file_path_list:
|
| 62 |
+
st.write(f'{len(img_file_path_list)}枚のイメージファイルが見つかりました')
|
| 63 |
+
else:
|
| 64 |
+
st.write('イメージファイルが見つかりませんでした')
|
| 65 |
+
st.write('再度アップロードしなおしてください')
|
| 66 |
+
sys.exit()
|
| 67 |
+
|
| 68 |
+
if st.button('表情分析開始'):
|
| 69 |
+
# 画像を指定して表情認識を実行
|
| 70 |
+
with st.spinner('表情分析中...'):
|
| 71 |
+
ema = EmotionAnalyzer(img_file_path_list)
|
| 72 |
+
df_emotions = ema.create_emotion_dataflame()
|
| 73 |
+
best_image_record = ema.detect_best_image(df_emotions, emotion_ratios)
|
| 74 |
+
|
| 75 |
+
# ベストショットのファイル名
|
| 76 |
+
best_image_record_filename = best_image_record.name
|
| 77 |
+
|
| 78 |
+
# 保存した画像を表示
|
| 79 |
+
result_detection = df_emotions.loc[best_image_record_filename]['detections']
|
| 80 |
+
fig_list = result_detection.plot_detections(faces='landmarks', faceboxes=True, muscles=False, poses=False,
|
| 81 |
+
gazes=False,
|
| 82 |
+
add_titles=True,
|
| 83 |
+
au_barplot=False, emotion_barplot=True,
|
| 84 |
+
plot_original_image=True)
|
| 85 |
+
st.markdown('ベストショットはこちら!')
|
| 86 |
+
|
| 87 |
+
# 保存した画像を表示
|
| 88 |
+
img = Image.open(best_image_record_filename)
|
| 89 |
+
st.image(img, width=500)
|
| 90 |
+
|
| 91 |
+
# 分析結果を表示
|
| 92 |
+
st.markdown('分析結果')
|
| 93 |
+
st.pyplot(fig_list[0])
|
emotion_analyzer.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
from feat import Detector
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
# 検出器の定義
|
| 6 |
+
detector = Detector()
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class EmotionAnalyzer:
|
| 10 |
+
def __init__(self, img_file_path_list):
|
| 11 |
+
self.img_file_path_list = img_file_path_list
|
| 12 |
+
|
| 13 |
+
def detect_best_image(self, df_emotions, emotion_ratios):
|
| 14 |
+
"""
|
| 15 |
+
各表情の値と設定値との二乗和を取り、最も値が小さい(設定値に近い)写真をベストショットをする
|
| 16 |
+
"""
|
| 17 |
+
df_emotions['distance'] = np.sqrt(
|
| 18 |
+
sum((df_emotions[emotion] - ratio) ** 2 for emotion, ratio in emotion_ratios.items())
|
| 19 |
+
)
|
| 20 |
+
best_image_record = df_emotions.loc[df_emotions['distance'].idxmin()]
|
| 21 |
+
return best_image_record
|
| 22 |
+
|
| 23 |
+
@staticmethod
|
| 24 |
+
def analyze_emotion(img_file_path):
|
| 25 |
+
"""
|
| 26 |
+
イメージファイルから感情を分析
|
| 27 |
+
"""
|
| 28 |
+
# 検出器の定義
|
| 29 |
+
detector = Detector()
|
| 30 |
+
result = detector.detect_image(img_file_path)
|
| 31 |
+
result_emotions_mean = result.emotions.mean()
|
| 32 |
+
return result_emotions_mean, result
|
| 33 |
+
|
| 34 |
+
def create_emotion_dataflame(self):
|
| 35 |
+
"""
|
| 36 |
+
イメージファイルの分析結果をデータフレーム化
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
emotions_deg_list = []
|
| 40 |
+
result_detections_list = []
|
| 41 |
+
for img_file_path in self.img_file_path_list:
|
| 42 |
+
result_emotions_mean, result_detections = self.analyze_emotion(img_file_path)
|
| 43 |
+
emotions_deg_list.append(result_emotions_mean)
|
| 44 |
+
result_detections_list.append(result_detections)
|
| 45 |
+
|
| 46 |
+
df_emotion_result = pd.DataFrame(emotions_deg_list, index=self.img_file_path_list)
|
| 47 |
+
df_emotion_result['detections'] = result_detections_list
|
| 48 |
+
|
| 49 |
+
return df_emotion_result
|
imgs/for_unzipped_file.txt
ADDED
|
File without changes
|
imgs/sample_photo/1569373_s.jpg
ADDED
|
imgs/sample_photo/28978173_s.jpg
ADDED
|
imgs/sample_photo/4024998_s.jpg
ADDED
|
imgs/sample_photo/4191057_s.jpg
ADDED
|
requirements.txt
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
altair==5.3.0
|
| 2 |
+
attrs==23.2.0
|
| 3 |
+
av==12.0.0
|
| 4 |
+
blinker==1.8.2
|
| 5 |
+
cachetools==5.3.3
|
| 6 |
+
celluloid==0.2.0
|
| 7 |
+
certifi==2024.2.2
|
| 8 |
+
charset-normalizer==3.3.2
|
| 9 |
+
click==8.1.7
|
| 10 |
+
contourpy==1.1.1
|
| 11 |
+
cycler==0.12.1
|
| 12 |
+
easing-functions==1.0.4
|
| 13 |
+
filelock==3.14.0
|
| 14 |
+
fonttools==4.52.1
|
| 15 |
+
fsspec==2024.5.0
|
| 16 |
+
gitdb==4.0.11
|
| 17 |
+
GitPython==3.1.43
|
| 18 |
+
h5py==3.11.0
|
| 19 |
+
idna==3.7
|
| 20 |
+
imageio==2.34.1
|
| 21 |
+
importlib_resources==6.4.0
|
| 22 |
+
Jinja2==3.1.4
|
| 23 |
+
joblib==1.4.2
|
| 24 |
+
jsonschema==4.22.0
|
| 25 |
+
jsonschema-specifications==2023.12.1
|
| 26 |
+
kiwisolver==1.4.5
|
| 27 |
+
kornia==0.7.2
|
| 28 |
+
kornia_rs==0.1.3
|
| 29 |
+
lazy_loader==0.4
|
| 30 |
+
lxml==5.2.2
|
| 31 |
+
markdown-it-py==3.0.0
|
| 32 |
+
MarkupSafe==2.1.5
|
| 33 |
+
matplotlib==3.7.5
|
| 34 |
+
mdurl==0.1.2
|
| 35 |
+
mpmath==1.3.0
|
| 36 |
+
networkx==3.1
|
| 37 |
+
nibabel==5.2.1
|
| 38 |
+
nilearn==0.10.4
|
| 39 |
+
nltools==0.5.1
|
| 40 |
+
numexpr==2.8.4
|
| 41 |
+
numpy==1.23.5
|
| 42 |
+
packaging==24.0
|
| 43 |
+
pandas==2.0.3
|
| 44 |
+
pillow==10.3.0
|
| 45 |
+
pipdeptree==2.23.0
|
| 46 |
+
pkgutil_resolve_name==1.3.10
|
| 47 |
+
protobuf==4.25.3
|
| 48 |
+
py-feat==0.6.2
|
| 49 |
+
pyarrow==16.1.0
|
| 50 |
+
pydeck==0.9.1
|
| 51 |
+
Pygments==2.18.0
|
| 52 |
+
pynv==0.3
|
| 53 |
+
pyparsing==3.1.2
|
| 54 |
+
python-dateutil==2.9.0.post0
|
| 55 |
+
pytz==2024.1
|
| 56 |
+
PyWavelets==1.4.1
|
| 57 |
+
referencing==0.35.1
|
| 58 |
+
requests==2.32.2
|
| 59 |
+
rich==13.7.1
|
| 60 |
+
rpds-py==0.18.1
|
| 61 |
+
scikit-image==0.21.0
|
| 62 |
+
scikit-learn==1.3.2
|
| 63 |
+
scipy==1.10.1
|
| 64 |
+
seaborn==0.13.2
|
| 65 |
+
six==1.16.0
|
| 66 |
+
smmap==5.0.1
|
| 67 |
+
streamlit==1.35.0
|
| 68 |
+
sympy==1.12
|
| 69 |
+
tenacity==8.3.0
|
| 70 |
+
threadpoolctl==3.5.0
|
| 71 |
+
tifffile==2023.7.10
|
| 72 |
+
toml==0.10.2
|
| 73 |
+
toolz==0.12.1
|
| 74 |
+
torch==2.2.2
|
| 75 |
+
torchvision==0.17.2
|
| 76 |
+
tornado==6.4
|
| 77 |
+
tqdm==4.66.4
|
| 78 |
+
typing_extensions==4.12.0
|
| 79 |
+
tzdata==2024.1
|
| 80 |
+
urllib3==2.2.1
|
| 81 |
+
xgboost==2.0.3
|
| 82 |
+
zipp==3.18.2
|