Merge pull request #311 from kwagyeman/shadows

Add single image shadow remover code.
This commit is contained in:
Ibrahim Abd Elkader 2018-01-29 18:03:25 +02:00 committed by GitHub
commit b98b24eb9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 825 additions and 434 deletions

View File

@ -181,6 +181,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
orb.o \
template.o \
phasecorrelation.o \
shadow_removal.o \
font.o \
jpeg.o \
lbp.o \

View File

@ -59,6 +59,7 @@ SRCS += $(addprefix img/, \
orb.c \
template.c \
phasecorrelation.c \
shadow_removal.c \
font.c \
jpeg.c \
lbp.c \

View File

@ -836,13 +836,9 @@ static void imlib_erode_dilate(image_t *img, int ksize, int threshold, int e_or_
int acc = e_or_d ? 0 : -1; // don't count center pixel...
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)) {
acc += !!IM_GET_GS_PIXEL(img, x+k, y+j);
} else { // outer pixels should not affect result.
acc += e_or_d ? 0 : 1;
// 1 for erode prevents acc from being lower.
// 0 for dilate prevents acc from being higher.
}
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 += !!IM_GET_GS_PIXEL(img, x_k, y_j);
}
}
if (!e_or_d) {
@ -876,13 +872,9 @@ static void imlib_erode_dilate(image_t *img, int ksize, int threshold, int e_or_
int acc = e_or_d ? 0 : -1; // don't count center pixel...
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)) {
acc += !!IM_GET_RGB565_PIXEL(img, x+k, y+j);
} else { // outer pixels should not affect result.
acc += e_or_d ? 0 : 1;
// 1 for erode prevents acc from being lower.
// 0 for dilate prevents acc from being higher.
}
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 += !!IM_GET_RGB565_PIXEL(img, x_k, y_j);
}
}
if (!e_or_d) {
@ -1074,323 +1066,8 @@ void imlib_min(image_t *img, const char *path, image_t *other)
imlib_image_operation(img, path, other, imlib_min_line_op, NULL);
}
// http://arma.sourceforge.net/shadows/
// http://dx.doi.org/10.1016/j.patcog.2011.10.001
#define imlib_remove_shadows_kernel_rank 1
#define imlib_remove_shadows_kernel_size ((imlib_remove_shadows_kernel_rank * 2) + 1)
typedef struct imlib_remove_shadows_line_op_state {
uint16_t *img_lines[imlib_remove_shadows_kernel_size];
uint16_t *other_lines[imlib_remove_shadows_kernel_size];
uint16_t *out_lines[imlib_remove_shadows_kernel_size];
int lines_processed;
} imlib_remove_shadows_line_op_state_t;
static void imlib_remove_shadows_sub_sub_line_op(image_t *img, int line, void *data, bool vflipped)
{
imlib_remove_shadows_line_op_state_t *state = (imlib_remove_shadows_line_op_state_t *) data;
state->lines_processed += 1;
if (state->lines_processed >= imlib_remove_shadows_kernel_size) {
int y = vflipped ? ((line - 1) + imlib_remove_shadows_kernel_size) : ((line + 1) - imlib_remove_shadows_kernel_size);
int index = y % imlib_remove_shadows_kernel_size;
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y), state->out_lines[index], img->w * sizeof(uint16_t));
}
}
static void imlib_remove_shadows_sub_line_op(image_t *img, int line, void *data, bool vflipped)
{
imlib_remove_shadows_line_op_state_t *state = (imlib_remove_shadows_line_op_state_t *) data;
if (state->lines_processed >= imlib_remove_shadows_kernel_rank) {
int y = vflipped ? (line + imlib_remove_shadows_kernel_rank) : (line - imlib_remove_shadows_kernel_rank);
int index = y % imlib_remove_shadows_kernel_size;
for (int x = 0, xx = img->w; x < xx; x++) {
int img_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->img_lines[index], x);
int img_r = COLOR_RGB565_TO_R8(img_pixel);
int img_g = COLOR_RGB565_TO_G8(img_pixel);
int img_b = COLOR_RGB565_TO_B8(img_pixel);
float img_v = IM_MAX(IM_MAX(img_r, img_g), img_b);
int other_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->other_lines[index], x);
int other_r = COLOR_RGB565_TO_R8(other_pixel);
int other_g = COLOR_RGB565_TO_G8(other_pixel);
int other_b = COLOR_RGB565_TO_B8(other_pixel);
float other_v = IM_MAX(IM_MAX(other_r, other_g), other_b);
float ratio = img_v / other_v;
if ((0.3f < ratio) && (ratio < 1.0f)) {
int minY = IM_MAX(y - imlib_remove_shadows_kernel_rank, 0);
int maxY = IM_MIN(y + imlib_remove_shadows_kernel_rank, img->h - 1);
int minX = IM_MAX(x - imlib_remove_shadows_kernel_rank, 0);
int maxX = IM_MIN(x + imlib_remove_shadows_kernel_rank, img->w - 1);
int windowArea = (maxX - minX + 1) * (maxY - minY + 1);
int hDiffSum = 0;
int sDiffSum = 0;
for (int k_y = minY; k_y <= maxY; k_y++) {
int k_index = k_y % imlib_remove_shadows_kernel_size;
for (int k_x = minX; k_x <= maxX; k_x++) {
int k_img_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->img_lines[k_index], k_x);
int k_img_r = COLOR_RGB565_TO_R8(k_img_pixel);
int k_img_g = COLOR_RGB565_TO_G8(k_img_pixel);
int k_img_b = COLOR_RGB565_TO_B8(k_img_pixel);
int k_img_cmax = IM_MAX(IM_MAX(k_img_r, k_img_g), k_img_b);
int k_img_cmin = IM_MAX(IM_MAX(k_img_r, k_img_g), k_img_b);
float k_img_cdel = k_img_cmax - k_img_cmin;
float k_img_h = 0;
float k_img_s = k_img_cmax ? (k_img_cdel / k_img_cmax) : 0;
int k_other_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->other_lines[k_index], k_x);
int k_other_r = COLOR_RGB565_TO_R8(k_other_pixel);
int k_other_g = COLOR_RGB565_TO_G8(k_other_pixel);
int k_other_b = COLOR_RGB565_TO_B8(k_other_pixel);
int k_other_cmax = IM_MAX(IM_MAX(k_other_r, k_other_g), k_other_b);
int k_other_cmin = IM_MAX(IM_MAX(k_other_r, k_other_g), k_other_b);
float k_other_cdel = k_other_cmax - k_other_cmin;
float k_other_h = 0;
float k_other_s = k_other_cmax ? (k_other_cdel / k_other_cmax) : 0;
if (k_img_cdel) {
if (k_img_cmax == k_img_r) {
k_img_h = ((k_img_g - k_img_b) / k_img_cdel) + 0;
} else if (k_img_cmax == k_img_g) {
k_img_h = ((k_img_b - k_img_r) / k_img_cdel) + 2;
} else if (k_img_cmax == k_img_b) {
k_img_h = ((k_img_r - k_img_g) / k_img_cdel) + 4;
}
k_img_h *= 60;
if (k_img_h < 0) k_img_h += 360.0;
}
if (k_other_cdel) {
if (k_other_cmax == k_other_r) {
k_other_h = ((k_other_g - k_other_b) / k_other_cdel) + 0;
} else if (k_other_cmax == k_other_g) {
k_other_h = ((k_other_b - k_other_r) / k_other_cdel) + 2;
} else if (k_other_cmax == k_other_b) {
k_other_h = ((k_other_r - k_other_g) / k_other_cdel) + 4;
}
k_other_h *= 60;
if (k_other_h < 0) k_other_h += 360.0;
}
int hDiff = abs(k_img_h - k_other_h);
hDiffSum += (hDiff > 90) ? (180 - hDiff) : hDiff;
sDiffSum += k_img_s - k_other_s;
}
}
bool hIsShadow = (hDiffSum / windowArea) < 48;
bool sIsShadow = (sDiffSum / windowArea) < 40;
IMAGE_PUT_RGB565_PIXEL_FAST(state->out_lines[index], x, (hIsShadow && sIsShadow) ? other_pixel : img_pixel);
} else {
IMAGE_PUT_RGB565_PIXEL_FAST(state->out_lines[index], x, img_pixel);
}
}
}
imlib_remove_shadows_sub_sub_line_op(img, line, data, vflipped);
}
static void imlib_remove_shadows_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped)
{
imlib_remove_shadows_line_op_state_t *state = (imlib_remove_shadows_line_op_state_t *) data;
memcpy(state->img_lines[line % imlib_remove_shadows_kernel_size],
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line), img->w * sizeof(uint16_t));
memcpy(state->other_lines[line % imlib_remove_shadows_kernel_size],
(uint16_t *) other, img->w * sizeof(uint16_t));
imlib_remove_shadows_sub_line_op(img, line, data, vflipped);
if (state->lines_processed == img->h) {
for (int i = 0; i < imlib_remove_shadows_kernel_rank; i++) {
line += vflipped ? -1 : 1;
imlib_remove_shadows_sub_line_op(img, line, data, vflipped);
}
for (int i = 0; i < imlib_remove_shadows_kernel_rank; i++) {
line += vflipped ? -1 : 1;
imlib_remove_shadows_sub_sub_line_op(img, line, data, vflipped);
}
}
}
void imlib_remove_shadows(image_t *img, const char *path, image_t *other)
{
imlib_remove_shadows_line_op_state_t state;
for (int i = 0; i < imlib_remove_shadows_kernel_size; i++) {
state.img_lines[i] = fb_alloc(img->w * sizeof(uint16_t));
state.other_lines[i] = fb_alloc(img->w * sizeof(uint16_t));
state.out_lines[i] = fb_alloc(img->w * sizeof(uint16_t));
}
state.lines_processed = 0;
imlib_image_operation(img, path, other, imlib_remove_shadows_line_op, (void *) &state);
for (int i = 0; i < imlib_remove_shadows_kernel_size; i++) {
fb_free();
fb_free();
fb_free();
}
}
#undef imlib_remove_shadows_kernel_size
#undef imlib_remove_shadows_kernel_rank
////////////////////////////////////////////////////////////////////////////////
void imlib_chrominvar(image_t *img)
{
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
break;
}
case IMAGE_BPP_GRAYSCALE: {
break;
}
case IMAGE_BPP_RGB565: {
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
for (int x = 0, xx = img->w; x < xx; x++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x);
float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)];
float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)];
float b_lin = xyz_table[COLOR_RGB565_TO_B8(pixel)];
float lin_sum = r_lin + g_lin + b_lin;
float r_lin_div = 0.0f;
float g_lin_div = 0.0f;
float b_lin_div = 0.0f;
if (lin_sum > 0.0f) {
lin_sum = 1.0f / lin_sum;
r_lin_div = r_lin * lin_sum;
g_lin_div = g_lin * lin_sum;
b_lin_div = b_lin * lin_sum;
}
int r_lin_div_int = IM_MAX(IM_MIN(r_lin_div * 255.0f, COLOR_R8_MAX), COLOR_R8_MIN);
int g_lin_div_int = IM_MAX(IM_MIN(g_lin_div * 255.0f, COLOR_G8_MAX), COLOR_G8_MIN);
int b_lin_div_int = IM_MAX(IM_MIN(b_lin_div * 255.0f, COLOR_B8_MAX), COLOR_B8_MIN);
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, COLOR_R8_G8_B8_TO_RGB565(r_lin_div_int, g_lin_div_int, b_lin_div_int));
}
}
break;
}
default: {
break;
}
}
}
extern const uint16_t invariant_table[65536];
void imlib_illuminvar(image_t *img) // http://ai.stanford.edu/~alireza/publication/cic15.pdf
{
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
break;
}
case IMAGE_BPP_GRAYSCALE: {
break;
}
case IMAGE_BPP_RGB565: {
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
for (int x = 0, xx = img->w; x < xx; x++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x);
#ifdef OMV_HAVE_INVARIANT_TABLE
int rgb565 = invariant_table[pixel];
#else
float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)] + 1.0;
float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)] + 1.0;
float b_lin = xyz_table[COLOR_RGB565_TO_B8(pixel)] + 1.0;
float r_lin_sharp = (r_lin * 0.9968f) + (g_lin * 0.0228f) + (b_lin * 0.0015f);
float g_lin_sharp = (r_lin * -0.0071f) + (g_lin * 0.9933f) + (b_lin * 0.0146f);
float b_lin_sharp = (r_lin * 0.0103f) + (g_lin * -0.0161f) + (b_lin * 0.9839f);
float lin_sharp_avg = r_lin_sharp * g_lin_sharp * b_lin_sharp;
lin_sharp_avg = (lin_sharp_avg > 0.0f) ? fast_cbrtf(lin_sharp_avg) : 0.0f;
float r_lin_sharp_div = 0.0f;
float g_lin_sharp_div = 0.0f;
float b_lin_sharp_div = 0.0f;
if (lin_sharp_avg > 0.0f) {
lin_sharp_avg = 1.0f / lin_sharp_avg;
r_lin_sharp_div = r_lin_sharp * lin_sharp_avg;
g_lin_sharp_div = g_lin_sharp * lin_sharp_avg;
b_lin_sharp_div = b_lin_sharp * lin_sharp_avg;
}
float r_lin_sharp_div_log = (r_lin_sharp_div > 0.0f) ? fast_log(r_lin_sharp_div) : 0.0f;
float g_lin_sharp_div_log = (g_lin_sharp_div > 0.0f) ? fast_log(g_lin_sharp_div) : 0.0f;
float b_lin_sharp_div_log = (b_lin_sharp_div > 0.0f) ? fast_log(b_lin_sharp_div) : 0.0f;
float chi_x = (r_lin_sharp_div_log * 0.7071f) + (g_lin_sharp_div_log * -0.7071f) + (b_lin_sharp_div_log * 0.0000f);
float chi_y = (r_lin_sharp_div_log * 0.4082f) + (g_lin_sharp_div_log * 0.4082f) + (b_lin_sharp_div_log * -0.8164f);
float e_t_x = 0.9326f;
float e_t_y = -0.3609f;
float p_th_00 = e_t_x * e_t_x;
float p_th_01 = e_t_x * e_t_y;
float p_th_10 = e_t_y * e_t_x;
float p_th_11 = e_t_y * e_t_y;
float x_th_x = (p_th_00 * chi_x) + (p_th_01 * chi_y);
float x_th_y = (p_th_10 * chi_x) + (p_th_11 * chi_y);
float r_chi = (x_th_x * 0.7071f) + (x_th_y * 0.4082f);
float g_chi = (x_th_x * -0.7071f) + (x_th_y * 0.4082f);
float b_chi = (x_th_x * 0.0000f) + (x_th_y * -0.8164f);
float r_chi_invariant = fast_expf(r_chi);
float g_chi_invariant = fast_expf(g_chi);
float b_chi_invariant = fast_expf(b_chi);
float chi_invariant_sum = r_chi_invariant + g_chi_invariant + b_chi_invariant;
float r_chi_invariant_m = 0.0f;
float g_chi_invariant_m = 0.0f;
float b_chi_invariant_m = 0.0f;
if (chi_invariant_sum > 0.0f) {
chi_invariant_sum = 1.0f / chi_invariant_sum;
r_chi_invariant_m = r_chi_invariant * chi_invariant_sum;
g_chi_invariant_m = g_chi_invariant * chi_invariant_sum;
b_chi_invariant_m = b_chi_invariant * chi_invariant_sum;
}
int r_chi_invariant_m_int = IM_MAX(IM_MIN(r_chi_invariant_m * 255.0f, COLOR_R8_MAX), COLOR_R8_MIN);
int g_chi_invariant_m_int = IM_MAX(IM_MIN(g_chi_invariant_m * 255.0f, COLOR_G8_MAX), COLOR_G8_MIN);
int b_chi_invariant_m_int = IM_MAX(IM_MIN(b_chi_invariant_m * 255.0f, COLOR_B8_MAX), COLOR_B8_MIN);
int rgb565 = COLOR_R8_G8_B8_TO_RGB565(r_chi_invariant_m_int, g_chi_invariant_m_int, b_chi_invariant_m_int);
#endif
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, rgb565);
}
}
break;
}
default: {
break;
}
}
}
void imlib_histeq(image_t *img)
{
switch(img->bpp) {

View File

@ -172,8 +172,8 @@ color_thresholds_list_lnk_data_t;
#define COLOR_Y_MIN -128
#define COLOR_Y_MAX 127
#define COLOR_U_MIN -128
#define COLOR_V_MAX 127
#define COLOR_U_MIN -128
#define COLOR_U_MAX 127
#define COLOR_V_MIN -128
#define COLOR_V_MAX 127
extern const uint8_t rb528_table[32];
@ -243,10 +243,10 @@ extern const int8_t yuv_table[196608];
_r_lin = (_r_lin > 0.0031308f) ? ((1.055f * powf(_r_lin, 0.416666f)) - 0.055f) : (_r_lin * 12.92f); \
_g_lin = (_g_lin > 0.0031308f) ? ((1.055f * powf(_g_lin, 0.416666f)) - 0.055f) : (_g_lin * 12.92f); \
_b_lin = (_b_lin > 0.0031308f) ? ((1.055f * powf(_b_lin, 0.416666f)) - 0.055f) : (_b_lin * 12.92f); \
unsigned int _r = IM_MAX(IM_MIN(roundf(_r_lin * COLOR_R5_MAX), COLOR_R5_MAX), COLOR_R5_MIN); \
unsigned int _g = IM_MAX(IM_MIN(roundf(_g_lin * COLOR_G6_MAX), COLOR_G6_MAX), COLOR_G6_MIN); \
unsigned int _b = IM_MAX(IM_MIN(roundf(_b_lin * COLOR_B5_MAX), COLOR_B5_MAX), COLOR_B5_MIN); \
COLOR_R5_G6_B5_TO_RGB565(_r, _g, _b); \
unsigned int _rgb565_r = IM_MAX(IM_MIN(roundf(_r_lin * COLOR_R5_MAX), COLOR_R5_MAX), COLOR_R5_MIN); \
unsigned int _rgb565_g = IM_MAX(IM_MIN(roundf(_g_lin * COLOR_G6_MAX), COLOR_G6_MAX), COLOR_G6_MIN); \
unsigned int _rgb565_b = IM_MAX(IM_MIN(roundf(_b_lin * COLOR_B5_MAX), COLOR_B5_MAX), COLOR_B5_MIN); \
COLOR_R5_G6_B5_TO_RGB565(_rgb565_r, _rgb565_g, _rgb565_b); \
})
// https://en.wikipedia.org/wiki/YCbCr -> JPEG Conversion
@ -941,6 +941,12 @@ typedef struct percentile {
int8_t BValue;
} percentile_t;
typedef struct threshold {
uint8_t LValue;
int8_t AValue;
int8_t BValue;
} threshold_t;
typedef struct statistics {
uint8_t LMean, LMedian, LMode, LSTDev, LMin, LMax, LLQ, LUQ;
int8_t AMean, AMedian, AMode, ASTDev, AMin, AMax, ALQ, AUQ;
@ -1133,10 +1139,10 @@ int imlib_image_mean(image_t *src); // grayscale only
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);
void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert);
void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert);
void imlib_median_filter(image_t *img, const int ksize, const int percentile, bool threshold, int offset, bool invert);
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);
@ -1221,6 +1227,7 @@ void imlib_rotation_corr(image_t *img, float x_rotation, float y_rotation,
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_threshold(threshold_t *out, image_bpp_t bpp, histogram_t *ptr);
void imlib_get_statistics(statistics_t *out, image_bpp_t bpp, histogram_t *ptr);
bool imlib_get_regression(find_lines_list_lnk_data_t *out, image_t *ptr, rectangle_t *roi, unsigned int x_stride, unsigned int y_stride,
list_t *thresholds, bool invert, unsigned int area_threshold, unsigned int pixels_threshold, bool robust);

View File

@ -15,7 +15,7 @@
// ...
// 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)
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;
@ -26,14 +26,15 @@ void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset
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;
}
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) {
@ -55,16 +56,17 @@ void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset
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);
}
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) {

View File

@ -11,7 +11,7 @@
#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)
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;
@ -23,18 +23,17 @@ void imlib_median_filter(image_t *img, const int ksize, const int percentile, bo
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;
}
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) {
@ -59,16 +58,12 @@ void imlib_median_filter(image_t *img, const int ksize, const int percentile, bo
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;
}
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);
@ -79,6 +74,7 @@ void imlib_median_filter(image_t *img, const int ksize, const int percentile, bo
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) {

View File

@ -17,7 +17,7 @@
// 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)
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;
@ -29,15 +29,16 @@ void imlib_midpoint_filter(image_t *img, const int ksize, const int bias, bool t
int min = 255, max = 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);
min = IM_MIN(min, pixel);
max = IM_MAX(max, pixel);
}
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) {
@ -59,24 +60,25 @@ void imlib_midpoint_filter(image_t *img, const int ksize, const int bias, bool t
int b_min = 255, b_max = 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_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);
}
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) {

View File

@ -15,7 +15,7 @@
// ...
// 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)
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);
@ -27,18 +27,19 @@ void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset
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;
}
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) {
@ -67,31 +68,32 @@ void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset
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;
}
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) {

View File

@ -30,9 +30,9 @@ void imlib_morph(image_t *img, const int ksize, const int8_t *krn, const float m
int ptr = 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)) {
acc += krn[ptr++] * IM_GET_GS_PIXEL(img, x+k, y+j);
}
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
@ -60,12 +60,12 @@ void imlib_morph(image_t *img, const int ksize, const int8_t *krn, const float m
int ptr = 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 += krn[ptr] * IM_R565(pixel);
g_acc += krn[ptr] * IM_G565(pixel);
b_acc += krn[ptr++] * IM_B565(pixel);
}
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

