Skip to content

Jpeg mode #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 101 additions & 66 deletions adafruit_ov2640.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@

OV2640_COLOR_RGB = 0
OV2640_COLOR_YUV = 1
OV2640_COLOR_JPEG = 2

_IMAGE_MODE_Y8_DVP_EN = const(0x40)
_IMAGE_MODE_JPEG_EN = const(0x10)
Expand Down Expand Up @@ -886,55 +887,70 @@
]
)

# _ov2640_settings_jpeg3 = bytes([
# _BANK_SEL, _BANK_DSP,
# _RESET, _RESET_JPEG | _RESET_DVP,
# _IMAGE_MODE, _IMAGE_MODE_JPEG_EN | _IMAGE_MODE_HREF_VSYNC,
# 0xD7, 0x03,
# 0xE1, 0x77,
# 0xE5, 0x1F,
# 0xD9, 0x10,
# 0xDF, 0x80,
# 0x33, 0x80,
# 0x3C, 0x10,
# 0xEB, 0x30,
# 0xDD, 0x7F,
# _RESET, 0x00,
# ])

_ov2640_settings_yuv422 = bytes(
[
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
_IMAGE_MODE,
_IMAGE_MODE_YUV422,
0xD7,
0x01,
0xE1,
0x67,
_RESET,
0x00,
]
)

_ov2640_settings_rgb565 = bytes(
[
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
_IMAGE_MODE,
_IMAGE_MODE_RGB565,
0xD7,
0x03,
0xE1,
0x77,
_RESET,
0x00,
]
)
_ov2640_color_settings = {
OV2640_COLOR_JPEG: bytes(
[
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_JPEG | _RESET_DVP,
_IMAGE_MODE,
_IMAGE_MODE_JPEG_EN | _IMAGE_MODE_HREF_VSYNC,
0xD7,
0x03,
0xE1,
0x77,
0xE5,
0x1F,
0xD9,
0x10,
0xDF,
0x80,
0x33,
0x80,
0x3C,
0x10,
0xEB,
0x30,
0xDD,
0x7F,
_RESET,
0x00,
]
),
OV2640_COLOR_YUV: bytes(
[
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
_IMAGE_MODE,
_IMAGE_MODE_YUV422,
0xD7,
0x01,
0xE1,
0x67,
_RESET,
0x00,
]
),
OV2640_COLOR_RGB: bytes(
[
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
_IMAGE_MODE,
_IMAGE_MODE_RGB565,
0xD7,
0x03,
0xE1,
0x77,
_RESET,
0x00,
]
),
}


class _RegBits:
Expand Down Expand Up @@ -1114,6 +1130,19 @@ def capture(self, buf):
captured image. Note that this can be a ulab array or a displayio Bitmap.
"""
self._imagecapture.capture(buf)
if self.colorspace == OV2640_COLOR_JPEG:
eoi = buf.find(b"\xff\xd9")
if eoi != -1:
# terminate the JPEG data just after the EOI marker
return memoryview(buf)[: eoi + 2]
return None

@property
def capture_buffer_size(self):
"""Return the size of capture buffer to use with current resolution & colorspace settings"""
if self.colorspace == OV2640_COLOR_JPEG:
return self.width * self.height // 5
return self.width * self.height * 2

@property
def mclk_frequency(self):
Expand All @@ -1140,17 +1169,15 @@ def colorspace(self):
@colorspace.setter
def colorspace(self, colorspace):
self._colorspace = colorspace
self._write_list(
_ov2640_settings_rgb565
if colorspace == OV2640_COLOR_RGB
else _ov2640_settings_yuv422
)
self._set_size_and_colorspace()

def _set_colorspace(self):
colorspace = self._colorspace
settings = _ov2640_color_settings[colorspace]

self._write_list(settings)
# written twice?
self._write_list(
_ov2640_settings_rgb565
if colorspace == OV2640_COLOR_RGB
else _ov2640_settings_yuv422
)
self._write_list(settings)
time.sleep(0.01)

def deinit(self):
Expand All @@ -1168,8 +1195,8 @@ def size(self):
"""Get or set the captured image size, one of the ``OV2640_SIZE_`` constants."""
return self._size

@size.setter
def size(self, size):
def _set_size_and_colorspace(self):
size = self._size
width, height, ratio = _resolution_info[size]
offset_x, offset_y, max_x, max_y = _ratio_table[ratio]
mode = _OV2640_MODE_UXGA
Expand All @@ -1190,7 +1217,11 @@ def size(self, size):
offset_y //= 2

self._set_window(mode, offset_x, offset_y, max_x, max_y, width, height)

@size.setter
def size(self, size):
self._size = size
self._set_size_and_colorspace()

def _set_flip(self):
bits = 0
Expand Down Expand Up @@ -1267,15 +1298,19 @@ def _set_window(
((height >> 6) & 0x04) | ((width >> 8) & 0x03),
]

pclk_auto = 1
pclk_auto = 0
pclk_div = 8
clk_2x = 0
clk_div = 7
clk_div = 0

if self._colorspace != OV2640_COLOR_JPEG:
pclk_auto = 1
clk_div = 7

if mode == _OV2640_MODE_CIF:
regs = _ov2640_settings_to_cif
# if pixformat is not jpeg:
clk_div = 3
if self._colorspace != OV2640_COLOR_JPEG:
clk_div = 3
elif mode == _OV2640_MODE_SVGA:
regs = _ov2640_settings_to_svga
else:
Expand All @@ -1294,7 +1329,7 @@ def _set_window(
time.sleep(0.01)

# Reestablish colorspace
self.colorspace = self._colorspace
self._set_colorspace()

# Reestablish test pattern
if self._test_pattern:
Expand Down
36 changes: 36 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,39 @@ Ensure your device works with this simple test.
.. literalinclude:: ../examples/ov2640_simpletest.py
:caption: examples/ov2640_simpletest.py
:linenos:

Display an image from the camera on the Kaluga 1.3 board, if it is fitted with an ili9341 display.

.. literalinclude:: ../examples/ov2640_displayio_kaluga1_3_ili9341.py
:caption: ../examples/ov2640_displayio_kaluga1_3_ili9341.py
:linenos:

Display an image from the camera on the Kaluga 1.3 board, if it is fitted with an st7789 display.

.. literalinclude:: ../examples/ov2640_displayio_kaluga1_3_st7789.py
:caption: ../examples/ov2640_displayio_kaluga1_3_st7789.py
:linenos:

Display an image from the camera connected to a Raspberry Pi Pico with an st7789 2" display

.. literalinclude:: ../examples/ov2640_displayio_pico_st7789_2in.py
:caption: ../examples/ov2640_displayio_pico_st7789_2in.py
:linenos:

Save an image to internal flash on Kaluga 1.3

.. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3.py
:caption: ../examples/ov2640_jpeg_kaluga1_3.py
:linenos:

``boot.py`` for the above program

.. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3_boot.py
:caption: ../examples/ov2640_jpeg_kaluga1_3_boot.py
:linenos:

Preview images on LCD then save to SD on Kaluga 1.3 fitted with an ili9341 display

.. literalinclude:: ../examples/ov2640_jpeg_sd_kaluga1_3.py
:caption: ../examples/ov2640_jpeg_sd_kaluga1_3.py
:linenos:
3 changes: 0 additions & 3 deletions examples/ov2640_displayio_kaluga1_3_ili9341.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
rotation=90! This demo is for the ili9341. If the display is garbled, try adding
rotation=90, or try modifying it to use ST7799.

The camera included with the Kaluga development kit is the incompatible OV2640,
it won't work.

The audio board must be mounted between the Kaluga and the LCD, it provides the
I2C pull-ups(!)
"""
Expand Down
3 changes: 0 additions & 3 deletions examples/ov2640_displayio_kaluga1_3_st7789.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
of straight lines, it may be an ili9341. If it has a bunch of wiggly traces,
it may be an st7789. If in doubt, try both demos.

