diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a2ac3ca..1e7a24e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(PC_FFTW3 REQUIRED fftw3f) pkg_check_modules(PC_ALSA REQUIRED alsa) pkg_check_modules(PC_LIBCONFIG REQUIRED libconfig) +pkg_search_module (PC_JSON-C REQUIRED json-c json) +find_library(MATRIX_CREATOR_HAL matrix_creator_hal) include_directories("${PROJECT_SOURCE_DIR}/include") @@ -22,6 +24,10 @@ include_directories(include/odas) add_subdirectory(include) +find_path (JSON_C_INCLUDE_DIR json.h PATH_SUFFIXES json-c json) +find_library (JSON_C_LIBRARIES NAMES json-c libjson-c) +include_directories (${JSON_C_INCLUDE_DIR}) + set(SRC src/general/format.c @@ -209,6 +215,15 @@ target_link_libraries(odaslive odas ) +add_executable(matrix-odas + demo/matrix-demos/matrix-odas.cpp +) +target_link_libraries(matrix-odas + odas + ${JSON_C_LIBRARIES} + ${MATRIX_CREATOR_HAL} +) + add_executable(odasserver demo/odasserver/main.c ) diff --git a/config/matrix-demo/matrix_creator.cfg b/config/matrix-demo/matrix_creator.cfg new file mode 100644 index 00000000..dee18da1 --- /dev/null +++ b/config/matrix-demo/matrix_creator.cfg @@ -0,0 +1,367 @@ +# Configuration file for MATRIX Creator Raspberry Pi shield + +version = "2.1"; + +# Raw + +raw: +{ + + fS = 48000; + hopSize = 512; + nBits = 16; + nChannels = 8; + + # Input with raw signal from microphones + interface: { + type = "soundcard"; + card = 2; + device = 0; + } + +} + +# Mapping + +mapping: +{ + + map: (1, 2, 3, 4, 5, 6, 7, 8); + +} + +# General + +general: +{ + + epsilon = 1E-20; + + size: + { + hopSize = 128; + frameSize = 256; + }; + + samplerate: + { + mu = 16000; + sigma2 = 0.01; + }; + + speedofsound: + { + mu = 343.0; + sigma2 = 25.0; + }; + + mics = ( + + # Microphone 1 + { + mu = ( +0.020091, -0.048504, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 2 + { + mu = ( -0.020091, -0.048504, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 3 + { + mu = ( -0.048504, -0.020091, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 4 + { + mu = ( -0.048504, +0.020091, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 5 + { + mu = ( -0.020091, +0.048504, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 6 + { + mu = ( +0.020091, +0.048504, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 7 + { + mu = ( +0.048504, +0.020091, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 8 + { + mu = ( +0.048504, -0.020091, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + } + + ); + + # Spatial filters to include only a range of direction if required + # (may be useful to remove false detections from the floor, or + # limit the space search to a restricted region) + spatialfilters = ( + + { + + direction = ( +0.000, +0.000, +1.000 ); + angle = (80.0, 90.0); + + } + + ); + + nThetas = 181; + gainMin = 0.25; + +}; + +# Stationnary noise estimation + +sne: +{ + + b = 3; + alphaS = 0.1; + L = 150; + delta = 3.0; + alphaD = 0.1; + +} + +# Sound Source Localization + +ssl: +{ + + nPots = 4; + nMatches = 10; + probMin = 0.5; + nRefinedLevels = 1; + interpRate = 4; + + # Number of scans: level is the resolution of the sphere + # and delta is the size of the maximum sliding window + # (delta = -1 means the size is automatically computed) + scans = ( + { level = 2; delta = -1; }, + { level = 4; delta = -1; } + ); + + # Output to export potential sources + potential: { + + format = "json"; + interface: { + type = "socket"; + ip = "127.0.0.1"; + port = 9001; + } + + }; + +}; + +# Sound Source Tracking + +sst: +{ + + # Mode is either "kalman" or "particle" + + mode = "kalman"; + + # Add is either "static" or "dynamic" + + add = "dynamic"; + + # Parameters used by both the Kalman and particle filter + + active = ( + { weight = 1.0; mu = 0.4; sigma2 = 0.0025 } + ); + + inactive = ( + { weight = 1.0; mu = 0.25; sigma2 = 0.0025 } + ); + + sigmaR2_prob = 0.0025; + sigmaR2_active = 0.0225; + sigmaR2_target = 0.0025; + Pfalse = 0.1; + Pnew = 0.1; + Ptrack = 0.8; + + theta_new = 0.9; + N_prob = 5; + theta_prob = 0.8; + N_inactive = ( 250, 250, 250, 250 ); + theta_inactive = 0.9; + + # Parameters used by the Kalman filter only + + kalman: { + + sigmaQ = 0.001; + + }; + + # Parameters used by the particle filter only + + particle: { + + nParticles = 1000; + st_alpha = 2.0; + st_beta = 0.04; + st_ratio = 0.5; + ve_alpha = 0.05; + ve_beta = 0.2; + ve_ratio = 0.3; + ac_alpha = 0.5; + ac_beta = 0.2; + ac_ratio = 0.2; + Nmin = 0.7; + + }; + + target: (); + + # Output to export tracked sources + tracked: { + + format = "undefined"; + interface: { + type = "blackhole"; + } + + }; + +} + +sss: +{ + + # Mode is either "dds", "dgss" or "dmvdr" + + mode_sep = "dds"; + mode_pf = "ss"; + + gain_sep = 1.0; + gain_pf = 10.0; + + dds: { + + }; + + dgss: { + + mu = 0.01; + lambda = 0.5; + + }; + + dmvdr: { + + }; + + ms: { + + alphaPmin = 0.07; + eta = 0.5; + alphaZ = 0.8; + thetaWin = 0.3; + alphaWin = 0.3; + maxAbsenceProb = 0.9; + Gmin = 0.01; + winSizeLocal = 3; + winSizeGlobal = 23; + winSizeFrame = 256; + + }; + + ss: { + + Gmin = 0.01; + Gmid = 0.5; + Gslope = 10.0; + + } + + separated: { + + fS = 16000; + hopSize = 128; + nBits = 16; + + format = "undefined"; + interface: { + type = "blackhole"; + } + + }; + + postfiltered: { + + fS = 16000; + hopSize = 128; + nBits = 16; + gain = 10.0; + + interface: { + type = "blackhole"; + } + + }; + +}; + +classify: +{ + + frameSize = 4096; + winSize = 3; + tauMin = 88; + tauMax = 551; + deltaTauMax = 20; + alpha = 0.3; + gamma = 0.05; + phiMin = 0.5; + r0 = 0.2; + + category: { + + format = "undefined"; + + interface: { + type = "blackhole"; + } + + } + +} diff --git a/config/matrix-demo/matrix_voice.cfg b/config/matrix-demo/matrix_voice.cfg new file mode 100644 index 00000000..d90a7241 --- /dev/null +++ b/config/matrix-demo/matrix_voice.cfg @@ -0,0 +1,367 @@ +# Configuration file for MATRIX Voice Raspberry Pi shield + +version = "2.1"; + +# Raw + +raw: +{ + + fS = 48000; + hopSize = 512; + nBits = 16; + nChannels = 8; + + # Input with raw signal from microphones + interface: { + type = "soundcard"; + card = 2; + device = 0; + } + +} + +# Mapping + +mapping: +{ + + map: (1, 2, 3, 4, 5, 6, 7, 8); + +} + +# General + +general: +{ + + epsilon = 1E-20; + + size: + { + hopSize = 128; + frameSize = 256; + }; + + samplerate: + { + mu = 16000; + sigma2 = 0.01; + }; + + speedofsound: + { + mu = 343.0; + sigma2 = 25.0; + }; + + mics = ( + + # Microphone 1 + { + mu = ( +0.000, +0.000, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 2 + { + mu = ( -0.038133, +0.003576, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 3 + { + mu = ( -0.020980, +0.032043, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 4 + { + mu = ( +0.011971, +0.036381, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 5 + { + mu = ( +0.035908, +0.013323, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 6 + { + mu = ( +0.032805, -0.019767, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 7 + { + mu = ( +0.004999, -0.037972, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + }, + + # Microphone 8 + { + mu = ( -0.026571, -0.027584, +0.000 ); + sigma2 = ( +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000, +0.000 ); + direction = ( +0.000, +0.000, +1.000 ); + angle = ( 80.0, 100.0 ); + } + + ); + + # Spatial filters to include only a range of direction if required + # (may be useful to remove false detections from the floor, or + # limit the space search to a restricted region) + spatialfilters = ( + + { + + direction = ( +0.000, +0.000, +1.000 ); + angle = (80.0, 90.0); + + } + + ); + + nThetas = 181; + gainMin = 0.25; + +}; + +# Stationnary noise estimation + +sne: +{ + + b = 3; + alphaS = 0.1; + L = 150; + delta = 3.0; + alphaD = 0.1; + +} + +# Sound Source Localization + +ssl: +{ + + nPots = 4; + nMatches = 10; + probMin = 0.5; + nRefinedLevels = 1; + interpRate = 4; + + # Number of scans: level is the resolution of the sphere + # and delta is the size of the maximum sliding window + # (delta = -1 means the size is automatically computed) + scans = ( + { level = 2; delta = -1; }, + { level = 4; delta = -1; } + ); + + # Output to export potential sources + potential: { + + format = "json"; + interface: { + type = "socket"; + ip = "127.0.0.1"; + port = 9001; + } + + }; + +}; + +# Sound Source Tracking + +sst: +{ + + # Mode is either "kalman" or "particle" + + mode = "kalman"; + + # Add is either "static" or "dynamic" + + add = "dynamic"; + + # Parameters used by both the Kalman and particle filter + + active = ( + { weight = 1.0; mu = 0.4; sigma2 = 0.0025 } + ); + + inactive = ( + { weight = 1.0; mu = 0.25; sigma2 = 0.0025 } + ); + + sigmaR2_prob = 0.0025; + sigmaR2_active = 0.0225; + sigmaR2_target = 0.0025; + Pfalse = 0.1; + Pnew = 0.1; + Ptrack = 0.8; + + theta_new = 0.9; + N_prob = 5; + theta_prob = 0.8; + N_inactive = ( 250, 250, 250, 250 ); + theta_inactive = 0.9; + + # Parameters used by the Kalman filter only + + kalman: { + + sigmaQ = 0.001; + + }; + + # Parameters used by the particle filter only + + particle: { + + nParticles = 1000; + st_alpha = 2.0; + st_beta = 0.04; + st_ratio = 0.5; + ve_alpha = 0.05; + ve_beta = 0.2; + ve_ratio = 0.3; + ac_alpha = 0.5; + ac_beta = 0.2; + ac_ratio = 0.2; + Nmin = 0.7; + + }; + + target: (); + + # Output to export tracked sources + tracked: { + + format = "undefined"; + interface: { + type = "blackhole"; + } + + }; + +} + +sss: +{ + + # Mode is either "dds", "dgss" or "dmvdr" + + mode_sep = "dds"; + mode_pf = "ss"; + + gain_sep = 1.0; + gain_pf = 10.0; + + dds: { + + }; + + dgss: { + + mu = 0.01; + lambda = 0.5; + + }; + + dmvdr: { + + }; + + ms: { + + alphaPmin = 0.07; + eta = 0.5; + alphaZ = 0.8; + thetaWin = 0.3; + alphaWin = 0.3; + maxAbsenceProb = 0.9; + Gmin = 0.01; + winSizeLocal = 3; + winSizeGlobal = 23; + winSizeFrame = 256; + + }; + + ss: { + + Gmin = 0.01; + Gmid = 0.5; + Gslope = 10.0; + + } + + separated: { + + fS = 16000; + hopSize = 128; + nBits = 16; + + format = "undefined"; + interface: { + type = "blackhole"; + } + + }; + + postfiltered: { + + fS = 16000; + hopSize = 128; + nBits = 16; + gain = 10.0; + + interface: { + type = "blackhole"; + } + + }; + +}; + +classify: +{ + + frameSize = 4096; + winSize = 3; + tauMin = 88; + tauMax = 551; + deltaTauMax = 20; + alpha = 0.3; + gamma = 0.05; + phiMin = 0.5; + r0 = 0.2; + + category: { + + format = "undefined"; + + interface: { + type = "blackhole"; + } + + } + +} diff --git a/demo/matrix-demos/README.md b/demo/matrix-demos/README.md new file mode 100644 index 00000000..01cb92ae --- /dev/null +++ b/demo/matrix-demos/README.md @@ -0,0 +1,71 @@ +# Running ODAS using a MATRIX Creator/Voice board + +## Install Matrix Software + +```batch +# Add repo and key +curl https://apt.matrix.one/doc/apt-key.gpg | sudo apt-key add - +echo "deb https://apt.matrix.one/raspbian $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/matrixlabs.list + +# Update packages and install +sudo apt-get update +sudo apt-get upgrade + +# Installation +sudo apt install matrixio-creator-init +sudo apt install libmatrixio-creator-hal +sudo apt install libmatrixio-creator-hal-dev +sudo reboot + +``` + +After reboot, install the MATRIX Kernel Modules as follows: + +```batch +sudo apt install matrixio-kernel-modules +sudo reboot +``` + +## Install ODAS Prerequisites + +You will need CMake, GCC and the following external libraries: + +```batch +sudo apt-get install g++ git cmake +sudo apt-get install libfftw3-dev +sudo apt-get install libconfig-dev +sudo apt-get install libasound2-dev +sudo apt install libjson-c-dev +``` + +## Installing ODAS + +Clone the ODAS project: + +```batch +git clone https://github.com/introlab/odas.git +``` + +Create a folder to build the project and build it: + +```batch +cd odas +mkdir build +cd build +cmake .. +make +``` + +## Run the demo! + +You need to run two applications. The `odaslive` that performs all the cool audio processing and the `matrix-odas` that receives the result and draws it in the MATRIX Everloop. + +```batch +cd ~/odas/bin +./matrix-odas & +./odaslive -vc ../config/matrix-demo/matrix_voice.cfg +``` + +Make some noise! ... you should see a blue lights indicating where the sound is coming from. + +![](./matrix-odas-running.gif) diff --git a/demo/matrix-demos/matrix-odas-running.gif b/demo/matrix-demos/matrix-odas-running.gif new file mode 100644 index 00000000..d25cae79 Binary files /dev/null and b/demo/matrix-demos/matrix-odas-running.gif differ diff --git a/demo/matrix-demos/matrix-odas.cpp b/demo/matrix-demos/matrix-odas.cpp new file mode 100644 index 00000000..da3f7668 --- /dev/null +++ b/demo/matrix-demos/matrix-odas.cpp @@ -0,0 +1,205 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hal = matrix_hal; + +// ENERGY_COUNT : Number of sound energy slots to maintain. +#define ENERGY_COUNT 36 +// MAX_VALUE : controls smoothness +#define MAX_VALUE 200 +// INCREMENT : controls sensitivity +#define INCREMENT 20 +// DECREMENT : controls delay in the dimming +#define DECREMENT 1 +// MAX_BRIGHTNESS: Filters out low energy +#define MIN_THRESHOLD 10 +// MAX_BRIGHTNESS: 0 - 255 +#define MAX_BRIGHTNESS 50 + +double x, y, z, E; +int energy_array[ENERGY_COUNT]; +const double leds_angle_mcreator[35] = { + 170, 159, 149, 139, 129, 118, 108, 98, 87, 77, 67, 57, + 46, 36, 26, 15, 5, 355, 345, 334, 324, 314, 303, 293, + 283, 273, 262, 252, 242, 231, 221, 211, 201, 190, 180}; + +const double led_angles_mvoice[18] = {170, 150, 130, 110, 90, 70, + 50, 30, 10, 350, 330, 310, + 290, 270, 250, 230, 210, 190}; + +void increase_pots() { + // Convert x,y to angle. TODO: See why x axis from ODAS is inverted + double angle_xy = fmodf((atan2(y, x) * (180.0 / M_PI)) + 360, 360); + // Convert angle to index + int i_angle = angle_xy / 360 * ENERGY_COUNT; // convert degrees to index + // Set energy for this angle + energy_array[i_angle] += INCREMENT * E; + // Set limit at MAX_VALUE + energy_array[i_angle] = + energy_array[i_angle] > MAX_VALUE ? MAX_VALUE : energy_array[i_angle]; +} + +void decrease_pots() { + for (int i = 0; i < ENERGY_COUNT; i++) { + energy_array[i] -= (energy_array[i] > 0) ? DECREMENT : 0; + } +} + +void json_parse_array(json_object *jobj, char *key) { + // Forward Declaration + void json_parse(json_object * jobj); + enum json_type type; + json_object *jarray = jobj; + if (key) { + if (json_object_object_get_ex(jobj, key, &jarray) == false) { + printf("Error parsing json object\n"); + return; + } + } + + int arraylen = json_object_array_length(jarray); + int i; + json_object *jvalue; + + for (i = 0; i < arraylen; i++) { + jvalue = json_object_array_get_idx(jarray, i); + type = json_object_get_type(jvalue); + + if (type == json_type_array) { + json_parse_array(jvalue, NULL); + } else if (type != json_type_object) { + } else { + json_parse(jvalue); + } + } +} + +void json_parse(json_object *jobj) { + enum json_type type; + unsigned int count = 0; + decrease_pots(); + json_object_object_foreach(jobj, key, val) { + type = json_object_get_type(val); + switch (type) { + case json_type_boolean: + break; + case json_type_double: + if (!strcmp(key, "x")) { + x = json_object_get_double(val); + } else if (!strcmp(key, "y")) { + y = json_object_get_double(val); + } else if (!strcmp(key, "z")) { + z = json_object_get_double(val); + } else if (!strcmp(key, "E")) { + E = json_object_get_double(val); + } + increase_pots(); + count++; + break; + case json_type_int: + break; + case json_type_string: + break; + case json_type_object: + if (json_object_object_get_ex(jobj, key, &jobj) == false) { + printf("Error parsing json object\n"); + return; + } + json_parse(jobj); + break; + case json_type_array: + json_parse_array(jobj, key); + break; + } + } +} + +int main(int argc, char *argv[]) { + // Everloop Initialization + hal::MatrixIOBus bus; + if (!bus.Init()) return false; + hal::EverloopImage image1d(bus.MatrixLeds()); + hal::Everloop everloop; + everloop.Setup(&bus); + + // Clear all LEDs + for (hal::LedValue &led : image1d.leds) { + led.red = 0; + led.green = 0; + led.blue = 0; + led.white = 0; + } + everloop.Write(&image1d); + + char verbose = 0x00; + + int server_id; + struct sockaddr_in server_address; + int connection_id; + char *message; + int messageSize; + + int c; + unsigned int portNumber = 9001; + const unsigned int nBytes = 10240; + + server_id = socket(AF_INET, SOCK_STREAM, 0); + + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = htonl(INADDR_ANY); + server_address.sin_port = htons(portNumber); + + printf("Binding socket........... "); + fflush(stdout); + bind(server_id, (struct sockaddr *)&server_address, sizeof(server_address)); + printf("[OK]\n"); + + printf("Listening socket......... "); + fflush(stdout); + listen(server_id, 1); + printf("[OK]\n"); + + printf("Waiting for connection in port %d ... ", portNumber); + fflush(stdout); + connection_id = accept(server_id, (struct sockaddr *)NULL, NULL); + printf("[OK]\n"); + + message = (char *)malloc(sizeof(char) * nBytes); + + printf("Receiving data........... \n\n"); + + while ((messageSize = recv(connection_id, message, nBytes, 0)) > 0) { + message[messageSize] = 0x00; + + // printf("message: %s\n\n", message); + json_object *jobj = json_tokener_parse(message); + json_parse(jobj); + + for (int i = 0; i < bus.MatrixLeds(); i++) { + // led index to angle + int led_angle = bus.MatrixName() == hal::kMatrixCreator + ? leds_angle_mcreator[i] + : led_angles_mvoice[i]; + // Convert from angle to pots index + int index_pots = led_angle * ENERGY_COUNT / 360; + // Mapping from pots values to color + int color = energy_array[index_pots] * MAX_BRIGHTNESS / MAX_VALUE; + // Removing colors below the threshold + color = (color < MIN_THRESHOLD) ? 0 : color; + + image1d.leds[i].red = 0; + image1d.leds[i].green = 0; + image1d.leds[i].blue = color; + image1d.leds[i].white = 0; + } + everloop.Write(&image1d); + } +} \ No newline at end of file