dseditor commited on
Commit
0bf2a8f
·
verified ·
1 Parent(s): 76d5afa

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +417 -0
  2. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ NotebookLM Watermark Remover - Gradio App
3
+ 去除 NotebookLM 浮水印並提供 PNG 打包下載或 PPT 轉換
4
+ """
5
+
6
+ import gradio as gr
7
+ import fitz # PyMuPDF
8
+ from pathlib import Path
9
+ import tempfile
10
+ import shutil
11
+ import zipfile
12
+ from PIL import Image
13
+ import numpy as np
14
+ from skimage.restoration import inpaint
15
+ from pptx import Presentation
16
+ from pptx.util import Inches
17
+ import io
18
+
19
+
20
+ class NotebookLMWatermarkRemover:
21
+ """NotebookLM 浮水印移除器"""
22
+
23
+ def __init__(self):
24
+ """初始化"""
25
+ # NotebookLM logo 的固定位置 (基於 2867x1600 解析度)
26
+ self.base_width = 2867
27
+ self.base_height = 1600
28
+ self.logo_coords = (1530, 1595, 2620, 2860) # r1, r2, c1, c2
29
+
30
+ def pdf_to_pngs(self, pdf_path: str, output_dir: Path, dpi: int = 150) -> list:
31
+ """
32
+ 將 PDF 轉換為 PNG 圖片
33
+
34
+ Args:
35
+ pdf_path: PDF 檔案路徑
36
+ output_dir: 輸出目錄
37
+ dpi: 解析度
38
+
39
+ Returns:
40
+ PNG 檔案路徑列表
41
+ """
42
+ pdf_doc = fitz.open(pdf_path)
43
+ output_dir.mkdir(parents=True, exist_ok=True)
44
+
45
+ zoom = dpi / 72
46
+ mat = fitz.Matrix(zoom, zoom)
47
+
48
+ png_files = []
49
+ page_count = len(pdf_doc)
50
+
51
+ for page_num, page in enumerate(pdf_doc, 1):
52
+ pix = page.get_pixmap(matrix=mat, alpha=False)
53
+ output_path = output_dir / f"page_{page_num:04d}.png"
54
+ pix.save(str(output_path))
55
+ png_files.append(output_path)
56
+
57
+ yield f"轉換進度: {page_num}/{page_count}", None, None
58
+
59
+ pdf_doc.close()
60
+ return png_files
61
+
62
+ def remove_watermark(self, image_path: str, output_path: str) -> bool:
63
+ """
64
+ 移除 NotebookLM 浮水印
65
+
66
+ Args:
67
+ image_path: 輸入圖片路徑
68
+ output_path: 輸出圖片路徑
69
+
70
+ Returns:
71
+ 是否成功
72
+ """
73
+ try:
74
+ image = Image.open(image_path)
75
+ image_array = np.array(image)
76
+
77
+ # 創建遮罩
78
+ mask = np.zeros(image_array.shape[:-1], dtype=bool)
79
+
80
+ # 計算縮放比例
81
+ image_width, image_height = image_array.shape[1], image_array.shape[0]
82
+ ratio = image_width / self.base_width
83
+
84
+ # 檢查比例
85
+ if abs(ratio - (image_height / self.base_height)) > 0.01:
86
+ # 比例不符,可能不是 NotebookLM 生成的圖片
87
+ # 直接複製原圖
88
+ image.save(output_path)
89
+ return True
90
+
91
+ # 調整座標
92
+ r1, r2, c1, c2 = self.logo_coords
93
+ r1 = int(r1 * ratio)
94
+ r2 = int(r2 * ratio)
95
+ c1 = int(c1 * ratio)
96
+ c2 = int(c2 * ratio)
97
+
98
+ # 標記 logo 區域
99
+ mask[r1:r2, c1:c2] = True
100
+
101
+ # 執行修復
102
+ image_result = inpaint.inpaint_biharmonic(image_array, mask, channel_axis=-1)
103
+
104
+ # 保存結果
105
+ Image.fromarray((image_result * 255).astype("uint8")).save(output_path)
106
+ return True
107
+
108
+ except Exception as e:
109
+ print(f"移除浮水印失敗: {e}")
110
+ return False
111
+
112
+ def create_zip(self, png_files: list, zip_path: str):
113
+ """
114
+ 將 PNG 檔案打包為 ZIP
115
+
116
+ Args:
117
+ png_files: PNG 檔案路徑列表
118
+ zip_path: ZIP 輸出路徑
119
+ """
120
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
121
+ for png_file in png_files:
122
+ zipf.write(png_file, png_file.name)
123
+
124
+ def create_ppt(self, png_files: list, ppt_path: str):
125
+ """
126
+ 將 PNG 檔案轉換為 PPT
127
+
128
+ Args:
129
+ png_files: PNG 檔案路徑列表
130
+ ppt_path: PPT 輸出路徑
131
+ """
132
+ prs = Presentation()
133
+ prs.slide_width = Inches(10) # 16:9
134
+ prs.slide_height = Inches(5.625)
135
+
136
+ blank_slide_layout = prs.slide_layouts[6]
137
+
138
+ for png_file in png_files:
139
+ slide = prs.slides.add_slide(blank_slide_layout)
140
+ slide.shapes.add_picture(
141
+ str(png_file),
142
+ 0, 0,
143
+ width=prs.slide_width,
144
+ height=prs.slide_height
145
+ )
146
+
147
+ prs.save(ppt_path)
148
+
149
+
150
+ # 全域變數
151
+ remover = NotebookLMWatermarkRemover()
152
+
153
+
154
+ def process_pdf_to_png(pdf_file, progress=gr.Progress()):
155
+ """
156
+ 處理 PDF: 轉 PNG + 去浮水印 + 打包下載
157
+
158
+ Args:
159
+ pdf_file: 上傳的 PDF 檔案
160
+
161
+ Returns:
162
+ (狀態訊息, ZIP 檔案路徑)
163
+ """
164
+ if pdf_file is None:
165
+ return "請上傳 PDF 檔案", None
166
+
167
+ try:
168
+ # 創建臨時目錄
169
+ temp_dir = Path(tempfile.mkdtemp())
170
+ png_dir = temp_dir / "pngs"
171
+ cleaned_dir = temp_dir / "cleaned"
172
+ cleaned_dir.mkdir(parents=True, exist_ok=True)
173
+
174
+ # 步驟 1: PDF 轉 PNG
175
+ progress(0, desc="正在轉換 PDF 為 PNG...")
176
+ pdf_doc = fitz.open(pdf_file.name)
177
+ page_count = len(pdf_doc)
178
+
179
+ zoom = 150 / 72
180
+ mat = fitz.Matrix(zoom, zoom)
181
+
182
+ png_files = []
183
+ for page_num, page in enumerate(pdf_doc, 1):
184
+ progress((page_num / page_count) * 0.5, desc=f"轉換 PDF: {page_num}/{page_count}")
185
+
186
+ pix = page.get_pixmap(matrix=mat, alpha=False)
187
+ output_path = cleaned_dir / f"page_{page_num:04d}.png"
188
+ pix.save(str(output_path))
189
+ png_files.append(output_path)
190
+
191
+ pdf_doc.close()
192
+
193
+ # 步驟 2: 移除浮水印
194
+ progress(0.5, desc="正在移除浮水印...")
195
+ cleaned_files = []
196
+
197
+ for idx, png_file in enumerate(png_files, 1):
198
+ progress(0.5 + (idx / len(png_files)) * 0.4, desc=f"移除浮水印: {idx}/{len(png_files)}")
199
+
200
+ cleaned_path = cleaned_dir / f"cleaned_{png_file.name}"
201
+ success = remover.remove_watermark(str(png_file), str(cleaned_path))
202
+
203
+ if success:
204
+ # 替換原檔案
205
+ shutil.move(str(cleaned_path), str(png_file))
206
+ cleaned_files.append(png_file)
207
+
208
+ # 步驟 3: 打包為 ZIP
209
+ progress(0.9, desc="正在打包 ZIP...")
210
+ zip_path = temp_dir / "notebooklm_cleaned.zip"
211
+
212
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
213
+ for png_file in cleaned_files:
214
+ zipf.write(png_file, png_file.name)
215
+
216
+ progress(1.0, desc="完成!")
217
+
218
+ return f"✓ 成功處理 {len(cleaned_files)} 張圖片", str(zip_path)
219
+
220
+ except Exception as e:
221
+ return f"✗ 處理失敗: {str(e)}", None
222
+
223
+
224
+ def process_pdf_to_ppt(pdf_file, progress=gr.Progress()):
225
+ """
226
+ 處理 PDF: 轉 PNG + 去浮水印 + 轉 PPT
227
+
228
+ Args:
229
+ pdf_file: 上傳的 PDF 檔案
230
+
231
+ Returns:
232
+ (狀態訊息, PPT 檔案路徑)
233
+ """
234
+ if pdf_file is None:
235
+ return "請上傳 PDF 檔案", None
236
+
237
+ try:
238
+ # 創建臨時目錄
239
+ temp_dir = Path(tempfile.mkdtemp())
240
+ png_dir = temp_dir / "pngs"
241
+ png_dir.mkdir(parents=True, exist_ok=True)
242
+
243
+ # 步驟 1: PDF 轉 PNG
244
+ progress(0, desc="正在轉換 PDF 為 PNG...")
245
+ pdf_doc = fitz.open(pdf_file.name)
246
+ page_count = len(pdf_doc)
247
+
248
+ zoom = 150 / 72
249
+ mat = fitz.Matrix(zoom, zoom)
250
+
251
+ png_files = []
252
+ for page_num, page in enumerate(pdf_doc, 1):
253
+ progress((page_num / page_count) * 0.4, desc=f"轉換 PDF: {page_num}/{page_count}")
254
+
255
+ pix = page.get_pixmap(matrix=mat, alpha=False)
256
+ output_path = png_dir / f"page_{page_num:04d}.png"
257
+ pix.save(str(output_path))
258
+ png_files.append(output_path)
259
+
260
+ pdf_doc.close()
261
+
262
+ # 步驟 2: 移除浮水印
263
+ progress(0.4, desc="正在移除浮水印...")
264
+
265
+ for idx, png_file in enumerate(png_files, 1):
266
+ progress(0.4 + (idx / len(png_files)) * 0.4, desc=f"移除浮水印: {idx}/{len(png_files)}")
267
+
268
+ temp_path = png_dir / f"temp_{png_file.name}"
269
+ success = remover.remove_watermark(str(png_file), str(temp_path))
270
+
271
+ if success:
272
+ shutil.move(str(temp_path), str(png_file))
273
+
274
+ # 步驟 3: 轉換為 PPT
275
+ progress(0.8, desc="正在生成 PPT...")
276
+ ppt_path = temp_dir / "notebooklm_cleaned.pptx"
277
+
278
+ prs = Presentation()
279
+ prs.slide_width = Inches(10)
280
+ prs.slide_height = Inches(5.625)
281
+ blank_slide_layout = prs.slide_layouts[6]
282
+
283
+ for idx, png_file in enumerate(png_files, 1):
284
+ progress(0.8 + (idx / len(png_files)) * 0.2, desc=f"生成 PPT: {idx}/{len(png_files)}")
285
+
286
+ slide = prs.slides.add_slide(blank_slide_layout)
287
+ slide.shapes.add_picture(
288
+ str(png_file),
289
+ 0, 0,
290
+ width=prs.slide_width,
291
+ height=prs.slide_height
292
+ )
293
+
294
+ prs.save(str(ppt_path))
295
+
296
+ progress(1.0, desc="完成!")
297
+
298
+ return f"✓ 成功生成 {len(png_files)} 頁 PPT", str(ppt_path)
299
+
300
+ except Exception as e:
301
+ return f"✗ 處理失敗: {str(e)}", None
302
+
303
+
304
+ # 創建 Gradio 介面
305
+ with gr.Blocks(title="NotebookLM 浮水印移除工具", theme=gr.themes.Soft()) as app:
306
+ gr.Markdown("""
307
+ # 🎨 NotebookLM 浮水印移除工具
308
+
309
+ 自動移除 NotebookLM 生成的 PDF 右下角浮水印,並提供兩種下載方式:
310
+ - **PNG 打包下載**: 將所有頁面轉為 PNG 並打包成 ZIP
311
+ - **PPT 格式下載**: 將所有頁面轉為 PowerPoint 簡報
312
+
313
+ > 💡 提示: 此工具僅適用於 NotebookLM 生成的 PDF (解析度 2867x1600)
314
+ """)
315
+
316
+ with gr.Tabs():
317
+ # 標籤 1: PNG 打包下載
318
+ with gr.Tab("📦 PNG 打包下載"):
319
+ gr.Markdown("### 上傳 PDF,去除浮水印後下載 PNG 圖片包")
320
+
321
+ with gr.Row():
322
+ with gr.Column():
323
+ pdf_input_png = gr.File(
324
+ label="上傳 PDF 檔案",
325
+ file_types=[".pdf"],
326
+ type="filepath"
327
+ )
328
+ process_png_btn = gr.Button("🚀 開始處理", variant="primary", size="lg")
329
+
330
+ with gr.Column():
331
+ status_png = gr.Textbox(
332
+ label="處理狀態",
333
+ placeholder="等待處理...",
334
+ interactive=False
335
+ )
336
+ zip_output = gr.File(
337
+ label="下載 ZIP 檔案",
338
+ interactive=False
339
+ )
340
+
341
+ gr.Markdown("""
342
+ #### 📝 說明
343
+ 1. 上傳 NotebookLM 生成的 PDF 檔案
344
+ 2. 點擊「開始處理」按鈕
345
+ 3. 等待處理完成 (約 30-60 秒)
346
+ 4. 下載包含所有去除浮水印後的 PNG 圖片的 ZIP 檔案
347
+ """)
348
+
349
+ # 標籤 2: PPT 格式下載
350
+ with gr.Tab("📊 PPT 格式下載"):
351
+ gr.Markdown("### 上傳 PDF,去除浮水印後轉換為 PowerPoint")
352
+
353
+ with gr.Row():
354
+ with gr.Column():
355
+ pdf_input_ppt = gr.File(
356
+ label="上傳 PDF 檔案",
357
+ file_types=[".pdf"],
358
+ type="filepath"
359
+ )
360
+ process_ppt_btn = gr.Button("🚀 開始處理", variant="primary", size="lg")
361
+
362
+ with gr.Column():
363
+ status_ppt = gr.Textbox(
364
+ label="處理狀態",
365
+ placeholder="等待處理...",
366
+ interactive=False
367
+ )
368
+ ppt_output = gr.File(
369
+ label="下載 PPT 檔案",
370
+ interactive=False
371
+ )
372
+
373
+ gr.Markdown("""
374
+ #### 📝 說明
375
+ 1. 上傳 NotebookLM 生成的 PDF 檔案
376
+ 2. 點擊「開始處理」按鈕
377
+ 3. 等待處理完成 (約 30-60 秒)
378
+ 4. 下載去除浮水印後的 PowerPoint 簡報檔案
379
+
380
+ > 💡 提示: PPT 中每頁都是一張完整的圖片,保持原始版面
381
+ """)
382
+
383
+ # 底部資訊
384
+ gr.Markdown("""
385
+ ---
386
+ ### ℹ️ 技術資訊
387
+ - **浮水印移除**: 使用雙調和插值法 (Biharmonic Inpainting)
388
+ - **支援格式**: PDF 輸入,PNG/PPT 輸出
389
+ - **解析度**: 150 DPI (2867x1600)
390
+ - **處理時間**: 約 2-5 秒/頁
391
+
392
+ ### ⚠️ 注意事項
393
+ - 僅適用於 NotebookLM 生成的標準格式 PDF
394
+ - 浮水印位置固定在右下角
395
+ - 處理大型 PDF 可能需要較長時間
396
+
397
+ ### 🔗 相關連結
398
+ - [GitHub Repository](https://github.com/your-repo)
399
+ - [Report Issues](https://github.com/your-repo/issues)
400
+ """)
401
+
402
+ # 綁定事件
403
+ process_png_btn.click(
404
+ fn=process_pdf_to_png,
405
+ inputs=[pdf_input_png],
406
+ outputs=[status_png, zip_output]
407
+ )
408
+
409
+ process_ppt_btn.click(
410
+ fn=process_pdf_to_ppt,
411
+ inputs=[pdf_input_ppt],
412
+ outputs=[status_ppt, ppt_output]
413
+ )
414
+
415
+
416
+ if __name__ == "__main__":
417
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio==4.44.0
2
+ PyMuPDF==1.23.8
3
+ Pillow==10.1.0
4
+ numpy==1.24.3
5
+ scikit-image==0.22.0
6
+ python-pptx==0.6.23