Skip to content

Commit 038232a

Browse files
Implement ICA (brainflow-dev#596)
* wip:initial commit for ICA Signed-off-by: Andrey Parfenov <[email protected]> Co-authored-by: Musa Mahmood <[email protected]>
1 parent 8131730 commit 038232a

File tree

34 files changed

+1052
-12
lines changed

34 files changed

+1052
-12
lines changed

.github/workflows/run_matlab.yml

+4
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ jobs:
4343
uses: matlab-actions/run-command@v1
4444
with:
4545
command: addpath('matlab_package/brainflow'),addpath('matlab_package/brainflow/examples'),addpath('matlab_package/brainflow/inc'),addpath('matlab_package/brainflow/lib'),Spo2
46+
- name: Run ICA Test
47+
uses: matlab-actions/run-command@v1
48+
with:
49+
command: addpath('matlab_package/brainflow'),addpath('matlab_package/brainflow/examples'),addpath('matlab_package/brainflow/inc'),addpath('matlab_package/brainflow/lib'),ICA

.github/workflows/run_unix.yml

+8
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ jobs:
198198
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/transforms.py
199199
- name: Downsampling Python
200200
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/downsampling.py
201+
- name: ICA Python
202+
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/ica.py
201203
- name: CSP Python
202204
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/csp.py
203205
- name: Windowing Python
@@ -248,6 +250,12 @@ jobs:
248250
mvn exec:java -Dexec.mainClass="brainflow.examples.Denoising"
249251
env:
250252
LD_LIBRARY_PATH: ${{ github.workspace }}/installed/lib
253+
- name: ICA Java
254+
run: |
255+
cd $GITHUB_WORKSPACE/java_package/brainflow
256+
mvn exec:java -Dexec.mainClass="brainflow.examples.ICA"
257+
env:
258+
LD_LIBRARY_PATH: ${{ github.workspace }}/installed/lib
251259
- name: Downsampling Java
252260
run: |
253261
cd $GITHUB_WORKSPACE/java_package/brainflow

.github/workflows/run_windows.yml

+3
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ jobs:
253253
- name: Serialization Cpp Test
254254
run: .\cpp_package\examples\signal_processing\build\Release\serialization.exe
255255
shell: cmd
256+
- name: ICA C# Test
257+
run: .\csharp_package\brainflow\examples\ica\bin\Release\netcoreapp3.1\ica.exe
258+
shell: cmd
256259
# Tests for MLModule
257260
- name: EEG Metrics Python Test
258261
run: python %GITHUB_WORKSPACE%\python_package\examples\tests\eeg_metrics.py --board-id -1

.github/workflows/valgrind.yml

+4
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,7 @@ jobs:
151151
run: valgrind --error-exitcode=1 --track-origins=yes --leak-check=full $GITHUB_WORKSPACE/cpp_package/examples/signal_processing/build/band_power
152152
env:
153153
LD_LIBRARY_PATH: $GITHUB_WORKSPACE/installed/lib
154+
- name: ICA Cpp
155+
run: valgrind --error-exitcode=1 --track-origins=yes --leak-check=full $GITHUB_WORKSPACE/cpp_package/examples/signal_processing/build/ica
156+
env:
157+
LD_LIBRARY_PATH: $GITHUB_WORKSPACE/installed/lib

CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ configure_msvc_runtime ()
3232
if (WARNINGS_AS_ERRORS)
3333
if (MSVC)
3434
add_compile_options (/WX)
35+
if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
36+
add_compile_options (/bigobj)
37+
endif (CMAKE_BUILD_TYPE EQUAL "DEBUG")
3538
else ()
3639
add_compile_options (-Werror -Wno-varargs)
3740
endif ()

cpp_package/examples/signal_processing/CMakeLists.txt

+22
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,25 @@ target_link_libraries (
269269
${DataHandlerPath}
270270
${BoardControllerPath}
271271
)
272+
273+
##################
274+
## Demo for ICA ##
275+
##################
276+
add_executable (
277+
ica
278+
src/ica.cpp
279+
)
280+
281+
target_include_directories (
282+
ica PUBLIC
283+
${brainflow_INCLUDE_DIRS}
284+
)
285+
286+
target_link_libraries (
287+
ica PUBLIC
288+
# for some systems(ubuntu for example) order matters
289+
${BrainflowPath}
290+
${MLModulePath}
291+
${DataHandlerPath}
292+
${BoardControllerPath}
293+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include <iostream>
2+
#include <stdlib.h>
3+
#include <string>
4+
5+
#ifdef _WIN32
6+
#include <windows.h>
7+
#else
8+
#include <unistd.h>
9+
#endif
10+
11+
#include "board_shim.h"
12+
#include "data_filter.h"
13+
14+
using namespace std;
15+
16+
17+
int main (int argc, char *argv[])
18+
{
19+
BoardShim::enable_dev_board_logger ();
20+
21+
struct BrainFlowInputParams params;
22+
int res = 0;
23+
int board_id = (int)BoardIds::SYNTHETIC_BOARD;
24+
std::vector<int> eeg_channels = BoardShim::get_eeg_channels (board_id);
25+
int channel_to_use = eeg_channels[4];
26+
// use synthetic board for demo
27+
BoardShim *board = new BoardShim (board_id, params);
28+
29+
try
30+
{
31+
board->prepare_session ();
32+
board->start_stream ();
33+
34+
#ifdef _WIN32
35+
Sleep (10000);
36+
#else
37+
sleep (10);
38+
#endif
39+
40+
board->stop_stream ();
41+
BrainFlowArray<double, 2> data =
42+
board->get_board_data (500, (int)BrainFlowPresets::DEFAULT_PRESET);
43+
board->release_session ();
44+
45+
BrainFlowArray<double, 2> data_reshaped (data.get_address (channel_to_use), 5, 100);
46+
std::tuple<BrainFlowArray<double, 2>, BrainFlowArray<double, 2>, BrainFlowArray<double, 2>,
47+
BrainFlowArray<double, 2>>
48+
returned_matrixes = DataFilter::perform_ica (data_reshaped, 2);
49+
std::cout << std::get<3> (returned_matrixes) << std::endl;
50+
}
51+
catch (const BrainFlowException &err)
52+
{
53+
BoardShim::log_message ((int)LogLevels::LEVEL_ERROR, err.what ());
54+
res = err.exit_code;
55+
if (board->is_prepared ())
56+
{
57+
board->release_session ();
58+
}
59+
}
60+
61+
delete board;
62+
63+
return res;
64+
}

cpp_package/src/data_filter.cpp

+60-1
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,65 @@ double DataFilter::calc_stddev (double *data, int start_pos, int end_pos)
515515
return output;
516516
}
517517

518+
std::tuple<BrainFlowArray<double, 2>, BrainFlowArray<double, 2>, BrainFlowArray<double, 2>,
519+
BrainFlowArray<double, 2>>
520+
DataFilter::perform_ica (
521+
const BrainFlowArray<double, 2> &data, int num_components, std::vector<int> channels)
522+
{
523+
if ((data.empty ()) || (channels.empty ()) || (num_components < 1))
524+
{
525+
throw BrainFlowException (
526+
"Invalid params", (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR);
527+
}
528+
529+
int cols = data.get_size (1);
530+
int channels_len = (int)channels.size ();
531+
double *data_1d = new double[cols * channels_len];
532+
double *w = new double[num_components * num_components];
533+
double *k = new double[channels_len * num_components];
534+
double *a = new double[num_components * channels_len];
535+
double *s = new double[cols * num_components];
536+
537+
for (int i = 0; i < channels_len; i++)
538+
{
539+
for (int j = 0; j < cols; j++)
540+
{
541+
data_1d[j + cols * i] = data.at (channels[i], j);
542+
}
543+
}
544+
int res = ::perform_ica (data_1d, channels_len, cols, num_components, w, k, a, s);
545+
if (res != (int)BrainFlowExitCodes::STATUS_OK)
546+
{
547+
delete[] data_1d;
548+
delete[] w;
549+
delete[] k;
550+
delete[] a;
551+
delete[] s;
552+
throw BrainFlowException ("failed to perform_ica", res);
553+
}
554+
BrainFlowArray<double, 2> w_mat (w, num_components, num_components);
555+
BrainFlowArray<double, 2> k_mat (k, num_components, channels_len);
556+
BrainFlowArray<double, 2> a_mat (a, channels_len, num_components);
557+
BrainFlowArray<double, 2> s_mat (s, num_components, cols);
558+
delete[] data_1d;
559+
delete[] w;
560+
delete[] k;
561+
delete[] a;
562+
delete[] s;
563+
return std::make_tuple (w_mat, k_mat, a_mat, s_mat);
564+
}
565+
566+
std::tuple<BrainFlowArray<double, 2>, BrainFlowArray<double, 2>, BrainFlowArray<double, 2>,
567+
BrainFlowArray<double, 2>>
568+
DataFilter::perform_ica (const BrainFlowArray<double, 2> &data, int num_components)
569+
{
570+
std::vector<int> channels;
571+
int rows = data.get_size (0);
572+
for (int i = 0; i < rows; i++)
573+
channels.push_back (i);
574+
return perform_ica (data, num_components, channels);
575+
}
576+
518577
double DataFilter::get_railed_percentage (double *data, int data_len, int gain)
519578
{
520579
double output = 0;
@@ -538,4 +597,4 @@ std::string DataFilter::get_version ()
538597
std::string verion_str (version, string_len);
539598

540599
return verion_str;
541-
}
600+
}

cpp_package/src/inc/data_filter.h

+22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <complex>
4+
#include <tuple>
45
#include <utility>
56
#include <vector>
67
// include it here to allow user include only this single file
@@ -213,6 +214,27 @@ class DataFilter
213214
static double calc_stddev (double *data, int start_pos, int end_pos);
214215
/// calc railed percentage
215216
static double get_railed_percentage (double *data, int data_len, int gain);
217+
/**
218+
* calculate ICA
219+
* @param data input 2d array, rows are samples
220+
* @param num_components number of components to find
221+
* @param channels rows to use
222+
* @return unmixed signal
223+
*/
224+
static std::tuple<BrainFlowArray<double, 2>, BrainFlowArray<double, 2>,
225+
BrainFlowArray<double, 2>, BrainFlowArray<double, 2>>
226+
perform_ica (
227+
const BrainFlowArray<double, 2> &data, int num_components, std::vector<int> channels);
228+
/**
229+
* calculate ICA
230+
* @param data input 2d array, rows are samples
231+
* @param num_components number of components to find
232+
* @return unmixed signal
233+
*/
234+
static std::tuple<BrainFlowArray<double, 2>, BrainFlowArray<double, 2>,
235+
BrainFlowArray<double, 2>, BrainFlowArray<double, 2>>
236+
perform_ica (const BrainFlowArray<double, 2> &data, int num_components);
237+
216238

217239
/// get brainflow version
218240
static std::string get_version ();

csharp_package/brainflow/brainflow.sln

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "signal_filtering", "example
3434
EndProject
3535
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "transforms", "examples\transforms\transforms.csproj", "{452B56E9-0103-4412-AEE9-6C1377D0F5BD}"
3636
EndProject
37+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ica", "examples\ica\ica.csproj", "{7980F4A1-3479-4E7D-A8B8-F449C3DECCCF}"
38+
EndProject
3739
Global
3840
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3941
Debug|Any CPU = Debug|Any CPU
@@ -88,6 +90,10 @@ Global
8890
{452B56E9-0103-4412-AEE9-6C1377D0F5BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
8991
{452B56E9-0103-4412-AEE9-6C1377D0F5BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
9092
{452B56E9-0103-4412-AEE9-6C1377D0F5BD}.Release|Any CPU.Build.0 = Release|Any CPU
93+
{7980F4A1-3479-4E7D-A8B8-F449C3DECCCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
94+
{7980F4A1-3479-4E7D-A8B8-F449C3DECCCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
95+
{7980F4A1-3479-4E7D-A8B8-F449C3DECCCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
96+
{7980F4A1-3479-4E7D-A8B8-F449C3DECCCF}.Release|Any CPU.Build.0 = Release|Any CPU
9197
EndGlobalSection
9298
GlobalSection(SolutionProperties) = preSolution
9399
HideSolutionNode = FALSE
@@ -104,6 +110,7 @@ Global
104110
{0AC60FC0-F528-463C-BB9B-5839224039D4} = {FF155433-168F-4A55-BA98-DFD197A98185}
105111
{FEFC5F67-E548-4D78-862B-87F9B3D0494A} = {FF155433-168F-4A55-BA98-DFD197A98185}
106112
{452B56E9-0103-4412-AEE9-6C1377D0F5BD} = {FF155433-168F-4A55-BA98-DFD197A98185}
113+
{7980F4A1-3479-4E7D-A8B8-F449C3DECCCF} = {FF155433-168F-4A55-BA98-DFD197A98185}
107114
EndGlobalSection
108115
GlobalSection(ExtensibilityGlobals) = postSolution
109116
SolutionGuid = {C8CE9ED2-4273-45D4-AD7D-4B9227349E89}

csharp_package/brainflow/brainflow/data_filter.cs

+57-8
Original file line numberDiff line numberDiff line change
@@ -581,14 +581,7 @@ public static void write_file (double[,] data, string file_name, string file_mod
581581
throw new BrainFlowError (res);
582582
}
583583

584-
double[,] result = new double[num_rows[0], num_cols[0]];
585-
for (int i = 0; i < num_rows[0]; i++)
586-
{
587-
for (int j = 0; j < num_cols[0]; j++)
588-
{
589-
result[i, j] = data_arr[i * num_cols[0] + j];
590-
}
591-
}
584+
double[,] result = data_arr.Reshape(num_rows[0], num_cols[0]);
592585
return result;
593586
}
594587

@@ -643,6 +636,62 @@ public static Tuple<double[], double[]> get_custom_band_powers (double[,] data,
643636
return return_data;
644637
}
645638

639+
/// <summary>
640+
/// Calculate ICA
641+
/// </summary>
642+
/// <param name="data"></param>
643+
/// <param name="num_components"></param>
644+
/// <returns></returns>
645+
public static Tuple<double[,], double[,], double[,], double[,]> perform_ica (double[,] data, int num_components)
646+
{
647+
if (data == null)
648+
{
649+
throw new BrainFlowError ((int)BrainFlowExitCodes.INVALID_ARGUMENTS_ERROR);
650+
}
651+
int[] channels = new int[data.GetLength(0)];
652+
for (int i = 0; i < channels.Length; i++)
653+
channels[i] = i;
654+
return perform_ica (data, num_components, channels);
655+
}
656+
657+
/// <summary>
658+
/// Calculate ICA
659+
/// </summary>
660+
/// <param name="data"></param>
661+
/// <param name="num_components"></param>
662+
/// <param name="channels"></param>
663+
/// <returns></returns>
664+
public static Tuple<double[,], double[,], double[,], double[,]> perform_ica (double[,] data, int num_components, int[] channels)
665+
{
666+
if ((num_components < 1) || (data == null) || (channels == null))
667+
{
668+
throw new BrainFlowError((int)BrainFlowExitCodes.INVALID_ARGUMENTS_ERROR);
669+
}
670+
int cols = data.GetLength (1);
671+
double[] data_1d = new double[cols * channels.Length];
672+
for (int i = 0; i < channels.Length; i++)
673+
{
674+
Array.Copy (data.GetRow (channels[i]), 0, data_1d, i * data.GetRow (channels[i]).Length, data.GetRow (channels[i]).Length);
675+
}
676+
int channels_len = channels.Length;
677+
double[] w = new double[num_components * num_components];
678+
double[] k = new double[channels_len * num_components];
679+
double[] a = new double[channels_len * num_components];
680+
double[] s = new double[cols * num_components];
681+
682+
int res = DataHandlerLibrary.perform_ica (data_1d, channels_len, cols, num_components, w, k, a, s);
683+
if (res != (int)BrainFlowExitCodes.STATUS_OK)
684+
{
685+
throw new BrainFlowError (res);
686+
}
687+
double[,] w_mat = w.Reshape (num_components, num_components);
688+
double[,] k_mat = k.Reshape (num_components, channels_len);
689+
double[,] a_mat = a.Reshape (channels_len, num_components);
690+
double[,] s_mat = s.Reshape (num_components, cols);
691+
Tuple<double[,], double[,], double[,], double[,]> return_data = new Tuple<double[,], double[,], double[,], double[,]> (w_mat, k_mat, a_mat, s_mat);
692+
return return_data;
693+
}
694+
646695
/// <summary>
647696
/// calculate avg and stddev bandpowers across channels
648697
/// </summary>

0 commit comments

Comments
 (0)