# Image upload

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

# Create 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 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
    }
    respone = requests.post(
        f'{OSAI_URL}/Categories', headers=HEADERS, json=request
    ).json()
    return respone

# Upload

# Start and end of upload

You will need to 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 must stop uploading to unlock the dataset. If you forget to do so, 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
  • ImagsMetadata - 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

For easier management of 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": 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.get(
        url=f'{OSAI_URL}/Users/SignIn', params={"token": 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 creaded with id: {dataset_id}')
    return dataset_id


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


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 creaded with id: 514
-----Uploading------
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>