Upate filtering operations

Binary images are now handled. Cleaned up and optimized code. Some speed
gains after shifting to multiplies and not using int8_t.

Added a sharp and unsharp mask feature. Fixed up guassian. Added a
laplacian operation for edge detection.
This commit is contained in:
Kwabena W. Agyeman 2018-03-18 22:06:03 -04:00
parent 93f82440f2
commit d7159fba3e
20 changed files with 1105 additions and 708 deletions

View File

@ -162,11 +162,11 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
fmath.o \
fsort.o \
fft.o \
filter.o \
haar.o \
imlib.o \
collections.o \
stats.o \
morph.o \
integral.o \
integral_mw.o \
kmeans.o \
@ -177,10 +177,6 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
rgb2rgb_tab.o \
invariant_tab.o \
mathop.o \
midpoint.o \
mean.o \
mode.o \
median.o \
pool.o \
point.o \
rectangle.o \

View File

@ -32,11 +32,11 @@ SRCS += $(addprefix img/, \
fmath.c \
fsort.c \
fft.c \
filter.c \
haar.c \
imlib.c \
collections.c \
stats.c \
morph.c \
integral.c \
integral_mw.c \
kmeans.c \
@ -47,10 +47,6 @@ SRCS += $(addprefix img/, \
rgb2rgb_tab.c \
invariant_tab.c \
mathop.c \
midpoint.c \
mean.c \
mode.c \
median.c \
pool.c \
point.c \
rectangle.c \

View File

@ -19,7 +19,7 @@ typedef struct gvec {
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);
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;

817
src/omv/img/filter.c Normal file
View File

@ -0,0 +1,817 @@
/* This file is part of the OpenMV project.
* Copyright (c) 2013-2018 Ibrahim Abdelkader <iabdalkader@openmv.io> & Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*/
#include "fsort.h"
#include "imlib.h"
void imlib_histeq(image_t *img)
{
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
// Can't run this on a binary image.
break;
}
case IMAGE_BPP_GRAYSCALE: {
int a = img->w * img->h;
float s = (COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN) / ((float) a);
uint32_t *hist = fb_alloc0((COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1)*sizeof(uint32_t));
uint8_t *pixels = (uint8_t *) img->data;
// Compute the image histogram
for (int i=0; i<a; i++) {
hist[pixels[i]-COLOR_GRAYSCALE_MIN] += 1;
}
// Compute the CDF
for (int i=0, sum=0; i<(COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1); i++) {
sum += hist[i];
hist[i] = sum;
}
for (int i=0; i<a; i++) {
int pixel = pixels[i];
pixels[i] = (s * hist[pixel-COLOR_GRAYSCALE_MIN]) + COLOR_GRAYSCALE_MIN;
}
fb_free();
break;
}
case IMAGE_BPP_RGB565: {
int a = img->w * img->h;
float s = (COLOR_Y_MAX-COLOR_Y_MIN) / ((float) a);
uint32_t *hist = fb_alloc0((COLOR_Y_MAX-COLOR_Y_MIN+1)*sizeof(uint32_t));
uint16_t *pixels = (uint16_t *) img->data;
// Compute image histogram
for (int i=0; i<a; i++) {
hist[COLOR_RGB565_TO_Y(pixels[i])-COLOR_Y_MIN] += 1;
}
// Compute the CDF
for (int i=0, sum=0; i<(COLOR_Y_MAX-COLOR_Y_MIN+1); i++) {
sum += hist[i];
hist[i] = sum;
}
for (int i=0; i<a; i++) {
int pixel = pixels[i];
pixels[i] = imlib_yuv_to_rgb((s * hist[COLOR_RGB565_TO_Y(pixel)-COLOR_Y_MIN]),
COLOR_RGB565_TO_U(pixel),
COLOR_RGB565_TO_V(pixel));
}
fb_free();
break;
}
default: {
break;
}
}
}
// ksize == 0 -> 1x1 kernel
// ksize == 1 -> 3x3 kernel
// ...
// ksize == n -> ((n*2)+1)x((n*2)+1) kernel
void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask)
{
int brows = ksize + 1;
image_t buf;
buf.w = img->w;
buf.h = brows;
buf.bpp = img->bpp;
float over_n = 1.0f / (((ksize*2)+1)*((ksize*2)+1));
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
// Can't run this on a binary image.
break;
}
case IMAGE_BPP_GRAYSCALE: {
buf.data = fb_alloc(IMAGE_GRAYSCALE_LINE_LEN_BYTES(img) * brows);
for (int y = 0, yy = img->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
uint8_t *buf_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int acc = 0;
for (int j = -ksize; j <= ksize; j++) {
uint8_t *k_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
}
}
int pixel = fast_roundf(acc * over_n);
if (threshold) {
if (((pixel - offset) < IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x)) ^ invert) {
pixel = COLOR_GRAYSCALE_BINARY_MAX;
} else {
pixel = COLOR_GRAYSCALE_BINARY_MIN;
}
}
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
fb_free();
break;
}
case IMAGE_BPP_RGB565: {
buf.data = fb_alloc(IMAGE_RGB565_LINE_LEN_BYTES(img) * brows);
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
uint16_t *buf_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int r_acc = 0, g_acc = 0, b_acc = 0;
for (int j = -ksize; j <= ksize; j++) {
uint16_t *k_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
r_acc += COLOR_RGB565_TO_R5(pixel);
g_acc += COLOR_RGB565_TO_G6(pixel);
b_acc += COLOR_RGB565_TO_B5(pixel);
}
}
int pixel = COLOR_R5_G6_B5_TO_RGB565(fast_roundf(r_acc * over_n),
fast_roundf(g_acc * over_n),
fast_roundf(b_acc * over_n));
if (threshold) {
if (((COLOR_RGB565_TO_Y(pixel) - offset) < COLOR_RGB565_TO_Y(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x))) ^ invert) {
pixel = COLOR_RGB565_BINARY_MAX;
} else {
pixel = COLOR_RGB565_BINARY_MIN;
}
}
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
fb_free();
break;
}
default: {
break;
}
}
}
void imlib_median_filter(image_t *img, const int ksize, float percentile, bool threshold, int offset, bool invert, image_t *mask)
{
int brows = ksize + 1;
image_t buf;
buf.w = img->w;
buf.h = brows;
buf.bpp = img->bpp;
int n = ((ksize*2)+1)*((ksize*2)+1), int_percentile = fast_roundf(percentile * (n - 1));
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
// Can't run this on a binary image.
break;
}
case IMAGE_BPP_GRAYSCALE: {
buf.data = fb_alloc(IMAGE_GRAYSCALE_LINE_LEN_BYTES(img) * brows);
int *data = fb_alloc(n*sizeof(int));
for (int y = 0, yy = img->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
uint8_t *buf_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int *data_ptr = data;
for (int j = -ksize; j <= ksize; j++) {
uint8_t *k_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
*data_ptr++ = IMAGE_GET_GRAYSCALE_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
}
}
fsort(data, n);
int pixel = data[int_percentile];
if (threshold) {
if (((pixel - offset) < IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x)) ^ invert) {
pixel = COLOR_GRAYSCALE_BINARY_MAX;
} else {
pixel = COLOR_GRAYSCALE_BINARY_MIN;
}
}
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
fb_free();
fb_free();
break;
}
case IMAGE_BPP_RGB565: {
buf.data = fb_alloc(IMAGE_RGB565_LINE_LEN_BYTES(img) * brows);
int *r_data = fb_alloc(n*sizeof(int));
int *g_data = fb_alloc(n*sizeof(int));
int *b_data = fb_alloc(n*sizeof(int));
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
uint16_t *buf_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int *r_data_ptr = r_data, *g_data_ptr = g_data, *b_data_ptr = b_data;
for (int j = -ksize; j <= ksize; j++) {
uint16_t *k_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
*r_data_ptr++ = COLOR_RGB565_TO_R5(pixel);
*g_data_ptr++ = COLOR_RGB565_TO_G6(pixel);
*b_data_ptr++ = COLOR_RGB565_TO_B5(pixel);
}
}
fsort(r_data, n);
fsort(g_data, n);
fsort(b_data, n);
int pixel = COLOR_R5_G6_B5_TO_RGB565(r_data[int_percentile],
g_data[int_percentile],
b_data[int_percentile]);
if (threshold) {
if (((COLOR_RGB565_TO_Y(pixel) - offset) < COLOR_RGB565_TO_Y(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x))) ^ invert) {
pixel = COLOR_RGB565_BINARY_MAX;
} else {
pixel = COLOR_RGB565_BINARY_MIN;
}
}
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
fb_free();
fb_free();
fb_free();
fb_free();
break;
}
default: {
break;
}
}
}
void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask)
{
int brows = ksize + 1;
image_t buf;
buf.w = img->w;
buf.h = brows;
buf.bpp = img->bpp;
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
// Can't run this on a binary image.
break;
}
case IMAGE_BPP_GRAYSCALE: {
buf.data = fb_alloc(IMAGE_GRAYSCALE_LINE_LEN_BYTES(img) * brows);
int *bins = fb_alloc((COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1)*sizeof(int));
for (int y = 0, yy = img->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
uint8_t *buf_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
memset(bins, 0, (COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1)*sizeof(int));
int mcount = 0, mode = 0;
for (int j = -ksize; j <= ksize; j++) {
uint8_t *k_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
bins[pixel]++;
if (bins[pixel] > mcount) {
mcount = bins[pixel];
mode = pixel;
}
}
}
int pixel = mode;
if (threshold) {
if (((pixel - offset) < IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x)) ^ invert) {
pixel = COLOR_GRAYSCALE_BINARY_MAX;
} else {
pixel = COLOR_GRAYSCALE_BINARY_MIN;
}
}
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
fb_free();
fb_free();
break;
}
case IMAGE_BPP_RGB565: {
buf.data = fb_alloc(IMAGE_RGB565_LINE_LEN_BYTES(img) * brows);
int *r_bins = fb_alloc((COLOR_R5_MAX-COLOR_R5_MIN+1)*sizeof(int));
int *g_bins = fb_alloc((COLOR_G6_MAX-COLOR_G6_MIN+1)*sizeof(int));
int *b_bins = fb_alloc((COLOR_B5_MAX-COLOR_B5_MIN+1)*sizeof(int));
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
uint16_t *buf_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
memset(r_bins, 0, (COLOR_R5_MAX-COLOR_R5_MIN+1)*sizeof(int));
memset(g_bins, 0, (COLOR_G6_MAX-COLOR_G6_MIN+1)*sizeof(int));
memset(b_bins, 0, (COLOR_B5_MAX-COLOR_B5_MIN+1)*sizeof(int));
int r_mcount = 0, r_mode = 0;
int g_mcount = 0, g_mode = 0;
int b_mcount = 0, b_mode = 0;
for (int j = -ksize; j <= ksize; j++) {
uint16_t *k_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
int r_pixel = COLOR_RGB565_TO_R5(pixel);
int g_pixel = COLOR_RGB565_TO_G6(pixel);
int b_pixel = COLOR_RGB565_TO_B5(pixel);
r_bins[r_pixel]++;
g_bins[g_pixel]++;
b_bins[b_pixel]++;
if (r_bins[r_pixel] > r_mcount) {
r_mcount = r_bins[r_pixel];
r_mode = r_pixel;
}
if (g_bins[g_pixel] > g_mcount) {
g_mcount = g_bins[g_pixel];
g_mode = g_pixel;
}
if (b_bins[b_pixel] > b_mcount) {
b_mcount = b_bins[b_pixel];
b_mode = b_pixel;
}
}
}
int pixel = COLOR_R5_G6_B5_TO_RGB565(r_mode, g_mode, b_mode);
if (threshold) {
if (((COLOR_RGB565_TO_Y(pixel) - offset) < COLOR_RGB565_TO_Y(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x))) ^ invert) {
pixel = COLOR_RGB565_BINARY_MAX;
} else {
pixel = COLOR_RGB565_BINARY_MIN;
}
}
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
fb_free();
fb_free();
fb_free();
fb_free();
break;
}
default: {
break;
}
}
}
void imlib_midpoint_filter(image_t *img, const int ksize, float bias, bool threshold, int offset, bool invert, image_t *mask)
{
int brows = ksize + 1;
image_t buf;
buf.w = img->w;
buf.h = brows;
buf.bpp = img->bpp;
float max_bias = bias, min_bias = 1.0f - bias;
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
// Can't run this on a binary image.
break;
}
case IMAGE_BPP_GRAYSCALE: {
buf.data = fb_alloc(IMAGE_GRAYSCALE_LINE_LEN_BYTES(img) * brows);
for (int y = 0, yy = img->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
uint8_t *buf_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int min = COLOR_GRAYSCALE_MAX, max = COLOR_GRAYSCALE_MIN;
for (int j = -ksize; j <= ksize; j++) {
uint8_t *k_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
min = IM_MIN(min, pixel);
max = IM_MAX(max, pixel);
}
}
int pixel = fast_roundf((min*min_bias)+(max*max_bias));
if (threshold) {
if (((pixel - offset) < IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x)) ^ invert) {
pixel = COLOR_GRAYSCALE_BINARY_MAX;
} else {
pixel = COLOR_GRAYSCALE_BINARY_MIN;
}
}
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
fb_free();
break;
}
case IMAGE_BPP_RGB565: {
buf.data = fb_alloc(IMAGE_RGB565_LINE_LEN_BYTES(img) * brows);
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
uint16_t *buf_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int r_min = COLOR_R5_MAX, r_max = COLOR_R5_MIN;
int g_min = COLOR_G6_MAX, g_max = COLOR_G6_MIN;
int b_min = COLOR_B5_MAX, b_max = COLOR_B5_MIN;
for (int j = -ksize; j <= ksize; j++) {
uint16_t *k_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
int r_pixel = COLOR_RGB565_TO_R5(pixel);
int g_pixel = COLOR_RGB565_TO_G6(pixel);
int b_pixel = COLOR_RGB565_TO_B5(pixel);
r_min = IM_MIN(r_min, r_pixel);
r_max = IM_MAX(r_max, r_pixel);
g_min = IM_MIN(g_min, g_pixel);
g_max = IM_MAX(g_max, g_pixel);
b_min = IM_MIN(b_min, b_pixel);
b_max = IM_MAX(b_max, b_pixel);
}
}
int pixel = COLOR_R5_G6_B5_TO_RGB565(fast_roundf((r_min*min_bias)+(r_max*max_bias)),
fast_roundf((g_min*min_bias)+(g_max*max_bias)),
fast_roundf((b_min*min_bias)+(b_max*max_bias)));
if (threshold) {
if (((COLOR_RGB565_TO_Y(pixel) - offset) < COLOR_RGB565_TO_Y(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x))) ^ invert) {
pixel = COLOR_RGB565_BINARY_MAX;
} else {
pixel = COLOR_RGB565_BINARY_MIN;
}
}
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
fb_free();
break;
}
default: {
break;
}
}
}
// http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
void imlib_morph(image_t *img, const int ksize, const int *krn, const float m, const int b, bool threshold, int offset, bool invert, image_t *mask)
{
int brows = ksize + 1;
image_t buf;
buf.w = img->w;
buf.h = brows;
buf.bpp = img->bpp;
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
// Can't run this on a binary image.
break;
}
case IMAGE_BPP_GRAYSCALE: {
buf.data = fb_alloc(IMAGE_GRAYSCALE_LINE_LEN_BYTES(img) * brows);
for (int y = 0, yy = img->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
uint8_t *buf_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int acc = 0, ptr = 0;
for (int j = -ksize; j <= ksize; j++) {
uint8_t *k_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
acc += krn[ptr++] * IMAGE_GET_GRAYSCALE_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
}
}
int pixel = IM_MAX(IM_MIN(fast_roundf(acc * m) + b, COLOR_GRAYSCALE_MAX), COLOR_GRAYSCALE_MIN);
if (threshold) {
if (((pixel - offset) < IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x)) ^ invert) {
pixel = COLOR_GRAYSCALE_BINARY_MAX;
} else {
pixel = COLOR_GRAYSCALE_BINARY_MIN;
}
}
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_GRAYSCALE_LINE_LEN_BYTES(img));
}
fb_free();
break;
}
case IMAGE_BPP_RGB565: {
buf.data = fb_alloc(IMAGE_RGB565_LINE_LEN_BYTES(img) * brows);
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
uint16_t *buf_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows));
for (int x = 0, xx = img->w; x < xx; x++) {
if (mask && (!image_get_mask_pixel(mask, x, y))) {
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
continue; // Short circuit.
}
int r_acc = 0, g_acc = 0, b_acc = 0, ptr = 0;
for (int j = -ksize; j <= ksize; j++) {
uint16_t *k_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img,
IM_MIN(IM_MAX(y + j, 0), (img->h - 1)));
for (int k = -ksize; k <= ksize; k++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(k_row_ptr,
IM_MIN(IM_MAX(x + k, 0), (img->w - 1)));
r_acc += krn[ptr] * COLOR_RGB565_TO_R5(pixel);
g_acc += krn[ptr] * COLOR_RGB565_TO_G6(pixel);
b_acc += krn[ptr++] * COLOR_RGB565_TO_B5(pixel);
}
}
int pixel = COLOR_R5_G6_B5_TO_RGB565(IM_MAX(IM_MIN(fast_roundf(r_acc * m) + b, COLOR_R5_MAX), COLOR_R5_MIN),
IM_MAX(IM_MIN(fast_roundf(g_acc * m) + b, COLOR_G6_MAX), COLOR_G6_MIN),
IM_MAX(IM_MIN(fast_roundf(b_acc * m) + b, COLOR_B5_MAX), COLOR_B5_MIN));
if (threshold) {
if (((COLOR_RGB565_TO_Y(pixel) - offset) < COLOR_RGB565_TO_Y(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x))) ^ invert) {
pixel = COLOR_RGB565_BINARY_MAX;
} else {
pixel = COLOR_RGB565_BINARY_MIN;
}
}
IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, pixel);
}
if (y >= ksize) { // Transfer buffer lines...
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, (y - ksize)),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
}
// Copy any remaining lines from the buffer image...
for (int y = img->h - ksize, yy = img->h; y < yy; y++) {
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y),
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows)),
IMAGE_RGB565_LINE_LEN_BYTES(img));
}
fb_free();
break;
}
default: {
break;
}
}
}

