From e5eb7185fbe281114ba619ed8fc9dfde616060c0 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 21 Dec 2017 22:22:12 -0500 Subject: [PATCH 1/2] Line ops can now accept a parameter value. --- src/omv/img/imlib.c | 73 +++++++++++++++++++++++++++------------------ src/omv/img/imlib.h | 4 +-- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/omv/img/imlib.c b/src/omv/img/imlib.c index a2e790033..a8186bd96 100644 --- a/src/omv/img/imlib.c +++ b/src/omv/img/imlib.c @@ -397,7 +397,7 @@ static void imlib_read_pixels(FIL *fp, image_t *img, int line_start, int line_en } } -void imlib_image_operation(image_t *img, const char *path, image_t *other, line_op_t op) +void imlib_image_operation(image_t *img, const char *path, image_t *other, line_op_t op, void *data) { if (path) { uint32_t size = fb_avail() / 2; @@ -429,9 +429,9 @@ void imlib_image_operation(image_t *img, const char *path, image_t *other, line_ imlib_read_pixels(&fp, &temp, 0, can_do, &rs); for (int j=0; jh-i-can_do)+j, temp.pixels+(temp.w*temp.bpp*j)); + op(img, (img->h-i-can_do)+j, temp.pixels+(temp.w*temp.bpp*j), data); } } } @@ -443,7 +443,7 @@ void imlib_image_operation(image_t *img, const char *path, image_t *other, line_ ff_not_equal(NULL); } for (int i=0; ih; i++) { - op(img, i, other->pixels + (img->w * img->bpp * i)); + op(img, i, other->pixels + (img->w * img->bpp * i), data); } } } @@ -687,8 +687,10 @@ void imlib_invert(image_t *img) } } -static void imlib_b_and_line_op(image_t *img, int line, uint8_t *other) +static void imlib_b_and_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { @@ -704,11 +706,13 @@ static void imlib_b_and_line_op(image_t *img, int line, uint8_t *other) void imlib_b_and(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_b_and_line_op); + imlib_image_operation(img, path, other, imlib_b_and_line_op, NULL); } -static void imlib_b_nand_line_op(image_t *img, int line, uint8_t *other) +static void imlib_b_nand_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { @@ -724,11 +728,13 @@ static void imlib_b_nand_line_op(image_t *img, int line, uint8_t *other) void imlib_b_nand(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_b_nand_line_op); + imlib_image_operation(img, path, other, imlib_b_nand_line_op, NULL); } -static void imlib_b_or_line_op(image_t *img, int line, uint8_t *other) +static void imlib_b_or_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { @@ -744,11 +750,13 @@ static void imlib_b_or_line_op(image_t *img, int line, uint8_t *other) void imlib_b_or(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_b_or_line_op); + imlib_image_operation(img, path, other, imlib_b_or_line_op, NULL); } -static void imlib_b_nor_line_op(image_t *img, int line, uint8_t *other) +static void imlib_b_nor_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { @@ -764,11 +772,13 @@ static void imlib_b_nor_line_op(image_t *img, int line, uint8_t *other) void imlib_b_nor(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_b_nor_line_op); + imlib_image_operation(img, path, other, imlib_b_nor_line_op, NULL); } -static void imlib_b_xor_line_op(image_t *img, int line, uint8_t *other) +static void imlib_b_xor_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { @@ -784,11 +794,13 @@ static void imlib_b_xor_line_op(image_t *img, int line, uint8_t *other) void imlib_b_xor(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_b_xor_line_op); + imlib_image_operation(img, path, other, imlib_b_xor_line_op, NULL); } -static void imlib_b_xnor_line_op(image_t *img, int line, uint8_t *other) +static void imlib_b_xnor_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { @@ -804,7 +816,7 @@ static void imlib_b_xnor_line_op(image_t *img, int line, uint8_t *other) void imlib_b_xnor(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_b_xnor_line_op); + imlib_image_operation(img, path, other, imlib_b_xnor_line_op, NULL); } static void imlib_erode_dilate(image_t *img, int ksize, int threshold, int e_or_d) @@ -936,8 +948,10 @@ void imlib_negate(image_t *img) } } -static void imlib_difference_line_op(image_t *img, int line, uint8_t *other) +static void imlib_difference_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { @@ -957,11 +971,13 @@ static void imlib_difference_line_op(image_t *img, int line, uint8_t *other) void imlib_difference(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_difference_line_op); + imlib_image_operation(img, path, other, imlib_difference_line_op, NULL); } -static void imlib_replace_line_op(image_t *img, int line, uint8_t *other) +static void imlib_replace_line_op(image_t *img, int line, uint8_t *other, void *data) { + data = data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); memcpy(pixels, other, img->w * sizeof(uint8_t)); @@ -973,17 +989,17 @@ static void imlib_replace_line_op(image_t *img, int line, uint8_t *other) void imlib_replace(image_t *img, const char *path, image_t *other) { - imlib_image_operation(img, path, other, imlib_replace_line_op); + imlib_image_operation(img, path, other, imlib_replace_line_op, NULL); } -static uint32_t alpha_temp; - -static void imlib_blend_line_op(image_t *img, int line, uint8_t *other) +static void imlib_blend_line_op(image_t *img, int line, uint8_t *other, void *data) { + uint32_t alpha = (uint32_t) data; + if (IM_IS_GS(img)) { uint8_t *pixels = img->pixels + (img->w * line); for (int i=0; iw; i++) { - pixels[i] = __SMUAD(alpha_temp,__PKHBT(pixels[i],other[i],16))>>8; + pixels[i] = __SMUAD(alpha,__PKHBT(pixels[i],other[i],16))>>8; } } else { uint16_t *pixels = ((uint16_t *) img->pixels) + (img->w * line); @@ -992,9 +1008,9 @@ static void imlib_blend_line_op(image_t *img, int line, uint8_t *other) uint32_t vr = __PKHBT(IM_R565(pixel), IM_R565(other_pixel), 16); uint32_t vg = __PKHBT(IM_G565(pixel), IM_G565(other_pixel), 16); uint32_t vb = __PKHBT(IM_B565(pixel), IM_B565(other_pixel), 16); - uint32_t r = __SMUAD(alpha_temp, vr)>>8; - uint32_t g = __SMUAD(alpha_temp, vg)>>8; - uint32_t b = __SMUAD(alpha_temp, vb)>>8; + uint32_t r = __SMUAD(alpha, vr)>>8; + uint32_t g = __SMUAD(alpha, vg)>>8; + uint32_t b = __SMUAD(alpha, vb)>>8; pixels[i] = IM_RGB565(r, g, b); } } @@ -1002,8 +1018,7 @@ static void imlib_blend_line_op(image_t *img, int line, uint8_t *other) void imlib_blend(image_t *img, const char *path, image_t *other, int alpha) { - alpha_temp = __PKHBT((256-alpha), alpha, 16); - imlib_image_operation(img, path, other, imlib_blend_line_op); + imlib_image_operation(img, path, other, imlib_blend_line_op, (void *) __PKHBT((256-alpha), alpha, 16)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/omv/img/imlib.h b/src/omv/img/imlib.h index d7924615d..6618d5f8d 100644 --- a/src/omv/img/imlib.h +++ b/src/omv/img/imlib.h @@ -896,7 +896,7 @@ typedef struct img_read_settings { save_image_format_t format; } img_read_settings_t; -typedef void (*line_op_t)(image_t*, int, uint8_t*); +typedef void (*line_op_t)(image_t*, int, uint8_t*, void*); typedef enum descriptor_type { DESC_LBP, @@ -1059,7 +1059,7 @@ void jpeg_read_pixels(FIL *fp, image_t *img); void jpeg_read(image_t *img, const char *path); void jpeg_write(image_t *img, const char *path, int quality); bool imlib_read_geometry(FIL *fp, image_t *img, const char *path, img_read_settings_t *rs); -void imlib_image_operation(image_t *img, const char *path, image_t *other, line_op_t op); +void imlib_image_operation(image_t *img, const char *path, image_t *other, line_op_t op, void *data); void imlib_load_image(image_t *img, const char *path); void imlib_save_image(image_t *img, const char *path, rectangle_t *roi, int quality); void imlib_copy_image(image_t *dst, image_t *src, rectangle_t *roi); From e2025e8d1e9ed7a7a3617578cd380bd927ac0793 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sat, 23 Dec 2017 15:01:13 -0500 Subject: [PATCH 2/2] SSIM Code Image comparison using SSIM. It can be used to detect image differences... but, the algorithm was designed to compare image quality and look at compression artifacts. Anyway, it works kinda okay for detecting frame differences. --- src/omv/img/imlib.h | 3 + src/omv/img/stats.c | 134 ++++++++++++++++++ src/omv/py/py_image.c | 97 +++++++++++++ src/omv/py/qstrdefsomv.h | 11 +- .../structural-similarity.py | 35 +++++ 5 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 usr/examples/20-Frame-Differencing/structural-similarity.py diff --git a/src/omv/img/imlib.h b/src/omv/img/imlib.h index 6618d5f8d..e993edcc8 100644 --- a/src/omv/img/imlib.h +++ b/src/omv/img/imlib.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -1211,6 +1213,7 @@ void imlib_rotation_corr(image_t *img, float x_rotation, float y_rotation, float z_rotation, float x_translation, float y_translation, float zoom); // Statistics +void imlib_get_similarity(image_t *img, const char *path, image_t *other, float *avg, float *std, float *min, float *max); void imlib_get_histogram(histogram_t *out, image_t *ptr, rectangle_t *roi); void imlib_get_percentile(percentile_t *out, image_bpp_t bpp, histogram_t *ptr, float percentile); void imlib_get_statistics(statistics_t *out, image_bpp_t bpp, histogram_t *ptr); diff --git a/src/omv/img/stats.c b/src/omv/img/stats.c index 36b5de418..ea6e98f55 100644 --- a/src/omv/img/stats.c +++ b/src/omv/img/stats.c @@ -5,6 +5,140 @@ #include "imlib.h" +typedef struct imlib_similatiry_line_op_state { + int *sumBucketsOfX, *sumBucketsOfY, *sum2BucketsOfX, *sum2BucketsOfY, *sum2Buckets; + float similarity_sum, similarity_sum_2, similarity_min, similarity_max; + int lines_processed; +} imlib_similatiry_line_op_state_t; + +void imlib_similarity_line_op(image_t *img, int line, uint8_t *other, void *data) +{ + imlib_similatiry_line_op_state_t *state = (imlib_similatiry_line_op_state_t *) data; + float c1 = 0, c2 = 0; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line); + uint32_t *other_row_ptr = (uint32_t *) other; + for (int x = 0, xx = (img->w + 7) / 8; x < xx; x++) { + for (int i = 0, ii = IM_MIN((img->w - (x * 8)), 8); i < ii; i++) { + int pixel = IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x + i); + int other_pixel = IMAGE_GET_BINARY_PIXEL_FAST(other_row_ptr, x + i); + state->sumBucketsOfX[x] += pixel; + state->sumBucketsOfY[x] += other_pixel; + state->sum2BucketsOfX[x] += pixel * pixel; + state->sum2BucketsOfY[x] += other_pixel * other_pixel; + state->sum2Buckets[x] += pixel * other_pixel; + } + } + c1 = COLOR_BINARY_MAX * 0.01f; + c2 = COLOR_BINARY_MAX * 0.03f; + break; + } + case IMAGE_BPP_GRAYSCALE: { + uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line); + uint8_t *other_row_ptr = (uint8_t *) other; + for (int x = 0, xx = (img->w + 7) / 8; x < xx; x++) { + for (int i = 0, ii = IM_MIN((img->w - (x * 8)), 8); i < ii; i++) { + int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x + i); + int other_pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(other_row_ptr, x + i); + state->sumBucketsOfX[x] += pixel; + state->sumBucketsOfY[x] += other_pixel; + state->sum2BucketsOfX[x] += pixel * pixel; + state->sum2BucketsOfY[x] += other_pixel * other_pixel; + state->sum2Buckets[x] += pixel * other_pixel; + } + } + c1 = COLOR_GRAYSCALE_MAX * 0.01f; + c2 = COLOR_GRAYSCALE_MAX * 0.03f; + break; + } + case IMAGE_BPP_RGB565: { + uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line); + uint16_t *other_row_ptr = (uint16_t *) other; + for (int x = 0, xx = (img->w + 7) / 8; x < xx; x++) { + for (int i = 0, ii = IM_MIN((img->w - (x * 8)), 8); i < ii; i++) { + int pixel = COLOR_RGB565_TO_L(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x + i)); + int other_pixel = COLOR_RGB565_TO_L(IMAGE_GET_RGB565_PIXEL_FAST(other_row_ptr, x + i)); + state->sumBucketsOfX[x] += pixel; + state->sumBucketsOfY[x] += other_pixel; + state->sum2BucketsOfX[x] += pixel * pixel; + state->sum2BucketsOfY[x] += other_pixel * other_pixel; + state->sum2Buckets[x] += pixel * other_pixel; + } + } + c1 = COLOR_L_MAX * 0.01f; + c2 = COLOR_L_MAX * 0.03f; + break; + } + default: { + break; + } + } + + // https://en.wikipedia.org/wiki/Structural_similarity + if (((state->lines_processed + 1) == img->h) || (!((state->lines_processed + 1) % 8))) { + for (int x = 0, xx = (img->w + 7) / 8; x < xx; x++) { + int w = IM_MIN((img->w - (x * 8)), 8); + int h = IM_MIN((img->h - ((state->lines_processed / 8) * 8)), 8); + int size = w * h; + + int mx = state->sumBucketsOfX[x] / size; + int my = state->sumBucketsOfY[x] / size; + int vx = state->sum2BucketsOfX[x] - ((mx * state->sumBucketsOfX[x]) + (mx * state->sumBucketsOfX[x])) + (size * mx * mx); + int vy = state->sum2BucketsOfY[x] - ((my * state->sumBucketsOfY[x]) + (my * state->sumBucketsOfY[x])) + (size * my * my); + int vxy = state->sum2Buckets[x] - ((mx * state->sumBucketsOfY[x]) + (my * state->sumBucketsOfX[x])) + (size * mx * my); + + float ssim = ( ((2*mx*my) + c1) * ((2*vxy) + c2) ) / ( ((mx*mx) + (my*my) + c1) * (vx + vy + c2) ); + + state->similarity_sum += ssim; + state->similarity_sum_2 += ssim * ssim; + state->similarity_min = IM_MIN(state->similarity_min, ssim); + state->similarity_max = IM_MAX(state->similarity_max, ssim); + + state->sumBucketsOfX[x] = 0; + state->sumBucketsOfY[x] = 0; + state->sum2BucketsOfX[x] = 0; + state->sum2BucketsOfY[x] = 0; + state->sum2Buckets[x] = 0; + } + } + + state->lines_processed += 1; +} + +void imlib_get_similarity(image_t *img, const char *path, image_t *other, float *avg, float *std, float *min, float *max) +{ + int h_blocks = (img->w + 7) / 8; + int v_blocks = (img->h + 7) / 8; + int blocks = h_blocks * v_blocks; + + int int_h_blocks = h_blocks * sizeof(int); + imlib_similatiry_line_op_state_t state; + state.sumBucketsOfX = fb_alloc0(int_h_blocks); + state.sumBucketsOfY = fb_alloc0(int_h_blocks); + state.sum2BucketsOfX = fb_alloc0(int_h_blocks); + state.sum2BucketsOfY = fb_alloc0(int_h_blocks); + state.sum2Buckets = fb_alloc0(int_h_blocks); + state.similarity_sum = 0.0f; + state.similarity_sum_2 = 0.0f; + state.similarity_min = FLT_MAX; + state.similarity_max = FLT_MIN; + state.lines_processed = 0; + + imlib_image_operation(img, path, other, imlib_similarity_line_op, (void *) &state); + *avg = state.similarity_sum / blocks; + *std = fast_sqrtf((state.similarity_sum_2 / blocks) - ((*avg) * (*avg))); + *min = state.similarity_min; + *max = state.similarity_max; + + fb_free(); + fb_free(); + fb_free(); + fb_free(); + fb_free(); +} + void imlib_get_histogram(histogram_t *out, image_t *ptr, rectangle_t *roi) { switch(ptr->bpp) { diff --git a/src/omv/py/py_image.c b/src/omv/py/py_image.c index 6b65a40e6..2faa82c12 100644 --- a/src/omv/py/py_image.c +++ b/src/omv/py/py_image.c @@ -1319,6 +1319,101 @@ static mp_obj_t py_image_mask_ellipse(mp_obj_t img_obj) return img_obj; } +// Similarity Object // +#define py_similarity_obj_size 4 +typedef struct py_similarity_obj { + mp_obj_base_t base; + mp_obj_t avg, std, min, max; +} py_similarity_obj_t; + +static void py_similarity_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) +{ + py_similarity_obj_t *self = self_in; + mp_printf(print, + "{mean:%f, stdev:%f, min:%f, max:%f}", + (double) mp_obj_get_float(self->avg), + (double) mp_obj_get_float(self->std), + (double) mp_obj_get_float(self->min), + (double) mp_obj_get_float(self->max)); +} + +static mp_obj_t py_similarity_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) +{ + if (value == MP_OBJ_SENTINEL) { // load + py_similarity_obj_t *self = self_in; + if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(py_similarity_obj_size, index, &slice)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "only slices with step=1 (aka None) are supported")); + } + mp_obj_tuple_t *result = mp_obj_new_tuple(slice.stop - slice.start, NULL); + mp_seq_copy(result->items, &(self->avg) + slice.start, result->len, mp_obj_t); + return result; + } + switch (mp_get_index(self->base.type, py_similarity_obj_size, index, false)) { + case 0: return self->avg; + case 1: return self->std; + case 2: return self->min; + case 3: return self->max; + } + } + return MP_OBJ_NULL; // op not supported +} + +mp_obj_t py_similarity_mean(mp_obj_t self_in) { return ((py_similarity_obj_t *) self_in)->avg; } +mp_obj_t py_similarity_stdev(mp_obj_t self_in) { return ((py_similarity_obj_t *) self_in)->std; } +mp_obj_t py_similarity_min(mp_obj_t self_in) { return ((py_similarity_obj_t *) self_in)->min; } +mp_obj_t py_similarity_max(mp_obj_t self_in) { return ((py_similarity_obj_t *) self_in)->max; } + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_similarity_mean_obj, py_similarity_mean); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_similarity_stdev_obj, py_similarity_stdev); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_similarity_min_obj, py_similarity_min); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_similarity_max_obj, py_similarity_max); + +STATIC const mp_rom_map_elem_t py_similarity_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_mean), MP_ROM_PTR(&py_similarity_mean_obj) }, + { MP_ROM_QSTR(MP_QSTR_stdev), MP_ROM_PTR(&py_similarity_stdev_obj) }, + { MP_ROM_QSTR(MP_QSTR_min), MP_ROM_PTR(&py_similarity_min_obj) }, + { MP_ROM_QSTR(MP_QSTR_max), MP_ROM_PTR(&py_similarity_max_obj) } +}; + +STATIC MP_DEFINE_CONST_DICT(py_similarity_locals_dict, py_similarity_locals_dict_table); + +static const mp_obj_type_t py_similarity_type = { + { &mp_type_type }, + .name = MP_QSTR_similarity, + .print = py_similarity_print, + .subscr = py_similarity_subscr, + .locals_dict = (mp_obj_t) &py_similarity_locals_dict, +}; + +static mp_obj_t py_image_get_similarity(mp_obj_t img_obj, mp_obj_t other_obj) +{ + image_t *arg_img = py_image_cobj(img_obj); + PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); + + float avg, std, min, max; + + if (MP_OBJ_IS_STR(other_obj)) { + fb_alloc_mark(); + imlib_get_similarity(arg_img, mp_obj_str_get_str(other_obj), NULL, &avg, &std, &min, &max); + fb_alloc_mark(); + } else { + image_t *arg_other = py_image_cobj(other_obj); + fb_alloc_mark(); + imlib_get_similarity(arg_img, NULL, arg_other, &avg, &std, &min, &max); + fb_alloc_free_till_mark(); + } + + py_similarity_obj_t *o = m_new_obj(py_similarity_obj_t); + o->base.type = &py_similarity_type; + o->avg = mp_obj_new_float(avg); + o->std = mp_obj_new_float(std); + o->min = mp_obj_new_float(min); + o->max = mp_obj_new_float(max); + return o; +} + // Statistics Object // #define py_statistics_obj_size 24 typedef struct py_statistics_obj { @@ -3880,6 +3975,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_rotation_corr_obj, 1, py_image_rotati #endif STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_mask_ellipse_obj, py_image_mask_ellipse); /* Image Statistics */ +STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_get_similarity_obj, py_image_get_similarity); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_get_histogram_obj, 1, py_image_get_histogram); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_get_statistics_obj, 1, py_image_get_statistics); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_get_regression_obj, 2, py_image_get_regression); @@ -3989,6 +4085,7 @@ static const mp_map_elem_t locals_dict_table[] = { #endif {MP_OBJ_NEW_QSTR(MP_QSTR_mask_ellipse), (mp_obj_t)&py_image_mask_ellipse_obj}, /* Image Statistics */ + {MP_OBJ_NEW_QSTR(MP_QSTR_get_similarity), (mp_obj_t)&py_image_get_similarity_obj}, {MP_OBJ_NEW_QSTR(MP_QSTR_get_hist), (mp_obj_t)&py_image_get_histogram_obj}, {MP_OBJ_NEW_QSTR(MP_QSTR_get_histogram), (mp_obj_t)&py_image_get_histogram_obj}, {MP_OBJ_NEW_QSTR(MP_QSTR_histogram), (mp_obj_t)&py_image_get_histogram_obj}, diff --git a/src/omv/py/qstrdefsomv.h b/src/omv/py/qstrdefsomv.h index aa458df5a..55c826ec8 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -359,6 +359,15 @@ Q(x_translation) Q(y_translation) // duplicate Q(zoom) +// Structural Similarity +Q(get_similarity) +// Similarity Object +Q(similarity) +// duplicate Q(mean) +Q(stdev) +// duplicate Q(min) +// duplicate Q(max) + // Get Histogram Q(get_hist) Q(get_histogram) @@ -397,7 +406,7 @@ Q(b_value) // duplicate Q(mean) // duplicate Q(median) // duplicate Q(mode) -Q(stdev) +// duplicate Q(stdev) // duplicate Q(min) // duplicate Q(max) Q(lq) diff --git a/usr/examples/20-Frame-Differencing/structural-similarity.py b/usr/examples/20-Frame-Differencing/structural-similarity.py new file mode 100644 index 000000000..4a1e73706 --- /dev/null +++ b/usr/examples/20-Frame-Differencing/structural-similarity.py @@ -0,0 +1,35 @@ +# Structural Similarity (SSIM) Example +# +# Note: You will need an SD card to run this example. +# +# This example shows off how to use the SSIM algorithm on your OpenMV Cam +# to detect differences between two images. The SSIM algorithm compares +# 8x8 blocks of pixels between two images to determine a similarity +# score between two images. + +import sensor, image, pyb, os, time + +# The image has likely changed if the sim.min() is lower than this. +MIN_TRIGGER_THRESHOLD = -0.4 + +sensor.reset() # Initialize the camera sensor. +sensor.set_pixformat(sensor.RGB565) # or sensor.GRAYSCALE +sensor.set_framesize(sensor.QVGA) # or sensor.QQVGA (or others) +sensor.skip_frames(time = 2000) # Let new settings take affect. +sensor.set_auto_whitebal(False) # Turn off white balance. +clock = time.clock() # Tracks FPS. + +if not "temp" in os.listdir(): os.mkdir("temp") # Make a temp directory + +print("About to save background image...") +sensor.skip_frames(time = 2000) # Give the user time to get ready. +sensor.snapshot().save("temp/bg.bmp") +print("Saved background image!") + +while(True): + clock.tick() # Track elapsed milliseconds between snapshots(). + img = sensor.snapshot() # Take a picture and return the image. + sim = img.get_similarity("temp/bg.bmp") + change = "- Change -" if sim.min() < MIN_TRIGGER_THRESHOLD else "- No Change -" + + print(clock.fps(), change, sim)