Skip to content

Commit 7698787

Browse files
authored
Merge pull request #23 from adafruit/blendmode
Add demonstration of bitmafilter.blend
2 parents 7dd0d35 + dd3a665 commit 7698787

File tree

5 files changed

+234
-0
lines changed

5 files changed

+234
-0
lines changed

adafruit_pycamera/imageprocessing.py

+120
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,123 @@ def emboss_greyscale(bitmap, mask=None):
8282
def ironbow(bitmap, mask=None):
8383
"""Convert an image to false color using the 'ironbow palette'"""
8484
return bitmapfilter.false_color(bitmap, ironbow_palette, mask=mask)
85+
86+
87+
# pylint: disable=invalid-name
88+
def alphablend_maker(frac, nfrac=None):
89+
"""Create an alpha-blending function for a specific fractional value
90+
91+
The resulting function can be used with ``bitmapfilter.blend`` and
92+
``bitmapfilter.blend_precompute``.
93+
"""
94+
if nfrac is None:
95+
nfrac = 1 - frac
96+
97+
def inner(a, b):
98+
return frac * a + nfrac * b
99+
100+
return inner
101+
102+
103+
def screen_func(a, b):
104+
"""The 'screen' blend mode.
105+
106+
This function can be used with ``bitmapfilter.blend`` and
107+
``bitmapfilter.blend_precompute``."""
108+
return 1 - (1 - a) * (1 - b)
109+
110+
111+
def overlay_func(a, b):
112+
"""The 'overlay' blend mode.
113+
114+
This function can be used with ``bitmapfilter.blend`` and
115+
``bitmapfilter.blend_precompute``."""
116+
return 2 * a * b if a < 0.5 else 1 - 2 * (1 - a) * (1 - b)
117+
118+
119+
def hard_light_func(a, b):
120+
"""The 'hard light' blend mode.
121+
122+
This function can be used with ``bitmapfilter.blend`` and
123+
``bitmapfilter.blend_precompute``."""
124+
return 2 * a * b if b < 0.5 else 1 - 2 * (1 - a) * (1 - b)
125+
126+
127+
# illusions.hu formula version
128+
def soft_light_func(a, b):
129+
"""The 'soft light' blend mode.
130+
131+
There are various soft light blend functions. The "illusions.hu" variant of
132+
soft light is used.
133+
134+
This function can be used with ``bitmapfilter.blend`` and
135+
``bitmapfilter.blend_precompute``."""
136+
return a ** (2 ** (2 * 0.5 - b))
137+
138+
139+
def color_dodge_func(a, b):
140+
"""The 'color dodge' blend mode.
141+
142+
This function can be used with ``bitmapfilter.blend`` and
143+
``bitmapfilter.blend_precompute``."""
144+
return a / (1 - b) if b != 1 else 1
145+
146+
147+
def linear_dodge_func(a, b):
148+
"""The 'linear dodge' blend mode.
149+
150+
This function can be used with ``bitmapfilter.blend`` and
151+
``bitmapfilter.blend_precompute``."""
152+
return a + b
153+
154+
155+
def divide_func(a, b):
156+
"""The 'divide' blend mode.
157+
158+
This function can be used with ``bitmapfilter.blend`` and
159+
``bitmapfilter.blend_precompute``."""
160+
return a / b if b else 1
161+
162+
163+
def multiply_func(a, b):
164+
"""The 'multiply' blend mode.
165+
166+
This function can be used with ``bitmapfilter.blend`` and
167+
``bitmapfilter.blend_precompute``."""
168+
return a * b
169+
170+
171+
def subtract_func(a, b):
172+
"""The 'subtract' blend mode.
173+
174+
This function can be used with ``bitmapfilter.blend`` and
175+
``bitmapfilter.blend_precompute``."""
176+
return a - b
177+
178+
179+
def color_burn_func(a, b):
180+
"""The 'color burn' blend mode.
181+
182+
This function can be used with ``bitmapfilter.blend`` and
183+
``bitmapfilter.blend_precompute``."""
184+
return a * (1 - b)
185+
186+
187+
def linear_burn_func(a, b):
188+
"""The 'linear burn' blend mode.
189+
190+
This function can be used with ``bitmapfilter.blend`` and
191+
``bitmapfilter.blend_precompute``."""
192+
return a + b - 1
193+
194+
195+
darken_only_func = min
196+
"""The 'darken only' blend mode.
197+
198+
This function can be used with ``bitmapfilter.blend`` and
199+
``bitmapfilter.blend_precompute``."""
200+
lighten_only_func = max
201+
"""The 'screen' blend mode.
202+
203+
This function can be used with ``bitmapfilter.blend`` and
204+
``bitmapfilter.blend_precompute``."""