View File

@ -0,0 +1,503 @@
/* 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 "imlib.h"
// http://arma.sourceforge.net/shadows/
// http://dx.doi.org/10.1016/j.patcog.2011.10.001
#define imlib_remove_shadows_kernel_rank 1
#define imlib_remove_shadows_kernel_size ((imlib_remove_shadows_kernel_rank * 2) + 1)
typedef struct imlib_remove_shadows_line_op_state {
uint16_t *img_lines[imlib_remove_shadows_kernel_size];
uint16_t *other_lines[imlib_remove_shadows_kernel_size];
uint16_t *out_lines[imlib_remove_shadows_kernel_size];
int lines_processed;
} imlib_remove_shadows_line_op_state_t;
static void imlib_remove_shadows_sub_sub_line_op(image_t *img, int line, void *data, bool vflipped)
{
imlib_remove_shadows_line_op_state_t *state = (imlib_remove_shadows_line_op_state_t *) data;
state->lines_processed += 1;
if (state->lines_processed >= imlib_remove_shadows_kernel_size) {
int y = vflipped ? ((line - 1) + imlib_remove_shadows_kernel_size) : ((line + 1) - imlib_remove_shadows_kernel_size);
int index = y % imlib_remove_shadows_kernel_size;
memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y), state->out_lines[index], img->w * sizeof(uint16_t));
}
}
static void imlib_remove_shadows_sub_line_op(image_t *img, int line, void *data, bool vflipped)
{
imlib_remove_shadows_line_op_state_t *state = (imlib_remove_shadows_line_op_state_t *) data;
if (state->lines_processed >= imlib_remove_shadows_kernel_rank) {
int y = vflipped ? (line + imlib_remove_shadows_kernel_rank) : (line - imlib_remove_shadows_kernel_rank);
int index = y % imlib_remove_shadows_kernel_size;
for (int x = 0, xx = img->w; x < xx; x++) {
int img_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->img_lines[index], x);
int img_r = COLOR_RGB565_TO_R8(img_pixel);
int img_g = COLOR_RGB565_TO_G8(img_pixel);
int img_b = COLOR_RGB565_TO_B8(img_pixel);
float img_v = IM_MAX(IM_MAX(img_r, img_g), img_b);
int other_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->other_lines[index], x);
int other_r = COLOR_RGB565_TO_R8(other_pixel);
int other_g = COLOR_RGB565_TO_G8(other_pixel);
int other_b = COLOR_RGB565_TO_B8(other_pixel);
float other_v = IM_MAX(IM_MAX(other_r, other_g), other_b);
float ratio = img_v / other_v;
if ((0.3f < ratio) && (ratio < 1.0f)) {
int minY = IM_MAX(y - imlib_remove_shadows_kernel_rank, 0);
int maxY = IM_MIN(y + imlib_remove_shadows_kernel_rank, img->h - 1);
int minX = IM_MAX(x - imlib_remove_shadows_kernel_rank, 0);
int maxX = IM_MIN(x + imlib_remove_shadows_kernel_rank, img->w - 1);
int windowArea = (maxX - minX + 1) * (maxY - minY + 1);
int hDiffSum = 0;
int sDiffSum = 0;
for (int k_y = minY; k_y <= maxY; k_y++) {
int k_index = k_y % imlib_remove_shadows_kernel_size;
for (int k_x = minX; k_x <= maxX; k_x++) {
int k_img_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->img_lines[k_index], k_x);
int k_img_r = COLOR_RGB565_TO_R8(k_img_pixel);
int k_img_g = COLOR_RGB565_TO_G8(k_img_pixel);
int k_img_b = COLOR_RGB565_TO_B8(k_img_pixel);
int k_img_cmax = IM_MAX(IM_MAX(k_img_r, k_img_g), k_img_b);
int k_img_cmin = IM_MAX(IM_MAX(k_img_r, k_img_g), k_img_b);
float k_img_cdel = k_img_cmax - k_img_cmin;
float k_img_h = 0;
float k_img_s = k_img_cmax ? (k_img_cdel / k_img_cmax) : 0;
int k_other_pixel = IMAGE_GET_RGB565_PIXEL_FAST(state->other_lines[k_index], k_x);
int k_other_r = COLOR_RGB565_TO_R8(k_other_pixel);
int k_other_g = COLOR_RGB565_TO_G8(k_other_pixel);
int k_other_b = COLOR_RGB565_TO_B8(k_other_pixel);
int k_other_cmax = IM_MAX(IM_MAX(k_other_r, k_other_g), k_other_b);
int k_other_cmin = IM_MAX(IM_MAX(k_other_r, k_other_g), k_other_b);
float k_other_cdel = k_other_cmax - k_other_cmin;
float k_other_h = 0;
float k_other_s = k_other_cmax ? (k_other_cdel / k_other_cmax) : 0;
if (k_img_cdel) {
if (k_img_cmax == k_img_r) {
k_img_h = ((k_img_g - k_img_b) / k_img_cdel) + 0;
} else if (k_img_cmax == k_img_g) {
k_img_h = ((k_img_b - k_img_r) / k_img_cdel) + 2;
} else if (k_img_cmax == k_img_b) {
k_img_h = ((k_img_r - k_img_g) / k_img_cdel) + 4;
}
k_img_h *= 60;
if (k_img_h < 0) k_img_h += 360.0;
}
if (k_other_cdel) {
if (k_other_cmax == k_other_r) {
k_other_h = ((k_other_g - k_other_b) / k_other_cdel) + 0;
} else if (k_other_cmax == k_other_g) {
k_other_h = ((k_other_b - k_other_r) / k_other_cdel) + 2;
} else if (k_other_cmax == k_other_b) {
k_other_h = ((k_other_r - k_other_g) / k_other_cdel) + 4;
}
k_other_h *= 60;
if (k_other_h < 0) k_other_h += 360.0;
}
int hDiff = abs(k_img_h - k_other_h);
hDiffSum += (hDiff >= 90) ? (180 - hDiff) : hDiff;
sDiffSum += k_img_s - k_other_s;
}
}
bool hIsShadow = (hDiffSum / windowArea) < 48;
bool sIsShadow = (sDiffSum / windowArea) < 40;
IMAGE_PUT_RGB565_PIXEL_FAST(state->out_lines[index], x, (hIsShadow && sIsShadow) ? other_pixel : img_pixel);
} else {
IMAGE_PUT_RGB565_PIXEL_FAST(state->out_lines[index], x, img_pixel);
}
}
}
imlib_remove_shadows_sub_sub_line_op(img, line, data, vflipped);
}
static void imlib_remove_shadows_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped)
{
imlib_remove_shadows_line_op_state_t *state = (imlib_remove_shadows_line_op_state_t *) data;
memcpy(state->img_lines[line % imlib_remove_shadows_kernel_size],
IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line), img->w * sizeof(uint16_t));
memcpy(state->other_lines[line % imlib_remove_shadows_kernel_size],
(uint16_t *) other, img->w * sizeof(uint16_t));
imlib_remove_shadows_sub_line_op(img, line, data, vflipped);
if (state->lines_processed == img->h) {
for (int i = 0; i < imlib_remove_shadows_kernel_rank; i++) {
line += vflipped ? -1 : 1;
imlib_remove_shadows_sub_line_op(img, line, data, vflipped);
}
for (int i = 0; i < imlib_remove_shadows_kernel_rank; i++) {
line += vflipped ? -1 : 1;
imlib_remove_shadows_sub_sub_line_op(img, line, data, vflipped);
}
}
}
void imlib_remove_shadows(image_t *img, const char *path, image_t *other)
{
if (path || other) {
imlib_remove_shadows_line_op_state_t state;
for (int i = 0; i < imlib_remove_shadows_kernel_size; i++) {
state.img_lines[i] = fb_alloc(img->w * sizeof(uint16_t));
state.other_lines[i] = fb_alloc(img->w * sizeof(uint16_t));
state.out_lines[i] = fb_alloc(img->w * sizeof(uint16_t));
}
state.lines_processed = 0;
imlib_image_operation(img, path, other, imlib_remove_shadows_line_op, (void *) &state);
for (int i = 0; i < imlib_remove_shadows_kernel_size; i++) {
fb_free();
fb_free();
fb_free();
}
} else {
// Create Shadow Mask
image_t temp_image;
temp_image.w = img->w;
temp_image.h = img->h;
temp_image.bpp = img->bpp;
temp_image.data = fb_alloc(image_size(img));
memcpy(temp_image.data, img->data, image_size(img));
rectangle_t r;
r.x = 0;
r.y = 0;
r.w = temp_image.w;
r.h = temp_image.h;
histogram_t h;
h.LBinCount = COLOR_L_MAX - COLOR_L_MIN + 1;
h.ABinCount = COLOR_A_MAX - COLOR_A_MIN + 1;
h.BBinCount = COLOR_B_MAX - COLOR_B_MIN + 1;
h.LBins = fb_alloc(h.LBinCount * sizeof(float));
h.ABins = fb_alloc(h.ABinCount * sizeof(float));
h.BBins = fb_alloc(h.BBinCount * sizeof(float));
imlib_get_histogram(&h, &temp_image, &r);
statistics_t s;
imlib_get_statistics(&s, temp_image.bpp, &h);
int sum = 0;
int mean = s.LMean * 0.8f;
for (int y = 0, yy = temp_image.h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&temp_image, y);
for (int x = 0, xx = temp_image.w; x < xx; x++) {
sum += COLOR_RGB565_TO_L(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x)) < mean;
}
}
if (sum > ((temp_image.w * temp_image.h) / 20)) { // Don't do anything if the image is mostly flat.
threshold_t t;
imlib_get_threshold(&t, temp_image.bpp, &h);
simple_color_t t_l, t_h;
t_l.L = COLOR_L_MIN;
t_l.A = COLOR_A_MIN;
t_l.B = COLOR_B_MIN;
t_h.L = t.LValue;
t_h.A = COLOR_A_MAX;
t_h.B = COLOR_B_MAX;
imlib_binary(&temp_image, 1, &t_l, &t_h, false);
imlib_erode(&temp_image, 3, 30);
imlib_dilate(&temp_image, 1, 1);
// Get Shadow Average
image_t temp_image_2;
temp_image_2.w = temp_image.w;
temp_image_2.h = temp_image.h;
temp_image_2.bpp = temp_image.bpp;
temp_image_2.data = fb_alloc(image_size(&temp_image));
memcpy(temp_image_2.data, temp_image.data, image_size(&temp_image));
imlib_erode(&temp_image_2, 3, 48);
int shadow_r_sum = 0;
int shadow_g_sum = 0;
int shadow_b_sum = 0;
int shadow_count = 0;
for (int y = 0, yy = temp_image_2.h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&temp_image_2, y);
uint16_t *row_ptr_2 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
for (int x = 0, xx = temp_image_2.w; x < xx; x++) {
if (IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x)) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr_2, x);
int r = COLOR_RGB565_TO_R8(pixel);
int g = COLOR_RGB565_TO_G8(pixel);
int b = COLOR_RGB565_TO_R8(pixel);
shadow_r_sum += r;
shadow_g_sum += g;
shadow_b_sum += b;
shadow_count += 1;
}
}
}
memcpy(temp_image_2.data, temp_image.data, image_size(&temp_image));
imlib_invert(&temp_image_2);
imlib_erode(&temp_image_2, 5, 120);
imlib_invert(&temp_image_2);
imlib_b_xor(&temp_image_2, NULL, &temp_image);
imlib_erode(&temp_image_2, 2, 24);
int not_shadow_r_sum = 0;
int not_shadow_g_sum = 0;
int not_shadow_b_sum = 0;
int not_shadow_count = 0;
for (int y = 0, yy = temp_image_2.h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&temp_image_2, y);
uint16_t *row_ptr_2 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
for (int x = 0, xx = temp_image_2.w; x < xx; x++) {
if (IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x)) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr_2, x);
int r = COLOR_RGB565_TO_R8(pixel);
int g = COLOR_RGB565_TO_G8(pixel);
int b = COLOR_RGB565_TO_R8(pixel);
not_shadow_r_sum += r;
not_shadow_g_sum += g;
not_shadow_b_sum += b;
not_shadow_count += 1;
}
}
}
// Fill in the umbra... (inner part of the shadow)...
memcpy(temp_image_2.data, temp_image.data, image_size(&temp_image));
imlib_mean_filter(&temp_image, 2, false, 0, false, NULL);
if (shadow_count && not_shadow_count) {
float shadow_r_average = ((float) shadow_r_sum) / ((float) shadow_count);
float shadow_g_average = ((float) shadow_g_sum) / ((float) shadow_count);
float shadow_b_average = ((float) shadow_b_sum) / ((float) shadow_count);
float not_shadow_r_average = ((float) not_shadow_r_sum) / ((float) not_shadow_count);
float not_shadow_g_average = ((float) not_shadow_g_sum) / ((float) not_shadow_count);
float not_shadow_b_average = ((float) not_shadow_b_sum) / ((float) not_shadow_count);
float diff_r = not_shadow_r_average - shadow_r_average;
float diff_g = not_shadow_g_average - shadow_g_average;
float diff_b = not_shadow_b_average - shadow_b_average;
for (int y = 0; y < img->h; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&temp_image, y);
uint16_t *row_ptr_2 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
for (int x = 0; x < img->w; x++) {
float alpha = ((float) (COLOR_RGB565_TO_Y(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x)) - COLOR_Y_MIN)) / ((float) (COLOR_Y_MAX - COLOR_Y_MIN));
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr_2, x);
int r = COLOR_RGB565_TO_R8(pixel);
int g = COLOR_RGB565_TO_G8(pixel);
int b = COLOR_RGB565_TO_B8(pixel);
int r_new = IM_MIN(IM_MAX(r + (diff_r * alpha), COLOR_R8_MIN), COLOR_R8_MAX);
int g_new = IM_MIN(IM_MAX(g + (diff_g * alpha), COLOR_G8_MIN), COLOR_G8_MAX);
int b_new = IM_MIN(IM_MAX(b + (diff_b * alpha), COLOR_B8_MIN), COLOR_B8_MAX);
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr_2, x, COLOR_R8_G8_B8_TO_RGB565(r_new, g_new, b_new));
}
}
}
// Fill in the penumbra... (outer part of the shadow)...
memcpy(temp_image.data, temp_image_2.data, image_size(&temp_image_2));
imlib_erode(&temp_image_2, 1, 8);
imlib_b_xor(&temp_image, NULL, &temp_image_2);
imlib_dilate(&temp_image, 3, 0);
imlib_median_filter(img, 2, 12, false, 0, false, &temp_image);
fb_free(); // temp_image_2
}
fb_free(); // BBins
fb_free(); // ABins
fb_free(); // LBins
fb_free(); // temp_image
}
}
#undef imlib_remove_shadows_kernel_size
#undef imlib_remove_shadows_kernel_rank
extern const float xyz_table[256];
void imlib_chrominvar(image_t *img)
{
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
break;
}
case IMAGE_BPP_GRAYSCALE: {
break;
}
case IMAGE_BPP_RGB565: {
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
for (int x = 0, xx = img->w; x < xx; x++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x);
float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)];
float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)];
float b_lin = xyz_table[COLOR_RGB565_TO_B8(pixel)];
float lin_sum = r_lin + g_lin + b_lin;
float r_lin_div = 0.0f;
float g_lin_div = 0.0f;
float b_lin_div = 0.0f;
if (lin_sum > 0.0f) {
lin_sum = 1.0f / lin_sum;
r_lin_div = r_lin * lin_sum;
g_lin_div = g_lin * lin_sum;
b_lin_div = b_lin * lin_sum;
}
int r_lin_div_int = IM_MAX(IM_MIN(r_lin_div * 255.0f, COLOR_R8_MAX), COLOR_R8_MIN);
int g_lin_div_int = IM_MAX(IM_MIN(g_lin_div * 255.0f, COLOR_G8_MAX), COLOR_G8_MIN);
int b_lin_div_int = IM_MAX(IM_MIN(b_lin_div * 255.0f, COLOR_B8_MAX), COLOR_B8_MIN);
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, COLOR_R8_G8_B8_TO_RGB565(r_lin_div_int, g_lin_div_int, b_lin_div_int));
}
}
break;
}
default: {
break;
}
}
}
extern const uint16_t invariant_table[65536];
void imlib_illuminvar(image_t *img) // http://ai.stanford.edu/~alireza/publication/cic15.pdf
{
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
break;
}
case IMAGE_BPP_GRAYSCALE: {
break;
}
case IMAGE_BPP_RGB565: {
for (int y = 0, yy = img->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
for (int x = 0, xx = img->w; x < xx; x++) {
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x);
#ifdef OMV_HAVE_INVARIANT_TABLE
int rgb565 = invariant_table[pixel];
#else
float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)] + 1.0;
float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)] + 1.0;
float b_lin = xyz_table[COLOR_RGB565_TO_B8(pixel)] + 1.0;
float r_lin_sharp = (r_lin * 0.9968f) + (g_lin * 0.0228f) + (b_lin * 0.0015f);
float g_lin_sharp = (r_lin * -0.0071f) + (g_lin * 0.9933f) + (b_lin * 0.0146f);
float b_lin_sharp = (r_lin * 0.0103f) + (g_lin * -0.0161f) + (b_lin * 0.9839f);
float lin_sharp_avg = r_lin_sharp * g_lin_sharp * b_lin_sharp;
lin_sharp_avg = (lin_sharp_avg > 0.0f) ? fast_cbrtf(lin_sharp_avg) : 0.0f;
float r_lin_sharp_div = 0.0f;
float g_lin_sharp_div = 0.0f;
float b_lin_sharp_div = 0.0f;
if (lin_sharp_avg > 0.0f) {
lin_sharp_avg = 1.0f / lin_sharp_avg;
r_lin_sharp_div = r_lin_sharp * lin_sharp_avg;
g_lin_sharp_div = g_lin_sharp * lin_sharp_avg;
b_lin_sharp_div = b_lin_sharp * lin_sharp_avg;
}
float r_lin_sharp_div_log = (r_lin_sharp_div > 0.0f) ? fast_log(r_lin_sharp_div) : 0.0f;
float g_lin_sharp_div_log = (g_lin_sharp_div > 0.0f) ? fast_log(g_lin_sharp_div) : 0.0f;
float b_lin_sharp_div_log = (b_lin_sharp_div > 0.0f) ? fast_log(b_lin_sharp_div) : 0.0f;
float chi_x = (r_lin_sharp_div_log * 0.7071f) + (g_lin_sharp_div_log * -0.7071f) + (b_lin_sharp_div_log * 0.0000f);
float chi_y = (r_lin_sharp_div_log * 0.4082f) + (g_lin_sharp_div_log * 0.4082f) + (b_lin_sharp_div_log * -0.8164f);
float e_t_x = 0.9326f;
float e_t_y = -0.3609f;
float p_th_00 = e_t_x * e_t_x;
float p_th_01 = e_t_x * e_t_y;
float p_th_10 = e_t_y * e_t_x;
float p_th_11 = e_t_y * e_t_y;
float x_th_x = (p_th_00 * chi_x) + (p_th_01 * chi_y);
float x_th_y = (p_th_10 * chi_x) + (p_th_11 * chi_y);
float r_chi = (x_th_x * 0.7071f) + (x_th_y * 0.4082f);
float g_chi = (x_th_x * -0.7071f) + (x_th_y * 0.4082f);
float b_chi = (x_th_x * 0.0000f) + (x_th_y * -0.8164f);
float r_chi_invariant = fast_expf(r_chi);
float g_chi_invariant = fast_expf(g_chi);
float b_chi_invariant = fast_expf(b_chi);
float chi_invariant_sum = r_chi_invariant + g_chi_invariant + b_chi_invariant;
float r_chi_invariant_m = 0.0f;
float g_chi_invariant_m = 0.0f;
float b_chi_invariant_m = 0.0f;
if (chi_invariant_sum > 0.0f) {
chi_invariant_sum = 1.0f / chi_invariant_sum;
r_chi_invariant_m = r_chi_invariant * chi_invariant_sum;
g_chi_invariant_m = g_chi_invariant * chi_invariant_sum;
b_chi_invariant_m = b_chi_invariant * chi_invariant_sum;
}
int r_chi_invariant_m_int = IM_MAX(IM_MIN(r_chi_invariant_m * 255.0f, COLOR_R8_MAX), COLOR_R8_MIN);
int g_chi_invariant_m_int = IM_MAX(IM_MIN(g_chi_invariant_m * 255.0f, COLOR_G8_MAX), COLOR_G8_MIN);
int b_chi_invariant_m_int = IM_MAX(IM_MIN(b_chi_invariant_m * 255.0f, COLOR_B8_MAX), COLOR_B8_MIN);
int rgb565 = COLOR_R8_G8_B8_TO_RGB565(r_chi_invariant_m_int, g_chi_invariant_m_int, b_chi_invariant_m_int);
#endif
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, rgb565);
}
}
break;
}
default: {
break;
}
}
}

View File

@ -290,7 +290,7 @@ void imlib_get_percentile(percentile_t *out, image_bpp_t bpp, histogram_t *ptr,
for (int i = 0, j = ptr->BBinCount; i < j; i++) {
if ((median_count < percentile) && (percentile <= (median_count + ptr->BBins[i]))) {
out->BValue = fast_roundf((i * mult) + COLOR_A_MIN);
out->BValue = fast_roundf((i * mult) + COLOR_B_MIN);
break;
}
@ -305,6 +305,64 @@ void imlib_get_percentile(percentile_t *out, image_bpp_t bpp, histogram_t *ptr,
}
}
static int ostu(int bincount, float *bins)
{
float cdf[bincount]; memset(cdf, 0, bincount * sizeof(float));
float weighted_cdf[bincount]; memset(weighted_cdf, 0, bincount * sizeof(float));
cdf[0] = bins[0];
weighted_cdf[0] = 0 * bins[0];
for (int i = 1; i < bincount; i++) {
cdf[i] = cdf[i - 1] + bins[i];
weighted_cdf[i] = weighted_cdf[i - 1] + (i * bins[i]);
}
float variance[bincount]; memset(variance, 0, bincount * sizeof(float));
float max_variance = 0.0f;
int threshold = 0;
for (int i = 0, ii = bincount - 1; i < ii; i++) {
if ((cdf[i] != 0.0f) && (cdf[i] != 1.0f)) {
variance[i] = powf((cdf[i] * weighted_cdf[bincount - 1]) - weighted_cdf[i], 2.0f) / (cdf[i] * (1.0f - cdf[i]));
} else {
variance[i] = 0.0f;
}
if (variance[i] > max_variance) {
max_variance = variance[i];
threshold = i;
}
}
return threshold;
}
void imlib_get_threshold(threshold_t *out, image_bpp_t bpp, histogram_t *ptr)
{
memset(out, 0, sizeof(threshold_t));
switch(bpp) {
case IMAGE_BPP_BINARY: {
out->LValue = (ostu(ptr->LBinCount, ptr->LBins) * (COLOR_BINARY_MAX - COLOR_BINARY_MIN)) / (ptr->LBinCount - 1);
break;
}
case IMAGE_BPP_GRAYSCALE: {
out->LValue = (ostu(ptr->LBinCount, ptr->LBins) * (COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN)) / (ptr->LBinCount - 1);
break;
}
case IMAGE_BPP_RGB565: {
out->LValue = (ostu(ptr->LBinCount, ptr->LBins) * (COLOR_L_MAX - COLOR_L_MIN)) / (ptr->LBinCount - 1);
out->AValue = (ostu(ptr->ABinCount, ptr->ABins) * (COLOR_A_MAX - COLOR_A_MIN)) / (ptr->ABinCount - 1);
out->BValue = (ostu(ptr->BBinCount, ptr->BBins) * (COLOR_B_MAX - COLOR_B_MIN)) / (ptr->BBinCount - 1);
break;
}
default: {
break;
}
}
}
void imlib_get_statistics(statistics_t *out, image_bpp_t bpp, histogram_t *ptr)
{
memset(out, 0, sizeof(statistics_t));

View File

@ -1214,9 +1214,11 @@ static mp_obj_t py_image_midpoint(uint n_args, const mp_obj_t *args, mp_map_t *k
int arg_threshold = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_offset), 0);
int arg_invert = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP);
int bias = py_helper_lookup_float(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_bias), 0.5) * 256;
imlib_midpoint_filter(arg_img, arg_ksize, IM_MIN(IM_MAX(bias, 0), 256), arg_threshold, arg_offset, arg_invert);
imlib_midpoint_filter(arg_img, arg_ksize, IM_MIN(IM_MAX(bias, 0), 256), arg_threshold, arg_offset, arg_invert,
(kw_arg != NULL) ? py_image_cobj(kw_arg->value) : NULL);
return args[0];
}
@ -1231,8 +1233,10 @@ static mp_obj_t py_image_mean(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar
int arg_threshold = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_offset), 0);
int arg_invert = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP);
imlib_mean_filter(arg_img, arg_ksize, arg_threshold, arg_offset, arg_invert);
imlib_mean_filter(arg_img, arg_ksize, arg_threshold, arg_offset, arg_invert,
(kw_arg != NULL) ? py_image_cobj(kw_arg->value) : NULL);
return args[0];
}
@ -1247,8 +1251,10 @@ static mp_obj_t py_image_mode(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar
int arg_threshold = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_offset), 0);
int arg_invert = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP);
imlib_mode_filter(arg_img, arg_ksize, arg_threshold, arg_offset, arg_invert);
imlib_mode_filter(arg_img, arg_ksize, arg_threshold, arg_offset, arg_invert,
(kw_arg != NULL) ? py_image_cobj(kw_arg->value) : NULL);
return args[0];
}
@ -1264,10 +1270,12 @@ static mp_obj_t py_image_median(uint n_args, const mp_obj_t *args, mp_map_t *kw_
int arg_threshold = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
int arg_offset = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_offset), 0);
int arg_invert = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP);
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), arg_threshold, arg_offset, arg_invert);
imlib_median_filter(arg_img, arg_ksize, IM_MIN(IM_MAX(percentile, 0), n-1), arg_threshold, arg_offset, arg_invert,
(kw_arg != NULL) ? py_image_cobj(kw_arg->value) : NULL);
return args[0];
}
@ -1327,23 +1335,27 @@ static mp_obj_t py_image_min(mp_obj_t img_obj, mp_obj_t other_obj)
}
#ifdef OMV_ENABLE_REMOVE_SHADOWS
static mp_obj_t py_image_remove_shadows(mp_obj_t img_obj, mp_obj_t other_obj)
static mp_obj_t py_image_remove_shadows(uint n_args, const mp_obj_t *args)
{
image_t *arg_img = py_image_cobj(img_obj);
image_t *arg_img = py_image_cobj(args[0]);
PY_ASSERT_TRUE_MSG(IM_IS_RGB565(arg_img), "Image format is not supported.");
if (MP_OBJ_IS_STR(other_obj)) {
if (n_args < 2) {
fb_alloc_mark();
imlib_remove_shadows(arg_img, mp_obj_str_get_str(other_obj), NULL);
imlib_remove_shadows(arg_img, NULL, NULL);
fb_alloc_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();
} else {
image_t *arg_other = py_image_cobj(other_obj);
image_t *arg_other = py_image_cobj(args[1]);
fb_alloc_mark();
imlib_remove_shadows(arg_img, NULL, arg_other);
fb_alloc_free_till_mark();
}
return img_obj;
return args[0];
}
#endif // OMV_ENABLE_REMOVE_SHADOWS
@ -1875,6 +1887,86 @@ static const mp_obj_type_t py_percentile_type = {
.locals_dict = (mp_obj_t) &py_percentile_locals_dict,
};
// Threshold Object //
#define py_threshold_obj_size 3
typedef struct py_threshold_obj {
mp_obj_base_t base;
image_bpp_t bpp;
mp_obj_t LValue, AValue, BValue;
} py_threshold_obj_t;
static void py_threshold_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind)
{
py_threshold_obj_t *self = self_in;
switch(self->bpp) {
case IMAGE_BPP_BINARY: {
mp_printf(print, "{value:%d}", mp_obj_get_int(self->LValue));
break;
}
case IMAGE_BPP_GRAYSCALE: {
mp_printf(print, "{value:%d}", mp_obj_get_int(self->LValue));
break;
}
case IMAGE_BPP_RGB565: {
mp_printf(print, "{l_value:%d, a_value:%d, b_value:%d}", mp_obj_get_int(self->LValue), mp_obj_get_int(self->AValue), mp_obj_get_int(self->BValue));
break;
}
default: {
mp_printf(print, "{}");
break;
}
}
}
static mp_obj_t py_threshold_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value)
{
if (value == MP_OBJ_SENTINEL) { // load
py_threshold_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_threshold_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->LValue) + slice.start, result->len, mp_obj_t);
return result;
}
switch (mp_get_index(self->base.type, py_threshold_obj_size, index, false)) {
case 0: return self->LValue;
case 1: return self->AValue;
case 2: return self->BValue;
}
}
return MP_OBJ_NULL; // op not supported
}
mp_obj_t py_threshold_value(mp_obj_t self_in) { return ((py_threshold_obj_t *) self_in)->LValue; }
mp_obj_t py_threshold_l_value(mp_obj_t self_in) { return ((py_threshold_obj_t *) self_in)->LValue; }
mp_obj_t py_threshold_a_value(mp_obj_t self_in) { return ((py_threshold_obj_t *) self_in)->AValue; }
mp_obj_t py_threshold_b_value(mp_obj_t self_in) { return ((py_threshold_obj_t *) self_in)->BValue; }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_threshold_value_obj, py_threshold_value);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_threshold_l_value_obj, py_threshold_l_value);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_threshold_a_value_obj, py_threshold_a_value);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_threshold_b_value_obj, py_threshold_b_value);
STATIC const mp_rom_map_elem_t py_threshold_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&py_threshold_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_l_value), MP_ROM_PTR(&py_threshold_l_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_a_value), MP_ROM_PTR(&py_threshold_a_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_b_value), MP_ROM_PTR(&py_threshold_b_value_obj) }
};
STATIC MP_DEFINE_CONST_DICT(py_threshold_locals_dict, py_threshold_locals_dict_table);
static const mp_obj_type_t py_threshold_type = {
{ &mp_type_type },
.name = MP_QSTR_threshold,
.print = py_threshold_print,
.subscr = py_threshold_subscr,
.locals_dict = (mp_obj_t) &py_threshold_locals_dict,
};
// Histogram Object //
#define py_histogram_obj_size 3
typedef struct py_histogram_obj {
@ -1984,6 +2076,47 @@ mp_obj_t py_histogram_get_percentile(mp_obj_t self_in, mp_obj_t percentile)
return o;
}
mp_obj_t py_histogram_get_threshold(mp_obj_t self_in)
{
histogram_t hist;
hist.LBinCount = ((mp_obj_list_t *) ((py_histogram_obj_t *) self_in)->LBins)->len;
hist.ABinCount = ((mp_obj_list_t *) ((py_histogram_obj_t *) self_in)->ABins)->len;
hist.BBinCount = ((mp_obj_list_t *) ((py_histogram_obj_t *) self_in)->BBins)->len;
fb_alloc_mark();
hist.LBins = fb_alloc(hist.LBinCount * sizeof(float));
hist.ABins = fb_alloc(hist.ABinCount * sizeof(float));
hist.BBins = fb_alloc(hist.BBinCount * sizeof(float));
for (int i = 0; i < hist.LBinCount; i++) {
hist.LBins[i] = mp_obj_get_float(((mp_obj_list_t *) ((py_histogram_obj_t *) self_in)->LBins)->items[i]);
}
for (int i = 0; i < hist.ABinCount; i++) {
hist.ABins[i] = mp_obj_get_float(((mp_obj_list_t *) ((py_histogram_obj_t *) self_in)->ABins)->items[i]);
}
for (int i = 0; i < hist.BBinCount; i++) {
hist.BBins[i] = mp_obj_get_float(((mp_obj_list_t *) ((py_histogram_obj_t *) self_in)->BBins)->items[i]);
}
threshold_t t;
imlib_get_threshold(&t, ((py_histogram_obj_t *) self_in)->bpp, &hist);
if (hist.BBinCount) fb_free();
if (hist.ABinCount) fb_free();
if (hist.LBinCount) fb_free();
fb_alloc_free_till_mark();
py_threshold_obj_t *o = m_new_obj(py_threshold_obj_t);
o->base.type = &py_threshold_type;
o->bpp = ((py_threshold_obj_t *) self_in)->bpp;
o->LValue = mp_obj_new_int(t.LValue);
o->AValue = mp_obj_new_int(t.AValue);
o->BValue = mp_obj_new_int(t.BValue);
return o;
}
mp_obj_t py_histogram_get_statistics(mp_obj_t self_in)
{
histogram_t hist;
@ -2051,6 +2184,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_histogram_l_bins_obj, py_histogram_l_bins);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_histogram_a_bins_obj, py_histogram_a_bins);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_histogram_b_bins_obj, py_histogram_b_bins);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_histogram_get_percentile_obj, py_histogram_get_percentile);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_histogram_get_threshold_obj, py_histogram_get_threshold);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_histogram_get_statistics_obj, py_histogram_get_statistics);
STATIC const mp_rom_map_elem_t py_histogram_locals_dict_table[] = {
@ -2059,6 +2193,7 @@ STATIC const mp_rom_map_elem_t py_histogram_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_a_bins), MP_ROM_PTR(&py_histogram_a_bins_obj) },
{ MP_ROM_QSTR(MP_QSTR_b_bins), MP_ROM_PTR(&py_histogram_b_bins_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_percentile), MP_ROM_PTR(&py_histogram_get_percentile_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_threshold), MP_ROM_PTR(&py_histogram_get_threshold_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_stats), MP_ROM_PTR(&py_histogram_get_statistics_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_statistics), MP_ROM_PTR(&py_histogram_get_statistics_obj) },
{ MP_ROM_QSTR(MP_QSTR_statistics), MP_ROM_PTR(&py_histogram_get_statistics_obj) }
@ -4219,7 +4354,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_blend_obj, 2, py_image_blend);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_max_obj, py_image_max);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_min_obj, py_image_min);
#ifdef OMV_ENABLE_REMOVE_SHADOWS
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_remove_shadows_obj, py_image_remove_shadows);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_image_remove_shadows_obj, 1, 2, py_image_remove_shadows);
#endif
/* Image Morphing */
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_morph_obj, 3, py_image_morph);

View File

@ -413,6 +413,7 @@ Q(histogram)
// duplicate Q(a_bins)
// duplicate Q(b_bins)
Q(get_percentile)
Q(get_threshold)
Q(get_stats)
Q(get_statistics)
Q(statistics)
@ -422,6 +423,12 @@ Q(value)
Q(l_value)
Q(a_value)
Q(b_value)
// Threshold Object
// duplicate Q(threshold)
// duplicate Q(value)
// duplicate Q(l_value)
// duplicate Q(a_value)
// duplicate Q(b_value)
// Get Statistics
// duplicate Q(get_stats)