blind_watermark

Crates.ioblind_watermark
lib.rsblind_watermark
version0.1.2
created_at2025-12-04 01:15:34.866249+00
updated_at2025-12-05 08:24:52.901915+00
descriptionPicture blind watermarking.
homepagehttps://github.com/naganohara-yoshino/blind-watermark-rust
repositoryhttps://github.com/naganohara-yoshino/blind-watermark-rust
max_upload_size
id1965622
size6,003,914
長野原よしの (naganohara-yoshino)

documentation

https://docs.rs/blind_watermark

README

Blind Watermark

A Rust library and CLI tool for blind image watermarking using DWT (Discrete Wavelet Transform), DCT (Discrete Cosine Transform), and SVD (Singular Value Decomposition). The algorithm is originally implemented in python by Guo Fei at this repository. This is the rust rewritten and modified version.

Features

  • Blind Watermarking: Extract the watermark without needing the original image.
  • Robust Algorithm: Combines DWT, DCT, and SVD for embedding watermarks in the frequency domain.
  • High Performance: Developed in Rust, leveraging the faer crate for efficient matrix computations and rayon for multi-threading support.
  • Flexible: Supports embedding arbitrary binary data (e.g. Vec<u8>). String watermarking is natively supported.
  • Random Strategy: Supports randomized block selection for embedding watermarks, enhancing security.
  • CLI Tool: A command-line interface for easy embedding and extracting watermarks.

Original Image

Original

Processed Image

Processed

Installation

Pre-Compiled Binaries are available at GitHub Releases. If you want to install from source, use:

cargo install blind_watermark

Usage

CLI

You can get help by running:

blind_watermark --help

Library

Add this to your Cargo.toml:

[dependencies]
blind_watermark = "0.1"

Embedding a Watermark

use blind_watermark::prelude::*;

fn main() {
    let example = "example.jpg";
    let processed = "processed.png";
    let watermark = "こんにちは❗😊";
    let seed = Some(0);
    embed_watermark_string(example, processed, watermark, seed).unwrap();
}

OR

use bitvec::prelude::*;
use image::{DynamicImage, ImageReader, Rgba32FImage};

fn main() {
    // 1. Load the image using the `image` crate
    let img = ImageReader::open("input.jpg")
        .expect("Failed to open image")
        .decode()
        .expect("Failed to decode image")
        .into_rgba32f();

    // 2. Convert to YCbCrA format
    let ycbcr: YCrBrAMat = img.into();

    // 3. Define watermark (bits)
    let watermark = bits![u8, Lsb0; 0, 1, 0, 1, 1, 0, 1, 0]; // Example bits

    // 4. Configure watermark settings (Optional)
    let config = WatermarkConfigBuilder::default()
        .strength_1(36)
        .mode(WatermarkMode::Strategy(12345)) // Use random strategy with seed
        .build()
        .unwrap();

    // 5. Process pipeline: Padding -> DWT -> Cut Blocks -> Embed -> Assemble -> IDWT -> Remove Padding
    let processed = ycbcr
        .add_padding()
        .dwt()
        .cut()
        .embed_watermark_bits(watermark, &config)
        .assemble()
        .idwt()
        .remove_padding();

    // 6. Save the result
    let processed_image: Rgba32FImage = processed.into();
    let output_image: DynamicImage = processed_image.into();
    output_image.to_rgb8().save("watermarked.png").unwrap();
}

Extracting a Watermark

To extract the watermark, you only need the watermarked image and the length of the watermark.

use blind_watermark::prelude::*;

fn main() {
    let processed = "processed.png";
    let watermark_len = get_wm_len("こんにちは❗😊".as_bytes());
    let seed = Some(0);
    let extracted = extract_watermark_string(processed, watermark_len, seed).unwrap();
    println!("Extracted bits: {:?}", extracted);
}

OR

use bitvec::prelude::*;
use blind_watermark::prelude::*;
use image::ImageReader;

fn main() {
    // 1. Load the watermarked image
    let img = ImageReader::open("watermarked.png")
        .expect("Failed to open image")
        .decode()
        .expect("Failed to decode image")
        .into_rgba32f();

    let ycbcr: YCrBrAMat = img.into();

    // 2. Use the same config as embedding
    let config = WatermarkConfigBuilder::default()
        .strength_1(36)
        .mode(WatermarkMode::Strategy(12345))
        .build()
        .unwrap();

    let watermark_len = 8; // Length of the embedded watermark

    // 3. Process pipeline: Padding -> DWT -> Cut Blocks -> Extract
    let extracted_bits = ycbcr
        .add_padding()
        .dwt()
        .cut()
        .extract_watermark_bits(watermark_len, &config);

    println!("Extracted bits: {:?}", extracted_bits);

Algorithm Details

The library implements a hybrid DWT-DCT-SVD watermarking scheme:

  1. Preprocessing: The image is converted to YCbCr color space.
  2. DWT: A Discrete Wavelet Transform is applied to decompose the image into frequency subbands (LL, HL, LH, HH).
  3. Block Selection: The LL (Low-Low) subband is divided into 4x4 blocks.
  4. DCT & SVD: Each block undergoes Discrete Cosine Transform (DCT) followed by Singular Value Decomposition (SVD).
  5. Embedding: The watermark bits are embedded by quantizing the singular values of the blocks.
  6. Reconstruction: The inverse transformations (ISVD, IDCT, IDWT) are applied to generate the watermarked image.

This approach ensures that the watermark is embedded in the significant features of the image, providing robustness while maintaining visual quality.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Commit count: 0

cargo fmt