All Products
Search
Document Center

Object Storage Service:Blind watermarking

Last Updated:Mar 20, 2026

Embed invisible text into images stored in Object Storage Service (OSS) buckets without affecting visual quality. Use blind watermarking to protect your images and trace leaks—then extract the hidden text whenever you need it.

Use cases

  • Authentication and accountability: Determine whether your images have been stolen by malicious attackers.

  • Duplicate upload check: Determine whether an uploaded image already exists in a resource library.

  • Resource leak prevention: Obtain source information from the blind watermark embedded in a distributed image.

How it works

Blind watermarking is a two-step workflow:

  1. Add a watermark: Submit a synchronous request (x-oss-process) with the image/blindwatermark action. OSS embeds the watermark and saves the result as a new object.

  2. Parse a watermark: Submit an asynchronous request (x-oss-async-process) with the image/deblindwatermark action. OSS extracts the watermark text and delivers it via a Message Notification Service (MNS) message.

The embedded watermark survives a certain degree of attacks, including cropping, scaling, doodling, and color swapping.

Limitations

Review the following constraints before getting started:

ConstraintDetails
Supported formatsJPG, PNG, BMP, WebP, TIFF
Minimum image size80 x 80 pixels
Maximum image size10,000 x 10,000 pixels
Aspect ratioThe ratio of the shorter edge to the longer edge must be greater than 1:2
Unsupported imagesSolid black, solid white, and low-resolution images. Images smaller than 200 x 200 pixels may not produce reliable results
Watermark typeText only
Anonymous accessNot supported

Prerequisites

Before you begin, ensure that you have:

Add a blind watermark

Action: image/blindwatermark

Before passing the watermark text in the content parameter, encode it using URL-safe Base64:

  1. Encode the text in Base64.

  2. Replace + with - and / with _.

  3. Remove all trailing = characters.

You can use the watermark encoding tool to encode the text. For example, Alibaba Cloud Copyright encodes to 6Zi_6YeM5LqR54mI5p2D5omA5pyJ.

All examples below use x-oss-process to submit a synchronous watermarking request. The watermarked image is saved to the path specified by sys/saveas.

SDK for Java

SDK for Java 3.17.4 or later is required.

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.GenericResult;
import com.aliyun.oss.model.ProcessObjectRequest;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Formatter;

public class Demo {
    public static void main(String[] args) throws IOException {
        // Set the endpoint to match the region of your bucket.
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // Set the region ID corresponding to the endpoint. Example: cn-hangzhou.
        String region = "cn-hangzhou";
        // Load access credentials from environment variables.
        // Configure OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
        EnvironmentVariableCredentialsProvider credentialsProvider = new EnvironmentVariableCredentialsProvider();

        // Specify the bucket that contains the source image.
        String bucketName = "examplebucket";
        // Specify the source image path. Example: sourceDir/source.jpg.
        String sourceImage = "sourceDir/source.jpg";

        // Create an OSSClient instance using the V4 signature algorithm.
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();

        try {
            StringBuilder sbStyle = new StringBuilder();
            Formatter styleFormatter = new Formatter(sbStyle);

            // Encode the watermark text using URL-safe Base64 without padding.
            String content = "Copyright © Alibaba Cloud";
            String encodedContent = java.util.Base64.getUrlEncoder().withoutPadding()
                    .encodeToString(content.getBytes(StandardCharsets.UTF_8));
            String styleType = "image/blindwatermark,content_" + encodedContent;

            // Specify the output path for the watermarked image.
            String targetImage = "targetDir/target.jpg";
            styleFormatter.format("%s|sys/saveas,o_%s,b_%s",
                    styleType,
                    java.util.Base64.getUrlEncoder().withoutPadding()
                            .encodeToString(targetImage.getBytes(StandardCharsets.UTF_8)),
                    java.util.Base64.getUrlEncoder().withoutPadding()
                            .encodeToString(bucketName.getBytes(StandardCharsets.UTF_8)));

            ProcessObjectRequest request = new ProcessObjectRequest(bucketName, sourceImage, sbStyle.toString());
            GenericResult processResult = ossClient.processObject(request);

            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(processResult.getResponse().getContent(), StandardCharsets.UTF_8));
            StringBuilder responseContent = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                responseContent.append(line).append("\n");
            }
            reader.close();
            processResult.getResponse().getContent().close();
            System.out.println(responseContent.toString());
        } catch (OSSException oe) {
            System.err.println("Error Message: " + oe.getErrorMessage());
            System.err.println("Error Code: " + oe.getErrorCode());
            System.err.println("Request ID: " + oe.getRequestId());
        } catch (ClientException ce) {
            System.err.println("Error Message: " + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

SDK for Python

SDK for Python 2.18.4 or later is required.

# -*- coding: utf-8 -*-
import base64
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# Load access credentials from environment variables.
# Configure OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())

# Set the endpoint to match the region of your bucket.
endpoint = 'https://oss-cn-hangzhou.aliyuncs.com'

# Specify the bucket that contains the source image.
source_bucket_name = 'examplebucket'
# Specify the bucket to store the watermarked image. Must be in the same region as the source bucket.
target_bucket_name = 'examplebucket-target'
# Specify the source image path. Example: sourceDir/source.jpg.
source_image_name = 'sourceDir/source.jpg'

bucket = oss2.Bucket(auth, endpoint, source_bucket_name)

# Encode the watermark text using URL-safe Base64.
content = 'Copyright © Alibaba Cloud'
style = "image/blindwatermark,content_{0}".format(
    oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(content))))

