mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Merge pull request #112 from kwagyeman/master
Add mean, median, and mode filters.
This commit is contained in:
commit
749cd96e0c
@ -129,6 +129,7 @@ endif
|
||||
OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
|
||||
blob.o \
|
||||
fmath.o \
|
||||
fsort.o \
|
||||
haar.o \
|
||||
imlib.o \
|
||||
stats.o \
|
||||
@ -141,8 +142,10 @@ OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
|
||||
yuv_tab.o \
|
||||
rainbow_tab.o \
|
||||
rgb2rgb_tab.o \
|
||||
median.o \
|
||||
midpoint.o \
|
||||
mean.o \
|
||||
mode.o \
|
||||
median.o \
|
||||
point.o \
|
||||
rectangle.o \
|
||||
bmp.o \
|
||||
|
||||
@ -29,6 +29,7 @@ endif
|
||||
SRCS += $(addprefix img/, \
|
||||
blob.c \
|
||||
fmath.c \
|
||||
fsort.c \
|
||||
haar.c \
|
||||
imlib.c \
|
||||
stats.c \
|
||||
@ -41,8 +42,10 @@ SRCS += $(addprefix img/, \
|
||||
yuv_tab.c \
|
||||
rainbow_tab.c \
|
||||
rgb2rgb_tab.c \
|
||||
median.c \
|
||||
midpoint.c \
|
||||
mean.c \
|
||||
mode.c \
|
||||
median.c \
|
||||
point.c \
|
||||
rectangle.c \
|
||||
bmp.c \
|
||||
|
||||
303
src/omv/img/fsort.c
Normal file
303
src/omv/img/fsort.c
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Fast 9 and 25 bin sort.
|
||||
*
|
||||
*/
|
||||
#include "mdefs.h"
|
||||
#include "fsort.h"
|
||||
// http://pages.ripco.net/~jgamble/nw.html
|
||||
|
||||
ALWAYS_INLINE static void cmpswp(uint8_t *a, uint8_t *b)
|
||||
{
|
||||
if ((*b) < (*a)) {
|
||||
uint8_t tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// Network for N=9, using Best Known Arrangement.
|
||||
|
||||
// There are 25 comparators in this network,
|
||||
// grouped into 9 parallel operations.
|
||||
|
||||
// [[0,1],[3,4],[6,7]]
|
||||
// [[1,2],[4,5],[7,8]]
|
||||
// [[0,1],[3,4],[6,7],[2,5]]
|
||||
// [[0,3],[1,4],[5,8]]
|
||||
// [[3,6],[4,7],[2,5]]
|
||||
// [[0,3],[1,4],[5,7],[2,6]]
|
||||
// [[1,3],[4,6]]
|
||||
// [[2,4],[5,6]]
|
||||
// [[2,3]]
|
||||
|
||||
// This is graphed in 17 columns.
|
||||
|
||||
static void fsort9(uint8_t *data)
|
||||
{
|
||||
cmpswp(data+0, data+1);
|
||||
cmpswp(data+3, data+4);
|
||||
cmpswp(data+6, data+7);
|
||||
|
||||
cmpswp(data+1, data+2);
|
||||
cmpswp(data+4, data+5);
|
||||
cmpswp(data+7, data+8);
|
||||
|
||||
cmpswp(data+0, data+1);
|
||||
cmpswp(data+3, data+4);
|
||||
cmpswp(data+6, data+7);
|
||||
cmpswp(data+2, data+5);
|
||||
|
||||
cmpswp(data+0, data+3);
|
||||
cmpswp(data+1, data+4);
|
||||
cmpswp(data+5, data+8);
|
||||
|
||||
cmpswp(data+3, data+6);
|
||||
cmpswp(data+4, data+7);
|
||||
cmpswp(data+2, data+5);
|
||||
|
||||
cmpswp(data+0, data+3);
|
||||
cmpswp(data+1, data+4);
|
||||
cmpswp(data+5, data+7);
|
||||
cmpswp(data+2, data+6);
|
||||
|
||||
cmpswp(data+1, data+3);
|
||||
cmpswp(data+4, data+6);
|
||||
|
||||
cmpswp(data+2, data+4);
|
||||
cmpswp(data+5, data+6);
|
||||
|
||||
cmpswp(data+2, data+3);
|
||||
}
|
||||
|
||||
// Network for N=25, using Bose-Nelson Algorithm.
|
||||
|
||||
// There are 154 comparators in this network,
|
||||
// grouped into 27 parallel operations.
|
||||
|
||||
// [[1,2],[4,5],[7,8],[10,11],[13,14],[16,17],[19,20],[21,22],[23,24]]
|
||||
// [[0,2],[3,5],[6,8],[9,11],[12,14],[15,17],[18,20],[21,23],[22,24]]
|
||||
// [[0,1],[3,4],[2,5],[6,7],[9,10],[8,11],[12,13],[15,16],[14,17],[18,19],[22,23],[20,24]]
|
||||
// [[0,3],[1,4],[6,9],[7,10],[5,11],[12,15],[13,16],[18,22],[19,23],[17,24]]
|
||||
// [[2,4],[1,3],[8,10],[7,9],[0,6],[14,16],[13,15],[18,21],[20,23],[11,24]]
|
||||
// [[2,3],[8,9],[1,7],[4,10],[14,15],[19,21],[20,22],[16,23]]
|
||||
// [[2,8],[1,6],[3,9],[5,10],[20,21],[12,19],[15,22],[17,23]]
|
||||
// [[2,7],[4,9],[12,18],[13,20],[14,21],[16,22],[10,23]]
|
||||
// [[2,6],[5,9],[4,7],[14,20],[13,18],[17,22],[11,23]]
|
||||
// [[3,6],[5,8],[14,19],[16,20],[17,21],[0,13],[9,22]]
|
||||
// [[5,7],[4,6],[14,18],[15,19],[17,20],[0,12],[8,21],[10,22]]
|
||||
// [[5,6],[15,18],[17,19],[1,14],[7,20],[11,22]]
|
||||
// [[16,18],[2,15],[1,12],[6,19],[8,20],[11,21]]
|
||||
// [[17,18],[2,14],[3,16],[7,19],[10,20]]
|
||||
// [[2,13],[4,17],[5,18],[8,19],[11,20]]
|
||||
// [[2,12],[5,17],[4,16],[3,13],[9,19]]
|
||||
// [[5,16],[3,12],[4,14],[10,19]]
|
||||
// [[5,15],[4,12],[11,19],[9,16],[10,17]]
|
||||
// [[5,14],[8,15],[11,18],[10,16]]
|
||||
// [[5,13],[7,14],[11,17]]
|
||||
// [[5,12],[6,13],[8,14],[11,16]]
|
||||
// [[6,12],[8,13],[10,14],[11,15]]
|
||||
// [[7,12],[9,13],[11,14]]
|
||||
// [[8,12],[11,13]]
|
||||
// [[9,12]]
|
||||
// [[10,12]]
|
||||
// [[11,12]]
|
||||
|
||||
// This is graphed in 89 columns.
|
||||
|
||||
static void fsort25(uint8_t *data)
|
||||
{
|
||||
cmpswp(data+1, data+2);
|
||||
cmpswp(data+4, data+5);
|
||||
cmpswp(data+7, data+8);
|
||||
cmpswp(data+10, data+11);
|
||||
cmpswp(data+13, data+14);
|
||||
cmpswp(data+16, data+17);
|
||||
cmpswp(data+19, data+20);
|
||||
cmpswp(data+21, data+22);
|
||||
cmpswp(data+23, data+24);
|
||||
|
||||
cmpswp(data+0, data+2);
|
||||
cmpswp(data+3, data+5);
|
||||
cmpswp(data+6, data+8);
|
||||
cmpswp(data+9, data+11);
|
||||
cmpswp(data+12, data+14);
|
||||
cmpswp(data+15, data+17);
|
||||
cmpswp(data+18, data+20);
|
||||
cmpswp(data+21, data+23);
|
||||
cmpswp(data+22, data+24);
|
||||
|
||||
cmpswp(data+0, data+1);
|
||||
cmpswp(data+3, data+4);
|
||||
cmpswp(data+2, data+5);
|
||||
cmpswp(data+6, data+7);
|
||||
cmpswp(data+9, data+10);
|
||||
cmpswp(data+8, data+11);
|
||||
cmpswp(data+12, data+13);
|
||||
cmpswp(data+15, data+16);
|
||||
cmpswp(data+14, data+17);
|
||||
cmpswp(data+18, data+19);
|
||||
cmpswp(data+22, data+23);
|
||||
cmpswp(data+20, data+24);
|
||||
|
||||
cmpswp(data+0, data+3);
|
||||
cmpswp(data+1, data+4);
|
||||
cmpswp(data+6, data+9);
|
||||
cmpswp(data+7, data+10);
|
||||
cmpswp(data+5, data+11);
|
||||
cmpswp(data+12, data+15);
|
||||
cmpswp(data+13, data+16);
|
||||
cmpswp(data+18, data+22);
|
||||
cmpswp(data+19, data+23);
|
||||
cmpswp(data+17, data+24);
|
||||
|
||||
cmpswp(data+2, data+4);
|
||||
cmpswp(data+1, data+3);
|
||||
cmpswp(data+8, data+10);
|
||||
cmpswp(data+7, data+9);
|
||||
cmpswp(data+0, data+6);
|
||||
cmpswp(data+14, data+16);
|
||||
cmpswp(data+13, data+15);
|
||||
cmpswp(data+18, data+21);
|
||||
cmpswp(data+20, data+23);
|
||||
cmpswp(data+11, data+24);
|
||||
|
||||
cmpswp(data+2, data+3);
|
||||
cmpswp(data+8, data+9);
|
||||
cmpswp(data+1, data+7);
|
||||
cmpswp(data+4, data+10);
|
||||
cmpswp(data+14, data+15);
|
||||
cmpswp(data+19, data+21);
|
||||
cmpswp(data+20, data+22);
|
||||
cmpswp(data+16, data+23);
|
||||
|
||||
cmpswp(data+2, data+8);
|
||||
cmpswp(data+1, data+6);
|
||||
cmpswp(data+3, data+9);
|
||||
cmpswp(data+5, data+10);
|
||||
cmpswp(data+20, data+21);
|
||||
cmpswp(data+12, data+19);
|
||||
cmpswp(data+15, data+22);
|
||||
cmpswp(data+17, data+23);
|
||||
|
||||
cmpswp(data+2, data+7);
|
||||
cmpswp(data+4, data+9);
|
||||
cmpswp(data+12, data+18);
|
||||
cmpswp(data+13, data+20);
|
||||
cmpswp(data+14, data+21);
|
||||
cmpswp(data+16, data+22);
|
||||
cmpswp(data+10, data+23);
|
||||
|
||||
cmpswp(data+2, data+6);
|
||||
cmpswp(data+5, data+9);
|
||||
cmpswp(data+4, data+7);
|
||||
cmpswp(data+14, data+20);
|
||||
cmpswp(data+13, data+18);
|
||||
cmpswp(data+17, data+22);
|
||||
cmpswp(data+11, data+23);
|
||||
|
||||
cmpswp(data+3, data+6);
|
||||
cmpswp(data+5, data+8);
|
||||
cmpswp(data+14, data+19);
|
||||
cmpswp(data+16, data+20);
|
||||
cmpswp(data+17, data+21);
|
||||
cmpswp(data+0, data+13);
|
||||
cmpswp(data+9, data+22);
|
||||
|
||||
cmpswp(data+5, data+7);
|
||||
cmpswp(data+4, data+6);
|
||||
cmpswp(data+14, data+18);
|
||||
cmpswp(data+15, data+19);
|
||||
cmpswp(data+17, data+20);
|
||||
cmpswp(data+0, data+12);
|
||||
cmpswp(data+8, data+21);
|
||||
cmpswp(data+10, data+22);
|
||||
|
||||
cmpswp(data+5, data+6);
|
||||
cmpswp(data+15, data+18);
|
||||
cmpswp(data+17, data+19);
|
||||
cmpswp(data+1, data+14);
|
||||
cmpswp(data+7, data+20);
|
||||
cmpswp(data+11, data+22);
|
||||
|
||||
cmpswp(data+16, data+18);
|
||||
cmpswp(data+2, data+15);
|
||||
cmpswp(data+1, data+12);
|
||||
cmpswp(data+6, data+19);
|
||||
cmpswp(data+8, data+20);
|
||||
cmpswp(data+11, data+21);
|
||||
|
||||
cmpswp(data+17, data+18);
|
||||
cmpswp(data+2, data+14);
|
||||
cmpswp(data+3, data+16);
|
||||
cmpswp(data+7, data+19);
|
||||
cmpswp(data+10, data+20);
|
||||
|
||||
cmpswp(data+2, data+13);
|
||||
cmpswp(data+4, data+17);
|
||||
cmpswp(data+5, data+18);
|
||||
cmpswp(data+8, data+19);
|
||||
cmpswp(data+11, data+20);
|
||||
|
||||
cmpswp(data+2, data+12);
|
||||
cmpswp(data+5, data+17);
|
||||
cmpswp(data+4, data+16);
|
||||
cmpswp(data+3, data+13);
|
||||
cmpswp(data+9, data+19);
|
||||
|
||||
cmpswp(data+5, data+16);
|
||||
cmpswp(data+3, data+12);
|
||||
cmpswp(data+4, data+14);
|
||||
cmpswp(data+10, data+19);
|
||||
|
||||
cmpswp(data+5, data+15);
|
||||
cmpswp(data+4, data+12);
|
||||
cmpswp(data+11, data+19);
|
||||
cmpswp(data+9, data+16);
|
||||
cmpswp(data+10, data+17);
|
||||
|
||||
cmpswp(data+5, data+14);
|
||||
cmpswp(data+8, data+15);
|
||||
cmpswp(data+11, data+18);
|
||||
cmpswp(data+10, data+16);
|
||||
|
||||
cmpswp(data+5, data+13);
|
||||
cmpswp(data+7, data+14);
|
||||
cmpswp(data+11, data+17);
|
||||
|
||||
cmpswp(data+5, data+12);
|
||||
cmpswp(data+6, data+13);
|
||||
cmpswp(data+8, data+14);
|
||||
cmpswp(data+11, data+16);
|
||||
|
||||
cmpswp(data+6, data+12);
|
||||
cmpswp(data+8, data+13);
|
||||
cmpswp(data+10, data+14);
|
||||
cmpswp(data+11, data+15);
|
||||
|
||||
cmpswp(data+7, data+12);
|
||||
cmpswp(data+9, data+13);
|
||||
cmpswp(data+11, data+14);
|
||||
|
||||
cmpswp(data+8, data+12);
|
||||
cmpswp(data+11, data+13);
|
||||
|
||||
cmpswp(data+9, data+12);
|
||||
|
||||
cmpswp(data+10, data+12);
|
||||
|
||||
cmpswp(data+11, data+12);
|
||||
}
|
||||
|
||||
void fsort(uint8_t *data, int n)
|
||||
{
|
||||
switch(n) {
|
||||
case 1: return;
|
||||
case 9: fsort9(data); return;
|
||||
case 25: fsort25(data); return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
13
src/omv/img/fsort.h
Normal file
13
src/omv/img/fsort.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Fast 9 and 25 bin sort.
|
||||
*
|
||||
*/
|
||||
#ifndef __FSORT_H__
|
||||
#define __FSORT_H__
|
||||
#include <stdint.h>
|
||||
void fsort(uint8_t *data, int n);
|
||||
#endif /* __FSORT_H__ */
|
||||
@ -462,7 +462,9 @@ void imlib_statistics(image_t *img, rectangle_t *r, statistics_t *out);
|
||||
|
||||
/* Image Filtering */
|
||||
void imlib_midpoint_filter(image_t *img, const int ksize, const int bias);
|
||||
void imlib_median_filter(image_t *src, int r);
|
||||
void imlib_mean_filter(image_t *img, const int ksize);
|
||||
void imlib_mode_filter(image_t *img, const int ksize);
|
||||
void imlib_median_filter(image_t *img, const int ksize, const int percentile);
|
||||
|
||||
/* Clustering functions */
|
||||
array_t *cluster_kmeans(array_t *points, int k);
|
||||
|
||||
81
src/omv/img/mean.c
Normal file
81
src/omv/img/mean.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
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.
|
||||
buffer[((y%brows)*img->w)+x] = acc/n;
|
||||
}
|
||||
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++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
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 *) buffer)[((y%brows)*img->w)+x] = IM_RGB565(r_acc/n, g_acc/n, b_acc/n);
|
||||
}
|
||||
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();
|
||||
}
|
||||
@ -3,176 +3,93 @@
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* O(N) median_gs filter with histograms.
|
||||
* Median filtering.
|
||||
*
|
||||
*/
|
||||
#include <arm_math.h>
|
||||
#include <string.h>
|
||||
#include "imlib.h"
|
||||
#include "fb_alloc.h"
|
||||
#include "fsort.h"
|
||||
|
||||
typedef struct {
|
||||
int n;
|
||||
union {
|
||||
int h[256];
|
||||
struct {
|
||||
int r[32];
|
||||
int g[64];
|
||||
int b[32];
|
||||
};
|
||||
};
|
||||
} histo_t;
|
||||
|
||||
void del_pixels(image_t * im, int row, int col, int size, histo_t *h)
|
||||
void imlib_median_filter(image_t *img, const int ksize, const int percentile)
|
||||
{
|
||||
if (im->bpp==1) {
|
||||
for (int i = row - size; i <= row + size && i < im->h; i++) {
|
||||
if (i < 0) continue;
|
||||
h->h[im->pixels[i*im->w+col]]--;
|
||||
h->n--;
|
||||
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++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
const uint8_t pixel = IM_GET_GS_PIXEL(img, x+k, y+j);
|
||||
*data_ptr++ = pixel;
|
||||
} else {
|
||||
*data_ptr++ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
fsort(data, n);
|
||||
int median = data[percentile];
|
||||
// We're writing into the buffer like if it were a window.
|
||||
buffer[((y%brows)*img->w)+x] = median;
|
||||
}
|
||||
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 i = row - size; i <= row + size && i < im->h; i++) {
|
||||
if (i < 0) continue;
|
||||
uint16_t c = ((uint16_t*)im->pixels)[i*im->w+col];
|
||||
h->r[IM_R565(c)]--;
|
||||
h->g[IM_G565(c)]--;
|
||||
h->b[IM_B565(c)]--;
|
||||
h->n--;
|
||||
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++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
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);
|
||||
} else {
|
||||
*r_data_ptr++ = 0;
|
||||
*g_data_ptr++ = 0;
|
||||
*b_data_ptr++ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 *) buffer)[((y%brows)*img->w)+x] = IM_RGB565(r_median, g_median, b_median);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_pixels(image_t * im, int row, int col, int size, histo_t *h)
|
||||
{
|
||||
if (im->bpp==1) {
|
||||
for (int i = row - size; i <= row + size && i < im->h; i++) {
|
||||
if (i < 0) continue;
|
||||
h->h[im->pixels[i*im->w+col]]++;
|
||||
h->n++;
|
||||
}
|
||||
} else {
|
||||
for (int i = row - size; i <= row + size && i < im->h; i++) {
|
||||
if (i < 0) continue;
|
||||
uint16_t c = ((uint16_t*)im->pixels)[i*im->w+col];
|
||||
h->r[IM_R565(c)]++;
|
||||
h->g[IM_G565(c)]++;
|
||||
h->b[IM_B565(c)]++;
|
||||
h->n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_histo(image_t *im, int row, int size, histo_t *h)
|
||||
{
|
||||
memset(h, 0, sizeof(histo_t));
|
||||
for (int j = 0; j < size && j < im->w; j++) {
|
||||
add_pixels(im, row, j, size, h);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t median_gs(histo_t *h)
|
||||
{
|
||||
int i, n;
|
||||
for (i=0, n=h->n/2; (n -= h->h[i]) >0; i++) {
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
uint16_t median_rgb(histo_t *h)
|
||||
{
|
||||
int rx = h->n/2;
|
||||
int gx = h->n/2;
|
||||
int bx = h->n/2;
|
||||
uint16_t r=0, g=0, b=0;
|
||||
|
||||
for (int i=0; (rx>0 || gx>0 || bx>0); i++) {
|
||||
if (rx > 0) {
|
||||
rx -= h->r[i];
|
||||
r = i;
|
||||
}
|
||||
|
||||
if (gx > 0) {
|
||||
gx -= h->g[i];
|
||||
g = i;
|
||||
}
|
||||
|
||||
if (bx > 0) {
|
||||
bx -= h->b[i];
|
||||
b = i;
|
||||
}
|
||||
}
|
||||
|
||||
return IM_RGB565(r, g, b);
|
||||
}
|
||||
|
||||
void median_filter_rgb(image_t *in, int size)
|
||||
{
|
||||
int k_rows = 24;
|
||||
histo_t *h = fb_alloc(sizeof(*h));
|
||||
uint16_t *dst = (uint16_t*) in->data;
|
||||
uint16_t *data = fb_alloc(in->w * k_rows * sizeof(*data));
|
||||
|
||||
for (int row=0; row<in->h; row++) {
|
||||
init_histo(in, row, size, h);
|
||||
data[(row%k_rows)*in->w+size] = median_rgb(h);
|
||||
|
||||
for (int col=0; col<in->w; col++) {
|
||||
del_pixels(in, row, col - size, size, h);
|
||||
add_pixels(in, row, col + size, size, h);
|
||||
data[(row%k_rows)*in->w+col] = median_rgb(h);
|
||||
}
|
||||
|
||||
if ((row+1)%k_rows==0) {
|
||||
memcpy(dst, data, (in->w*(k_rows-size)*2));
|
||||
dst += (in->w*(k_rows-size));
|
||||
}
|
||||
|
||||
if ((row>size && (row+1)%k_rows==size) || row+1==in->h){
|
||||
memcpy(dst, data+in->w*(k_rows-size), (in->w*size*2));
|
||||
dst += (in->w*size);
|
||||
}
|
||||
|
||||
}
|
||||
fb_free_all();
|
||||
}
|
||||
|
||||
void median_filter_gs(image_t *in, int size)
|
||||
{
|
||||
int k_rows = 24;
|
||||
histo_t *h = fb_alloc(sizeof(*h));
|
||||
uint8_t *dst = (uint8_t*) in->data;
|
||||
uint8_t *data = fb_alloc(in->w * k_rows * sizeof(*data));
|
||||
|
||||
for (int row=0; row<in->h; row++) {
|
||||
init_histo(in, row, size, h);
|
||||
data[(row%k_rows)*in->w+size] = median_gs(h);
|
||||
|
||||
for (int col=0; col<in->w; col++) {
|
||||
del_pixels(in, row, col - size, size, h);
|
||||
add_pixels(in, row, col + size, size, h);
|
||||
data[(row%k_rows)*in->w+col] = median_gs(h);
|
||||
}
|
||||
|
||||
if ((row+1)%k_rows==0) {
|
||||
memcpy(dst, data, (in->w*(k_rows-size)));
|
||||
dst += (in->w*(k_rows-size));
|
||||
}
|
||||
|
||||
if ((row>size && (row+1)%k_rows==size) || row+1==in->h){
|
||||
memcpy(dst, data+in->w*(k_rows-size), (in->w*size));
|
||||
dst += (in->w*size);
|
||||
}
|
||||
|
||||
}
|
||||
fb_free_all();
|
||||
}
|
||||
|
||||
void imlib_median_filter(image_t *in, int size)
|
||||
{
|
||||
if (in->bpp == 1) {
|
||||
//median_filter_test(in, size);
|
||||
median_filter_gs(in, size);
|
||||
} else {
|
||||
median_filter_rgb(in, size);
|
||||
}
|
||||
fb_free();
|
||||
}
|
||||
|
||||
111
src/omv/img/mode.c
Normal file
111
src/omv/img/mode.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
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.
|
||||
buffer[((y%brows)*img->w)+x] = mode;
|
||||
}
|
||||
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++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
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 *) buffer)[((y%brows)*img->w)+x] = IM_RGB565(r_mode, g_mode, b_mode);
|
||||
}
|
||||
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();
|
||||
}
|
||||
@ -710,6 +710,48 @@ static mp_obj_t py_image_midpoint(uint n_args, const mp_obj_t *args, mp_map_t *k
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_mean(mp_obj_t img_obj, mp_obj_t k_obj)
|
||||
{
|
||||
image_t *arg_img = py_image_cobj(img_obj);
|
||||
PY_ASSERT_FALSE_MSG(IM_IS_JPEG(arg_img),
|
||||
"Operation not supported on JPEG");
|
||||
|
||||
int arg_ksize = mp_obj_get_int(k_obj);
|
||||
PY_ASSERT_TRUE_MSG(arg_ksize >= 0, "Kernel Size must be >= 0");
|
||||
|
||||
imlib_mean_filter(arg_img, arg_ksize);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_mode(mp_obj_t img_obj, mp_obj_t k_obj)
|
||||
{
|
||||
image_t *arg_img = py_image_cobj(img_obj);
|
||||
PY_ASSERT_FALSE_MSG(IM_IS_JPEG(arg_img),
|
||||
"Operation not supported on JPEG");
|
||||
|
||||
int arg_ksize = mp_obj_get_int(k_obj);
|
||||
PY_ASSERT_TRUE_MSG(arg_ksize >= 0, "Kernel Size must be >= 0");
|
||||
|
||||
imlib_mode_filter(arg_img, arg_ksize);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_median(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
{
|
||||
image_t *arg_img = py_image_cobj(args[0]);
|
||||
PY_ASSERT_FALSE_MSG(IM_IS_JPEG(arg_img),
|
||||
"Operation not supported on JPEG");
|
||||
|
||||
int arg_ksize = mp_obj_get_int(args[1]);
|
||||
PY_ASSERT_TRUE_MSG(arg_ksize >= 0, "Kernel Size must be >= 0");
|
||||
PY_ASSERT_TRUE_MSG(arg_ksize <= 2, "Kernel Size must be <= 2");
|
||||
|
||||
int n = ((arg_ksize*2)+1)*((arg_ksize*2)+1);
|
||||
int percentile = py_helper_lookup_float(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_percentile), 0.5) * n;
|
||||
imlib_median_filter(arg_img, arg_ksize, IM_MIN(IM_MAX(percentile, 0), n-1));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_scale(mp_obj_t image_obj, mp_obj_t size_obj)
|
||||
{
|
||||
int w,h;
|
||||
@ -860,17 +902,6 @@ static mp_obj_t py_image_histeq(mp_obj_t image_obj)
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_median(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
{
|
||||
// Read args
|
||||
image_t *image = py_image_cobj(args[0]);
|
||||
int ksize = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_size), 1);
|
||||
|
||||
// Call median filter
|
||||
imlib_median_filter(image, ksize);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_threshold(mp_obj_t image_obj, mp_obj_t color_list_obj, mp_obj_t threshold)
|
||||
{
|
||||
color_t *color;
|
||||
@ -1198,7 +1229,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_morph_obj, 3, py_image_morph);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_statistics_obj, 1, py_image_statistics);
|
||||
/* Image Filtering */
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_midpoint_obj, 2, py_image_midpoint);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_median_obj, 1, py_image_median);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_mean_obj, py_image_mean);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_mode_obj, py_image_mode);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_median_obj, 2, py_image_median);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_scale_obj, py_image_scale);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_scaled_obj, py_image_scaled);
|
||||
@ -1258,6 +1291,8 @@ static const mp_map_elem_t locals_dict_table[] = {
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_statistics), (mp_obj_t)&py_image_statistics_obj},
|
||||
/* Image Filtering */
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_midpoint), (mp_obj_t)&py_image_midpoint_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&py_image_mean_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_mode), (mp_obj_t)&py_image_mode_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_median), (mp_obj_t)&py_image_median_obj},
|
||||
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_scale), (mp_obj_t)&py_image_scale_obj},
|
||||
|
||||
@ -55,6 +55,8 @@ Q(difference)
|
||||
Q(morph)
|
||||
Q(statistics)
|
||||
Q(midpoint)
|
||||
Q(mean)
|
||||
Q(mode)
|
||||
Q(median)
|
||||
Q(kp_desc)
|
||||
Q(lbp_desc)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user