minhwai commited on
Commit
f586132
Β·
verified Β·
1 Parent(s): 1c64f16

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +236 -35
app.py CHANGED
@@ -1,7 +1,185 @@
1
  import streamlit as st
 
2
  import cv2
3
  import numpy as np
4
- from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  def change_hair_to_blonde(image):
7
  # Convert to OpenCV format
@@ -23,45 +201,68 @@ def change_hair_to_blonde(image):
23
  image_blonde = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
24
  return image_blonde
25
 
26
- def add_dark_noise(image):
27
  # Convert to OpenCV format
28
  image_np = np.array(image)
29
- # Generate random dark noise
30
- noise = np.random.randint(0, 100, image_np.shape, dtype=np.uint8)
31
- # Subtract noise from the image
32
- noisy_image = cv2.subtract(image_np, noise)
33
  return noisy_image
34
 
35
- def apply_cartoon_filter(image):
36
- # Convert to OpenCV format
37
- img_rgb = np.array(image).astype(np.uint8)
38
- img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
39
- img_gray = cv2.medianBlur(img_gray, 5)
40
- edges = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
41
- color = cv2.bilateralFilter(img_rgb, 9, 300, 300)
42
- cartoon = cv2.bitwise_and(color, color, mask=edges)
43
- return cartoon
44
-
45
- st.title("이미지 처리 MVP")
 
 
 
 
 
 
46
 
47
- uploaded_file = st.file_uploader("이미지λ₯Ό μ„ νƒν•˜μ„Έμš”...", type=["jpg", "png", "jpeg"])
48
 
49
  if uploaded_file is not None:
50
  image = Image.open(uploaded_file)
