Docs
Search…
Matlab (linux) Example
Welcome to APEER's MATLAB module example. This example serves as a starting point to show you how to build modules based on MATLAB for APEER

Introduction

What to bear in mind?

MATLAB is a commercial software distributed by The MathWorks, Inc.. It requires a valid license to run. However the MATLAB runtime can be used by anyone without holding a license and can be obtained here. The runtime is OS dependent and needs the MATLAB code to compiled for the dedicated OS. To compile your MATLAB code you need the MATLAB compiler which is distributed along with MATLAB and requires the paid license.
Therefore MATLAB modules on APEER only support compiled MATLAB programs. Due to the fact, that APEER uses the Linux runtime, the MATLAB code can be compiled only in the Linux environment.

How to start?

In order to run the module we need following files and folders:
  • srcfolder with the apeer_main.m and execute.m files
  • libfolder with the ApeerDevKit.m library
  • bin folder with the binary file (file after code compilation)
  • Dockerfile
  • module_specification.json

Matlab (linux) Example Module

Writing the module

The entry point file apeer_main.m takes care of connecting your algorithms/code/program to the APEER environment.
apeer_main.m
1
function [] = apeer_main(varargin)
2
3
adk = ApeerDevKit(varargin{:});
4
inputs = adk.get_inputs();
5
6
outputs = execute(inputs.input_image);
7
8
adk.set_file_output('output_image', outputs.output_image);
9
adk.finalize();
10
11
end
Copied!
To make interacting with the APEER environment as easy as possible we provide the APEER Dev Kit (ADK) which can be found in the lib folder. Using the adk you can assume to have all inputs as defined in your module_specification.json in a struct whose fields you can pass to your code.
In our example we have just one input as you can see in module_specification.json
Files are actually paths to the files
module_specification.json
1
{
2
"spec": {
3
"inputs": {
4
"input_image": {
5
"type:file": {}
6
}
7
},
8
"outputs": {
9
"output_image": {
10
"type:file": {}
11
}
12
}
13
},
14
"ui": {
15
"inputs": {
16
"input_image": {
17
"index": 0,
18
"label": "Original image",
19
"description": "Low contrast image which contrast should be improved",
20
"widget:none": null
21
}
22
},
23
"outputs": {
24
"output_image": {
25
"index": 1,
26
"label": "Equalised image",
27
"description": "High contrast output image using histogram equalization"
28
}
29
}
30
}
31
}
32
Copied!
Writing back the outputs to the APEER environment is as easy as reading. Use adk.set_output([output_key], [value]) for non-file outputs and adk.set_file_output([output_key], [path_to_file]) for file outputs. How you return the values from your code to the entry point file doesn't really matter. We recommend a struct for simplicity.
Let us have a look on the script that holds the main logic of our module and performs the actual operation,execute.m:
execute.m
1
function outputs = execute(image_path)
2
3
[~, name, ext] = fileparts(image_path);
4
out_image_path = strcat(name, ext);
5
6
% process ome-tiff images
7
if endsWith(lower(image_path), ".ome.tiff") || endsWith(lower(image_path), ".ome.tif")
8
9
%get omexml string
10
img_info = imfinfo(image_path);
11
t = Tiff(image_path, 'r');
12
omeXML = getTag(t, 'ImageDescription');
13
14
%process the multidimensional array and save the result
15
for i = 1:size(img_info,1)
16
array = imread(image_path,'tif',i, 'Info', img_info);
17
result = histeq(array); %change your code here or create an external function and add additional input parameters to this main function
18
imwrite(result, out_image_path, 'tif', 'Description', omeXML, 'WriteMode','append')
19
end
20
21
else % process images of other formats
22
23
array = imread(image_path);
24
result = histeq(array); %change your code here or create an external function and add additional input parameters to this main function
25
imwrite(result, out_image_path);
26
27
end
28
29
outputs.output_image = out_image_path;
30
31
end
Copied!
The script reads the input image and performs the histogram equalization with the histeq function. This is the place where you can modify the code so that it performs the desired operation. In order to handle more advanced code, you can create an external function (in the src folder) and add it to the main script. The function will be applied to each channel. Please modify the code in the both parts of the ifstatement so that it could be used with both .ome.tiff and basic formats (.png, .jpg...). Finally, the output image is saved and the output image path is passed as a struct. All input parameters that are required by your function should be passed to the first line of theexecute.m function and all outputs should be passed to the outputs structure (image path for output image).
In order to test your code locally, you can run the script test_your_module.m. The code will use the test image placed in the input folder.
test_your_module.m
1
clear;
2
3
addpath("src", "lib")
4
5
image_path = "input/cells.ome.tiff";
6
7
output = execute(image_path);
Copied!
After you successfully finish your module, you can compile your code so that it works on APEER. This example module contains a compile-script (compile.sh) which works for this example. The command used to call the MATLAB compiler is mcc. All possible options can be checked here. The one used by this example are
  • -m compiles to a standalone executable
  • -v displays compilation steps
  • -I includes library folders (in our case we use the ApeerDevKit.m)
  • -d specifies the output folder
