# Image upload

Here is an example of creating a new dataset and uploading images to it with categories and annotations.

# Create a dataset

def create_dataset(dataset_name):
    request = {
        "name": dataset_name
    }
    response = requests.post(url=f'{OSAI_URL}/Datasets', headers=HEADERS, json=request).json()
    dataset_id = response["id"]
    return dataset_id

# Create a category

When using the API to upload images from categories, you will need to create the categories you need in advance so that when you upload images, you provide the correct id of the categories used in the dataset.

def create_category(dataset_id, category_name):
    request = {
        "datasetId": dataset_id,
        "name": category_name
    }
    response = requests.post(
        f'{OSAI_URL}/Categories', headers=HEADERS, json=request
    ).json()
    return respone

# Upload

# Start and end of upload

You must start the upload manually. The dataset will then be locked and you will be able to upload photos. Once you have finished uploading photos, you need to stop uploading to unlock the dataset. If you forget to do this, the dataset will automatically unlock itself after a certain period of time of not receiving new photos.

def start_uploading(dataset_id):
    response = requests.post(
        f'{OSAI_URL}/Datasets/{dataset_id}/StartUploading', headers=HEADERS
    ).json()
    return response


def finish_uploading(dataset_id, upload_id):
    response = requests.post(
        f'{OSAI_URL}/Datasets/{dataset_id}/FinishUploading', headers=HEADERS, params={"datasetUploadId": upload_id}
    )
    print(response)    

# Send images

def send_images(dataset_id, upload_id, path):
    images_data = get_images_data(path)
    categories_id = create_categories_for_images_data(images_data, dataset_id)

    for image in images_data:
        response = upload_image(dataset_id, categories_id, upload_id, image)
        print(response)

Photos are sent as multipart/form-data, which must include:

  • DatasetID
  • DatasetUploadID
  • ImagesMetadata - containing data such as:
    • name
    • annotationType
    • annotation
    • classifyCategoryId
  • ImagesFles
def upload_image(dataset_id, categories_id, upload_id, image):
    annotation = get_annotation(image)
    encoded_content = base64.b64encode(bytes(annotation['content'], 'utf-8'))
    file_name = image['name'] + image['extension']
    file_path= image['path'] + os.sep + file_name
    form_data = {
            'datasetId': dataset_id,
            'datasetUploadId': upload_id,
            'imagesMetadata[0][name]': file_name,
            'imagesMetadata[0][annotationType]': annotation['type'],
            'imagesMetadata[0][annotation]': encoded_content,
            'imagesMetadata[0][classifyCategoryId]': categories_id[image['category']],
        }
    files = {'imagesFiles': (file_name, open(file_path, 'rb'))} 
    response = requests.post(
            f'{OSAI_URL}/Images/UploadChunk',headers=HEADERS, 
            params={'skipUnsupportedImages': False}, data=form_data, files=files
        )
    
    return response

# Get image metadata

To make it easier to manage uploaded images, you can break down information about a photo by uploading classifications and annotations to it.

def get_images_data(path):    
    image_list =[] 
    for root, _dirs, files in os.walk(path):
        for file in files:
            suffix = pathlib.Path(file).suffix
            name = pathlib.Path(file).stem
            if suffix == '.jpg' or suffix == '.png':
                annotation = None
                if  os.path.isfile(root + os.sep + name + '.xml'):
                    annotation = '.xml'
                image_list.append({'path': root, 'name': name, 'extension': suffix, 'category': os.path.basename(root), 'annotation_file':annotation})
    
    return image_list           

# Add categories to upload

In this example, we use the parent folder to assign the classification.

def create_categories_for_images_data(images_data, dataset_id):
    categories = []
    categories_ids = {}
    for image in images_data:
        if image['category'] not in categories:
            categories.append(image['category'])
    for category in categories:
        categories_ids[category] = create_category(dataset_id, category)['id']
    
    return categories_ids

# Add .xml annotation to upload

The .xml files require that the metadata for the images include a valid annotationType so that the server will know how to decode the information it has. The .xml file should be uploaded as a encoded string in base64.

def get_annotation(image_data):
    annotation = {}
    if image_data['annotation_file'] == '.xml':
        file_path = image_data['path']+ os.sep + image_data['name'] + image_data['annotation_file']
        annotation['type']=0
        with open(file_path) as file:
            annotation['content'] = file.read()
    else:
        annotation['type']=None
        annotation['content'] = ''
    
    return annotation

