Giter Club home page Giter Club logo

adafruit_circuitpython_imageload's Introduction

Introduction

Documentation Status

Discord

Build Status

Code Style: Black

This library decodes an image file into new bitmap and palette objects of the provided type. It's designed to load code needed during decoding as needed. This is meant to minimize the memory overhead of the decoding code.

Only certain types of bitmaps work with this library, and they often have to be exported in specific ways. To find out what types are supported and how to make them, see this learn guide page.

Usage Example

import board
import displayio
import adafruit_imageload

image, palette = adafruit_imageload.load(
    "images/4bit.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette
)
tile_grid = displayio.TileGrid(image, pixel_shader=palette)

group = displayio.Group()
group.append(tile_grid)
board.DISPLAY.root_group = group
while True:
    pass

Documentation

API documentation for this library can be found on Read the Docs.

For information on building library documentation, please check out this guide.

Contributing

Contributions are welcome! Please read our Code of Conduct before contributing to help this project stay welcoming.

adafruit_circuitpython_imageload's People

Contributors

caternuson avatar cogliano avatar crookedstorm avatar daveputz avatar deshipu avatar dhalbert avatar errolyn avatar evaherrada avatar fionawhim avatar foamyguy avatar jepler avatar jposada202020 avatar kattni avatar kmatch98 avatar ladyada avatar lesamouraipourpre avatar makermelissa avatar matt-land avatar neradoc avatar retiredwizard avatar siehputz avatar sommersoft avatar tannewt avatar tekktrik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

adafruit_circuitpython_imageload's Issues

Errors with CP 4.x

I have problems getting this to work on CircuitPython 4.1.0-beta-0.

If I don't specify the bitmap and palette parameters, the image loads, but then displayio.TileGrid throws:

TypeError: unsupported bitmap type

If I specify bitmap=displayio.Bitmap and palette=displayio.Palette, depending on the depth of the image being loaded I either get:

  File "adafruit_imageload/bmp/__init__.py", line 63, in load
  File "adafruit_imageload/bmp/indexed.py", line 55, in load
TypeError: join expects a list of str/bytes objects consistent with self object

with 4bpp BMP, and:

  File "adafruit_imageload/bmp/__init__.py", line 63, in load
  File "adafruit_imageload/bmp/indexed.py", line 48, in load
OverflowError: overflow converting long int to machine word

with 8bpp BMP.

Loading a 24bpp BMP seems to work, but then displayio.TileGrid throws:

NotImplementedError: True color BMP unsupported

Add PNM support

According to atmakers: One tip: add pnm format next. It’s easy to support and imagemagick converts everything to it

Add JPG to Imageload

Not sure if imageload has JPG capability. RTD is lacking details. Would very much appreciate a JPG simpletest example.

Add buffer option for storing bitmaps to prevent out memory due to fragmentation.

For the case of sequentially loading large bitmaps and then freeing them from memory, it can sometimes cause the RAM to get fragmented so there is no contiguous chunk of memory large enough to hold the next bitmap:

MemoryError: memory allocation failed

It could be useful to have a way to define a bitmap "buffer" into which you can load a bitmap, and prevent memory segmentation. This would have to accommodate the varying color depth ("bits_per_pixel") of bitmaps or require this to be an input when creating the bitmap buffer. This buffer could be added as an optional input parameter to the adafruit_imageload.load function to direct it where to store the loaded bitmap.

The simpletest example doesn't show an image

The simpletest example here: imageload_simpletest.py does not actually show any thing on the screen. The example creates an image but then doesn't prove it's existence in any way to the user (i.e. show it on the screen)

From a new users perspective it's going to seem like "nothing happened"

It should get updated to add the image to the screen and have a main loop to keep it showing until stopped.

Docs - Download link needs updated

The Download link on the docs page has an extra underline in it leading to 404 on github.

image

current URL:

https://github.com/adafruit/Adafruit__CircuitPython_ImageLoad/releases/latest

Possible removal of 'type juggling' of references (mypy related)

I am working on #56, and having is some difficulty. I am using mypy, which was used in the original issue to identity files with untyped functions.
Mypy has exposed a code smell in this repo (type juggling- example below), which was originally intentionally, for the sake of saving memory in CircuitPython.

class Palette:
    """ ... implemented in Adafruit_Blinka_Display.displayio """
