All Products
Search
Document Center

Object Storage Service:Blind watermarking

Last Updated:May 22, 2025

Blind watermarking allows you to add blind text watermarks to images stored in Object Storage Service (OSS) buckets and decode these blind watermarks.

Overview

Blind watermarking is a watermarking technique that allows you to embed invisible text information into images. If you want to obtain the watermark content, you can use the image/deblindwatermark action to decode watermarks. The watermark content can be correctly decoded even if the image is under a certain degree of attack, such as cropping, resizing, doodling, and color change.

Scenarios

  • Proof of ownership and legal evidence: Blind watermarks can be used to prove ownership in case of ownership disputes.

  • Detection for duplicate images: Blind watermarks can be used to detect whether an image to upload already exists in the resource library.

  • Resource leak prevention: Blind watermarks can be used to trace the source that distributes unauthorized images. This protects image copyrights.

Usage

  • Add a blind watermark by using the x-oss-process parameter for synchronous processing. You need to save the watermarked image as a new image.

  • Decode a blind watermark by using the x-oss-async-process parameter for asynchronous processing. The text watermark obtained from the decoding operation is included in the asynchronous message.

Prerequisites

  • IMM is activated.

  • A bucket is bound to the IMM project. For information about how to bind an OSS bucket to an IMM project in the OSS console, see Get started. For information about how to bind a bucket to an IMM project by using the IMM API, see AttachOSSBucket.

  • For blind watermark decoding, the bucket containing the watermarked image is in the same region as the source image and is bound to the IMM project used for watermarking the image.

Add a blind watermark

Action: image/blindwatermark

Steps

  1. Encode watermark-related parameters in Base64.

  2. Use the following instructions to process special characters in the Base64-encoded content:

    • Replace plus signs (+) with hyphens (-).

    • Replace forward slashes (/) with underscores (_).

    • Omit all equal signs (=) at the end of the Base64-encoded content.

Java

OSS SDK for Java V3.17.4 or later is required.

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
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 {
        // Specify the endpoint of the region. In this example, the endpoint of the China (Hangzhou) region is used. 
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";

        // We recommend that you do not hardcode access credentials in the project code. Otherwise, access credentials may be leaked, which compromises the security of all resources in your account. 
        // In this example, access credentials are obtained from environment variables. Before you run the sample code, make sure that the environment variables are configured. 
        EnvironmentVariableCredentialsProvider credentialsProvider = new EnvironmentVariableCredentialsProvider();

        // Specify the name of the bucket in which the source image is stored. Example: examplebucket. 
        String bucketName = "examplebucket";

        // Specify the name of the source image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg.
        String sourceImage = "sourceDir/source.jpg";

        // Create an OSSClient instance. 
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);

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

            // Specify the watermark content.
            String content = "Copyrighted by Alibaba Cloud";
            // Add the blind watermark.
            byte[] encodedContentBytes = java.util.Base64.getEncoder().encode(content.getBytes(StandardCharsets.UTF_8));
            String encodedContentStr = new String(encodedContentBytes, StandardCharsets.UTF_8);
            String styleType = "image/blindwatermark,content_" + encodedContentStr;

            // Specify the name of the processed image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg. 
            String targetImage = "targetDir/target.jpg";
            styleFormatter.format("%s|sys/saveas,o_%s,b_%s",
                    styleType,
                    java.util.Base64.getEncoder().encodeToString(targetImage.getBytes(StandardCharsets.UTF_8)),
                    java.util.Base64.getEncoder().encodeToString(bucketName.getBytes(StandardCharsets.UTF_8)));

            System.out.println(sbStyle.toString());
            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("Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.");
            System.err.println("Error Message:" + oe.getErrorMessage());
            System.err.println("Error Code:" + oe.getErrorCode());
            System.err.println("Request ID:" + oe.getRequestId());
            System.err.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.err.println("Caught an ClientException, which means the client encountered a serious internal problem while trying to communicate with OSS, such as not being able to access the network.");
            System.err.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

Python

OSS SDK for Python V2.18.4 or later is required.

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

# Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
endpoint = 'https://oss-cn-hangzhou.aliyuncs.com'
# Specify the name of the bucket in which the source image is stored. 
source_bucket_name = 'source_bucket_name'
# Specify the name of the bucket in which you want to save the output image. The bucket must be within the same region as the bucket in which the source image is stored. 
taget_bucket_name = 'taget_bucket_name'
# Specify the name of the source image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg. 
source_image_name = 'sourceDir/source.jpg'

# Create a bucket instance. You must use the bucket instance to call all object-related operations. 
bucket = oss2.Bucket(auth, endpoint, source_bucket_name)

# Specify the watermark content.
content = 'Copyrighted by Alibaba Cloud'

# Add a blind watermark.
style = "image/blindwatermark,content_{0}".format(oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(content))))