The camera included with the Kaluga development kit is the incompatible OV2640,
it won't work.

The audio board must be mounted between the Kaluga and the LCD, it provides the
I2C pull-ups(!)
"""
Expand Down
55 changes: 55 additions & 0 deletions examples/ov2640_jpeg_kaluga1_3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

"""
The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
tested on v1.3.

The audio board must be mounted between the Kaluga and the LCD, it provides the
I2C pull-ups(!)

You also need to place ov2640_jpeg_kaluga1_3_boot.py at CIRCUITPY/boot.py
and reset the board to make the internal flash readable by CircuitPython.
You can make CIRCUITPY readable from your PC by booting CircuitPython in
safe mode or holding the "MODE" button on the audio daughterboard while
powering on or resetting the board.
"""

import board
import busio
import adafruit_ov2640


bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
cam = adafruit_ov2640.OV2640(
bus,
data_pins=board.CAMERA_DATA,
clock=board.CAMERA_PCLK,
vsync=board.CAMERA_VSYNC,
href=board.CAMERA_HREF,
mclk=board.CAMERA_XCLK,
mclk_frequency=20_000_000,
size=adafruit_ov2640.OV2640_SIZE_QVGA,
)

pid = cam.product_id
ver = cam.product_version
print(f"Detected pid={pid:x} ver={ver:x}")
# cam.test_pattern = True

cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
b = bytearray(cam.capture_buffer_size)
jpeg = cam.capture(b)

print(f"Captured {len(jpeg)} bytes of jpeg data")
try:
with open("/jpeg.jpg", "wb") as f:
f.write(jpeg)
except OSError as e:
print(e)
print(
"A 'read-only filesystem' error occurs if you did not correctly install\nov2640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the board"
)
print("Wrote to CIRCUITPY/jpeg.jpg")
23 changes: 23 additions & 0 deletions examples/ov2640_jpeg_kaluga1_3_boot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""Use this file as CIRCUITPY/boot.py in conjunction with ov2640_jpeg_kaluga1_3.py

It makes the CIRCUITPY filesystem writable to CircuitPython
(and read-only to the PC) unless the "MODE" button on the audio
daughterboard is held while the board is powered on or reset.
"""

import analogio
import board
import storage

V_MODE = 1.98
V_RECORD = 2.41

a = analogio.AnalogIn(board.IO6)
a_voltage = a.value * a.reference_voltage / 65535
if abs(a_voltage - V_MODE) > 0.05: # If mode is NOT pressed...
print("storage writable by CircuitPython")
storage.remount("/", readonly=False)
Loading