mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
draw_image performance and negative scaling. (#758)
Improve draw_image performance and negative scaling.
This commit is contained in:
parent
c6f727e4fa
commit
414c6e1a51
95
scripts/examples/03-Drawing/image_drawing_advanced.py
Normal file
95
scripts/examples/03-Drawing/image_drawing_advanced.py
Normal file
@ -0,0 +1,95 @@
|
||||
# Draw Image Testing script with bounce
|
||||
#
|
||||
# Exercise draw image with many different values for testing
|
||||
|
||||
import sensor, image, time, pyb
|
||||
|
||||
sensor.reset()
|
||||
sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE...
|
||||
sensor.set_framesize(sensor.QQVGA) # or QQVGA...
|
||||
sensor.skip_frames(time = 2000)
|
||||
clock = time.clock()
|
||||
|
||||
BOUNCE = True
|
||||
RESCALE = True
|
||||
|
||||
SMALL_IMAGE_SCALE = 3
|
||||
|
||||
CYCLE_FORMATS = True
|
||||
CYCLE_MASK = True
|
||||
|
||||
# Used when CYCLE_FORMATS or CYCLE_MASK is true
|
||||
value_mixer = 0
|
||||
|
||||
# Location of small image
|
||||
x=100
|
||||
y=50
|
||||
|
||||
# Bounce direction
|
||||
xd=.1
|
||||
yd=.1
|
||||
|
||||
# Small image scaling
|
||||
rescale = 1.0
|
||||
rd=0.01
|
||||
max_rescale = 5
|
||||
min_rescale = -max_rescale
|
||||
|
||||
# Boundary to bounce within
|
||||
xmin = -sensor.width() / SMALL_IMAGE_SCALE - 8
|
||||
ymin = -sensor.height() / SMALL_IMAGE_SCALE - 8
|
||||
xmax = sensor.width() + 8
|
||||
ymax = sensor.height() + 8
|
||||
|
||||
while(True):
|
||||
clock.tick()
|
||||
|
||||
status = ""
|
||||
value_mixer = value_mixer + 1
|
||||
|
||||
img = sensor.snapshot()
|
||||
# Makes a scaled copy of the sensor
|
||||
small_img = img.mean_pooled(SMALL_IMAGE_SCALE, SMALL_IMAGE_SCALE)
|
||||
|
||||
status = 'rgb565 '
|
||||
if CYCLE_FORMATS:
|
||||
image_format = (value_mixer >> 8) & 3
|
||||
# To test combining different formats
|
||||
if (image_format==1): small_img = small_img.to_bitmap(copy=True); status = 'bitmap '
|
||||
if (image_format==2): small_img = small_img.to_grayscale(copy=True); status = 'grayscale '
|
||||
if (image_format==3): small_img = small_img.to_rgb565(copy=True); status = 'rgb565 '
|
||||
|
||||
# update small image location
|
||||
if BOUNCE:
|
||||
x = x + xd
|
||||
if (x<xmin or x>xmax):
|
||||
xd = -xd
|
||||
|
||||
y = y + yd
|
||||
if (y<ymin or y>ymax):
|
||||
yd = -yd
|
||||
|
||||
# Update small image scale
|
||||
if RESCALE:
|
||||
rescale = rescale + rd
|
||||
if (rescale<min_rescale or rescale>max_rescale):
|
||||
rd = -rd
|
||||
|
||||
# Find the center of the image
|
||||
scaled_width = int(small_img.width() * abs(rescale))
|
||||
scaled_height= int(small_img.height() * abs(rescale))
|
||||
draw_x = int(x - (scaled_width >> 1))
|
||||
draw_y = int(y - (scaled_height >> 1))
|
||||
|
||||
apply_mask = CYCLE_MASK and ((value_mixer >> 9) & 1)
|
||||
if apply_mask:
|
||||
img.draw_image(small_img, draw_x, draw_y, mask=small_img.to_bitmap(copy=True), x_scale=-rescale, y_scale=rescale, alpha=240)
|
||||
status += 'alpha:240 '
|
||||
status += '+mask '
|
||||
else:
|
||||
img.draw_image(small_img, draw_x, draw_y, x_scale=-rescale, y_scale=rescale, alpha=128)
|
||||
status += 'alpha:128 '
|
||||
|
||||
img.draw_string(8, 0, status, mono_space = False)
|
||||
|
||||
print(clock.fps())
|
||||
@ -11,29 +11,43 @@
|
||||
#include "font.h"
|
||||
#include "imlib.h"
|
||||
|
||||
// Get pixel (handles boundary check and image type check).
|
||||
int imlib_get_pixel(image_t *img, int x, int y)
|
||||
{
|
||||
if ((0 <= x) && (x < img->w) && (0 <= y) && (y < img->h)) {
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
return IMAGE_GET_BINARY_PIXEL(img, x, y);
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
return IMAGE_GET_GRAYSCALE_PIXEL(img, x, y);
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
return IMAGE_GET_RGB565_PIXEL(img, x, y);
|
||||
}
|
||||
default: {
|
||||
return -1;
|
||||
}
|
||||
void* imlib_compute_row_ptr(image_t *img, int y) {
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
return IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y);
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
return IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
return IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
|
||||
}
|
||||
default: {
|
||||
// This shouldn't happen, at least we return a valid memory block
|
||||
return img->data;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline int imlib_get_pixel_fast(int img_bpp, void *row_ptr, int x)
|
||||
{
|
||||
switch(img_bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
return IMAGE_GET_BINARY_PIXEL_FAST((uint32_t*)row_ptr, x);
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
return IMAGE_GET_GRAYSCALE_PIXEL_FAST((uint8_t*)row_ptr, x);
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
return IMAGE_GET_RGB565_PIXEL_FAST((uint16_t*)row_ptr, x);
|
||||
}
|
||||
default: {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set pixel (handles boundary check and image type check).
|
||||
void imlib_set_pixel(image_t *img, int x, int y, int p)
|
||||
{
|
||||
@ -474,55 +488,177 @@ static int safe_map_pixel(int dst_bpp, int src_bpp, int pixel)
|
||||
}
|
||||
}
|
||||
|
||||
void imlib_draw_image(image_t *img, image_t *other, int x_off, int y_off, float x_scale, float y_scale, float alpha, image_t *mask, const uint16_t *color_palette)
|
||||
void imlib_draw_image(image_t *img, image_t *other, int x_off, int y_off, float x_scale, float y_scale, int alpha, image_t *mask, const uint16_t *color_palette)
|
||||
{
|
||||
float over_xscale = IM_DIV(1.0, x_scale), over_yscale = IM_DIV(1.0f, y_scale);
|
||||
// Scaler to convert from img scale to other scale
|
||||
const float over_xscale = IM_DIV(1.0f, x_scale), over_yscale = IM_DIV(1.0f, y_scale);
|
||||
|
||||
const float neg_alpha = 1.0f - alpha;
|
||||
const int img_bpp = img->bpp;
|
||||
const int other_bpp = other->bpp;
|
||||
const int xx = fast_floorf(other->w * x_scale);
|
||||
const int yy = fast_floorf(other->h * y_scale);
|
||||
|
||||
for (int y = 0; y < yy; y++) {
|
||||
int other_y = fast_floorf(y * over_yscale);
|
||||
// Left or top of other is out of bounds
|
||||
int other_x_start = (x_off < 0) ? -x_off : 0;
|
||||
int other_y_start = (y_off < 0) ? -y_off : 0;
|
||||
|
||||
// Scaled other size
|
||||
int other_width_scaled = fast_floorf(abs(other->w * x_scale));
|
||||
int other_height_scaled = fast_floorf(abs(other->h * y_scale));
|
||||
|
||||
for (int x = 0; x < xx; x++) {
|
||||
int other_x = fast_floorf(x * over_xscale);
|
||||
// Right or bottom of image is out of bounds
|
||||
int other_x_end = (x_off + other_width_scaled >= img->w) ? img->w - x_off : other_width_scaled;
|
||||
int other_y_end = (y_off + other_height_scaled >= img->h) ? img->h - y_off : other_height_scaled;
|
||||
|
||||
if ((!mask) || image_get_mask_pixel(mask, other_x, other_y)) {
|
||||
// Check bounds are within img
|
||||
if (other_x_start + x_off >= img->w || other_y_start + y_off >= img->h) return;
|
||||
if (other_x_end + x_off <= 0 || other_y_end + y_off <= 0) return;
|
||||
|
||||
int other_pixel = imlib_get_pixel(other, other_x, other_y);
|
||||
other_pixel = color_palette
|
||||
? safe_map_pixel(img_bpp, IMAGE_BPP_RGB565, color_palette[other_pixel])
|
||||
: safe_map_pixel(img_bpp, other_bpp, other_pixel);
|
||||
int img_pixel = imlib_get_pixel(img, x_off + x, y_off + y);
|
||||
|
||||
int result_pixel;
|
||||
switch (img_bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
result_pixel = (other_pixel*alpha + img_pixel*neg_alpha)>=0.5?1:0;
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
result_pixel = other_pixel*alpha + img_pixel*neg_alpha;
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
int r = COLOR_RGB565_TO_R5(other_pixel)*alpha + COLOR_RGB565_TO_R5(img_pixel)*neg_alpha;
|
||||
int g = COLOR_RGB565_TO_G6(other_pixel)*alpha + COLOR_RGB565_TO_G6(img_pixel)*neg_alpha;
|
||||
int b = COLOR_RGB565_TO_B5(other_pixel)*alpha + COLOR_RGB565_TO_B5(img_pixel)*neg_alpha;
|
||||
result_pixel = COLOR_R5_G6_B5_TO_RGB565(r, g, b);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result_pixel = 0;
|
||||
break;
|
||||
// If scaling is negative we essentially flip the other coordinates so they work from bottom right instead of top left
|
||||
if (over_xscale < 0) {
|
||||
other_width_scaled--;
|
||||
other_x_start -= other_width_scaled;
|
||||
other_x_end -= other_width_scaled;
|
||||
x_off += other_width_scaled;
|
||||
}
|
||||
|
||||
if (over_yscale < 0) {
|
||||
other_height_scaled--;
|
||||
other_y_start -= other_height_scaled;
|
||||
other_y_end -= other_height_scaled;
|
||||
y_off += other_height_scaled;
|
||||
}
|
||||
|
||||
switch(img_bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
// If alpha is less that 128 on a bitmap we're just copying the image back to the image, so do nothing
|
||||
if (alpha >= 128) {
|
||||
// Iterate the img area to be updated
|
||||
for (int y = other_y_start; y < other_y_end; y++) {
|
||||
uint32_t *img_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y_off + y);
|
||||
|
||||
const int other_y = fast_floorf(y * over_yscale);
|
||||
void *other_row_ptr = imlib_compute_row_ptr(other, other_y);
|
||||
|
||||
for (int x = other_x_start; x < other_x_end; x++) {
|
||||
const int other_x = fast_floorf(x * over_xscale);
|
||||
|
||||
if ((!mask) || image_get_mask_pixel(mask, other_x, other_y)) {
|
||||
uint32_t result_pixel = safe_map_pixel(IMAGE_BPP_BINARY, other_bpp, imlib_get_pixel_fast(other_bpp, other_row_ptr, other_x));
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(img_row_ptr, x_off + x, result_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imlib_set_pixel(img, x_off + x, y_off + y, result_pixel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
// Packaed alpha for SMUAD calls
|
||||
const uint32_t va = (alpha << 16) + (256 - alpha);
|
||||
|
||||
// Iterate the img area to be updated
|
||||
for (int y = other_y_start; y < other_y_end; y++) {
|
||||
// Pre-add x_off here to save adding it inside the central loop
|
||||
uint8_t *img_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y_off + y) + x_off;
|
||||
|
||||
const int other_y = fast_floorf(y * over_yscale);
|
||||
void *other_row_ptr = imlib_compute_row_ptr(other, other_y);
|
||||
|
||||
for (int x = other_x_start; x < other_x_end; x++) {
|
||||
const int other_x = fast_floorf(x * over_xscale);
|
||||
|
||||
if ((!mask) || image_get_mask_pixel(mask, other_x, other_y)) {
|
||||
const uint8_t other_pixel = safe_map_pixel(IMAGE_BPP_GRAYSCALE, other_bpp, imlib_get_pixel_fast(other_bpp, other_row_ptr, other_x));
|
||||
|
||||
uint8_t result_pixel;
|
||||
if (alpha==256) {
|
||||
result_pixel = other_pixel;
|
||||
}
|
||||
else {
|
||||
const uint8_t img_pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(img_row_ptr, x);
|
||||
|
||||
const uint32_t vgs = (other_pixel << 16) + img_pixel;
|
||||
result_pixel = __SMUAD(va, vgs)>>8;
|
||||
}
|
||||
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(img_row_ptr, x, result_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
alpha >>= 3;
|
||||
uint32_t alpha_complement = 32 - alpha;
|
||||
// Iterate the img area to be updated
|
||||
for (int y = other_y_start; y < other_y_end; y++) {
|
||||
// Pre-add x_off here to save adding it inside the central loop
|
||||
uint16_t *img_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y_off + y) + x_off;
|
||||
|
||||
const int other_y = fast_floorf(y * over_yscale);
|
||||
void *other_row_ptr = imlib_compute_row_ptr(other, other_y);
|
||||
|
||||
for (int x = other_x_start; x < other_x_end; x++) {
|
||||
const int other_x = fast_floorf(x * over_xscale);
|
||||
|
||||
if ((!mask) || image_get_mask_pixel(mask, other_x, other_y)) {
|
||||
uint32_t other_pixel = imlib_get_pixel_fast(other_bpp, other_row_ptr, other_x);
|
||||
other_pixel = color_palette
|
||||
? color_palette[other_pixel]
|
||||
: safe_map_pixel(IMAGE_BPP_RGB565, other_bpp, other_pixel);
|
||||
|
||||
uint32_t result_pixel;
|
||||
if (alpha==32) { //256
|
||||
result_pixel = other_pixel;
|
||||
}
|
||||
else {
|
||||
uint32_t img_pixel = IMAGE_GET_RGB565_PIXEL_FAST(img_row_ptr, x);
|
||||
// 0000000000000000-gggbbbbbrrrrrggg
|
||||
|
||||
// ** Extract green component of pixel to high word **
|
||||
img_pixel = img_pixel | (img_pixel << 16);
|
||||
// gggbbbbbrrrrrggg-gggbbbbbrrrrrggg
|
||||
// Rotate to fix endianness
|
||||
img_pixel = __ROR(img_pixel, 8);
|
||||
// rrrrrggggggbbbbb-rrrrrggggggbbbbb
|
||||
// Clear 5 bits per component a multiply
|
||||
img_pixel &= 0x7E0F81F;
|
||||
// 00000gggggg00000-rrrrr000000bbbbb
|
||||
|
||||
// ** Extract green component of pixel to high word **
|
||||
other_pixel = other_pixel | (other_pixel << 16);
|
||||
// gggbbbbbrrrrrggg-gggbbbbbrrrrrggg
|
||||
// Rotate to fix endianness
|
||||
other_pixel = __ROR(other_pixel, 8);
|
||||
// rrrrrggggggbbbbb-rrrrrggggggbbbbb
|
||||
// Clear 5 bits per component for the multiply
|
||||
other_pixel &= 0x7E0F81F;
|
||||
// 00000gggggg00000-rrrrr000000bbbbb
|
||||
|
||||
// Combine foreground and background with alpha applied
|
||||
result_pixel = (img_pixel * alpha_complement) + (other_pixel * alpha);
|
||||
// GGGGGG.....RRRRR-.....0BBBBB..... (.'s are remainders)
|
||||
|
||||
// Round component values
|
||||
result_pixel >>= 5;
|
||||
// 00000GGGGGG.....-RRRRR.....0BBBBB
|
||||
result_pixel &= 0x7E0F81F;
|
||||
// 00000GGGGGG00000-RRRRR000000BBBBB
|
||||
|
||||
// Merge green component back to low word
|
||||
result_pixel = result_pixel | (result_pixel >> 16);
|
||||
// 00000GGGGGG00000-RRRRRGGGGGGBBBBB
|
||||
// Switch endianness
|
||||
result_pixel = __REV16(result_pixel);
|
||||
// GGG0000000000GGG-GGGGBBBBBRRRRRGG
|
||||
// Don't bother rounding off high word it'll get removed on store
|
||||
}
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(img_row_ptr, x, result_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1283,7 +1283,7 @@ void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c, int thickness
|
||||
void imlib_draw_ellipse(image_t *img, int cx, int cy, int rx, int ry, int rotation, int c, int thickness, bool fill);
|
||||
void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c, float scale, int x_spacing, int y_spacing, bool mono_space,
|
||||
int char_rotation, bool char_hmirror, bool char_vflip, int string_rotation, bool string_hmirror, bool string_hflip);
|
||||
void imlib_draw_image(image_t *img, image_t *other, int x_off, int y_off, float x_scale, float y_scale, float alpha, image_t *mask, const uint16_t *color_palette);
|
||||
void imlib_draw_image(image_t *img, image_t *other, int x_off, int y_off, float x_scale, float y_scale, int alpha, image_t *mask, const uint16_t *color_palette);
|
||||
void imlib_flood_fill(image_t *img, int x, int y,
|
||||
float seed_threshold, float floating_threshold,
|
||||
int c, bool invert, bool clear_background, image_t *mask);
|
||||
|
||||
@ -1924,13 +1924,11 @@ STATIC mp_obj_t py_image_draw_image(uint n_args, const mp_obj_t *args, mp_map_t
|
||||
|
||||
float arg_x_scale =
|
||||
py_helper_keyword_float(n_args, args, offset + 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_scale), 1.0f);
|
||||
PY_ASSERT_TRUE_MSG((0.0f <= arg_x_scale), "Error: 0.0 <= x_scale!");
|
||||
float arg_y_scale =
|
||||
py_helper_keyword_float(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_scale), 1.0f);
|
||||
PY_ASSERT_TRUE_MSG((0.0f <= arg_y_scale), "Error: 0.0 <= y_scale!");
|
||||
float arg_alpha =
|
||||
py_helper_keyword_int(n_args, args, offset + 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 256) / 256.0f;
|
||||
PY_ASSERT_TRUE_MSG((0 <= arg_alpha) && (arg_alpha <= 1), "Error: 0 <= alpha <= 256!");
|
||||
int arg_alpha =
|
||||
py_helper_keyword_int(n_args, args, offset + 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 256);
|
||||
PY_ASSERT_TRUE_MSG((0 <= arg_alpha) && (arg_alpha <= 256), "Error: 0 <= alpha <= 256!");
|
||||
image_t *arg_msk =
|
||||
py_helper_keyword_to_image_mutable_mask(n_args, args, offset + 3, kw_args);
|
||||
int palette = py_helper_keyword_int(n_args, args, offset + 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_color_palette), -1);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user