View File

@ -6,14 +6,15 @@
* Fast 9 and 25 bin sort.
*
*/
#include <stdlib.h>
#include "fsort.h"
#include "common.h"
// http://pages.ripco.net/~jgamble/nw.html
ALWAYS_INLINE static void cmpswp(uint8_t *a, uint8_t *b)
static void cmpswp(int *a, int *b)
{
if ((*b) < (*a)) {
uint8_t tmp = *a;
int tmp = *a;
*a = *b;
*b = tmp;
}
@ -36,7 +37,7 @@ ALWAYS_INLINE static void cmpswp(uint8_t *a, uint8_t *b)
// This is graphed in 17 columns.
static void fsort9(uint8_t *data)
static void fsort9(int *data)
{
cmpswp(data+0, data+1);
cmpswp(data+3, data+4);
@ -108,7 +109,7 @@ static void fsort9(uint8_t *data)
// This is graphed in 89 columns.
static void fsort25(uint8_t *data)
static void fsort25(int *data)
{
cmpswp(data+1, data+2);
cmpswp(data+4, data+5);
@ -292,12 +293,17 @@ static void fsort25(uint8_t *data)
cmpswp(data+11, data+12);
}
void fsort(uint8_t *data, int n)
static int fsort_compare(const void *a, const void *b)
{
return (*((int *) a)) - (*((int *) b));
}
void fsort(int *data, int n)
{
switch(n) {
case 1: return;
case 9: fsort9(data); return;
case 25: fsort25(data); return;
default: return;
default: qsort(data, n, sizeof(int), fsort_compare);
}
}

View File

@ -9,5 +9,5 @@
#ifndef __FSORT_H__
#define __FSORT_H__
#include <stdint.h>
void fsort(uint8_t *data, int n);
void fsort(int *data, int n);
#endif /* __FSORT_H__ */

View File

@ -227,13 +227,13 @@ const int8_t kernel_gauss_5[5*5] = {
1, 4, 6, 4, 1
};
const int8_t kernel_laplacian_3[3*3] = {
const int kernel_laplacian_3[3*3] = {
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
};
const int8_t kernel_high_pass_3[3*3] = {
const int kernel_high_pass_3[3*3] = {
-1, -1, -1,
-1, +8, -1,
-1, -1, -1
@ -521,71 +521,6 @@ void imlib_save_image(image_t *img, const char *path, rectangle_t *roi, int qual
////////////////////////////////////////////////////////////////////////////////
void imlib_histeq(image_t *img)
{
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
// Can't run this on a binary image.
break;
}
case IMAGE_BPP_GRAYSCALE: {
int a = img->w * img->h;
float s = (COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN) / ((float) a);
uint32_t *hist = fb_alloc0((COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1)*sizeof(uint32_t));
uint8_t *pixels = (uint8_t *) img->pixels;
// Compute the image histogram
for (int i=0; i<a; i++) {
hist[pixels[i]-COLOR_GRAYSCALE_MIN] += 1;
}
// Compute the CDF
for (int i=0, sum=0; i<(COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1); i++) {
sum += hist[i];
hist[i] = sum;
}
for (int i=0; i<a; i++) {
int pixel = pixels[i];
pixels[i] = (s * hist[pixel-COLOR_GRAYSCALE_MIN]) + COLOR_GRAYSCALE_MIN;
}
fb_free();
break;
}
case IMAGE_BPP_RGB565: {
int a = img->w * img->h;
float s = (COLOR_Y_MAX-COLOR_Y_MIN) / ((float) a);
uint32_t *hist = fb_alloc0((COLOR_Y_MAX-COLOR_Y_MIN+1)*sizeof(uint32_t));
uint16_t *pixels = (uint16_t *) img->pixels;
// Compute image histogram
for (int i=0; i<a; i++) {
hist[COLOR_RGB565_TO_Y(pixels[i])-COLOR_Y_MIN] += 1;
}
// Compute the CDF
for (int i=0, sum=0; i<(COLOR_Y_MAX-COLOR_Y_MIN+1); i++) {
sum += hist[i];
hist[i] = sum;
}
for (int i=0; i<a; i++) {
int pixel = pixels[i];
pixels[i] = imlib_yuv_to_rgb((s * hist[COLOR_RGB565_TO_Y(pixel)-COLOR_Y_MIN]),
COLOR_RGB565_TO_U(pixel),
COLOR_RGB565_TO_V(pixel));
}
fb_free();
break;
}
default: {
break;
}
}
}
// A simple algorithm for correcting lens distortion.
// See http://www.tannerhelland.com/4743/simple-algorithm-correcting-lens-distortion/
void imlib_lens_corr(image_t *img, float strength, float zoom)
@ -701,23 +636,6 @@ void imlib_lens_corr(image_t *img, float strength, float zoom)
}
}
void imlib_mask_ellipse(image_t *img)
{
int h = img->w/2;
int v = img->h/2;
int a = h * h;
int b = v * v;
uint8_t *pixels = img->pixels;
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
if ((((x-h)*(x-h)*100) / a + ((y-v)*(y-v)*100) / b) > 100) {
pixels[y*img->w+x] = 0;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
int imlib_image_mean(image_t *src, int *r_mean, int *g_mean, int *b_mean)

View File

@ -711,8 +711,8 @@ extern const uint8_t g826_table[256];
// Image kernels
extern const int8_t kernel_gauss_3[9];
extern const int8_t kernel_gauss_5[25];
extern const int8_t kernel_laplacian_3[9];
extern const int8_t kernel_high_pass_3[9];
extern const int kernel_laplacian_3[9];
extern const int kernel_high_pass_3[9];
#define IM_RGB5652L(p) \
({ __typeof__ (p) _p = (p); \
@ -1131,9 +1131,6 @@ bool rectangle_subimg(image_t *img, rectangle_t *r, rectangle_t *r_out);
array_t *rectangle_merge(array_t *rectangles);
void rectangle_expand(rectangle_t *r, int x, int y);
/* Image Morphing */
void imlib_morph(image_t *img, const int ksize, const int8_t *krn, const float m, const int b);
/* Separable 2D convolution */
void imlib_sepconv3(image_t *img, const int8_t *krn, const float m, const int b);
@ -1141,14 +1138,6 @@ void imlib_sepconv3(image_t *img, const int8_t *krn, const float m, const int b)
int imlib_image_mean(image_t *src, int *r_mean, int *g_mean, int *b_mean);
int imlib_image_std(image_t *src); // grayscale only
/* Image Filtering */
void imlib_midpoint_filter(image_t *img, const int ksize, const int bias, bool threshold, int offset, bool invert, image_t *mask);
void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask);
void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask);
void imlib_median_filter(image_t *img, const int ksize, const int percentile, bool threshold, int offset, bool invert, image_t *mask);
void imlib_histeq(image_t *img);
void imlib_mask_ellipse(image_t *img);
/* Template Matching */
void imlib_midpoint_pool(image_t *img_i, image_t *img_o, int x_div, int y_div, const int bias);
void imlib_mean_pool(image_t *img_i, image_t *img_o, int x_div, int y_div);
@ -1244,6 +1233,13 @@ void imlib_min(image_t *img, const char *path, image_t *other, image_t *mask);
void imlib_max(image_t *img, const char *path, image_t *other, image_t *mask);
void imlib_difference(image_t *img, const char *path, image_t *other, image_t *mask);
void imlib_blend(image_t *img, const char *path, image_t *other, float alpha, image_t *mask);
// Filtering Functions
void imlib_histeq(image_t *img);
void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask);
void imlib_median_filter(image_t *img, const int ksize, float percentile, bool threshold, int offset, bool invert, image_t *mask);
void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask);
void imlib_midpoint_filter(image_t *img, const int ksize, float bias, bool threshold, int offset, bool invert, image_t *mask);
void imlib_morph(image_t *img, const int ksize, const int *krn, const float m, const int b, bool threshold, int offset, bool invert, image_t *mask);
// Image Correction
void imlib_logpolar_int(image_t *dst, image_t *src, rectangle_t *roi, bool linear, bool reverse); // helper/internal
void imlib_logpolar(image_t *img, bool linear, bool reverse);

View File

@ -1,85 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Mean filtering.
*
*/
#include <string.h>
#include "imlib.h"
#include "fb_alloc.h"
// krn_s == 0 -> 1x1 kernel
// krn_s == 1 -> 3x3 kernel
// ...
// krn_s == n -> ((n*2)+1)x((n*2)+1) kernel
void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask)
{
int n = ((ksize*2)+1)*((ksize*2)+1);
int brows = ksize + 1;
uint8_t *buffer = fb_alloc(img->w * brows * img->bpp);
if (IM_IS_GS(img)) {
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
int acc = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint8_t pixel = IM_GET_GS_PIXEL(img, x_k, y_j);
acc += pixel;
}
}
// We're writing into the buffer like if it were a window.
uint8_t pixel = acc/n;
if (mask && (!IM_GET_GS_PIXEL(mask, x, y))) pixel = IM_GET_GS_PIXEL(img, x, y);
buffer[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((pixel-offset)<IM_GET_GS_PIXEL(img, x, y))^invert) ? 255 : 0);
}
if (y>=ksize) {
memcpy(img->pixels+((y-ksize)*img->w),
buffer+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint8_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(img->pixels+(y*img->w),
buffer+((y%brows)*img->w),
img->w * sizeof(uint8_t));
}
} else {
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
int r_acc = 0;
int g_acc = 0;
int b_acc = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint16_t pixel = IM_GET_RGB565_PIXEL(img, x_k, y_j);
r_acc += IM_R565(pixel);
g_acc += IM_G565(pixel);
b_acc += IM_B565(pixel);
}
}
// We're writing into the buffer like if it were a window.
uint16_t pixel = IM_RGB565(r_acc/n, g_acc/n, b_acc/n);
if (mask && (!IM_GET_RGB565_PIXEL(mask, x, y))) pixel = IM_GET_RGB565_PIXEL(img, x, y);
((uint16_t *) buffer)[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((COLOR_RGB565_TO_Y(pixel)-offset)<COLOR_RGB565_TO_Y(IM_GET_RGB565_PIXEL(img, x, y)))^invert) ? 65535 : 0);
}
if (y>=ksize) {
memcpy(((uint16_t *) img->pixels)+((y-ksize)*img->w),
((uint16_t *) buffer)+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(((uint16_t *) img->pixels)+(y*img->w),
((uint16_t *) buffer)+((y%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
fb_free();
}

View File

@ -1,93 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Median filtering.
*
*/
#include <string.h>
#include "imlib.h"
#include "fb_alloc.h"
#include "fsort.h"
void imlib_median_filter(image_t *img, const int ksize, const int percentile, bool threshold, int offset, bool invert, image_t *mask)
{
int n = ((ksize*2)+1)*((ksize*2)+1);
int brows = ksize + 1;
uint8_t *buffer = fb_alloc(img->w * brows * img->bpp);
if (IM_IS_GS(img)) {
uint8_t data[n];
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
uint8_t *data_ptr = data;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint8_t pixel = IM_GET_GS_PIXEL(img, x_k, y_j);
*data_ptr++ = pixel;
}
}
fsort(data, n);
int median = data[percentile];
// We're writing into the buffer like if it were a window.
uint8_t pixel = median;
if (mask && (!IM_GET_GS_PIXEL(mask, x, y))) pixel = IM_GET_GS_PIXEL(img, x, y);
buffer[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((pixel-offset)<IM_GET_GS_PIXEL(img, x, y))^invert) ? 255 : 0);
}
if (y>=ksize) {
memcpy(img->pixels+((y-ksize)*img->w),
buffer+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint8_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(img->pixels+(y*img->w),
buffer+((y%brows)*img->w),
img->w * sizeof(uint8_t));
}
} else {
uint8_t r_data[n];
uint8_t g_data[n];
uint8_t b_data[n];
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
uint8_t *r_data_ptr = r_data;
uint8_t *g_data_ptr = g_data;
uint8_t *b_data_ptr = b_data;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint16_t pixel = IM_GET_RGB565_PIXEL(img, x_k, y_j);
*r_data_ptr++ = IM_R565(pixel);
*g_data_ptr++ = IM_G565(pixel);
*b_data_ptr++ = IM_B565(pixel);
}
}
fsort(r_data, n);
fsort(g_data, n);
fsort(b_data, n);
int r_median = r_data[percentile];
int g_median = g_data[percentile];
int b_median = b_data[percentile];
// We're writing into the buffer like if it were a window.
uint16_t pixel = IM_RGB565(r_median, g_median, b_median);
if (mask && (!IM_GET_RGB565_PIXEL(mask, x, y))) pixel = IM_GET_RGB565_PIXEL(img, x, y);
((uint16_t *) buffer)[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((COLOR_RGB565_TO_Y(pixel)-offset)<COLOR_RGB565_TO_Y(IM_GET_RGB565_PIXEL(img, x, y)))^invert) ? 65535 : 0);
}
if (y>=ksize) {
memcpy(((uint16_t *) img->pixels)+((y-ksize)*img->w),
((uint16_t *) buffer)+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(((uint16_t *) img->pixels)+(y*img->w),
((uint16_t *) buffer)+((y%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
fb_free();
}

View File

@ -1,97 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Min/Max/Midpoint Filtering.
*
*/
#include <string.h>
#include "imlib.h"
#include "fb_alloc.h"
// krn_s == 0 -> 1x1 kernel
// krn_s == 1 -> 3x3 kernel
// ...
// krn_s == n -> ((n*2)+1)x((n*2)+1) kernel
// bias == 0 to 256 -> 0.0 to 1.0 (0.0==min filter, 1.0==max filter)
void imlib_midpoint_filter(image_t *img, const int ksize, const int bias, bool threshold, int offset, bool invert, image_t *mask)
{
int min_bias = (256-bias);
int max_bias = bias;
int brows = ksize + 1;
uint8_t *buffer = fb_alloc(img->w * brows * img->bpp);
if (IM_IS_GS(img)) {
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
int min = 255, max = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint8_t pixel = IM_GET_GS_PIXEL(img, x_k, y_j);
min = IM_MIN(min, pixel);
max = IM_MAX(max, pixel);
}
}
// We're writing into the buffer like if it were a window.
int pixel = ((min*min_bias)+(max*max_bias))>>8;
if (mask && (!IM_GET_GS_PIXEL(mask, x, y))) pixel = IM_GET_GS_PIXEL(img, x, y);
buffer[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((pixel-offset)<IM_GET_GS_PIXEL(img, x, y))^invert) ? 255 : 0);
}
if (y>=ksize) {
memcpy(img->pixels+((y-ksize)*img->w),
buffer+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint8_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(img->pixels+(y*img->w),
buffer+((y%brows)*img->w),
img->w * sizeof(uint8_t));
}
} else {
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
int r_min = 255, r_max = 0;
int g_min = 255, g_max = 0;
int b_min = 255, b_max = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint16_t pixel = IM_GET_RGB565_PIXEL(img, x_k, y_j);
int red = IM_R565(pixel);
int green = IM_G565(pixel);
int blue = IM_B565(pixel);
r_min = IM_MIN(r_min, red);
r_max = IM_MAX(r_max, red);
g_min = IM_MIN(g_min, green);
g_max = IM_MAX(g_max, green);
b_min = IM_MIN(b_min, blue);
b_max = IM_MAX(b_max, blue);
}
}
// We're writing into the buffer like if it were a window.
uint16_t pixel = IM_RGB565(((r_min*min_bias)+(r_max*max_bias))>>8,
((g_min*min_bias)+(g_max*max_bias))>>8,
((b_min*min_bias)+(b_max*max_bias))>>8);
if (mask && (!IM_GET_RGB565_PIXEL(mask, x, y))) pixel = IM_GET_RGB565_PIXEL(img, x, y);
((uint16_t *) buffer)[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((COLOR_RGB565_TO_Y(pixel)-offset)<COLOR_RGB565_TO_Y(IM_GET_RGB565_PIXEL(img, x, y)))^invert) ? 65535 : 0);
}
if (y>=ksize) {
memcpy(((uint16_t *) img->pixels)+((y-ksize)*img->w),
((uint16_t *) buffer)+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(((uint16_t *) img->pixels)+(y*img->w),
((uint16_t *) buffer)+((y%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
fb_free();
}

View File

@ -1,115 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Mode filtering.
*
*/
#include <string.h>
#include "imlib.h"
#include "fb_alloc.h"
// krn_s == 0 -> 1x1 kernel
// krn_s == 1 -> 3x3 kernel
// ...
// krn_s == n -> ((n*2)+1)x((n*2)+1) kernel
void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask)
{
int brows = ksize + 1;
uint8_t *buffer = fb_alloc(img->w * brows * img->bpp);
if (IM_IS_GS(img)) {
uint8_t *bins = fb_alloc(256);
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
memset(bins, 0, 256);
int mcount = 0, mode = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint8_t pixel = IM_GET_GS_PIXEL(img, x_k, y_j);
bins[pixel]++;
if(bins[pixel] > mcount) {
mcount = bins[pixel];
mode = pixel;
}
}
}
// We're writing into the buffer like if it were a window.
uint8_t pixel = mode;
if (mask && (!IM_GET_GS_PIXEL(mask, x, y))) pixel = IM_GET_GS_PIXEL(img, x, y);
buffer[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((pixel-offset)<IM_GET_GS_PIXEL(img, x, y))^invert) ? 255 : 0);
}
if (y>=ksize) {
memcpy(img->pixels+((y-ksize)*img->w),
buffer+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint8_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(img->pixels+(y*img->w),
buffer+((y%brows)*img->w),
img->w * sizeof(uint8_t));
}
fb_free();
} else {
uint8_t *r_bins = fb_alloc(32);
uint8_t *g_bins = fb_alloc(64);
uint8_t *b_bins = fb_alloc(32);
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
memset(r_bins, 0, 32);
memset(g_bins, 0, 64);
memset(b_bins, 0, 32);
int r_mcount = 0, r_mode = 0;
int g_mcount = 0, g_mode = 0;
int b_mcount = 0, b_mode = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint16_t pixel = IM_GET_RGB565_PIXEL(img, x_k, y_j);
int red = IM_R565(pixel);
int green = IM_G565(pixel);
int blue = IM_B565(pixel);
r_bins[red]++;
if(r_bins[red] > r_mcount) {
r_mcount = r_bins[red];
r_mode = red;
}
g_bins[green]++;
if(g_bins[green] > g_mcount) {
g_mcount = g_bins[green];
g_mode = green;
}
b_bins[blue]++;
if(b_bins[blue] > b_mcount) {
b_mcount = b_bins[blue];
b_mode = blue;
}
}
}
// We're writing into the buffer like if it were a window.
uint16_t pixel = IM_RGB565(r_mode, g_mode, b_mode);
if (mask && (!IM_GET_RGB565_PIXEL(mask, x, y))) pixel = IM_GET_RGB565_PIXEL(img, x, y);
((uint16_t *) buffer)[((y%brows)*img->w)+x] = (!threshold) ? pixel : ((((COLOR_RGB565_TO_Y(pixel)-offset)<COLOR_RGB565_TO_Y(IM_GET_RGB565_PIXEL(img, x, y)))^invert) ? 65535 : 0);
}
if (y>=ksize) {
memcpy(((uint16_t *) img->pixels)+((y-ksize)*img->w),
((uint16_t *) buffer)+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(((uint16_t *) img->pixels)+(y*img->w),
((uint16_t *) buffer)+((y%brows)*img->w),
img->w * sizeof(uint16_t));
}
fb_free();
fb_free();
fb_free();
}
fb_free();
}

