diff --git a/src/omv/img/bmp.c b/src/omv/img/bmp.c index 054108723..a11396541 100644 --- a/src/omv/img/bmp.c +++ b/src/omv/img/bmp.c @@ -21,7 +21,8 @@ static uint32_t bmp_fmt; static uint32_t bmp_row_bytes; // This function inits the geometry values of an image. -int bmp_read_geometry(FIL *fp, image_t *img, const char *path) +int bmp_read_geometry(FIL *fp, image_t *img, const char *path, + bool *w_flipped, bool *h_flipped) { FRESULT res = f_open(fp, path, FA_READ|FA_OPEN_EXISTING); if (res != FR_OK) { @@ -112,6 +113,13 @@ int bmp_read_geometry(FIL *fp, image_t *img, const char *path) return -1; } + if (w_flipped) { + *w_flipped = (bmp_w < 0); // Raster scan perspective + } + if (h_flipped) { + *h_flipped = (bmp_h >= 0); // Raster scan perspective + } + return FR_OK; } @@ -129,8 +137,8 @@ int bmp_read_pixels(FIL *fp, image_t *img, int line_start, int line_end) uint8_t pixel; READ_BYTE(fp, &pixel); if (j < img->w) { - if (bmp_h < 0) { // vertical flip - if (bmp_w < 0) { // horizontal flip + if (bmp_h < 0) { // vertical flip (BMP file perspective) + if (bmp_w < 0) { // horizontal flip (BMP file perspective) IM_SET_GS_PIXEL(img, (img->w-j-1), i, pixel); } else { IM_SET_GS_PIXEL(img, j, i, pixel); @@ -153,8 +161,8 @@ int bmp_read_pixels(FIL *fp, image_t *img, int line_start, int line_end) READ_WORD(fp, &pixel); IM_SWAP16(pixel); if (j < img->w) { - if (bmp_h < 0) { // vertical flip - if (bmp_w < 0) { // horizontal flip + if (bmp_h < 0) { // vertical flip (BMP file perspective) + if (bmp_w < 0) { // horizontal flip (BMP file perspective) IM_SET_RGB565_PIXEL(img, (img->w-j-1), i, pixel); } else { IM_SET_RGB565_PIXEL(img, j, i, pixel); @@ -206,7 +214,7 @@ static int bmp_read_int(image_t *img, const char *path) { FIL fp; - int res = bmp_read_geometry(&fp, img, path); + int res = bmp_read_geometry(&fp, img, path, NULL, NULL); if (res != FR_OK) { return res; } diff --git a/src/omv/img/imlib.c b/src/omv/img/imlib.c index 7c7575024..307841f17 100644 --- a/src/omv/img/imlib.c +++ b/src/omv/img/imlib.c @@ -10,9 +10,11 @@ #include #include #include +#include #include "array.h" #include "imlib.h" #include "ff.h" +#include "ff_wrapper.h" #include "mdefs.h" #include "font.h" #include "fb_alloc.h" @@ -24,6 +26,76 @@ extern const int8_t lab_table[196608]; /* Grayscale [0..255] to rainbox lookup */ extern const uint16_t rainbow_table[256]; +//////////////////////////////////////////////////////////////////////////////// + +#define IMLIB_READ_FUNCTION_LINE_BUFFER (32) + +static enum { PPM_IMAGE, BMP_IMAGE, JPEG_IMAGE } imlib_image_type; + +int imlib_read_geometry(FIL *fp, image_t *image, const char *path, + bool *w_flipped, bool *h_flipped, bool disable_jpeg) +{ + FRESULT res = f_open(fp, path, FA_READ|FA_OPEN_EXISTING); + if (res != FR_OK) { + return res; + } + + char magic[2]; + READ_DATA(fp, &magic, 2); + + res = f_close(fp); + if (res != FR_OK) { + return res; + } + + if ((magic[0]=='P') + && ((magic[1]=='2') || (magic[1]=='3') + || (magic[1]=='5') || (magic[1]=='6'))) { // PPM + imlib_image_type = PPM_IMAGE; + *w_flipped = false; + *h_flipped = false; + return ppm_read_geometry(fp, image, path); + } else if ((magic[0]=='B') && (magic[1]=='M')) { // BMP + imlib_image_type = BMP_IMAGE; + return bmp_read_geometry(fp, image, path, w_flipped, h_flipped); + } else if ((magic[0]==0xFF) && (magic[1]==0xD8)) { // JPEG + if (disable_jpeg) { + nlr_jump(mp_obj_new_exception_msg( + &mp_type_OSError, + "Operation not supported on JPEG")); + } + imlib_image_type = JPEG_IMAGE; + *w_flipped = false; + *h_flipped = false; + return jpeg_read_geometry(fp, image, path); + } + + return -1; +} + +int imlib_read_pixels(FIL *fp, image_t *img, int line_start, int line_end) +{ + switch (imlib_image_type) { + case PPM_IMAGE: return ppm_read_pixels(fp, img, line_start, line_end); + case BMP_IMAGE: return bmp_read_pixels(fp, img, line_start, line_end); + default: return jpeg_read_pixels(fp, img); + } +} + +/* those just call ppm for now */ +int imlib_load_image(image_t *image, const char *path) +{ + // needs to be redone still + return ppm_read(image, path); +} + +int imlib_save_image(image_t *image, const char *path, rectangle_t *roi) +{ + return ppm_write_subimg(image, path, roi); +} + +//////////////////////////////////////////////////////////////////////////////// + // Get pixel (handles boundary check and image type check). int imlib_get_pixel(image_t *img, int x, int y) { @@ -46,16 +118,7 @@ void imlib_set_pixel(image_t *img, int x, int y, int p) } } -/* those just call ppm for now */ -int imlib_load_image(image_t *image, const char *path) -{ - return ppm_read(image, path); -} - -int imlib_save_image(image_t *image, const char *path, rectangle_t *roi) -{ - return ppm_write_subimg(image, path, roi); -} +//////////////////////////////////////////////////////////////////////////////// void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c) { @@ -138,6 +201,8 @@ void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int } } +//////////////////////////////////////////////////////////////////////////////// + void imlib_binary(image_t *img, int num_thresholds, simple_color_t *l_thresholds, simple_color_t *h_thresholds, bool invert) { if (IM_IS_GS(img)) { @@ -443,6 +508,8 @@ float imlib_orientation_degrees(image_t *img, int *sum, int *x_center, int *y_ce return (imlib_orientation_radians(img, sum, x_center, y_center, r) * 180.0) / M_PI; } +//////////////////////////////////////////////////////////////////////////////// + void imlib_negate(image_t *img) { if (IM_IS_GS(img)) { @@ -491,6 +558,8 @@ void imlib_difference(image_t *img, const char *file, image_t *other) } } +//////////////////////////////////////////////////////////////////////////////// + uint32_t imlib_lab_distance(struct color *c0, struct color *c1) { uint32_t sum=0; diff --git a/src/omv/img/imlib.h b/src/omv/img/imlib.h index af12a78d5..d0d022b2b 100644 --- a/src/omv/img/imlib.h +++ b/src/omv/img/imlib.h @@ -280,6 +280,27 @@ typedef enum interp { INTERP_BICUBIC } interp_t; +/* Image file functions */ +int ppm_read_geometry(FIL *fp, image_t *img, const char *path); +int ppm_read_pixels(FIL *fp, image_t *img, int line_start, int line_end); +int ppm_read(image_t *img, const char *path); +int ppm_write_subimg(image_t *img, const char *path, rectangle_t *r); +int bmp_read_geometry(FIL *fp, image_t *img, const char *path, + bool *w_flipped, bool *h_flipped); +int bmp_read_pixels(FIL *fp, image_t *img, int line_start, int line_end); +int bmp_read(image_t *img, const char *path); +int bmp_write_subimg(image_t *img, const char *path, rectangle_t *r); +int jpeg_read_geometry(FIL *fp, image_t *img, const char *path); +int jpeg_read_pixels(FIL *fp, image_t *img); +int jpeg_read(image_t *img, const char *path); +int jpeg_write(image_t *img, const char *path); +int imlib_read_geometry(FIL *fp, image_t *img, const char *path, + bool *w_flipped, bool *h_flipped, bool disable_jpeg); +int imlib_read_pixels(FIL *fp, image_t *img, int line_start, int line_end); +int imlib_load_image(image_t *image, const char *path); +int imlib_save_image(image_t *image, const char *path, rectangle_t *roi); + +/* Basic image functions */ int imlib_get_pixel(image_t *img, int x, int y); void imlib_set_pixel(image_t *img, int x, int y, int p); @@ -288,18 +309,6 @@ point_t *point_alloc(int16_t x, int16_t y); bool point_equal(point_t *p1, point_t *p2); float point_distance(point_t *p1, point_t *p2); -/* Image file functions */ -int ppm_read_geometry(FIL *fp, image_t *img, const char *path); -int ppm_read_pixels(FIL *fp, image_t *img, int line_start, int line_end); -int ppm_read(image_t *img, const char *path); -int ppm_write_subimg(image_t *img, const char *path, rectangle_t *r); -int bmp_read_geometry(FIL *fp, image_t *img, const char *path); -int bmp_read_pixels(FIL *fp, image_t *img, int line_start, int line_end); -int bmp_read(image_t *img, const char *path); -int bmp_write_subimg(image_t *img, const char *path, rectangle_t *r); -int imlib_load_image(image_t *image, const char *path); -int imlib_save_image(image_t *image, const char *path, rectangle_t *roi); - /* Rectangle functions */ rectangle_t *rectangle_alloc(int16_t x, int16_t y, int16_t w, int16_t h); bool rectangle_equal(rectangle_t *r1, rectangle_t *r2); @@ -349,7 +358,6 @@ void imlib_histeq(struct image *src); void imlib_median_filter(image_t *src, int r); void imlib_erode(image_t *src, int ksize); void imlib_dilate(image_t *src, int ksize); -void imlib_invert(image_t *src); void imlib_threshold(image_t *src, image_t *dst, color_t *color, int color_size, int threshold); void imlib_rainbow(image_t *src, struct image *dst); array_t *imlib_count_blobs(struct image *image); diff --git a/src/omv/img/jpeg.c b/src/omv/img/jpeg.c index 14698ca48..d57369aa5 100644 --- a/src/omv/img/jpeg.c +++ b/src/omv/img/jpeg.c @@ -11,6 +11,7 @@ #include #include "ff.h" +#include "ff_wrapper.h" #include "xalloc.h" #include "imlib.h" #include "stm32f4xx_hal.h" @@ -632,3 +633,109 @@ void jpeg_compress(image_t *src, image_t *dst, int quality) printf("time: %lums\n", HAL_GetTick() - start); #endif } + +// This function inits the geometry values of an image. +int jpeg_read_geometry(FIL *fp, image_t *img, const char *path) +{ + FRESULT res = f_open(fp, path, FA_READ|FA_OPEN_EXISTING); + if (res != FR_OK) { + return res; + } + + for (;;) { + uint16_t header; + READ_WORD(fp, &header); + IM_SWAP16(header); + if ((0xFFD0 <= header) && (header <= 0xFFD9)) { + continue; + } else if (((0xFFC0 <= header) && (header <= 0xFFCF)) + || ((0xFFDA <= header) && (header <= 0xFFDF)) + || ((0xFFE0 <= header) && (header <= 0xFFEF)) + || ((0xFFF0 <= header) && (header <= 0xFFFE))) + { + uint16_t size; + READ_WORD(fp, &size); + IM_SWAP16(size); + if (((0xFFC0 <= header) && (header <= 0xFFC3)) + || ((0xFFC5 <= header) && (header <= 0xFFC7)) + || ((0xFFC9 <= header) && (header <= 0xFFCB)) + || ((0xFFCD <= header) && (header <= 0xFFCF))) + { + READ_BYTE_IGNORE(fp); + uint16_t width; + READ_WORD(fp, &width); + IM_SWAP16(width); + uint16_t height; + READ_WORD(fp, &height); + IM_SWAP16(height); + img->w = width; + img->h = height; + img->bpp = f_size(fp); + return FR_OK; + } + FRESULT res = f_lseek(fp, f_tell(fp) + size - 2); + if (res != FR_OK) { + f_close(fp); + return res; + } + } else { + f_close(fp); + return -1; + } + } +} + +// This function reads the pixel values of an image. +int jpeg_read_pixels(FIL *fp, image_t *img) +{ + FRESULT res = f_lseek(fp, 0); + if (res != FR_OK) { + f_close(fp); + return res; + } + READ_DATA(fp, img->pixels, img->bpp); + return FR_OK; +} + +static int jpeg_read_int(image_t *img, const char *path) +{ + FIL fp; + + int res = jpeg_read_geometry(&fp, img, path); + if (res != FR_OK) { + return res; + } + + if (!img->pixels) { // don't allocate if already allocated... + img->pixels = xalloc(img->bpp); + } + + res = jpeg_read_pixels(&fp, img); + if (res != FR_OK) { + return res; + } + + return f_close(&fp); +} + +int jpeg_read(image_t *img, const char *path) +{ + const uint8_t *backup = img->pixels; + const int res = jpeg_read_int(img, path); + // free image if I didn't start with one... + if ((res != FR_OK) && (!backup) && (img->pixels)) { + xfree(img->pixels); + } + return res; +} + +int jpeg_write(image_t *img, const char *path) +{ + FIL fp; + FRESULT res = f_open(&fp, path, FA_WRITE|FA_CREATE_ALWAYS); + if (res != FR_OK) { + return res; + } + WRITE_DATA(&fp, img->pixels, img->bpp); + return f_close(&fp); +}