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
Encode watermark-related parameters in Base64.
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 | 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. |
|
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. |
|
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.