View File

@ -1,93 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Generic image convolution function.
*
*/
#include <string.h>
#include "imlib.h"
#include "fb_alloc.h"
// krn_s == 0 -> 1x1 kernel
// krn_s == 1 -> 3x3 kernel
// ...
// krn_s == n -> ((n*2)+1)x((n*2)+1) kernel
//
// pixel = (krn_sum / m) + b
//
// http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
void imlib_morph(image_t *img, const int ksize, const int8_t *krn, const float m, const int b)
{
int brows = ksize + 1;
uint8_t *buffer = fb_alloc(img->w * brows * img->bpp);
if (IM_IS_GS(img)) {
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
int acc = 0;
int ptr = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
acc += krn[ptr++] * IM_GET_GS_PIXEL(img, x_k, y_j);
}
}
acc = (acc * m) + b; // scale, offset, and clamp
acc = IM_MAX(IM_MIN(acc, IM_MAX_GS), 0);
// We're writing into the buffer like if it were a window.
buffer[((y%brows)*img->w)+x] = acc;
}
if (y>=ksize) {
memcpy(img->pixels+((y-ksize)*img->w),
buffer+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint8_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(img->pixels+(y*img->w),
buffer+((y%brows)*img->w),
img->w * sizeof(uint8_t));
}
} else {
for (int y=0; y<img->h; y++) {
for (int x=0; x<img->w; x++) {
int r_acc = 0;
int g_acc = 0;
int b_acc = 0;
int ptr = 0;
for (int j=-ksize; j<=ksize; j++) {
for (int k=-ksize; k<=ksize; k++) {
int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1);
int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1);
const uint16_t pixel = IM_GET_RGB565_PIXEL(img, x_k, y_j);
r_acc += krn[ptr] * IM_R565(pixel);
g_acc += krn[ptr] * IM_G565(pixel);
b_acc += krn[ptr++] * IM_B565(pixel);
}
}
r_acc = (r_acc * m) + b; // scale, offset, and clamp
r_acc = IM_MAX(IM_MIN(r_acc, IM_MAX_R5), 0);
g_acc = (g_acc * m) + b; // scale, offset, and clamp
g_acc = IM_MAX(IM_MIN(g_acc, IM_MAX_G6), 0);
b_acc = (b_acc * m) + b; // scale, offset, and clamp
b_acc = IM_MAX(IM_MIN(b_acc, IM_MAX_B5), 0);
// We're writing into the buffer like if it were a window.
((uint16_t *) buffer)[((y%brows)*img->w)+x] = IM_RGB565(r_acc, g_acc, b_acc);
}
if (y>=ksize) {
memcpy(((uint16_t *) img->pixels)+((y-ksize)*img->w),
((uint16_t *) buffer)+(((y-ksize)%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
for (int y=img->h-ksize; y<img->h; y++) {
memcpy(((uint16_t *) img->pixels)+(y*img->w),
((uint16_t *) buffer)+((y%brows)*img->w),
img->w * sizeof(uint16_t));
}
}
fb_free();
}

View File

@ -1571,8 +1571,8 @@ STATIC mp_obj_t py_image_blend(uint n_args, const mp_obj_t *args, mp_map_t *kw_a
image_t *arg_img =
py_helper_arg_to_image_mutable(args[0]);
float arg_alpha =
IM_MAX(IM_MIN(py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha),
128), 256), 0) / 256.0f;
py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 128) / 256.0f;
PY_ASSERT_TRUE_MSG((0 <= arg_alpha) && (arg_alpha <= 1), "Error: 0 <= alpha <= 256!");
image_t *arg_msk =
py_helper_keyword_to_image_mutable_mask(n_args, args, 3, kw_args);
@ -1631,11 +1631,9 @@ STATIC mp_obj_t py_image_median(uint n_args, const mp_obj_t *args, mp_map_t *kw_
py_helper_arg_to_image_mutable(args[0]);
int arg_ksize =
py_helper_arg_to_ksize(args[1]);
PY_ASSERT_TRUE_MSG(arg_ksize <= 2, "KernelSize must be <= 2!");
int n = py_helper_ksize_to_n(arg_ksize);
int arg_percentile =
IM_MAX(IM_MIN(py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_percentile),
0.5) * n, n - 1), 0);
float arg_percentile =
py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_percentile), 0.5f);
PY_ASSERT_TRUE_MSG((0 <= arg_percentile) && (arg_percentile <= 1), "Error: 0 <= percentile <= 1!");
bool arg_threshold =
py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset =
@ -1680,9 +1678,9 @@ STATIC mp_obj_t py_image_midpoint(uint n_args, const mp_obj_t *args, mp_map_t *k
py_helper_arg_to_image_mutable(args[0]);
int arg_ksize =
py_helper_arg_to_ksize(args[1]);
int arg_bias =
IM_MAX(IM_MIN(py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_bias),
0.5) * 256, 256), 0);
float arg_bias =
py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_bias), 0.5f);
PY_ASSERT_TRUE_MSG((0 <= arg_bias) && (arg_bias <= 1), "Error: 0 <= bias <= 1!");
bool arg_threshold =
py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset =
@ -1705,19 +1703,18 @@ STATIC mp_obj_t py_image_morph(uint n_args, const mp_obj_t *args, mp_map_t *kw_a
py_helper_arg_to_image_mutable(args[0]);
int arg_ksize =
py_helper_arg_to_ksize(args[1]);
int n = py_helper_ksize_to_n(arg_ksize);
mp_obj_t *krn;
mp_obj_get_array_fixed_n(args[2], n, &krn);
int8_t arg_krn[n];
int arg_krn[n];
int arg_m = 0;
for (int i = 0; i < n; i++) {
int value = mp_obj_get_int(krn[i]);
PY_ASSERT_FALSE_MSG((value < -128) || (127 < value),
"Kernel Values must be between [-128:127] inclusive!");
arg_krn[i] = value;
arg_m += arg_krn[i];
arg_krn[i] = mp_obj_get_int(krn[i]);
arg_m += abs(arg_krn[i]);
}
if (arg_m == 0) {
@ -1728,9 +1725,17 @@ STATIC mp_obj_t py_image_morph(uint n_args, const mp_obj_t *args, mp_map_t *kw_a
py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mul), 1.0f / arg_m);
float arg_add =
py_helper_keyword_float(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_add), 0.0f);
bool arg_threshold =
py_helper_keyword_int(n_args, args, 5, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset =
py_helper_keyword_int(n_args, args, 6, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_offset), 0);
bool arg_invert =
py_helper_keyword_int(n_args, args, 7, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
image_t *arg_msk =
py_helper_keyword_to_image_mutable_mask(n_args, args, 8, kw_args);
fb_alloc_mark();
imlib_morph(arg_img, arg_ksize, arg_krn, arg_mul, arg_add);
imlib_morph(arg_img, arg_ksize, arg_krn, arg_mul, arg_add, arg_threshold, arg_offset, arg_invert, arg_msk);
fb_alloc_free_till_mark();
return args[0];
}
@ -1741,23 +1746,107 @@ STATIC mp_obj_t py_image_gaussian(uint n_args, const mp_obj_t *args, mp_map_t *k
image_t *arg_img =
py_helper_arg_to_image_mutable(args[0]);
int arg_ksize =
mp_obj_get_int(args[1]);
PY_ASSERT_TRUE_MSG((arg_ksize == 3 || arg_ksize == 5), "KernelSize must be 3 or 5!");
py_helper_arg_to_ksize(args[1]);
if (arg_ksize == 3) {
fb_alloc_mark();
imlib_morph(arg_img, 1, kernel_gauss_3, 1.0f/16.0f, 0.0f);
fb_alloc_free_till_mark();
} else if (arg_ksize == 5) {
fb_alloc_mark();
imlib_morph(arg_img, 2, kernel_gauss_5, 1.0f/256.0f, 0.0f);
fb_alloc_free_till_mark();
int k_2 = arg_ksize * 2;
int n = k_2 + 1;
int pascal[n];
pascal[0] = 1;
for (int i = 0; i < k_2; i++) { // Compute a row of pascal's triangle.
pascal[i + 1] = (pascal[i] * (k_2 - i)) / (i + 1);
}
int arg_krn[n * n];
int arg_m = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
arg_krn[(i * n) + j] = pascal[i] * pascal[j];
arg_m += abs(arg_krn[(i * n) + j]);
}
}
bool arg_unsharp =
py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_unsharp), false);
if (arg_unsharp) arg_krn[((n/2)*n)+(n/2)] -= arg_m * 2;
if (arg_unsharp) arg_m = -arg_m;
float arg_mul =
py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mul), 1.0f / arg_m);
float arg_add =
py_helper_keyword_float(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_add), 0.0f);
bool arg_threshold =
py_helper_keyword_int(n_args, args, 5, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset =
py_helper_keyword_int(n_args, args, 6, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_offset), 0);
bool arg_invert =
py_helper_keyword_int(n_args, args, 7, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
image_t *arg_msk =
py_helper_keyword_to_image_mutable_mask(n_args, args, 8, kw_args);
fb_alloc_mark();
imlib_morph(arg_img, arg_ksize, arg_krn, arg_mul, arg_add, arg_threshold, arg_offset, arg_invert, arg_msk);
fb_alloc_free_till_mark();
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_gaussian_obj, 2, py_image_gaussian);
STATIC mp_obj_t py_image_laplacian(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
image_t *arg_img =
py_helper_arg_to_image_mutable(args[0]);
int arg_ksize =
py_helper_arg_to_ksize(args[1]);
int k_2 = arg_ksize * 2;
int n = k_2 + 1;
int pascal[n];
pascal[0] = 1;
for (int i = 0; i < k_2; i++) { // Compute a row of pascal's triangle.
pascal[i + 1] = (pascal[i] * (k_2 - i)) / (i + 1);
}
int arg_krn[n * n];
int arg_m = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
arg_krn[(i * n) + j] = -(pascal[i] * pascal[j]);
arg_m += abs(arg_krn[(i * n) + j]);
}
}
bool arg_sharpen =
py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_sharpen), false);
arg_krn[((n/2)*n)+(n/2)] += arg_m + arg_sharpen;
arg_m = 1;
float arg_mul =
py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mul), 1.0f / arg_m);
float arg_add =
py_helper_keyword_float(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_add), 0.0f);
bool arg_threshold =
py_helper_keyword_int(n_args, args, 5, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset =
py_helper_keyword_int(n_args, args, 6, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_offset), 0);
bool arg_invert =
py_helper_keyword_int(n_args, args, 7, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
image_t *arg_msk =
py_helper_keyword_to_image_mutable_mask(n_args, args, 8, kw_args);
fb_alloc_mark();
imlib_morph(arg_img, arg_ksize, arg_krn, arg_mul, arg_add, arg_threshold, arg_offset, arg_invert, arg_msk);
fb_alloc_free_till_mark();
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_laplacian_obj, 2, py_image_laplacian);
/////////////////////////
// Shadow Removal Methods
/////////////////////////
@ -1771,11 +1860,11 @@ STATIC mp_obj_t py_image_remove_shadows(uint n_args, const mp_obj_t *args)
if (n_args < 2) {
fb_alloc_mark();
imlib_remove_shadows(arg_img, NULL, NULL);
fb_alloc_mark();
fb_alloc_free_till_mark();
} else if (MP_OBJ_IS_STR(args[1])) {
fb_alloc_mark();
imlib_remove_shadows(arg_img, mp_obj_str_get_str(args[1]), NULL);
fb_alloc_mark();
fb_alloc_free_till_mark();
} else {
fb_alloc_mark();
imlib_remove_shadows(arg_img, NULL, py_helper_arg_to_image_color(args[1]));
@ -1893,13 +1982,6 @@ STATIC mp_obj_t py_image_rotation_corr(uint n_args, const mp_obj_t *args, mp_map
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_rotation_corr_obj, 1, py_image_rotation_corr);
#endif // IMLIB_ENABLE_ROTATION_CORR
STATIC mp_obj_t py_image_mask_ellipse(mp_obj_t img_obj)
{
imlib_mask_ellipse(py_helper_arg_to_image_grayscale(img_obj));
return img_obj;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_mask_ellipse_obj, py_image_mask_ellipse);
//////////////
// Get Methods
//////////////
@ -4674,7 +4756,10 @@ static const mp_rom_map_elem_t locals_dict_table[] = {
{MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&py_image_mode_obj)},
{MP_ROM_QSTR(MP_QSTR_midpoint), MP_ROM_PTR(&py_image_midpoint_obj)},
{MP_ROM_QSTR(MP_QSTR_morph), MP_ROM_PTR(&py_image_morph_obj)},
{MP_ROM_QSTR(MP_QSTR_blur), MP_ROM_PTR(&py_image_gaussian_obj)},
{MP_ROM_QSTR(MP_QSTR_gaussian), MP_ROM_PTR(&py_image_gaussian_obj)},
{MP_ROM_QSTR(MP_QSTR_gaussian_blur), MP_ROM_PTR(&py_image_gaussian_obj)},
{MP_ROM_QSTR(MP_QSTR_laplacian), MP_ROM_PTR(&py_image_laplacian_obj)},
/* Shadow Removal Methods */
#ifdef IMLIB_ENABLE_REMOVE_SHADOWS
{MP_ROM_QSTR(MP_QSTR_remove_shadows), MP_ROM_PTR(&py_image_remove_shadows_obj)},
@ -4696,7 +4781,6 @@ static const mp_rom_map_elem_t locals_dict_table[] = {
#ifdef IMLIB_ENABLE_ROTATION_CORR
{MP_ROM_QSTR(MP_QSTR_rotation_corr), MP_ROM_PTR(&py_image_rotation_corr_obj)},
#endif
{MP_ROM_QSTR(MP_QSTR_mask_ellipse), MP_ROM_PTR(&py_image_mask_ellipse_obj)},
/* Get Methods */
#ifdef IMLIB_ENABLE_GET_SIMILARITY
{MP_ROM_QSTR(MP_QSTR_get_similarity), MP_ROM_PTR(&py_image_get_similarity_obj)},

View File

@ -38,11 +38,6 @@ Q(width)
Q(height)
Q(format)
Q(size)
Q(morph)
Q(midpoint)
Q(mean)
Q(mode)
Q(median)
Q(gaussian)
Q(midpoint_pool)
Q(midpoint_pooled)
@ -62,12 +57,6 @@ Q(find_hog)
Q(cmp_lbp)
Q(quality)
Q(roi)
Q(offset)
Q(threshold)
Q(mul)
Q(add)
Q(bias)
Q(percentile)
Q(normalized)
Q(filter_outliers)
Q(scale_factor)
@ -411,7 +400,7 @@ Q(b_xnor)
// Erode
Q(erode)
// duplicate Q(threshold)
Q(threshold)
// duplicate Q(mask)
// Dilate
@ -428,7 +417,7 @@ Q(hmirror)
Q(vflip)
// Add Op
// duplicate Q(add)
Q(add)
// duplicate Q(mask)
// Sub Op
@ -437,9 +426,9 @@ Q(reverse)
// duplicate Q(mask)
// Mul Op
// duplicate Q(mul)
Q(mul)
// duplicate Q(invert)
// duplicate Q(msk)
// duplicate Q(mask)
// Div Op
Q(div)
@ -463,13 +452,69 @@ Q(blend)
// duplicate Q(alpha)
// duplicate Q(mask)
// Linear Polar
Q(linpolar)
// duplicate Q(reverse)
// Histogram Equalization
Q(histeq)
// Log Polar
Q(logpolar)
// duplicate Q(reverse)
// Mean
Q(mean)
// duplicate Q(threshold)
Q(offset)
// duplicate Q(invert)
// duplicate Q(mask)
// Median
Q(median)
Q(percentile)
// duplicate Q(threshold)
// duplicate Q(offset)
// duplicate Q(invert)
// duplicate Q(mask)
// Mode
Q(mode)
// duplicate Q(threshold)
// duplicate Q(offset)
// duplicate Q(invert)
// duplicate Q(mask)
// Midpoint
Q(midpoint)
Q(bias)
// duplicate Q(threshold)
// duplicate Q(offset)
// duplicate Q(invert)
// duplicate Q(mask)
// Moprh
Q(morph)
// duplicate Q(mul)
// duplicate Q(add)
// duplicate Q(threshold)
// duplicate Q(offset)
// duplicate Q(invert)
// duplicate Q(mask)
// Gaussian Blur
Q(blur)
Q(gaussian)
Q(gaussian_blur)
Q(unsharp)
// duplicate Q(mul)
// duplicate Q(add)
// duplicate Q(threshold)
// duplicate Q(offset)
// duplicate Q(invert)
// duplicate Q(mask)
// Laplacian
Q(laplacian)
Q(sharpen)
// duplicate Q(mul)
// duplicate Q(add)
// duplicate Q(threshold)
// duplicate Q(offset)
// duplicate Q(invert)
// duplicate Q(mask)
// Shadow Removal
Q(remove_shadows)
@ -480,8 +525,13 @@ Q(chrominvar)
// Illumination Invariant
Q(illuminvar)
// Histogram Equalization
Q(histeq)
// Linear Polar
Q(linpolar)
// duplicate Q(reverse)
// Log Polar
Q(logpolar)
// duplicate Q(reverse)
// Lens Correction
Q(lens_corr)

View File

@ -0,0 +1,21 @@
# Blur Filter Example
#
# This example shows off using the guassian filter to blur images.
import sensor, image, time
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QQVGA) # or sensor.QVGA (or others)
sensor.skip_frames(time = 2000) # Let new settings take affect.
clock = time.clock() # Tracks FPS.
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
# Run the kernel on every pixel of the image.
img.gaussian(1)
print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
# connected to your computer. The FPS should increase once disconnected.

View File

@ -1,40 +0,0 @@
# Edge Detection Example:
#
# This example demonstrates using the morph function on an image to do edge
# detection and then thresholding and filtering that image afterwards.
import sensor, image, time
kernel_size = 1 # kernel width = (size*2)+1, kernel height = (size*2)+1
kernel = [-1, -1, -1,\
-1, +8, -1,\
-1, -1, -1]
# This is a high pass filter kernel. see here for more kernels:
# http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
thresholds = [(100, 255)] # grayscale thresholds
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QQVGA) # or sensor.QVGA (or others)
sensor.skip_frames(time = 2000) # Let new settings take affect.
clock = time.clock() # Tracks FPS.
# On the OV7725 sensor, edge detection can be enhanced
# significantly by setting the sharpness/edge registers.
# Note: This will be implemented as a function later.
if (sensor.get_id() == sensor.OV7725):
sensor.__write_reg(0xAC, 0xDF)
sensor.__write_reg(0x8F, 0xFF)
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
img.morph(kernel_size, kernel)
img.binary(thresholds)
# Erode pixels with less than 2 neighbors using a 3x3 image kernel
img.erode(1, threshold = 2)
print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
# connected to your computer. The FPS should increase once disconnected.

