File size: 10,291 Bytes
4992ddf
7a27c75
4992ddf
 
b0233d9
4992ddf
f3f180a
f7a4d28
7a27c75
f3f180a
7a27c75
 
 
 
 
 
 
 
 
 
f3f180a
 
7a27c75
 
f3f180a
f7a4d28
7a27c75
 
 
 
 
 
 
 
 
4992ddf
 
7a27c75
4992ddf
f3f180a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4992ddf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3f180a
4992ddf
 
 
f3f180a
4992ddf
 
f3f180a
4992ddf
 
 
f3f180a
4992ddf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3f180a
 
 
 
 
 
 
 
6811e34
f3f180a
 
 
 
 
 
 
 
6811e34
 
f3f180a
 
 
 
 
 
 
 
6811e34
f3f180a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181d698
 
4992ddf
 
 
 
 
 
f3f180a
4992ddf
f3f180a
4992ddf
f3f180a
 
4992ddf
 
f3f180a
 
4992ddf
f3f180a
 
4992ddf
f3f180a
 
4992ddf
 
 
 
f3f180a
4992ddf
f3f180a
4992ddf
f3f180a
4992ddf
 
 
b0233d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ed7c8
4992ddf
 
 
b0233d9
4992ddf
66420dd
4992ddf
f3f180a
4992ddf
6811e34
f3f180a
6811e34
f3f180a
 
 
 
 
 
 
 
 
66420dd
7a27c75
66420dd
f3f180a
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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)