minhwai's picture
Update app.py
f3f180a verified
import streamlit as st
import streamlit.components.v1 as components
import cv2
import numpy as np
from PIL import Image, ExifTags
# κ°€μž₯ λ¨Όμ € set_page_config() 호좜
st.set_page_config(page_title="λ”₯페이크 사전 방지 ν•„ν„°(ν…ŒμŠ€νŠΈ)", layout="wide")
ga_code = """
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-PZPBGNENQG"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-PZPBGNENQG');
</script>
"""
# Streamlit에 GA μ½”λ“œ μ‚½μž…
components.html(ga_code, height=0)
# λ‚˜λ¨Έμ§€ Streamlit μ½”λ“œ
st.title("λ”₯페이크 사전 방지 ν•„ν„°(ν…ŒμŠ€νŠΈ)")
st.markdown("")
st.markdown("<span style='font-size: 18px;'>μ•ˆλ…•ν•˜μ„Έμš”! μ €ν¬λŠ” λ”₯νŽ˜μ΄ν¬λ‘œλΆ€ν„° μ—¬λŸ¬λΆ„μ˜ 사진을 λ³΄ν˜Έν•˜λŠ” μ†”λ£¨μ…˜μ„ κ°œλ°œν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
st.markdown("<span style='font-size: 18px;'>μ €ν¬μ˜ λͺ©ν‘œλŠ” μ˜¨λΌμΈμ— κ²Œμ‹œλœ 개인의 사진이 μ•…μ„± λ”₯페이크 μ˜μƒμ— μ‚¬μš©λ˜μ§€ μ•Šλ„λ‘ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. ν˜„μž¬λŠ” κ°œλ°œμ„ λ§ˆλ¬΄λ¦¬ν•˜κ³  μ„œλΉ„μŠ€ν™” ν•˜κΈ° μ „, μ—¬λŸ¬λΆ„μ˜ μ˜κ²¬μ„ λ“£κΈ° μœ„ν•΄ κ°„λ‹¨ν•œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
st.markdown("<span style='font-size: 18px;'>졜근 SNS에 μ—…λ‘œλ“œλœ 이미지가 λ”₯페이크 포λ₯΄λ…Έλ¬Όμ— μ•…μš©λ˜λŠ” 사둀가 맀일 보고되고 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ 해결책을 κ°•κ΅¬ν•˜κΈ° μœ„ν•΄, μ—¬λŸ¬λΆ„μ˜ μ†Œμ€‘ν•œ 의견이 ν•„μš”ν•©λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
st.markdown("<span style='font-size: 18px;'>μ•„λž˜ 링크λ₯Ό 톡해 저희 μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν•΄ 보신 ν›„, 인터뷰에 μ°Έμ—¬ν•΄ μ£Όμ‹œλ©΄ 큰 도움이 λ˜κ² μŠ΅λ‹ˆλ‹€. μ—¬λŸ¬λΆ„μ˜ ν”Όλ“œλ°±μ€ μ„œλΉ„μŠ€ κ°œμ„ μ— κ·€μ€‘ν•œ μžλ£Œκ°€ 될 κ²ƒμž…λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
st.markdown("")
st.markdown("<span style='font-size: 18px;'>λ™μž‘ 원리 : 1. 이미지λ₯Ό μ—…λ‘œλ“œν•˜λ©΄, 사전 방지 ν•„ν„°κ°€ 적용된 이미지λ₯Ό λ³΄μ—¬λ“œλ¦½λ‹ˆλ‹€. 2. ν•˜λ‹¨μ˜ 흰 λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄ λ”₯페이크 λͺ¨λΈμ„ 톡해 μƒμ„±λœ κ²°κ³Όλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
st.markdown("<span style='font-size: 18px;'>μ—¬λŸ¬λΆ„μ˜ 참여에 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€!</span>", unsafe_allow_html=True)
st.markdown("<span style='font-size: 14px;'> *사전 방지 ν•„ν„°λž€: μ—¬λŸ¬λΆ„μ˜ 사진이 λ”₯페이크 λͺ¨λΈμ— ν•™μŠ΅λ˜μ§€ λͺ»ν•˜λ„둝 λ°©ν•΄ν•˜λŠ” λ…Έμ΄μ¦ˆ(noise)ν˜•νƒœμ˜ ν•„ν„°.</span>", unsafe_allow_html=True)
st.markdown(
"""
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<style>
/* 곡톡 μŠ€νƒ€μΌ */
.stFileUploader label,
.stRadio label,
.stRadio div,
.custom-caption-1,
.custom-caption-2,
.button-container,
.stButton button,
.survey,
.survey-1,
.survey-2,
.a-tag,
a {
transition: all 0.3s ease;
}
.stFileUploader label {
font-size: 20px;
font-weight: 500;
color: #1f77b4;
}
.stRadio label {
font-size: 20px;
font-weight: 500;
color: #1f77b4;
}
.stRadio div {
display: flex;
gap: 20px;
}
.custom-caption-1 {
font-size: 36px;
font-weight: bold;
text-align: center;
margin-top: 10px;
padding: 0 0 200px 0;
}
.custom-caption-2 {
font-size: 36px;
font-weight: bold;
text-align: center;
margin-top: 10px;
padding: 0 0 30px 0;
}
.button-container {
text-align: center;
margin-top: 30px;
}
.stButton button {
width: 50%;
font-size: 25px;
padding: 10px 20px;
background-color: #FFFFFF;
font-weight: bold;
color: black;
opacity: 0.8;
border: 3px solid black;
border-radius: 5px;
cursor: pointer;
margin: 0 auto 50px auto;
display: block;
}
.stButton button:hover {
background-color: #FFFFFF;
border: 3px solid #FF0080;
color: #FF0080;
opacity: 1;
}
.survey {
text-align: center;
margin-top: 10px
}
.survey-1 {
font-size: 25px;
text-align: center;
margin-top: 10px
font-weight: bold;
}
.survey-2 {
text-align: center;
margin-top: 10px
font-weight: bold;
padding 0 auto 50px auto
}
.a-tag {
color: #FF0080;
text-decoration: none;
}
a:hover {
color: #FF0080;
text-decoration: none;
}
/* 슀마트폰 ν™”λ©΄ μŠ€νƒ€μΌ */
@media only screen and (max-width: 600px) {
.stFileUploader label,
.stRadio label,
.stButton button,
.survey-1 {
font-size: 16px;
}
.custom-caption-1,
.custom-caption-2 {
font-size: 24px;
padding: 0 0 20px 0;
}
.stButton button {
width: 100%;
font-size: 18px;
}
}
/* νƒœλΈ”λ¦Ώ ν™”λ©΄ μŠ€νƒ€μΌ */
@media only screen and (min-width: 601px) and (max-width: 1024px) {
.stFileUploader label,
.stRadio label,
.stButton button,
.survey-1 {
font-size: 18px;
}
.custom-caption-1,
.custom-caption-2 {
font-size: 28px;
padding: 0 0 40px 0;
}
.stButton button {
width: 75%;
font-size: 20px;
}
}
/* λ°μŠ€ν¬ν†± ν™”λ©΄ μŠ€νƒ€μΌ */
@media only screen and (min-width: 1025px) {
.stFileUploader label,
.stRadio label,
.stButton button,
.survey-1 {
font-size: 20px;
}
.custom-caption-1,
.custom-caption-2 {
font-size: 36px;
padding: 0 0 200px 0;
}
.stButton button {
width: 50%;
font-size: 25px;
}
}
</style>
""",
unsafe_allow_html=True,
)
def change_hair_to_blonde(image):
# Convert to OpenCV format
image = np.array(image)
# Convert the image to HSV color space
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
# Define the range for hair color (dark colors)
lower_hair = np.array([0, 0, 0])
upper_hair = np.array([180, 255, 30])
# Create a mask for hair
mask = cv2.inRange(hsv, lower_hair, upper_hair)
# Change hair color to blonde (light yellow)
hsv[mask > 0] = (30, 255, 200)
# Convert back to RGB color space
image_blonde = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
return image_blonde
def add_noise(image):
# Convert to OpenCV format
image_np = np.array(image)
# Generate random noise
noise = np.random.normal(0, 25, image_np.shape).astype(np.uint8)
# Add noise to the image
noisy_image = cv2.add(image_np, noise)
return noisy_image
def correct_image_orientation(image):
try:
for orientation in ExifTags.TAGS.keys():
if ExifTags.TAGS[orientation] == 'Orientation':
break
exif = image._getexif()
if exif is not None:
orientation = exif.get(orientation, 1)
if orientation == 3:
image = image.rotate(180, expand=True)
elif orientation == 6:
image = image.rotate(270, expand=True)
elif orientation == 8:
image = image.rotate(90, expand=True)
except (AttributeError, KeyError, IndexError):
pass
return image
uploaded_file = st.file_uploader("이미지λ₯Ό μ—…λ‘œλ“œν•˜μ„Έμš”...", type=["jpg", "png", "jpeg"])
if uploaded_file is not None:
image = Image.open(uploaded_file)
image = correct_image_orientation(image)
st.write("이미지 처리 쀑...")
# Save the original image as a numpy array
image_np = np.array(image)
col1, col2 = st.columns(2)
with col1:
st.image(image, use_column_width=True)
st.markdown('<div class="custom-caption-1">μ—…λ‘œλ“œν•œ 이미지</div>', unsafe_allow_html=True)
with col2:
st.image(image, use_column_width=True)
st.markdown('<div class="custom-caption-1">ν•„ν„°λ₯Ό μž…νžŒ 이미지</div>', unsafe_allow_html=True)
button_clicked = st.button("μƒλ‹¨μ˜ 두 사진을 λ”₯페이크 λͺ¨λΈμ— ν•™μŠ΅μ‹œν‚€κΈ°")
st.markdown('<p class="survey">μœ„ μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©ν•΄ λ³΄μ…¨κ±°λ‚˜, 저희 기술적 원리에 관심이 μžˆμœΌμ‹  λΆ„λ“€κ»˜μ„  μ•„λž˜μ˜ κ°„λ‹¨ν•œ 인터뷰에 μ°Έμ—¬ν•΄ μ£Όμ‹œλ©΄ μ§„μ‹¬μœΌλ‘œ κ°μ‚¬λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€.</p>', unsafe_allow_html=True)
st.markdown('<p class="survey-1"><a href="https://docs.google.com/forms/d/e/1FAIpQLSdzRtuvQyp3CQDhlxEag40v2yDM7u9NYpJ2gv5kgwuNbo1gUA/viewform?usp=sf_link" target="_blank" class="a-tag">μ—¬κΈ°λ₯Ό ν΄λ¦­ν•˜μ—¬ 인터뷰에 응해 μ£Όμ‹ λ‹€λ©΄ 큰 도움이 될 것 κ°™μŠ΅λ‹ˆλ‹€!!</a></p>', unsafe_allow_html=True)
st.markdown('<p class="survey-2">μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν•΄ μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€! 쒋은 ν•˜λ£¨ λ³΄λ‚΄μ„Έμš”!</p>', unsafe_allow_html=True)
if button_clicked:
with col1:
processed_image = change_hair_to_blonde(image)
st.image(processed_image, use_column_width=True)
st.markdown('<div class="custom-caption-2">원본 이미지λ₯Ό λ”₯페이크 λͺ¨λΈμ— λ„£μ—ˆμ„ 경우</div>', unsafe_allow_html=True)
st.markdown("<span>이해λ₯Ό 돕기 μœ„ν•΄ 사진에 λ…Έλž€μƒ‰μ„ μž…νžˆλŠ” λ”₯페이크 μ•Œκ³ λ¦¬μ¦˜ 적용. 원본 μ΄λ―Έμ§€λŠ” λ”₯페이크 μ•Œκ³ λ¦¬μ¦˜μ˜ 영ν–₯을 λ°›μŒ.</span>", unsafe_allow_html=True)
with col2:
deepfake_image = add_noise(image)
st.image(deepfake_image, use_column_width=True)
st.markdown('<div class="custom-caption-2">사전 방지 ν•„ν„° 이미지λ₯Ό λ”₯페이크 λͺ¨λΈμ— λ„£μ—ˆμ„ 경우</div>', unsafe_allow_html=True)
st.markdown("<span>사전 방지 ν•„ν„°λ₯Ό μž…νžŒ μ΄λ―Έμ§€λŠ” λ”₯페이크 μ•Œκ³ λ¦¬μ¦˜μ˜ 영ν–₯을 받지 μ•Šκ³  λ…Έμ΄μ¦ˆ μ²˜λ¦¬κ°€ λ˜μ–΄ μ•Œμ•„λ³΄κΈ° νž˜λ“  사진을 좜λ ₯. 즉, λ”₯페이크 사진 합성을 방해함.</span>", unsafe_allow_html=True)