diff --git a/conf/airframes/tudelft/bebop2_optitrack_visionfront.xml b/conf/airframes/tudelft/bebop2_optitrack_visionfront.xml
new file mode 100644
index 0000000000..ffb9c1e212
--- /dev/null
+++ b/conf/airframes/tudelft/bebop2_optitrack_visionfront.xml
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/modules/cv_opticflow.xml b/conf/modules/cv_opticflow.xml
index b313d4de82..a787ffe102 100644
--- a/conf/modules/cv_opticflow.xml
+++ b/conf/modules/cv_opticflow.xml
@@ -43,6 +43,8 @@
+
+
@@ -50,6 +52,13 @@
+
+
+
+
+
+
+
@@ -58,6 +67,7 @@
+
@@ -78,6 +88,12 @@
+
+
+
+
+
+
@@ -112,6 +128,7 @@
+
diff --git a/conf/userconf/tudelft/conf.xml b/conf/userconf/tudelft/conf.xml
index 0bd6fb715d..94f29e42fd 100644
--- a/conf/userconf/tudelft/conf.xml
+++ b/conf/userconf/tudelft/conf.xml
@@ -485,6 +485,18 @@
settings_modules="modules/gps_ubx_ucenter.xml modules/air_data.xml modules/geo_mag.xml modules/ins_extended.xml modules/ahrs_int_cmpl_quat.xml modules/stabilization_indi_simple.xml modules/nav_basic_rotorcraft.xml modules/guidance_rotorcraft.xml modules/gps.xml modules/imu_common.xml"
gui_color="#ffffbf17bf17"
/>
+
-
diff --git a/sw/airborne/modules/computer_vision/lib/vision/act_fast.c b/sw/airborne/modules/computer_vision/lib/vision/act_fast.c
new file mode 100644
index 0000000000..8b52ff1027
--- /dev/null
+++ b/sw/airborne/modules/computer_vision/lib/vision/act_fast.c
@@ -0,0 +1,168 @@
+/*
+Copyright (c) 2017, Guido de Croon, TU Delft
+All rights reserved.
+*/
+
+
+/**
+ * @file modules/computer_vision/lib/vision/act_fast.c
+ * @brief Finds corners in an image by actively scanning the image. This method is inspired by the work in:
+ * de Croon, G.C.H.E., and Nolfi, S. (2013, May). Act-corner: Active corner finding for optic flow determination. In Robotics and Automation (ICRA), 2013 IEEE International Conference on (pp. 4679-4684). IEEE.
+ *
+ * The main idea of this particular implementation, called ACT-FAST, is that actively scanning the image allows to:
+ * 1. Skip uniform areas in the image. If these areas are contiguous, many non-corners will never be evaluated.
+ * 2. Follow edges. Typically, following an edge will bring an agent to a corner.
+ * These two simple rules lead to a significant lower number of corner evaluations, while still detecting a reasonable number of corners.
+ * Moreover, since the agents scanning the image start on a grid, corners will be quite well-distributed over the image.
+ * Each step of the agent starts by classifying the agent location as a corner or not with FAST.
+ *
+ * For bigger images (e.g., 640 x 640), the computational advantage of ACT-FAST over the normal, exhaustive application of FAST becomes significant (in the order of a factor > 10).
+ *
+ * The code here is part of the following publication:
+ * de Croon, G.C.H.E. "ACT-FAST: efficiently finding corners by actively exploring images.", in submission.
+ *
+ */
+
+#include "fast_rosten.h"
+#include "act_fast.h"
+#include "math.h"
+#include "image.h"
+
+
+#define MAX_AGENTS 1000
+struct agent_t agents[MAX_AGENTS];
+
+/**
+ * Do an ACT-FAST corner detection.
+ * @param[in] *img The image to do the corner detection on
+ * @param[in] threshold The threshold which we use for FAST9
+ * @param[in] *num_corners reference to the amount of corners found, set by this function
+ * @param[in] **ret_corners pointer to the array which contains the corners that were detected.
+ * @param[in] n_agents The number of agents that will scan the image for corners
+ * @param[in] n_time_steps The maximum number of time steps allowed for scanning
+ * @param[in] long_step When there is not enough texture, the agent will take a long step to a next point of this length in pixels
+ * @param[in] short_step When there is texture, the agent will follow the edge with this short step in pixels
+ * @param[in] min_gradient The minimum gradient, in order to determine when to take a long or short step
+ * @param[in] gradient_method: 0 = simple {-1, 0, 1}, 1 = Sobel {-1,0,1,-2,0,2,-1,0,1}
+*/
+void act_fast(struct image_t *img, uint8_t fast_threshold, uint16_t *num_corners, struct point_t **ret_corners,
+ uint16_t n_agents, uint16_t n_time_steps, float long_step, float short_step, int min_gradient, int gradient_method)
+{
+
+ /*
+ * Procedure:
+ * 1) initialize agent positions
+ * 2) loop over the agents, moving and checking for corners
+ */
+
+ // ensure that n_agents is never bigger than MAX_AGENTS
+ n_agents = (n_agents < MAX_AGENTS) ? n_agents : MAX_AGENTS;
+
+ int border = 4;
+
+ // ***********************************
+ // 1) initialize the agents' positions
+ // ***********************************
+
+ // grid sampling with a border:
+ int init_border = 10;
+ float GRID_ROWS = (int) ceil(sqrtf((float) n_agents));
+ float step_size_x = (img->w - 2 * init_border) / (GRID_ROWS - 1);
+ float step_size_y = (img->h - 2 * init_border) / (GRID_ROWS - 1);
+
+ int a = 0;
+ float px, py, pnorm;
+ for (int c = 0; c < GRID_ROWS; c++) {
+ for (int r = 0; r < GRID_ROWS; r++) {
+ // px, py represent the preferred direction of the agent when there is no texture
+ // here we initialize it differently for each agent:
+ // TODO: don't we have a randf function in Paparazzi?
+ px = ((float)(rand() % 10000)) / 10000.0f;
+ py = ((float)(rand() % 10000)) / 10000.0f;
+ pnorm = sqrtf(px * px + py * py);
+ struct agent_t ag = { (border + c * step_size_x), (border + r * step_size_y), 1, px / pnorm, py / pnorm};
+ agents[a] = ag;
+ a++;
+ if (a == n_agents) { break; }
+ }
+
+ // only initialize a maximum of n_agents agents.
+ if (a == n_agents) { break; }
+ }
+
+ /* ********************************************************
+ * 2) loop over the agents, moving and checking for corners
+ * ********************************************************/
+
+ // gradient
+ int dx, dy;
+
+ // loop over all time steps:
+ for (int t = 0; t < n_time_steps; t++) {
+ // loop over the agents
+ for (a = 0; a < n_agents; a++) {
+ // only do something if the agent is active:
+ if (agents[a].active) {
+ // check if this position is a corner:
+ uint16_t x = (uint16_t) agents[a].x;
+ uint16_t y = (uint16_t) agents[a].y;
+ if (fast9_detect_pixel(img, fast_threshold, x, y)) {
+ // we arrived at a corner, yeah!!!
+ agents[a].active = 0;
+ break;
+ } else {
+ // make a step:
+ struct point_t loc = {agents[a].x, agents[a].y};
+ image_gradient_pixel(img, &loc, gradient_method, &dx, &dy);
+ int gradient = (abs(dx) + abs(dy)) / 2;
+ if (abs(gradient) >= min_gradient) {
+ // determine the angle and make a step in that direction:
+ float norm_factor = sqrtf((float)(dx * dx + dy * dy));
+ agents[a].x += (dy / norm_factor) * short_step;
+ agents[a].y += (dx / norm_factor) * short_step;
+ } else {
+ // make a step in the preferred direction:
+ agents[a].x += agents[a].preferred_dir_x * long_step;
+ agents[a].y += agents[a].preferred_dir_y * long_step;
+ }
+ }
+
+ // let the agent move over the image in a toroid world:
+ if (agents[a].x > img->w - border) {
+ agents[a].x = border;
+ } else if (agents[a].x < border) {
+ agents[a].x = img->w - border;
+ }
+ if (agents[a].y > img->h - border) {
+ agents[a].y = border;
+ } else if (agents[a].y < border) {
+ agents[a].y = img->h - border;
+ }
+ }
+ }
+ }
+
+ // Transform agents to corners:
+ (*num_corners) = 0;
+ for (a = 0; a < n_agents; a++) {
+
+ // for active agents do a last check on the new position:
+ if (agents[a].active) {
+ // check if the last step brought the agent to a corner:
+ uint16_t x = (uint16_t) agents[a].x;
+ uint16_t y = (uint16_t) agents[a].y;
+ if (fast9_detect_pixel(img, fast_threshold, x, y)) {
+ // we arrived at a corner, yeah!!!
+ agents[a].active = 0;
+ }
+ }
+
+ // if inactive, the agent is a corner:
+ if (!agents[a].active) {
+ (*ret_corners)[(*num_corners)].x = (uint32_t) agents[a].x;
+ (*ret_corners)[(*num_corners)].y = (uint32_t) agents[a].y;
+ (*num_corners)++;
+ }
+ }
+}
+
diff --git a/sw/airborne/modules/computer_vision/lib/vision/act_fast.h b/sw/airborne/modules/computer_vision/lib/vision/act_fast.h
new file mode 100644
index 0000000000..2e5802714b
--- /dev/null
+++ b/sw/airborne/modules/computer_vision/lib/vision/act_fast.h
@@ -0,0 +1,42 @@
+/*
+Copyright (c) 2017, Guido de Croon, TU Delft
+All rights reserved.
+*/
+
+/**
+ * @file modules/computer_vision/lib/vision/act_fast.h
+ * @brief Finds corners in an image by actively scanning the image. This method is inspired by the work in:
+ * de Croon, G.C.H.E., and Nolfi, S. (2013, May). Act-corner: Active corner finding for optic flow determination. In Robotics and Automation (ICRA), 2013 IEEE International Conference on (pp. 4679-4684). IEEE.
+ *
+ * The main idea of this particular implementation, called ACT-FAST, is that actively scanning the image allows to:
+ * 1. Skip uniform areas in the image. If these areas are contiguous, many non-corners will never be evaluated.
+ * 2. Follow edges. Typically, following an edge will bring an agent to a corner.
+ * These two simple rules lead to a significant lower number of corner evaluations, while still detecting a reasonable number of corners.
+ * Moreover, since the agents scanning the image start on a grid, corners will be quite well-distributed over the image.
+ * Each step of the agent starts by classifying the agent location as a corner or not with FAST.
+ *
+ * For bigger images (e.g., 640 x 640), the computational advantage of ACT-FAST over the normal, exhaustive application of FAST becomes significant (in the order of a factor > 10).
+ *
+ * The code here is part of the following publication:
+ * de Croon, G.C.H.E. "ACT-FAST: efficiently finding corners by actively exploring images.", in submission.
+ *
+ */
+
+#ifndef ACT_FAST_H
+#define ACT_FAST_H
+
+struct agent_t {
+ float x;
+ float y;
+ int active;
+ float preferred_dir_x;
+ float preferred_dir_y;
+};
+
+#include "std.h"
+#include "lib/vision/image.h"
+
+void act_fast(struct image_t *img, uint8_t fast_threshold, uint16_t *num_corners, struct point_t **ret_corners,
+ uint16_t n_agents, uint16_t n_time_steps, float long_step, float short_step, int min_gradient, int gradient_method);
+
+#endif
diff --git a/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.c b/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.c
index 6eb4065d6b..464658a64e 100644
--- a/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.c
+++ b/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.c
@@ -63,13 +63,8 @@ void fast9_detect(struct image_t *img, uint8_t threshold, uint16_t min_dist, uin
pixel_size = 2;
}
- // Padding less than min_dist could cause overflow on some comparisons below.
- if (x_padding < min_dist) {
- x_padding = min_dist;
- }
- if (y_padding < min_dist) {
- y_padding = min_dist;
- }
+ if(x_padding < min_dist) x_padding = min_dist;
+ if(y_padding < min_dist) y_padding = min_dist;
if (!roi) {
x_start = 3 + x_padding;
@@ -90,9 +85,7 @@ void fast9_detect(struct image_t *img, uint8_t threshold, uint16_t min_dist, uin
// Go trough all the pixels (minus the borders and inside the requested roi)
for (y = y_start; y < y_end; y++) {
- if (min_dist > 0) {
- y_min = y - min_dist;
- }
+ if (min_dist > 0) { y_min = y - min_dist; }
for (x = x_start; x < x_end; x++) {
// First check if we aren't in range vertical (TODO: fix less intensive way)
@@ -113,15 +106,6 @@ void fast9_detect(struct image_t *img, uint8_t threshold, uint16_t min_dist, uin
if ((*ret_corners)[i].y < y_min) {
break;
}
- /*
- // If detecting with already existing corners gives too much overlap uncomment this comparison instead of the one above.
- // But, it will make the detection more time consuming
- // TODO: maybe sort the corners before calling...
- if(ret_corners[i].y < y_min || ret_corners[i].y > y_max){
- i--;
- continue;
- }
- */
if (x_min < (*ret_corners)[i].x && (*ret_corners)[i].x < x_max) {
need_skip = 1;
@@ -3714,3 +3698,3565 @@ static void fast_make_offsets(int32_t *pixel, uint16_t row_stride, uint8_t pixel
pixel[15] = -1 * pixel_size + row_stride * 3 * pixel_size;
}
+
+/**
+ * Do a FAST9 corner detection for a single pixel. Returns 0 when not a corner, and 1 when a corner.
+ * @param[in] *img The image to do the corner detection on
+ * @param[in] threshold The threshold which we use for FAST9
+ * @param[in] x, the x-coordinate of the pixel
+ * @param[in] y, the y-coordinate of the pixel
+ */
+int fast9_detect_pixel(struct image_t *img, uint8_t threshold, uint16_t x, uint16_t y) {
+
+ // Set the pixel size
+ uint8_t pixel_size = 1;
+ if (img->type == IMAGE_YUV422) {
+ pixel_size = 2;
+ }
+
+ int pixel[16];
+ // Calculate the pixel offsets: not efficient to do this every time:
+ fast_make_offsets(pixel, img->w, pixel_size);
+
+ uint16_t border = 4;
+ if(x < border || x > img->w - border || y < border || y > img->h - border) {
+ return 0;
+ }
+ else {
+ // Calculate the threshold values
+ const uint8_t *p = ((uint8_t *)img->buf) + y * img->w * pixel_size + x * pixel_size + pixel_size / 2;
+ int16_t cb = *p + threshold;
+ int16_t c_b = *p - threshold;
+
+ // Do the checks if it is a corner
+ if (p[pixel[0]] > cb)
+ if (p[pixel[1]] > cb)
+ if (p[pixel[2]] > cb)
+ if (p[pixel[3]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[7]] < c_b)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[15]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[6]] < c_b)
+ {}
+ else if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[6]] < c_b)
+ {}
+ else if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[12]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ {}
+ else if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ {}
+ else if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] < c_b)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[4]] < c_b)
+ {}
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[4]] < c_b)
+ {}
+ else if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[2]] < c_b)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[9]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[3]] < c_b)
+ {}
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[9]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[3]] < c_b)
+ {}
+ else if (p[pixel[12]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[1]] < c_b)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[2]] > cb)
+ if (p[pixel[3]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[8]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[3]] < c_b)
+ if (p[pixel[2]] < c_b)
+ {}
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[2]] > cb)
+ if (p[pixel[3]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[8]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[3]] < c_b)
+ if (p[pixel[2]] < c_b)
+ {}
+ else if (p[pixel[11]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[0]] < c_b)
+ if (p[pixel[1]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[3]] > cb)
+ if (p[pixel[2]] > cb)
+ {}
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[2]] < c_b)
+ if (p[pixel[3]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[1]] < c_b)
+ if (p[pixel[2]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[3]] > cb)
+ {}
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[2]] < c_b)
+ if (p[pixel[3]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[4]] > cb)
+ {}
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] < c_b)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ {}
+ else if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[12]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[6]] > cb)
+ {}
+ else if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] < c_b)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[6]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[15]] < c_b)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[6]] > cb)
+ {}
+ else if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ {}
+ else if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[4]] > cb)
+ {}
+ else if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[9]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[3]] > cb)
+ {}
+ else if (p[pixel[12]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[8]] > cb)
+ if (p[pixel[7]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[10]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[3]] > cb)
+ if (p[pixel[2]] > cb)
+ {}
+ else if (p[pixel[11]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[3]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[2]] < c_b)
+ if (p[pixel[3]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[7]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[7]] > cb)
+ if (p[pixel[8]] > cb)
+ if (p[pixel[9]] > cb)
+ if (p[pixel[6]] > cb)
+ if (p[pixel[5]] > cb)
+ if (p[pixel[4]] > cb)
+ if (p[pixel[3]] > cb)
+ if (p[pixel[2]] > cb)
+ if (p[pixel[1]] > cb)
+ {}
+ else if (p[pixel[10]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] > cb)
+ if (p[pixel[11]] > cb)
+ if (p[pixel[12]] > cb)
+ if (p[pixel[13]] > cb)
+ if (p[pixel[14]] > cb)
+ if (p[pixel[15]] > cb)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[7]] < c_b)
+ if (p[pixel[8]] < c_b)
+ if (p[pixel[9]] < c_b)
+ if (p[pixel[6]] < c_b)
+ if (p[pixel[5]] < c_b)
+ if (p[pixel[4]] < c_b)
+ if (p[pixel[3]] < c_b)
+ if (p[pixel[2]] < c_b)
+ if (p[pixel[1]] < c_b)
+ {}
+ else if (p[pixel[10]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else if (p[pixel[10]] < c_b)
+ if (p[pixel[11]] < c_b)
+ if (p[pixel[12]] < c_b)
+ if (p[pixel[13]] < c_b)
+ if (p[pixel[14]] < c_b)
+ if (p[pixel[15]] < c_b)
+ {}
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ // if not returned yet, it is a corner:
+ return 1;
+ }
+}
diff --git a/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.h b/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.h
index c831bd14e1..70391152ad 100644
--- a/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.h
+++ b/sw/airborne/modules/computer_vision/lib/vision/fast_rosten.h
@@ -38,5 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "lib/vision/image.h"
void fast9_detect(struct image_t *img, uint8_t threshold, uint16_t min_dist, uint16_t x_padding, uint16_t y_padding, uint16_t *num_corners, uint16_t *ret_corners_length, struct point_t **ret_corners, uint16_t *roi);
+int fast9_detect_pixel(struct image_t *img, uint8_t threshold, uint16_t x, uint16_t y);
+
#endif
diff --git a/sw/airborne/modules/computer_vision/lib/vision/image.c b/sw/airborne/modules/computer_vision/lib/vision/image.c
index a0d58a2847..4b5151994a 100644
--- a/sw/airborne/modules/computer_vision/lib/vision/image.c
+++ b/sw/airborne/modules/computer_vision/lib/vision/image.c
@@ -599,6 +599,77 @@ void image_show_flow(struct image_t *img, struct flow_t *vectors, uint16_t point
}
}
+/**
+ * Get the gradient at a pixel location
+ * @param[in,out] *img The image
+ * @param[in] loc The location at which to get the gradient
+ * @param[in] method 0 = {-1, 0, 1}, 1 = Sobel {-1, 0, 1; -2, 0, 2; -1, 0, 1}
+ * @param[in] dx The gradient in x-direction
+ * @param[in] dy The gradient in y-direction
+ */
+void image_gradient_pixel(struct image_t *img, struct point_t *loc, int method, int *dx, int* dy) {
+ // create the simple and sobel filter only once:
+
+ int gradient_x, gradient_y, index;
+ gradient_x = 0;
+ gradient_y = 0;
+
+ // get image buffer and take into account YUV vs. grayscale:
+ uint8_t *img_buf = (uint8_t *)img->buf;
+ uint8_t pixel_width = (img->type == IMAGE_YUV422) ? 2 : 1;
+ uint8_t add_ind = pixel_width - 1;
+
+ // check if all pixels will fall in the image:
+ if(loc->x >= 1 && loc->x < img->w-1 && loc->y >= 1 && loc->y < img->h - 1) {
+ if(method == 0) {
+
+ // *************
+ // Simple method
+ // *************
+
+ // dx:
+ index = loc->y * img->w * pixel_width + (loc->x-1) * pixel_width;
+ gradient_x -= (int) img_buf[index+add_ind];
+ index = loc->y * img->w * pixel_width + (loc->x+1) * pixel_width;
+ gradient_x += (int) img_buf[index+add_ind];
+ // dy:
+ index = (loc->y-1) * img->w * pixel_width + loc->x * pixel_width;
+ gradient_y -= (int) img_buf[index+add_ind];
+ index = (loc->y+1) * img->w * pixel_width + loc->x * pixel_width;
+ gradient_y += (int) img_buf[index+add_ind];
+ }
+ else {
+
+ // *****
+ // Sobel
+ // *****
+ static int Sobel[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
+ static int total_sobel = 8;
+
+ int filt_ind_y = 0;
+ int filt_ind_x;
+ for (int x = -1; x <= 1; x++) {
+ for (int y = -1; y <= 1; y++) {
+ index = (loc->y + y) * img->w * pixel_width + (loc->x+x) * pixel_width;
+ if(x!=0) {
+ filt_ind_x = (x+1)%3 + (y+1)*3;
+ gradient_x += Sobel[filt_ind_x] * (int) img_buf[index+add_ind];
+ }
+ if(y!=0) {
+ gradient_y += Sobel[filt_ind_y] * (int) img_buf[index+add_ind];
+ }
+ filt_ind_y++;
+ }
+ }
+ gradient_x /= total_sobel;
+ }
+ }
+
+ // TODO: more efficient would be to use dx, dy directly:
+ (*dx) = gradient_x;
+ (*dy) = gradient_y;
+}
+
/**
* Draw a pink line on the image
* @param[in,out] *img The image to show the line on
diff --git a/sw/airborne/modules/computer_vision/lib/vision/image.h b/sw/airborne/modules/computer_vision/lib/vision/image.h
index bebe4e8a86..5528acf9a9 100644
--- a/sw/airborne/modules/computer_vision/lib/vision/image.h
+++ b/sw/airborne/modules/computer_vision/lib/vision/image.h
@@ -105,5 +105,6 @@ void image_draw_line(struct image_t *img, struct point_t *from, struct point_t *
void image_draw_line_color(struct image_t *img, struct point_t *from, struct point_t *to, uint8_t *color);
void pyramid_next_level(struct image_t *input, struct image_t *output, uint8_t border_size);
void pyramid_build(struct image_t *input, struct image_t *output_array, uint8_t pyr_level, uint16_t border_size);
+void image_gradient_pixel(struct image_t *img, struct point_t *loc, int method, int *dx, int* dy);
#endif
diff --git a/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.c b/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.c
index b0d2634d39..1b9eb8b6f4 100644
--- a/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.c
+++ b/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.c
@@ -39,6 +39,7 @@
#include "lib/vision/image.h"
#include "lib/vision/lucas_kanade.h"
#include "lib/vision/fast_rosten.h"
+#include "lib/vision/act_fast.h"
#include "lib/vision/edge_flow.h"
#include "size_divergence.h"
#include "linear_flow_fit.h"
@@ -48,6 +49,12 @@
#define OPTICFLOW_SHOW_FLOW 0
#define OPTICFLOW_SHOW_CORNERS 0
+#define EXHAUSTIVE_FAST 0
+#define ACT_FAST 1
+// TODO: these are now adapted, but perhaps later could be a setting:
+uint16_t n_time_steps = 10;
+uint16_t n_agents = 25;
+
// What methods are run to determine divergence, lateral flow, etc.
// SIZE_DIV looks at line sizes and only calculates divergence
#define SIZE_DIV 1
@@ -55,6 +62,12 @@
// relative velocities in x, y, z (divergence / time to contact), the slope of the surface, and the surface roughness.
#define LINEAR_FIT 1
+#ifndef OPTICFLOW_CORNER_METHOD
+// This can be estimated by total possible image height / total Field of view
+#define OPTICFLOW_CORNER_METHOD ACT_FAST
+#endif
+PRINT_CONFIG_VAR(OPTICFLOW_CORNER_METHOD)
+
// Camera parameters (defaults are from an ARDrone 2)
#ifndef OPTICFLOW_FOV_W
#define OPTICFLOW_FOV_W 0.89360857702
@@ -187,6 +200,26 @@ PRINT_CONFIG_VAR(OPTICFLOW_FAST9_REGION_DETECT)
#endif
PRINT_CONFIG_VAR(OPTICFLOW_FAST9_NUM_REGIONS)
+#ifndef OPTICFLOW_ACTFAST_LONG_STEP
+#define OPTICFLOW_ACTFAST_LONG_STEP 10
+#endif
+PRINT_CONFIG_VAR(OPTICFLOW_ACTFAST_LONG_STEP)
+
+#ifndef OPTICFLOW_ACTFAST_SHORT_STEP
+#define OPTICFLOW_ACTFAST_SHORT_STEP 2
+#endif
+PRINT_CONFIG_VAR(OPTICFLOW_ACTFAST_SHORT_STEP)
+
+#ifndef OPTICFLOW_ACTFAST_GRADIENT_METHOD
+#define OPTICFLOW_ACTFAST_GRADIENT_METHOD 1
+#endif
+PRINT_CONFIG_VAR(OPTICFLOW_ACTFAST_GRADIENT_METHOD)
+
+#ifndef OPTICFLOW_ACTFAST_MIN_GRADIENT
+#define OPTICFLOW_ACTFAST_MIN_GRADIENT 10
+#endif
+PRINT_CONFIG_VAR(OPTICFLOW_ACTFAST_MIN_GRADIENT)
+
// Defaults for ARdrone
#ifndef OPTICFLOW_BODY_TO_CAM_PHI
#define OPTICFLOW_BODY_TO_CAM_PHI 0
@@ -245,6 +278,12 @@ void opticflow_calc_init(struct opticflow_t *opticflow)
opticflow->fast9_rsize = 512;
opticflow->fast9_ret_corners = calloc(opticflow->fast9_rsize, sizeof(struct point_t));
+ opticflow->corner_method = OPTICFLOW_CORNER_METHOD;
+ opticflow->actfast_long_step = OPTICFLOW_ACTFAST_LONG_STEP;
+ opticflow->actfast_short_step = OPTICFLOW_ACTFAST_SHORT_STEP;
+ opticflow->actfast_min_gradient = OPTICFLOW_ACTFAST_MIN_GRADIENT;
+ opticflow->actfast_gradient_method = OPTICFLOW_ACTFAST_GRADIENT_METHOD;
+
struct FloatEulers euler = {OPTICFLOW_BODY_TO_CAM_PHI, OPTICFLOW_BODY_TO_CAM_THETA, OPTICFLOW_BODY_TO_CAM_PSI};
float_rmat_of_eulers(&body_to_cam, &euler);
}
@@ -307,23 +346,47 @@ bool calc_fast9_lukas_kanade(struct opticflow_t *opticflow, struct image_t *img,
} else if (!opticflow->feature_management) {
// needs to be set to 0 because result is now static
result->corner_cnt = 0;
- // FAST corner detection
- // TODO: There is something wrong with fast9_detect destabilizing FPS. This problem is reduced with putting min_distance
- // to 0 (see defines), however a more permanent solution should be considered
- fast9_detect(&opticflow->prev_img_gray, opticflow->fast9_threshold, opticflow->fast9_min_distance,
- opticflow->fast9_padding, opticflow->fast9_padding, &result->corner_cnt,
- &opticflow->fast9_rsize,
- &opticflow->fast9_ret_corners,
- NULL);
+
+ if (opticflow->corner_method == EXHAUSTIVE_FAST) {
+ // FAST corner detection
+ // TODO: There is something wrong with fast9_detect destabilizing FPS. This problem is reduced with putting min_distance
+ // to 0 (see defines), however a more permanent solution should be considered
+ fast9_detect(&opticflow->prev_img_gray, opticflow->fast9_threshold, opticflow->fast9_min_distance,
+ opticflow->fast9_padding, opticflow->fast9_padding, &result->corner_cnt,
+ &opticflow->fast9_rsize,
+ &opticflow->fast9_ret_corners,
+ NULL);
+
+ } else if (opticflow->corner_method == ACT_FAST) {
+ // ACT-FAST corner detection:
+ act_fast(&opticflow->prev_img_gray, opticflow->fast9_threshold, &result->corner_cnt,
+ &opticflow->fast9_ret_corners, n_agents, n_time_steps,
+ opticflow->actfast_long_step, opticflow->actfast_short_step, opticflow->actfast_min_gradient,
+ opticflow->actfast_gradient_method);
+ }
// Adaptive threshold
if (opticflow->fast9_adaptive) {
+
+ // This works well for exhaustive FAST, but drives the threshold to the minimum for ACT-FAST:
// Decrease and increase the threshold based on previous values
- if (result->corner_cnt < 40
- && opticflow->fast9_threshold > FAST9_LOW_THRESHOLD) { // TODO: Replace 40 with OPTICFLOW_MAX_TRACK_CORNERS / 2
- opticflow->fast9_threshold--;
+ if (result->corner_cnt < 40) { // TODO: Replace 40 with OPTICFLOW_MAX_TRACK_CORNERS / 2
+ // make detections easier:
+ if (opticflow->fast9_threshold > FAST9_LOW_THRESHOLD) {
+ opticflow->fast9_threshold--;
+ }
+
+ if (opticflow->corner_method == ACT_FAST) {
+ n_time_steps++;
+ n_agents++;
+ }
+
} else if (result->corner_cnt > OPTICFLOW_MAX_TRACK_CORNERS * 2 && opticflow->fast9_threshold < FAST9_HIGH_THRESHOLD) {
opticflow->fast9_threshold++;
+ if (opticflow->corner_method == ACT_FAST && n_time_steps > 5 && n_agents > 10) {
+ n_time_steps--;
+ n_agents--;
+ }
}
}
}
@@ -436,8 +499,10 @@ bool calc_fast9_lukas_kanade(struct opticflow_t *opticflow, struct image_t *img,
// Velocity calculation
// Right now this formula is under assumption that the flow only exist in the center axis of the camera.
// TODO Calculate the velocity more sophisticated, taking into account the drone's angle and the slope of the ground plane.
- result->vel_cam.x = (float)result->flow_der_x * result->fps * agl_dist_value_filtered / (opticflow->subpixel_factor * OPTICFLOW_FX);
- result->vel_cam.y = (float)result->flow_der_y * result->fps * agl_dist_value_filtered / (opticflow->subpixel_factor * OPTICFLOW_FY);
+ result->vel_cam.x = (float)result->flow_der_x * result->fps * agl_dist_value_filtered /
+ (opticflow->subpixel_factor * OPTICFLOW_FX);
+ result->vel_cam.y = (float)result->flow_der_y * result->fps * agl_dist_value_filtered /
+ (opticflow->subpixel_factor * OPTICFLOW_FY);
result->vel_cam.z = result->divergence * result->fps * agl_dist_value_filtered;
//Apply a median filter to the velocity if wanted
@@ -457,8 +522,10 @@ bool calc_fast9_lukas_kanade(struct opticflow_t *opticflow, struct image_t *img,
result->corner_cnt = result->tracked_cnt;
//get the new positions of the corners and the "residual" subpixel positions
for (uint16_t i = 0; i < result->tracked_cnt; i++) {
- opticflow->fast9_ret_corners[i].x = (uint32_t)((vectors[i].pos.x + (float)vectors[i].flow_x) / opticflow->subpixel_factor);
- opticflow->fast9_ret_corners[i].y = (uint32_t)((vectors[i].pos.y + (float)vectors[i].flow_y) / opticflow->subpixel_factor);
+ opticflow->fast9_ret_corners[i].x = (uint32_t)((vectors[i].pos.x + (float)vectors[i].flow_x) /
+ opticflow->subpixel_factor);
+ opticflow->fast9_ret_corners[i].y = (uint32_t)((vectors[i].pos.y + (float)vectors[i].flow_y) /
+ opticflow->subpixel_factor);
opticflow->fast9_ret_corners[i].x_sub = (uint16_t)((vectors[i].pos.x + vectors[i].flow_x) % opticflow->subpixel_factor);
opticflow->fast9_ret_corners[i].y_sub = (uint16_t)((vectors[i].pos.y + vectors[i].flow_y) % opticflow->subpixel_factor);
opticflow->fast9_ret_corners[i].count = vectors[i].pos.count;
@@ -482,8 +549,10 @@ static void manage_flow_features(struct image_t *img, struct opticflow_t *opticf
while (c1 < (int16_t)result->corner_cnt - 1) {
bool exists = false;
for (int16_t i = c1 + 1; i < result->corner_cnt; i++) {
- if (abs((int16_t)opticflow->fast9_ret_corners[c1].x - (int16_t)opticflow->fast9_ret_corners[i].x) < opticflow->fast9_min_distance / 2
- && abs((int16_t)opticflow->fast9_ret_corners[c1].y - (int16_t)opticflow->fast9_ret_corners[i].y) < opticflow->fast9_min_distance / 2) {
+ if (abs((int16_t)opticflow->fast9_ret_corners[c1].x - (int16_t)opticflow->fast9_ret_corners[i].x) <
+ opticflow->fast9_min_distance / 2
+ && abs((int16_t)opticflow->fast9_ret_corners[c1].y - (int16_t)opticflow->fast9_ret_corners[i].y) <
+ opticflow->fast9_min_distance / 2) {
// if too close, replace the corner with the last one in the list:
opticflow->fast9_ret_corners[c1].x = opticflow->fast9_ret_corners[result->corner_cnt - 1].x;
opticflow->fast9_ret_corners[c1].y = opticflow->fast9_ret_corners[result->corner_cnt - 1].y;
@@ -550,7 +619,8 @@ static void manage_flow_features(struct image_t *img, struct opticflow_t *opticf
bool exists = false;
for (uint16_t k = 0; k < result->corner_cnt; k++) {
if (abs((int16_t)new_corners[j].x - (int16_t)opticflow->fast9_ret_corners[k].x) < (int16_t)opticflow->fast9_min_distance
- && abs((int16_t)new_corners[j].y - (int16_t)opticflow->fast9_ret_corners[k].y) < (int16_t)opticflow->fast9_min_distance) {
+ && abs((int16_t)new_corners[j].y - (int16_t)opticflow->fast9_ret_corners[k].y) < (int16_t)
+ opticflow->fast9_min_distance) {
exists = true;
break;
}
@@ -697,8 +767,10 @@ bool calc_edgeflow_tot(struct opticflow_t *opticflow, struct image_t *img,
result->flow_der_y = result->flow_y;
result->corner_cnt = getAmountPeaks(edge_hist_x, 500 , img->w);
result->tracked_cnt = getAmountPeaks(edge_hist_x, 500 , img->w);
- result->divergence = -1.0 * (float)edgeflow.div_x / RES; // Also multiply the divergence with -1.0 to make it on par with the LK algorithm of
- result->div_size = result->divergence; // Fill the div_size with the divergence to atleast get some divergenge measurement when switching from LK to EF
+ result->divergence = -1.0 * (float)edgeflow.div_x /
+ RES; // Also multiply the divergence with -1.0 to make it on par with the LK algorithm of
+ result->div_size =
+ result->divergence; // Fill the div_size with the divergence to atleast get some divergenge measurement when switching from LK to EF
result->surface_roughness = 0.0f;
//......................Calculating VELOCITY ..................... //
diff --git a/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.h b/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.h
index 34569d7032..9dec343a48 100644
--- a/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.h
+++ b/sw/airborne/modules/computer_vision/opticflow/opticflow_calculator.h
@@ -44,6 +44,7 @@ struct opticflow_t {
struct image_t prev_img_gray; ///< Previous gray image frame
uint8_t method; ///< Method to use to calculate the optical flow
+ uint8_t corner_method; ///< Method to use for determining where the corners are
uint16_t window_size; ///< Window size for the blockmatching algorithm (general value for all methods)
uint16_t search_distance; ///< Search distance for blockmatching alg.
bool derotation; ///< Derotation switched on or off (depended on the quality of the gyroscope measurement)
@@ -69,6 +70,13 @@ struct opticflow_t {
bool feature_management; ///< Decides whether to keep track corners in memory for the next frame instead of re-detecting every time
bool fast9_region_detect; ///< Decides whether to detect fast9 corners in specific regions of interest or the whole image (only for feature management)
uint8_t fast9_num_regions; ///< The number of regions of interest the image is split into
+
+ float actfast_long_step; ///< Step size to take when there is no texture
+ float actfast_short_step; ///< Step size to take when there is an edge to be followed
+ int actfast_min_gradient; ///< Threshold that decides when there is sufficient texture for edge following
+ int actfast_gradient_method; ///< Whether to use a simple or Sobel filter
+
+
};