# Specify the output path for the watermarked image.
target_image_name = 'targetDir/target.jpg'
process = "{0}|sys/saveas,o_{1},b_{2}".format(
    style,
    oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(target_image_name))),
    oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(target_bucket_name))))

result = bucket.process_object(source_image_name, process)
print(result)

SDK for Go

SDK for Go 3.0.2 or later is required.

package main

import (
    "encoding/base64"
    "fmt"
    "os"
    "strings"
    "github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
    // Load access credentials from environment variables.
    // Configure OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
    provider, err := oss.NewEnvironmentVariableCredentialsProvider()
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(-1)
    }

    // Set the endpoint to match the region of your bucket.
    client, err := oss.New("https://oss-cn-hangzhou.aliyuncs.com", "", "",
        oss.SetCredentialsProvider(&provider))
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(-1)
    }

    // Specify the bucket that contains the source image.
    bucketName := "examplebucket"
    bucket, err := client.Bucket(bucketName)
    if err != nil {
        handleError(err)
        return
    }

    // Specify the source image path. Example: sourceDir/source.jpg.
    sourceImageName := "sourceDir/source.jpg"
    // Specify the bucket to store the watermarked image. Must be in the same region.
    targetBucketName := "examplebucket-target"
    // Specify the output path for the watermarked image.
    targetImageName := "targetDir/target.jpg"

    // Encode the watermark text using URL-safe Base64 without padding.
    content := "Copyright © Alibaba Cloud"
    encodedContent := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(content)), "=")
    style := fmt.Sprintf("image/blindwatermark,content_%s", encodedContent)

    encodedImageName := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(targetImageName)), "=")
    encodedBucketName := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(targetBucketName)), "=")
    process := fmt.Sprintf("%s|sys/saveas,o_%s,b_%s", style, encodedImageName, encodedBucketName)

    result, err := bucket.ProcessObject(sourceImageName, process)
    if err != nil {
        handleError(err)
    } else {
        fmt.Println(result)
    }
}

func handleError(err error) {
    fmt.Println("Error:", err)
    os.Exit(-1)
}

SDK for PHP

SDK for PHP 2.7.0 or later is required.

<?php
if (is_file(__DIR__ . '/../autoload.php')) {
    require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
    require_once __DIR__ . '/../vendor/autoload.php';
}

use OSS\OssClient;
use OSS\Core\OssException;

// Load access credentials from environment variables.
// Configure OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
$accessKeyId = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");

// Set the endpoint to match the region of your bucket.
$endpoint = "https://oss-cn-hangzhou.aliyuncs.com";

// Specify the bucket that contains the source image.
$bucket = "examplebucket";
// Specify the source image path. Example: sourceDir/source.jpg.
$object = "sourceDir/source.jpg";
// Specify the output path for the watermarked image.
$save_object = "targetDir/target.jpg";

function base64url_encode($data)
{
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

try {
    $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false);

    // Encode the watermark text using URL-safe Base64 without padding.
    $content = "Copyright © Alibaba Cloud";
    $encodedContent = base64url_encode($content);
    $style = "image/blindwatermark,content_$encodedContent";

    $process = $style .
               '|sys/saveas,' .
               'o_' . base64url_encode($save_object) .
               ',b_' . base64url_encode($bucket);

    $result = $ossClient->processObject($bucket, $object, $process);
    print_r($result);
} catch (OssException $e) {
    echo "Error: " . $e->getMessage();
}
?>