51
- st.image(image, caption='μ—…λ‘œλ“œλœ 이미지', use_column_width=True)
52
- st.write("")
53
- st.write("처리 쀑...")
54
-
55
- action = st.radio("μž‘μ—…μ„ μ„ νƒν•˜μ„Έμš”:", ('A', 'B', 'ν•„ν„° 적용'))
56
-
57
- if action == 'A':
58
- # Just display the original image
59
- st.image(image, caption='원본 이미지', use_column_width=True)
60
- elif action == 'B':
61
- # Add dark noise to the original image
62
- dark_noisy_image = add_dark_noise(image)
63
- st.image(dark_noisy_image, caption='μ–΄λ‘μš΄ λ…Έμ΄μ¦ˆκ°€ μΆ”κ°€λœ 이미지', use_column_width=True)
64
- elif action == 'ν•„ν„° 적용':
65
- # Apply a cartoon filter for a 'deepfake' effect
66
- cartoon_image = apply_cartoon_filter(image)
67
- st.image(cartoon_image, caption='ν•„ν„°κ°€ 적용된 이미지', use_column_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import streamlit.components.v1 as components
3
  import cv2
4
  import numpy as np
5
+ from PIL import Image, ExifTags
6
+
7
+ # κ°€μž₯ λ¨Όμ € set_page_config() 호좜
8
+ st.set_page_config(page_title="λ”₯페이크 사전 λ°©μ§€ ν•„ν„°(ν…ŒμŠ€νŠΈ)", layout="wide")
9
+
10
+ ga_code = """
11
+ <!-- Global site tag (gtag.js) - Google Analytics -->
12
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-PZPBGNENQG"></script>
13
+ <script>
14
+ window.dataLayer = window.dataLayer || [];
15
+ function gtag(){dataLayer.push(arguments);}
16
+ gtag('js', new Date());
17
+ gtag('config', 'G-PZPBGNENQG');
18
+ </script>
19
+ """
20
+
21
+ # Streamlit에 GA μ½”λ“œ μ‚½μž…
22
+ components.html(ga_code, height=0)
23
+
24
+ # λ°˜μ‘ν˜• λ””μžμΈμ„ μœ„ν•œ CSS
25
+ css = """
26
+ <style>
27
+ /* 곡톡 μŠ€νƒ€μΌ */
28
+ .stFileUploader label,
29
+ .stRadio label,
30
+ .stButton button,
31
+ .survey-1 {
32
+ transition: all 0.3s ease;
33
+ }
34
+ .stFileUploader label {
35
+ font-size: 20px;
36
+ font-weight: 500;
37
+ color: #1f77b4;
38
+ }
39
+ .stRadio label {
40
+ font-size: 20px;
41
+ font-weight: 500;
42
+ color: #1f77b4;
43
+ }
44
+ .stRadio div {
45
+ display: flex;
46
+ gap: 20px;
47
+ }
48
+ .custom-caption-1 {
49
+ font-size: 36px;
50
+ font-weight: bold;
51
+ text-align: center;
52
+ margin-top: 10px;
53
+ padding: 0 0 20px 0;
54
+ }
55
+ .custom-caption-2 {
56
+ font-size: 36px;
57
+ font-weight: bold;
58
+ text-align: center;
59
+ margin-top: 10px;
60
+ padding: 0 0 30px 0;
61
+ }
62
+ .button-container {
63
+ text-align: center;
64
+ margin-top: 30px;
65
+ }
66
+ .stButton button {
67
+ width: 50%;
68
+ font-size: 25px;
69
+ padding: 10px 20px;
70
+ background-color: #FFFFFF;
71
+ font-weight: bold;
72
+ color: black;
73
+ opacity: 0.8;
74
+ border: 3px solid black;
75
+ border-radius: 5px;
76
+ cursor: pointer;
77
+ margin: 0 auto 50px auto;
78
+ display: block;
79
+ }
80
+ .stButton button:hover {
81
+ background-color: #FFFFFF;
82
+ border: 3px solid #FF0080;
83
+ color: #FF0080;
84
+ opacity: 1;
85
+ }
86
+ .survey {
87
+ text-align: center;
88
+ margin-top: 10px
89
+ }
90
+ .survey-1 {
91
+ font-size: 25px;
92
+ text-align: center;
93
+ margin-top: 10px;
94
+ font-weight: bold;
95
+ }
96
+ .survey-2 {
97
+ text-align: center;
98
+ margin-top: 10px;
99
+ font-weight: bold;
100
+ padding: 0 auto 50px auto;
101
+ }
102
+ .a-tag {
103
+ color: #FF0080;
104
+ text-decoration: none;
105
+ }
106
+ a:hover {
107
+ color: #FF0080;
108
+ text-decoration: none;
109
+ }
110
+
111
+ /* 슀마트폰 ν™”λ©΄ μŠ€νƒ€μΌ */
112
+ @media only screen and (max-width: 600px) {
113
+ .stFileUploader label,
114
+ .stRadio label,
115
+ .stButton button,
116
+ .survey-1 {
117
+ font-size: 16px;
118
+ }
119
+ .custom-caption-1,
120
+ .custom-caption-2 {
121
+ font-size: 24px;
122
+ padding: 0 0 20px 0;
123
+ }
124
+ .stButton button {
125
+ width: 100%;
126
+ font-size: 18px;
127
+ }
128
+ }
129
+
130
+ /* νƒœλΈ”λ¦Ώ ν™”λ©΄ μŠ€νƒ€μΌ */
131
+ @media only screen and (min-width: 601px) and (max-width: 1024px) {
132
+ .stFileUploader label,
133
+ .stRadio label,
134
+ .stButton button,
135
+ .survey-1 {
136
+ font-size: 18px;
137
+ }
138
+ .custom-caption-1,
139
+ .custom-caption-2 {
140
+ font-size: 28px;
141
+ padding: 0 0 40px 0;
142
+ }
143
+ .stButton button {
144
+ width: 75%;
145
+ font-size: 20px;
146
+ }
147
+ }
148
+
149
+ /* λ°μŠ€ν¬ν†± ν™”λ©΄ μŠ€νƒ€μΌ */
150
+ @media only screen and (min-width: 1025px) {
151
+ .stFileUploader label,
152
+ .stRadio label,
153
+ .stButton button,
154
+ .survey-1 {
155
+ font-size: 20px;
156
+ }
157
+ .custom-caption-1,
158
+ .custom-caption-2 {
159
+ font-size: 36px;
160
+ padding: 0 0 200px 0;
161
+ }
162
+ .stButton button {
163
+ width: 50%;
164
+ font-size: 25px;
165
+ }
166
+ }
167
+ </style>
168
+ """
169
+
170
+ # CSSλ₯Ό HTML둜 μ‚½μž…
171
+ st.markdown(css, unsafe_allow_html=True)
172
+
173
+ st.title("λ”₯페이크 사전 λ°©μ§€ ν•„ν„°(ν…ŒμŠ€νŠΈ)")
174
+ st.markdown("")
175
+ st.markdown("<span style='font-size: 18px;'>μ•ˆλ…•ν•˜μ„Έμš”! μ €ν¬λŠ” λ”₯νŽ˜μ΄ν¬λ‘œλΆ€ν„° μ—¬λŸ¬λΆ„μ˜ 사진을 λ³΄ν˜Έν•˜λŠ” μ†”λ£¨μ…˜μ„ κ°œλ°œν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
176
+ st.markdown("<span style='font-size: 18px;'>μ €ν¬μ˜ λͺ©ν‘œλŠ” μ˜¨λΌμΈμ— κ²Œμ‹œλœ 개인의 사진이 μ•…μ„± λ”₯페이크 μ˜μƒμ— μ‚¬μš©λ˜μ§€ μ•Šλ„λ‘ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. ν˜„μž¬λŠ” κ°œλ°œμ„ λ§ˆλ¬΄λ¦¬ν•˜κ³  μ„œλΉ„μŠ€ν™” ν•˜κΈ° μ „, μ—¬λŸ¬λΆ„μ˜ μ˜κ²¬μ„ λ“£κΈ° μœ„ν•΄ κ°„λ‹¨ν•œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
177
+ st.markdown("<span style='font-size: 18px;'>졜근 SNS에 μ—…λ‘œλ“œλœ 이미지가 λ”₯페이크 포λ₯΄λ…Έλ¬Όμ— μ•…μš©λ˜λŠ” 사둀가 맀일 보고되고 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ 해결책을 κ°•κ΅¬ν•˜κΈ° μœ„ν•΄, μ—¬λŸ¬λΆ„μ˜ μ†Œμ€‘ν•œ 의견이 ν•„μš”ν•©λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
178
+ st.markdown("<span style='font-size: 18px;'>μ•„λž˜ 링크λ₯Ό 톡해 저희 μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν•΄ 보신 ν›„, 인터뷰에 μ°Έμ—¬ν•΄ μ£Όμ‹œλ©΄ 큰 도움이 λ˜κ² μŠ΅λ‹ˆλ‹€. μ—¬λŸ¬λΆ„μ˜ ν”Όλ“œλ°±μ€ μ„œλΉ„μŠ€ κ°œμ„ μ— κ·€μ€‘ν•œ μžλ£Œκ°€ 될 κ²ƒμž…λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
179
+ st.markdown("")
180
+ st.markdown("<span style='font-size: 18px;'>λ™μž‘ 원리 : 1. 이미지λ₯Ό μ—…λ‘œλ“œν•˜λ©΄, 사전 λ°©μ§€ ν•„ν„°κ°€ 적용된 이미지λ₯Ό λ³΄μ—¬λ“œλ¦½λ‹ˆλ‹€. 2. ν•˜λ‹¨μ˜ 흰 λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄ λ”₯페이크 λͺ¨λΈμ„ 톡해 μƒμ„±λœ κ²°κ³Όλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.</span>", unsafe_allow_html=True)
181
+ st.markdown("<span style='font-size: 18px;'>μ—¬λŸ¬λΆ„μ˜ 참여에 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€!</span>", unsafe_allow_html=True)
182
+ st.markdown("<span style='font-size: 14px;'> *사전 λ°©μ§€ ν•„ν„°λž€: μ—¬λŸ¬λΆ„μ˜ 사진이 λ”₯페이크 λͺ¨λΈμ— ν•™μŠ΅λ˜μ§€ λͺ»ν•˜λ„둝 λ°©ν•΄ν•˜λŠ” λ…Έμ΄μ¦ˆ(noise)ν˜•νƒœμ˜ ν•„ν„°.</span>", unsafe_allow_html=True)
183
 
184
  def change_hair_to_blonde(image):
185
  # Convert to OpenCV format
 
201
  image_blonde = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
202
  return image_blonde
203
 
204
+ def add_noise(image):
205
  # Convert to OpenCV format
206
  image_np = np.array(image)
207
+ # Generate random noise
208
+ noise = np.random.normal(0, 25, image_np.shape).astype(np.uint8)
209
+ # Add noise to the image
210
+ noisy_image = cv2.add(image_np, noise)
211
  return noisy_image
212
 
213
+ def correct_image_orientation(image):
214
+ try:
215
+ for orientation in ExifTags.TAGS.keys():
216
+ if ExifTags.TAGS[orientation] == 'Orientation':
217
+ break
218
+ exif = image._getexif()
219
+ if exif is not None:
220
+ orientation = exif.get(orientation, 1)
221
+ if orientation == 3:
222
+ image = image.rotate(180, expand=True)
223
+ elif orientation == 6:
224
+ image = image.rotate(270, expand=True)
225
+ elif orientation == 8:
226
+ image = image.rotate(90, expand=True)
227
+ except (AttributeError, KeyError, IndexError):
228
+ pass
229
+ return image
230
 
231
+ uploaded_file = st.file_uploader("이미지λ₯Ό μ—…λ‘œλ“œν•˜μ„Έμš”...", type=["jpg", "png", "jpeg"])
232
 
233
  if uploaded_file is not None:
234
  image = Image.open(uploaded_file)
235
+ image = correct_image_orientation(image)
236
+
237
+ st.write("이미지 처리 쀑...")
238
+
239
+ # Save the original image as a numpy array
240
+ image_np = np.array(image)
241
+
242
+ col1, col2 = st.columns(2)
243
+
244
+ with col1:
245
+ st.image(image, use_column_width=True)
246
+ st.markdown('<div class="custom-caption-1">μ—…λ‘œλ“œν•œ 이미지</div>', unsafe_allow_html=True)
247
+
248
+ with col2:
249
+ st.image(image, use_column_width=True)
250
+ st.markdown('<div class="custom-caption-1">ν•„ν„°λ₯Ό μž…νžŒ 이미지</div>', unsafe_allow_html=True)
251
+
252
+ button_clicked = st.button("μƒλ‹¨μ˜ 두 사진을 λ”₯페이크 λͺ¨λΈμ— ν•™μŠ΅μ‹œν‚€κΈ°")
253
+ st.markdown('<p class="survey">μœ„ μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©ν•΄ λ³΄μ…¨κ±°λ‚˜, 저희 기술적 원리에 관심이 μžˆμœΌμ‹  λΆ„λ“€κ»˜μ„  μ•„λž˜μ˜ κ°„λ‹¨ν•œ 인터뷰에 μ°Έμ—¬ν•΄ μ£Όμ‹œλ©΄ μ§„μ‹¬μœΌλ‘œ κ°μ‚¬λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€.</p>', unsafe_allow_html=True)
254
+ 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)
255
+ st.markdown('<p class="survey-2">μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν•΄ μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€! 쒋은 ν•˜λ£¨ λ³΄λ‚΄μ„Έμš”!</p>', unsafe_allow_html=True)
256
+
257
+ if button_clicked:
258
+ with col1:
259
+ processed_image = change_hair_to_blonde(image)
260
+ st.image(processed_image, use_column_width=True)
261
+ st.markdown('<div class="custom-caption-2">원본 이미지λ₯Ό λ”₯페이크 λͺ¨λΈμ— λ„£μ—ˆμ„ 경우</div>', unsafe_allow_html=True)
262
+ st.markdown("<span>이해λ₯Ό 돕기 μœ„ν•΄ 사진에 λ…Έλž€μƒ‰μ„ μž…νžˆλŠ” λ”₯페이크 μ•Œκ³ λ¦¬μ¦˜ 적용. 원본 μ΄λ―Έμ§€λŠ” λ”₯페이크 μ•Œκ³ λ¦¬μ¦˜μ˜ 영ν–₯을 λ°›μŒ.</span>", unsafe_allow_html=True)
263
+
264
+ with col2:
265
+ deepfake_image = add_noise(image)
266
+ st.image(deepfake_image, use_column_width=True)
267
+ st.markdown('<div class="custom-caption-2">사전 λ°©μ§€ ν•„ν„° 이미지λ₯Ό λ”₯페이크 λͺ¨λΈμ— λ„£μ—ˆμ„ 경우</div>', unsafe_allow_html=True)
268
+ st.markdown("<span>사전 λ°©μ§€ ν•„ν„°λ₯Ό μž…νžŒ μ΄λ―Έμ§€λŠ” λ”₯페이크 μ•Œκ³ λ¦¬μ¦˜μ˜ 영ν–₯을 λ°›μ§€ μ•Šκ³  λ…Έμ΄μ¦ˆ μ²˜λ¦¬κ°€ λ˜μ–΄ μ•Œμ•„λ³΄κΈ° νž˜λ“  사진을 좜λ ₯. 즉, λ”₯페이크 사진 합성을 방해함.</span>", unsafe_allow_html=True)