examples/camera/boot.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
5+
"""Automatically create the /sd mount point at boot time"""
6+
7+
import os
8+
import storage
9+
10+
storage.remount("/", readonly=False)
11+
12+
try:
13+
os.mkdir("/sd")
14+
except OSError:
15+
pass # It's probably 'file exists', OK to ignore
16+
17+
storage.remount("/", readonly=True)

examples/filter/code.py

+94
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,95 @@
2121
from adafruit_pycamera import imageprocessing
2222
from adafruit_pycamera import PyCameraBase
2323

24+
25+
blend_50_50 = bitmapfilter.blend_precompute(imageprocessing.alphablend_maker(0.5))
26+
screen = bitmapfilter.blend_precompute(imageprocessing.screen_func)
27+
overlay = bitmapfilter.blend_precompute(imageprocessing.overlay_func)
28+
hard_light = bitmapfilter.blend_precompute(imageprocessing.hard_light_func)
29+
soft_light = bitmapfilter.blend_precompute(imageprocessing.soft_light_func)
30+
color_dodge = bitmapfilter.blend_precompute(imageprocessing.color_dodge_func)
31+
# linear_dodge = bitmapfilter.blend_precompute(imageprocessing.linear_dodge_func)
32+
# divide = bitmapfilter.blend_precompute(imageprocessing.divide_func)
33+
multiply = bitmapfilter.blend_precompute(imageprocessing.multiply_func)
34+
# subtract = bitmapfilter.blend_precompute(imageprocessing.subtract_func)
35+
# color_burn = bitmapfilter.blend_precompute(imageprocessing.color_burn_func)
36+
# linear_burn = bitmapfilter.blend_precompute(imageprocessing.linear_burn_func)
37+
# darken_only = bitmapfilter.blend_precompute(min)
38+
# lighten_only = bitmapfilter.blend_precompute(max)
39+
40+
41+
def blender(f):
42+
def inner(b):
43+
return bitmapfilter.blend(b, b, testpattern, f)
44+
45+
return inner
46+
47+
48+
def reverse_blender(f):
49+
def inner(b):
50+
return bitmapfilter.blend(b, testpattern, b, f)
51+
52+
return inner
53+
54+
55+
inverse_greyscale_weights = bitmapfilter.ChannelMixer(
56+
1 - 0.299,
57+
1 - 0.587,
58+
1 - 0.114,
59+
1 - 0.299,
60+
1 - 0.587,
61+
1 - 0.114,
62+
1 - 0.299,
63+
1 - 0.587,
64+
1 - 0.114,
65+
)
66+
67+
blur_more = [
68+
4,
69+
15,
70+
24,
71+
15,
72+
4,
73+
15,
74+
61,
75+
97,
76+
61,
77+
15,
78+
24,
79+
97,
80+
154,
81+
97,
82+
24,
83+
15,
84+
61,
85+
97,
86+
61,
87+
15,
88+
4,
89+
15,
90+
24,
91+
15,
92+
4,
93+
]
94+
95+
96+
# "Sketch" filter based on
97+
# https://www.freecodecamp.org/news/sketchify-turn-any-image-into-a-pencil-sketch-with-10-lines-of-code-cf67fa4f68ce/
98+
def sketch(b):
99+
bitmapfilter.mix(b, inverse_greyscale_weights)
100+
memoryview(auxbuffer)[:] = memoryview(b)
101+
bitmapfilter.morph(auxbuffer, blur_more)
102+
bitmapfilter.blend(b, auxbuffer, b, color_dodge)
103+
bitmapfilter.mix(b, inverse_greyscale_weights) # get rid of magenta halos
104+
return b
105+
106+
24107
effects = [
108+
("sketch", sketch),
109+
("50/50", blender(blend_50_50)),
110+
("multiply", blender(multiply)),
111+
("soft light", blender(soft_light)),
112+
("hard_light", blender(hard_light)),
25113
("blue cast", imageprocessing.blue_cast),
26114
("blur", imageprocessing.blur),
27115
("bright", lambda b: bitmapfilter.mix(b, bitmapfilter.ChannelScale(2.0, 2.0, 2.0))),
@@ -65,6 +153,9 @@ def cycle(seq):
65153
pycam = PyCameraBase()
66154
pycam.init_display()
67155

156+
testpattern = displayio.Bitmap(208, 208, 65535)
157+
auxbuffer = displayio.Bitmap(208, 208, 65535)
158+
68159

69160
def main():
70161
filename = "/cornell_box_208x208.jpg"
@@ -74,6 +165,9 @@ def main():
74165
decoder.open(filename)
75166
decoder.decode(bitmap0)
76167

168+
decoder.open("/testpattern_208x208.jpg")
169+
decoder.decode(testpattern)
170+
77171
label = Label(font=FONT, x=0, y=8)
78172
pycam.display.root_group = label
79173
pycam.display.refresh()
6 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
2+
3+
SPDX-License-Identifier: Unlicense

0 commit comments

Comments
 (0)