View File

@ -0,0 +1,21 @@
# Edge Filter Example
#
# This example shows off using the laplacian filter to detect edges.
import sensor, image, time
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QQVGA) # or sensor.QVGA (or others)
sensor.skip_frames(time = 2000) # Let new settings take affect.
clock = time.clock() # Tracks FPS.
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
# Run the kernel on every pixel of the image.
img.laplacian(1)
print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
# connected to your computer. The FPS should increase once disconnected.

View File

@ -1,15 +1,9 @@
# Sharpen Filter Example:
# Sharpen Filter Example
#
# This example demonstrates using morph to sharpen images.
# This example shows off using the laplacian filter to sharpen images.
import sensor, image, time
kernel_size = 1 # kernel width = (size*2)+1, kernel height = (size*2)+1
kernel = [-1, -1, -1,\
-1, +9, -1,\
-1, -1, -1]
# This is a sharpen filter kernel.
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QQVGA) # or sensor.QVGA (or others)
@ -21,7 +15,7 @@ while(True):
img = sensor.snapshot() # Take a picture and return the image.
# Run the kernel on every pixel of the image.
img.morph(kernel_size, kernel)
img.laplacian(1, sharpen=True)
print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
# connected to your computer. The FPS should increase once disconnected.

View File

@ -0,0 +1,21 @@
# Unsharp Filter Example
#
# This example shows off using the guassian filter to unsharp mask filter images.
import sensor, image, time
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QQVGA) # or sensor.QVGA (or others)
sensor.skip_frames(time = 2000) # Let new settings take affect.
clock = time.clock() # Tracks FPS.
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
# Run the kernel on every pixel of the image.
img.gaussian(1, unsharp=True)
print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
# connected to your computer. The FPS should increase once disconnected.