# Example

import requests
import os
import pathlib
import base64

OSAI_URL = 'https://app-eu.onestepai.com/api'
API_TOKEN = 'OUR ACCESS TOKEN GENERATED IN OSAI'
HEADERS = {'Authorization': None}

def main():
    token = sign_in()
    global HEADERS   
    HEADERS = {'Authorization': f'Bearer {token.text}'}

    print('------Create dataset------')
    dataset_id = create_dataset('holy_hand_grenade')
    print('-----Uploading------')
    upload_id = start_uploading(dataset_id)
    send_images(dataset_id, upload_id, "zdj")
    finish_uploading(dataset_id, upload_id)


def sign_in():
    response = requests.post(
        url=f'{OSAI_URL}/Users/SignIn', json=API_TOKEN)
    return response


def create_dataset(dataset_name):
    request = {
        'name': dataset_name
    }
    response = requests.post(url=f'{OSAI_URL}/Datasets', headers=HEADERS, json = request).json()
    dataset_id = response['id']
    print(f'Dataset created with id: {dataset_id}')
    return dataset_id


def create_category(dataset_id, category_name):
    request = {
        'datasetId': dataset_id,
        'name': category_name
    }
    response = requests.post(
        f'{OSAI_URL}/Categories', headers=HEADERS, json=request
    ).json()
    return response


def send_images(dataset_id, upload_id, path):
    images_data = get_images_data(path)
    categories_id = create_categories_for_images_data(images_data, dataset_id)

    for image in images_data:
        response = upload_image(dataset_id, categories_id, upload_id, image)
        print(response)


def start_uploading(dataset_id):
    response = requests.post(
        f'{OSAI_URL}/Datasets/{dataset_id}/StartUploading', headers=HEADERS
    ).json()
    return response


def finish_uploading(dataset_id, upload_id):
    response = requests.post(
        f'{OSAI_URL}/Datasets/{dataset_id}/FinishUploading', headers=HEADERS, params={'datasetUploadId': upload_id}
    )
    print(response)    


def upload_image(dataset_id, categories_id, upload_id, image):
    annotation = get_annotation(image)
    encoded_content = base64.b64encode(bytes(annotation['content'], 'utf-8'))
    file_name = image['name'] + image['extension']
    file_path= image['path'] + os.sep + file_name
    form_data = {
            'datasetId': dataset_id,
            'datasetUploadId': upload_id,
            'imagesMetadata[0][name]': file_name,
            'imagesMetadata[0][annotationType]': annotation['type'],
            'imagesMetadata[0][annotation]': encoded_content,
            'imagesMetadata[0][classifyCategoryId]': categories_id[image['category']],
        }
    files = {'imagesFiles': (file_name, open(file_path, 'rb'))} 
    response = requests.post(
            f'{OSAI_URL}/Images/UploadChunk',headers=HEADERS, 
            params={'skipUnsupportedImages': False}, data=form_data, files=files
        )
    
    return response


def get_annotation(image_data):
    annotation = {}
    if image_data['annotation_file'] == '.xml':
        file_path = image_data['path']+ os.sep + image_data['name'] + image_data['annotation_file']
        annotation['type']=0
        with open(file_path) as file:
            annotation['content'] = file.read()
    else:
        annotation['type']=None
        annotation['content'] = ''
    
    return annotation


def create_categories_for_images_data(images_data, dataset_id):
    categories = []
    categories_ids = {}
    for image in images_data:
        if image['category'] not in categories:
            categories.append(image['category'])
    for category in categories:
        categories_ids[category] = create_category(dataset_id, category)['id']
    
    return categories_ids


def get_images_data(path):    
    image_list =[] 
    for root, _dirs, files in os.walk(path):
        for file in files:
            suffix = pathlib.Path(file).suffix
            name = pathlib.Path(file).stem
            if suffix == '.jpg' or suffix == '.png':
                annotation = None
                if  os.path.isfile(root + os.sep + name + '.xml'):
                    annotation = '.xml'
                image_list.append({'path': root, 'name': name, 'extension': suffix, 'category': os.path.basename(root), 'annotation_file': annotation})
    
    return image_list           
                

if __name__ == '__main__':
    main()
------Create dataset------
Dataset created with id: 514
-----Uploading------
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>