summaryrefslogtreecommitdiffstats
path: root/src/proc/objects.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/proc/objects.cpp')
-rw-r--r--src/proc/objects.cpp550
1 files changed, 550 insertions, 0 deletions
diff --git a/src/proc/objects.cpp b/src/proc/objects.cpp
new file mode 100644
index 0000000..dd00820
--- /dev/null
+++ b/src/proc/objects.cpp
@@ -0,0 +1,550 @@
+/******************************************************************************
+ * Copyright (C) 2018 Alejandro Colomar Andrés *
+ * SPDX-License-Identifier: GPL-2.0-only *
+ ******************************************************************************/
+
+
+/******************************************************************************
+ ******* headers **************************************************************
+ ******************************************************************************/
+#include "vision-artificial/proc/objects.hpp"
+
+#include <cstddef>
+#include <cstdio>
+
+#include <opencv2/opencv.hpp>
+
+#include "libalx/base/math/median.hpp"
+
+#include "vision-artificial/image/iface.hpp"
+#include "vision-artificial/proc/common.hpp"
+#include "vision-artificial/user/iface.hpp"
+
+
+/******************************************************************************
+ ******* macros ***************************************************************
+ ******************************************************************************/
+#define OBJECTS_MAX (0xFFF)
+#define PATTERN_SQUARE_LEN_MM (10.0)
+
+#define PATTERN_DILATE (2)
+
+
+/******************************************************************************
+ ******* enums ****************************************************************
+ ******************************************************************************/
+
+
+/******************************************************************************
+ ******* structs / unions *****************************************************
+ ******************************************************************************/
+struct Point {
+ uint16_t x_pix;
+ uint16_t y_pix;
+
+ double x_mm;
+ double y_mm;
+};
+
+struct Pattern_Square {
+ struct Point pos;
+ uint8_t len;
+};
+
+struct Pattern_Properties {
+ struct Point center;
+ struct Point origin;
+
+ double angle;
+
+ uint16_t height;
+ uint16_t width;
+
+ /* Squares (distance calibration) */
+ struct Pattern_Square square[OBJECTS_MAX];
+};
+
+struct Objects_Properties {
+ struct Point pos;
+
+ double angle;
+
+ double area_pix2;
+ double area_mm2;
+ double perimeter_pix;
+ double perimeter_mm;
+
+ double ratio_p2_a;
+ double area_rect;
+ double ratio_a_arect;
+ double perimeter_rect;
+ double ratio_p_prect;
+};
+
+
+/******************************************************************************
+ ******* variables ************************************************************
+ ******************************************************************************/
+/* Global --------------------------------------------------------------------*/
+/* Static --------------------------------------------------------------------*/
+static class std::vector <class std::vector <class cv::Point_ <int>>> contours;
+static class cv::Mat hierarchy;
+static class cv::RotatedRect rect_rot[OBJECTS_MAX];
+static class cv::Rect_ <int> rect[OBJECTS_MAX];
+static struct Pattern_Properties pattern;
+static ptrdiff_t squares_n;
+static double ratio_mm_pix;
+static ptrdiff_t objects_n;
+static struct Objects_Properties objects[OBJECTS_MAX];
+
+
+/******************************************************************************
+ ******* static functions (prototypes) ****************************************
+ ******************************************************************************/
+static void result_objects (int status);
+
+static void pattern_bgr2gray (void);
+static int pattern_find (void);
+static void pattern_rotation_get (void);
+static void pattern_rotation_fix (void);
+static void pattern_dimensions_get (void);
+static void pattern_dimensions_fix (void);
+static void pattern_squares_find (void);
+static void pattern_squares_pos_get (void);
+static void pattern_squares_len_get (void);
+static void pattern_calib_mm_pix (void);
+
+static void objects_bgr2gray (void);
+static void objects_rotation_fix (void);
+static void objects_dimensions_fix (void);
+static void objects_segment (void);
+static int objects_contours (void);
+static void objects_position_pix (void);
+static void objects_size_pix (void);
+static void objects_shape (void);
+static void objects_position_mm (void);
+static void objects_size_mm (void);
+static void objects_log (void);
+
+
+/******************************************************************************
+ ******* global functions *****************************************************
+ ******************************************************************************/
+int proc_objects_calibrate (void)
+{
+ int status;
+
+ proc_save_mem(0);
+ /* Calibrate angle with the whole pattern */
+ clock_start();
+ pattern_bgr2gray();
+ status = pattern_find();
+ if (status) {
+ result_objects(status);
+ return status;
+ }
+ pattern_rotation_get();
+ pattern_rotation_fix();
+ clock_stop("Calibrate: rotation");
+
+ /* Segmentate pattern squares */
+ clock_start();
+ pattern_dimensions_get();
+ pattern_dimensions_fix();
+ clock_stop("Calibrate: dimensions");
+
+ /* Find squares */
+ clock_start();
+ pattern_squares_find();
+ pattern_squares_pos_get();
+ pattern_squares_len_get();
+ pattern_calib_mm_pix();
+ clock_stop("Calibrate (mm per pix)");
+
+ result_objects(OBJECTS_OK);
+ return 0;
+}
+
+int proc_objects (void)
+{
+ int status;
+
+ proc_save_mem(0);
+ /* Align image */
+ clock_start();
+ objects_bgr2gray();
+ objects_rotation_fix();
+ objects_dimensions_fix();
+ clock_stop("Align image to pattern");
+
+ /* Segment objects */
+ clock_start();
+ objects_segment();
+ clock_stop("Segment objects");
+
+ /* Find objects positions */
+ clock_start();
+ status = objects_contours();
+ if (status) {
+ result_objects(status);
+ return status;
+ }
+ clock_stop("Find objects");
+
+ /* Get objects properties in pixels */
+ clock_start();
+ objects_position_pix();
+ objects_position_mm();
+ clock_stop("Objects positions");
+
+ /* Get objects properties in mm */
+ clock_start();
+ objects_size_pix();
+ objects_size_mm();
+ clock_stop("Objects sizes");
+
+ /* Get objects properties in mm */
+ clock_start();
+ objects_shape();
+ clock_stop("Objects shapes");
+
+ /* Print properties of objects into log */
+ clock_start();
+ objects_log();
+ clock_stop("Objects properties (log)");
+
+ result_objects(OBJECTS_OK);
+ return 0;
+}
+
+
+/******************************************************************************
+ ******* static functions (definitions) ***************************************
+ ******************************************************************************/
+static void result_objects (int status)
+{
+
+ switch (status) {
+ case OBJECTS_OK:
+ user_iface_log_write(0, "Coin: OK");
+ break;
+ case OBJECTS_NOK_OBJECTS:
+ user_iface_log_write(0, "Coin: NOK_OBJECTS");
+ break;
+
+ case OBJECTS_NOK_SIZE:
+ user_iface_log_write(0, "Coin: NOK_SIZE");
+ break;
+ default:
+ user_iface_log_write(0, "Coin: NOK");
+ }
+}
+
+/* calibration ---------------------------------------------------------------*/
+static void pattern_bgr2gray (void)
+{
+
+ proc_load_mem(0);
+
+ proc_cvt_color(cv::COLOR_BGR2GRAY);
+ proc_save_mem(1);
+}
+
+static int pattern_find (void)
+{
+
+ proc_load_mem(1);
+
+ proc_threshold(cv::THRESH_BINARY_INV, IMG_IFACE_THR_OTSU);
+ proc_erode_dilate(16);
+ proc_dilate(2);
+ proc_contours(&contours, &hierarchy);
+ if (contours.size() != 1)
+ return OBJECTS_NOK_PATTERN;
+
+ return 0;
+}
+
+static void pattern_rotation_get (void)
+{
+ char txt[LOG_LINE_LEN];
+
+ proc_min_area_rect(&(contours[0]), &(rect_rot[0]), true);
+
+ /* If angle is < -45º, it is taking into account the incorrect side */
+ if (rect_rot[0].angle < -45.0)
+ rect_rot[0].angle += 90.0;
+ pattern.angle = rect_rot[0].angle;
+ pattern.center.x_pix = rect_rot[0].center.x;
+ pattern.center.y_pix = rect_rot[0].center.y;
+
+ snprintf(txt, LOG_LINE_LEN, "pattern angle = %lf DEG",
+ -pattern.angle);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, "pattern center (x) = %i pix",
+ pattern.center.x_pix);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, "pattern center (y) = %i pix",
+ pattern.center.y_pix);
+ user_iface_log_write(0, txt);
+}
+
+static void pattern_rotation_fix (void)
+{
+
+ proc_load_mem(1);
+
+ /* invert image before rotation to avoid black bands */
+ proc_not();
+ proc_rotate(pattern.center.x_pix, pattern.center.y_pix, pattern.angle);
+ proc_threshold(cv::THRESH_BINARY, IMG_IFACE_THR_OTSU);
+ proc_save_mem(2);
+}
+
+static void pattern_dimensions_get (void)
+{
+
+ proc_load_mem(2);
+
+ proc_erode_dilate(16);
+ proc_dilate(PATTERN_DILATE);
+ proc_contours(&contours, &hierarchy);
+ proc_bounding_rect(&(contours[0]), &(rect[0]), true);
+}
+
+static void pattern_dimensions_fix (void)
+{
+
+ /* Need to correct also pattern dilatation (pattern_dimensions_get()) */
+ pattern.origin.x_pix = rect[0].x;
+ pattern.origin.y_pix = rect[0].y;
+ pattern.width = rect[0].width;
+ pattern.height = rect[0].height;
+
+ proc_load_mem(2);
+ proc_ROI(pattern.origin.x_pix, pattern.origin.y_pix,
+ pattern.width, pattern.height);
+ proc_save_mem(3);
+}
+
+static void pattern_squares_find (void)
+{
+
+ proc_load_mem(3);
+
+ proc_distance_transform();
+ proc_local_max();
+ proc_dilate(6);
+ proc_save_mem(4);
+
+ proc_contours(&contours, &hierarchy);
+ squares_n = contours.size();
+}
+
+static void pattern_squares_pos_get (void)
+{
+
+ for (ptrdiff_t i = 0; i < squares_n; i++) {
+ proc_bounding_rect(&(contours[i]), &(rect[i]), true);
+ pattern.square[i].pos.x_pix = rect[i].x + rect[i].width/2.0;
+ pattern.square[i].pos.y_pix = rect[i].y + rect[i].height/2.0;
+ }
+}
+
+static void pattern_squares_len_get (void)
+{
+
+ proc_load_mem(4);
+ for (ptrdiff_t i = 0; i < squares_n; i++) {
+ proc_pixel_get(pattern.square[i].pos.x_pix,
+ pattern.square[i].pos.y_pix,
+ &(pattern.square[i].len));
+ pattern.square[i].len *= 2;
+ }
+}
+
+static void pattern_calib_mm_pix (void)
+{
+ uint8_t pattern_len[OBJECTS_MAX];
+ uint8_t median_size;
+ char txt[LOG_LINE_LEN];
+
+ for (ptrdiff_t i = 0; i < squares_n; i++)
+ pattern_len[i] = pattern.square[i].len;
+ median_size = alx_median_u8(squares_n, pattern_len);
+ ratio_mm_pix = PATTERN_SQUARE_LEN_MM / median_size;
+
+ snprintf(txt, LOG_LINE_LEN, "mm/pix = %lf", ratio_mm_pix);
+ user_iface_log_write(0, txt);
+}
+
+/* process -------------------------------------------------------------------*/
+static void objects_bgr2gray (void)
+{
+
+ proc_load_mem(0);
+
+ proc_cvt_color(cv::COLOR_BGR2GRAY);
+ proc_save_mem(1);
+}
+
+static void objects_rotation_fix (void)
+{
+
+ proc_load_mem(1);
+
+ /* invert image before rotation to avoid black bands */
+ proc_not();
+ proc_rotate(pattern.center.x_pix, pattern.center.y_pix, pattern.angle);
+ proc_save_mem(2);
+}
+
+static void objects_dimensions_fix (void)
+{
+
+ /* Need to correct also pattern dilatation (pattern_dimensions_get()) */
+ proc_load_mem(2);
+
+ proc_ROI(pattern.origin.x_pix, pattern.origin.y_pix,
+ pattern.width, pattern.height);
+ proc_threshold(cv::THRESH_BINARY, IMG_IFACE_THR_OTSU);
+ proc_save_mem(3);
+}
+
+static void objects_segment (void)
+{
+
+ proc_load_mem(3);
+
+ proc_dilate_erode(3);
+ proc_erode_dilate(3);
+ proc_save_mem(4);
+
+ proc_distance_transform();
+ proc_threshold(cv::THRESH_BINARY_INV, 30);
+ proc_distance_transform();
+ proc_skeleton();
+ proc_threshold(cv::THRESH_BINARY_INV, 10);
+ proc_save_ref();
+ proc_save_mem(5);
+
+ proc_load_mem(3);
+ proc_and_2ref();
+ proc_distance_transform();
+ proc_threshold(cv::THRESH_BINARY, 10);
+ proc_save_mem(6);
+}
+
+static int objects_contours (void)
+{
+
+ proc_load_mem(6);
+
+ proc_contours(&contours, &hierarchy);
+ objects_n = contours.size();
+ if (!objects_n)
+ return OBJECTS_NOK_OBJECTS;
+
+ return 0;
+}
+
+static void objects_position_pix (void)
+{
+
+ for (ptrdiff_t i = 0; i < objects_n; i++) {
+ proc_fit_ellipse(&(contours[i]), &(rect_rot[i]), true);
+ objects[i].pos.x_pix = rect_rot[i].center.x;
+ objects[i].pos.y_pix = rect_rot[i].center.y;
+ objects[i].angle = -rect_rot[i].angle + 90.0;
+ if (objects[i].angle < 0)
+ objects[i].angle += 180.0;
+ }
+}
+
+static void objects_position_mm (void)
+{
+
+ for (ptrdiff_t i = 0; i < objects_n; i++) {
+ objects[i].pos.x_mm = ratio_mm_pix * objects[i].pos.x_pix;
+ objects[i].pos.y_mm = ratio_mm_pix * objects[i].pos.y_pix;
+ }
+}
+
+static void objects_size_pix (void)
+{
+ double area[OBJECTS_MAX];
+ double perimeter[OBJECTS_MAX];
+
+ proc_contours_size(&contours, area, perimeter);
+ for (ptrdiff_t i = 0; i < objects_n; i++) {
+ objects[i].area_pix2 = area[i];
+ objects[i].perimeter_pix = perimeter[i];
+ }
+}
+
+static void objects_size_mm (void)
+{
+
+ for (ptrdiff_t i = 0; i < objects_n; i++) {
+ objects[i].area_mm2 = pow(ratio_mm_pix, 2) *
+ objects[i].area_pix2;
+ objects[i].perimeter_mm = ratio_mm_pix *
+ objects[i].perimeter_pix;
+ }
+}
+
+static void objects_shape (void)
+{
+
+ for (ptrdiff_t i = 0; i < objects_n; i++) {
+ proc_min_area_rect(&(contours[i]), &(rect_rot[i]), true);
+
+ objects[i].ratio_p2_a = pow(objects[i].perimeter_pix, 2) /
+ objects[i].area_pix2;
+ objects[i].area_rect = rect_rot[i].size.width *
+ rect_rot[i].size.height;
+ objects[i].perimeter_rect = 2.0 * (rect_rot[i].size.width +
+ rect_rot[i].size.height);
+ objects[i].ratio_p_prect = objects[i].perimeter_pix /
+ objects[i].perimeter_rect;
+ objects[i].ratio_a_arect = objects[i].area_pix2 /
+ objects[i].area_rect;
+ }
+}
+
+static void objects_log (void)
+{
+ char txt[LOG_LINE_LEN];
+
+ for (ptrdiff_t i = 0; i < objects_n; i++) {
+ snprintf(txt, LOG_LINE_LEN, "Object[%ti]:", i);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, " pos: (%.1lf, %.1lf) mm",
+ objects[i].pos.x_mm,
+ objects[i].pos.y_mm);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, " ang = %.1lf DEG",
+ objects[i].angle);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, " A = %.1lf mm2",
+ objects[i].area_mm2);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, " P = %.1lf mm",
+ objects[i].perimeter_mm);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, " p2/A = %.2lf",
+ objects[i].ratio_p2_a);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, " p/p_r = %.2lf",
+ objects[i].ratio_p_prect);
+ user_iface_log_write(0, txt);
+ snprintf(txt, LOG_LINE_LEN, " A/A_r = %.2lf",
+ objects[i].ratio_a_arect);
+ user_iface_log_write(0, txt);
+ }
+}
+
+
+/******************************************************************************
+ ******* end of file **********************************************************
+ ******************************************************************************/