毕业设计底层

#include “imlib.h”

typedef struct xylr
{
int16_t x, y, l, r, t_l, b_l;
}
xylr_t;

static float sign(float x)
{
return x / fabsf(x);
}

static int sum_m_to_n(int m, int n)
{
return ((n * (n + 1)) - (m * (m - 1))) / 2;
}

static int sum_2_m_to_n(int m, int n)
{
return ((n * (n + 1) * ((2 * n) + 1)) - (m * (m - 1) * ((2 * m) - 1))) / 6;
}

static int cumulative_moving_average(int avg, int x, int n)
{
return (x + (n * avg)) / (n + 1);
}

static void bin_up(uint16_t *hist, uint16_t size, unsigned int max_size, uint16_t **new_hist, uint16_t *new_size)
{
int start = -1;

for (int i = 0; i < size; i++) {
    if (hist[i]) {
        start = i;
        break;
    }
}

if (start != -1) {
    int end = start;

    for (int i = start + 1; i < size; i++) {
        if (!hist[i]) {
            break;
        }
        end = i;
    }

    int bin_count = end - start + 1; // >= 1
    *new_size = IM_MIN(max_size, bin_count);
    *new_hist = xalloc0((*new_size) * sizeof(uint16_t));
    float div_value = (*new_size) / ((float) bin_count); // Reversed so we can multiply below.

    for (int i = 0; i < bin_count; i++) {
        (*new_hist)[fast_floorf(i*div_value)] += hist[start+i];
    }
}

}

static void merge_bins(int b_dst_start, int b_dst_end, uint16_t **b_dst_hist, uint16_t *b_dst_hist_len,
int b_src_start, int b_src_end, uint16_t **b_src_hist, uint16_t *b_src_hist_len,
unsigned int max_size)
{
int start = IM_MIN(b_dst_start, b_src_start);
int end = IM_MAX(b_dst_end, b_src_end);

int bin_count = end - start + 1; // >= 1
uint16_t new_size = IM_MIN(max_size, bin_count);
uint16_t *new_hist = xalloc0(new_size * sizeof(uint16_t));
float div_value = new_size / ((float) bin_count); // Reversed so we can multiply below.

int b_dst_bin_count = b_dst_end - b_dst_start + 1; // >= 1
uint16_t b_dst_new_size = IM_MIN((*b_dst_hist_len), b_dst_bin_count);
float b_dst_div_value = b_dst_new_size / ((float) b_dst_bin_count); // Reversed so we can multiply below.

int b_src_bin_count = b_src_end - b_src_start + 1; // >= 1
uint16_t b_src_new_size = IM_MIN((*b_src_hist_len), b_src_bin_count);
float b_src_div_value = b_src_new_size / ((float) b_src_bin_count); // Reversed so we can multiply below.

for(int i = 0; i < bin_count; i++) {
    if ((b_dst_start <= (i + start)) && ((i + start) <= b_dst_end)) {
        int index = fast_floorf((i + start - b_dst_start) * b_dst_div_value);
        new_hist[fast_floorf(i*div_value)] += (*b_dst_hist)[index];
        (*b_dst_hist)[index] = 0; // prevent from adding again...
    }
    if ((b_src_start <= (i + start)) && ((i + start) <= b_src_end)) {
        int index = fast_floorf((i + start - b_src_start) * b_src_div_value);
        new_hist[fast_floorf(i*div_value)] += (*b_src_hist)[index];
        (*b_src_hist)[index] = 0; // prevent from adding again...
    }
}

xfree(*b_dst_hist);
xfree(*b_src_hist);

*b_dst_hist_len = new_size;
(*b_dst_hist) = new_hist;
*b_src_hist_len = 0;
(*b_src_hist) = NULL;

}

static float calc_roundness(float blob_a, float blob_b, float blob_c)
{
float roundness_div = fast_sqrtf((blob_b * blob_b) + ((blob_a - blob_c) * (blob_a - blob_c)));
float roundness_sin = IM_DIV(blob_b, roundness_div);
float roundness_cos = IM_DIV(blob_a - blob_c, roundness_div);
float roundness_add = (blob_a + blob_c) / 2;
float roundness_cos_mul = (blob_a - blob_c) / 2;
float roundness_sin_mul = blob_b / 2;

float roundness_0 = roundness_add + (roundness_cos * roundness_cos_mul) + (roundness_sin * roundness_sin_mul);
float roundness_1 = roundness_add + (roundness_cos * roundness_cos_mul) - (roundness_sin * roundness_sin_mul);
float roundness_2 = roundness_add - (roundness_cos * roundness_cos_mul) + (roundness_sin * roundness_sin_mul);
float roundness_3 = roundness_add - (roundness_cos * roundness_cos_mul) - (roundness_sin * roundness_sin_mul);

float roundness_max = IM_MAX(roundness_0, IM_MAX(roundness_1, IM_MAX(roundness_2, roundness_3)));
float roundness_min = IM_MIN(roundness_0, IM_MIN(roundness_1, IM_MIN(roundness_2, roundness_3)));

return IM_DIV(roundness_min, roundness_max);

}