For other languages, see Save As.

REST API

Use x-oss-process in a PostObject request. Include parameters in the request body. To calculate the Authorization header, see Signature V4 (recommended).

The following example embeds the text "Alibaba Cloud Copyright" with low strength and quality 90, then saves the result to oss://image-demo/outobjprefix.jpg:

POST /example.jpg?x-oss-process HTTP/1.1
Host: image-demo.oss-cn-hangzhou.aliyuncs.com
Date: Fri, 28 Oct 2022 06:40:10 GMT
Authorization: SignatureValue

x-oss-process=image/blindwatermark,content_6Zi_6YeM5LqR54mI5p2D5omA5pyJ,s_low,q_90|sys/saveas,b_aW1hZ2UtZGVtbw,o_b3V0b2JqcHJlZml4LmpwZw

Parse a blind watermark

Action: image/deblindwatermark

All examples below use x-oss-async-process to submit an asynchronous parsing request. The extracted watermark text is delivered in an MNS message to the specified topic.

Important

The bucket containing the watermarked image must be in the same region and bound to the same IMM project that was used when the watermark was added.

SDK for Java

SDK for Java 3.17.4 or later is required.

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.AsyncProcessObjectRequest;
import com.aliyun.oss.model.AsyncProcessObjectResult;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Demo {
    public static void main(String[] args) throws IOException {
        // Set the endpoint to match the region of your bucket.
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // Set the region ID corresponding to the endpoint. Example: cn-hangzhou.
        String region = "cn-hangzhou";
        // Load access credentials from environment variables.
        EnvironmentVariableCredentialsProvider credentialsProvider = new EnvironmentVariableCredentialsProvider();

        // Specify the bucket that contains the watermarked image.
        String bucketName = "examplebucket";
        // Specify the watermarked image path.
        String sourceKey = "targetDir/target.jpg";
        // Specify the MNS topic to receive the extraction result.
        String topic = "imm-blindwatermark-test";

        // Create an OSSClient instance using the V4 signature algorithm.
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();

        try {
            // Extract the watermark text from the image.
            String style = "image/deblindwatermark,s_low,t_text";
            String encodedTopic = Base64.getUrlEncoder().withoutPadding()
                    .encodeToString(topic.getBytes(StandardCharsets.UTF_8));
            String process = String.format("%s|sys/notify,topic_%s", style, encodedTopic);

            AsyncProcessObjectRequest request = new AsyncProcessObjectRequest(bucketName, sourceKey, process);
            AsyncProcessObjectResult result = ossClient.asyncProcessObject(request);

            System.out.println(result.getRequestId()); // OSS request ID for tracking
            System.out.println(result.getTaskId());    // Task ID for the asynchronous job
        } catch (OSSException oe) {
            System.err.println("Error Message: " + oe.getErrorMessage());
            System.err.println("Error Code: " + oe.getErrorCode());
            System.err.println("Request ID: " + oe.getRequestId());
        } catch (ClientException ce) {
            System.err.println("Error Message: " + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

SDK for Python

SDK for Python 2.18.4 or later is required.

# -*- coding: utf-8 -*-
import base64
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# Set the endpoint to match the region of your bucket.
endpoint = 'https://oss-cn-hangzhou.aliyuncs.com'
# Load access credentials from environment variables.
# Configure OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())

# Specify the bucket that contains the watermarked image.
bucket_name = 'examplebucket'
# Specify the watermarked image path.
source_key = 'targetDir/target.jpg'
# Specify the MNS topic to receive the extraction result.
topic = 'imm-blindwatermark-test'

bucket = oss2.Bucket(auth, endpoint, bucket_name)

# Extract the watermark text from the image.
style = 'image/deblindwatermark,s_low,t_text'
process = "{0}|sys/notify,topic_{1}".format(
    style,
    oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(topic))).replace('=', ''))

result = bucket.async_process_object(source_key, process)
print(result.request_id)  # OSS request ID for tracking
print(result.task_id)     # Task ID for the asynchronous job

SDK for Go

SDK for Go 3.0.2 or later is required.

package main

import (
    "encoding/base64"
    "fmt"
    "os"
    "strings"
    "github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
    // Load access credentials from environment variables.
    // Configure OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
    provider, err := oss.NewEnvironmentVariableCredentialsProvider()
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(-1)
    }

    // Set the endpoint to match the region of your bucket.
    endpoint := "https://oss-cn-hangzhou.aliyuncs.com"
    client, err := oss.New(endpoint, "", "", oss.SetCredentialsProvider(provider))
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(-1)
    }

    // Specify the bucket that contains the watermarked image.
    bucketName := "examplebucket"
    bucket, err := client.Bucket(bucketName)
    if err != nil {
        handleError(err)
        return
    }

    // Specify the watermarked image path.
    sourceKey := "targetDir/target.jpg"
    // Specify the MNS topic to receive the extraction result.
    topic := "imm-blindwatermark-test"

    // Extract the watermark text from the image.
    style := "image/deblindwatermark,s_low,t_text"
    encodedTopic := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(topic)), "=")
    process := fmt.Sprintf("%s|sys/notify,topic_%s", style, encodedTopic)

    result, err := bucket.AsyncProcessObject(sourceKey, process)
    if err != nil {
        handleError(err)
    } else {
        fmt.Println(result.RequestID) // OSS request ID for tracking
        fmt.Println(result.TaskId)   // Task ID for the asynchronous job
    }
}