# Specify the name of the output image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg. 
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(taget_bucket_name))))
result = bucket.process_object(source_image_name, process)
print(result)

Go

OSS SDK for Go V3.0.2 or later is required.

package main

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

func main() {
	// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(-1)
	}

	// Create an OSSClient instance. 
	// Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.  
	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 name of the bucket in which the source image is stored. 
	bucketName := "srcbucket"
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		handleError(err)
		return
	}

	// Specify the name of the source image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg. 
	sourceImageName := "sourceDir/source.jpg"

	// Specify the name of the destination bucket. The destination bucket must be located in the same region as the source bucket. 
	targetBucketName := "destbucket"

	// Specify the name of the processed image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg. 
	targetImageName := "targetDir/target.jpg"

	// Specify the watermark content.
	content := "Copyrighted by Alibaba Cloud"

	// Add the blind watermark.
	encodedContent := base64.URLEncoding.EncodeToString([]byte(content))
	style := fmt.Sprintf("image/blindwatermark,content_%s", encodedContent)

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

	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)
}

PHP

OSS SDK for PHP V2.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;

// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
$accessKeyId = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");

// Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
$endpoint = "https://oss-cn-hangzhou.aliyuncs.com";

// Specify the name of the bucket in which the source image is stored. Example: examplebucket. 
$bucket = "examplebucket";

// Specify the name of the source image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg. 
$object = "sourceDir/source.jpg";

// Specify the name of the processed image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg. 
$save_object = "targetDir/target.jpg";

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

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

    // If the source image does not exist in the source bucket, you must upload the image to the source bucket. 
    // $ossClient->uploadFile($bucket, $object, "D:\\localpath\\exampleobject.jpg");

    $content = "Copyrighted by Alibaba Cloud";
    // Add the blind watermark.
    $encodedContent = base64url_encode($content);
    $style = "image/blindwatermark,content_$encodedContent";

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

    // Save the processed image as example-new.png to the current bucket. 
    $result = $ossClient->processObject($bucket, $object, $process);
    // Display the result. 
    print_r($result);
} catch (OssException $e) {
    echo "Error: " . $e->getMessage();
}
?>

For information about how to use SDKs for other languages, see sys/saveas.

Decode a blind watermark

Action: image/deblindwatermark

Java

OSS SDK for Java V3.17.4 or later is required.

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.model.AsyncProcessObjectRequest;
import com.aliyun.oss.model.GenericResult;

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

public class Demo {
    public static void main(String[] args) throws IOException {
        // Specify the endpoint of the region. In this example, the endpoint of the China (Hangzhou) region is used. 
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";

        // We recommend that you do not hardcode access credentials in the project code. Otherwise, access credentials may be leaked, which compromises the security of all resources in your account. 
        // In this example, access credentials are obtained from environment variables. Before you run the sample code, make sure that the environment variables are configured. 
        EnvironmentVariableCredentialsProvider credentialsProvider = new EnvironmentVariableCredentialsProvider();

        // Specify the name of the bucket in which the source image is stored. Example: examplebucket. 
        String bucketName = "target_bucket_name";

        // Specify the name of the source image. If the image is not stored in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg.
        String sourceKey = "targetDir/target.jpg";

        // The Simple Message Queue (SMQ) topic.
        String topic = "imm-blindwatermark-test";

        // Create an OSSClient instance. 
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);

        try {
            // Extract the watermark from the specified 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);
            
            // Call the asynchronous processing operation.
            GenericResult result = ossClient.asyncProcessObject(request);
            System.out.println(result.getRequestId());

        } catch (OSSException oe) {
            System.err.println("Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.");
            System.err.println("Error Message:" + oe.getErrorMessage());
            System.err.println("Error Code:" + oe.getErrorCode());
            System.err.println("Request ID:" + oe.getRequestId());
            System.err.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.err.println("Caught an ClientException, which means the client encountered a serious internal problem while trying to communicate with OSS, such as not being able to access the network.");
            System.err.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

Python

OSS SDK for Python V2.18.4 or later is required.

# -*- coding: utf-8 -*-
import base64
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
# Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
endpoint = 'https://oss-cn-hangzhou.aliyuncs.com'
# Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# Specify the name of the bucket that contains the watermarked image. 
bucket_name = 'taget_bucket_name'
# Specify the name of the watermarked image. 
source_key = 'targetDir/target.jpg'
# Specify the Simple Message Queue (SMQ) topic. 
topic = 'imm-blindwatermark-test'

# Create a bucket instance. You must use the bucket instance to call all object-related operations. 
bucket = oss2.Bucket(auth, endpoint, bucket_name)

# Extract the watermark content from the specified 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('=', ''))

# Call the asynchronous processing operation. 
result = bucket.async_process_object(source_key, process)
print(result.request_id)

Go

OSS SDK for Go V3.0.2 or later is required.

package main

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

func main() {
    // Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
    provider, err := oss.NewEnvironmentVariableCredentialsProvider()
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(-1)
    }

    // Create an OSSClient instance. 
    endpoint := "https://oss-cn-hangzhou.aliyuncs.com"
    client, err := oss.New(endpoint, "", "", oss.WithCredentialsProvider(provider))
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(-1)
    }

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

