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:
Add a watermark: Submit a synchronous request (
x-oss-process) with theimage/blindwatermarkaction. OSS embeds the watermark and saves the result as a new object.Parse a watermark: Submit an asynchronous request (
x-oss-async-process) with theimage/deblindwatermarkaction. 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:
| Constraint | Details |
|---|---|
| Supported formats | JPG, PNG, BMP, WebP, TIFF |
| Minimum image size | 80 x 80 pixels |
| Maximum image size | 10,000 x 10,000 pixels |
| Aspect ratio | The ratio of the shorter edge to the longer edge must be greater than 1:2 |
| Unsupported images | Solid black, solid white, and low-resolution images. Images smaller than 200 x 200 pixels may not produce reliable results |
| Watermark type | Text only |
| Anonymous access | Not supported |
Prerequisites
Before you begin, ensure that you have:
An IMM project created and bound to your OSS bucket. To bind using the OSS console, see Quick Start. To bind using an API, see AttachOSSBucket
When parsing a watermark: the image's bucket must be in the same region and bound to the same IMM project that was used when the watermark was added
Add a blind watermark
Action: image/blindwatermark
Before passing the watermark text in the content parameter, encode it using URL-safe Base64:
Encode the text in Base64.
Replace
+with-and/with_.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_b3V0b2JqcHJlZml4LmpwZwParse 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.
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 jobSDK 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_ZG9jLWltYWdlcwParameters
Add a blind watermark
Action: image/blindwatermark
| Parameter | Required | Description | Valid values |
|---|---|---|---|
content | No | URL-safe Base64-encoded watermark text. The original text can be up to 256 characters. | Up to 256 characters before encoding |
s | No | Watermark strength. Higher strength improves attack resistance but causes more visible distortion. | low (default), medium, high |
q | No | Output 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
| Parameter | Required | Description | Valid values |
|---|---|---|---|
s | No | Watermark extraction level. Higher levels take longer but produce better results. | low (default), medium, high |
t | No | Type 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/saveasto save processed images to a specific locationNotifications - Learn how to configure MNS notifications to receive watermark extraction results
Asynchronous processing - Learn how to use
x-oss-async-processwith other OSS SDKs