palette = Palette.init  # palette's type is a Callable (we may or not need this object later, so lets defer and pass down a reference to a Callable)
... its later, in a different file ...
def load(..., palette = None, ...):
    ....
    palette = palette(*args) # palette's type is now Palette instance
    palette.make_transparent()  # mypy is now really angry, because a Callable shouldn't have any methods 

There is a certain elegance to the way it was done: reuse the reference to the Callable, possibly allowing deallocation of the reference to the looked up function. But this is counter to what Mypy expects, because its a behavior commonly associated with having a bug in your code: Developer thinks they have a foo, but they accidentally overwrote it somewhere with variable name reuse.

So discuss the balance between "we want working type annotations, so the code is understandable and maintainable" and "we need to save memory through tricks, and are ok with here be dragons". Scott wrote & I refactored a lot of the original code 3 years ago. Now looking back, It took me ~3 hours to determine what was going on, meaning there is a high barrier to entry for contributors. I'll be in the camp for "slay the dragons", and stop reusing variable names.

I know there are inline mypy decorators that can be dropped throughout the code to help get closer to mypy 'passing' by explicitly indicating when it happens, but its going to take a lot of them (every time the type changes) and be precarious to maintain. Inline decorators are intended to be used in exceptional cases, but there are many in the repo. So not a real option.

So:

  • compromise on types: add best effort types, mypy never passes, so automated tooling can't be added to maintain types in the repo. Memory footprint is maintained, but the repo is still difficult to contribute to, and teaches anti-patterns.
  • compromise on memory: a future release of imageload removes type juggling at the cost of slightly higher memory usage, and type checking can be added as a commit hook. Worst case is some memory constrained board that worked with a past version of the library now runs out of memory.

Missing Type Annotations

There are missing type annotations for some functions in this library.

The typing module does not exist on CircuitPython devices so the import needs to be wrapped in try/except to catch the error for missing import. There is an example of how that is done here:

try:
    from typing import List, Tuple
except ImportError:
    pass

Once imported the typing annotations for the argument type(s), and return type(s) can be added to the function signature. Here is an example of a function that has had this done already:

def wrap_text_to_pixels(
    string: str, max_width: int, font=None, indent0: str = "", indent1: str = ""
) -> List[str]:

If you are new to Git or Github we have a guide about contributing to our projects here: https://learn.adafruit.com/contribute-to-circuitpython-with-git-and-github

There is also a guide that covers our CI utilities and how to run them locally to ensure they will pass in Github Actions here: https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/check-your-code In particular the pages: Sharing docs on ReadTheDocs and Check your code with pre-commit contain the tools to install and commands to run locally to run the checks.

If you are attempting to resolve this issue and need help, you can post a comment on this issue and tag both @FoamyGuy and @kattni or reach out to us on Discord: https://adafru.it/discord in the #circuitpython-dev channel.

The following locations are reported by mypy to be missing type annotations:

  • adafruit_imageload/pnm/ppm_binary.py:23
  • adafruit_imageload/pnm/ppm_ascii.py:23
  • adafruit_imageload/pnm/ppm_ascii.py:54
  • adafruit_imageload/pnm/pbm_binary.py:23
  • adafruit_imageload/pnm/pbm_binary.py:44
  • adafruit_imageload/pnm/pbm_binary.py:53
  • adafruit_imageload/pnm/pbm_ascii.py:23
  • adafruit_imageload/pnm/pgm/binary.py:19
  • adafruit_imageload/pnm/pgm/binary.py:43
  • adafruit_imageload/pnm/pgm/ascii.py:19
  • adafruit_imageload/pnm/pgm/ascii.py:55
  • adafruit_imageload/bmp/negative_height_check.py:12
  • adafruit_imageload/gif.py:23
  • adafruit_imageload/gif.py:60
  • adafruit_imageload/gif.py:81
  • adafruit_imageload/gif.py:98
  • adafruit_imageload/gif.py:112
  • adafruit_imageload/gif.py:136
  • adafruit_imageload/pnm/pgm/__init__.py:20
  • adafruit_imageload/pnm/__init__.py:23
  • adafruit_imageload/bmp/indexed.py:26
  • adafruit_imageload/bmp/indexed.py:125
  • adafruit_imageload/bmp/__init__.py:20
  • adafruit_imageload/__init__.py:20

BMP RLE error or support?

