mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
172 lines
6.2 KiB
C
172 lines
6.2 KiB
C
/*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2013-2024 OpenMV, LLC.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* Edge Detection.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include "imlib.h"
|
|
#include "fb_alloc.h"
|
|
#ifdef IMLIB_ENABLE_BINARY_OPS
|
|
|
|
typedef struct gvec {
|
|
uint16_t t;
|
|
uint16_t g;
|
|
} gvec_t;
|
|
|
|
void imlib_edge_simple(image_t *src, rectangle_t *roi, int low_thresh, int high_thresh) {
|
|
imlib_morph(src, 1, kernel_high_pass_3, 1.0f, 0.0f, false, 0, false, NULL);
|
|
list_t thresholds;
|
|
list_init(&thresholds, sizeof(color_thresholds_list_lnk_data_t));
|
|
color_thresholds_list_lnk_data_t lnk_data;
|
|
lnk_data.LMin = low_thresh;
|
|
lnk_data.LMax = high_thresh;
|
|
list_push_back(&thresholds, &lnk_data);
|
|
imlib_binary(src, src, &thresholds, false, false, NULL);
|
|
list_free(&thresholds);
|
|
imlib_erode(src, 1, 2, NULL);
|
|
}
|
|
|
|
void imlib_edge_canny(image_t *src, rectangle_t *roi, int low_thresh, int high_thresh) {
|
|
int w = src->w;
|
|
|
|
gvec_t *gm = fb_alloc0(roi->w * roi->h * sizeof *gm, FB_ALLOC_NO_HINT);
|
|
|
|
//1. Noise Reduction with a Gaussian filter
|
|
imlib_sepconv3(src, kernel_gauss_3, 1.0f / 16.0f, 0.0f);
|
|
|
|
//2. Finding Image Gradients
|
|
for (int gy = 1, y = roi->y + 1; y < roi->y + roi->h - 1; y++, gy++) {
|
|
for (int gx = 1, x = roi->x + 1; x < roi->x + roi->w - 1; x++, gx++) {
|
|
int vx = 0, vy = 0;
|
|
// sobel kernel in the horizontal direction
|
|
vx = src->data [(y - 1) * w + x - 1]
|
|
- src->data [(y - 1) * w + x + 1]
|
|
+ (src->data[(y + 0) * w + x - 1] << 1)
|
|
- (src->data[(y + 0) * w + x + 1] << 1)
|
|
+ src->data [(y + 1) * w + x - 1]
|
|
- src->data [(y + 1) * w + x + 1];
|
|
|
|
// sobel kernel in the vertical direction
|
|
vy = src->data [(y - 1) * w + x - 1]
|
|
+ (src->data[(y - 1) * w + x + 0] << 1)
|
|
+ src->data [(y - 1) * w + x + 1]
|
|
- src->data [(y + 1) * w + x - 1]
|
|
- (src->data[(y + 1) * w + x + 0] << 1)
|
|
- src->data [(y + 1) * w + x + 1];
|
|
|
|
// Find magnitude
|
|
int g = (int) fast_sqrtf(vx * vx + vy * vy);
|
|
// Find the direction and round angle to 0, 45, 90 or 135
|
|
int t = (int) fast_fabsf((atan2f(vy, vx) * 180.0f / M_PI));
|
|
if (t < 22) {
|
|
t = 0;
|
|
} else if (t < 67) {
|
|
t = 45;
|
|
} else if (t < 112) {
|
|
t = 90;
|
|
} else if (t < 160) {
|
|
t = 135;
|
|
} else if (t <= 180) {
|
|
t = 0;
|
|
}
|
|
|
|
gm[gy * roi->w + gx].t = t;
|
|
gm[gy * roi->w + gx].g = g;
|
|
}
|
|
}
|
|
|
|
// 3. Hysteresis Thresholding
|
|
// 4. Non-maximum Suppression and output
|
|
for (int gy = 0, y = roi->y; y < roi->y + roi->h; y++, gy++) {
|
|
for (int gx = 0, x = roi->x; x < roi->x + roi->w; x++, gx++) {
|
|
int i = y * w + x;
|
|
gvec_t *va = NULL, *vb = NULL, *vc = &gm[gy * roi->w + gx];
|
|
|
|
// Clear the borders
|
|
if (y == (roi->y) || y == (roi->y + roi->h - 1) ||
|
|
x == (roi->x) || x == (roi->x + roi->w - 1)) {
|
|
src->data[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
if (vc->g < low_thresh) {
|
|
// Not an edge
|
|
src->data[i] = 0;
|
|
continue;
|
|
// Check if strong or weak edge
|
|
} else if (vc->g >= high_thresh ||
|
|
gm[(gy - 1) * roi->w + (gx - 1)].g >= high_thresh ||
|
|
gm[(gy - 1) * roi->w + (gx + 0)].g >= high_thresh ||
|
|
gm[(gy - 1) * roi->w + (gx + 1)].g >= high_thresh ||
|
|
gm[(gy + 0) * roi->w + (gx - 1)].g >= high_thresh ||
|
|
gm[(gy + 0) * roi->w + (gx + 1)].g >= high_thresh ||
|
|
gm[(gy + 1) * roi->w + (gx - 1)].g >= high_thresh ||
|
|
gm[(gy + 1) * roi->w + (gx + 0)].g >= high_thresh ||
|
|
gm[(gy + 1) * roi->w + (gx + 1)].g >= high_thresh) {
|
|
vc->g = vc->g;
|
|
} else {
|
|
// Not an edge
|
|
src->data[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
switch (vc->t) {
|
|
case 0: {
|
|
va = &gm[(gy + 0) * roi->w + (gx - 1)];
|
|
vb = &gm[(gy + 0) * roi->w + (gx + 1)];
|
|
break;
|
|
}
|
|
|
|
case 45: {
|
|
va = &gm[(gy + 1) * roi->w + (gx - 1)];
|
|
vb = &gm[(gy - 1) * roi->w + (gx + 1)];
|
|
break;
|
|
}
|
|
|
|
case 90: {
|
|
va = &gm[(gy + 1) * roi->w + (gx + 0)];
|
|
vb = &gm[(gy - 1) * roi->w + (gx + 0)];
|
|
break;
|
|
}
|
|
|
|
case 135: {
|
|
va = &gm[(gy + 1) * roi->w + (gx + 1)];
|
|
vb = &gm[(gy - 1) * roi->w + (gx - 1)];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!(vc->g > va->g && vc->g > vb->g)) {
|
|
src->data[i] = 0;
|
|
} else {
|
|
src->data[i] = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
fb_free();
|
|
}
|
|
#endif
|