Please note that you need a valid license for the MATLAB compiler to compile your code.
The compilation has to happen before pushing your changes to APEER.
The compiled files are placed in the bin folder and contain the binary file apeer_main, the rest of the generated files can be erased. The compilation cannot be performed on APEER directly due to the above mentioned license situation.
Depending on the version you used to compile your code you need to select a compatible MATLAB runtime. The Dockerfile will take care of most of the steps needed to setup the MATLAB runtime. You only have to set the environment variable to the correct MATLAB runtime version and the runtime installation link (Linux dependent), which you will find here.
1
ENV MCR_VERSION v96
2
ENV MCR_DOWNLOAD_LINK http://ssd.mathworks.com/supportfiles/downloads/R2019a/Release/2/deployment_files/installer/complete/glnxa64/MATLAB_Runtime_R2019a_Update_2_glnxa64.zip
Copied!
The CMD in the Dockerfile will call the binary you've just compiled. So in case you change it's name or location you need to update the CMD.

Fast lane for impatient coders ;)

Of course we have prepared the zipped project folder and some ready to run files for copy and paste:
apeer_main.m
execute.m
compile.sh
Dockerfile
module_specification.json
test_your_module
ApeerDevKit
1
function [] = apeer_main(varargin)
2
3
adk = ApeerDevKit(varargin{:});
4
inputs = adk.get_inputs();
5
6
outputs = execute(inputs.input_image);
7
8
adk.set_file_output('output_image', outputs.output_image);
9
adk.finalize();
10
11
end
12
13
Copied!
1
function outputs = execute(image_path)
2
3
[~, name, ext] = fileparts(image_path);
4
out_image_path = strcat(name, ext);
5
6
% process ome-tiff images
7
if endsWith(lower(image_path), ".ome.tiff") || endsWith(lower(image_path), ".ome.tif")
8
9
%get omexml string
10
img_info = imfinfo(image_path);
11
t = Tiff(image_path, 'r');
12
omeXML = getTag(t, 'ImageDescription');
13
14
%process the multidimensional array and save the result
15
for i = 1:size(img_info,1)
16
array = imread(image_path,'tif',i, 'Info', img_info);
17
result = histeq(array); %change your code here or create an external function and add additional input parameters to this main function
18
imwrite(result, out_image_path, 'tif', 'Description', omeXML, 'WriteMode','append')
19
end
20
21
else % process images of other formats
22
23
array = imread(image_path);
24
result = histeq(array); %change your code here or create an external function and add additional input parameters to this main function
25
imwrite(result, out_image_path);
26
27
end
28
29
outputs.output_image = out_image_path;
30
31
end
32
Copied!
1
#!/bin/sh
2
3
mcc -mv src/apeer_main.m -I lib -d bin
Copied!
1
FROM ubuntu:16.04
2
3
# make sure the matlab release and the runtime point to the same version
4
# check the matlab runtime version here: https://www.mathworks.com/products/compiler/matlab-runtime.html
5
# and copy the link to the runtime installation
6
ENV MCR_VERSION v96
7
ENV MCR_DOWNLOAD_LINK http://ssd.mathworks.com/supportfiles/downloads/R2019a/Release/2/deployment_files/installer/complete/glnxa64/MATLAB_Runtime_R2019a_Update_2_glnxa64.zip
8
9
# install required system packages
10
RUN apt-get update -y && apt-get install -y \
11
wget \
12
unzip \
13
openjdk-8-jdk && \
14
apt-get clean && rm -rf /var/lib/apt/lists/*
15
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/jre
16
17
# install matlab runtime
18
RUN mkdir /root/mcr && \
19
cd /root/mcr && \
20
wget --no-verbose --output-document mcr_runtime.zip ${MCR_DOWNLOAD_LINK} && \
21
unzip mcr_runtime.zip && \
22
mkdir /opt/matlab-compiler-runtime && \
23
./install -mode silent -agreeToLicense yes -destinationFolder /opt/matlab-compiler-runtime && \
24
cd / && \
25
rm -rf /root/mcr
26
27
# add matlab runtime to library path
28
ENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:\
29
/opt/matlab-compiler-runtime/${MCR_VERSION}/runtime/glnxa64:\
30
/opt/matlab-compiler-runtime/${MCR_VERSION}/bin/glnxa64:\
31
/opt/matlab-compiler-runtime/${MCR_VERSION}/sys/os/glnxa64
32
33
#### YOUR CODE ####
34
WORKDIR /app
35
COPY bin/apeer_main .
36
RUN chmod o+x ./apeer_main && chmod g+x ./apeer_main
37
CMD [ "./apeer_main" ]
38
Copied!
1
{
2
"spec": {
3
"inputs": {
4
"input_image": {
5
"type:file": {}
6
}
7
},
8
"outputs": {
9
"output_image": {
10
"type:file": {}
11
}
12
}
13
},
14
"ui": {
15
"inputs": {
16
"input_image": {
17
"index": 0,
18
"label": "Original image",
19
"description": "Low contrast image which's contrast should be improved",
20
"widget:none": null
21
}
22
},
23
"outputs": {
24
"output_image": {
25
"index": 1,
26
"label": "Equalized image",
27
"description": "High contrast output image using histogram equalization"
28
}
29
}
30
}
31
}
Copied!
1
clear;
2
3
addpath("src", "lib")
4
5
image_path = "input/cells.ome.tiff";
6
7
output = execute(image_path);
8
Copied!
1
classdef ApeerDevKit < handle
2
%ApeerDevKit SDK for creating MATLAB modules on https://www.apeer.com
3
% Reads the inputs from the APEER environment and takes care of correctly
4
% writing back your outputs. Please note that you have to be aware of the
5
% in- and output-keys in your module_specification. These are the keys
6
% used to access the returned input struct as well as the names of the
7
% fields in the output struct.
8
9
properties
10
args = struct("debug", false, "quiet", false);
11
output_params_file = "";
12
output_struct = struct();
13
end
14
15
methods
16
function obj = ApeerDevKit(varargin)
17
%ApeerDevKit Initializes the apeer dev kit
18
%
19
% You can pass --debug or -d to set the ADK into debug mode. It
20
% will then not try to copy files into the APEER directories but
21
% leave all file outputs as is.
22
23
obj.parse_arguments(varargin{:});
24
25
if ~obj.args.debug
26
[status, message] = mkdir("/output");
27
if status == 0
28
warning("[ADK] Could not create folder /output: %s", message);
29
end
30
end
31
32
if ~obj.args.quiet; fprintf("[ADK] #### APEER SDK initialized ####\n\n"); end
33
end
34
35
function inputs_struct = get_inputs(obj)
36
%get_inputs Reads all inputs from the APEER environment
37
%
38
% Inputs are returned as a struct containing all inputs as fields
39
% with their respective data type as specified in the
40
% module_specification.
41
42
if ~obj.args.quiet; fprintf("[ADK] ## Reading inputs ##\n\n"); end
43
44
wfe_input_json_key = "WFE_INPUT_JSON";
45
input_json = getenv(wfe_input_json_key);
46
if isempty(input_json)
47
error("adk:WfeInputJsonNotFound", "[ADK] Could not find environment variable ""%s""", wfe_input_json_key);
48
end
49
if ~obj.args.quiet; fprintf("[ADK] Found environment variable ""%s"":\n\n %s\n\n", wfe_input_json_key, input_json); end
50
51
try
52
inputs_struct = jsondecode(input_json);
53
if obj.args.debug
54
obj.output_params_file = inputs_struct.WFE_output_params_file;
55
else
56
obj.output_params_file = sprintf("/output/%s", inputs_struct.WFE_output_params_file);
57
end
58
if ~obj.args.quiet; fprintf("[ADK] %s decoded to inputs structure:\n\n", wfe_input_json_key); end
59
if ~obj.args.quiet; disp(inputs_struct); end
60
catch ex
61
error("adk:InvalidWfeInputJson", "[ADK] Could not decode input_json\n\n%s", getReport(ex));
62
end
63
64
if ~obj.args.quiet; fprintf("[ADK] #### Reading inputs done ####\n\n"); end
65
end
66
67
function set_output(obj, output_key, output_value)
68
%set_output Assigns the given output_value to the given output_key
69
70
output_value_text = output_value;
71
72
if islogical(output_value)
73
output_value_text = "false";
74
if output_value
75
output_value_text = "true";
76
end
77
elseif iscell(output_value)
78
output_value_text = "[";
79
for i = 1:length(output_value)
80
output_value_text = output_value_text + "" + output_value{i} + """;";
81
end
82
output_value_text = output_value_text.strip(';') + "]";
83
end
84
85
if ~obj.args.quiet; fprintf("[ADK] Setting output ""%s"" to ""%s""\n\n", output_key, output_value_text); end
86
obj.output_struct.(output_key) = output_value;
87
end
88
89
function set_file_output(obj, output_key, output_file_path)
90
%set_output Copies the output file to the APEER output folder
91
92
if ~obj.args.debug
93
output_file_path = obj.move_file_to_output_folder(output_file_path);
94
end
95
obj.set_output(output_key, output_file_path);
96
end
97
98
function set_multi_file_output(obj, output_key, output_file_paths)
99
%set_output Copies the output file to the APEER output folder
100
101
if ~obj.args.debug
102
for i = 1:length(output_file_paths)
103
output_file_paths{i} = obj.move_file_to_output_folder(output_file_paths{i});
104
end
105
end
106
obj.set_output(output_key, output_file_paths);
107
end
108
109
function finalize(obj)
110
%finalize Writes all assigned outputs
111
%
112
% Writes all output values as assigned by set_output and
113
% set_file_output to the output params file as given by the APEER
114
% environment.
115
116
if ~obj.args.quiet; fprintf("[ADK] #### Finalizing APEER SDK ####\n\n"); end
117
if ~obj.args.quiet; fprintf("[ADK] Encoding output structure:\n\n"); end
118
disp(obj.output_struct);
119
120
try
121
output_json = jsonencode(obj.output_struct);
122
if ~obj.args.quiet; fprintf("[ADK] Encoded output structure to json:\n\n %s\n\n", output_json); end
123
catch ex
124
error("[ADK] Could not encode output structure\n\n%s", getReport(ex));
125
end
126
127
if ~obj.args.quiet; fprintf("[ADK] Writing encoded outputs to ""%s""\n\n", obj.output_params_file); end
128
129
[fileId, message] = fopen(obj.output_params_file, "w");
130
if fileId == -1
131
error("[ADK] Could not open file ""%s"": %s", obj.output_params_file, message);
132
end
133
134
if ~obj.args.quiet; fprintf(fileId, output_json); end
135
136
if fclose(fileId)
137
error("[ADK] Could not write to file ""%s""", obj.output_params_file);
138
end
139
140
if ~obj.args.debug
141
if ~obj.args.quiet; fprintf("[ADK] Content of ""/output"":\n\n "); end
142
if ~obj.args.quiet; disp(ls("/output")); end
143
end
144
145
if ~obj.args.quiet; fprintf("[ADK] #### APEER SDK finalized ####\n\n"); end
146
end
147
end
148
149
methods (Access = private)
150
function parse_arguments(obj, varargin)
151
for i = 1:numel(varargin)
152
if strcmp(varargin{i}, "--debug") || strcmp(varargin{i}, "-d")
153
obj.args.debug = true;
154
elseif strcmp(varargin{i}, "--quiet") || strcmp(varargin{i}, "-q")
155
obj.args.quiet = true;
156
end
157
end
158
159
if obj.args.debug
160
if ~obj.args.quiet; warning("APEER SDK running in debug mode."); end
161
end
162
end
163
164
function output_file_path = move_file_to_output_folder(obj, output_file_path)
165
[path, filename, ext] = fileparts(output_file_path);
166
if ~startsWith(path, "/output/")
167
destination = sprintf("/output/%s%s", filename, ext);
168
if ~obj.args.quiet; fprintf("[ADK] Moving ""%s"" to ""%s""\n\n", output_file_path, destination); end
169
[status, message] = movefile(output_file_path, destination);
170
if status == 0
171
error("[ADK] Could not move ""%s"" to ""%s"": %s", output_file_path, destination, message);
172
end
173
output_file_path = destination;
174
end
175
end
176
end
177
end
178
179
Copied!
You can also download the source code by creating a new 'Matlab (linux) Example' Module in APEER.
Matlab example.zip
466KB
Binary
Matlab (linux) Example Source Code

You couldn't find all the information you need? Contact us!

If you need help, just check out our FAQ. For any further questions please contact us at [email protected] or have a look at How-tos section in our blog or follow us on Twitter to stay up to date.
Last modified 2yr ago