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 ae9942893..7be35d89b 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 @@ -896,7 +898,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 +1061,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); @@ -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 c8c55f8c5..77be49895 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 bd12622f0..3ed09b04a 100644 --- a/src/omv/py/py_image.c +++ b/src/omv/py/py_image.c @@ -1357,6 +1357,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 { @@ -3918,6 +4013,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); @@ -4027,6 +4123,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 86decad30..0c3417a5e 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -360,6 +360,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) @@ -398,7 +407,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)