    // Specify the name of the watermarked image. 
    sourceKey := "targetDir/target.jpg"

    // Specify the Simple Message Queue (SMQ) topic. 
    topic := "imm-blindwatermark-test"

    // Extract the watermark content from the specified 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)

    // Call the media processing operation. 
    result, err := bucket.AsyncProcessObject(sourceKey, process)
    if err != nil {
        handleError(err)
    } else {
        fmt.Println(result.RequestID)
    }
}

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

PHP

OSS SDK for PHP V2.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;

// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
$accessKeyId = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");

// Specify the endpoint of the region. In this example, the endpoint of the China (Hangzhou) region is used. 
$endpoint = 'https://oss-cn-hangzhou.aliyuncs.com';

// Specify the name of the bucket that contains the watermarked image. 
$bucketName = 'target_bucket_name';

// Specify the name of the watermarked image. 
$sourceKey = 'targetDir/target.jpg';

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

try {
    // Create an OSSClient instance. 
    $client = new OssClient($accessKeyId, $accessKeySecret, $endpoint);

    // Specify the Simple Message Queue (SMQ) topic. 
    $topic = 'imm-blindwatermark-test';

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

    // Call the media processing operation. 
    $result = $client->asyncProcessObject($bucketName, $sourceKey, $process);
    echo "Request ID: " . $result['request_id'] . "\n";
} catch (OssException $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>

For information about how to use SDKs for other languages, see Asynchronous processing.

Parameters

Blind watermarking

Action: image/blindwatermark

Parameter

Required

Description

Value

content

No

The Base-64 encoded text watermark. The value must be a Base64-encoded, URL-safe string. You can use the encoding tool to encode the watermark content. For example, if the text watermark is Copyrighted by Alibaba Cloud, enter the string Q29weXJpZ2h0ZWQgYnkgQWxpYmFiYSBDbG91ZA as the value of this parameter.

The watermark string before Base64 encoding can be up to 256 characters in length.

s

No

The watermark strength level. A higher strength level indicates higher attack resistance but a higher distortion rate.

  • low (default): low watermark strength level.

  • medium: medium watermark strength level.

  • high: high watermark strength level.

q

No

The storage quality of the output image that carries the watermark. A higher quality indicates a larger image size and a higher watermark decoding quality.

Note

This parameter takes effect only if the input image format is JPG.

Default value: 90. Valid values: 70 to 100.

The sys/saveas parameter is required when you add blind watermarks to images. For more information, see sys/saveas.

Decode a blind watermark

Action: image/deblindwatermark

Parameter

Required

Description

Value

s

No

The level of watermark extraction. A higher level indicates a longer time and a higher quality.

  • low (default): low watermark extraction level.

  • medium: medium watermark extraction level.

  • high: high watermark extraction level.

t

No

The type of the watermark.

Text: text watermark.

The notify parameter can be used when you decode blind watermarks. For more information, see Use the notification feature.

Use the OSS API

If your business requires a high level of customization, you can directly call RESTful APIs. To directly call an API, you must include the signature calculation in your code. For more information about how to calculate the Authorization header, see (Recommended) Include a V4 signature in the Authorization header. Blind watermarking and blind watermark decoding initiate POST requests, in which request parameters must be placed in the request body.

Add a blind watermark

You can process images by specifying the x-oss-process parameter in the PostObject request. For more information, see PostObject.

Example

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

// The watermark content is the "Copyrighted by Alibaba Cloud" text. The watermark strength level is low. The quality of the output image is 90. The watermark is saved as oss://image-demo/outobjprefix.jpg.
x-oss-process=image/blindwatermark,content_Q29weXJpZ2h0ZWQgYnkgQWxpYmFiYSBDbG91ZA,s_low,q_90|sys/saveas,b_aW1hZ2UtZGVtbw,o_b3V0b2JqcHJlZml4LmpwZw

Decode a blind watermark

You can extract a blind watermark from an image by adding watermark decoding parameters to the PostObject operation. For more information, see PostObject.

Example

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

// Decode the watermark added in the previous step. The topic is doc-images. 
x-oss-async-process=image/deblindwatermark|sys/notify,topic_ZG9jLWltYWdlcw

Usage notes

  • Blind watermarking supports only JPG, PNG, BMP, WebP, and TIFF images.

  • The minimum image width and height are 80 pixels. The maximum image width and height are 10,000 pixels.

  • The ratio of the short edge to the long edge must be greater than 1:2.

  • Blind watermarking does not support pure-white or pure-black images, or images with a too low resolution (for example, those smaller than 200 × 200 pixels).

  • Only text watermarks can be added to images.