Skip to content

Commit 0926af9

Browse files
committed
Evasive bug exterminated.
1 parent 3d3a4f7 commit 0926af9

File tree

1 file changed

+47
-35
lines changed

1 file changed

+47
-35
lines changed

filter/avgrow.py

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,48 @@
11
#!/usr/bin/env python3
22

33
"""
4-
Averaging image pixels in a row until reaching `abs(average - current) > threshold`, then repeating in a column.
4+
Averaging image pixels in a row until reaching `abs(average - current) > threshold`,
5+
then repeating in a column.
56
67
Created by: `Ilya Razmanov <https://dnyarri.github.io/>`_
78
8-
History:
9-
---------
10-
11-
0.10.12.3 Initial version - 12 Oct 2024. RGB only.
12-
13-
0.10.13.2 Forces RGB, skips alpha. Changed threshold condition to r, g, b separately.
14-
Seem to be just what I need.
15-
16-
0.10.25.1 Bugfix for tiny details.
17-
18-
1.16.5.10 Modularization, some optimization. Force keep alpha.
19-
20-
1.17.10.1 Edge condition bugfix.
21-
22-
2.19.14.16 Wrap around processing introduced.
23-
24-
3.20.1.1 Substantial rewriting with `map` to correctly support L.
25-
No force RGB anymore.
26-
27-
3.20.7.14 Alpha filtering. Full support for L, LA, RGB, RGBA filtering with one `map`.
28-
29-
3.20.20.3 Code harmonization. Lambdas completely replaced with operators
30-
and defined functions to improve speed.
31-
32-
3.22.13.11 Unnecessary map to list conversion removed, necessary replaced with [*map] unpacking.
33-
349
"""
3510

11+
# History:
12+
# ---------
13+
#
14+
# 0.10.12.3 Initial version - 12 Oct 2024. RGB only.
15+
#
16+
# 0.10.13.2 Forces RGB, skips alpha. Changed threshold condition to r, g, b separately.
17+
# Seem to be just what I need.
18+
#
19+
# 0.10.25.1 Bugfix for tiny details.
20+
#
21+
# 1.16.5.10 Modularization, some optimization. Force keep alpha.
22+
#
23+
# 1.17.10.1 Edge condition bugfix.
24+
#
25+
# 2.19.14.16 Wrap around processing introduced.
26+
#
27+
# 3.20.1.1 Substantial rewriting with `map` to correctly support L.
28+
# No force RGB anymore.
29+
#
30+
# 3.20.7.14 Alpha filtering. Full support for L, LA, RGB, RGBA filtering with one `map`.
31+
#
32+
# 3.20.20.3 Code harmonization. Lambdas completely replaced with operators
33+
# and defined functions to improve speed.
34+
#
35+
# 3.22.13.11 Unnecessary map to list conversions removed,
36+
# necessary ones replaced with [*map] unpacking.
37+
#
38+
# 3.22.18.8 Evasive bug discovered and presumably exterminated.
39+
#
40+
3641
__author__ = 'Ilya Razmanov'
3742
__copyright__ = '(c) 2024-2025 Ilya Razmanov'
3843
__credits__ = 'Ilya Razmanov'
3944
__license__ = 'unlicense'
40-
__version__ = '3.22.13.11'
45+
__version__ = '3.22.18.8'
4146
__maintainer__ = 'Ilya Razmanov'
4247
__email__ = 'ilyarazmanov@gmail.com'
4348
__status__ = 'Production'
@@ -52,7 +57,7 @@ def create_image(X: int, Y: int, Z: int) -> list[list[list[int]]]:
5257
return new_image
5358

5459

55-
def filter(source_image: list[list[list[int]]], threshold_x: int, threshold_y: int, wrap_around: bool = False, keep_alpha: bool = False) -> list[list[list[int]]]:
60+
def filter(source_image: list[list[list[int]]], threshold_x: int | float, threshold_y: int | float, wrap_around: bool = False, keep_alpha: bool = False) -> list[list[list[int]]]:
5661
"""Average image pixels in a row until `abs(average - current) > threshold` criterion met, then repeat in a column."""
5762

5863
# ↓ Determining image sizes.
@@ -110,15 +115,19 @@ def _criterion_x(channel: int, channel_sum: int) -> bool:
110115
# Uses outer scope `number` and `threshold_x`.
111116

112117
for y in range(0, Y, 1):
113-
x_start = 0 # Defining start of inner loop of averaging until threshold.
118+
x_start = 0 # Defining start of inner loop of averaging until threshold hit.
114119
number = 1 # Number of pixels being read during averaging loop.
115-
pixel = source_image[cy(y)][0] # Current pixel.
120+
pixel = source_image[cy(y)][0] # Starting pixel.
116121
pixels_sum = pixel # Sum of pixels being read during averaging loop.
117122
for x in range(0, X + x_overhead, 1):
118123
number += 1
124+
# ↓ Core part of averaging - adding up.
125+
# It is important to sum pixels read BEFORE current pixel
126+
# so when checking threshold and writing an averaged line
127+
# edge pixel is not included into average.
128+
pixels_sum = [*map(add, pixel, pixels_sum)]
119129
pixel = source_image[cy(y)][cx(x)]
120-
pixels_sum = [*map(add, pixel, pixels_sum)] # Core part of averaging - adding up.
121-
if any(map(_criterion_x, pixel[:Z_COLOR], pixels_sum[:Z_COLOR])) or (x == (X - 1 + x_overhead)):
130+
if any(map(_criterion_x, pixel[:Z_COLOR], pixels_sum[:Z_COLOR])):
122131
average_pixel = [*map(floordiv, pixels_sum, (number,) * Z)] # Inner loop result.
123132
for i in range(x_start, x - 1, 1):
124133
intermediate_image[y][cx(i)] = average_pixel
@@ -143,9 +152,9 @@ def _criterion_y(channel: int, channel_sum: int) -> bool:
143152
pixels_sum = pixel
144153
for y in range(0, Y + y_overhead, 1):
145154
number += 1
146-
pixel = intermediate_image[cy(y)][cx(x)]
147155
pixels_sum = [*map(add, pixel, pixels_sum)]
148-
if any(map(_criterion_y, pixel[:Z_COLOR], pixels_sum[:Z_COLOR])) or (y == (Y - 1 + y_overhead)):
156+
pixel = intermediate_image[cy(y)][cx(x)]
157+
if any(map(_criterion_y, pixel[:Z_COLOR], pixels_sum[:Z_COLOR])):
149158
average_pixel = [*map(floordiv, pixels_sum, (number,) * Z)]
150159
for i in range(y_start, y - 1, 1):
151160
result_image[cy(i)][x] = average_pixel
@@ -166,6 +175,8 @@ def _criterion_y(channel: int, channel_sum: int) -> bool:
166175
return resultimage_plus_alpha
167176
else:
168177
return result_image
178+
179+
169180
# ↑ filter finished
170181

171182
# ↓ Dummy stub for standalone execution attempt
@@ -174,4 +185,5 @@ def _criterion_y(channel: int, channel_sum: int) -> bool:
174185
need_help = input('Would you like to read some help (y/n)?')
175186
if need_help.startswith(('y', 'Y')):
176187
import avgrow
188+
177189
help(avgrow)

0 commit comments

Comments
 (0)