alt text

Script For Layout Model Of Surya OCR.

Please refer to surya-layout-fien-tuneCrossEntropyLoss.ipynb (Differnt Repository) file to understand fine-tuning process.

Setup Instructions

Clone the Surya OCR GitHub Repository

git clone https://github.com/vikp/surya.git
cd surya

Switch to v0.4.14

git checkout f7c6c04

Install Dependencies

You can install the required dependencies using the following command:

pip install -r requirements.txt

Image Pre-processing

For image pre-processing we can directly import a function and image processor from surya ocr github repository.

from surya.input.processing import prepare_image_detection
from surya.model.detection.segformer import load_processor
from PIL import Image
image = Image.open("path/to/image")
images = [prepare_image_detection(img=image, processor=load_processor())]
images = torch.stack(images, dim=0).to(model.dtype).to(torch.float32).to(device)

When an image is passed through prepare_image_detection function, the image is converted into the shape : [1 ,12 , 300 , 300 ] (This is the format(shape) that is expected by Surya Layout Model)

1 (Batch Size): The first dimension is the batch size, which is 1 in this case. It means that this tensor contains data for one example (or image) at a time.

12 (Number of Channels or Classes): The second dimension represents the number of channels or classes.

300 (Height): The third dimension corresponds to the height of the image or spatial dimensions of the data. In this case, the height is 300 pixels.

300 (Width): The fourth dimension corresponds to the width of the image or spatial dimensions. The width is also 300 pixels.

Loading Model

from surya.model.detection.segformer import load_model
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = load_model("ketanmore/surya-ocr-arabic-layout") 
output = model(images).to(device).to(torch.float32)

Post-Processing

Now, since we have output let's understand it. The surya layout model is based of Segformer architecture, this means that surya model produces a segmentation mask. Following are the labels that are known to surya :

print(model.config.id2label) # This snippet can be used to print classes(labels) used for segmentaion
{0: 'Blank',
 1: 'Caption',
 2: 'Footnote',
 3: 'Formula',
 4: 'List-item',
 5: 'Page-footer',
 6: 'Page-header',
 7: 'Picture',
 8: 'Section-header',
 9: 'Table',
 10: 'Text',
 11: 'Title'}

The surya layout model classifies each pixesl into any of the above value resulting in formation of a mask.

Ploting the mask

import torch
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
probabilities = torch.softmax(outputs.logits, dim=1)
mask = torch.argmax(probabilities, dim=1).squeeze(0) 
mask = mask.cpu().numpy()
class_labels = {
    0: 'Blank',
    1: 'Caption',
    2: 'Footnote',
    3: 'Formula',
    4: 'List-item',
    5: 'Page-footer',
    6: 'Page-header',
    7: 'Picture',
    8: 'Section-header',
    9: 'Table',
    10: 'Text',
    11: 'Title'
}

# Create a color map, one unique color for each class
colors = plt.cm.get_cmap('tab20', len(class_labels))

def plot_segmentation_mask(mask, class_labels, colors):
    fig, ax = plt.subplots(figsize=(10, 10))
    # Display the image
    ax.imshow(mask, cmap=colors, interpolation='nearest')

    # Add class labels
    unique_classes = np.unique(mask)
    for cls in unique_classes:
        # Find the first occurrence of this class in the mask
        indices = np.argwhere(mask == cls)
        if indices.size > 0:
            representative_index = indices[len(indices)//2]  # Pick middle index as representative
            ax.text(representative_index[1], representative_index[0], class_labels[cls],
                    verticalalignment='center', horizontalalignment='center',
                    color='white', fontsize=12, weight='bold')

    plt.axis('off')
    plt.show()

# Call the function with the mask, labels, and colors
plot_segmentation_mask(mask, class_labels, colors)

Ploting Bounding Boxes on the image

To perform this task, we can use some function from surya OCR github repository :

Converting Logits into bounding boxes.

from surya.layout import parallel_get_regions

def logits_to_bboxes(logits,image) :
    correct_shape = (300, 300)  
    logits_temp = F.interpolate(logits, size=correct_shape, mode='bilinear', align_corners=False)
    logits_temp = logits_temp.cpu().detach().numpy().astype(np.float32)

    heatmap_count = logits_temp.shape[1]
    heatmaps = [logits_temp[i][k] for i in range(logits_temp.shape[0]) for k in range(heatmap_count)]
    regions = parallel_get_regions(heatmaps=heatmaps, orig_size=image.size, id2label=model.config.id2label)

    final_bboxes = []
    for i in regions.bboxes :
        final_bboxes.append(i.bbox)
    return final_bboxes


bb = logits_to_bboxes(outputs.logits,image)
print(bb)

# Sample Output
[[41.0, 293.0, 345.0, 517.0],
 [39.0, 141.0, 343.0, 320.0],
 [332.0, 136.0, 528.0, 405.0],
 [332.0, 367.0, 528.0, 714.0],
 [126.0, 62.0, 437.0, 95.0],
 [218.0, 106.0, 343.0, 138.0]]

Now we have bounding boxes that can be used for ploting on images. For this we can directly use the function present in the surya OCR github repository :

from surya.postprocessing.heatmap import draw_bboxes_on_image
from PIL import Image

img = draw_bboxes_on_image(bboxes,Image.open(image_path))

Add-ons

You can directly refer to Compare.ipynb notebook present in the repo to directly start comparing orignal and fine-tuned model.

Downloads last month
7
Safetensors
Model size
30M params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support