void imlib_find_blobs(list_t *out, image_t *ptr, rectangle_t roi, unsigned int x_stride, unsigned int y_stride,
list_t thresholds, bool invert, unsigned int area_threshold, unsigned int pixels_threshold,
bool merge, int margin,
bool (threshold_cb)(void,find_blobs_list_lnk_data_t
), void threshold_cb_arg,
bool (merge_cb)(void,find_blobs_list_lnk_data_t
,find_blobs_list_lnk_data_t
), void *merge_cb_arg,
unsigned int x_hist_bins_max, unsigned int y_hist_bins_max)
{
// Same size as the image so we don’t have to translate.
image_t bmp;
bmp.w = ptr->w;
bmp.h = ptr->h;
bmp.bpp = IMAGE_BPP_BINARY;
bmp.data = fb_alloc0(image_size(&bmp));

uint16_t *x_hist_bins = NULL;
if (x_hist_bins_max) x_hist_bins = fb_alloc(ptr->w * sizeof(uint16_t));

uint16_t *y_hist_bins = NULL;
if (y_hist_bins_max) y_hist_bins = fb_alloc(ptr->h * sizeof(uint16_t));

lifo_t lifo;
size_t lifo_len;
lifo_alloc_all(&lifo, &lifo_len, sizeof(xylr_t));

list_init(out, sizeof(find_blobs_list_lnk_data_t));

size_t code = 0;
for (list_lnk_t *it = iterator_start_from_head(thresholds); it; it = iterator_next(it)) {
    color_thresholds_list_lnk_data_t lnk_data;
    iterator_get(thresholds, it, &lnk_data);

    switch(ptr->bpp) {
        case IMAGE_BPP_BINARY: {
            for (int y = roi->y, yy = roi->y + roi->h, y_max = yy - 1; y < yy; y += y_stride) {
                uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(ptr, y);
                uint32_t *bmp_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y);
                for (int x = roi->x + (y % x_stride), xx = roi->x + roi->w, x_max = xx - 1; x < xx; x += x_stride) {
                    if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row_ptr, x))
                    && COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x), &lnk_data, invert)) {
                        int old_x = x;
                        int old_y = y;

                        float corners_acc[FIND_BLOBS_CORNERS_RESOLUTION];
                        point_t corners[FIND_BLOBS_CORNERS_RESOLUTION];
                        int corners_n[FIND_BLOBS_CORNERS_RESOLUTION];
                        // These values are initialized to their maximum before we minimize.
                        for (int i = 0; i < FIND_BLOBS_CORNERS_RESOLUTION; i++) {
                            corners[i].x = IM_MAX(IM_MIN(x_max * sign(cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]), x_max), 0);
                            corners[i].y = IM_MAX(IM_MIN(y_max * sign(sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]), y_max), 0);
                            corners_acc[i] = (corners[i].x * cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]) +
                                             (corners[i].y * sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]);
                            corners_n[i] = 1;
                        }

                        int blob_pixels = 0;
                        int blob_perimeter = 0;
                        int blob_cx = 0;
                        int blob_cy = 0;
                        long long blob_a = 0;
                        long long blob_b = 0;
                        long long blob_c = 0;

                        if (x_hist_bins) memset(x_hist_bins, 0, ptr->w * sizeof(uint16_t));
                        if (y_hist_bins) memset(y_hist_bins, 0, ptr->h * sizeof(uint16_t));

                        // Scanline Flood Fill Algorithm //

                        for(;;) {
                            int left = x, right = x;
                            uint32_t *row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(ptr, y);
                            uint32_t *bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y);

                            while ((left > roi->x)
                            && (!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, left - 1))
                            && COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, left - 1), &lnk_data, invert)) {
                                left--;
                            }

                            while ((right < (roi->x + roi->w - 1))
                            && (!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, right + 1))
                            && COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, right + 1), &lnk_data, invert)) {
                                right++;
                            }

                            for (int i = left; i <= right; i++) {
                                IMAGE_SET_BINARY_PIXEL_FAST(bmp_row, i);
                            }

                            int sum = sum_m_to_n(left, right);
                            int sum_2 = sum_2_m_to_n(left, right);
                            int cnt = right - left + 1;
                            int avg = sum / cnt;

                            for (int i = 0; i < FIND_BLOBS_CORNERS_RESOLUTION; i++) {
                                int x_new = (cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i] > 0) ? left :
                                            ((cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i] == 0) ? avg :
                                                                                              right);
                                float z = (x_new * cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]) +
                                          (y * sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]);
                                if (z < corners_acc[i]) {
                                    corners_acc[i] = z;
                                    corners[i].x = x_new;
                                    corners[i].y = y;
                                    corners_n[i] = 1;
                                } else if (z == corners_acc[i]) {
                                    corners[i].x = cumulative_moving_average(corners[i].x, x_new, corners_n[i]);
                                    corners[i].y = cumulative_moving_average(corners[i].y, y, corners_n[i]);
                                    corners_n[i] += 1;
                                }
                            }

                            blob_pixels += cnt;
                            blob_perimeter += 2;
                            blob_cx += sum;
                            blob_cy += y * cnt;
                            blob_a += sum_2;
                            blob_b += y * sum;
                            blob_c += y * y * cnt;

                            if (y_hist_bins) y_hist_bins[y] += cnt;
                            if (x_hist_bins) for (int i = left; i <= right; i++) x_hist_bins[i] += 1;

                            int top_left = left;
                            int bot_left = left;
                            bool break_out = false;
                            for(;;) {
                                if (lifo_size(&lifo) < lifo_len) {

                                    if (y > roi->y) {
                                        row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(ptr, y - 1);
                                        bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y - 1);

                                        bool recurse = false;
                                        for (int i = top_left; i <= right; i++) {
                                            bool ok = true; // Does nothing if thresholding is skipped.

                                            if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, i))
                                            && (ok = COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, i), &lnk_data, invert))) {
                                                xylr_t context;
                                                context.x = x;
                                                context.y = y;
                                                context.l = left;
                                                context.r = right;
                                                context.t_l = i + 1; // Don't test the same pixel again...
                                                context.b_l = bot_left;
                                                lifo_enqueue(&lifo, &context);
                                                x = i;
                                                y = y - 1;
                                                recurse = true;
                                                break;
                                            }

                                            blob_perimeter += (!ok) && (i != left) && (i != right);
                                        }
                                        if (recurse) {
                                            break;
                                        }
                                    } else {
                                        blob_perimeter += right - left + 1;
                                    }

                                    if (y < (roi->y + roi->h - 1)) {
                                        row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(ptr, y + 1);
                                        bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y + 1);

                                        bool recurse = false;
                                        for (int i = bot_left; i <= right; i++) {
                                            bool ok = true; // Does nothing if thresholding is skipped.

                                            if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, i))
                                            && (ok = COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, i), &lnk_data, invert))) {
                                                xylr_t context;
                                                context.x = x;
                                                context.y = y;
                                                context.l = left;
                                                context.r = right;
                                                context.t_l = top_left;
                                                context.b_l = i + 1; // Don't test the same pixel again...
                                                lifo_enqueue(&lifo, &context);
                                                x = i;
                                                y = y + 1;
                                                recurse = true;
                                                break;
                                            }

                                            blob_perimeter += (!ok) && (i != left) && (i != right);
                                        }
                                        if (recurse) {
                                            break;
                                        }
                                    } else {
                                        blob_perimeter += right - left + 1;
                                    }
                                } else {
                                    blob_perimeter += (right - left + 1) * 2;
                                }

                                if (!lifo_size(&lifo)) {
                                    break_out = true;
                                    break;
                                }

                                xylr_t context;
                                lifo_dequeue(&lifo, &context);
                                x = context.x;
                                y = context.y;
                                left = context.l;
                                right = context.r;
                                top_left = context.t_l;
                                bot_left = context.b_l;
                            }

                            if (break_out) {
                                break;
                            }
                        }

                        rectangle_t rect;
                        rect.x = corners[(FIND_BLOBS_CORNERS_RESOLUTION*0)/4].x; // l
                        rect.y = corners[(FIND_BLOBS_CORNERS_RESOLUTION*1)/4].y; // t
                        rect.w = corners[(FIND_BLOBS_CORNERS_RESOLUTION*2)/4].x - corners[(FIND_BLOBS_CORNERS_RESOLUTION*0)/4].x + 1; // r - l + 1
                        rect.h = corners[(FIND_BLOBS_CORNERS_RESOLUTION*3)/4].y - corners[(FIND_BLOBS_CORNERS_RESOLUTION*1)/4].y + 1; // b - t + 1

                        if (((rect.w * rect.h) >= area_threshold) && (blob_pixels >= pixels_threshold)) {

                            // http://www.cse.usf.edu/~r1k/MachineVisionBook/MachineVision.files/MachineVision_Chapter2.pdf
                            // https://www.strchr.com/standard_deviation_in_one_pass
                            //
                            // a = sigma(x*x) + (mx*sigma(x)) + (mx*sigma(x)) + (sigma()*mx*mx)
                            // b = sigma(x*y) + (mx*sigma(y)) + (my*sigma(x)) + (sigma()*mx*my)
                            // c = sigma(y*y) + (my*sigma(y)) + (my*sigma(y)) + (sigma()*my*my)
                            //
                            // blob_a = sigma(x*x)
                            // blob_b = sigma(x*y)
                            // blob_c = sigma(y*y)
                            // blob_cx = sigma(x)
                            // blob_cy = sigma(y)
                            // blob_pixels = sigma()

                            float b_mx = blob_cx / ((float) blob_pixels);
                            float b_my = blob_cy / ((float) blob_pixels);
                            int mx = fast_roundf(b_mx); // x centroid
                            int my = fast_roundf(b_my); // y centroid
                            int small_blob_a = blob_a - ((mx * blob_cx) + (mx * blob_cx)) + (blob_pixels * mx * mx);
                            int small_blob_b = blob_b - ((mx * blob_cy) + (my * blob_cx)) + (blob_pixels * mx * my);
                            int small_blob_c = blob_c - ((my * blob_cy) + (my * blob_cy)) + (blob_pixels * my * my);

                            find_blobs_list_lnk_data_t lnk_blob;
                            memcpy(lnk_blob.corners, corners, FIND_BLOBS_CORNERS_RESOLUTION * sizeof(point_t));
                            memcpy(&lnk_blob.rect, &rect, sizeof(rectangle_t));
                            lnk_blob.pixels = blob_pixels;
                            lnk_blob.perimeter = blob_perimeter;
                            lnk_blob.code = 1 << code;
                            lnk_blob.count = 1;
                            lnk_blob.centroid_x = b_mx;
                            lnk_blob.centroid_y = b_my;
                            lnk_blob.rotation = (small_blob_a != small_blob_c) ? (fast_atan2f(2 * small_blob_b, small_blob_a - small_blob_c) / 2.0f) : 0.0f;
                            lnk_blob.roundness = calc_roundness(small_blob_a, small_blob_b, small_blob_c);
                            lnk_blob.x_hist_bins_count = 0;
                            lnk_blob.x_hist_bins = NULL;
                            lnk_blob.y_hist_bins_count = 0;
                            lnk_blob.y_hist_bins = NULL;
                            // These store the current average accumulation.
                            lnk_blob.centroid_x_acc = lnk_blob.centroid_x * lnk_blob.pixels;
                            lnk_blob.centroid_y_acc = lnk_blob.centroid_y * lnk_blob.pixels;
                            lnk_blob.rotation_acc_x = cosf(lnk_blob.rotation) * lnk_blob.pixels;
                            lnk_blob.rotation_acc_y = sinf(lnk_blob.rotation) * lnk_blob.pixels;
                            lnk_blob.roundness_acc = lnk_blob.roundness * lnk_blob.pixels;

                            if (x_hist_bins) {
                                bin_up(x_hist_bins, ptr->w, x_hist_bins_max, &lnk_blob.x_hist_bins, &lnk_blob.x_hist_bins_count);
                            }

                            if (y_hist_bins) {
                                bin_up(y_hist_bins, ptr->h, y_hist_bins_max, &lnk_blob.y_hist_bins, &lnk_blob.y_hist_bins_count);
                            }

                            if (((threshold_cb_arg == NULL) || threshold_cb(threshold_cb_arg, &lnk_blob))) {
                                list_push_back(out, &lnk_blob);
                            } else {
                                if (lnk_blob.x_hist_bins) xfree(lnk_blob.x_hist_bins);
                                if (lnk_blob.y_hist_bins) xfree(lnk_blob.y_hist_bins);
                            }
                        }

                        x = old_x;
                        y = old_y;
                    }
                }
            }
            break;
        }
        case IMAGE_BPP_GRAYSCALE: {
            for (int y = roi->y, yy = roi->y + roi->h, y_max = yy - 1; y < yy; y += y_stride) {
                uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y);
                uint32_t *bmp_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y);
                for (int x = roi->x + (y % x_stride), xx = roi->x + roi->w, x_max = xx - 1; x < xx; x += x_stride) {
                    if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row_ptr, x))
                    && COLOR_THRESHOLD_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x), &lnk_data, invert)) {
                        int old_x = x;
                        int old_y = y;

                        float corners_acc[FIND_BLOBS_CORNERS_RESOLUTION];
                        point_t corners[FIND_BLOBS_CORNERS_RESOLUTION];
                        int corners_n[FIND_BLOBS_CORNERS_RESOLUTION];
                        // These values are initialized to their maximum before we minimize.
                        for (int i = 0; i < FIND_BLOBS_CORNERS_RESOLUTION; i++) {
                            corners[i].x = IM_MAX(IM_MIN(x_max * sign(cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]), x_max), 0);
                            corners[i].y = IM_MAX(IM_MIN(y_max * sign(sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]), y_max), 0);
                            corners_acc[i] = (corners[i].x * cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]) +
                                             (corners[i].y * sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]);
                            corners_n[i] = 1;
                        }

                        int blob_pixels = 0;
                        int blob_perimeter = 0;
                        int blob_cx = 0;
                        int blob_cy = 0;
                        long long blob_a = 0;
                        long long blob_b = 0;
                        long long blob_c = 0;

                        if (x_hist_bins) memset(x_hist_bins, 0, ptr->w * sizeof(uint16_t));
                        if (y_hist_bins) memset(y_hist_bins, 0, ptr->h * sizeof(uint16_t));

                        // Scanline Flood Fill Algorithm //

                        for(;;) {
                            int left = x, right = x;
                            uint8_t *row = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y);
                            uint32_t *bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y);

                            while ((left > roi->x)
                            && (!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, left - 1))
                            && COLOR_THRESHOLD_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, left - 1), &lnk_data, invert)) {
                                left--;
                            }

                            while ((right < (roi->x + roi->w - 1))
                            && (!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, right + 1))
                            && COLOR_THRESHOLD_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, right + 1), &lnk_data, invert)) {
                                right++;
                            }

                            for (int i = left; i <= right; i++) {
                                IMAGE_SET_BINARY_PIXEL_FAST(bmp_row, i);
                            }

                            int sum = sum_m_to_n(left, right);
                            int sum_2 = sum_2_m_to_n(left, right);
                            int cnt = right - left + 1;
                            int avg = sum / cnt;

                            for (int i = 0; i < FIND_BLOBS_CORNERS_RESOLUTION; i++) {
                                int x_new = (cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i] > 0) ? left :
                                            ((cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i] == 0) ? avg :
                                                                                              right);
                                float z = (x_new * cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]) +
                                          (y * sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]);
                                if (z < corners_acc[i]) {
                                    corners_acc[i] = z;
                                    corners[i].x = x_new;
                                    corners[i].y = y;
                                    corners_n[i] = 1;
                                } else if (z == corners_acc[i]) {
                                    corners[i].x = cumulative_moving_average(corners[i].x, x_new, corners_n[i]);
                                    corners[i].y = cumulative_moving_average(corners[i].y, y, corners_n[i]);
                                    corners_n[i] += 1;
                                }
                            }

                            blob_pixels += cnt;
                            blob_perimeter += 2;
                            blob_cx += sum;
                            blob_cy += y * cnt;
                            blob_a += sum_2;
                            blob_b += y * sum;
                            blob_c += y * y * cnt;

                            if (y_hist_bins) y_hist_bins[y] += cnt;
                            if (x_hist_bins) for (int i = left; i <= right; i++) x_hist_bins[i] += 1;

                            int top_left = left;
                            int bot_left = left;
                            bool break_out = false;
                            for(;;) {
                                if (lifo_size(&lifo) < lifo_len) {

                                    if (y > roi->y) {
                                        row = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y - 1);
                                        bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y - 1);

                                        bool recurse = false;
                                        for (int i = top_left; i <= right; i++) {
                                            bool ok = true; // Does nothing if thresholding is skipped.

                                            if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, i))
                                            && (ok = COLOR_THRESHOLD_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, i), &lnk_data, invert))) {
                                                xylr_t context;
                                                context.x = x;
                                                context.y = y;
                                                context.l = left;
                                                context.r = right;
                                                context.t_l = i + 1; // Don't test the same pixel again...
                                                context.b_l = bot_left;
                                                lifo_enqueue(&lifo, &context);
                                                x = i;
                                                y = y - 1;
                                                recurse = true;
                                                break;
                                            }

                                            blob_perimeter += (!ok) && (i != left) && (i != right);
                                        }
                                        if (recurse) {
                                            break;
                                        }
                                    } else {
                                        blob_perimeter += right - left + 1;
                                    }

                                    if (y < (roi->y + roi->h - 1)) {
                                        row = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y + 1);
                                        bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y + 1);

                                        bool recurse = false;
                                        for (int i = bot_left; i <= right; i++) {
                                            bool ok = true; // Does nothing if thresholding is skipped.

                                            if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, i))
                                            && (ok = COLOR_THRESHOLD_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, i), &lnk_data, invert))) {
                                                xylr_t context;
                                                context.x = x;
                                                context.y = y;
                                                context.l = left;
                                                context.r = right;
                                                context.t_l = top_left;
                                                context.b_l = i + 1; // Don't test the same pixel again...
                                                lifo_enqueue(&lifo, &context);
                                                x = i;
                                                y = y + 1;
                                                recurse = true;
                                                break;
                                            }

                                            blob_perimeter += (!ok) && (i != left) && (i != right);
                                        }
                                        if (recurse) {
                                            break;
                                        }
                                    } else {
                                        blob_perimeter += right - left + 1;
                                    }
                                } else {
                                    blob_perimeter += (right - left + 1) * 2;
                                }

                                if (!lifo_size(&lifo)) {
                                    break_out = true;
                                    break;
                                }

                                xylr_t context;
                                lifo_dequeue(&lifo, &context);
                                x = context.x;
                                y = context.y;
                                left = context.l;
                                right = context.r;
                                top_left = context.t_l;
                                bot_left = context.b_l;
                            }

                            if (break_out) {
                                break;
                            }
                        }

                        rectangle_t rect;
                        rect.x = corners[(FIND_BLOBS_CORNERS_RESOLUTION*0)/4].x; // l
                        rect.y = corners[(FIND_BLOBS_CORNERS_RESOLUTION*1)/4].y; // t
                        rect.w = corners[(FIND_BLOBS_CORNERS_RESOLUTION*2)/4].x - corners[(FIND_BLOBS_CORNERS_RESOLUTION*0)/4].x + 1; // r - l + 1
                        rect.h = corners[(FIND_BLOBS_CORNERS_RESOLUTION*3)/4].y - corners[(FIND_BLOBS_CORNERS_RESOLUTION*1)/4].y + 1; // b - t + 1

                        if (((rect.w * rect.h) >= area_threshold) && (blob_pixels >= pixels_threshold)) {

                            // http://www.cse.usf.edu/~r1k/MachineVisionBook/MachineVision.files/MachineVision_Chapter2.pdf
                            // https://www.strchr.com/standard_deviation_in_one_pass
                            //
                            // a = sigma(x*x) + (mx*sigma(x)) + (mx*sigma(x)) + (sigma()*mx*mx)
                            // b = sigma(x*y) + (mx*sigma(y)) + (my*sigma(x)) + (sigma()*mx*my)
                            // c = sigma(y*y) + (my*sigma(y)) + (my*sigma(y)) + (sigma()*my*my)
                            //
                            // blob_a = sigma(x*x)
                            // blob_b = sigma(x*y)
                            // blob_c = sigma(y*y)
                            // blob_cx = sigma(x)
                            // blob_cy = sigma(y)
                            // blob_pixels = sigma()

                            float b_mx = blob_cx / ((float) blob_pixels);
                            float b_my = blob_cy / ((float) blob_pixels);
                            int mx = fast_roundf(b_mx); // x centroid
                            int my = fast_roundf(b_my); // y centroid
                            int small_blob_a = blob_a - ((mx * blob_cx) + (mx * blob_cx)) + (blob_pixels * mx * mx);
                            int small_blob_b = blob_b - ((mx * blob_cy) + (my * blob_cx)) + (blob_pixels * mx * my);
                            int small_blob_c = blob_c - ((my * blob_cy) + (my * blob_cy)) + (blob_pixels * my * my);

                            find_blobs_list_lnk_data_t lnk_blob;
                            memcpy(lnk_blob.corners, corners, FIND_BLOBS_CORNERS_RESOLUTION * sizeof(point_t));
                            memcpy(&lnk_blob.rect, &rect, sizeof(rectangle_t));
                            lnk_blob.pixels = blob_pixels;
                            lnk_blob.perimeter = blob_perimeter;
                            lnk_blob.code = 1 << code;
                            lnk_blob.count = 1;
                            lnk_blob.centroid_x = b_mx;
                            lnk_blob.centroid_y = b_my;
                            lnk_blob.rotation = (small_blob_a != small_blob_c) ? (fast_atan2f(2 * small_blob_b, small_blob_a - small_blob_c) / 2.0f) : 0.0f;
                            lnk_blob.roundness = calc_roundness(small_blob_a, small_blob_b, small_blob_c);
                            lnk_blob.x_hist_bins_count = 0;
                            lnk_blob.x_hist_bins = NULL;
                            lnk_blob.y_hist_bins_count = 0;
                            lnk_blob.y_hist_bins = NULL;
                            // These store the current average accumulation.
                            lnk_blob.centroid_x_acc = lnk_blob.centroid_x * lnk_blob.pixels;
                            lnk_blob.centroid_y_acc = lnk_blob.centroid_y * lnk_blob.pixels;
                            lnk_blob.rotation_acc_x = cosf(lnk_blob.rotation) * lnk_blob.pixels;
                            lnk_blob.rotation_acc_y = sinf(lnk_blob.rotation) * lnk_blob.pixels;
                            lnk_blob.roundness_acc = lnk_blob.roundness * lnk_blob.pixels;

                            if (x_hist_bins) {
                                bin_up(x_hist_bins, ptr->w, x_hist_bins_max, &lnk_blob.x_hist_bins, &lnk_blob.x_hist_bins_count);
                            }

                            if (y_hist_bins) {
                                bin_up(y_hist_bins, ptr->h, y_hist_bins_max, &lnk_blob.y_hist_bins, &lnk_blob.y_hist_bins_count);
                            }

                            if (((threshold_cb_arg == NULL) || threshold_cb(threshold_cb_arg, &lnk_blob))) {
                                list_push_back(out, &lnk_blob);
                            } else {
                                if (lnk_blob.x_hist_bins) xfree(lnk_blob.x_hist_bins);
                                if (lnk_blob.y_hist_bins) xfree(lnk_blob.y_hist_bins);
                            }
                        }

                        x = old_x;
                        y = old_y;
                    }
                }
            }
            break;
        }
        case IMAGE_BPP_RGB565: {
            for (int y = roi->y, yy = roi->y + roi->h, y_max = yy - 1; y < yy; y += y_stride) {
                uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y);
                uint32_t *bmp_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y);
                for (int x = roi->x + (y % x_stride), xx = roi->x + roi->w, x_max = xx - 1; x < xx; x += x_stride) {
                    if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row_ptr, x))
                    && COLOR_THRESHOLD_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x), &lnk_data, invert)) {
                        int old_x = x;
                        int old_y = y;

                        float corners_acc[FIND_BLOBS_CORNERS_RESOLUTION];
                        point_t corners[FIND_BLOBS_CORNERS_RESOLUTION];
                        int corners_n[FIND_BLOBS_CORNERS_RESOLUTION];
                        // Ensures that maximum goes all the way to the edge of the image.
                        for (int i = 0; i < FIND_BLOBS_CORNERS_RESOLUTION; i++) {
                            corners[i].x = IM_MAX(IM_MIN(x_max * sign(cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]), x_max), 0);
                            corners[i].y = IM_MAX(IM_MIN(y_max * sign(sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]), y_max), 0);
                            corners_acc[i] = (corners[i].x * cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]) +
                                             (corners[i].y * sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]);
                            corners_n[i] = 1;
                        }

                        int blob_pixels = 0;
                        int blob_perimeter = 0;
                        int blob_cx = 0;
                        int blob_cy = 0;
                        long long blob_a = 0;
                        long long blob_b = 0;
                        long long blob_c = 0;

                        if (x_hist_bins) memset(x_hist_bins, 0, ptr->w * sizeof(uint16_t));
                        if (y_hist_bins) memset(y_hist_bins, 0, ptr->h * sizeof(uint16_t));

                        // Scanline Flood Fill Algorithm //

                        for(;;) {
                            int left = x, right = x;
                            uint16_t *row = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y);
                            uint32_t *bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y);

                            while ((left > roi->x)
                            && (!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, left - 1))
                            && COLOR_THRESHOLD_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, left - 1), &lnk_data, invert)) {
                                left--;
                            }

                            while ((right < (roi->x + roi->w - 1))
                            && (!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, right + 1))
                            && COLOR_THRESHOLD_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, right + 1), &lnk_data, invert)) {
                                right++;
                            }

                            for (int i = left; i <= right; i++) {
                                IMAGE_SET_BINARY_PIXEL_FAST(bmp_row, i);
                            }

                            int sum = sum_m_to_n(left, right);
                            int sum_2 = sum_2_m_to_n(left, right);
                            int cnt = right - left + 1;
                            int avg = sum / cnt;

                            for (int i = 0; i < FIND_BLOBS_CORNERS_RESOLUTION; i++) {
                                int x_new = (cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i] > 0) ? left :
                                            ((cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i] == 0) ? avg :
                                                                                              right);
                                float z = (x_new * cos_table[FIND_BLOBS_ANGLE_RESOLUTION*i]) +
                                          (y * sin_table[FIND_BLOBS_ANGLE_RESOLUTION*i]);
                                if (z < corners_acc[i]) {
                                    corners_acc[i] = z;
                                    corners[i].x = x_new;
                                    corners[i].y = y;
                                    corners_n[i] = 1;
                                } else if (z == corners_acc[i]) {
                                    corners[i].x = cumulative_moving_average(corners[i].x, x_new, corners_n[i]);
                                    corners[i].y = cumulative_moving_average(corners[i].y, y, corners_n[i]);
                                    corners_n[i] += 1;
                                }
                            }

                            blob_pixels += cnt;
                            blob_perimeter += 2;
                            blob_cx += sum;
                            blob_cy += y * cnt;
                            blob_a += sum_2;
                            blob_b += y * sum;
                            blob_c += y * y * cnt;

                            if (y_hist_bins) y_hist_bins[y] += cnt;
                            if (x_hist_bins) for (int i = left; i <= right; i++) x_hist_bins[i] += 1;

                            int top_left = left;
                            int bot_left = left;
                            bool break_out = false;
                            for(;;) {
                                if (lifo_size(&lifo) < lifo_len) {

                                    if (y > roi->y) {
                                        row = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y - 1);
                                        bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y - 1);

                                        bool recurse = false;
                                        for (int i = top_left; i <= right; i++) {
                                            bool ok = true; // Does nothing if thresholding is skipped.

                                            if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, i))
                                            && (ok = COLOR_THRESHOLD_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, i), &lnk_data, invert))) {
                                                xylr_t context;
                                                context.x = x;
                                                context.y = y;
                                                context.l = left;
                                                context.r = right;
                                                context.t_l = i + 1; // Don't test the same pixel again...
                                                context.b_l = bot_left;
                                                lifo_enqueue(&lifo, &context);
                                                x = i;
                                                y = y - 1;
                                                recurse = true;
                                                break;
                                            }

                                            blob_perimeter += (!ok) && (i != left) && (i != right);
                                        }
                                        if (recurse) {
                                            break;
                                        }
                                    } else {
                                        blob_perimeter += right - left + 1;
                                    }

                                    if (y < (roi->y + roi->h - 1)) {
                                        row = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y + 1);
                                        bmp_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&bmp, y + 1);

                                        bool recurse = false;
                                        for (int i = bot_left; i <= right; i++) {
                                            bool ok = true; // Does nothing if thresholding is skipped.

                                            if ((!IMAGE_GET_BINARY_PIXEL_FAST(bmp_row, i))
                                            && (ok = COLOR_THRESHOLD_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, i), &lnk_data, invert))) {
                                                xylr_t context;
                                                context.x = x;
                                                context.y = y;
                                                context.l = left;
                                                context.r = right;
                                                context.t_l = top_left;
                                                context.b_l = i + 1; // Don't test the same pixel again...
                                                lifo_enqueue(&lifo, &context);
                                                x = i;
                                                y = y + 1;
                                                recurse = true;
                                                break;
                                            }

                                            blob_perimeter += (!ok) && (i != left) && (i != right);
                                        }
                                        if (recurse) {
                                            break;
                                        }
                                    } else {
                                        blob_perimeter += right - left + 1;
                                    }
                                } else {
                                    blob_perimeter += (right - left + 1) * 2;
                                }

                                if (!lifo_size(&lifo)) {
                                    break_out = true;
                                    break;
                                }

                                xylr_t context;
                                lifo_dequeue(&lifo, &context);
                                x = context.x;
                                y = context.y;
                                left = context.l;
                                right = context.r;
                                top_left = context.t_l;
                                bot_left = context.b_l;
                            }

                            if (break_out) {
                                break;
                            }
                        }

                        rectangle_t rect;
                        rect.x = corners[(FIND_BLOBS_CORNERS_RESOLUTION*0)/4].x; // l
                        rect.y = corners[(FIND_BLOBS_CORNERS_RESOLUTION*1)/4].y; // t
                        rect.w = corners[(FIND_BLOBS_CORNERS_RESOLUTION*2)/4].x - corners[(FIND_BLOBS_CORNERS_RESOLUTION*0)/4].x + 1; // r - l + 1
                        rect.h = corners[(FIND_BLOBS_CORNERS_RESOLUTION*3)/4].y - corners[(FIND_BLOBS_CORNERS_RESOLUTION*1)/4].y + 1; // b - t + 1

                        if (((rect.w * rect.h) >= area_threshold) && (blob_pixels >= pixels_threshold)) {

                            // http://www.cse.usf.edu/~r1k/MachineVisionBook/MachineVision.files/MachineVision_Chapter2.pdf
                            // https://www.strchr.com/standard_deviation_in_one_pass
                            //
                            // a = sigma(x*x) + (mx*sigma(x)) + (mx*sigma(x)) + (sigma()*mx*mx)
                            // b = sigma(x*y) + (mx*sigma(y)) + (my*sigma(x)) + (sigma()*mx*my)
                            // c = sigma(y*y) + (my*sigma(y)) + (my*sigma(y)) + (sigma()*my*my)
                            //
                            // blob_a = sigma(x*x)
                            // blob_b = sigma(x*y)
                            // blob_c = sigma(y*y)
                            // blob_cx = sigma(x)
                            // blob_cy = sigma(y)
                            // blob_pixels = sigma()

                            float b_mx = blob_cx / ((float) blob_pixels);
                            float b_my = blob_cy / ((float) blob_pixels);
                            int mx = fast_roundf(b_mx); // x centroid
                            int my = fast_roundf(b_my); // y centroid
                            int small_blob_a = blob_a - ((mx * blob_cx) + (mx * blob_cx)) + (blob_pixels * mx * mx);
                            int small_blob_b = blob_b - ((mx * blob_cy) + (my * blob_cx)) + (blob_pixels * mx * my);
                            int small_blob_c = blob_c - ((my * blob_cy) + (my * blob_cy)) + (blob_pixels * my * my);

                            find_blobs_list_lnk_data_t lnk_blob;
                            memcpy(lnk_blob.corners, corners, FIND_BLOBS_CORNERS_RESOLUTION * sizeof(point_t));
                            memcpy(&lnk_blob.rect, &rect, sizeof(rectangle_t));
                            lnk_blob.pixels = blob_pixels;
                            lnk_blob.perimeter = blob_perimeter;
                            lnk_blob.code = 1 << code;
                            lnk_blob.count = 1;
                            lnk_blob.centroid_x = b_mx;
                            lnk_blob.centroid_y = b_my;
                            lnk_blob.rotation = (small_blob_a != small_blob_c) ? (fast_atan2f(2 * small_blob_b, small_blob_a - small_blob_c) / 2.0f) : 0.0f;
                            lnk_blob.roundness = calc_roundness(small_blob_a, small_blob_b, small_blob_c);
                            lnk_blob.x_hist_bins_count = 0;
                            lnk_blob.x_hist_bins = NULL;
                            lnk_blob.y_hist_bins_count = 0;
                            lnk_blob.y_hist_bins = NULL;
                            // These store the current average accumulation.
                            lnk_blob.centroid_x_acc = lnk_blob.centroid_x * lnk_blob.pixels;
                            lnk_blob.centroid_y_acc = lnk_blob.centroid_y * lnk_blob.pixels;
                            lnk_blob.rotation_acc_x = cosf(lnk_blob.rotation) * lnk_blob.pixels;
                            lnk_blob.rotation_acc_y = sinf(lnk_blob.rotation) * lnk_blob.pixels;
                            lnk_blob.roundness_acc = lnk_blob.roundness * lnk_blob.pixels;

                            if (x_hist_bins) {
                                bin_up(x_hist_bins, ptr->w, x_hist_bins_max, &lnk_blob.x_hist_bins, &lnk_blob.x_hist_bins_count);
                            }

                            if (y_hist_bins) {
                                bin_up(y_hist_bins, ptr->h, y_hist_bins_max, &lnk_blob.y_hist_bins, &lnk_blob.y_hist_bins_count);
                            }

                            if (((threshold_cb_arg == NULL) || threshold_cb(threshold_cb_arg, &lnk_blob))) {
                                list_push_back(out, &lnk_blob);
                            } else {
                                if (lnk_blob.x_hist_bins) xfree(lnk_blob.x_hist_bins);
                                if (lnk_blob.y_hist_bins) xfree(lnk_blob.y_hist_bins);
                            }
                        }

                        x = old_x;
                        y = old_y;
                    }
                }
            }
            break;
        }
        default: {
            break;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值