From 2a69687acd3634a9bd7dafdf0aea3b7a6ebc87c5 Mon Sep 17 00:00:00 2001 From: breeze303 Date: Sat, 1 Nov 2025 10:00:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0web=E3=80=81docker=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 21 +++++ .gitignore | 20 +++++ Dockerfile | 23 +++++ README.md | 195 +++++++++++++------------------------------ README_cn.md | 193 ++++++++++++++---------------------------- docker-compose.yml | 7 ++ pyproject.toml | 3 + requirements.txt | 1 + templates/index.html | 126 ++++++++++++++++++++++++++++ web_app.py | 120 ++++++++++++++++++++++++++ 10 files changed, 442 insertions(+), 267 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 pyproject.toml create mode 100644 templates/index.html create mode 100644 web_app.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..551bce15 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +# Git +.git +.gitignore + +# Docker +Dockerfile +.dockerignore + +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd + +# Virtual environment +venv/ +.venv/ + +# IDE / Editor +.idea/ +.vscode/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f0e5dfdb --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Python +__pycache__/ +blind_watermark/__pycache__/ +*.pyc +*.pyo +*.pyd + +# Virtual environment +venv/ +.venv/ + +# IDE / Editor +.idea/ +.vscode/ + +# Uploads folder +uploads/ + +# OS-specific +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0c07da8b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Use an official Python runtime as a parent image +FROM python:3.12-slim + +# Set the working directory in the container +WORKDIR /app + +# Copy the requirements file into the container +COPY requirements.txt . + +# Install system dependencies required by OpenCV +RUN apt-get update && apt-get install -y libgl1 libglib2.0-0 + +# Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code +COPY . . + +# Expose the port the app runs on +EXPOSE 5891 + +# Run the application +CMD ["python", "web_app.py"] diff --git a/README.md b/README.md index bbfea0ee..fdb149df 100644 --- a/README.md +++ b/README.md @@ -1,177 +1,102 @@ - - - # blind-watermark - Blind watermark based on DWT-DCT-SVD. - [![PyPI](https://img.shields.io/pypi/v/blind_watermark)](https://pypi.org/project/blind_watermark/) [![Build Status](https://travis-ci.com/guofei9987/blind_watermark.svg?branch=master)](https://travis-ci.com/guofei9987/blind_watermark) [![codecov](https://codecov.io/gh/guofei9987/blind_watermark/branch/master/graph/badge.svg)](https://codecov.io/gh/guofei9987/blind_watermark) [![License](https://img.shields.io/pypi/l/blind_watermark.svg)](https://github.com/guofei9987/blind_watermark/blob/master/LICENSE) ![Python](https://img.shields.io/badge/python->=3.5-green.svg) ![Platform](https://img.shields.io/badge/platform-windows%20|%20linux%20|%20macos-green.svg) -[![stars](https://img.shields.io/github/stars/guofei9987/blind_watermark.svg?style=social)](https://github.com/guofei9987/blind_watermark/) -[![fork](https://img.shields.io/github/forks/guofei9987/blind_watermark?style=social)](https://github.com/guofei9987/blind_watermark/fork) -[![Downloads](https://pepy.tech/badge/blind-watermark)](https://pepy.tech/project/blind-watermark) -[![Discussions](https://img.shields.io/badge/discussions-green.svg)](https://github.com/guofei9987/blind_watermark/discussions) -Featured|HelloGitHub - **Documentation:** [https://BlindWatermark.github.io/blind_watermark/#/en/](https://BlindWatermark.github.io/blind_watermark/#/en/) -- **文档:** [https://BlindWatermark.github.io/blind_watermark/#/zh/](https://BlindWatermark.github.io/blind_watermark/#/zh/) -- **中文 readme** [README_cn.md](README_cn.md) -- **Source code:** [https://github.com/guofei9987/blind_watermark](https://github.com/guofei9987/blind_watermark) - - - -# install -```bash -pip install blind-watermark -``` - -For the current developer version: -```bach -git clone git@github.com:guofei9987/blind_watermark.git -cd blind_watermark -pip install . -``` - -# How to use +- **中文文档:** [https://BlindWatermark.github.io/blind_watermark/#/zh/](https://BlindWatermark.github.io/blind_watermark/#/zh/) +# Quick Start: Web App -## Use in bash +For ease of use, this project includes a modern web interface. +## How to Run -```bash -# embed watermark into image: -blind_watermark --embed --pwd 1234 examples/pic/ori_img.jpeg "watermark text" examples/output/embedded.png -# extract watermark from image: -blind_watermark --extract --pwd 1234 --wm_shape 111 examples/output/embedded.png -``` +### Option 1: Run with Docker (Recommended) +This is the simplest and recommended method to avoid local environment issues. +1. **Start the Application:** + In the project root directory, run the following command: + ```bash + docker-compose up -d + ``` +2. **Access the Application:** + Open your browser and navigate to `http://localhost:5891`. -## Use in Python +3. **Stop the Application:** + In the terminal, press `Ctrl+C`, then run the following command to clean up the containers: + ```bash + docker-compose down + ``` -Original Image + Watermark = Watermarked Image +### Option 2: Run Natively -![origin_image](docs/原图.jpeg) + '@guofei9987 开源万岁!' = ![打上水印的图](docs/打上水印的图.jpg) +1. **Install Dependencies:** + ```bash + pip install -r requirements.txt + ``` +2. **Start the Server:** + ```bash + python web_app.py + ``` -See the [codes](/examples/example_str.py) +3. **Access the Application:** + Open your browser and navigate to `http://localhost:5891`. -Embed watermark: -```python -from blind_watermark import WaterMark -bwm1 = WaterMark(password_img=1, password_wm=1) -bwm1.read_img('pic/ori_img.jpg') -wm = '@guofei9987 开源万岁!' -bwm1.read_wm(wm, mode='str') -bwm1.embed('output/embedded.png') -len_wm = len(bwm1.wm_bit) -print('Put down the length of wm_bit {len_wm}'.format(len_wm=len_wm)) -``` +# Advanced Usage (CLI & Library) -Extract watermark: -```python -bwm1 = WaterMark(password_img=1, password_wm=1) -wm_extract = bwm1.extract('output/embedded.png', wm_shape=len_wm, mode='str') -print(wm_extract) +## Installation +```bash +pip install blind-watermark ``` -Output: ->@guofei9987 开源万岁! - -### attacks on Watermarked Image - - -|attack method|image after attack|extracted watermark| -|--|--|--| -|Rotate 45 Degrees|![旋转攻击](docs/旋转攻击.jpg)|'@guofei9987 开源万岁!'| -|Random crop|![截屏攻击](docs/截屏攻击2_还原.jpg)|'@guofei9987 开源万岁!'| -|Masks| ![多遮挡攻击](docs/多遮挡攻击.jpg) |'@guofei9987 开源万岁!'| -|Vertical cut|![横向裁剪攻击](docs/横向裁剪攻击_填补.jpg)|'@guofei9987 开源万岁!'| -|Horizontal cut|![纵向裁剪攻击](docs/纵向裁剪攻击_填补.jpg)|'@guofei9987 开源万岁!'| -|Resize|![缩放攻击](docs/缩放攻击.jpg)|'@guofei9987 开源万岁!'| -|Pepper Noise|![椒盐攻击](docs/椒盐攻击.jpg)|'@guofei9987 开源万岁!'| -|Brightness 10% Down|![亮度攻击](docs/亮度攻击.jpg)|'@guofei9987 开源万岁!'| - +## Usage as a CLI Tool +```bash +# Embed watermark into an image: +blind_watermark --embed --pwd 1234 examples/pic/ori_img.jpeg "watermark text" examples/output/embedded.png - - - -### embed images - -embed watermark: -```python -from blind_watermark import WaterMark - -bwm1 = WaterMark(password_wm=1, password_img=1) -# read original image -bwm1.read_img('pic/ori_img.jpg') -# read watermark -bwm1.read_wm('pic/watermark.png') -# embed -bwm1.embed('output/embedded.png') +# Extract watermark from an image: +blind_watermark --extract --pwd 1234 --wm_shape 111 examples/output/embedded.png ``` +## Usage as a Python Library +See the [examples directory](/examples/) for more details. -Extract watermark: +**Example: Text Watermark** ```python -bwm1 = WaterMark(password_wm=1, password_img=1) -# notice that wm_shape is necessary -bwm1.extract(filename='output/embedded.png', wm_shape=(128, 128), out_wm_name='output/extracted.png', ) -``` - - -|attack method|image after attack|extracted watermark| -|--|--|--| -|Rotate 45 Degrees|![旋转攻击](docs/旋转攻击.jpg)|![](docs/旋转攻击_提取水印.png)| -|Random crop|![截屏攻击](docs/截屏攻击2_还原.jpg)|![多遮挡_提取水印](docs/多遮挡攻击_提取水印.png)| -|Mask| ![多遮挡攻击](docs/多遮挡攻击.jpg) |![多遮挡_提取水印](docs/多遮挡攻击_提取水印.png)| - - -### embed array of bits +from blind_watermark import WaterMark -See it [here](/examples/example_bit.py) +bwm = WaterMark(password_img=1, password_wm=1) +bwm.read_img('pic/ori_img.jpg') +bwm.read_wm('Hello World', mode='str') +bwm.embed('output/embedded.png') +# To extract, you need the length of the watermark bit string +len_wm = len(bwm.wm_bit) +print(f"Watermark length to use for extraction: {len_wm}") -As demo, we embed 6 bytes data: -```python -wm = [True, False, True, True, True, False] +wm_extract = bwm.extract('output/embedded.png', wm_shape=len_wm, mode='str') +print(f"Extracted watermark: {wm_extract}") ``` -Embed: +**Example: Image Watermark** ```python from blind_watermark import WaterMark -bwm1 = WaterMark(password_img=1, password_wm=1) -bwm1.read_ori_img('pic/ori_img.jpg') -bwm1.read_wm([True, False, True, True, True, False], mode='bit') -bwm1.embed('output/embedded.png') -``` +bwm = WaterMark(password_img=1, password_wm=1) +bwm.read_img('pic/ori_img.jpg') +bwm.read_wm('pic/watermark.png', mode='img') # Use a watermark image +bwm.embed('output/embedded_with_img_wm.png') -Extract: -```python -bwm1 = WaterMark(password_img=1, password_wm=1, wm_shape=6) -wm_extract = bwm1.extract('output/打上水印的图.png', mode='bit') -print(wm_extract) +# To extract, you need the shape of the watermark image (height, width) +wm_shape = (128, 128) # Example shape +bwm.extract('output/embedded_with_img_wm.png', wm_shape=wm_shape, out_wm_name='output/extracted_wm.png', mode='img') ``` -Notice that `wm_shape` (shape of watermark) is necessary - -The output `wm_extract` is an array of float. set a threshold such as 0.5. - - -# Concurrency - -```python -WaterMark(..., processes=None) -``` -- `processes` number of processes, can be integer. Default `None`, which means using all processes. - -## Related Project - -- text_blind_watermark (Embed message into text): [https://github.com/guofei9987/text_blind_watermark](https://github.com/guofei9987/text_blind_watermark) -- HideInfo(hide as image, hide as sounds, hide as text):[https://github.com/guofei9987/HideInfo](https://github.com/guofei9987/HideInfo) diff --git a/README_cn.md b/README_cn.md index 007b77f6..80919694 100644 --- a/README_cn.md +++ b/README_cn.md @@ -1,7 +1,6 @@ # blind-watermark -基于频域的数字盲水印 - +基于频域的数字盲水印。 [![PyPI](https://img.shields.io/pypi/v/blind_watermark)](https://pypi.org/project/blind_watermark/) [![Build Status](https://travis-ci.com/guofei9987/blind_watermark.svg?branch=master)](https://travis-ci.com/guofei9987/blind_watermark) @@ -9,165 +8,95 @@ [![License](https://img.shields.io/pypi/l/blind_watermark.svg)](https://github.com/guofei9987/blind_watermark/blob/master/LICENSE) ![Python](https://img.shields.io/badge/python->=3.5-green.svg) ![Platform](https://img.shields.io/badge/platform-windows%20|%20linux%20|%20macos-green.svg) -[![stars](https://img.shields.io/github/stars/guofei9987/blind_watermark.svg?style=social)](https://github.com/guofei9987/blind_watermark/) -[![fork](https://img.shields.io/github/forks/guofei9987/blind_watermark?style=social)](https://github.com/guofei9987/blind_watermark/fork) -[![Downloads](https://pepy.tech/badge/blind-watermark)](https://pepy.tech/project/blind-watermark) -[![Discussions](https://img.shields.io/badge/discussions-green.svg)](https://github.com/guofei9987/blind_watermark/discussions) - - **Documentation:** [https://BlindWatermark.github.io/blind_watermark/#/en/](https://BlindWatermark.github.io/blind_watermark/#/en/) -- **文档:** [https://BlindWatermark.github.io/blind_watermark/#/zh/](https://BlindWatermark.github.io/blind_watermark/#/zh/) -- **English readme** [README.md](README.md) -- **Source code:** [https://github.com/guofei9987/blind_watermark](https://github.com/guofei9987/blind_watermark) +- **中文文档:** [https://BlindWatermark.github.io/blind_watermark/#/zh/](https://BlindWatermark.github.io/blind_watermark/#/zh/) -# 安装 -```bash -pip install blind-watermark -``` - -或者安装最新开发版本 -```bach -git clone git@github.com:guofei9987/blind_watermark.git -cd blind_watermark -pip install . -``` +# 快速开始:Web应用 -# 如何使用 - -## 命令行中使用 - -```bash -# 嵌入水印: -blind_watermark --embed --pwd 1234 examples/pic/ori_img.jpeg "watermark text" examples/output/embedded.png -# 提取水印: -blind_watermark --extract --pwd 1234 --wm_shape 111 examples/output/embedded.png -``` +为了方便使用,本项目已集成了一个美观、简洁的Web操作界面。 +## 运行方式 +### 方式一:通过 Docker 运行 (推荐) +这是最简单、最推荐的运行方式,可以避免本机环境的各种问题。 -## Python 中使用 +1. **启动应用:** + 在项目根目录下,运行以下命令: + ```bash + docker-compose up -d + ``` + Docker将会自动构建镜像并启动服务。 -原图 + 水印 = 打上水印的图 +2. **访问应用:** + 打开浏览器,访问 `http://localhost:5891` 即可开始使用。 -![origin_image](docs/原图.jpeg) + '@guofei9987 开源万岁!' = ![打上水印的图](docs/打上水印的图.jpg) +3. **停止应用:** + 在终端中,按 `Ctrl+C`,然后运行以下命令来彻底清理容器: + ```bash + docker-compose down + ``` +### 方式二:在本地直接运行 +1. **安装依赖:** + ```bash + pip install -r requirements.txt + ``` +2. **启动服务:** + ```bash + python web_app.py + ``` +3. **访问应用:** + 打开浏览器,访问 `http://localhost:5891`。 -参考 [代码](/examples/example_str.py) +# 高级用法 (命令行与库) -嵌入水印 -```python -from blind_watermark import WaterMark - -bwm1 = WaterMark(password_img=1, password_wm=1) -bwm1.read_img('pic/ori_img.jpg') -wm = '@guofei9987 开源万岁!' -bwm1.read_wm(wm, mode='str') -bwm1.embed('output/embedded.png') -len_wm = len(bwm1.wm_bit) -print('Put down the length of wm_bit {len_wm}'.format(len_wm=len_wm)) +## 安装 +```bash +pip install blind-watermark ``` +## 在命令行中使用 +```bash +# 嵌入水印到图片: +blind_watermark --embed --pwd 1234 examples/pic/ori_img.jpeg "watermark text" examples/output/embedded.png -提取水印 -```python -bwm1 = WaterMark(password_img=1, password_wm=1) -wm_extract = bwm1.extract('output/embedded.png', wm_shape=len_wm, mode='str') -print(wm_extract) +# 从图片中提取水印: +blind_watermark --extract --pwd 1234 --wm_shape 111 examples/output/embedded.png ``` -Output: ->@guofei9987 开源万岁! - - -### 各种攻击后的效果 - -|攻击方式|攻击后的图片|提取的水印| -|--|--|--| -|旋转攻击45度|![旋转攻击](docs/旋转攻击.jpg)|'@guofei9987 开源万岁!'| -|随机截图|![截屏攻击](docs/截屏攻击2_还原.jpg)|'@guofei9987 开源万岁!'| -|多遮挡| ![多遮挡攻击](docs/多遮挡攻击.jpg) |'@guofei9987 开源万岁!'| -|纵向裁剪|![横向裁剪攻击](docs/横向裁剪攻击_填补.jpg)|'@guofei9987 开源万岁!'| -|横向裁剪|![纵向裁剪攻击](docs/纵向裁剪攻击_填补.jpg)|'@guofei9987 开源万岁!'| -|缩放攻击|![缩放攻击](docs/缩放攻击.jpg)|'@guofei9987 开源万岁!'| -|椒盐攻击|![椒盐攻击](docs/椒盐攻击.jpg)|'@guofei9987 开源万岁!'| -|亮度攻击|![亮度攻击](docs/亮度攻击.jpg)|'@guofei9987 开源万岁!'| +## 在 Python 中作为库使用 +更多用法请参考 [examples 目录](/examples/)。 - -### 嵌入图片 - -参考 [代码](/examples/example_str.py) - - -嵌入: +**示例:文本水印** ```python from blind_watermark import WaterMark -bwm1 = WaterMark(password_wm=1, password_img=1) -# read original image -bwm1.read_img('pic/ori_img.jpg') -# read watermark -bwm1.read_wm('pic/watermark.png') -# embed -bwm1.embed('output/embedded.png') -``` - -提取: -```python -bwm1 = WaterMark(password_wm=1, password_img=1) -# notice that wm_shape is necessary -bwm1.extract(filename='output/embedded.png', wm_shape=(128, 128), out_wm_name='output/extracted.png', ) -``` - -|攻击方式|攻击后的图片|提取的水印| -|--|--|--| -|旋转攻击45度|![旋转攻击](docs/旋转攻击.jpg)|![](docs/旋转攻击_提取水印.png)| -|随机截图|![截屏攻击](docs/截屏攻击2_还原.jpg)|![](docs/旋转攻击_提取水印.png)| -|多遮挡| ![多遮挡攻击](docs/多遮挡攻击.jpg) |![多遮挡_提取水印](docs/多遮挡攻击_提取水印.png)| - - - -### 隐水印还可以是二进制数据 - -参考 [代码](/examples/example_bit.py) +bwm = WaterMark(password_img=1, password_wm=1) +bwm.read_img('pic/ori_img.jpg') +bwm.read_wm('你好,世界', mode='str') +bwm.embed('output/embedded.png') +# 提取时,需要水印的比特长度 +len_wm = len(bwm.wm_bit) +print(f"用于提取的水印长度: {len_wm}") -作为 demo, 如果要嵌入是如下长度为6的二进制数据 -```python -wm = [True, False, True, True, True, False] +wm_extract = bwm.extract('output/embedded.png', wm_shape=len_wm, mode='str') +print(f"提取出的水印: {wm_extract}") ``` -嵌入水印 - +**示例:图片水印** ```python -# 除了嵌入图片,也可以嵌入比特类数据 from blind_watermark import WaterMark -bwm1 = WaterMark(password_img=1, password_wm=1) -bwm1.read_ori_img('pic/ori_img.jpg') -bwm1.read_wm([True, False, True, True, True, False], mode='bit') -bwm1.embed('output/打上水印的图.png') -``` - -解水印:(注意设定水印形状 `wm_shape`) -```python -bwm1 = WaterMark(password_img=1, password_wm=1, wm_shape=6) -wm_extract = bwm1.extract('output/打上水印的图.png', mode='bit') -print(wm_extract) -``` - -解出的水印是一个0~1之间的实数,方便用户自行卡阈值。如果水印信息量远小于图片可容纳量,偏差极小。 - -# 并行计算 +bwm = WaterMark(password_img=1, password_wm=1) +bwm.read_img('pic/ori_img.jpg') +bwm.read_wm('pic/watermark.png', mode='img') # 使用图片作为水印 +bwm.embed('output/embedded_with_img_wm.png') -```python -WaterMark(..., processes=None) +# 提取时,需要水印图片的尺寸 (height, width) +wm_shape = (128, 128) # 示例尺寸 +bwm.extract('output/embedded_with_img_wm.png', wm_shape=wm_shape, out_wm_name='output/extracted_wm.png', mode='img') ``` -- `processes`: 整数,指定线程数。默认为 `None`, 表示使用全部线程。 - - -## 相关项目 - -- text_blind_watermark (文本盲水印,把信息隐秘地打入文本): [https://github.com/guofei9987/text_blind_watermark](https://github.com/guofei9987/text_blind_watermark) -- HideInfo(藏物于图、藏物于音、藏图于文):[https://github.com/guofei9987/HideInfo](https://github.com/guofei9987/HideInfo) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..fb83ef8a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +services: + web: + build: . + ports: + - "5891:5891" + volumes: + - .:/app diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..4973c843 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel", "numpy"] +build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index 2bc42877..5dc551df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ numpy>=1.17.0 opencv-python setuptools PyWavelets +Flask diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 00000000..e98ef7ec --- /dev/null +++ b/templates/index.html @@ -0,0 +1,126 @@ + + + + + + + 盲水印工具 + + + +
+

