ebhon commited on
Commit
4eae26e
·
verified ·
1 Parent(s): 5d094c5

Update pages/01_See_How_It_Works.py

Browse files
Files changed (1) hide show
  1. pages/01_See_How_It_Works.py +251 -324
pages/01_See_How_It_Works.py CHANGED
@@ -1,324 +1,251 @@
1
- import streamlit as st
2
- import os
3
- from PIL import Image
4
- import torch
5
- from manga_translator.translator import MangaTextDetector
6
- import tempfile
7
- import cv2
8
- import io
9
- import numpy as np
10
- from PIL import ImageDraw, ImageFont
11
-
12
- # Set page config for wider layout and title
13
- st.set_page_config(
14
- page_title="Process Details - Manga Translator",
15
- page_icon="🔍",
16
- layout="wide"
17
- )
18
-
19
- # Custom CSS to match main page exactly
20
- st.markdown("""
21
- <style>
22
- /* Reset container styles */
23
- .block-container {
24
- padding: 2rem 1rem !important;
25
- max-width: none;
26
- }
27
-
28
- /* Main content area */
29
- .main .block-container {
30
- padding-left: calc(250px + 1rem) !important;
31
- }
32
-
33
- /* Sidebar styling */
34
- section[data-testid="stSidebar"] {
35
- width: 250px !important;
36
- background-color: rgb(240, 242, 246) !important;
37
- position: fixed !important;
38
- left: 0 !important;
39
- top: 0 !important;
40
- height: 100vh !important;
41
- }
42
-
43
- /* Header styling */
44
- h1 {
45
- font-weight: 700 !important;
46
- color: rgb(49, 51, 63) !important;
47
- margin-bottom: 0.5rem !important;
48
- font-size: 2.25rem !important;
49
- text-align: left !important;
50
- }
51
-
52
- /* Subheader styling */
53
- .subheader {
54
- color: rgb(49, 51, 63);
55
- font-size: 1rem;
56
- margin-bottom: 2rem;
57
- }
58
-
59
- /* Upload section styling */
60
- .stUploadButton {
61
- background-color: white !important;
62
- border: 1px solid rgb(224, 224, 224) !important;
63
- border-radius: 0.5rem !important;
64
- padding: 1rem !important;
65
- }
66
-
67
- /* Button styling */
68
- .stButton button {
69
- background-color: rgb(255, 75, 75);
70
- color: white;
71
- border: none;
72
- padding: 0.5rem 1rem;
73
- border-radius: 0.25rem;
74
- }
75
-
76
- /* Process section styling */
77
- .process-section {
78
- margin-top: 2rem;
79
- padding: 1.5rem;
80
- background-color: white;
81
- border-radius: 0.5rem;
82
- }
83
-
84
- /* Step headers */
85
- .step-header {
86
- font-size: 1.5rem;
87
- color: rgb(49, 51, 63);
88
- margin: 1.5rem 0 1rem 0;
89
- padding-bottom: 0.5rem;
90
- border-bottom: 1px solid rgb(224, 224, 224);
91
- }
92
-
93
- /* Hide default menu text */
94
- .css-17lntkn {
95
- display: none;
96
- }
97
- .css-pkbazv {
98
- display: none;
99
- }
100
- </style>
101
- """, unsafe_allow_html=True)
102
-
103
- # Initialize session state for storing processed results
104
- if 'processed_results' not in st.session_state:
105
- st.session_state.processed_results = {}
106
-
107
- # Create temp directory if needed
108
- temp_dir = tempfile.mkdtemp()
109
-
110
- # Header - now left-aligned like main page
111
- st.title("See How It Works")
112
- st.markdown('<div class="subheader">Explore the step-by-step translation process!</div>', unsafe_allow_html=True)
113
-
114
- def get_font(size):
115
- """Get font with proper error handling"""
116
- try:
117
- return ImageFont.truetype('font/CC Wild Words Roman.ttf', size)
118
- except:
119
- try:
120
- # Fallback to Arial if available
121
- return ImageFont.truetype('arial.ttf', size)
122
- except:
123
- return ImageFont.load_default()
124
-
125
- def calculate_font_size(text, max_width, max_height):
126
- """Calculate optimal font size based on text and box dimensions"""
127
- # Base size calculations
128
- min_size = 12
129
- max_size = min(60, int(max_height * 0.8))
130
-
131
- # Content-specific adjustments
132
- if any(c in text for c in '!?!?'): # Emphasis characters
133
- min_size = 16
134
- max_size = min(max_size, int(max_height * 0.9))
135
- elif text.isupper():
136
- max_size = min(max_size, int(max_height * 0.7))
137
- elif len(text.split()) <= 2:
138
- min_size = 16
139
-
140
- # Calculate based on text length and area
141
- char_count = len(text)
142
- area = max_width * max_height
143
- chars_per_area = char_count / (area + 1)
144
-
145
- # Adjust size based on density
146
- size_factor = 1.0
147
- if chars_per_area > 0.01:
148
- size_factor = 0.8
149
- elif chars_per_area < 0.005:
150
- size_factor = 1.2
151
-
152
- initial_size = int(min(max_width / (char_count * 0.6), max_height * 0.8) * size_factor)
153
- return max(min_size, min(initial_size, max_size))
154
-
155
- def process_detailed_image(uploaded_file):
156
- """Process image and store detailed results in session state."""
157
- if uploaded_file.name not in st.session_state.processed_results:
158
- try:
159
- detector = MangaTextDetector('best.pt')
160
-
161
- # Save temporary file
162
- temp_path = os.path.join(temp_dir, uploaded_file.name)
163
- with open(temp_path, "wb") as f:
164
- f.write(uploaded_file.getbuffer())
165
-
166
- # Process image
167
- image, detections, result_image, processed_regions, translated_image = detector.process_image(temp_path)
168
-
169
- # Store all results in session state
170
- st.session_state.processed_results[uploaded_file.name] = {
171
- 'image': image,
172
- 'detections': detections,
173
- 'result_image': result_image,
174
- 'processed_regions': processed_regions,
175
- 'translated_image': translated_image
176
- }
177
-
178
- # Clean up temp file
179
- try:
180
- os.remove(temp_path)
181
- except:
182
- pass
183
-
184
- return True
185
- except Exception as e:
186
- st.error(f"❌ Error: {str(e)}")
187
- return False
188
- return True
189
-
190
- # File uploader with clean styling
191
- uploaded_files = st.file_uploader(
192
- "Choose manga pages",
193
- type=['jpg', 'jpeg', 'png'],
194
- accept_multiple_files=True,
195
- help="Upload manga pages to see the detailed translation process"
196
- )
197
-
198
- if uploaded_files:
199
- # Process images
200
- with st.spinner("Processing your manga pages..."):
201
- for uploaded_file in uploaded_files:
202
- if process_detailed_image(uploaded_file):
203
- results = st.session_state.processed_results[uploaded_file.name]
204
-
205
- st.markdown('<div class="process-section">', unsafe_allow_html=True)
206
- st.markdown(f"### 📝 Processing: {uploaded_file.name}")
207
-
208
- # 1. Original Image
209
- st.markdown('<div class="step-header">1️⃣ Original Image</div>', unsafe_allow_html=True)
210
- st.image(
211
- cv2.cvtColor(results['image'], cv2.COLOR_BGR2RGB),
212
- caption="Original Image",
213
- use_column_width=True
214
- )
215
-
216
- # 2. Text Detection
217
- st.markdown('<div class="step-header">2️⃣ Text Detection</div>', unsafe_allow_html=True)
218
- st.write("Detected text regions and speech bubbles are highlighted:")
219
- st.image(
220
- cv2.cvtColor(results['result_image'], cv2.COLOR_BGR2RGB),
221
- caption="Detected Regions",
222
- use_column_width=True
223
- )
224
-
225
- # 3. Detected Text Regions
226
- st.markdown('<div class="step-header">3️⃣ Detected Text Regions</div>', unsafe_allow_html=True)
227
- if results['processed_regions'] and results['processed_regions']['text_regions']:
228
- for i, region in enumerate(results['processed_regions']['text_regions'], 1):
229
- with st.expander(f"Region {i}"):
230
- col1, col2 = st.columns(2)
231
- with col1:
232
- x1, y1, x2, y2 = region['coords']
233
- region_img = results['image'][y1:y2, x1:x2]
234
- st.image(
235
- cv2.cvtColor(region_img, cv2.COLOR_BGR2RGB),
236
- caption=f"Region {i}"
237
- )
238
-
239
- # Add region statistics
240
- st.markdown(
241
- f'<div class="region-stats">'
242
- f'Region Size: {x2-x1}x{y2-y1} pixels<br>'
243
- f'Type: {region["type"].capitalize()}<br>'
244
- f'Text Length: {len(region["text"])} chars'
245
- f'</div>',
246
- unsafe_allow_html=True
247
- )
248
-
249
- with col2:
250
- st.markdown("**Japanese Text:**")
251
- st.code(region['text'])
252
- if 'translation' in region:
253
- st.markdown("**English Translation:**")
254
- st.code(region['translation'])
255
-
256
- # Show text preview with improved rendering
257
- preview_height = 100
258
- preview_width = 400
259
- preview_img = np.ones((preview_height, preview_width, 3), dtype=np.uint8) * 255
260
- preview_img = cv2.cvtColor(preview_img, cv2.COLOR_BGR2RGB)
261
- pil_img = Image.fromarray(preview_img)
262
- draw = ImageDraw.Draw(pil_img)
263
-
264
- # Calculate font size based on text and preview dimensions
265
- text = region['translation']
266
- font_size = calculate_font_size(text, preview_width * 0.9, preview_height * 0.8)
267
- font = get_font(font_size)
268
-
269
- # Center text
270
- bbox = draw.textbbox((0, 0), text, font=font)
271
- text_width = bbox[2] - bbox[0]
272
- text_height = bbox[3] - bbox[1]
273
- x = (preview_width - text_width) // 2
274
- y = (preview_height - text_height) // 2
275
-
276
- # Draw with improved outline
277
- outline_width = max(1, min(3, int(font_size / 20)))
278
- for dx in range(-outline_width, outline_width + 1):
279
- for dy in range(-outline_width, outline_width + 1):
280
- if dx == 0 and dy == 0:
281
- continue
282
- draw.text((x + dx, y + dy), text,
283
- font=font, fill=(255, 255, 255))
284
-
285
- draw.text((x, y), text, font=font, fill=(0, 0, 0))
286
- st.image(pil_img, caption="Text Preview", use_column_width=True)
287
- else:
288
- st.warning("No text regions detected in this image.")
289
-
290
- # 4. Final Translation
291
- st.markdown('<div class="step-header">4️⃣ Final Translation</div>', unsafe_allow_html=True)
292
- if results['translated_image'] is not None:
293
- st.image(
294
- cv2.cvtColor(results['translated_image'], cv2.COLOR_BGR2RGB),
295
- caption="Final Translated Image",
296
- use_column_width=True
297
- )
298
-
299
- # Download button with improved styling
300
- st.markdown('<div class="download-btn">', unsafe_allow_html=True)
301
- translated_bytes = cv2.imencode('.png', results['translated_image'])[1].tobytes()
302
- st.download_button(
303
- label="⬇️ Download Translated Image",
304
- data=translated_bytes,
305
- file_name=f"translated_{uploaded_file.name}",
306
- mime="image/png"
307
- )
308
- st.markdown('</div>', unsafe_allow_html=True)
309
- else:
310
- st.warning("Translation could not be completed.")
311
-
312
- st.markdown('</div>', unsafe_allow_html=True)
313
- st.markdown("---")
314
-
315
- else:
316
- st.info("👆 Upload a manga page to see the detailed translation process!")
317
-
318
- st.subheader("What You'll See")
319
- st.write("""
320
- 1. **Original Image**: Your uploaded manga page
321
- 2. **Text Detection**: View detected text regions and bubbles
322
- 3. **Detected Text**: See each text region with its translation
323
- 4. **Final Translation**: The complete translated image
324
- """)
 
