Skip to content

Commit 6da6447

Browse files
committed
Add an image viewer application
1 parent 82d2335 commit 6da6447

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

examples/viewer/code.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# SPDX-FileCopyrightText: 2024 Jeff Epler for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
5+
"""Image viewer
6+
7+
This will display all *jpeg* format images on the inserted SD card.
8+
9+
Press up or down to move by +- 10 images.
10+
Press left or right to move by +- 1 image.
11+
12+
Otherwise, images cycle every DISPLAY_INTERVAL milliseconds (default 8000 = 8 seconds)
13+
"""
14+
15+
import time
16+
import os
17+
import displayio
18+
from jpegio import JpegDecoder
19+
from adafruit_ticks import ticks_less, ticks_ms, ticks_add, ticks_diff
20+
from adafruit_pycamera import PyCameraBase
21+
22+
DISPLAY_INTERVAL = 8000 # milliseconds
23+
24+
decoder = JpegDecoder()
25+
26+
pycam = PyCameraBase()
27+
pycam.init_display()
28+
29+
30+
def load_resized_image(bitmap, filename):
31+
print(f"loading {filename}")
32+
bitmap.fill(0b01000_010000_01000) # fill with a middle grey
33+
34+
bw, bh = bitmap.width, bitmap.height
35+
t0 = ticks_ms()
36+
h, w = decoder.open(filename)
37+
t1 = ticks_ms()
38+
print(f"{ticks_diff(t1, t0)}ms to open")
39+
scale = 0
40+
print(f"Full image size is {w}x{h}")
41+
print(f"Bitmap is {bw}x{bh} pixels")
42+
while (w >> scale) > bw or (h >> scale) > bh and scale < 3:
43+
scale += 1
44+
sw = w >> scale
45+
sh = h >> scale
46+
print(f"will load at {scale=}, giving {sw}x{sh} pixels")
47+
48+
if sw > bw: # left/right sides cut off
49+
x = 0
50+
x1 = (sw - bw) // 2
51+
else: # horizontally centered
52+
x = (bw - sw) // 2
53+
x1 = 0
54+
55+
if sh > bh: # top/bottom sides cut off
56+
y = 0
57+
y1 = (sh - bh) // 2
58+
else: # vertically centered
59+
y = (bh - sh) // 2
60+
y1 = 0
61+
62+
print(f"{x=} {y=} {x1=} {y1=}")
63+
decoder.decode(bitmap, x=x, y=y, x1=x1, y1=y1, scale=scale)
64+
t1 = ticks_ms()
65+
print(f"{ticks_diff(t1, t0)}ms to decode")
66+
67+
68+
def mount_sd():
69+
if not pycam.card_detect.value:
70+
pycam.display_message("No SD card\ninserted", color=0xFF0000)
71+
return []
72+
pycam.display_message("Mounting\nSD Card", color=0xFFFFFF)
73+
for _ in range(3):
74+
try:
75+
print("Mounting card")
76+
pycam.mount_sd_card()
77+
print("Success!")
78+
break
79+
except OSError as e:
80+
print("Retrying!", e)
81+
time.sleep(0.5)
82+
else:
83+
pycam.display_message("SD Card\nFailed!", color=0xFF0000)
84+
time.sleep(0.5)
85+
all_images = [
86+
f"/sd/{filename}"
87+
for filename in os.listdir("/sd")
88+
if filename.lower().endswith(".jpg")
89+
]
90+
pycam.display_message(f"Found {len(all_images)}\nimages", color=0xFFFFFF)
91+
time.sleep(0.5)
92+
pycam.display.refresh()
93+
return all_images
94+
95+
96+
def main():
97+
image_counter = 0
98+
last_image_counter = 0
99+
deadline = ticks_ms()
100+
all_images = mount_sd()
101+
102+
bitmap = displayio.Bitmap(pycam.display.width, pycam.display.height, 65535)
103+
104+
while True:
105+
pycam.keys_debounce()
106+
if pycam.card_detect.fell:
107+
print("SD card removed")
108+
pycam.unmount_sd_card()
109+
pycam.display_message("SD Card\nRemoved", color=0xFFFFFF)
110+
time.sleep(0.5)
111+
pycam.display.refresh()
112+
all_images = []
113+
114+
now = ticks_ms()
115+
if pycam.card_detect.rose:
116+
print("SD card inserted")
117+
all_images = mount_sd()
118+
image_counter = 0
119+
deadline = now
120+
121+
if all_images:
122+
if pycam.up.fell:
123+
image_counter = (last_image_counter - 10) % len(all_images)
124+
deadline = now
125+
126+
if pycam.down.fell:
127+
image_counter = (last_image_counter + 10) % len(all_images)
128+
deadline = now
129+
130+
if pycam.left.fell:
131+
image_counter = (last_image_counter - 1) % len(all_images)
132+
deadline = now
133+
134+
if pycam.right.fell:
135+
image_counter = (last_image_counter + 1) % len(all_images)
136+
deadline = now
137+
138+
if ticks_less(deadline, now):
139+
print(now, deadline, ticks_less(deadline, now), all_images)
140+
deadline = ticks_add(deadline, DISPLAY_INTERVAL)
141+
filename = all_images[image_counter]
142+
last_image_counter = image_counter
143+
image_counter = (image_counter + 1) % len(all_images)
144+
try:
145+
load_resized_image(bitmap, filename)
146+
except Exception as e: # pylint: disable=broad-exception-caught
147+
pycam.display_message(f"Failed to read\n{filename}", color=0xFF0000)
148+
print(e)
149+
deadline = ticks_add(now, 500)
150+
pycam.blit(bitmap, y_offset=0)
151+
152+
153+
main()

0 commit comments

Comments
 (0)