盲水印工具

+ + {% if error %} +
+

{{ error }}

+
+ {% endif %} + + {% if result == 'embedded' %} +
+

水印嵌入成功!

+

水印形状为:{{ wm_shape }} (已自动为您填入下方提取表单)

+

点击此处下载带水印的图片

+
+ {% elif result == 'extracted' %} +
+

提取成功!

+ {% if wm_is_image %} +

提取出的水印图片:
Extracted Watermark

+ {% else %} +

提取出的水印文字为:{{ wm_content }}

+ {% endif %} +
+ {% endif %} + +

嵌入水印

+
+

选择图片

+ +

选择水印类型
+ 文本水印 + 图片水印 +

+ +
+

输入水印文字

+
+ + +

输入密码

+

+
+ +

提取水印

+
+

选择带水印的图片

+

输入水印形状

+

输入密码

+

+
+
+ + + + + diff --git a/web_app.py b/web_app.py new file mode 100644 index 00000000..426642cb --- /dev/null +++ b/web_app.py @@ -0,0 +1,120 @@ + +import os +import cv2 +from flask import Flask, request, render_template, send_from_directory +from blind_watermark import WaterMark + +app = Flask(__name__) +app.config['UPLOAD_FOLDER'] = 'uploads/' + +@app.route('/') +def index(): + return render_template('index.html') + +@app.route('/embed', methods=['POST']) +def embed(): + if 'file' not in request.files or not request.files['file'].filename: + return render_template('index.html', error="请选择一个图片文件。") + + file = request.files['file'] + password = request.form.get('password', '123456') + wm_type = request.form.get('wm_type', 'text') + + password_int = abs(hash(password)) % (2**32) + + # Save the cover image + filename = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) + file.save(filename) + + bwm = WaterMark(password_img=password_int, password_wm=password_int) + bwm.read_img(filename) + + wm_shape_str = "" + + try: + if wm_type == 'text': + wm_content = request.form.get('wm_content') + if not wm_content: + return render_template('index.html', error="请输入水印文字。") + bwm.read_wm(wm_content, mode='str') + wm_shape_str = str(len(bwm.wm_bit)) + elif wm_type == 'image': + if 'wm_file' not in request.files or not request.files['wm_file'].filename: + return render_template('index.html', error="请选择一个水印图片。") + wm_file = request.files['wm_file'] + wm_filename = os.path.join(app.config['UPLOAD_FOLDER'], 'wm_' + wm_file.filename) + wm_file.save(wm_filename) + bwm.read_wm(wm_filename, mode='img') + # Get shape from the actual watermark image + wm_img = cv2.imread(wm_filename, cv2.IMREAD_GRAYSCALE) + if wm_img is None: + return render_template('index.html', error="无法读取水印图片文件。") + wm_shape_str = f"{wm_img.shape[0]},{wm_img.shape[1]}" + + # Embed the watermark + embedded_filename = 'embedded_' + file.filename + embedded_filepath = os.path.join(app.config['UPLOAD_FOLDER'], embedded_filename) + bwm.embed(embedded_filepath) + + except AssertionError as e: + return render_template('index.html', error=f"嵌入失败:{e}") + except Exception as e: + return render_template('index.html', error=f"发生未知错误: {e}") + + return render_template('index.html', + result='embedded', + wm_shape=wm_shape_str, + download_url=f'/uploads/{embedded_filename}') + +@app.route('/extract', methods=['POST']) +def extract(): + if 'file' not in request.files or not request.files['file'].filename: + return render_template('index.html', error="请选择一个带水印的图片文件。") + + file = request.files['file'] + password = request.form.get('password', '123456') + wm_shape_str = request.form.get('wm_shape', '') + + if not wm_shape_str: + return render_template('index.html', error="请输入水印形状。") + + password_int = abs(hash(password)) % (2**32) + bwm = WaterMark(password_img=password_int, password_wm=password_int) + + # Save the watermarked image + filename = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) + file.save(filename) + + wm_content = None + wm_is_image = False + try: + if ',' in wm_shape_str: + # Image watermark + shape = tuple(map(int, wm_shape_str.split(','))) + mode = 'img' + wm_is_image = True + out_wm_name = os.path.join(app.config['UPLOAD_FOLDER'], 'extracted_' + file.filename) + bwm.extract(filename, wm_shape=shape, mode=mode, out_wm_name=out_wm_name) + wm_content = f'/uploads/{os.path.basename(out_wm_name)}' + else: + # Text watermark + shape = int(wm_shape_str) + mode = 'str' + wm_is_image = False + wm_content = bwm.extract(filename, wm_shape=shape, mode=mode) + + except Exception as e: + return render_template('index.html', error=f"提取失败,请检查密码或水印形状是否正确。错误信息: {e}") + + return render_template('index.html', + result='extracted', + wm_content=wm_content, + wm_is_image=wm_is_image) + +@app.route('/uploads/') +def uploaded_file(filename): + return send_from_directory(app.config['UPLOAD_FOLDER'], filename) + +if __name__ == '__main__': + os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) + app.run(debug=True, host='0.0.0.0', port=5891)