1
+ import streamlit as st
2
+ import os
3
+ from PIL import Image
4
+ import torch
5
+ from manga_translator.translator import MangaTextDetector
6
+ import tempfile
7
+ import cv2
8
+ import io
9
+ import numpy as np
10
+ from PIL import ImageDraw, ImageFont
11
+
12
+ # Set page config for wider layout and title
13
+ st.set_page_config(
14
+ page_title="Process Details - Manga Translator",
15
+ page_icon="🔍",
16
+ layout="wide"
17
+ )
18
+
19
+ # Custom CSS for consistent layout with main page
20
+ st.markdown("""
21
+ <style>
22
+ body {
23
+ background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%) !important;
24
+ }
25
+ .main .block-container {
26
+ max-width: 900px;
27
+ margin: 0 auto;
28
+ padding-top: 2rem;
29
+ padding-bottom: 2rem;
30
+ }
31
+ .centered-box {
32
+ background: white;
33
+ border-radius: 16px;
34
+ box-shadow: 0 4px 32px rgba(0,0,0,0.08);
35
+ padding: 2rem;
36
+ margin-bottom: 2rem;
37
+ }
38
+ .stButton>button {
39
+ width: 100%;
40
+ }
41
+ .stAlert {
42
+ background-color: rgb(255, 251, 235);
43
+ border: none;
44
+ padding: 1rem;
45
+ margin: 1rem 0;
46
+ }
47
+ .download-btn {
48
+ text-align: center;
49
+ padding: 1rem;
50
+ }
51
+ .region-info {
52
+ border: 1px solid #e0e0e0;
53
+ padding: 1rem;
54
+ border-radius: 5px;
55
+ margin: 0.5rem 0;
56
+ }
57
+ .region-stats {
58
+ font-size: 0.9em;
59
+ color: #666;
60
+ margin-top: 0.5rem;
61
+ }
62
+ /* Remove custom sidebar width, use Streamlit default */
63
+ /* If you want to force 250px, uncomment below: */
64
+ /*
65
+ [data-testid="stSidebar"][aria-expanded="true"] {
66
+ min-width: 250px;
67
+ max-width: 250px;
68
+ }
69
+ [data-testid="stSidebar"][aria-expanded="false"] {
70
+ margin-left: -250px;
71
+ }
72
+ */
73
+ </style>
74
+ """, unsafe_allow_html=True)
75
+
76
+ # Main centered box for all content
77
+ st.markdown('<div class="centered-box">', unsafe_allow_html=True)
78
+
79
+ st.title("See How It Works")
80
+ st.write("Explore the step-by-step translation process!")
81
+
82
+ def get_font(size):
83
+ """Get font with proper error handling"""
84
+ try:
85
+ return ImageFont.truetype('font/CC Wild Words Roman.ttf', size)
86
+ except:
87
+ try:
88
+ # Fallback to Arial if available
89
+ return ImageFont.truetype('arial.ttf', size)
90
+ except:
91
+ return ImageFont.load_default()
92
+
93
+ def calculate_font_size(text, max_width, max_height):
94
+ """Calculate optimal font size based on text and box dimensions"""
95
+ min_size = 12
96
+ max_size = min(60, int(max_height * 0.8))
97
+ if any(c in text for c in '!?!?'):
98
+ min_size = 16
99
+ max_size = min(max_size, int(max_height * 0.9))
100
+ elif text.isupper():
101
+ max_size = min(max_size, int(max_height * 0.7))
102
+ elif len(text.split()) <= 2:
103
+ min_size = 16
104
+ char_count = len(text)
105
+ area = max_width * max_height
106
+ chars_per_area = char_count / (area + 1)
107
+ size_factor = 1.0
108
+ if chars_per_area > 0.01:
109
+ size_factor = 0.8
110
+ elif chars_per_area < 0.005:
111
+ size_factor = 1.2
112
+ initial_size = int(min(max_width / (char_count * 0.6), max_height * 0.8) * size_factor)
113
+ return max(min_size, min(initial_size, max_size))
114
+
115
+ def process_detailed_image(uploaded_file):
116
+ """Process image and store detailed results in session state."""
117
+ if uploaded_file.name not in st.session_state:
118
+ st.session_state.processed_results = {}
119
+ if uploaded_file.name not in st.session_state.processed_results:
120
+ try:
121
+ detector = MangaTextDetector('best.pt')
122
+ temp_path = os.path.join(tempfile.mkdtemp(), uploaded_file.name)
123
+ with open(temp_path, "wb") as f:
124
+ f.write(uploaded_file.getbuffer())
125
+ image, detections, result_image, processed_regions, translated_image = detector.process_image(temp_path)
126
+ st.session_state.processed_results[uploaded_file.name] = {
127
+ 'image': image,
128
+ 'detections': detections,
129
+ 'result_image': result_image,
130
+ 'processed_regions': processed_regions,
131
+ 'translated_image': translated_image
132
+ }
133
+ try:
134
+ os.remove(temp_path)
135
+ except:
136
+ pass
137
+ return True
138
+ except Exception as e:
139
+ st.error(f"❌ Error: {str(e)}")
140
+ return False
141
+ return True
142
+
143
+ uploaded_files = st.file_uploader(
144
+ "Choose manga pages",
145
+ type=['jpg', 'jpeg', 'png'],
146
+ accept_multiple_files=True,
147
+ help="Upload manga pages to see the detailed translation process"
148
+ )
149
+
150
+ if uploaded_files:
151
+ with st.spinner("Processing your manga pages..."):
152
+ for uploaded_file in uploaded_files:
153
+ if process_detailed_image(uploaded_file):
154
+ results = st.session_state.processed_results[uploaded_file.name]
155
+ st.header(f"📝 Processing: {uploaded_file.name}")
156
+ st.subheader("1️⃣ Original Image")
157
+ st.image(
158
+ cv2.cvtColor(results['image'], cv2.COLOR_BGR2RGB),
159
+ caption="Original Image",
160
+ use_column_width=True
161
+ )
162
+ st.subheader("2️⃣ Text Detection")
163
+ st.write("Detected text regions and speech bubbles are highlighted:")
164
+ st.image(
165
+ cv2.cvtColor(results['result_image'], cv2.COLOR_BGR2RGB),
166
+ caption="Detected Regions",
167
+ use_column_width=True
168
+ )
169
+ st.subheader("3️⃣ Detected Text Regions")
170
+ if results['processed_regions'] and results['processed_regions']['text_regions']:
171
+ for i, region in enumerate(results['processed_regions']['text_regions'], 1):
172
+ with st.expander(f"Region {i}"):
173
+ col1, col2 = st.columns(2)
174
+ with col1:
175
+ x1, y1, x2, y2 = region['coords']
176
+ region_img = results['image'][y1:y2, x1:x2]
177
+ st.image(
178
+ cv2.cvtColor(region_img, cv2.COLOR_BGR2RGB),
179
+ caption=f"Region {i}"
180
+ )
181
+ region_width = x2 - x1
182
+ region_height = y2 - y1
183
+ st.markdown(
184
+ f'<div class="region-stats">'
185
+ f'Region Size: {region_width}x{region_height} pixels<br>'
186
+ f'Type: {region["type"].capitalize()}<br>'
187
+ f'Text Length: {len(region["text"])} chars'
188
+ f'</div>',
189
+ unsafe_allow_html=True
190
+ )
191
+ with col2:
192
+ st.markdown("**Japanese Text:**")
193
+ st.code(region['text'])
194
+ if 'translation' in region:
195
+ st.markdown("**English Translation:**")
196
+ st.code(region['translation'])
197
+ preview_height = 100
198
+ preview_width = 400
199
+ preview_img = np.ones((preview_height, preview_width, 3), dtype=np.uint8) * 255
200
+ preview_img = cv2.cvtColor(preview_img, cv2.COLOR_BGR2RGB)
201
+ pil_img = Image.fromarray(preview_img)
202
+ draw = ImageDraw.Draw(pil_img)
203
+ text = region['translation']
204
+ font_size = calculate_font_size(text, preview_width * 0.9, preview_height * 0.8)
205
+ font = get_font(font_size)
206
+ bbox = draw.textbbox((0, 0), text, font=font)
207
+ text_width = bbox[2] - bbox[0]
208
+ text_height = bbox[3] - bbox[1]
209
+ x = (preview_width - text_width) // 2
210
+ y = (preview_height - text_height) // 2
211
+ outline_width = max(1, min(3, int(font_size / 20)))
212
+ for dx in range(-outline_width, outline_width + 1):
213
+ for dy in range(-outline_width, outline_width + 1):
214
+ if dx == 0 and dy == 0:
215
+ continue
216
+ draw.text((x + dx, y + dy), text,
217
+ font=font, fill=(255, 255, 255))
218
+ draw.text((x, y), text, font=font, fill=(0, 0, 0))
219
+ st.image(pil_img, caption="Text Preview", use_column_width=True)
220
+ else:
221
+ st.warning("No text regions detected in this image.")
222
+ st.subheader("4️⃣ Final Translation")
223
+ if results['translated_image'] is not None:
224
+ st.image(
225
+ cv2.cvtColor(results['translated_image'], cv2.COLOR_BGR2RGB),
226
+ caption="Final Translated Image",
227
+ use_column_width=True
228
+ )
229
+ st.markdown('<div class="download-btn">', unsafe_allow_html=True)
230
+ translated_bytes = cv2.imencode('.png', results['translated_image'])[1].tobytes()
231
+ st.download_button(
232
+ label="⬇️ Download Translated Image",
233
+ data=translated_bytes,
234
+ file_name=f"translated_{uploaded_file.name}",
235
+ mime="image/png"
236
+ )
237
+ st.markdown('</div>', unsafe_allow_html=True)
238
+ else:
239
+ st.warning("Translation could not be completed.")
240
+ st.markdown("---")
241
+ else:
242
+ st.info("👆 Upload a manga page to see the detailed translation process!")
243
+ st.subheader("What You'll See")
244
+ st.write("""
245
+ 1. **Original Image**: Your uploaded manga page
246
+ 2. **Text Detection**: View detected text regions and bubbles
247
+ 3. **Detected Text**: See each text region with its translation
248
+ 4. **Final Translation**: The complete translated image
249
+ """)
250
+
251
+ st.markdown('</div>', unsafe_allow_html=True)