Hi! I ran into a problem where ImageMagick’s convert was generating RLE-compressed BMP images, which were then displaying as garbage when I loaded them.

Would you accept a PR that either:

  • Added support for loading an RLE-compressed image
  • Raised an Error if it detected an RLE-compressed image (similar to how it errors on "true color" BMPs now)

I’m happy to provide either.

Possible issue with new readinto functionality

I'm getting this error raised from the simpletest script in this repo:

Traceback (most recent call last):
  File "code.py", line 8, in <module>
  File "adafruit_imageload/__init__.py", line 57, in load
  File "adafruit_imageload/__init__.py", line 48, in load
  File "adafruit_imageload/bmp/__init__.py", line 59, in load
  File "adafruit_imageload/bmp/indexed.py", line 91, in load
TypeError: extra keyword arguments given

My device is:

Adafruit CircuitPython 6.2.0-beta.4 on 2021-03-18; Adafruit Feather RP2040 with rp2040

Imageload palette has no len()

Im trying to dynamically index a palette that is being loaded via imageload in circuitpython, however, when i try to get the length of the palette an error occurs saying "object of type 'ColorConverter' has no len()" code sample below:

source_bitmap, source_palette = adafruit_imageload.load("bitmap.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)
test = len(source_palette)

adafruit_imageload missing 'bmp' directory in Adafruit_CircuitPython_Bundle releases

The adafruit_imageload library includes a bmp directory in its source which is not included in the Adafruit_CircuitPython_Bundle releases. As a result, importing and trying to use adafruit_imageload from Adafruit_CircuitPython_Bundle results in the error:

ImportError: no module named 'adafruit_imageload.bmp'

The problem can be fixed by manually dragging the bmp directory from adafruit_imageload source to the adafruit_imageload directory in the Adafruit_CircuitPython_Bundle release. It should probably be corrected in the Adafruit_CircuitPython_Bundle release build process however.

Pylint: import-outside-toplevel

Here's what I get when I run pylint 2.4 locally.

************* Module adafruit_imageload.bmp
adafruit_imageload/bmp/__init__.py:68:4: C0415: Import outside toplevel (%s) (import-outside-toplevel)
************* Module adafruit_imageload
adafruit_imageload/__init__.py:49:12: C0415: Import outside toplevel (%s) (import-outside-toplevel)
adafruit_imageload/__init__.py:53:12: C0415: Import outside toplevel (%s) (import-outside-toplevel)
adafruit_imageload/__init__.py:57:12: C0415: Import outside toplevel (%s) (import-outside-toplevel)
************* Module adafruit_imageload.pnm
adafruit_imageload/pnm/__init__.py:53:16: C0415: Import outside toplevel (%s) (import-outside-toplevel)
adafruit_imageload/pnm/__init__.py:60:16: C0415: Import outside toplevel (%s) (import-outside-toplevel)
adafruit_imageload/pnm/__init__.py:67:16: C0415: Import outside toplevel (%s) (import-outside-toplevel)
adafruit_imageload/pnm/__init__.py:79:16: C0415: Import outside toplevel (%s) (import-outside-toplevel)
adafruit_imageload/pnm/__init__.py:85:12: C0415: Import outside toplevel (%s) (import-outside-toplevel)
************* Module adafruit_imageload.pnm.pgm
adafruit_imageload/pnm/pgm/__init__.py:43:8: C0415: Import outside toplevel (%s) (import-outside-toplevel)
adafruit_imageload/pnm/pgm/__init__.py:48:8: C0415: Import outside toplevel (%s) (import-outside-toplevel)

bmp/__init__.py
The lines in question:

    from . import indexed

    return indexed.load(
        file,
        width,
        height,
        data_start,
        colors,
        color_depth,
        compression,
        bitmap=bitmap,
        palette=palette,
    )

__init__.py
The lines in question:

        if header.startswith(b"BM"):
            from . import bmp

            return bmp.load(file, bitmap=bitmap, palette=palette)
        if header.startswith(b"P"):
            from . import pnm

            return pnm.load(file, header, bitmap=bitmap, palette=palette)
        if header.startswith(b"GIF"):
            from . import gif

pnm/__init__.py
The lines in question:

            if magic_number in [b"P2", b"P5"]:
                from . import pgm

                return pgm.load(
                    file, magic_number, pnm_header, bitmap=bitmap, palette=palette
                )

            if magic_number == b"P3":
                from . import ppm_ascii

                return ppm_ascii.load(
                    file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
                )

            if magic_number == b"P6":
                from . import ppm_binary

                return ppm_binary.load(
                    file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
                )

and

            if magic_number.startswith(b"P1"):
                from . import pbm_ascii

                return pbm_ascii.load(
                    file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
                )

            from . import pbm_binary

            return pbm_binary.load(
                file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
            )

pnm/pgm/__init__.py
And finally:

    if magic_number == b"P2":  # To handle ascii PGM files.
        from . import ascii as pgm_ascii

        return pgm_ascii.load(file, width, height, bitmap=bitmap, palette=palette)

    if magic_number == b"P5":  # To handle binary PGM files.
        from . import binary

        return binary.load(file, width, height, bitmap=bitmap, palette=palette)

I'm not sure how resource-intensive the imported libraries are, but I'd assume for something that loads images, they might take up quite a bit of memory. It'd be useful to have someone test this on a board with a smallish amount of memory to see if it still works when everything is imported at once at the top of the file.

Referencing main issue: adafruit/Adafruit_CircuitPython_Bundle#232

allow imageload to work with file-like objects

The conclusion from #4831 is that we want to modify this library to be able to handle file-like objects such as BytesIO objects.

This would allow loading images directly from memory without needing to have them in file storage anywhere.

Ideally it should be able to work with images fetched from the web.

PNG file don't show properly. (wrong order in index?)

I've found a bug when I upload a PNG file. The image load as the right size but the index of color seems to be all over the place.

I tested both PNG and BMP and the BMP file works great.

# SPDX-FileCopyrightText: 2020 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# This example implements a simple two line scroller using
# Adafruit_CircuitPython_Display_Text. Each line has its own color
# and it is possible to modify the example to use other fonts and non-standard
# characters.

import adafruit_display_text.label
import board
import time
import ulab
import displayio
import framebufferio
import rgbmatrix
import terminalio
import adafruit_imageload

displayio.release_displays()

matrix = rgbmatrix.RGBMatrix(
    width=64, height=32, bit_depth=3,
    rgb_pins=[
        board.MTX_R1,
        board.MTX_G1,
        board.MTX_B1,
        board.MTX_R2,
        board.MTX_G2,
        board.MTX_B2
    ],
    addr_pins=[
        board.MTX_ADDRA,
        board.MTX_ADDRB,
        board.MTX_ADDRC,
        board.MTX_ADDRD
    ],
    clock_pin=board.MTX_CLK,
    latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE,
)

display = framebufferio.FramebufferDisplay(matrix)

# load png image
image, palette = adafruit_imageload.load("ANA.png")

print(f"Logo is {image.width}x{image.height}")

# make tilegrid for the loaded image
tile_grid = displayio.TileGrid(image, pixel_shader=palette)

print(f"TileGrid is {image.width}x{image.height}")

# Put each line of text into a Group, then show that group.
g = displayio.Group()

# add loaded image tilegrid
g.append(tile_grid)
display.show(g)

# You can add more effects in this loop. For instance, maybe you want to set the
# color of each label to a different value.
while True:
    display.refresh(minimum_frames_per_second=0)

PNG version

20231031_085443

BMP version
20231031_085429

AttributeError: 'Bitmap' object has no attribute '_load_row'

Guessing something's changed and just needs updating here.

Adafruit CircuitPython 4.0.0-beta.5 on 2019-03-17; Adafruit PyPortal with samd51j20
>>> import displayio
>>> import adafruit_imageload
>>> image, palette = adafruit_imageload.load("/4bit.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "adafruit_imageload/__init__.py", line 51, in load
  File "adafruit_imageload/__init__.py", line 49, in load
  File "adafruit_imageload/bmp/__init__.py", line 56, in load
  File "adafruit_imageload/bmp/indexed.py", line 80, in load
AttributeError: 'Bitmap' object has no attribute '_load_row'
>>> 

Provide default behavior for bitmap and palette parameters?

This is currently the simplest load statement:

image, palette = adafruit_imageload.load("omg_cute_kitteh.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)

Those options for the bitmap and palette parameter probably cover the majority of use cases. Could we make them default? That would then allow for:

image, palette = adafruit_imageload.load("omg_cute_kitteh.bmp")

The above statement currently runs without error, but returns NoneTypes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.