func handleError(err error) {
    fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    os.Exit(-1)
}

SDK for PHP

SDK for PHP 2.7.0 or later is required.

<?php
if (is_file(__DIR__ . '/../autoload.php')) {
    require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
    require_once __DIR__ . '/../vendor/autoload.php';
}

// Load access credentials from environment variables.
// Configure OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET before running this code.
$accessKeyId = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");

// Set the endpoint to match the region of your bucket.
$endpoint = 'https://oss-cn-hangzhou.aliyuncs.com';

// Specify the bucket that contains the watermarked image.
$bucketName = 'examplebucket';
// Specify the watermarked image path.
$sourceKey = 'targetDir/target.jpg';

function base64_url_encode($input) {
    return strtr(base64_encode($input), '+/', '-_');
}

try {
    $client = new OssClient($accessKeyId, $accessKeySecret, $endpoint);

    // Specify the MNS topic to receive the extraction result.
    $topic = 'imm-blindwatermark-test';

    // Extract the watermark text from the image.
    $style = 'image/deblindwatermark,s_low,t_text';
    $encodedTopic = str_replace('=', '', base64_url_encode($topic));
    $process = sprintf('%s|sys/notify,topic_%s', $style, $encodedTopic);

    $result = $client->asyncProcessObject($bucketName, $sourceKey, $process);

    $decodedResult = json_decode($result, true);
    echo "Request ID: " . $decodedResult['RequestId'] . "\n"; // OSS request ID for tracking
    echo "Task ID: " . $decodedResult['TaskId'] . "\n";      // Task ID for the asynchronous job
} catch (OssException $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>

For other languages, see Asynchronous processing.

REST API

Use x-oss-async-process in a PostObject request. Include parameters in the request body.

The following example parses the watermark from outobjprefix.jpg and delivers the result to the MNS topic doc-images:

POST /outobjprefix.jpg?x-oss-async-process HTTP/1.1
Host: image-demo.oss-cn-hangzhou.aliyuncs.com
Date: Fri, 28 Oct 2022 06:40:10 GMT
Authorization: SignatureValue

x-oss-async-process=image/deblindwatermark|sys/notify,topic_ZG9jLWltYWdlcw

Parameters

Add a blind watermark

Action: image/blindwatermark

ParameterRequiredDescriptionValid values
contentNoURL-safe Base64-encoded watermark text. The original text can be up to 256 characters.Up to 256 characters before encoding
sNoWatermark strength. Higher strength improves attack resistance but causes more visible distortion.low (default), medium, high
qNoOutput image quality. Higher quality means a larger file size and better watermark parsing accuracy. Applies to JPG images only.70-100. Default: 90

The sys/saveas parameter is required to save the watermarked image. For details, see Save an image to a specified location.

Parse a blind watermark

Action: image/deblindwatermark

ParameterRequiredDescriptionValid values
sNoWatermark extraction level. Higher levels take longer but produce better results.low (default), medium, high
tNoType of the embedded watermark.text

The sys/notify parameter is required to receive the extraction result. For details, see Notifications.

What's next

  • Save As - Learn how to use sys/saveas to save processed images to a specific location

  • Notifications - Learn how to configure MNS notifications to receive watermark extraction results

  • Asynchronous processing - Learn how to use x-oss-async-process with other OSS SDKs