> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Image analysis


The Cloudinary AI Content Analysis add-on provides three image analysis capabilities: image quality analysis, watermark detection, and AI-based image captioning.

## Image quality analysis

You can analyze the quality of an image using the Image Quality Analysis (IQA) model by setting the `detection` parameter to `iqa` when calling Cloudinary's [upload](image_upload_api_reference#upload) or [update](admin_api#update_details_of_an_existing_resource) methods.

A quality score from 0 to 1 is returned in the `score` attribute of the response, and a general quality estimation of `low`, `medium`, or `high` is returned in the `quality` attribute.

For example, invoking the `iqa` model while uploading `winter_fashion.jpg`:

```multi
|ruby
Cloudinary::Uploader.upload("winter_fashion.jpg", 
  detection: "iqa")

|php_2
$cloudinary->uploadApi()->upload("winter_fashion.jpg", 
  ["detection" => "iqa"]);

|python
cloudinary.uploader.upload("winter_fashion.jpg",
  detection = "iqa")

|nodejs
cloudinary.v2.uploader
.upload("winter_fashion.jpg", 
  { detection: "iqa" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("winter_fashion.jpg", ObjectUtils.asMap(
  "detection", "iqa"));

|csharp
var uploadParams = new ImageUploadParams() 
{
  File = new FileDescription(@"winter_fashion.jpg"),
  Detection = "iqa"
};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "winter_fashion.jpg", uploader.UploadParams{
		Detection: "iqa"})

|android
MediaManager.get().upload("winter_fashion.jpg")
  .option("detection", "iqa").dispatch();

|swift
let params = CLDUploadRequestParams().setDetection("iqa")
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "winter_fashion.jpg", params: params) 

|cli
cld uploader upload "winter_fashion.jpg" detection="iqa"

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST -F 'file=@/path/to/winter_fashion.jpg' -F 'detection=iqa' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'
```

The response includes the `iqa-analysis` field:

```json
  "info": {
    "detection": {
      "object_detection": {
        "status": "complete",
        "data": {
          "iqa": {
            "model_name": "iqa",
            "model_version": 1,
            "schema_version": 1,
            "tags": {
              "iqa-analysis": [
                {
                  "attributes": {
                    "quality": "medium",
                    "score": 0.4521875
                  },
                  "categories": [],
                  "confidence": 1.0
                }
              ]
            }
          }
        }
      }
    }
  },
```

