Download scripts
All files presented in this documentation can be downloaded from the CREDO project internal page (redmine).
Download detections
The data collected by CREDO is open to all, but for technical and logistical reasons, access is granted by providing the CREDO database admins with the relevant data. Once access has been granted, data collection and analysis can begin.
Below we explain in detail what each step looks like.
Access to detection data
To access the detection data, please contact the CREDO scientific coordinator (credodetector@credo.science) and:
- describe why you want access
- what are you going to do with the data.
In the email, please include your username (provided during registration) and send the email from the email address used during registration.
After approval by the coordinator, the email is forwarded to the CREDO database administrator who will grant access to the data for the account (unique name:username). The access added is assigned to the account and during the data retrieval stage the user will enter their account details.
Users can only start downloading data when they receive an email from the administrator informing them that access has been granted.
Download data
example how to use the function (in terminal / console)
python3 ./credo-data-exporter.py --user USERNAME --password PASSWORD
or
python3 ./credo-data-exporter.py --user USERNAME --password PASSWORD --mapping-type 'all'
An example of what we see when we run the script (in terminal/console).
When you run the script you do nothing - you just wait for it to finish.
Exported data will appear at https://s3.cloud.cyfronet.pl/credo/export_0c9b26cbf75fdb85.json?AWSAccessKeyId=9R47Q1Y3HB802EG2BPUK&Signature=J9jjQu4Ywn9YWVbSkQ8wTtQ2fHo%3D&Expires=1689689598
Waiting for mapping export to finish (retries: 0)
Waiting for mapping export to finish (retries: 1)
Waiting for mapping export to finish (retries: 2)
Waiting for mapping export to finish (retries: 3)
Waiting for mapping export to finish (retries: 4)
Waiting for mapping export to finish (retries: 5)
Waiting for mapping export to finish (retries: 6)
There is more data to download, updating again.
Exported data will appear at https://s3.cloud.cyfronet.pl/credo/export_3c96d65af9bb6de7.json?AWSAccessKeyId=9R47Q1Y3HB802EG2BPUK&Signature=zd9qAq3Y7E2TA5QORSL4DW1R%2BFU%3D&Expires=1689690869
Waiting for mapping export to finish (retries: 0)
Waiting for mapping export to finish (retries: 1)
...
Downloading detection is possible using the credo-data-exporter.py script can be downloaded from the credo.science github repository credo-api-tools.
You can also copy the code on the right-hand side of the page, and below that you will find some comments on the code.
Remember to give the appropriate permissions to the script before running it for the first time:
chmod +x credo-data-exporter.py
and install the appropriate libraries
python3
and requests
By default, the script will download the entire database which is worth around 100 GB and has data from 2018/2019 onwards. If you want to download only the latest data - e.g. from the last month then you need to do the following:
- In the place where you have the credo-data-exporter.py script downloaded, create a new directory named "credo-data-export" and open it.
- Create a file last_exported_detection and/or last_exported_ping (no extension) and open it with a text editing program.
- Insert a 13 character unix timestamp into it - e.g. for datatime 2023-06-01 00:00:00 - you can download it from unixtimestamp.com (the downloaded timestamp has 10 characters so add 3 characters to the end e.g. "000" -> for the mentioned date it will be 1685570400 -> 1685570400000).
- Save the file and run the script (in terminal/console)
When the data is downloaded, a 'credo-data-export' directory will be created, which will contain the following files and directories:
pings -> directory with all the json files with device run time information
detections -> directory with all json files containing detections
user_mapping.json ; device_mapping.json ; team_mapping.json -> files with mappings (list of all users, devices, teams)
device_id -> token of the device from which detections are taken
last_exported_detection - file containing UNIX timestamp (13 characters) of last detection
last_exported_ping - a file containing the UNIX timestamp (13 characters) of the last ping
credo-data-exporter.py
#!/usr/bin/env python3
from __future__ import absolute_import, division, print_function, unicode_literals
import argparse
import errno
import os
import platform
import random
import time
import requests
parser = argparse.ArgumentParser(description="Tool for incremental data export from CREDO")
parser.add_argument("--username", "-u", help="Username")
parser.add_argument("--password", "-p", help="Password")
parser.add_argument("--endpoint", help="API endpoint", default="https://api.credo.science/api/v2")
parser.add_argument("--dir", "-d", help="Path to data directory", default="credo-data-export")
parser.add_argument("--token", "-t", help="Access token, used instead of username and password to authenticate")
parser.add_argument("--max-chunk-size", "-m", help="Maximum number of events in each file", type=int, default=50000)
parser.add_argument("--data-type", "-k", help="Type of event to update (ping/detection/all/none)", default="all")
parser.add_argument("--mapping-type", "-l", help="Type of mapping to update (device/user/all/none)", default="none")
args = parser.parse_args()
args.endpoint = args.endpoint.rstrip("/")
args.dir = args.dir.rstrip("/")
def random_sleep(seconds):
time.sleep(seconds + random.random() * seconds)
def prepare_workspace():
if not os.path.exists(args.dir):
try:
os.makedirs(args.dir)
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
if not os.path.exists(args.dir + "/detections"):
os.makedirs(args.dir + "/detections")
if not os.path.exists(args.dir + "/pings"):
os.makedirs(args.dir + "/pings")
if not os.path.exists(args.dir + "/device_id"):
with open(args.dir + "/device_id", "w+") as f:
f.write(os.urandom(16).hex())
def get_base_request():
with open(args.dir + "/device_id") as f:
device_id = f.readline()
return {
"device_id": device_id,
"device_type": "credo-data-exporter",
"device_model": platform.system(),
"system_version": platform.release(),
"app_version": 1,
}
def get_token():
if args.token:
print("Using token provided by user")
return args.token
j = get_base_request()
j["username"] = args.username
j["password"] = args.password
r = requests.post(args.endpoint + "/user/login", json=j)
if not r.ok:
print(r.json())
r.raise_for_status()
return r.json()["token"]
def update_mapping(mapping_type):
j = get_base_request()
j["mapping_type"] = mapping_type
r = requests.post(args.endpoint + "/mapping_export", json=j, headers={"authorization": "Token " + get_token()})
if not r.ok:
print(r.json())
r.raise_for_status()
export_url = r.json()["url"]
print("Exported mapping will appear at {}".format(export_url))
random_sleep(30)
retries = 0
while True:
r = requests.get(export_url)
if r.status_code == 404:
print("Waiting for mapping export to finish (retries: {})".format(retries))
retries += 1
if retries in range(10):
random_sleep(30)
elif retries in range(10, 15):
random_sleep(300)
else:
print("Exiting because data was not ready in time. Try again later.")
return
else:
if not r.ok:
print(r)
r.raise_for_status()
break
with open("{}/{}_mapping.json".format(args.dir, mapping_type), "wb") as f:
for chunk in r.iter_content(4096):
f.write(chunk)
def update_data(data_type):
repeat = False
time_since = 0
if os.path.exists(args.dir + "/last_exported_" + data_type):
with open(args.dir + "/last_exported_" + data_type) as f:
time_since = int(f.readline())
j = get_base_request()
j["since"] = time_since
j["until"] = int((time.time() + 3600 * 24) * 1000)
j["limit"] = args.max_chunk_size
j["data_type"] = data_type
r = requests.post(args.endpoint + "/data_export", json=j, headers={"authorization": "Token " + get_token()})
if not r.ok:
print(r.json())
r.raise_for_status()
export_url = r.json()["url"]
print("Exported data will appear at {}".format(export_url))
random_sleep(30)
retries = 0
while True:
r = requests.get(export_url)
if r.status_code == 404:
print("Waiting for mapping export to finish (retries: {})".format(retries))
retries += 1
if retries in range(10):
random_sleep(60)
elif retries in range(10, 25):
random_sleep(300)
else:
print("Exiting because data was not ready in time. Try again later.")
return
else:
if not r.ok:
print(r)
r.raise_for_status()
events = r.json()[data_type + "s"]
if len(events) == args.max_chunk_size:
repeat = True
if events:
last_timestamp = events[-1]["time_received"]
del events
with open(
"{}/{}s/export_{}_{}.json".format(args.dir, data_type, time_since, last_timestamp), "wb"
) as f:
for chunk in r.iter_content(4096):
f.write(chunk)
with open(args.dir + "/last_exported_" + data_type, "w+") as f:
f.write(str(last_timestamp))
else:
print("No new events")
break
if repeat:
del r
print("There is more data to download, updating again.")
update_data(data_type)
def main():
if args.mapping_type in ["user", "all"]:
print("Updating user mapping")
update_mapping("user")
if args.mapping_type in ["device", "all"]:
print("Updating device mapping")
update_mapping("device")
if args.mapping_type in ["team", "all"]:
print("Updating team mapping")
update_mapping("team")
if args.data_type in ["detection", "all"]:
print("Updating detections")
update_data("detection")
if args.data_type in ["ping", "all"]:
print("Updating pings")
update_data("ping")
if __name__ == "__main__":
prepare_workspace()
main()
If you do not plan to edit the download script you can skip this section.
The first part in which a 'parser' is created with the following arguments:
--username -> unique username/account from the application
--password -> password to user account
--endpoint -> endpoint to the database from which detections will be taken - all code is open source so we allow for the possibility that endpoints will be different.
--dir -> path for writing data to the disk, change if different from the default path.
--token -> each user has a unique token, which is taken in the first stage, used instead of username and password for authentication.
--max-chunk-size -> Number of events in a single json file. If you do not want large files (weight > 100 mb) then you can set, for example, 10000
--data-type -> Type of data downloaded
--mapping-type -> additional download of mapping of users (public information only), devices, teams
def prepare_workspace - A function that creates directories and files - on first start-up.
def get_base_request and def get_token - functions for obtaining a user token.
def update_mapping and def update_data - data download and mapping functions. Both have an imposed maximum download time so If you have slower internet and the script breaks because it takes too long to download then you can change the 'sleep' option in the if for 'retries' (e.g.random_sleep(300) -> random_sleep(400), time in seconds).
if name == "main": -> a condition that runs the main() function, which will call the entire data download stage.
Example download process
About Data
Information on what information is contained in specific data
Detection
Example detection (in json file)
{"detections":
[
{
"id": 31927159,
"accuracy": 38.0,
"altitude": 267.0,
"frame_content": "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",
"height": 480,
"width": 640,
"x": 100,
"y": 258,
"latitude": 267.0,
"longitude": 19.44615015,
"provider": "gps",
"timestamp": 1683705157785,
"time_received": 1683705156043,
"source": "api_v2",
"visible": true,
"metadata": "
{
\"app\":
\"cd_orig\",
\"version\": 25,
\"max\": 119,
\"average\": 17.68880859375,
\"blacks\": 999.9934895833334,
\"black_threshold\": 37,
\"ax\": 0.40581718,
\"ay\": -0.26994625,
\"az\": 9.776723,
\"orientation\": 0.0,
\"temperature\": 29,
\"true_time\": 1683705155656
}",
"device_id": 22937,
"user_id": 36470,
"team_id": 12279
},
...
{
...
}
]
}
example file named
export_1683705140942_1683706659030.json
Field | Description | Constraint |
---|---|---|
id | unique detection identifier | Integer |
accuracy | Location accuracy (meters) | Floating point number |
altitude | Altitude (meters) | Floating point number |
frame_content | Base64 encoded and cropped PNG image of event (64x64 px) | 10000 characters or fewer |
height | Device sensor height | Integer |
width | Device sensor width | Integer |
x | X coordinate of event | Integer |
y | Y coordinate of event | Integer |
latitude | GPS latitude | Floating point number |
longitude | GPS longitude | Floating point number |
provider | Location provider | 20 characters or fewer |
timestamp | UNIX timestamp of detection time (in milliseconds) | Integer |
time_received | UNIX timestamp delivery of detections to the base (in milliseconds, 13 characters) | Integer |
source | Information on the version of the application | 20 characters or fewer |
visible | Information on detection visibility on api.credo.science | Boolean (True / False) |
metadata | Additional metadata (JSON object serialized to string) | 10000 characters or fewer |
device_id | Unique identifier of the device | Integer |
user_id | Unique identifier of the user | Integer |
team_id | Unique identifier of the team | Integer |
Information available in "metadata" in the version of the application available on 2023-07-12
Field | Description |
---|---|
app | TODO: description to be added |
cd_orig | TODO: description to be added |
version | version of the application |
max | maximum pixel value (brightness in the grey scale range 0 - 255) |
average | average pixel value |
blacks | threshold value below which a pixel is treated as dark/black |
ax | TODO: description to be added |
ay | TODO: description to be added |
az | TODO: description to be added |
orientation | TODO: description to be added |
temperature | battery temperature |
true_time | UNIX timestamp on a smartphone |
Pings
Example pings (in json file)
{"pings":
[
{
"id": 19018423,
"timestamp": 1668417412814,
"time_received": 1668417413025,
"delta_time": 31642,
"on_time": 31642,
"metadata": "",
"device_id": 23456,
"user_id": 37011
},
...
{
...
}
]
}
example file named (similar name as for detection - but different directory - "pings/" )
export_1668417410470_1668417958113.json
Field | Description | Constraint |
---|---|---|
id | unique pings identifier | Integer |
timestamp | UNIX timestamp of ping time (in milliseconds) | Integer |
time_received | UNIX timestamp delivery of pings to the base (in milliseconds, 13 characters) | Integer |
delta_time | Time since last detection / ping / startup (in milliseconds) | Integer |
on_time | Duration of detector working and registering events (in milliseconds, resets every ping) | Integer |
metadata | Additional metadata (JSON object serialized to string) | 10000 characters or fewer |
device_id | Unique identifier of the device | Integer |
user_id | Unique identifier of the user | Integer |
Device mapping
Example device mapping (in json file)
{"devices":
[
{
"id": 1,
"user_id": 2,
"device_type": "phone_android",
"device_model": "LG-H320",
"system_version": "21-5.0.1"
},
...
{
...
}
]
}
file name
device_mapping.json
Field | Description | Constraint |
---|---|---|
id | unique device identifier | Integer |
user_id | Unique identifier of the user-owner of the device | Integer |
device_type | Type of device | Characters |
device_model | Model (name) of device | Characters |
system_version | System version (e.g. Android) | Characters |
Team mapping
Example team mapping (in json file)
{"teams":
[
{
"id": 2,
"name": "IFJ"
},
...
{
...
}
]
}
file name
team_mapping.json
Field | Description | Constraint |
---|---|---|
id | unique team identifier | Integer |
name | unique name of the team | Characters |
User mapping
Example user mapping (in json file)
{"users":
[
{
"id": 1,
"username": "root",
"display_name": "root"
},
...
{
...
}
]
}
file name
user_mapping.json
Field | Description | Constraint |
---|---|---|
id | unique team identifier | Integer |
username | unique name of the user | Characters |
display_name | displayed on api.credo.science / public user name | Characters |
Example script
path_src.py
# Raw detection data downloaded with general CREDO script
start_path = "/media/slawekstu/CREDO-skrypty/api/"
detections_path = start_path + "credo-data-export/detections/"
pings_path = start_path + "credo-data-export/pings/"
# Mappings
device_mapping_src = start_path + "credo-data-export/device_mapping.json"
user_mapping_src = start_path + "credo-data-export/user_mapping.json"
team_mapping_src = start_path + "credo-data-export/team_mapping.json"
example how to use paths from path_src.py
from path_src import device_mapping_src, team_mapping_src, user_mapping_src
list_mapping = {
"devices": device_mapping_src,
"teams": team_mapping_src,
"users": user_mapping_src
}
All of the scripts presented below were created in the PyCharm IDE as a "Tutorials" project and all of the address variables for paths on disk were collected in a single file "path_src.py"
Example of mapping
mapping.py
import libraries and paths from another file
from path_src import device_mapping_src, team_mapping_src, user_mapping_src
import json
create a dictionary for the paths used
list_mapping = {
"devices": device_mapping_src,
"teams": team_mapping_src,
"users": user_mapping_src
}
functions read_mapping (from /functions/mapping.py)
def read_mapping(option, parameter, value):
with open(list_mapping[option]) as json_file:
json_load = json.load(json_file)
score = []
for record in json_load[option]:
if record[parameter] == value:
score.append(record)
if len(score) == 1:
score = score[0]
return score
example how to use the function (01_mapping.py)
from functions.mapping import read_mapping
# Global variables
our_username = "smph-kitkat"
our_team = "Fizyka kluczem"
def users_devices(username):
"""
Find information about the user you are looking for and then check what devices he has.
:param username: Username of the searched user
:return:
"""
our_user = read_mapping("users", "username", username)
print("From user_mapping: ", our_user)
# Our user's devices
our_devices = read_mapping("devices", "user_id", our_user["id"])
print("From device_mapping: ", our_devices)
def main():
users_devices(our_username)
print("From team_mapping: ", read_mapping("teams", "name", our_team))
if __name__ == '__main__':
main()
Example what we should get
From user_mapping: {'id': 1510, 'username': 'smph-kitkat', 'display_name': 'smph-kitkat'}
From device_mapping: [{'id': 1397, 'user_id': 1510, 'device_type': 'heatqlte', 'device_model': 'SM-G357FZ', 'system_version': '19-4.4.4'}, {'id': 14286, 'user_id': 1510, 'device_type': 'HWEVA', 'device_model': 'EVA-L19', 'system_version': '24-7.0'}, {'id': 14966, 'user_id': 1510, 'device_type': 'heatqlte', 'device_model': 'SM-G357FZ', 'system_version': '19-4.4.4'}, {'id': 20177, 'user_id': 1510, 'device_type': 'hero2lte', 'device_model': 'SM-G935F', 'system_version': '26-8.0.0'}]
From team_mapping: {'id': 10983, 'name': 'Fizyka kluczem'}
It is a good idea to keep functions that we will use frequently in separate directories. For this reason, we have created the function for reading mappings "read_mapping" in the "functions" directory.
If we would like to use this function to return the entire dictionary from the mapping file, it is sufficient to return the variable to which we have assigned the read file, i.e:
return json_load
Example of read detect / save img
detect_png.py
import libraries and paths from another file
import os
import json
import base64
from path_src import detections_path, png_detections_path
os.makedirs(png_detections_path + "img/", exist_ok=True)
functions read_detections (from /detect_png.py)
def read_detections(values,max):
# list of all json files and sort (after time)
list_file_json = sorted(os.listdir(detections_path))
list_file_json = list_file_json
index = 0
n_images = 0
# read one file json
one_file_name = list_file_json[values]
json_path = detections_path + one_file_name
print(json_path)
with open(json_path) as json_file:
json_load = json.load(json_file)
# loop through the elements in the selected file
for detection in json_load['detections']:
img = detection["frame_content"]
name = detection["id"]
# is detection visible on api.credo.science
visible = detection["visible"]
# if Yes continue, but only for max img
if visible is True and n_images < max:
print(visible)
image = img.encode('ascii')
# save img as png
adres = png_detections_path + "img/" + str(name) + ".png"
with open(adres, "wb") as fh:
fh.write(base64.decodebytes(image))
n_images += 1
example how to use
def main():
read_detections(30,200)
if __name__ == '__main__':
main()
In the first step, we need to create the appropriate directory using "os.makedirs".
In the "read_detections" function, we first list all json files in the location specified in the "detections_path" variable. We will sort the results obtained and thanks to this we will have files in the order of detection time.
We need to assign them to another variable to be able to reference by index. The following lines are responsible for this.
list_file_json = sorted(os.listdir(detections_path))
list_file_json = list_file_json
The detection time (unix timestamp, 13 characters) is already given in the file name as follows:
export_first-time_last-time.json
e.g. export_1683705140942_1683706659030.json
Then we take one file which is "value" in the list and read it.
We check if the detection is visible on api.credo.science ("visible" == True) and save it to a file - but only "max" images.
Useful functions
import os
import base64
import numpy as np
from io import BytesIO
from base64 import decodebytes
from PIL import Image # "install package Pillow"
On the right you will find a list of all the libraries that have been used in the examples below.
frame2png
def frame2png(frame_content, name, save_path):
decode = frame_content.encode('ascii')
os.makedirs(save_path, exist_ok=True)
with open(save_path + str(name) + ".png", "wb") as fh:
fh.write(base64.decodebytes(decode))
In order to be able to save to a file, the image code must first be encoded - the base64 library allows us to do this.
rgb2gray
def rgb2gray(rgb, coeff=None, normalize=True):
if coeff is None:
coeff = (0.299, 0.587, 0.114)
dot_product = np.dot(rgb[..., :3].astype('int64'), coeff)
gray = (dot_product / sum(coeff)).astype('uint8') if normalize else dot_product.astype('int64')
return gray
There are many methods for converting colour images into greyscale images. This function allows you to fine-tune the colour conversion pattern.
image_info
def image_info(frame_content):
decode = decodebytes(str.encode(frame_content))
imga = np.array(Image.open(BytesIO(decode)))
gray_img = rgb2gray(imga)
# stworzenie obrazka z bibliotekÄ… Pillow
image2 = Image.fromarray(imga)
detect_info = {"maximum brightness": gray_img.max(), "average brightness": gray_img.mean(),
"brighter than 60": np.sum(gray_img > 60), "color mode": image2.mode, "width": image2.size[0],
"height": image2.size[1]}
return detect_info
Example of how to extract information from a picture. More details can be found in the Pillow library.
img2save
def img2save(img, save_path, name):
os.makedirs(save_path, exist_ok=True)
im = Image.fromarray(img)
im.save(save_path + str(name) + ".png",)
Example how tou use aforementioned functions
# data required by the aforementioned functions
frame_content = "iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAIAAAC1nk4lAAAFgElEQVRoBc3BzYucBwHH8e/32dlkkxi1TRFDKJHUROIbnrypePKiePCg/gVepeIlR3sSPOpJKQgSPAhFvHhQqdRYxB5KD1IPCr4RKDQSQnbT3Zn5+TzzPDvPM7PzujOJ+XykS0mYoiTIpqQR1iaEMRlTSgknKYZwSkKoSCUsIYSKEE6SFRUQniiphJNEqSUsII+dEKZJJXTJigQhPA2kS0lYQJ4GcpKSMEWeHrIiWUQIT4ysSGYTQkUa4XGTpZQEmU0ILSE8brIi+T8QwkmyInl6SE1JWECeHlJTEmZSKmGKEBDCEyZLKZUghIY0wkqEMJcQVicLKK1QEsIEISwhlbAtsoBSS5CKECYIYTkhbIssoLSCNMJjJK2IoRS6ZKlCCGNha6QSWtIqCgZBSJgkS0lFGmFToaWUEmpKSaklNEKHLFVAQAjbEeZSTkqQLlmqgLAdYQlFSRhLKElDSrKUbE1YQqkl1GSClKRLSZgiKwkTZEJYg5JQkwlSki6llNAlywUoCnYLejAYMAh9DGNhNiWhpiR0yUwyppQSlAlhqVjQ2+HCBd5/yMERR30exiOEUAlT1CRqEkBlJAljMpNMUcYEQmgIYbbY4+wzXLly4aP3Hv73He72edDnPoZaaCkgJOGYCiRhTOaRKcqYIbSEMFuKHnvn+dDHL3z269/45p0/3/nVW7cH3BvQD3061CTMoiahJgvITErJUAotqYSKEBqhx9keF5/l+e995/uXL1+9/crLv7jz0yGP+jwMfQigMpJETQKoSeiSeZSSTFESlJKhFhpCmC32eN8Oe5e9fuvbL71w/dpPXv7RK2/cHvCgz0HoA2oS5pEVyRSllFAqaITlAvR2OQfnP7J389aLt25cu/GDH770mzd/OeTwgAch4ZgQJsjqZIrSCtIIKwm7XAxnP3bxky9+67sH+/d+9vMf/+Xem4ccHNIfEmRz0qV0GcbCDEKYlqJg7wxnP8CzX/nilz7zqZu//eOvX3vj1QccHDKMjCm1hLXISUpJKQJhwNrCLru77D5XfPBrX/7qhy8/9/vXX/3DW6/v0x/KkJZSSlhMSRiTmZTSDgxDaAihIYQFDD16Z/Dy+Utf+NznL1165nd/eu2v//z7o8F7RwnHlFLCPEotYUy6lISxAkJLGqEihMXOxjPwwpVrN164/o+7/3n34f137r+7f7jf74cNyBQloSYNIVSkElZk6MFe0bt44eLhUf/mpz/x4ODh2397+9HBERuwlETlWBJq0hBCQwjThNASwtjuTjHoD3f06vNXd87s/Ovuv/f33+O0FBlRGUlCTRpCqEgjTJNKaEgr1Ay9wr1z5x71Hx0dDtmAzCPTpBJaQqjIXAUMGCt2GA5oCWFdMpMsIZXQkEXCdkmHSiOhoSSsRCaElhCQStiQHFOBJIAKQUoJcwmhJY2AVEJLCKtTSgknyYjKsSQqlSAJa5BKQAgThLA6JWEmGVE5lgRQk1CStQmhIY2wLTKiMpKEETUJNRlTEpaQSmhIJbSEcGrSoSZRk9AlSlfCEkKoSCM0pBIaQliLHFOBJGoSukTpSliDEJBG2JB0qIwkoUuUkgWlDElYmzTCqSklWYUoJQtKwwFzCeGUhDCm1BKmyIiahHlEqVmQIQmVgLRkQtiEUkuYIqsQZSyhEhqyhFTCtsgqpKYkVMIEmU1aYVtkFTIhzKUQpkglIITNyVIyLSwiLWkFpBE2IcdUIImaBFCTUJKSklAJLSkpGdKQhlDAkLnC6UiHykgSOpTQERrSECKlBGnItLAV0qFyLAkjahJKoiRISUioKAkgpQRBKKgMQFphK2SSykgSThKCmoSStAJKyTAWGkLYFpmkMpKEFSkJNWUHhiEglYBQwID1SCWc9D+i6zigtzw+rgAAAABJRU5ErkJggg=="
name = "ex_1"
save_path = "/media/slawekstu/CREDO-skrypty/api/skrypty/pyCharm/data_save/Tutorials/"
os.makedirs(save_path,exist_ok=True)
frame2png(frame_content, name, save_path)
print(image_info(frame_content))
Example what we should get from
print(image_info(frame_content))
{'maximum brightness': 175, 'average brightness': 3.9580555555555557, 'brighter than 60': 32, 'color mode': 'RGB', 'width': 60, 'height': 60}
Writing to a file from an array is a little different than writing an image from a frame_content (base 64).
rescale
def rescale(frame_content, name, save_path, scale):
decode = decodebytes(str.encode(frame_content))
imga = np.array(Image.open(BytesIO(decode)))
imga_big = imga.repeat(scale, axis=0).repeat(scale, axis=1)
img2save(imga_big, save_path, str(name) + "_big")
print(imga.shape, "reskala: ", imga_big.shape)
Example how tou use rescale() functions
frame_content = "iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAIAAAC1nk4lAAAFgElEQVRoBc3BzYucBwHH8e/32dlkkxi1TRFDKJHUROIbnrypePKiePCg/gVepeIlR3sSPOpJKQgSPAhFvHhQqdRYxB5KD1IPCr4RKDQSQnbT3Zn5+TzzPDvPM7PzujOJ+XykS0mYoiTIpqQR1iaEMRlTSgknKYZwSkKoSCUsIYSKEE6SFRUQniiphJNEqSUsII+dEKZJJXTJigQhPA2kS0lYQJ4GcpKSMEWeHrIiWUQIT4ysSGYTQkUa4XGTpZQEmU0ILSE8brIi+T8QwkmyInl6SE1JWECeHlJTEmZSKmGKEBDCEyZLKZUghIY0wkqEMJcQVicLKK1QEsIEISwhlbAtsoBSS5CKECYIYTkhbIssoLSCNMJjJK2IoRS6ZKlCCGNha6QSWtIqCgZBSJgkS0lFGmFToaWUEmpKSaklNEKHLFVAQAjbEeZSTkqQLlmqgLAdYQlFSRhLKElDSrKUbE1YQqkl1GSClKRLSZgiKwkTZEJYg5JQkwlSki6llNAlywUoCnYLejAYMAh9DGNhNiWhpiR0yUwyppQSlAlhqVjQ2+HCBd5/yMERR30exiOEUAlT1CRqEkBlJAljMpNMUcYEQmgIYbbY4+wzXLly4aP3Hv73He72edDnPoZaaCkgJOGYCiRhTOaRKcqYIbSEMFuKHnvn+dDHL3z269/45p0/3/nVW7cH3BvQD3061CTMoiahJgvITErJUAotqYSKEBqhx9keF5/l+e995/uXL1+9/crLv7jz0yGP+jwMfQigMpJETQKoSeiSeZSSTFESlJKhFhpCmC32eN8Oe5e9fuvbL71w/dpPXv7RK2/cHvCgz0HoA2oS5pEVyRSllFAqaITlAvR2OQfnP7J389aLt25cu/GDH770mzd/OeTwgAch4ZgQJsjqZIrSCtIIKwm7XAxnP3bxky9+67sH+/d+9vMf/+Xem4ccHNIfEmRz0qV0GcbCDEKYlqJg7wxnP8CzX/nilz7zqZu//eOvX3vj1QccHDKMjCm1hLXISUpJKQJhwNrCLru77D5XfPBrX/7qhy8/9/vXX/3DW6/v0x/KkJZSSlhMSRiTmZTSDgxDaAihIYQFDD16Z/Dy+Utf+NznL1165nd/eu2v//z7o8F7RwnHlFLCPEotYUy6lISxAkJLGqEihMXOxjPwwpVrN164/o+7/3n34f137r+7f7jf74cNyBQloSYNIVSkElZk6MFe0bt44eLhUf/mpz/x4ODh2397+9HBERuwlETlWBJq0hBCQwjThNASwtjuTjHoD3f06vNXd87s/Ovuv/f33+O0FBlRGUlCTRpCqEgjTJNKaEgr1Ay9wr1z5x71Hx0dDtmAzCPTpBJaQqjIXAUMGCt2GA5oCWFdMpMsIZXQkEXCdkmHSiOhoSSsRCaElhCQStiQHFOBJIAKQUoJcwmhJY2AVEJLCKtTSgknyYjKsSQqlSAJa5BKQAgThLA6JWEmGVE5lgRQk1CStQmhIY2wLTKiMpKEETUJNRlTEpaQSmhIJbSEcGrSoSZRk9AlSlfCEkKoSCM0pBIaQliLHFOBJGoSukTpSliDEJBG2JB0qIwkoUuUkgWlDElYmzTCqSklWYUoJQtKwwFzCeGUhDCm1BKmyIiahHlEqVmQIQmVgLRkQtiEUkuYIqsQZSyhEhqyhFTCtsgqpKYkVMIEmU1aYVtkFTIhzKUQpkglIITNyVIyLSwiLWkFpBE2IcdUIImaBFCTUJKSklAJLSkpGdKQhlDAkLnC6UiHykgSOpTQERrSECKlBGnItLAV0qFyLAkjahJKoiRISUioKAkgpQRBKKgMQFphK2SSykgSThKCmoSStAJKyTAWGkLYFpmkMpKEFSkJNWUHhiEglYBQwID1SCWc9D+i6zigtzw+rgAAAABJRU5ErkJggg=="
name = "ex_1"
save_path = "/media/slawekstu/CREDO-skrypty/api/skrypty/pyCharm/data_save/Tutorials/"
rescale(frame_content, name, save_path, 15)
ex_1.png (60, 60, 3)
ex_1_big.png (900, 900, 3)