Memory Error png image

Hello!
I am using an M5stack (esp32) with 520kb RAM. I have successfully ported lvgl and I am able to render things like buttons etc.
Now I am trying to render an image (a logo when the device boots for the first time) but I am not able to do it, every time ( no matter the image size) I end up with MemoryError: memory allocation failed and it doesn’t seem to be connected with the image size. I already tried images with 100kb and with 2kb.
Right now I am following the example in https://docs.littlevgl.com/en/html/object-types/img.html
My code is:

import ustruct as struct

import lodepng as png
import lvgl as lv
import lvesp32
lv.init()
lv.log_register_print_cb(lambda level,path,line,msg: print('%s(%d): %s' % (path, line, msg)))
from ili9341 import ili9341
from imagetools import get_png_info, open_png

disp = ili9341()

# Register PNG image decoder
decoder = lv.img.decoder_create()
decoder.info_cb = get_png_info
decoder.open_cb = open_png

# Create a screen with a draggable image

with open('logo320240.png','rb') as f:
  png_data = f.read()

png_img_dsc = lv.img_dsc_t({
    'data_size': len(png_data),
    'data': png_data 
})

scr = lv.scr_act()

# Create an image on the left using the decoder

# lv.img.cache_set_size(2)
img1 = lv.img(scr)
img1.align(scr, lv.ALIGN.IN_TOP_LEFT, 0, 0)
img1.set_src(png_img_dsc)

I am quiet new to this and I am finding a bit difficult to find info about lvgl micropython usage, so let me know if I have done something wrong.

PS: Running gc.mem_free() before running anything else gives me ± 90000 bytes available.

Made another test based on https://github.com/littlevgl/lv_binding_micropython/blob/d564c86bccec5b4ac0e6e1c035921221d5dccbb6/examples/example1.py

My code:

# init

import ustruct as struct
import lvgl as lv
import lvesp32
lv.log_register_print_cb(lambda level,path,line,msg: print('%s(%d): %s' % (path, line, msg)))
from ili9341 import ili9341

disp = ili9341()

with open('logo2.bin','rb') as f:
  img_data = f.read()
# Create a screen with a draggable image

scr = lv.obj()
img = lv.img(scr)
img.align(scr, lv.ALIGN.IN_TOP_LEFT, 0, 0)
img_dsc = lv.img_dsc_t({
    'header':{
        'always_zero': 0,
        'w':320,
        'h':240,
        'cf':lv.img.CF.INDEXED_1BIT
    },
    'data_size': len(img_data),
    'data': img_data
})

img.set_src(img_dsc)

# Load the screen and display image

lv.scr_load(scr)

Error:

Traceback (most recent call last):
File “”, line 1, in
MemoryError: memory allocation failed, allocating 47896 bytes

So it doesn’t matter the picture size I always get a memory error… Even with the test picture from github I get the error.

Now I get a deformed picture on screen but it looks like it is appearing multiple times (like many copies at the same time at a different point in x.

Don’t really know where to go from here… If someone can give me a hand I would be grateful!

Even if the PNG is 20KB image, it can easily represent 200x150 image which would consume uncompressed 90KB (200×150×3 with 24bit colors). In a addition to that, lodepng library consumes more RAM for decompressing and processing the image, so if you have only 90KB it’s just not enough for storing the raw images.

Another thing that could limit you is lvgl image caching.
When lvgl caches images it consumes more ram. You can try disabling it by calling lv.img_cache_set_size(0).

I’m using ESP32 with psram so I have more than enough memory. With enough memory it works fine.

Probably your screen or image dimensions are different. Are you sure w=320 and h=240? not the other way around?

Why don’t you try smaller raw image? You specified the image size as 320x200, that would require allocating 192KB (320 x 200 x 3, with 24bit colors), much more than you have available.

Thank you for your help @amirgon !

aaah I wasn’t expecting this… When I was using the M5stack libraries to open imgs I had no problems with this exact same image. So I was not worried when I changed to littlevgl, this is unfortunate.

This doesn’t seem to work. The correct code would be lv.img.cache_set_size(0) but I get:
>>> …/…/lib/lv_bindings/lvgl/src/lv_draw/lv_img_cache.c(68): lv_img_cache_open: the cache size is 0
…/…/lib/lv_bindings/lvgl/src/lv_draw/lv_draw_img.c(62): Image draw error
…/…/lib/lv_bindings/lvgl/src/lv_draw/lv_img_cache.c(68): lv_img_cache_open: the cache size is 0
…/…/lib/lv_bindings/lvgl/src/lv_draw/lv_draw_img.c(62): Image draw error
…/…/lib/lv_bindings/lvgl/src/lv_draw/lv_img_cache.c(68): lv_img_cache_open: the cache size is 0
…/…/lib/lv_bindings/lvgl/src/lv_draw/lv_draw_img.c(62): Image draw error
…/…/lib/lv_bindings/lvgl/src/lv_draw/lv_img_cache.c(68): lv_img_cache_open: the cache size is 0
…/…/lib/lv_bindings/lvgl/src/lv_draw/lv_draw_img.c(62): Image draw error

So I imagine that I can’t set the cache to 0.

Yes, I am sure, I am using a M5stack which has a landscape screen (I have rotated it in the drivers).

So now I tried a 60x40 image and worked fine with the decoder (I can’t make it work with binary files with the code I posted above).

But this is very frustrating… I really wanted to use a full size image, I have done it before but just not with littlevgl, isn’t there any other way to do this? I would be very grateful if someone come up with a solution.

Once again, thank you for your time and help!

Your problem is RAM.
ESP32 has enough ram (520 KiB), even without psram.
Try to figure out what is taking so much RAM.
Here are some suggestions:

  • Try to use frozen modules in micropython. Freeze both your sources and external dependencies you have. Frozen modules consume flash instead of RAM.
  • When initializing ili9341 driver, use single buffer mode with higher factor. Set double_buffer=False and factor=8 for example.
  • Examine your code and optimize memory usage.