#
Python Examples
Using the REST API with Python 3.8
Here you can find some Python sample scripts that document how to use the REST API.
Substitute the Developer Token with your own token which you can find from the Developer Portal
You can also find more Python sample scripts from the repository Immersal Python Tools for Customers
#
Login
import requests
import json
def Login(url, username, password):
complete_url = url + '/login'
data = {
"login": username,
"password": password
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
Login('https://api.immersal.com', 'john.doe@immersal.com', 'mypassword')
# Successful login returns the user ID, developer token and the user level
{
"error": "none",
"userId": 0000,
"token": "081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2",
"level": 0
}
# Token authentication error
{
"error": "auth"
}
#
Account status
Get basic information about the account, e.g. map size limits.
import requests
import json
def AccountStatus(url, token):
complete_url = url + '/status'
data = {
"token": token,
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
AccountStatus('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2')
# Success
{
"error": "none",
"userId": 0000,
"imageCount": 8, # number of images in the workspace
"imageMax": 100,
"eulaAccepted": true,
"level": 0
}
# Token authentication error
{
"error": "auth"
}
#
Clear the workspace
The Immersal Mapper app uses a workspace to store the data before you submit it for construction.
You can exit the app and continue adding images to the workspace later. When you're done with mapping, there's a function to clear the workspace before starting a new mapping session.
import requests
import json
def ClearWorkspace(url, token, deleteAnchorImage):
complete_url = url + '/clear'
data = {
"token" : token,
"anchor" : deleteAnchorImage
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
ClearWorkspace('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2', True)
#
Restore map to workspace
You can also restore all images from previous maps to the workspace. This allows you to extend or update mapped locations.
import requests
import json
def RestoreMap(url, token, mapId):
complete_url = url + '/restore'
data = {
"token" : token,
"id" : mapId
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
RestoreMap('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2', 1234)
# Success
{
"error": "none"
}
# Token authentication error
{
"error": "auth"
}
# Incorrect map id error
{
"error": "job"
}
#
List account maps/jobs
Two variants. One for listing all user's maps and one for searching nearby maps.
You can leave the token string empty to search for public maps
import requests
import json
def ListJobs(url, token):
complete_url = url + '/list'
data = {
"token": token,
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
ListJobs('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2')
import requests
import json
def ListJobsGPS(url, token, latitude, longitude, radius):
complete_url = url + '/geolist'
data = {
"token": token,
"latitude": latitude,
"longitude": longitude,
"radius": radius
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
ListJobsGPS('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2', 60.8978, 26.9193, 200.0)
# Success
{
"error": "none",
"count": 75,
"jobs": [{
"id": 9386,
"type": 0,
"version": "1.8.0",
"creator": "0000",
"size": 5,
"status": "done",
"errno": "0",
"privacy": "0",
"name": "5img",
"latitude": 60.897810474,
"longitude": 26.919315117999997,
"altitude": 122.15941162109375,
"created": "2020-09-11 14:42:56",
"modified": "2020-09-11 14:43:05",
"sha256_al": "e7c12d8a23a21702e129ec05af1423c37f708975cc82c9e05b90111ddcd19d22",
"sha256_sparse": "fb988633c42610bb34f403a816a2602047ddffa461072b2af9721d6fab708ecc",
"sha256_dense": "0efd9b336af155126afe544b52ea6095abce65517a01aeceba9b4add427365fe",
"sha256_tex": "d86c09d4ba792a2df7367368cb94e1b51ced08c63d2761dcf74ed260c51667fd"
}, {
"id": 9384,
"type": 0,
"version": "1.8.0",
"creator": "0000",
"size": 9,
"status": "done",
"errno": "0",
"privacy": "0",
"name": "18test",
"latitude": 60.897835266666661,
"longitude": 26.91930419111111,
"altitude": 119.58823649088542,
"created": "2020-09-11 14:06:02",
"modified": "2020-09-11 14:06:21",
"sha256_al": "11d7ed2099c9b454d2c733261abd7642fdb7462259cf016e175b115bbbbf0aa6",
"sha256_sparse": "b53125dfa9449ed27bb8931b7defab6d725369249a7bf0752641522271d4249c",
"sha256_dense": "d1b942aa1fce7b7db641c9e4a151fe82832195f7a92f8e858510569cbf411c03",
"sha256_tex": "7284d11838d37a0faeee204e71f8085a11cf453c59c41488526e2d2d15c03d41"
}
...
}
# Token authentication error
{
"error": "auth"
}
#
Submit an image to workspace
To construct a map, you need to submit images from the location to the workspace. You need to know the camera intrinsics for map construction and also provide camera position for accurate metric scale and orientation for up-axis alignment.
import requests
import json
import base64
def ConvertToBase64(src_filepath):
with open(src_filepath, 'rb') as imageFileAsBinary:
fileContent = imageFileAsBinary.read()
b64_encoded_img = base64.b64encode(fileContent)
return b64_encoded_img
def SubmitImage(url, token, imagePath):
complete_url = url + '/captureb64'
data = {
"token": token,
"run": 0, # a running integer for the tracker session. Increment if tracking is lost or image is from a different session
"index": 0, # running index for images
"anchor": False, # flag for the image used as an anchor/map origin
"px": 0.0, # camera x position from the tracker
"py": 0.0, # camera y position from the tracker
"pz": 0.0, # camera z position from the tracker
"r00": 1.0, # camera orientation as a 3x3 matrix
"r01": 0.0,
"r02": 0.0,
"r10": 0.0,
"r11": 1.0,
"r12": 0.0,
"r20": 0.0,
"r21": 0.0,
"r22": 1.0,
"fx": 1455.0, # image focal length in pixels on x axis
"fy": 1455.0, # image focal length in pixels on y axis
"ox": 960.0, # image principal point on x axis
"oy": 720.0, # image principal point on y axis
"latitude" : 0.0,
"longitude" : 0.0,
"altitude" : 0.0,
"b64": str(ConvertToBase64(imagePath), 'utf-8') # base64 encoded .png image
}
json_data = json.dumps(data)
# print(json_data)
r = requests.post(complete_url, data=json_data)
print(r.text)
SubmitImage('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2', 'test.png')
# Success
{
"error": "none",
}
# Token authentication error
{
"error": "auth"
}
#
Start map construction
Submits data from the workspace and starts the map construction process.
def StartMapConstruction(url, token, mapName):
complete_url = url + '/construct'
data = {
"token": token,
"name": mapName,
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
StartMapConstruction('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2', 'myFirstMap')
# Success
{
"error": "none",
"id": 1234,
"size": 8 # number of images in the map
}
# Token authentication error
{
"error": "auth"
}
#
Stitch maps together
def StitchMaps(url, token, mapName, mapIds):
complete_url = url + '/fuse'
data = {
"token" : token,
"name" : mapName,
"mapIds": mapIds
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
StitchMaps('https://api.immersal.com', '081df5eeacde56ce0958660bdbbfa4a61e23ffb0790ae41943d64f95c1a60de2', 'stitchedMap', [1234, 5678])
# Success
{
"error": "none",
"id": 91011,
"size": 2
}
#
Download a map
You can leave the token string empty to download a public map
import requests
import json
import base64
def DownloadMap(url, token, mapId):
complete_url = url + '/mapb64'
data = {
"token": token,
"id": mapId
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
# print(r.text)
mapFile = base64.b64decode(json.loads(r.text)['b64'])
open(str(mapId) + '.bytes', 'wb').write(mapFile)
DownloadMap('https://api.immersal.com', '', 125156)
# Success
{
"error": "none",
"sha256_al": "d829ebc9138bb4cb68fe5009b39bf8639998684947052248cfeb10a6fc75ce6b",
"b64": "XQAAAAGSnQEAKUg ... +eFcJ1//88dco"
}
# incorrect map id or invalid token
{
"error": "not found"
}
#
Server localization
In addition to localizing on the device, you can use on-server localization. You can localize to multiple maps at once.
You can leave the token string empty to localize against public maps
import requests
import json
def ServerLocalize(url, token, imagePath):
complete_url = url + '/localize'
with open(imagePath, 'rb') as f:
img_bytes = f.read()
data = {
"token": token,
"fx": 1455.738159, # image focal length in pixels on x axis
"fy": 1455.738159, # image focal length in pixels on y axis
"ox": 962.615967, # image principal point on x axis
"oy": 694.292175, # image principal point on y axis
"mapIds": [{"id": 125156}] # a list of map ids to localize against
}
json_data = json.dumps(data)
json_bytes = json_data.encode()
body = json_bytes + b"\0" + img_bytes
r = requests.post(complete_url, data=body)
print(r.text)
ServerLocalize('https://api.immersal.com', '', 'test.png')
# Successful localization
{
"error": "none",
"success": true,
"map": 125156,
"px": -2.7424788475036621,
"py": 25.899345397949219,
"pz": -8.0469636917114258,
"r00": -0.11626922339200974,
"r01": -0.98492711782455444,
"r02": -0.12806272506713867,
"r10": 0.99144113063812256,
"r11": -0.1228010430932045,
"r12": 0.04432196170091629,
"r20": -0.059380136430263519,
"r21": -0.12181337177753448,
"r22": 0.99077522754669189,
"confidence": 37,
"time": 0.538425577
}
# Failed localization
{
"error": "none",
"success": false,
"map": -1,
"px": 0,
"py": 0,
"pz": 0,
"r00": 0,
"r01": 0,
"r02": 0,
"r10": 0,
"r11": 0,
"r12": 0,
"r20": 0,
"r21": 0,
"r22": 0,
"confidence": 0,
"time": 0.258835888
}
# Token authentication error
{
"error": "auth"
}
# Incorrect map id error, no valid maps to localize against
{
"error": "map count"
}
#
Get map local to global ECEF conversion matrix
To convert the found pose from local map coordinates to global coordinates, you can get the conversion matrix from the server. The map needs to have GPS coordinates to have the conversion matrix.
You can leave the token string empty to get ECEF for a public map
import requests
import json
def ServerECEF(url, token, mapId):
complete_url = url + '/ecef'
data = {
"token": token,
"id": mapId
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
ServerECEF('https://api.immersal.com', '', 125156)
# Success
{
"error": "none",
"ecef": [2884373.6879106648, 1340999.0750837622, 5509844.3036541771, -0.45108634233474731, -0.786507248878479, 0.42181453108787537, -0.20971845090389252, 0.55280953645706177, 0.80648607015609741, -0.86749023199081421, 0.27533257007598877, -0.41430991888046265, 1]
}
# incorrect map id, invalid token or conversion matrix unavailable
{
"error": "not found"
}
#
On-server localization and Visual GPS example
This example code shows how to combine on-server localization and ECEF conversion matrix for a complete Visual GPS pipeline It localizes the test.png test image against a map (125156) from Helsinki streets, converts the local map position to ECEF and WGS84 and then displays the result on Google Maps.
import requests
import json
import base64
import numpy as np
import webbrowser
def ConvertToBase64(src_filepath):
with open(src_filepath, 'rb') as imageFileAsBinary:
fileContent = imageFileAsBinary.read()
b64_encoded_img = base64.b64encode(fileContent)
return b64_encoded_img
def ServerLocalize(url, token, mapId, imagePath):
complete_url = url + '/localizeb64'
data = {
"token": token,
"fx": 727.87, # hard-coded camera intrinsics for the test image
"fy": 727.87,
"ox": 481.44,
"oy": 347.15,
"b64": str(ConvertToBase64(imagePath), 'utf-8'),
"mapIds": [{"id": mapId}]
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
return r.text
def ServerECEF(url, token, mapId):
complete_url = url + '/ecef'
data = {
"token": token,
"id": mapId
}
json_data = json.dumps(data)
r = requests.post(complete_url, data=json_data)
print(r.text)
return r.text
def EcefToWGS84(ecefPos):
a = np.double(6378137.0)
e = np.double(8.1819190842622e-2)
asq = np.power(a, 2)
esq = np.power(e, 2)
x = np.double(ecefPos[0])
y = np.double(ecefPos[1])
z = np.double(ecefPos[2])
b = np.sqrt(asq * (1.0 - esq))
bsq = np.power(b, 2)
ep = np.sqrt((asq - bsq) / bsq)
p = np.sqrt(np.power(x, 2) + np.power(y, 2))
th = np.arctan2(a * z, b * p)
lon = np.arctan2(y, x)
lat = np.arctan2((z + np.power(ep, 2) * b * np.power(np.sin(th), 3)), (p - esq * a * np.power(np.cos(th), 3)))
n = a / (np.sqrt(1.0 - esq * np.power(np.sin(lat), 2)))
alt = p / np.cos(lat) - n
lon = lon % (2 * np.pi)
lat = np.rad2deg(lat)
lon = np.rad2deg(lon)
if(lon > 180.0):
lon = lon - 360.0
return np.array([lat, lon, alt])
def MapPosToEcef(mapPos, m2e):
mp = np.array([ -mapPos[1], -mapPos[0], mapPos[2], 1.0 ])
S = m2e[12]
m = np.array([ [S*m2e[3], S*m2e[6], S*m2e[9], 0.0],
[S*m2e[4], S*m2e[7], S*m2e[10], 0.0],
[S*m2e[5], S*m2e[8], S*m2e[11], 0.0],
[ m2e[0], m2e[1], m2e[2], 1.0] ])
q = mp.dot(m)
return q
def Localize(url, token, mapId, imagePath):
localizationResult = json.loads(ServerLocalize(url, token, mapId, imagePath))
mapPos = np.array([localizationResult['px'], localizationResult['py'], localizationResult['pz'], 1.0])
ecefResult = json.loads(ServerECEF(url, token, mapId))
mapToEcef = np.array(ecefResult['ecef'])
ecefPos = MapPosToEcef(mapPos, mapToEcef)
wgs84 = EcefToWGS84(ecefPos)
print('lat: ' + str(wgs84[0].astype(float)),
'long: ' + str(wgs84[1].astype(float)),
'alt: ' + str(wgs84[2].astype(float)))
# Open location in Google Maps
webbrowser.open_new_tab(f'https://www.google.com/maps/search/{str(wgs84[0])},{str(wgs84[1])}')
Localize('https://api.immersal.com', '', 125156, 'test.png')