#π nested loop alternative
469 messages Β· Page 1 of 1 (latest)
@mild kayak
Remember to:
- Ask your Python question, not if you can ask or if there's an expert who can help.
- Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
- Explain what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
x_s, y_s = img.size # (3267, 4969)
step: int = ...
for x in range(0, x_s, step):
for y in range(0, y_s, step):
...
Can I somehow not make this a nested loop?
my first thought was itertools.product but that wouldn't necessarily work.
Why do you want to?
Why do you want to not make it nested?
you can imagine how slow it is
For speed? Why on earth would a nested loop be slower and an equivalent construct?
well u wouldn't be looping twice if u can do it in one step?
It has to do x*y amount of work. Anything you replace it with also has to do that.
no I can't
what are you doing in the double loop? if it's pass, it's blazingly fast; if it's time.sleep(1000000), it's slow even without looping
If you needed nested loops, you need that many steps. Doing it with 1 loop or 2 doesn't change that.
so uhm then how do I speed this up
Purplys' query is the relevant one. What's the loop for?
R, G, B = 0, 0, 0
surrounding_pixels: list[tuple[int, int]] = [
(-1,-1), (0,-1), (+1,-1),
(-1, 0), (0, 0), (+1, 0),
(-1,+1), (0,+1), (+1,+1)
]
depth: int = 3
for d in range(depth):
for x_o, y_o in surrounding_pixels:
x_o = (x_o - d) if (x_o < 0) else (x_o + d)
y_o = (y_o - d) if (y_o < 0) else (y_o + d)
x_o += x
y_o += y
x_o = 0 if x_o < 0 else x_o
x_o = x_s if x_o > x_s else x_o
y_o = 0 if y_o < 0 else y_o
y_o = y_s if y_o > y_s else y_o
try:
r, g, b = img.getpixel((x_o,y_o))
except IndexError:
pass
except Exception as e:
raise(e)
D: int = d + 1
R += r * (1 / (D * 9))
G += g * (1 / (D * 9))
B += b * (1 / (D * 9))
pixels[x, y] = (int(R), int(G), int(B))
this is what I'm doing inside the nested loop.
it basically blurs an image
by combining the 3x3 pixels into 1 big pixel
I gotta figure out a better way to deal with the depth
which is just a way to make 3x3 become like 6x6 9x9
so a box filter?
for bigger images
ig?
probably a variable box would be best to constant blur
look?
like make sure no matter what size an image is the blur boxes would be same sizd
size*
then cv2 seems easy enough
edited (I mistyped an extra 0)
oh but I'm not doing it because I want to blur an image its because I want to implement it myself
I thought u linked an algorithm of some sort but its just a library that does what Im looking todo
I don't think you're gonna find some fancy algorithm to speed up what you're doing
otherwise if you want a less out-of-the-box solution, use numpy and implement it yourself
hm
numpy is a big library that I have never really tried to learn
in this context what features would numpy have that'd help me?
it should i think
by allowing you to not loop in python
e.g. np.sum() can sum an entire 2d array (by looping in fast C code), instead of you having a double loop in python +=ing to a total(slow)
for y, x in product(range(y_s), repeat=2):
print(y, x)
>>> (0, 0)
>>> (0, 1)
>>> ...
>>> (0, 4969)
>>> ...
>>> (4949, 4949)
however x in this context maxes out at 3267
ic, thanks!
product(range(xs), range(ys))?
you're still doing something max_x * max_y times though
doesn't matter if you have a flat loop instead of a nested one
yeah
it gives me a bit more screen room tho so I think Im gonna keep product
for d in range(depth):
for x_o, y_o in surrounding_pixels:
x_o = (x_o - d) if (x_o < 0) else (x_o + d)
y_o = (y_o - d) if (y_o < 0) else (y_o + d)
x_o += x
y_o += y
x_o = 0 if x_o < 0 else x_o
x_o = x_s if x_o > x_s else x_o
y_o = 0 if y_o < 0 else y_o
y_o = y_s if y_o > y_s else y_o
try:
r, g, b = img.getpixel((x_o,y_o))
except IndexError:
pass
except Exception as e:
raise(e)
D: int = d + 1
R += r * (1 / (D * 9))
G += g * (1 / (D * 9))
B += b * (1 / (D * 9))
this is really what I have to fix to make it faster
But Felix can be quicker, even in Python. Think about the blur step - you're taking surrounding pixels and adding them. The cost of picking them up, per-pixel, is O(dx*dy) where dx and dy are the size of the blur rectangle. You might make some headway fetching them just once and adding to the target array in several places.
the first loop is basically for x in width for y in height
which ig can't be made faster
Your img - is it a Pillow Image? Or just some grid of pixels of your own?
pillow
Well a Pillow image is an array underneath. Packed binary data.
new_image = Image.new(mode="RGB", size=img.size)
pixels = new_image.load()
"""
(x,y)
[-1,-1] [0,-1] [+1,-1]
[-1, 0] [0 ,0] [+1, 0]
[-1,+1] [0,+1] [+1,+1]
"""
x_s, y_s = img.size
You can use numpy to do bulk adds at machine speed.
actually, you mean to use a sliding window? yeah that could make it better actually
oh?
It isn't "less" work in terms of compute steps, but the steps are faster.
how do I do that?
I might just be shifting the accesses from the fetch to the add π¦
Numpy works with the same arrays as Python. You can take the array from the Image and make a numpy array from it directly (not even copying the data - just sharing the array).
You can compute your blurred image by adding these arrays together, each slightly offset.
could I utilize numpy's matrix multiplication stuff to do the blurring step?
oh wait I c (I must be tired lol)
is it that slow to fetch?
Sorry, I wrote "Python" above - i meant Pillow.
cuz a matrix is just an array of NxN right?
Well not more than anything else, I was just thinking if only fetching once instead of eg 9 times for a 3x3 blur. But it just pushes the 9 to the add step.
so multiply the surrounding pixels matrix by a matrix of depth * 9
Yes.
Well, i thought you were effectively adding the image to itself, offset slightly (this adds the adjacent pixels).
Then divide at the end.
if it's a box filter, I don't think it's matmul but a convolution
aha
I actually remember that
it is a convolution
because this whole idea came after I watched an episode of 3 blue 1 brown
and the video was about convolution
π
so numpy probably has convolution stuff then ha
numpy.convole
This example makes a numpy array from the Image data: https://realpython.com/image-processing-with-the-python-pillow-library/#using-numpy-to-subtract-images-from-each-other
well numpy has a 1d convolution, no 2d solution out of the box
for that you might have to do numpy.stride_tricks.as_strided black magic
why would I need a 2d solution?
cause at least right now, you have a 2d image, and you're taking 3x3 chunks to convolve
cameron's suggestion should be way easier to implement tbh
couldn't I just use the 1d convolution for R then G then B
the problem isn't you have RGB, the problem is you have a 2d image
yes, I'm saying np.convolve only works with 1d arrays
an image is a 2d array
so you'll need to do some extra work
oh yes
my idea was
hmm
wait...
π
my head
so my idea was
oh it is
since the list contains tuples
so it isnt really a 1d array
hm.
so right now I am going thru the surrounding pixels one by one and adding their RGB values
what if I do R, then G, then B and do all the surrounding pixels at once
wouldn't that be faster?
with numpy's convolve
wdym by that
again, the RGB doesn't really matter, the 'problem' will still persist if you had a grayscale image
since an image is a 2d array, and np.convolve() only works with 1d arrays, you'll have to do stuff to somehow get a 2d convolution in numpy
actually ig it isn't that difficult since np.lib.slide_tricks.sliding_window_view exists now
makes "sliding windows" of a bigger array
do you know what sliding window is?
not really tbh
I mean I know what a sliding window is
in real life
not in code
π
so it makes a group of values that can be gotten and placed back?
for ease of explanation, let's work on 1d
[ 1 2 3 4 5 6 7 ]
```then you can make 3-lengthed windows(not an actual term) by 'sliding' a window across the array like this
```py
[|1 2 3|4 5 6 7 ]
[ 1|2 3 4|5 6 7 ]
[ 1 2|3 4 5|6 7 ]
...
[ 1 2 3 4|5 6 7|]
# to obtain
[1 2 3]
[2 3 4]
[3 4 5]
...
[5 6 7]
oh so u can grab a chunk of data from the array
but then can u place it back where it was with modified values?
I mean ig I can just create a new image
for 2d, say you had an 3x5 image and a 3x3 box filter. then you can make sliding windows of 3x3 by
|. . .|. .
|. . .|. .
|. . .|. .
.|. . .|.
.|. . .|.
.|. . .|.
. .|. . .|
. .|. . .|
. .|. . .|
```and you can element-wise multiply the box filter with these windows to get the blurred image
aha...
ok so this makes sense
I see why it'd be faster
wait but
oh yeah
wait no
waht...
so I got this 3x3 chunk of data
which is the rgb values of the pixels or is it the x, y positions of the pixels?
wait...
instead of looping over the x, y of the image size
why dont I create an array of rgb of the whole image
and loop over that
so [(rgb), (rgb), (rgb), ...]
because you're still looping in python
the whole point of numpy & friends' bjillion operations is so that you don't loop in python
aha
python loops that bad ha
so using numpy I wouldn't need a loop at all then
right?
ideally yes
sometimes you have stuff like
fibonacci = [1, 1]
for term in range(2, 10):
fibonacci.append(fibonacci[-1] + fibonacci[-2])
```i.e. you need values that depend on previous values
you don't have that here tho, so no need to worry
if you just want to use the blahblah.sliding_window_view, it's not that difficult
>>> import numpy as np
>>> from numpy.lib.stride_tricks import sliding_window_view
>>> a = np.arange(16).reshape(4, 4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>> sliding_window_view(a, (3, 3))
array([[[[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10]],
[[ 1, 2, 3],
[ 5, 6, 7],
[ 9, 10, 11]]],
[[[ 4, 5, 6],
[ 8, 9, 10],
[12, 13, 14]],
[[ 5, 6, 7],
[ 9, 10, 11],
[13, 14, 15]]]])
and that'd help me how?....
oh
wait
shit
...
img_np_array = ...
sliding_window_view(img_np_array, (3,3))
so that gives me the pixel with its 8 surrounding pixels
correct?
you get 3x3 chunks of the original, correct
then I need to do a convolution of that chunk with the offsets to blur the pixel
right?
and then just reshape it to match what it first was and put it back into the image
eh not really
now you just need the box-filter to do element-wise multiplication with these chunks
hm
can u give me the code to get a numpy array from a pil image from this link https://realpython.com/image-processing-with-the-python-pillow-library/#using-numpy-to-subtract-images-from-each-other
stupid real python keeps asking for an account
you should be able to just a = np.array(image)
elaborate please
ic thanks
>>> kernel = np.ones((3, 3)) / 9
>>> kernel # your 3x3 box filter
array([[0.11111111, 0.11111111, 0.11111111],
[0.11111111, 0.11111111, 0.11111111],
[0.11111111, 0.11111111, 0.11111111]])
>>> swv = sliding_window_view(a, (3, 3))
>>> np.multiply(swv, kernel) # element-wise multiply
array([[[[0. , 0.11111111, 0.22222222],
[0.44444444, 0.55555556, 0.66666667],
[0.88888889, 1. , 1.11111111]],
[[0.11111111, 0.22222222, 0.33333333],
[0.55555556, 0.66666667, 0.77777778],
[1. , 1.11111111, 1.22222222]]],
[[[0.44444444, 0.55555556, 0.66666667],
[0.88888889, 1. , 1.11111111],
[1.33333333, 1.44444444, 1.55555556]],
[[0.55555556, 0.66666667, 0.77777778],
[1. , 1.11111111, 1.22222222],
[1.44444444, 1.55555556, 1.66666667]]]])
>>> b = np.multiply(swv, kernel)
>>> b.sum(axis=(-1, -2)) # sum each chunk together to get the averaged value; remeber we already divided by 9 because of the elem-wise kernel multiplication earlier
array([[ 5., 6.],
[ 9., 10.]])
if you read the brackets carefully, the sliding_window_view gave us an array of array of 3x3 chunks
so the last 2 axes (-1, -2) combined are the 3x3 chunks we have (that we want to sum)
oh ic
ok and now with the b array I just create a new image right?
well not b
but the combination of all b arrays
new_image.extend(b)
wait
that'd require a loop nvm
im stupid
its alr done
b is the new pixels
lmao
with Image.open("1.jpg") as img:
pixels = array(img)
pixel_chunks = sliding_window_view(pixels, (3,3))
box_filter = ones((3,3)) / 9
blurred_pixel_chunks = multiply(pixels, box_filter)
new_image = Image.new(mode="RGB", size=img.size)
new_pixels = new_image.load()
new_pixels = blurred_pixel_chunks.sum(axis=(-1,-2))
new_image.show()
so this should be it correct?
is the new image gonna be the same size?
no right?
this code is not complete yet
probably
the example I showed is like a grayscale image, while you have an RGB image, meaning you'll have 3 2d matrices indicating the r g b values
if it doesn't work and you can't be bothered, you can technically just loop over the r g b (it's only 3 so it's not that bad)
no, because stuff like the corners aren't considered
one way is to pad some zeroes around the image
pixel_chunks = sliding_window_view(pixels, (3,3))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
raise ValueError(f'Since axis is `None`, must provide '
ValueError: Since axis is `None`, must provide window_shape for all dimensions of `x`; got 2 window_shape elements and `x.ndim` is 3.
hm
that's probably due to the rgb thing, so your image actually has 3 dimensions instead of 2 in my example
(image width, image length, and channel=3 for rgb)
[[[177 187 189]
[179 189 191]
[182 192 194]
...
[226 229 234]
[229 232 237]
[227 230 235]]
[[173 183 185]
[176 186 188]
[176 186 188]
...
[229 232 237]
[228 231 236]
[228 231 236]]
[[177 187 189]
[175 185 187]
[172 182 184]
...
[229 232 237]
[226 229 234]
[228 231 236]]
...
[[ 75 52 46]
[ 73 49 45]
[ 86 62 58]
...
[ 8 0 0]
[ 9 1 0]
[ 9 1 0]]
[[ 85 60 56]
[ 81 56 52]
[ 84 60 56]
...
[ 9 0 1]
[ 10 2 0]
[ 10 2 0]]
[[ 88 64 60]
[ 80 59 56]
[ 81 60 57]
...
[ 13 3 2]
[ 4 0 0]
[ 2 0 1]]]
yeah thats pixels
its a 3d image
I mean array
so ur saying loop thru it
well loop through the channels, then you're working with 'normal' 2d arrays again
then at the end you should have 3 2d arrays representing the blurred r, g, b channels, then merge them back together
icic
ugh
with Image.open("1.jpg") as img:
pixels = array(img)
new_image = Image.new(mode="RGB", size=img.size)
new_pixels = new_image.load()
blurred_pixels = []
for kernel in pixels:
kernel_chunks = sliding_window_view(kernel, (3,3))
box_filter = ones((3,3)) / 9
blurred_kernel_chunks = multiply(kernel, box_filter)
blurred_pixels.append(blurred_kernel_chunks.sum(axis=(-1,-2)))
new_pixels = blurred_pixels
new_image.show()
am I in the right track?
blurred_kernel_chunks = multiply(kernel, box_filter)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: operands could not be broadcast together with shapes (3267,3) (3,3)
uhmmm.....
this has thrown me off so hard
check the .shape of pixels? maybe channel is the last axis (I'm not familiar with pillow tbh)
[
[ # <-- row
[0, 0, 0], # <-- column
],
[ # <-- x
[0, 0, 0], # <-- x, y
]
]
it doesnt make sense
whys it like that/
?
OH
its row by row
it looks like it puts the channel last
so you have a 3-lengthed array in each cell, because it's r g b
now it makes more sense
(4969, 3267, 3)
its y, x, somethingf
oh rgb
lol
something is the channel, aka rgb
you can np.moveaxis the 3 to the front, then you can loop and it should work as intended
that contains lists of pixels in that row
that contain a 3 valued list with the rgb
why its not a tuple idk
ig so its immutable?
ok
blurred_kernel_chunks = multiply(kernel, box_filter)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: operands could not be broadcast together with shapes (4969,3267) (3,3)
with Image.open("1.jpg") as img:
stupid_pixels = array(img)
pixels = moveaxis(stupid_pixels, -1, 0)
new_image = Image.new(mode="RGB", size=img.size)
new_pixels = new_image.load()
blurred_pixels = []
for kernel in pixels:
kernel_chunks = sliding_window_view(kernel, (3,3))
box_filter = ones((3,3)) / 9
blurred_kernel_chunks = multiply(kernel, box_filter)
blurred_pixels.append(blurred_kernel_chunks.sum(axis=(-1,-2)))
new_pixels = blurred_pixels
new_image.show()
(4969, 3267, 3)
(3, 4969, 3267)
kernel_chunks = sliding_window_view(kernel, (3,3))
# ^^^^^^^^^^^^^
box_filter = ones((3,3)) / 9
# vvvvvv
blurred_kernel_chunks = multiply(kernel, box_filter)
new_pixels = blurred_pixels
new_image.show()
```you probably have to do more here, tho that's stepping into pillow and again I'm not that familiar with it
tho assigning to new_pixels would definitely not affect new_image
pixels[x, y] = (int(R), int(G), int(B))
thats all I did before
pixels is not just a list
its this PyAccess thing
You might want Pillow.Image.fromarray: https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.fromarray
to go back to a Pillow Image from your numpy image array
like whatever you're doing, I'm 99% certain that new_pixels = blurred_pixels definitely won't change anything about new_image
maybe try making an image from the array instead? yeah like cameron pointed to
new_image = Image.fromarray(blurred_pixels, "RGB")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/PIL/Image.py", line 3266, in fromarray
arr = obj.__array_interface__
^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute '__array_interface__'
do I reshape it?
I mean moveaxis
back
Note this caveat paragraph there: "In the case of NumPy, be aware that Pillow modes do not always correspond to NumPy dtypes. Pillow modes only offer 1-bit pixels, 8-bit pixels, 32-bit signed integer pixels, and 32-bit floating point pixels."
I think your blurred_pixels isn't a numpy array but a plain list?
Both Image and a numpy array manipulate a packed binary array of values, which is how they get to shovel their data back and forth so easily.
so I need blurred_pixels to be a numpy array
I thought you were doing the blurring using numpy anyway. I've been elsewhere.
blurred_pixels.append(blurred_kernel_chunk.sum(axis=(-1,-2)))
whats the numpy equivelant to this?
with Image.open("1.jpg") as img:
stupid_pixels = array(img)
pixels = moveaxis(stupid_pixels, -1, 0)
blurred_pixels = empty(pixels.shape)
for kernel in pixels:
kernel_chunk = sliding_window_view(kernel, (3,3))
box_filter = ones((3,3)) / 9
blurred_kernel_chunk = multiply(kernel_chunk, box_filter)
blurred_pixels.append(blurred_kernel_chunk.sum(axis=(-1,-2)))
blurred_pixels = moveaxis(blurred_pixels, -1, 0)
new_image = Image.fromarray(blurred_pixels, "RGB")
print("Time:", (time() - tick), "s")
new_image.show()
this is where we're at rn.
+= ?
basically we have a solution that works with a grayscale image
but OP's image has 3 color channels, so we decided that the easiest way to fix that is to loop over the channels
none, numpy's arrays are static in size
p sure you can turn the list into a numpy array easliy tho
I did
blurred_pixels = empty(pixels.shape)
thats numpy.empty
it should match the size
or do I do zeros
since I need something to refil the empty corners anyways
Somehow blurred_pixels is becoming not a numpy array. Put in some prints and see if you can see what it's type is print(type(blurred_pixels))
You can make a bigger image by padding the borders with zeroes. There are numpy methods for that kind of thing.
blurred_pixels.append(blurred_kernel_chunk.sum(axis=(-1,-2)))
^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'numpy.ndarray' object has no attribute 'append'
wdym
it is
still a ndarray
I just need to figure out how to build it again
Then Image.fromarray ought to work.
Check its type just before calling that to be sure.
you should be able to have blurred_pixels as a list, then at the end just turn it back into a numpy array again by blurred_pixels = np.array(blurred_pixels)
oh im so stupid
and remember to move the color channel axis to where it should be
alr done
nvm
wit
nvm
same result
what's the shape after you moveaxis?
what's the code now?
with Image.open("1.jpg") as img:
stupid_pixels = array(img)
pixels = moveaxis(stupid_pixels, -1, 0)
blurred_kernels = []
for kernel in pixels:
kernel_chunk = sliding_window_view(kernel, (3,3))
box_filter = ones((3,3)) / 9
blurred_kernel_chunk = multiply(kernel_chunk, box_filter)
blurred_kernels.append(blurred_kernel_chunk.sum(axis=(-1,-2)))
blurred_pixels = array(blurred_kernels)
blurred_pixels = moveaxis(blurred_pixels, 0, -1)
print(blurred_pixels.shape)
new_image = Image.fromarray(blurred_pixels, "RGB")
print("Time:", (time() - tick), "s")
new_image.show()
(4969, 3267, 3)
(3, 4969, 3267)
(4967, 3265, 3)
Time: 6.69256329536438 s
im somehow losing 2 pixels at the end...?
[[[1.76333333e+02 1.86333333e+02 1.88333333e+02]
[1.76666667e+02 1.86666667e+02 1.88666667e+02]
[1.76444444e+02 1.86444444e+02 1.88444444e+02]
...
[2.27222222e+02 2.30888889e+02 2.34555556e+02]
[2.27444444e+02 2.30777778e+02 2.35111111e+02]
[2.27777778e+02 2.30777778e+02 2.35777778e+02]]
[[1.75333333e+02 1.85666667e+02 1.87666667e+02]
[1.75777778e+02 1.86111111e+02 1.88111111e+02]
[1.76111111e+02 1.86444444e+02 1.88444444e+02]
...
[2.27555556e+02 2.31333333e+02 2.34777778e+02]
[2.26888889e+02 2.30444444e+02 2.34333333e+02]
[2.26777778e+02 2.30111111e+02 2.34444444e+02]]
[[1.75333333e+02 1.86000000e+02 1.88000000e+02]
[1.75777778e+02 1.86444444e+02 1.88444444e+02]
[1.76666667e+02 1.87333333e+02 1.89333333e+02]
...
[2.28000000e+02 2.31888889e+02 2.35111111e+02]
[2.26444444e+02 2.30222222e+02 2.33666667e+02]
[2.25555556e+02 2.29222222e+02 2.32888889e+02]]
...
[[7.51111111e+01 5.16666667e+01 4.57777778e+01]
[7.93333333e+01 5.53333333e+01 5.02222222e+01]
[8.30000000e+01 5.86666667e+01 5.42222222e+01]
...
[9.77777778e+00 8.88888889e-01 2.22222222e-01]
[9.66666667e+00 1.22222222e+00 0.00000000e+00]
[9.44444444e+00 1.44444444e+00 0.00000000e+00]]
[[7.86666667e+01 5.46666667e+01 4.98888889e+01]
[8.18888889e+01 5.76666667e+01 5.33333333e+01]
[8.50000000e+01 6.10000000e+01 5.67777778e+01]
...
[9.66666667e+00 6.66666667e-01 3.33333333e-01]
[9.55555556e+00 1.00000000e+00 1.11111111e-01]
[9.33333333e+00 1.22222222e+00 1.11111111e-01]]
...
this is what the image ends up becoming btw
I feel like this is wrong...?
[[[177 187 189]
[179 189 191]
[182 192 194]
...
[226 229 234]
[229 232 237]
[227 230 235]]
[[173 183 185]
[176 186 188]
[176 186 188]
...
[229 232 237]
[228 231 236]
[228 231 236]]
[[177 187 189]
[175 185 187]
[172 182 184]
...
[229 232 237]
[226 229 234]
[228 231 236]]
...
[[ 75 52 46]
[ 73 49 45]
[ 86 62 58]
...
[ 8 0 0]
[ 9 1 0]
[ 9 1 0]]
[[ 85 60 56]
[ 81 56 52]
[ 84 60 56]
...
[ 9 0 1]
[ 10 2 0]
[ 10 2 0]]
[[ 88 64 60]
[ 80 59 56]
[ 81 60 57]
...
[ 13 3 2]
[ 4 0 0]
[ 2 0 1]]]
I mean compared to this
wait what if it needs to be integers not floats
it do be looking jank
probably bugs
any idea how to fix this? π
I'd be lying if I said I understand how half of this works
yeah it was type jank
blurred_kernels.append(blurred_kernel_chunk.sum(axis=(-1,-2), dtype=np.uint8))
# ^^^^^^^^^^^^^^
I mean it worked....
not really blurred tho π
I thought this would happen
3x3 blur filter is too small
with Image.open("1.jpg") as img:
stupid_pixels = array(img)
print(stupid_pixels.shape)
pixels = moveaxis(stupid_pixels, -1, 0)
print(pixels.shape)
blurred_kernels = []
box_filter_size = (3,3)
for kernel in pixels:
kernel_chunk = sliding_window_view(kernel, window_shape=box_filter_size)
s1, s2 = box_filter_size
box_filter = ones(shape=box_filter_size) / (s1 * s2)
blurred_kernel_chunk = multiply(kernel_chunk, box_filter)
blurred_kernels.append(blurred_kernel_chunk.sum(axis=(-1,-2), dtype=uint8))
blurred_pixels = array(blurred_kernels)
blurred_pixels = moveaxis(blurred_pixels, 0, -1)
print(blurred_pixels.shape)
new_image = Image.fromarray(blurred_pixels, "RGB")
print("Time:", (time() - tick), "s")
new_image.show()
I alr implemented this
it'd probably work if you just changed it to 5x5 or whatever
imma try
before we fixed the data type
zsh would kill it cuz it ran too long
6 was max I could do
also some stuff I didn't think about cause I'm dumb:
- you can use
np.meaninstead ofnp.sumthen you don't need to divide by9on the kernel np.multiplyis just*(I thought it was matmul but then that's@lol)
nice parrot
it's not efficient computation wise (but is faster cause you're looping in C now)
on sliding_window_view's page you can see that it mentions that you can probably do better with more specialized stuff
side by side
its not rly blurring it rather than kinda messing with the colors
or am I not seeing it right
wait wdym by the 2nd one
np.multiply(a, b) is just a * b
this is using mean
instead of sum
also I took out the diving by filter size square
lmao what did I just create
I mean it's definitely blurring for me
with Image.open("...") as img:
stupid_pixels = np.array(img)
pixels = np.moveaxis(stupid_pixels, -1, 0)
blurred = []
for mat in pixels:
chunks = sliding_window_view(mat, (3, 3))
blurred.append(chunks.mean(axis=(-1, -2)))
blurred = np.array(blurred).astype(np.uint8)
blurred = np.moveaxis(blurred, 0, -1)
new_image = Image.fromarray(blurred, "RGB")
print("Time:", (time() - tick), "s")
new_image.show()
oh so ndarray has its __mu__ dunder set to matrix multiplication by default?
thats the * dunder right?
no, * is element-wise multiplication by default
matrix multiplication uses @, i.e. a @ b does matmul of a and b
it's a python term
hows urs so compact π
it's just that python specifies @ for __matmul__
except I'm pretty sure that nothing in python implements @ by default, so it's like there for other library to override ig
well since we're taking the average using mean now like a smart person, the box filter is no longer needed
and I removed some extra newlines
here's it running on an image on my pc in comparison
so it definitely do be blurring
3x3 vs 9x9
barely any diff
shit needs like 200
imma do 69
prob gonna crash
lol
how big is your image lol
you might run into some performance issues if you increase the kernel size again
yeah I saw
that was my error
when it didnt work
yeah...
its 3567 by 4969
I think
beeg parrot
its a 4k image π
yeah...
well 69x69 is still running
hasnt crashed or slowed down my pc
soooo
im hopeful
π
stuff like scipy.signal.convolve2d is gonna run a lot faster if your kernel is big
cause you technically don't need all of those windows to do convolution
aha...
so how do we implement that?
π
there we go
69
the magic number
took 240 seconds
so instead of sliding_window_view we use convolve2d?
in numpy only? it's a PITA so don't bother /hj
if you have to, maybe you'll need numpy.lib.stride_tricks.as_strided
what do u mean /handjob?
half joking
lmfa
well the mission rn is
with Image.open("1.jpg") as img:
stupid_pixels = array(img)
pixels = moveaxis(stupid_pixels, -1, 0)
blurred = []
filter_size = (69, 69)
for mat in pixels:
chunks = sliding_window_view(mat, filter_size)
blurred.append(chunks.mean(axis=(-1, -2)))
blurred = array(blurred).astype(uint8)
blurred = moveaxis(blurred, 0, -1)
new_image = Image.fromarray(blurred, "RGB")
print("Time:", (time() - tick), "s")
new_image.show()
how we take this
from 240s to like 5s
5s prob too extreme
30s
honestly just slap that convolve2d and you should get there
if you want to pure numpy then well... good luck cause I don't know either
ig I can explain the idea
who named this shit?
lets use convolve2d
afterall its just a math lib
its not blurring
where do I slap that bitch?
for mat in pixels:
box_filter = np.ones(filter_size) / np.prod(filter_size)
blurred.append( scipy.convolve2d(mat, box_filter) )
how do people do that thing where ur terminal shows like a loading thing
or like something that shows u that its still running or like progress updates
for the script
without slowing shit down
cuz its boring just looking at a still terminal hoping something will pop up
lol
im guessing the logging lib?
as for why it can run so much faster, imagine you have a 1d array again and a 10-lengthed window
[|1 2 3 ... 10|11 12 ... ]
[ 1|2 3 ... 10 11|12 ... ]
[ 1 2|3 ... 10 11 12|... ]
```notice that the the window only differs from each other by the head & tail element, i.e the first window is `1~10`, the second is `2~11`; the two differs by `-1+11`
so instead of re-calculating each window every time, you can just change the current window by 2 values
specialized algos will do this so they run faster than stride tricks
yeah
makes sense
I noticed that alr b4
I mean so far its not looking like its gonna do better than 240s π
maybe fftconvolve instead of convolve2d?
ugh
still runnning?
wth
its been like 5m
is this ever gonna end?
imma stop it and do 3x3 to see just how bad it is
ok 3s
wth?
ok
fftconvolve
25,25 in 2 seconds
looking good
there we go
69x69 in 3s
but now getting this weird black border
@junior thicket
vinegtte
probably cause it pads with 0s by default
try changing the mode ig
scipy.signal also says there's a oaconvolve which might be even faster
but honestly just try stuff out and see what works
methodstr {βautoβ, βdirectβ, βfftβ}, optional
A string indicating which method to use to calculate the convolution.
direct
The convolution is determined directly from sums, the definition of convolution.
fft
The Fourier Transform is used to perform the convolution by calling fftconvolve.
auto
Automatically chooses direct or Fourier method based on an estimate of which is faster (default). See Notes for more detail.
their regular convolve
just chooses which is gonna be fastest
oaconvolve 4.3
concolve auto 4.7
fftconvolve 4.5
if I wanted a variable filter size where it is determined by the size of an image how do I do that
like so each image has the same amount of blurring
not rly sure what the math would be for that
@junior thicket
This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.