> ## 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.

# OCR Text Detection and Extraction


[linkAssets]: dam_add_ons#ocr_text_detection_and_extraction

[Cloudinary](https://cloudinary.com) is a cloud-based service that provides an end-to-end image and video management solution including uploads, storage, transformations, optimizations and delivery. It offers a rich set of image transformation capabilities, including cropping, overlays, graphic improvements, and a large variety of special effects.

The OCR Text Detection and Extraction add-on, powered by the [Google Vision API](https://cloud.google.com/vision/), integrates seamlessly with Cloudinary's upload and transformation functionality. It extracts all detected text from images, including multi-page documents like TIFFs and PDFs.

You can use the extracted text directly for a variety of purposes, such as organizing or tagging images. Additionally, you can take advantage of special OCR-based transformations, such as blurring, pixelating, or overlaying other images on all detected text with simple transformation parameters. You can also use the add-on to ensure that important texts aren't cut off when you crop your images. 

You can use the add-on in normal mode for capturing text elements within a photograph or other graphical image, or in `document` mode for capturing dense text such as a scan of a document. If you expect images to include non-latin characters, you can instruct the add-on to analyze the image for a specific [language](#language_support).
> **INFO**:
>
> By default, delivery URLs that use this add-on either need to be [signed](#signed_urls) or [eagerly generated](eager_and_incoming_transformations#eager_transformations). You can optionally remove this requirement by selecting this add-on in the **Allow unsigned add-on transformations** section of the **Security** page in the Console Settings.
> (For simplicity, most of the examples on this page show eagerly generated URLs without signatures.)
The following example uses the normal mode of the OCR add-on to pixelate the license plate text in this car photograph:

Original

Pixelated

![Pixelate license plate](https://res.cloudinary.com/demo/image/upload/c_crop,g_west,h_1440,w_1520,x_50/e_pixelate_region:15,g_ocr_text/black_car.jpg "with_image: false")

```nodejs
cloudinary.image("black_car.jpg", {transformation: [
  {gravity: "west", height: 1440, width: 1520, x: 50, crop: "crop"},
  {effect: "pixelate_region:15", gravity: "ocr_text"}
  ]})
```

```react
new CloudinaryImage("black_car.jpg")
  .resize(
    crop()
      .width(1520)
      .height(1440)
      .gravity(compass("west"))
      .x(50)
  )
  .effect(
    pixelate()
      .squareSize(15)
      .region(ocr())
  );
```

```vue
new CloudinaryImage("black_car.jpg")
  .resize(
    crop()
      .width(1520)
      .height(1440)
      .gravity(compass("west"))
      .x(50)
  )
  .effect(
    pixelate()
      .squareSize(15)
      .region(ocr())
  );
```

```angular
new CloudinaryImage("black_car.jpg")
  .resize(
    crop()
      .width(1520)
      .height(1440)
      .gravity(compass("west"))
      .x(50)
  )
  .effect(
    pixelate()
      .squareSize(15)
      .region(ocr())
  );
```

```js
new CloudinaryImage("black_car.jpg")
  .resize(
    crop()
      .width(1520)
      .height(1440)
      .gravity(compass("west"))
      .x(50)
  )
  .effect(
    pixelate()
      .squareSize(15)
      .region(ocr())
  );
```

```python
CloudinaryImage("black_car.jpg").image(transformation=[
  {'gravity': "west", 'height': 1440, 'width': 1520, 'x': 50, 'crop': "crop"},
  {'effect': "pixelate_region:15", 'gravity': "ocr_text"}
  ])
```

```php
(new ImageTag('black_car.jpg'))
	->resize(Resize::crop()->width(1520)
->height(1440)
	->gravity(
	Gravity::compass(
	Compass::west()))
->x(50))
	->effect(Effect::pixelate()->squareSize(15)
	->region(
	Region::ocr())
	);
```

```java
cloudinary.url().transformation(new Transformation()
  .gravity("west").height(1440).width(1520).x(50).crop("crop").chain()
  .effect("pixelate_region:15").gravity("ocr_text")).imageTag("black_car.jpg");
```

```ruby
cl_image_tag("black_car.jpg", transformation: [
  {gravity: "west", height: 1440, width: 1520, x: 50, crop: "crop"},
  {effect: "pixelate_region:15", gravity: "ocr_text"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Gravity("west").Height(1440).Width(1520).X(50).Crop("crop").Chain()
  .Effect("pixelate_region:15").Gravity("ocr_text")).BuildImageTag("black_car.jpg")
```

```dart
cloudinary.image('black_car.jpg').transformation(Transformation()
	.resize(Resize.crop().width(1520)
.height(1440)
	.gravity(
	Gravity.compass(
	Compass.west()))
.x(50))
	.effect(Effect.pixelate().squareSize(15)
	.region(
	Region.ocr())
	));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setGravity("west").setHeight(1440).setWidth(1520).setX(50).setCrop("crop").chain()
  .setEffect("pixelate_region:15").setGravity("ocr_text")).generate("black_car.jpg")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .gravity("west").height(1440).width(1520).x(50).crop("crop").chain()
  .effect("pixelate_region:15").gravity("ocr_text")).generate("black_car.jpg");
```

```flutter
cloudinary.image('black_car.jpg').transformation(Transformation()
	.resize(Resize.crop().width(1520)
.height(1440)
	.gravity(
	Gravity.compass(
	Compass.west()))
.x(50))
	.effect(Effect.pixelate().squareSize(15)
	.region(
	Region.ocr())
	));
```

```kotlin
cloudinary.image {
	publicId("black_car.jpg")
	 resize(Resize.crop() { width(1520)
 height(1440)
	 gravity(
	Gravity.compass(
	Compass.west()))
 x(50) })
	 effect(Effect.pixelate() { squareSize(15)
	 region(
	Region.ocr())
	 }) 
}.generate()
```

```jquery
$.cloudinary.image("black_car.jpg", {transformation: [
  {gravity: "west", height: 1440, width: 1520, x: 50, crop: "crop"},
  {effect: "pixelate_region:15", gravity: "ocr_text"}
  ]})
```

```react_native
new CloudinaryImage("black_car.jpg")
  .resize(
    crop()
      .width(1520)
      .height(1440)
      .gravity(compass("west"))
      .x(50)
  )
  .effect(
    pixelate()
      .squareSize(15)
      .region(ocr())
  );
```
> **TIP**: This page describes how to use the OCR Text Detection and Extraction add-on programmatically, but that you can also use the add-on for DAM use cases in Assets. For more information, see [OCR Text Detection and Extraction][linkAssets] in the Assets user guide.#### Getting started

Before you can use the OCR Text Detection and Extraction add-on:

* You must have a Cloudinary account. If you don't already have one, you can [sign up](https://cloudinary.com/users/register_free) for a free account. 

* Register for the add-on: make sure you're logged in to your account and then go to the [Add-ons](https://console.cloudinary.com/app/settings/addons) page. For more information about add-on registrations, see [Registering for add-ons](cloudinary_add_ons#registering_for_add_ons).

* Keep in mind that many of the examples on this page use our SDKs. For SDK installation and configuration details, see the relevant [SDK](cloudinary_sdks) guide.
  
* If you're new to Cloudinary, you may want to take a look at the [Developer Kickstart](dev_kickstart) for a hands-on, step-by-step introduction to a variety of features.

## Extracting detected text

You can return all text detected in an image file in the JSON response of any `upload` or `update` call.

The returned content includes a summary of all returned text and the bounding box coordinates of the entire captured text, plus a breakdown of each text element (an individual word or other set of characters without a space) captured and the bounding box of each such text element.

### Requesting extracted text (upload/update methods)

To request inclusion of detected text in the response of your `upload` or `update` method call, set the `ocr` parameter to `adv_ocr` (for photos or images containing text elements)  or `adv_ocr:document` (for best results on text-heavy images such as scanned documents).

For example, when using the upload method:

```multi
|ruby
Cloudinary::Uploader.upload("concert_ticket.jpg",  
  ocr: "adv_ocr")

|php_2
$cloudinary->uploadApi()->upload("concert_ticket.jpg", 
  ["ocr" => "adv_ocr"]);

|python
cloudinary.uploader.upload("concert_ticket.jpg",
  ocr = "adv_ocr")

|nodejs
cloudinary.v2.uploader
.upload("concert_ticket.jpg", 
  { ocr: "adv_ocr" })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("concert_ticket.jpg", 
  ObjectUtils.asMap("ocr", "adv_ocr"));

|csharp
var uploadParams = new ImageUploadParams()
{
  File = new FileDescription(@"concert_ticket.jpg"),
  Ocr = "adv_ocr"
};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "concert_ticket.jpg", uploader.UploadParams{
		OCR: "adv_ocr"})

|android
MediaManager.get().upload("concert_ticket.jpg")
  .option("ocr", "adv_ocr").dispatch();

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

|cli
cld uploader upload "concert_ticket.jpg" ocr="adv_ocr"
```

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

Or when using the update method:

```multi
|ruby
Cloudinary::Api.update("concert_ticket", 
  ocr: "adv_ocr")

|php_2
$api->update("concert_ticket", 
  ["ocr" => "adv_ocr"]);

|python
cloudinary.api.update("concert_ticket",
  ocr = "adv_ocr")

|nodejs
cloudinary.v2.api
.update("concert_ticket", 
  { ocr: "adv_ocr" })
.then(result=>console.log(result));

|java
cloudinary.api().update("concert_ticket", 
  ObjectUtils.asMap("ocr", "adv_ocr"));

|csharp
var updateParams = new UpdateParams("concert_ticket")
{
  Ocr = "adv_ocr"
};
var updateResult = cloudinary.Update(updateParams);

|go
resp, err := cld.Admin.UpdateAsset(ctx, admin.UpdateAssetParams{
		PublicID: "concert_ticket",
		Ocr:      "adv_ocr"})

|cli
cld admin update "concert_ticket" ocr="adv_ocr"
```

### Extracted text in the JSON response

When you `upload` an image (or perform an `update` operation) with the `ocr` parameter set to `adv_ocr` or `adv_ocr:document`, the JSON response includes an `ocr` node under the `info` section.

The `ocr` node of the response includes the following:

* The name of the OCR engine used by the add-on (`adv_ocr`)
* The status of the OCR operation
* The detected locale of the text
* The outer bounding rectangle containing all of the detected text
* A `description` listing the entirety of the detected text content, with a newline character (`\n`) separating groups of text
* For multi-page files (e.g. PDFs), a node indicating the containing page
* The bounding rectangle of each individual detected text element and the `description` (text content) of that individual element

For example, an excerpt from the `ocr` section of the JSON response from a scanned restaurant receipt image may look something like this: 

```
"info": {
  "ocr": {
    "adv_ocr": {
      "status": "complete",
      "data": [
        { "textAnnotations": [
            { "locale": "en",
              "boundingPoly": {
                "vertices": [
                  { "y": 373,
                    "x": 297 },
                  { "y": 373,
                    "x": 1306 },
                  { "y": 2735,
                    "x": 1306 },
                  { "y": 2735,
                    "x": 297 }
                ]
              },
              "description": "CREDIT CARD VOUCHER\nANY RESTAURANT\nANYWHERE\n(69) 
                69696969\nDATE\n02/02/2014\nTIME\n11:11\nCARD TYPE\nMC\nACCT\n1234 1234 
                1234 1111\nTRANS KEY\nHYU87 89798234\nAUTH CODE:\n12345\nEXP 
                DATE:\n12/15\nCHECK:\n1341\nTABLE\n12\nSERVER\n34 
                MONIKA\nSubtotal\n$1969.69\nGratuity\nTotal\nSignature:\nCustomer Copy\n"
            },
            { "boundingPoly": {
                "vertices": [
                  { "y": 373,
                    "x": 561 },
                  { "y": 373,
                    "x": 726 },
                  { "y": 426,
                    "x": 726 },
                  { "y": 426,
                    "x": 561 }
                ]
              },
              "description": "CREDIT"
            },
            { "boundingPoly": {
                "vertices": [
                  {
		...
		...
		...
}
```

### Using extracted text to process images

Once you have extracted text in your response, you can access it based on the response structure. 

```multi
|ruby
$result = $cloudinary->uploadApi()->upload(some_image_file_path, ["ocr" => "adv_ocr"]);
if($result["info"]["ocr"]["adv_ocr"]["status"] == "complete"){
    $data = $result["info"]["ocr"]["adv_ocr"]["data"];
}

|php_2
use Cloudinary\Cloudinary;
​
$cloudinary = new Cloudinary();
$uploader = $cloudinary->uploadApi();
$result = $upload(some_image_file_path, ["ocr" => "adv_ocr"]);
​
if($result["info"]["ocr"]["adv_ocr"]["status"] == "complete"){
	$data = $result["info"]["ocr"]["adv_ocr"]["data"];
}

|python
result = cloudinary.uploader.upload(some_image_file_path, ocr="adv_ocr")
if result["info"]["ocr"]["adv_ocr"]["status"] == "complete":
    data = result["info"]["ocr"]["adv_ocr"]["data"]

|nodejs
let result = cloudinary.v2.uploader.upload('some_image_file_path', {ocr: 'adv_ocr'}, function(error, result) {
	if (result['info']['ocr']['adv_ocr']['status'] === 'complete') {
		let data = result['info']['ocr']['adv_ocr']['data'];
	}
});

|java
Map result = cloudinary.uploader().upload("my_path", ObjectUtils.asMap("ocr","adv_ocr"));
 
Map info = (Map) ((Map)result.get("info")).get("ocr");
    if (((Map)info.get("adv_ocr")).get("status").equals("complete")) {
        ArrayList data = (ArrayList) ((Map)info.get("adv_ocr")).get("data");
    }

|csharp
dynamic result = JsonConvert.DeserializeObject(uploadResult);
if (result.info.ocr.adv_ocr.status.Value == "complete")
{
    var data = result.info.ocr.adv_ocr.data;
}

|cli
cld uploader upload "black_car.jpeg" ocr="adv_ocr">result.json
```

Below are a few examples of ways to use the text extracted from an image:

#### 1. Write the detected text to a file:

In the example below, the text extracted from the image is saved in the file system in an `image_texts` subfolder using the filename **result\_\.txt**.

```multi
|ruby
if result['info']['ocr']['adv_ocr']['status'] == 'complete'
  data = result['info']['ocr']['adv_ocr']['data']
  texts = data.map{|blocks| 
    annotations = blocks['textAnnotations'] || []
    first_annotation = annotations.first || {}
    (first_annotation['description'] || '').strip
  }.compact.join("\n")
  File.open("image_texts/#{result['public_id']}.txt", 'w'){|f| f.write(texts)}
end

|php_2
if($result["info"]["ocr"]["adv_ocr"]["status"] == "complete"){
    $data = $result["info"]["ocr"]["adv_ocr"]["data"];
    $texts = "";
    foreach($data as $blocks){
	  $annotations = !empty($blocks["textAnnotations"]) ? $blocks["textAnnotations"] : [];
	  $first_annotation = !empty($annotations[0]) ? $annotations[0] : [];
	  $texts .= !empty($first_annotation["description"]) ? trim($first_annotation["description"])."\r\n" : "";
	}
	file_put_contents("image_texts/{$result['public_id']}.txt", $texts);
}

|python
if result["info"]["ocr"]["adv_ocr"]["status"] == "complete":
    data = result["info"]["ocr"]["adv_ocr"]["data"]
    texts = json.dumps(data, indent=4)
    f = open("image_texts/result_" + result["public_id"] + ".txt", "w")
    f.write(texts)
    f.close()

|nodejs
if (result['info']['ocr']['adv_ocr']['status'] === 'complete') {
	let data = result['info']['ocr']['adv_ocr']['data'];
	let texts = data.map(blocks => {
		let annotations = blocks['textAnnotations'] || [];
		let firstAnnotation = annotations[0] || {};
		return (firstAnnotation['description'] || '').trim();
	}).filter((obj) => obj).join('\n');
	fs.writeFile('image_texts/' + result['public_id'] + '.txt', texts, (err) => {
		if (err) throw err;
		console.log('Saved');
	});
}

|java
Map info = (Map) ((Map)result.get("info")).get("ocr");
    if (((Map)info.get("adv_ocr")).get("status").equals("complete")) {
        ArrayList data = (ArrayList) ((Map)info.get("adv_ocr")).get("data");
        JSONArray texts = new JSONArray(data);
        try (FileWriter writer = new FileWriter("image_texts"+result.get("public_id") + ".txt")) {
            writer.write(String.valueOf(texts));
            writer.close();
        }
    }

|csharp
if(result.info.ocr.adv_ocr.status.Value == "complete")
{
    var data = result.info.ocr.adv_ocr.data;
    var texts = Convert.ToString(data);
    File.WriteAllText("image_texts/result_" + result.public_id + ".txt", texts);
} 
 

|cli
cld admin update "black_car.jpeg" ocr="adv_ocr">result.json
```

#### 2. If an image has text, store it with a different public ID path

In the example below, the `rename` method is used to update the public IDs of images without text to sit under a `no_text` path, and changes the public ID's of images with text to an ID under the `with_text` path.

```multi
|ruby
if result['info']['ocr']['adv_ocr']['status'] == 'complete'
  data = result['info']['ocr']['adv_ocr']['data']
  folder = data.all?{|i| i.empty?} ?  'no_text' : 'with_text'
  Cloudinary::Uploader.rename(
    result['public_id'], # from_public_id
    "#{folder}/#{result['public_id']}" # to_public_id
  )
end

|php_2
if($result["info"]["ocr"]["adv_ocr"]["status"] == "complete"){
  $data = $result["info"]["ocr"]["adv_ocr"]["data"];
  $folder = "no_text";
  $folder = !empty(current($data)['textAnnotations']) ? "with_text" : "no_text";
  $uploader->rename(
    $result["public_id"], # from_public_id
    "{$folder}/{$result['public_id']}" # to_public_id
  );
}

|python
if result["info"]["ocr"]["adv_ocr"]["status"] == "complete":
    data = result["info"]["ocr"]["adv_ocr"]["data"]
    folder = "with_text" if ("textAnnotations" in data) else "no_text"
    cloudinary.uploader.rename(
        result["public_id"],                # from_public_id
        folder + "/" + result["public_id"]  # to_public_id
    )

|nodejs
if (result['info']['ocr']['adv_ocr']['status'] === 'complete') {
	let data = result['info']['ocr']['adv_ocr']['data'];
	let folder = data.every(el => {return Object.keys(el).length === 0}) ? 'no_text' : 'with_text'; 
	cloudinary.v2.uploader.rename(
		result['public_id'], // from_public_id
		folder + '/' + result['public_id'] // to_public_id
	);
}

|java
Map info = (Map) ((Map)result.get("info")).get("ocr");
    if (((Map)info.get("adv_ocr")).get("status").equals("complete")) {
        ArrayList data = (ArrayList) ((Map)info.get("adv_ocr")).get("data");
        String folder = "with_text";
        if (data.isEmpty() || data.get(0).equals(emptyMap())) {
            folder = "no_text";
        }
        String public_id = (String) result.get("public_id");
        Map res = cloudinary.uploader().rename(public_id, folder + "/"+ public_id,ObjectUtils.emptyMap());     
    }

|csharp
if(result.info.ocr.adv_ocr.status.Value == "complete")
{
    var data = result.info.ocr.adv_ocr.data;
    var folder = "with_text";
    if ((data.Count < 1) || (Convert.ToString(data.First) == "{}") || (String.IsNullOrEmpty(Convert.ToString(data.First))))
    {
        folder = "no_text";
    }
    var renameParams = new RenameParams(
        fromPublicId: data.public_id,               // from_public_id
        toPublicId: folder + "/" + data.public_id   // to_public_id
    );
    var renameResult = cloudinary.Rename(renameParams);
}

|cli
cld uploader rename "black_car" "with_text/black_car"
```

#### 3. Tag images with specific words if detected

For example, for each resume scanned into a career site, check whether the words "Cloudinary", "MBA", or "algorithm" appear. If so, tag the resume file with the relevant keywords.

```multi
|ruby
TAGS = %w(Cloudinary MBA algorithm).freeze
if result['info']['ocr']['adv_ocr']['status'] == 'complete'
  data = result['info']['ocr']['adv_ocr']['data']
  texts = data.map{|blocks| 
    annotations = blocks['textAnnotations'] || []
    first_annotation = annotations.first || {}
    (first_annotation['description'] || '').strip
  }.compact.join(" ")
  tags = TAGS.select{|tag| texts =~ /\b#{tag}\b/i}
  unless tags.empty?
    Cloudinary::Uploader.explicit(result['public_id'], type: 'upload', tags: tags)
  end
end

|php_2
define("TAGS", explode(" ", "Cloudinary MBA algorithm"));
​
if($result["info"]["ocr"]["adv_ocr"]["status"] == "complete"){
    $data = $result["info"]["ocr"]["adv_ocr"]["data"];
    foreach($data as $blocks){
	    $annotations = !empty($blocks["textAnnotations"]) ? $blocks["textAnnotations"] : [];
	    $first_annotation = !empty($annotations[0]) ? $annotations[0] : [];
	    $texts .= !empty($first_annotation["description"]) ? trim($first_annotation["description"])."\r\n" : "";
	}
	foreach(TAGS as $tag){
		if(strpos($texts, $tag) !== false){
			$tags[] = $tag;
		}	
	}
	if(!empty($tags)){
		$uploader->explicit($result["public_id"], ["type" => "upload", "tags" => $tags]);
	}
}

|python
TAGS = ("Cloudinary MBA algorithm").split(" ") 
if result["info"]["ocr"]["adv_ocr"]["status"] == "complete": 
  data = result["info"]["ocr"]["adv_ocr"]["data"]
  texts = json.dumps(data, indent=4) 
  tags = [tag for tag in TAGS if tag.lower() in texts.lower()] 
  if (bool(tags) and bool(tags[0])): 
    cloudinary.uploader.explicit(result["public_id"], 
    type = "upload", 
    tags = tags)

|nodejs
const TAGS = ['Cloudinary', 'MBA', 'algorithm'];
if (result['info']['ocr']['adv_ocr']['status'] === 'complete') {
	let data = result['info']['ocr']['adv_ocr']['data'];
	let texts = data.map(blocks => {
		let annotations = blocks['textAnnotations'] || [];
		let firstAnnotation = annotations[0] || {};
		return (firstAnnotation['description'] || '').trim();
	}).filter((obj) => obj).join(' ');
	let tags = TAGS.filter(tag => {return texts.match(new RegExp(tag, 'i'))});
	if (tags.length > 0) {
		cloudinary.v2.uploader.explicit(result['public_id'], {type: 'upload', tags: tags});
	}
}

|java
ArrayList <String> TAGS= new ArrayList();
TAGS.add("Cloudinary");
TAGS.add("MBA");
TAGS.add("algorithm")
Map info = (Map) ((Map)result.get("info")).get("ocr");
     
if (((Map)info.get("adv_ocr")).get("status").equals("complete")) {
    ArrayList data = (ArrayList) ((Map)info.get("adv_ocr")).get("data");
    JSONArray texts = new JSONArray(data);
    ArrayList tags = new ArrayList();
    
    for (String tag: TAGS) {
        if (texts.toString().toLowerCase().contains(tag.toLowerCase())){
          tags.add(tag);
        }
    }
    
    if (tags.size()>0){
        String public_id = (String) result.get("public_id");
        cloudinary.uploader().explicit(public_id, ObjectUtils.asMap("type", "upload", "tags", tags));
    }
}

|csharp
var TAGS = ("Cloudinary MBA algorithm").Split(" ").ToList();
if (result.info.ocr.adv_ocr.status.Value == "complete")
{
    var data = result.info.ocr.adv_ocr.data;
    var texts = Convert.ToString(data);
    var tags = new List<string>();
    foreach (var tag in TAGS)
    {
        if (texts.ToLower().Contains(tag.ToLower()))
            tags.Add(tag);
    }
    if (tags.Count > 0)
    {
        var explicitParams = new ExplicitParams("sample")
        {
            Type = "upload",
            PublicId = result.public_id,
            Tags = string.Join(",", tags.ToArray())
        };
                    
        var explicitResult = cloudinary.Explicit(explicitParams);
    }                            
}  

|cli
cld uploader add_tag "Cloudinary, MBA, algorithm" '["black_car"]'
```
 
## Blurring or pixelating detected text

Many images may have text, such as phone numbers, web site addresses, license plates, or other personal or commercial data, that you don't want visible in your delivered images. To blur or pixelate all detected text in an image, you can use Cloudinary's built-in `pixelate_region` or `blur_region` **effect** with the **gravity** parameter set to `ocr_text`. For example, we've blurred out the brand and model names on this smartphone:

Original

Blur branding texts

![blur brand name text](https://res.cloudinary.com/demo/image/upload/e_blur_region:800,g_ocr_text/smartphone2.jpg "with_image: false")

```nodejs
cloudinary.image("smartphone2.jpg", {effect: "blur_region:800", gravity: "ocr_text"})
```

```react
new CloudinaryImage("smartphone2.jpg").effect(
  blur()
    .strength(800)
    .region(ocr())
);
```

```vue
new CloudinaryImage("smartphone2.jpg").effect(
  blur()
    .strength(800)
    .region(ocr())
);
```

```angular
new CloudinaryImage("smartphone2.jpg").effect(
  blur()
    .strength(800)
    .region(ocr())
);
```

```js
new CloudinaryImage("smartphone2.jpg").effect(
  blur()
    .strength(800)
    .region(ocr())
);
```

```python
CloudinaryImage("smartphone2.jpg").image(effect="blur_region:800", gravity="ocr_text")
```

```php
(new ImageTag('smartphone2.jpg'))
	->effect(Effect::blur()->strength(800)
	->region(
	Region::ocr())
	);
```

```java
cloudinary.url().transformation(new Transformation().effect("blur_region:800").gravity("ocr_text")).imageTag("smartphone2.jpg");
```

```ruby
cl_image_tag("smartphone2.jpg", effect: "blur_region:800", gravity: "ocr_text")
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation().Effect("blur_region:800").Gravity("ocr_text")).BuildImageTag("smartphone2.jpg")
```

```dart
cloudinary.image('smartphone2.jpg').transformation(Transformation()
	.effect(Effect.blur().strength(800)
	.region(
	Region.ocr())
	));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setEffect("blur_region:800").setGravity("ocr_text")).generate("smartphone2.jpg")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().effect("blur_region:800").gravity("ocr_text")).generate("smartphone2.jpg");
```

```flutter
cloudinary.image('smartphone2.jpg').transformation(Transformation()
	.effect(Effect.blur().strength(800)
	.region(
	Region.ocr())
	));
```

```kotlin
cloudinary.image {
	publicId("smartphone2.jpg")
	 effect(Effect.blur() { strength(800)
	 region(
	Region.ocr())
	 }) 
}.generate()
```

```jquery
$.cloudinary.image("smartphone2.jpg", {effect: "blur_region:800", gravity: "ocr_text"})
```

```react_native
new CloudinaryImage("smartphone2.jpg").effect(
  blur()
    .strength(800)
    .region(ocr())
);
```

> **TIP**: When blurring or pixelating to hide content, you may want to take advantage of one of the [access control options](control_access_to_media) to prevent users from accessing the non-blurred or non-pixelated versions of the image.

## Overlaying detected text with images

Overlaying an image based on OCR text detection is similar to the process for overlaying images in other scenarios: you specify the image to overlay, the width of the overlay, and the gravity (location) for the overlay. When you specify `ocr_text` as the gravity, each detected text element is automatically covered with the specified image.

In most cases, it works best to specify a relative width instead of an absolute width for the overlay. The relative width adjusts the size of the overlay image relative to the size of the detected text element. To do this, just add the `fl_region_relative` flag to your transformation, and specify the width of the overlay image as a percentage (1.0 = 100%) of the text element.

For example, suppose you run a real estate website where individuals or companies can list homes for sale. For revenue recognition purposes, it's important that the listings do not display private phone numbers or those of other real estate organizations. So instead, you overlay an image with your site's contact information that covers any detected text in the uploaded images.

![cover text with an image](https://res.cloudinary.com/demo/image/upload/l_call_text/c_scale,fl_region_relative,w_1.1/fl_layer_apply,g_ocr_text/home_4_sale.jpg "thumb: w_700")

```nodejs
cloudinary.image("home_4_sale.jpg", {transformation: [
  {overlay: "call_text"},
  {flags: "region_relative", width: "1.1", crop: "scale"},
  {flags: "layer_apply", gravity: "ocr_text"}
  ]})
```

```react
new CloudinaryImage("home_4_sale.jpg").overlay(
  source(
    image("call_text").transformation(
      new Transformation().resize(scale().width(1.1).regionRelative())
    )
  ).position(new Position().gravity(focusOn(ocr())))
);
```

```vue
new CloudinaryImage("home_4_sale.jpg").overlay(
  source(
    image("call_text").transformation(
      new Transformation().resize(scale().width(1.1).regionRelative())
    )
  ).position(new Position().gravity(focusOn(ocr())))
);
```

```angular
new CloudinaryImage("home_4_sale.jpg").overlay(
  source(
    image("call_text").transformation(
      new Transformation().resize(scale().width(1.1).regionRelative())
    )
  ).position(new Position().gravity(focusOn(ocr())))
);
```

```js
new CloudinaryImage("home_4_sale.jpg").overlay(
  source(
    image("call_text").transformation(
      new Transformation().resize(scale().width(1.1).regionRelative())
    )
  ).position(new Position().gravity(focusOn(ocr())))
);
```

```python
CloudinaryImage("home_4_sale.jpg").image(transformation=[
  {'overlay': "call_text"},
  {'flags': "region_relative", 'width': "1.1", 'crop': "scale"},
  {'flags': "layer_apply", 'gravity': "ocr_text"}
  ])
```

```php
(new ImageTag('home_4_sale.jpg'))
	->overlay(Overlay::source(
	Source::image("call_text")
	->transformation((new Transformation())
	->resize(Resize::scale()->width(1.1)
	->regionRelative()
	))
	)
	->position((new Position())
	->gravity(
	Gravity::focusOn(
	FocusOn::ocr()))
	)
	);
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new Layer().publicId("call_text")).chain()
  .flags("region_relative").width(1.1).crop("scale").chain()
  .flags("layer_apply").gravity("ocr_text")).imageTag("home_4_sale.jpg");
```

```ruby
cl_image_tag("home_4_sale.jpg", transformation: [
  {overlay: "call_text"},
  {flags: "region_relative", width: 1.1, crop: "scale"},
  {flags: "layer_apply", gravity: "ocr_text"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Overlay(new Layer().PublicId("call_text")).Chain()
  .Flags("region_relative").Width(1.1).Crop("scale").Chain()
  .Flags("layer_apply").Gravity("ocr_text")).BuildImageTag("home_4_sale.jpg")
```

```dart
cloudinary.image('home_4_sale.jpg').transformation(Transformation()
	.overlay(Overlay.source(
	Source.image("call_text")
	.transformation(new Transformation()
	.resize(Resize.scale().width(1.1)
	.regionRelative()
	))
	)
	.position(Position()
	.gravity(
	Gravity.focusOn(
	FocusOn.ocr()))
	)
	));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setOverlay("call_text").chain()
  .setFlags("region_relative").setWidth(1.1).setCrop("scale").chain()
  .setFlags("layer_apply").setGravity("ocr_text")).generate("home_4_sale.jpg")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new Layer().publicId("call_text")).chain()
  .flags("region_relative").width(1.1).crop("scale").chain()
  .flags("layer_apply").gravity("ocr_text")).generate("home_4_sale.jpg");
```

```flutter
cloudinary.image('home_4_sale.jpg').transformation(Transformation()
	.overlay(Overlay.source(
	Source.image("call_text")
	.transformation(new Transformation()
	.resize(Resize.scale().width(1.1)
	.regionRelative()
	))
	)
	.position(Position()
	.gravity(
	Gravity.focusOn(
	FocusOn.ocr()))
	)
	));
```

```kotlin
cloudinary.image {
	publicId("home_4_sale.jpg")
	 overlay(Overlay.source(
	Source.image("call_text") {
	 transformation(Transformation {
	 resize(Resize.scale() { width(1.1F)
	 regionRelative()
	 }) })
	 }) {
	 position(Position() {
	 gravity(
	Gravity.focusOn(
	FocusOn.ocr()))
	 })
	 }) 
}.generate()
```

```jquery
$.cloudinary.image("home_4_sale.jpg", {transformation: [
  {overlay: new cloudinary.Layer().publicId("call_text")},
  {flags: "region_relative", width: "1.1", crop: "scale"},
  {flags: "layer_apply", gravity: "ocr_text"}
  ]})
```

```react_native
new CloudinaryImage("home_4_sale.jpg").overlay(
  source(
    image("call_text").transformation(
      new Transformation().resize(scale().width(1.1).regionRelative())
    )
  ).position(new Position().gravity(focusOn(ocr())))
);
```

Original sign

Sign with your text overlay

## Text-based cropping

When you want to be sure that text in an image is retained during a crop transformation, you can specify `ocr_text` as the `gravity` (`g_ocr_text` in URLs).

For example, the following example demonstrates what happens to the **itsSnacktime.com** text in the picture below if you crop it to a square with default (center gravity) cropping, `auto` gravity cropping, or `ocr_text` gravity cropping:

Original

default gravity (centered)   

auto gravity (focus on most prominent elements)

ocr_text gravity (focus on text regions)

The transformation code for the last image looks like this:
![ocr_text gravity](https://res.cloudinary.com/demo/image/upload/c_fill,g_ocr_text,h_250,w_250/snacktime.jpg "with_image: false")

```nodejs
cloudinary.image("snacktime.jpg", {gravity: "ocr_text", height: 250, width: 250, crop: "fill"})
```

```react
new CloudinaryImage("snacktime.jpg").resize(
  fill()
    .width(250)
    .height(250)
    .gravity(focusOn(ocr()))
);
```

```vue
new CloudinaryImage("snacktime.jpg").resize(
  fill()
    .width(250)
    .height(250)
    .gravity(focusOn(ocr()))
);
```

```angular
new CloudinaryImage("snacktime.jpg").resize(
  fill()
    .width(250)
    .height(250)
    .gravity(focusOn(ocr()))
);
```

```js
new CloudinaryImage("snacktime.jpg").resize(
  fill()
    .width(250)
    .height(250)
    .gravity(focusOn(ocr()))
);
```

```python
CloudinaryImage("snacktime.jpg").image(gravity="ocr_text", height=250, width=250, crop="fill")
```

```php
(new ImageTag('snacktime.jpg'))
	->resize(Resize::fill()->width(250)
->height(250)
	->gravity(
	Gravity::focusOn(
	FocusOn::ocr()))
	);
```

```java
cloudinary.url().transformation(new Transformation().gravity("ocr_text").height(250).width(250).crop("fill")).imageTag("snacktime.jpg");
```

```ruby
cl_image_tag("snacktime.jpg", gravity: "ocr_text", height: 250, width: 250, crop: "fill")
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation().Gravity("ocr_text").Height(250).Width(250).Crop("fill")).BuildImageTag("snacktime.jpg")
```

```dart
cloudinary.image('snacktime.jpg').transformation(Transformation()
	.resize(Resize.fill().width(250)
.height(250)
	.gravity(
	Gravity.focusOn(
	FocusOn.ocr()))
	));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setGravity("ocr_text").setHeight(250).setWidth(250).setCrop("fill")).generate("snacktime.jpg")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().gravity("ocr_text").height(250).width(250).crop("fill")).generate("snacktime.jpg");
```

```flutter
cloudinary.image('snacktime.jpg').transformation(Transformation()
	.resize(Resize.fill().width(250)
.height(250)
	.gravity(
	Gravity.focusOn(
	FocusOn.ocr()))
	));
```

```kotlin
cloudinary.image {
	publicId("snacktime.jpg")
	 resize(Resize.fill() { width(250)
 height(250)
	 gravity(
	Gravity.focusOn(
	FocusOn.ocr()))
	 }) 
}.generate()
```

```jquery
$.cloudinary.image("snacktime.jpg", {gravity: "ocr_text", height: 250, width: 250, crop: "fill"})
```

```react_native
new CloudinaryImage("snacktime.jpg").resize(
  fill()
    .width(250)
    .height(250)
    .gravity(focusOn(ocr()))
);
```

Alternatively, in cases where text is only one consideration of cropping priority, you can set the `gravity` parameter to `auto` with the `ocr_text` option (`g_auto:ocr_text` in URLs), which gives a higher priority to detected text, but also gives priority to faces and other very prominent elements of an image.

### Avoiding text

To minimize the likelihood of having text in a cropped image, set the `gravity` parameter to  `auto` with the `ocr_text_avoid` option (`g_auto:ocr_text_avoid` in URLs).

For example, in the photo below, you may not want to show the name of the flower shop.

 

Using `g_auto` by itself makes the shop front the focal point, but if we use `g_auto:ocr_text_avoid`, the side of the photo without the text is shown.

![Crop a picture avoiding text](https://res.cloudinary.com/demo/image/upload/ar_0.8,c_fill,g_auto:ocr_text_avoid,h_400/docs/flower_shop.jpg "with_image:false")

```nodejs
cloudinary.image("docs/flower_shop.jpg", {aspect_ratio: "0.8", gravity: "auto:ocr_text_avoid", height: 400, crop: "fill"})
```

```react
new CloudinaryImage("docs/flower_shop.jpg").resize(
  fill()
    .height(400)
    .aspectRatio(0.8)
    .gravity(autoGravity().autoFocus(focusOn(ocr()).avoid()))
);
```

```vue
new CloudinaryImage("docs/flower_shop.jpg").resize(
  fill()
    .height(400)
    .aspectRatio(0.8)
    .gravity(autoGravity().autoFocus(focusOn(ocr()).avoid()))
);
```

```angular
new CloudinaryImage("docs/flower_shop.jpg").resize(
  fill()
    .height(400)
    .aspectRatio(0.8)
    .gravity(autoGravity().autoFocus(focusOn(ocr()).avoid()))
);
```

```js
new CloudinaryImage("docs/flower_shop.jpg").resize(
  fill()
    .height(400)
    .aspectRatio(0.8)
    .gravity(autoGravity().autoFocus(focusOn(ocr()).avoid()))
);
```

```python
CloudinaryImage("docs/flower_shop.jpg").image(aspect_ratio="0.8", gravity="auto:ocr_text_avoid", height=400, crop="fill")
```

```php
(new ImageTag('docs/flower_shop.jpg'))
	->resize(Resize::fill()->height(400)
->aspectRatio(0.8)
	->gravity(
	Gravity::autoGravity()
	->autoFocus(
	AutoFocus::focusOn(
	FocusOn::ocr())->avoid())
	)
	);
```

```java
cloudinary.url().transformation(new Transformation().aspectRatio("0.8").gravity("auto:ocr_text_avoid").height(400).crop("fill")).imageTag("docs/flower_shop.jpg");
```

```ruby
cl_image_tag("docs/flower_shop.jpg", aspect_ratio: "0.8", gravity: "auto:ocr_text_avoid", height: 400, crop: "fill")
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation().AspectRatio("0.8").Gravity("auto:ocr_text_avoid").Height(400).Crop("fill")).BuildImageTag("docs/flower_shop.jpg")
```

```dart
cloudinary.image('docs/flower_shop.jpg').transformation(Transformation()
	.resize(Resize.fill().height(400)
.aspectRatio(0.8)
	.gravity(
	Gravity.autoGravity()
	.autoFocus(
	AutoFocus.focusOn(
	FocusOn.ocr()).avoid())
	)
	));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setAspectRatio("0.8").setGravity("auto:ocr_text_avoid").setHeight(400).setCrop("fill")).generate("docs/flower_shop.jpg")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().aspectRatio("0.8").gravity("auto:ocr_text_avoid").height(400).crop("fill")).generate("docs/flower_shop.jpg");
```

```flutter
cloudinary.image('docs/flower_shop.jpg').transformation(Transformation()
	.resize(Resize.fill().height(400)
.aspectRatio(0.8)
	.gravity(
	Gravity.autoGravity()
	.autoFocus(
	AutoFocus.focusOn(
	FocusOn.ocr()).avoid())
	)
	));
```

```kotlin
cloudinary.image {
	publicId("docs/flower_shop.jpg")
	 resize(Resize.fill() { height(400)
 aspectRatio(0.8F)
	 gravity(
	Gravity.autoGravity() {
	 autoFocus(
	AutoFocus.focusOn(
	FocusOn.ocr()) { avoid() })
	 })
	 }) 
}.generate()
```

```jquery
$.cloudinary.image("docs/flower_shop.jpg", {aspect_ratio: "0.8", gravity: "auto:ocr_text_avoid", height: 400, crop: "fill"})
```

```react_native
new CloudinaryImage("docs/flower_shop.jpg").resize(
  fill()
    .height(400)
    .aspectRatio(0.8)
    .gravity(autoGravity().autoFocus(focusOn(ocr()).avoid()))
);
```

g_auto

g_auto:ocr_text_avoid

 

## Signed URLs

Cloudinary's dynamic image transformation URLs are powerful tools for agile web and mobile development. However, due to the potential costs of your customers accessing unplanned dynamic URLs that apply the OCR text detection or extraction functionality, image transformation add-on URLs are required (by default) to be signed using Cloudinary's authenticated API or, alternatively, you can [eagerly generate](eager_and_incoming_transformations##eager_transformations) the requested derived images using Cloudinary's authenticated API. 

To create a signed Cloudinary URL using an SDK, set the `sign_url` parameter to `true` when building a URL or creating an image tag. 

For example, to generate a signed URL when applying a blur effect on the text of an image: 

![Code for applying blur on detected text with signed URL](https://res.cloudinary.com/demo/image/upload/s--BDoTEjNU--/e_blur_region:800,g_ocr_text/smartphone2.jpg "with_url: false, with_image:false")

```nodejs
cloudinary.image("smartphone2.jpg", {effect: "blur_region:800", gravity: "ocr_text", sign_url: true})
```

```react
new CloudinaryImage("smartphone2.jpg")
  .effect(
    blur()
      .strength(800)
      .region(ocr())
  )
  .setSignature("BDoTEjNU");
```

```vue
new CloudinaryImage("smartphone2.jpg")
  .effect(
    blur()
      .strength(800)
      .region(ocr())
  )
  .setSignature("BDoTEjNU");
```

```angular
new CloudinaryImage("smartphone2.jpg")
  .effect(
    blur()
      .strength(800)
      .region(ocr())
  )
  .setSignature("BDoTEjNU");
```

```js
new CloudinaryImage("smartphone2.jpg")
  .effect(
    blur()
      .strength(800)
      .region(ocr())
  )
  .setSignature("BDoTEjNU");
```

```python
CloudinaryImage("smartphone2.jpg").image(effect="blur_region:800", gravity="ocr_text", sign_url=True)
```

```php
(new ImageTag('smartphone2.jpg'))
	->effect(Effect::blur()->strength(800)
	->region(
	Region::ocr())
	)
	->sign();
```

```java
cloudinary.url().transformation(new Transformation().effect("blur_region:800").gravity("ocr_text")).signed(true).imageTag("smartphone2.jpg");
```

```ruby
cl_image_tag("smartphone2.jpg", effect: "blur_region:800", gravity: "ocr_text", sign_url: true)
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation().Effect("blur_region:800").Gravity("ocr_text")).Signed(true).BuildImageTag("smartphone2.jpg")
```

```dart
cloudinary.image('smartphone2.jpg').transformation(Transformation()
	.effect(Effect.blur().strength(800)
	.region(
	Region.ocr())
	)
	.setSignature("BDoTEjNU"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setEffect("blur_region:800").setGravity("ocr_text")).generate("smartphone2.jpg", signUrl: true)!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().effect("blur_region:800").gravity("ocr_text")).signed(true).generate("smartphone2.jpg");
```

```flutter
cloudinary.image('smartphone2.jpg').transformation(Transformation()
	.effect(Effect.blur().strength(800)
	.region(
	Region.ocr())
	)
	.setSignature("BDoTEjNU"));
```

```kotlin
cloudinary.image {
	publicId("smartphone2.jpg")
	 effect(Effect.blur() { strength(800)
	 region(
	Region.ocr())
	 })
	 signature() 
}.generate()
```

```jquery
$.cloudinary.image("smartphone2.jpg", {effect: "blur_region:800", gravity: "ocr_text"})
```

```react_native
new CloudinaryImage("smartphone2.jpg")
  .effect(
    blur()
      .strength(800)
      .region(ocr())
  )
  .setSignature("BDoTEjNU");
```

The generated Cloudinary URL shown below includes a signature component (`/s--BDoTEjNU--/`). Only URLs with a valid signature that matches the requested image transformation will be approved for on-the-fly image transformation and delivery.

[Sign URL of corrected image](https://res.cloudinary.com/demo/image/upload/s--BDoTEjNU--/e_blur_region:800,g_ocr_text/smartphone2.jpg "with_code: false")

For more details on signed URLs, see [Signed delivery URLs](control_access_to_media#enforcement_mechanism_signed_delivery_urls).

> **NOTES**:
>
> * You can optionally remove the signed URL default requirement for a particular add-on by selecting it in the **Allow unsigned add-on transformations** section of the **Security** page in the Cloudinary Settings. 

> * No OCR mechanism can identify 100% of the text in all images. The results may be  affected by things like font, color, contrast between text and background, text angle, and more.

> * The OCR engine requires images with a minimum resolution of 1024 X 768.

## Language support
By default, the add-on supports latin languages. You can instruct the add-on to perform the text detection in a non-latin language by adding the 2-letter language code to the `adv_ocr` value, separated by a colon. For example, if you expect your image to include Russian characters, set the value to `adv_ocr:ru`. Note that when you include a language code, the structure and breakdown of the response is different than without. The full list of supported languages and their language codes can be found [here](https://cloud.google.com/vision/docs/languages).

