int snake_gate_detection(struct image_t *img, int n_samples, int min_px_size, float min_gate_quality, float gate_thickness, int min_n_sides, uint8_t color_Ym, uint8_t color_YM, uint8_t color_Um, uint8_t color_UM, uint8_t color_Vm, uint8_t color_VM, struct gate_img *best_gate, struct gate_img *gates_c, int *n_gates) { static int last_frame_detection = 0; static int repeat_gate = 0; static struct gate_img previous_best_gate = {0}; static struct gate_img last_gate; bool check_initial_square = true; float iou_threshold = 0.7; // when bigger than this, gates are assumed to represent the same gate (*n_gates) = 0; // For a new image, set the total number of samples to 0: // This number is augmented when checking the color of a pixel. n_total_samples = 0; color_Y_min = color_Ym; color_Y_max = color_YM; color_U_min = color_Um; color_U_max = color_UM; color_V_min = color_Vm; color_V_max = color_VM; min_pixel_size = min_px_size; int x, y; best_quality = 0; best_gate->quality = 0; (*n_gates) = 0; // variables for snake gate detection: int y_low = 0; int x_l = 0; int y_high = 0; int x_h = 0; int x_low1 = 0; int y_l1 = 0; int x_high1 = 0; int y_h1 = 0; int x_low2 = 0; int y_l2 = 0; int x_high2 = 0; int y_h2 = 0; int sz = 0; int szx1 = 0; int szx2 = 0; //for (int i = 0; i < n_samples; i++) { while (n_total_samples < n_samples) { // TODO: would it work better to scan different lines in the image? // get a random coordinate: x = rand() % img->h; y = rand() % img->w; // check if it has the right color if (check_color_snake_gate_detection(img, x, y)) { // fill histogram (TODO: for a next pull request, in which we add the close-by histogram-detection) // histogram[x]++; // snake up and down: snake_up_and_down(img, x, y, &x_l, &y_low, &x_h, &y_high); // This assumes the gate to be square: sz = y_high - y_low; y_low = y_low + (sz * gate_thickness); y_high = y_high - (sz * gate_thickness); y = (y_high + y_low) / 2; // if the found part of the gate is large enough if (sz > min_pixel_size) { // snake left and right, both for the top and bottom part of the gate: snake_left_and_right(img, x_l, y_low, &x_low1, &y_l1, &x_high1, &y_h1); snake_left_and_right(img, x_h, y_high, &x_low2, &y_l2, &x_high2, &y_h2); x_low1 = x_low1 + (sz * gate_thickness); x_high1 = x_high1 - (sz * gate_thickness); x_low2 = x_low2 + (sz * gate_thickness); x_high2 = x_high2 - (sz * gate_thickness); // sizes of the left-right stretches: in y pixel coordinates szx1 = (x_high1 - x_low1); szx2 = (x_high2 - x_low2); // set the size according to the biggest detection: if (szx1 > szx2) { // determine the center x based on the bottom part: x = (x_high1 + x_low1) / 2; // set the size to the largest line found: sz = (sz > szx1) ? sz : szx1; } else { // determine the center x based on the top part: x = (x_high2 + x_low2) / 2; // set the size to the largest line found: sz = (sz > szx2) ? sz : szx2; } if (sz > min_pixel_size) { // create the gate: gates_c[(*n_gates)].x = x; gates_c[(*n_gates)].y = y; // store the half gate size: gates_c[(*n_gates)].sz = sz / 2; if (check_initial_square) { // check the gate quality: check_gate_initial(img, gates_c[(*n_gates)], &gates_c[(*n_gates)].quality, &gates_c[(*n_gates)].n_sides); } else { // The first two corners have a high y: gates_c[(*n_gates)].x_corners[0] = x_low2; gates_c[(*n_gates)].y_corners[0] = y_l2; gates_c[(*n_gates)].x_corners[1] = x_high2; gates_c[(*n_gates)].y_corners[1] = y_h2; // The third and fourth corner have a low y: gates_c[(*n_gates)].x_corners[2] = x_high1; gates_c[(*n_gates)].y_corners[2] = y_h1; gates_c[(*n_gates)].x_corners[3] = x_low1; gates_c[(*n_gates)].y_corners[3] = y_l1; // check the polygon: check_gate_outline(img, gates_c[(*n_gates)], &gates_c[(*n_gates)].quality, &gates_c[(*n_gates)].n_sides); } if (gates_c[(*n_gates)].quality > best_quality) { best_quality = gates_c[(*n_gates)].quality; } // set the corners to make a square gate for now: set_gate_points(&gates_c[(*n_gates)]); // TODO: for multiple gates we need to use intersection of union and fitness here. bool add_gate = true; float iou; for (int g = 0; g < (*n_gates); g++) { iou = intersection_over_union(gates_c[g].x_corners, gates_c[g].y_corners, gates_c[(*n_gates)].x_corners, gates_c[(*n_gates)].y_corners); if (iou > iou_threshold) { // we are looking at an existing gate: add_gate = false; if (gates_c[g].quality > gates_c[(*n_gates)].quality) { // throw the current gate away: break; } else { // throw the old gate away: // TODO: consider making a function for doing this "deep" copy gates_c[g].x = gates_c[(*n_gates)].x; gates_c[g].y = gates_c[(*n_gates)].y; gates_c[g].sz = gates_c[(*n_gates)].sz; gates_c[g].quality = gates_c[(*n_gates)].quality; memcpy(gates_c[g].x_corners, gates_c[(*n_gates)].x_corners, sizeof(int) * 4); memcpy(gates_c[g].y_corners, gates_c[(*n_gates)].y_corners, sizeof(int) * 4); } } } if (add_gate) { (*n_gates)++; } } if ((*n_gates) >= MAX_GATES) { break; } } } } #ifdef DEBUG_SNAKE_GATE // draw all candidates: printf("(*n_gates):%d\n", (*n_gates)); for (int i = 0; i < (*n_gates); i++) { //draw_gate_color_square(img, gates_c[i], white_color); draw_gate_color_polygon(img, gates_c[i], white_color); } #endif //init best gate best_gate->quality = 0; best_gate->n_sides = 0; repeat_gate = 0; // do an additional fit to improve the gate detection: if ((best_quality > min_gate_quality && (*n_gates) > 0) || last_frame_detection) { // go over all remaining gates: for (int gate_nr = 0; gate_nr < (*n_gates); gate_nr++) { // get gate information: gate_refine_corners(img, gates_c[gate_nr].x_corners, gates_c[gate_nr].y_corners, gates_c[gate_nr].sz); // also get the color fitness check_gate_outline(img, gates_c[gate_nr], &gates_c[gate_nr].quality, &gates_c[gate_nr].n_sides); // If the gate is good enough: if (gates_c[gate_nr].n_sides >= min_n_sides && gates_c[gate_nr].quality > best_gate->quality) { // store the information in the gate: best_gate->x = gates_c[gate_nr].x; best_gate->y = gates_c[gate_nr].y; best_gate->sz = gates_c[gate_nr].sz; best_gate->sz_left = gates_c[gate_nr].sz_left; best_gate->sz_right = gates_c[gate_nr].sz_right; best_gate->quality = gates_c[gate_nr].quality; best_gate->n_sides = gates_c[gate_nr].n_sides; memcpy(best_gate->x_corners, gates_c[gate_nr].x_corners, sizeof(best_gate->x_corners)); memcpy(best_gate->y_corners, gates_c[gate_nr].y_corners, sizeof(best_gate->y_corners)); } } // if the best gate is not good enough, but we did have a detection in the previous image: if ((best_gate->quality == 0 && best_gate->n_sides == 0) && last_frame_detection == 1) { // TODO: is it really important to do this sorting here to get the maximum size? Is the sz property not accurate enough? // Or can we not assume the standard arrangement of the corners? int x_values[4]; int y_values[4]; memcpy(x_values, last_gate.x_corners, sizeof(x_values)); memcpy(y_values, last_gate.y_corners, sizeof(y_values)); //sort small to large qsort(x_values, 4, sizeof(int), cmpfunc); qsort(y_values, 4, sizeof(int), cmpfunc); //check x size, maybe use y also later? int radius_p = x_values[3] - x_values[0]; // TODO: is 2*radius_p not huge? gate_refine_corners(img, last_gate.x_corners, last_gate.y_corners, 2 * radius_p); // also get the color fitness check_gate_outline(img, last_gate, &last_gate.quality, &last_gate.n_sides); // if the refined detection is good enough: if (last_gate.n_sides >= min_n_sides && last_gate.quality > best_gate->quality) { repeat_gate = 1; best_gate->quality = last_gate.quality; best_gate->n_sides = last_gate.n_sides; memcpy(best_gate->x_corners, last_gate.x_corners, sizeof(best_gate->x_corners)); memcpy(best_gate->y_corners, last_gate.y_corners, sizeof(best_gate->y_corners)); } } #ifdef DEBUG_SNAKE_GATE // draw the best gate: draw_gate(img, (*best_gate)); #endif } // prepare for the next time: previous_best_gate.x = best_gate->x; previous_best_gate.y = best_gate->y; previous_best_gate.sz = best_gate->sz; previous_best_gate.sz_left = best_gate->sz_left; previous_best_gate.sz_right = best_gate->sz_right; previous_best_gate.quality = best_gate->quality; previous_best_gate.n_sides = best_gate->n_sides; memcpy(previous_best_gate.x_corners, best_gate->x_corners, sizeof(best_gate->x_corners)); memcpy(previous_best_gate.y_corners, best_gate->y_corners, sizeof(best_gate->y_corners)); //color filtered version of image for overlay and debugging if (FILTER_IMAGE) { //filter) { image_yuv422_colorfilt(img, img, color_Y_min, color_Y_max, color_U_min, color_U_max, color_V_min, color_V_max); } if (best_gate->quality > (min_gate_quality * 2) && best_gate->n_sides >= min_n_sides) { // successful detection last_frame_detection = 1; //draw_gate_color(img, best_gate, blue_color); if (DRAW_GATE) { for (int gate_nr = 0; gate_nr < (*n_gates); gate_nr++) { if (gates_c[gate_nr].n_sides >= min_n_sides && gates_c[gate_nr].quality > 2 * min_gate_quality) { // draw the best gate: // draw_gate(img, gates_c[gate_nr]); draw_gate_color_polygon(img, gates_c[gate_nr], green_color); } } int size_crosshair = 10; if (repeat_gate == 0) { draw_gate_color_polygon(img, (*best_gate), blue_color); } else if (repeat_gate == 1) { draw_gate_color_polygon(img, (*best_gate), green_color); for (int i = 0; i < 3; i++) { struct point_t loc = { .x = last_gate.x_corners[i], .y = last_gate.y_corners[i] }; image_draw_crosshair(img, &loc, blue_color, size_crosshair); } } } //save for next iteration memcpy(last_gate.x_corners, best_gate->x_corners, sizeof(best_gate->x_corners)); memcpy(last_gate.y_corners, best_gate->y_corners, sizeof(best_gate->y_corners)); //previous best snake gate last_gate.x = best_gate->x; last_gate.y = best_gate->y; last_gate.sz = best_gate->sz; //SIGNAL NEW DETECTION AVAILABLE return SUCCESS_DETECT; } else {
// Function static struct image_t *detect_gate_func(struct image_t *img) { // detect the gate and draw it in the image: if (just_filtering) { // just color filter the image, so that the user can tune the thresholds: image_yuv422_colorfilt(img, img, color_Ym, color_YM, color_Um, color_UM, color_Vm, color_VM); } else { // perform snake gate detection: int n_gates; snake_gate_detection(img, n_samples, min_px_size, min_gate_quality, gate_thickness, min_n_sides, color_Ym, color_YM, color_Um, color_UM, color_Vm, color_VM, &best_gate, gates_c, &n_gates, exclude_top, exclude_bottom); #if !CAMERA_ROTATED_90DEG_RIGHT int temp[4]; #endif #ifdef DEBUG_GATE printf("\n**** START DEBUG DETECT GATE ****\n"); if (n_gates > 1) { for (int i = 0; i < n_gates; i++) { //printf("Gate %d out of %d\n", i, n_gates-1); if (gates_c[i].quality > min_gate_quality * 2 && gates_c[i].n_sides >= 3) { #if !CAMERA_ROTATED_90DEG_RIGHT // swap x and y coordinates: memcpy(temp, gates_c[i].x_corners, sizeof(gates_c[i].x_corners)); memcpy(gates_c[i].x_corners, gates_c[i].y_corners, sizeof(gates_c[i].x_corners)); memcpy(gates_c[i].y_corners, temp, sizeof(gates_c[i].y_corners)); #endif drone_position = get_world_position_from_image_points(gates_c[i].x_corners, gates_c[i].y_corners, world_corners, n_corners, DETECT_GATE_CAMERA.camera_intrinsics, cam_body); // debugging the drone position: printf("Position drone - gate %d, quality = %f: (%f, %f, %f)\n", i, gates_c[i].quality, drone_position.x, drone_position.y, drone_position.z); } } } #endif #ifdef DEBUG_GATE printf("ratio = %f\n", ratio); #endif if (best_gate.quality > min_gate_quality * 2) { #if !CAMERA_ROTATED_90DEG_RIGHT // swap x and y coordinates: memcpy(temp, best_gate.x_corners, sizeof(best_gate.x_corners)); memcpy(best_gate.x_corners, best_gate.y_corners, sizeof(best_gate.x_corners)); memcpy(best_gate.y_corners, temp, sizeof(best_gate.y_corners)); #endif #ifdef DEBUG_GATE // debugging snake gate: printf("Detected gate: "); for (int i = 0; i < 4; i++) { printf("(%d,%d) ", best_gate.x_corners[i], best_gate.y_corners[i]); } printf("\n"); #endif static bool simple_position = DETECT_GATE_SIMPLIFIED_PNP; if(simple_position) { float sz1_best, sz2_best; sz1_best = (float) (best_gate.x_corners[2] - best_gate.x_corners[0]); sz2_best = (float) (best_gate.y_corners[1] - best_gate.y_corners[0]); float size = (sz1_best > sz2_best) ? sz1_best : sz2_best; //float width, height; #if !CAMERA_ROTATED_90DEG_RIGHT //width = (float) img->w; //height = (float) img->h; float pix_x = (best_gate.x_corners[2] + best_gate.x_corners[0]) / 2.0f; float pix_y = (best_gate.y_corners[1] + best_gate.y_corners[0]) / 2.0f; float angle_x = (pix_x-DETECT_GATE_CAMERA.camera_intrinsics.center_x) / DETECT_GATE_CAMERA.camera_intrinsics.focal_x; float angle_y = (pix_y-DETECT_GATE_CAMERA.camera_intrinsics.center_y) / DETECT_GATE_CAMERA.camera_intrinsics.focal_y; float dist = gate_size_m * (DETECT_GATE_CAMERA.camera_intrinsics.focal_x / size); drone_position.x = -dist; drone_position.y = -angle_y*dist; drone_position.z = angle_x*dist; #else //width = (float) img->h; //height = (float) img->w; float pix_y = (best_gate.x_corners[1] + best_gate.x_corners[0]) / 2.0f; float pix_x = (best_gate.y_corners[2] + best_gate.y_corners[1]) / 2.0f; printf("Not simulating, pix_x = %f, pix_y = %f\n", pix_x, pix_y); float angle_x = (pix_x-DETECT_GATE_CAMERA.camera_intrinsics.center_y) / DETECT_GATE_CAMERA.camera_intrinsics.focal_y; float angle_y = (pix_y-DETECT_GATE_CAMERA.camera_intrinsics.center_x) / DETECT_GATE_CAMERA.camera_intrinsics.focal_x; float dist = gate_size_m * (DETECT_GATE_CAMERA.camera_intrinsics.focal_x / size); drone_position.x = -dist; drone_position.y = -angle_x*dist; drone_position.z = -angle_y*dist; #endif #ifdef DEBUG_GATE printf("angle_x = %f, angle_y = %f, dist = %f\n", angle_x, angle_y, dist); printf("pix_x = %f, pix_y = %f\n", pix_x, pix_y); printf("size = %f, focal = %f, %f, center = %f, %f\n", size, DETECT_GATE_CAMERA.camera_intrinsics.focal_x, DETECT_GATE_CAMERA.camera_intrinsics.focal_y, DETECT_GATE_CAMERA.camera_intrinsics.center_x, DETECT_GATE_CAMERA.camera_intrinsics.center_y); #endif } else { // TODO: try out RANSAC with all combinations of 3 corners out of 4 corners. drone_position = get_world_position_from_image_points(best_gate.x_corners, best_gate.y_corners, world_corners, n_corners, DETECT_GATE_CAMERA.camera_intrinsics, cam_body); } #ifdef DEBUG_GATE // debugging the drone position: printf("Position drone: (%f, %f, %f)\n", drone_position.x, drone_position.y, drone_position.z); printf("**** END DEBUG DETECT GATE ****\n"); #endif // send from thread to module - only when there is a best gate: pthread_mutex_lock(&gate_detect_mutex); detect_gate_x = drone_position.x; detect_gate_y = drone_position.y; detect_gate_z = drone_position.z; //printf("new measurement!!\n"); detect_gate_has_new_data = true; pthread_mutex_unlock(&gate_detect_mutex); } } return img; }