From 34f32dc2453585086e24ee36796ea36d1082e317 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 10:18:32 -0600 Subject: [PATCH 1/8] overlay feature and example --- adafruit_pycamera/__init__.py | 103 +++++++++++++++++- examples/basic_camera/code.py | 6 +- examples/overlay/code.py | 74 +++++++++++++ examples/overlay/heart_frame_rgb888.bmp | Bin 0 -> 230538 bytes .../overlay/heart_frame_rgb888.bmp.license | 2 + examples/overlay/pencil_frame_rgb888.bmp | Bin 0 -> 307338 bytes .../overlay/pencil_frame_rgb888.bmp.license | 2 + 7 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 examples/overlay/code.py create mode 100644 examples/overlay/heart_frame_rgb888.bmp create mode 100644 examples/overlay/heart_frame_rgb888.bmp.license create mode 100644 examples/overlay/pencil_frame_rgb888.bmp create mode 100644 examples/overlay/pencil_frame_rgb888.bmp.license diff --git a/adafruit_pycamera/__init__.py b/adafruit_pycamera/__init__.py index e0fa35f..071b2df 100644 --- a/adafruit_pycamera/__init__.py +++ b/adafruit_pycamera/__init__.py @@ -3,7 +3,8 @@ # # SPDX-License-Identifier: MIT """Library for the Adafruit PyCamera with OV5640 autofocus module""" - +# pylint: disable=too-many-lines +import gc import os import struct import time @@ -32,6 +33,11 @@ from adafruit_display_text import label from digitalio import DigitalInOut, Pull from rainbowio import colorwheel +from displayio import Bitmap, ColorConverter, Colorspace +from jpegio import JpegDecoder +import ulab.numpy as np +from adafruit_bitmapsaver import save_pixels +import adafruit_imageload __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PyCamera.git" @@ -147,7 +153,7 @@ class PyCameraBase: # pylint: disable=too-many-instance-attributes,too-many-pub espcamera.FrameSize.QVGA, # 320x240 # espcamera.FrameSize.CIF, # 400x296 # espcamera.FrameSize.HVGA, # 480x320 - espcamera.FrameSize.VGA, # 640x480 + espcamera.FrameSize.VGA, # 640x480 espcamera.FrameSize.SVGA, # 800x600 espcamera.FrameSize.XGA, # 1024x768 espcamera.FrameSize.HD, # 1280x720 @@ -230,6 +236,12 @@ def __init__(self) -> None: # pylint: disable=too-many-statements self.display = None self.pixels = None self.sdcard = None + self._last_saved_image_filename = None + self.decoder = None + self._overlay = None + self.overlay_transparency_color = None + self.overlay_bmp = None + self.combined_bmp = None self.splash = displayio.Group() # Reset display and I/O expander @@ -797,6 +809,7 @@ def open_next_image(self, extension="jpg"): os.stat(filename) except OSError: break + self._last_saved_image_filename = filename print("Writing to", filename) return open(filename, "wb") @@ -827,6 +840,78 @@ def capture_jpeg(self): else: print("# frame capture failed") + @property + def overlay(self) -> str: + """ + The overlay file to be used. A filepath string that points + to a .bmp file that has 24bit RGB888 Colorspace. + The overlay image will be shown in the camera preview, + and combined to create a modified version of the + final photo. + """ + return self._overlay + + @overlay.setter + def overlay(self, new_overlay_file: str) -> None: + if self.overlay_bmp is not None: + self.overlay_bmp.deinit() + self._overlay = new_overlay_file + cc888 = ColorConverter(input_colorspace=Colorspace.RGB888) + self.overlay_bmp, _ = adafruit_imageload.load(new_overlay_file, palette=cc888) + + arr = np.frombuffer(self.overlay_bmp, dtype=np.uint16) + arr.byteswap(inplace=True) + + del arr + + def _init_jpeg_decoder(self): + """ + Initialize the JpegDecoder if it hasn't been already. + Only needed if overlay is used. + """ + if self.decoder is None: + self.decoder = JpegDecoder() + + def blit_overlay_into_last_capture(self): + """ + Create a modified version of the last photo taken that pastes + the overlay image on top of the photo and saves the new version + in a separate but similarly named .bmp file on the SDCard. + """ + if self.overlay_bmp is None: + raise ValueError( + "Must set overlay before calling blit_overlay_into_last_capture" + ) + + self._init_jpeg_decoder() + + width, height = self.decoder.open(self._last_saved_image_filename) + photo_bitmap = Bitmap(width, height, 65535) + + self.decoder.decode(photo_bitmap, scale=0, x=0, y=0) + + bitmaptools.blit( + photo_bitmap, + self.overlay_bmp, + 0, + 0, + skip_source_index=self.overlay_transparency_color, + skip_dest_index=None, + ) + + cc565_swapped = ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED) + save_pixels( + self._last_saved_image_filename.replace(".jpg", "_modified.bmp"), + photo_bitmap, + cc565_swapped, + ) + + # RAM cleanup + photo_bitmap.deinit() + del photo_bitmap + del cc565_swapped + gc.collect() + def continuous_capture_start(self): """Switch the camera to continuous-capture mode""" pass # pylint: disable=unnecessary-pass @@ -872,6 +957,20 @@ def blit(self, bitmap, x_offset=0, y_offset=32): for status information. """ + if self.overlay_bmp is not None: + if self.combined_bmp is None: + self.combined_bmp = Bitmap(bitmap.width, bitmap.height, 65535) + + bitmaptools.blit(self.combined_bmp, bitmap, 0, 0) + + bitmaptools.rotozoom( + self.combined_bmp, + self.overlay_bmp, + scale=0.75, + skip_index=self.overlay_transparency_color, + ) + bitmap = self.combined_bmp + self._display_bus.send( 42, struct.pack(">hh", 80 + x_offset, 80 + x_offset + bitmap.width - 1) ) diff --git a/examples/basic_camera/code.py b/examples/basic_camera/code.py index cebda71..a58b4a3 100644 --- a/examples/basic_camera/code.py +++ b/examples/basic_camera/code.py @@ -1,16 +1,16 @@ # SPDX-FileCopyrightText: Copyright (c) 2023 john park for Adafruit Industries # # SPDX-License-Identifier: MIT -''' simple point-and-shoot camera example. No bells! Zero whistles! ''' +""" simple point-and-shoot camera example. No bells! Zero whistles! """ import time -import adafruit_pycamera # pylint: disable=import-error +import adafruit_pycamera # pylint: disable=import-error pycam = adafruit_pycamera.PyCamera() pycam.mode = 0 # only mode 0 (JPEG) will work in this example # User settings - try changing these: -pycam.resolution = 8 # 0-12 preset resolutions: +pycam.resolution = 8 # 0-12 preset resolutions: # 0: 240x240, 1: 320x240, 2: 640x480, 3: 800x600, 4: 1024x768, # 5: 1280x720, 6: 1280x1024, 7: 1600x1200, 8: 1920x1080, 9: 2048x1536, # 10: 2560x1440, 11: 2560x1600, 12: 2560x1920 diff --git a/examples/overlay/code.py b/examples/overlay/code.py new file mode 100644 index 0000000..95f098f --- /dev/null +++ b/examples/overlay/code.py @@ -0,0 +1,74 @@ +# SPDX-FileCopyrightText: Copyright (c) 2023 john park for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 202 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" simple point-and-shoot camera example, with an overlay frame image. """ + +import time +import traceback +import adafruit_pycamera # pylint: disable=import-error + +pycam = adafruit_pycamera.PyCamera() +pycam.mode = 0 # only mode 0 (JPEG) will work in this example + +# User settings - try changing these: +pycam.resolution = 1 # 0-12 preset resolutions: +# 0: 240x240, 1: 320x240, 2: 640x480 + +pycam.led_level = 1 # 0-4 preset brightness levels +pycam.led_color = 0 # 0-7 preset colors: 0: white, 1: green, 2: yellow, 3: red, +# 4: pink, 5: blue, 6: teal, 7: rainbow +pycam.effect = 0 # 0-7 preset FX: 0: normal, 1: invert, 2: b&w, 3: red, +# 4: green, 5: blue, 6: sepia, 7: solarize + +print("Overlay example camera ready.") +pycam.tone(800, 0.1) +pycam.tone(1200, 0.05) + +pycam.overlay = "/heart_frame_rgb888.bmp" +pycam.overlay_transparency_color = 0xE007 + +while True: + pycam.blit(pycam.continuous_capture()) + pycam.keys_debounce() + + if pycam.shutter.short_count: + print("Shutter released") + pycam.tone(1200, 0.05) + pycam.tone(1600, 0.05) + try: + pycam.display_message("snap", color=0x00DD00) + pycam.capture_jpeg() + pycam.display_message("overlay", color=0x00DD00) + pycam.blit_overlay_into_last_capture() + pycam.live_preview_mode() + except TypeError as exception: + traceback.print_exception(exception) + pycam.display_message("Failed", color=0xFF0000) + time.sleep(0.5) + pycam.live_preview_mode() + except RuntimeError as exception: + pycam.display_message("Error\nNo SD Card", color=0xFF0000) + time.sleep(0.5) + + if pycam.card_detect.fell: + print("SD card removed") + pycam.unmount_sd_card() + pycam.display.refresh() + + if pycam.card_detect.rose: + print("SD card inserted") + pycam.display_message("Mounting\nSD Card", color=0xFFFFFF) + for _ in range(3): + try: + print("Mounting card") + pycam.mount_sd_card() + print("Success!") + break + except OSError as exception: + print("Retrying!", exception) + time.sleep(0.5) + else: + pycam.display_message("SD Card\nFailed!", color=0xFF0000) + time.sleep(0.5) + pycam.display.refresh() diff --git a/examples/overlay/heart_frame_rgb888.bmp b/examples/overlay/heart_frame_rgb888.bmp new file mode 100644 index 0000000000000000000000000000000000000000..eab3d0a6c726972ed1a3dcd981e348c92bcaa41f GIT binary patch literal 230538 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 C7>2F@ literal 0 HcmV?d00001 diff --git a/examples/overlay/heart_frame_rgb888.bmp.license b/examples/overlay/heart_frame_rgb888.bmp.license new file mode 100644 index 0000000..831eb5c --- /dev/null +++ b/examples/overlay/heart_frame_rgb888.bmp.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT diff --git a/examples/overlay/pencil_frame_rgb888.bmp b/examples/overlay/pencil_frame_rgb888.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e92fc9a2fc1fe84ee9ffd3806a152cfe8a1274ae GIT binary patch literal 307338 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 literal 0 HcmV?d00001 diff --git a/examples/overlay/pencil_frame_rgb888.bmp.license b/examples/overlay/pencil_frame_rgb888.bmp.license new file mode 100644 index 0000000..831eb5c --- /dev/null +++ b/examples/overlay/pencil_frame_rgb888.bmp.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT From bca899bc0e4765644156e9686d6634fd0a0009c8 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 10:31:49 -0600 Subject: [PATCH 2/8] mockdoc for displayio --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 3b6c67e..968bc52 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,6 +39,7 @@ "busdisplay", "busio", "digitalio", + "displayio" "espcamera", "fourwire", "micropython", From 12f21fbacdb7dbcaada6994aff797a5a34570774 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 11:01:46 -0600 Subject: [PATCH 3/8] format --- docs/conf.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 968bc52..95c6641 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,8 +39,7 @@ "busdisplay", "busio", "digitalio", - "displayio" - "espcamera", + "displayio" "espcamera", "fourwire", "micropython", "neopixel", From bebfd04d84072ea9b125756414c917ee59a836f9 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 11:17:00 -0600 Subject: [PATCH 4/8] add optional requirement and fix docs build --- docs/conf.py | 3 ++- docs/mock/displayio.py | 21 +++++++++++++++++++++ optional_requirements.txt | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 95c6641..cbf1452 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,8 +39,9 @@ "busdisplay", "busio", "digitalio", - "displayio" "espcamera", + "espcamera", "fourwire", + "jpegio", "micropython", "neopixel", "sdcardio", diff --git a/docs/mock/displayio.py b/docs/mock/displayio.py index b4fd1c5..fb8979d 100644 --- a/docs/mock/displayio.py +++ b/docs/mock/displayio.py @@ -9,3 +9,24 @@ def __init__(self, i): def __setitem__(self, idx, value): self._data[idx] = value + + +class ColorConverter: + def __init__(self, colorspace): + self._colorspace = colorspace + + def convert(self, color_value) -> int: + pass + + +class Bitmap: + def __init__(self, width, height, color_count): + pass + + +class Colorspace: + pass + + +class Display: + pass \ No newline at end of file diff --git a/optional_requirements.txt b/optional_requirements.txt index d4e27c4..51f25c9 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,3 +1,5 @@ # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries # # SPDX-License-Identifier: Unlicense + +adafruit_bitmapsaver \ No newline at end of file From 2bb9b90874029561b64d47f9c8af4801e9954c15 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 11:18:58 -0600 Subject: [PATCH 5/8] fix requirement. code format --- docs/mock/displayio.py | 2 +- optional_requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/mock/displayio.py b/docs/mock/displayio.py index fb8979d..f18251d 100644 --- a/docs/mock/displayio.py +++ b/docs/mock/displayio.py @@ -29,4 +29,4 @@ class Colorspace: class Display: - pass \ No newline at end of file + pass diff --git a/optional_requirements.txt b/optional_requirements.txt index 51f25c9..f46e5d6 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: Unlicense -adafruit_bitmapsaver \ No newline at end of file +adafruit-circuitpython-bitmapsaver From ecce803b2d9021e8e4be493f3cd7762d7477c772 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 11:22:21 -0600 Subject: [PATCH 6/8] add imageload to optional reqs --- optional_requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/optional_requirements.txt b/optional_requirements.txt index f46e5d6..42b579d 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Unlicense adafruit-circuitpython-bitmapsaver +adafruit-circuitpython-imageload From a7a3c0eebf4c5e80b05227e52fb58d4fd78b7bdf Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 11:53:31 -0600 Subject: [PATCH 7/8] move imports so other libraries are optional. --- adafruit_pycamera/__init__.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/adafruit_pycamera/__init__.py b/adafruit_pycamera/__init__.py index 071b2df..59a8e3d 100644 --- a/adafruit_pycamera/__init__.py +++ b/adafruit_pycamera/__init__.py @@ -33,11 +33,6 @@ from adafruit_display_text import label from digitalio import DigitalInOut, Pull from rainbowio import colorwheel -from displayio import Bitmap, ColorConverter, Colorspace -from jpegio import JpegDecoder -import ulab.numpy as np -from adafruit_bitmapsaver import save_pixels -import adafruit_imageload __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PyCamera.git" @@ -853,6 +848,11 @@ def overlay(self) -> str: @overlay.setter def overlay(self, new_overlay_file: str) -> None: + # pylint: disable=import-outside-toplevel + from displayio import ColorConverter, Colorspace + import ulab.numpy as np + import adafruit_imageload + if self.overlay_bmp is not None: self.overlay_bmp.deinit() self._overlay = new_overlay_file @@ -865,6 +865,9 @@ def overlay(self, new_overlay_file: str) -> None: del arr def _init_jpeg_decoder(self): + # pylint: disable=import-outside-toplevel + from jpegio import JpegDecoder + """ Initialize the JpegDecoder if it hasn't been already. Only needed if overlay is used. @@ -882,6 +885,9 @@ def blit_overlay_into_last_capture(self): raise ValueError( "Must set overlay before calling blit_overlay_into_last_capture" ) + # pylint: disable=import-outside-toplevel + from adafruit_bitmapsaver import save_pixels + from displayio import Bitmap, ColorConverter, Colorspace self._init_jpeg_decoder() @@ -956,6 +962,8 @@ def blit(self, bitmap, x_offset=0, y_offset=32): The default preview capture is 240x176, leaving 32 pixel rows at the top and bottom for status information. """ + # pylint: disable=import-outside-toplevel + from displayio import Bitmap if self.overlay_bmp is not None: if self.combined_bmp is None: From 30544c7587fb37712e326451b6f919ff313cf5e2 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 20 Feb 2024 19:10:40 -0600 Subject: [PATCH 8/8] rename to code_simple and add overlay selector version --- examples/overlay/code_select.py | 91 ++++++++++++++++++++ examples/overlay/{code.py => code_simple.py} | 2 +- 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 examples/overlay/code_select.py rename examples/overlay/{code.py => code_simple.py} (97%) diff --git a/examples/overlay/code_select.py b/examples/overlay/code_select.py new file mode 100644 index 0000000..480675e --- /dev/null +++ b/examples/overlay/code_select.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: Copyright (c) 2023 john park for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" simple point-and-shoot camera example, with overly selecting using select button. + +Place all overlay files inside /sd/overlays/ directory. +""" +import os +import time +import traceback +import adafruit_pycamera # pylint: disable=import-error + + +pycam = adafruit_pycamera.PyCamera() +pycam.mode = 0 # only mode 0 (JPEG) will work in this example + +# User settings - try changing these: +pycam.resolution = 1 # 0-12 preset resolutions: +# 0: 240x240, 1: 320x240, 2: 640x480 + +pycam.led_level = 1 # 0-4 preset brightness levels +pycam.led_color = 0 # 0-7 preset colors: 0: white, 1: green, 2: yellow, 3: red, +# 4: pink, 5: blue, 6: teal, 7: rainbow +pycam.effect = 0 # 0-7 preset FX: 0: normal, 1: invert, 2: b&w, 3: red, +# 4: green, 5: blue, 6: sepia, 7: solarize + +print("Overlay example camera ready.") +pycam.tone(800, 0.1) +pycam.tone(1200, 0.05) + +overlay_files = os.listdir("/sd/overlays/") +cur_overlay_idx = 0 + +pycam.overlay = f"/sd/overlays/{overlay_files[cur_overlay_idx]}" +pycam.overlay_transparency_color = 0xE007 + +overlay_files = os.listdir("/sd/overlays/") +cur_overlay_idx = 0 + +while True: + pycam.blit(pycam.continuous_capture()) + pycam.keys_debounce() + # print(dir(pycam.select)) + if pycam.select.fell: + cur_overlay_idx += 1 + if cur_overlay_idx >= len(overlay_files): + cur_overlay_idx = 0 + print(f"changing overlay to {overlay_files[cur_overlay_idx]}") + pycam.overlay = f"/sd/overlays/{overlay_files[cur_overlay_idx]}" + + if pycam.shutter.short_count: + print("Shutter released") + pycam.tone(1200, 0.05) + pycam.tone(1600, 0.05) + try: + pycam.display_message("snap", color=0x00DD00) + pycam.capture_jpeg() + pycam.display_message("overlay", color=0x00DD00) + pycam.blit_overlay_into_last_capture() + pycam.live_preview_mode() + except TypeError as exception: + traceback.print_exception(exception) + pycam.display_message("Failed", color=0xFF0000) + time.sleep(0.5) + pycam.live_preview_mode() + except RuntimeError as exception: + pycam.display_message("Error\nNo SD Card", color=0xFF0000) + time.sleep(0.5) + + if pycam.card_detect.fell: + print("SD card removed") + pycam.unmount_sd_card() + pycam.display.refresh() + + if pycam.card_detect.rose: + print("SD card inserted") + pycam.display_message("Mounting\nSD Card", color=0xFFFFFF) + for _ in range(3): + try: + print("Mounting card") + pycam.mount_sd_card() + print("Success!") + break + except OSError as exception: + print("Retrying!", exception) + time.sleep(0.5) + else: + pycam.display_message("SD Card\nFailed!", color=0xFF0000) + time.sleep(0.5) + pycam.display.refresh() diff --git a/examples/overlay/code.py b/examples/overlay/code_simple.py similarity index 97% rename from examples/overlay/code.py rename to examples/overlay/code_simple.py index 95f098f..ffcea6a 100644 --- a/examples/overlay/code.py +++ b/examples/overlay/code_simple.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: Copyright (c) 2023 john park for Adafruit Industries -# SPDX-FileCopyrightText: Copyright (c) 202 Tim Cocks for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2024 Tim Cocks for Adafruit Industries # # SPDX-License-Identifier: MIT """ simple point-and-shoot camera example, with an overlay frame image. """