> **NOTES**:
>
> * You can also use [asynchronous handling](cloudinary_ai_content_analysis_automatic_tagging#asynchronous_handling) as described for automatic image tagging.

> * Learn about other ways to perform [image quality analysis](image_quality_analysis).

## Watermark detection

You can detect watermarks in images by setting the `detection` parameter to `watermark-detection` when calling Cloudinary's [upload](image_upload_api_reference#upload) or [update](admin_api#update_details_of_an_existing_resource) methods. The response can be one of the following: `banner` (see [Banners](#banners)), `watermark` (see [Watermarks](#watermarks)), or, if neither of these are detected, `clean`.

> **NOTE**: You can add the tags, `banner`, `watermark` or `clean` by setting the `auto_tagging` parameter, as described in [Adding tags to images](cloudinary_ai_content_analysis_automatic_tagging#adding_tags_to_images) and you can also use [asynchronous handling](cloudinary_ai_content_analysis_automatic_tagging#asynchronous_handling).

### Banners

If the image contains an opaque text/logo layer with a semi-transparent background it is likely that the image will be flagged as containing a **banner**.

For example, uploading the following image, requesting `watermark-detection`, the response shows 99% confidence that it contains a banner: 

![Example of a banner watermark](https://res.cloudinary.com/demo/image/upload/f_auto/q_auto/docs/roses-ban.jpg "thumb:c_scale,w_550/dpr_2.0, width: 500, with_url:false, with_code:false")

Upload request:

```multi
|ruby
Cloudinary::Uploader.upload("roses-ban.jpg", 
  detection: "watermark-detection")

|php_2
$cloudinary->uploadApi()->upload("roses-ban.jpg", 
  ["detection" => "watermark-detection"]);

|python
cloudinary.uploader.upload("roses-ban.jpg",
  detection = "watermark-detection")

|nodejs
cloudinary.v2.uploader
.upload("roses-ban.jpg", 
  { detection: "watermark-detection" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("roses-ban.jpg", ObjectUtils.asMap(
  "detection", "watermark-detection"));

|csharp
var uploadParams = new ImageUploadParams() 
{
  File = new FileDescription(@"roses-ban.jpg"),
  Detection = "watermark-detection"
};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "roses-ban.jpg", uploader.UploadParams{
		Detection: "watermark-detection"})

|android
MediaManager.get().upload("roses-ban.jpg")
  .option("detection", "watermark-detection").dispatch();

|swift
let params = CLDUploadRequestParams().setDetection("watermark-detection")
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "roses-ban.jpg", params: params) 

|cli
cld uploader upload "roses-ban.jpg" detection="watermark-detection"

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST -F 'file=@/path/to/roses-ban.jpg' -F 'detection=watermark-detection' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'
```

The response includes:

```json
  "info": {
    "detection": {
      "object_detection": {
        "status": "complete",
        "data": {
          "watermark-detection": {
            "model_name": "watermark-detection",
            "model_version": 1,
            "schema_version": 1,
            "tags": {
              "banner": [
                {
                  "categories": [],
                  "confidence": 0.99
                }
              ]
            }
          }
        }
      }
    }
  },
```

### Watermarks

If the image contains a semi-transparent layer, it is likely that the image will be flagged as containing a **watermark**.

For example, uploading the following image, requesting `watermark-detection`, the response shows 99% confidence that it contains a watermark: 

![Example of a watermark](https://res.cloudinary.com/demo/image/upload/f_auto/q_auto/docs/roses-wm.jpg "thumb:c_scale,w_550/dpr_2.0, width: 500, with_url:false, with_code:false")

Upload request:

```multi
|ruby
Cloudinary::Uploader.upload("roses-wm.jpg", 
  detection: "watermark-detection")

|php_2
$cloudinary->uploadApi()->upload("roses-wm.jpg", 
  ["detection" => "watermark-detection"]);

|python
cloudinary.uploader.upload("roses-wm.jpg",
  detection = "watermark-detection")

|nodejs
cloudinary.v2.uploader
.upload("roses-wm.jpg", 
  { detection: "watermark-detection" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("roses-wm.jpg", ObjectUtils.asMap(
  "detection", "watermark-detection"));

|csharp
var uploadParams = new ImageUploadParams() 
{
  File = new FileDescription(@"roses-wm.jpg"),
  Detection = "watermark-detection"
};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "roses-wm.jpg", uploader.UploadParams{
		Detection: "watermark-detection"})

|android
MediaManager.get().upload("roses-wm.jpg")
  .option("detection", "watermark-detection").dispatch();

|swift
let params = CLDUploadRequestParams().setDetection("watermark-detection")
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "roses-wm.jpg", params: params) 

|cli
cld uploader upload "roses-wm.jpg" detection="watermark-detection"

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST -F 'file=@/path/to/roses-wm.jpg' -F 'detection=watermark-detection' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'
```

The response includes:

```json
  "info": {
    "detection": {
      "object_detection": {
        "status": "complete",
        "data": {
          "watermark-detection": {
            "model_name": "watermark-detection",
            "model_version": 1,
            "schema_version": 1,
            "tags": {
              "watermark": [
                {
                  "categories": [],
                  "confidence": 0.99
                }
              ]
            }
          }
        }
      }
    }
  },
```

## AI-based image captioning

The Cloudinary AI Content Analysis add-on can be used to analyze an image and suggest a caption based on the image's contents.

Some example captions suggested by the AI:

 

1. **A brown dog standing on top of a street next to a sidewalk with a building in the background**
2. **A group of young children playing soccer on a soccer field with a goal post in the foreground and a goal post in the background**
3. **A hand reaching for a donut with chocolate and sprinkles on it on a dark surface**

By setting the `detection` parameter to `captioning` when calling Cloudinary's [upload](image_upload_api_reference#upload) or [update](admin_api#update_details_of_an_existing_resource) methods, the add-on automatically analyzes the content of the image. For example, invoking  the `captioning` detection model while uploading `toy_room.jpg`:

```multi
|ruby
Cloudinary::Uploader.upload("toy_room.jpg", 
  detection: "captioning")

|php_2
$cloudinary->uploadApi()->upload("toy_room.jpg", 
  ["detection" => "captioning"]);

|python
cloudinary.uploader.upload("toy_room.jpg",
  detection = "captioning")

|nodejs
cloudinary.v2.uploader
.upload("toy_room.jpg", 
  { detection: "captioning" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("toy_room.jpg", ObjectUtils.asMap(
  "detection", "captioning"));

|csharp
var uploadParams = new ImageUploadParams() 
{
  File = new FileDescription(@"toy_room.jpg"),
  Detection = "captioning"
};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "toy_room.jpg", uploader.UploadParams{
		Detection: "captioning"})

|android
MediaManager.get().upload("toy_room.jpg")
  .option("detection", "captioning").dispatch();

|swift
let params = CLDUploadRequestParams().setDetection("captioning")
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "toy_room.jpg", params: params) 

|cli
cld uploader upload "toy_room.jpg" detection="captioning"

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST -F 'file=@/path/to/toy_room.jpg' -F 'detection=captioning' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'
```

![Toy room](https://res.cloudinary.com/demo/image/upload/w_500/toy_room.jpg "with_code: false, with_url: false")

> **TIP**:
>
> :title=Tip
> You can use **upload presets** to centrally define a set of upload options including add-on operations to apply, instead of specifying them in each upload call. You can define multiple upload presets, and apply different presets in different upload scenarios. You can create new upload presets in the **Upload Presets** page of the [Console Settings](https://console.cloudinary.com/app/settings/upload/presets) or using the [upload_presets](admin_api#upload_presets) Admin API method. From the **Upload** page of the Console Settings, you can also select default upload presets to use for image, video, and raw API uploads (respectively) as well as default presets for image, video, and raw uploads performed via the Media Library UI. 
> **Learn more**: [Upload presets](upload_presets)

The upload API response includes the captioning information: 

```json
{
  "asset_id": "a30dc93a8580b272f05db9f3d47dbeab",
  "public_id": "1mqow1pnmgfxkkoackdp",
  ...
  ...
  "info": {
    "detection": {
      "captioning": {
        "status": "complete",
        "data": {
           "caption": "A little girl playing with a toy tablet in a room with other children's toys and toys on the floor"
        },
        "model_version": 1.0,
        "schema_version": 1.0
      }
    }
  },
  "original_filename": "toy_room"
  ...
  ...  
}
```

> **TIP**:
>
> :title=Tips

> * You can retrieve the caption text value from the response and then use the `update` method of the Admin API to add the caption text to the metadata of images stored in your product environment, such as the contextual metadata (`context`) or a structured metadata field (`metadata`).

> * After you've requested a caption using the `upload` or `update` method, you can use the Admin API [get details of a single resource](admin_api#get_details_of_a_single_resource_by_public_id) method to return details of the image, including the stored caption value.

> * You can also request analysis using the [Analyze API](analyze_api_guide) (Beta) which also accepts external assets to analyze.

> * [Watch a video tutorial](use_ai_to_generate_image_captions_tutorial) showing how to automatically set alt text for images in a Next.js application.

### Asynchronous handling

As the response may not be immediate, it is good practice to use asynchronous handling for these calls.

To make the call asynchronous, set the `async` parameter of the `upload` method to `true`. To be notified when the processing is complete, you can either set the `notification_url` parameter of the `upload` method (as in the example below) or the global webhook **Notification URL** in the **Upload** page of your Cloudinary Console Settings.

```multi
|ruby
Cloudinary::Uploader.upload("toy_room.jpg", 
  detection: "captioning",
  async: true, 
  notification_url: "https://mysite.example.com/upload_endpoint")

|php_2
$cloudinary->uploadApi()->upload("toy_room.jpg", 
  ["detection" => "captioning",
  "async" => true, 
  "notification_url" => "https://mysite.example.com/upload_endpoint"]);

|python
cloudinary.uploader.upload("toy_room.jpg",
  detection = "captioning", 
  async = True, 
  notification_url = "https://mysite.example.com/upload_endpoint")

|nodejs
cloudinary.v2.uploader
.upload("toy_room.jpg", 
  { detection: "captioning", 
    async: true,
    notification_url: "https://mysite.example.com/upload_endpoint" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("toy_room.jpg", ObjectUtils.asMap(
  "detection", "captioning", 
  "async", true,
  "notification_url", "https://mysite.example.com/upload_endpoint"));

|csharp
var uploadParams = new ImageUploadParams() 
{
  File = new FileDescription(@"toy_room.jpg"),
  Detection = "captioning",
  Async = true,
  NotificationUrl = "https://mysite.example.com/upload_endpoint"
};
var uploadResult = cloudinary.Upload(uploadParams);  

|go
resp, err := cld.Upload.Upload(ctx, "toy_room.jpg", uploader.UploadParams{
		Detection:   "captioning",
    Async: true,
    NotificationURL: "https://mysite.example.com/upload_endpoint"})

|android
MediaManager.get().upload("toy_room.jpg")
  .option("detection", "captioning")
  .option("async", true)
  .option("notification_url", "https://mysite.example.com/upload_endpoint").dispatch();

|swift
let params = CLDUploadRequestParams()
  .setDetection("captioning")
  .setAsync(true)
  .setNotificationUrl("https://mysite.example.com/upload_endpoint")
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "toy_room.jpg", params: params) 

|cli
cld uploader upload "toy_room.jpg" detection="captioning" async=true notification_url="https://mysite.example.com/upload_endpoint"

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST -F 'file=@/path/to/toy_room.jpg' -F 'detection=captioning' -F 'async=true' -F 'notification_url=https://mysite.example.com/upload_endpoint' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'
```

The response to an asynchronous upload call looks similar to this:

```json
{
  "status": "pending",
  "type": "upload",
  "batch_id": "a7877927ae1af0d1115485018ce92a6792c97938bb3edb9b0777d4663d6abbee"
}
```

When the processing is finished, the complete upload response is sent to the notification URL that you specified.

```json
{
  "notification_type": "upload",
  "timestamp": "2023-03-03T14:49:17+00:00",
  "request_id": "ce1fa51d4b3fcdc820276a345d28286b",
  "asset_id": "beaba30dc93a8580b272f05db9f3d47d",
  "public_id": "kdp1mqow1pnmgfxkkoac",
  ...
  ...
  "access_mode": "public",
    "info": {
      "detection": {
        "captioning": {
          "status": "complete",
          "data": {
             "caption": "A little girl playing with a toy tablet in a room with other children's toys and toys on the floor"
          },
          "model_version": 1.0,
          "schema_version": 1.0
        }
    }
  },
  "original_filename": "toy_room"
  ...
  ...
}
```
